package pkgbits
import (
"encoding/binary"
"errors"
"fmt"
"go/constant"
"go/token"
"io"
"math/big"
"os"
"runtime"
"strings"
)
type PkgDecoder struct {
version uint32
sync bool
pkgPath string
elemData string
elemEnds []uint32
elemEndsEnds [numRelocs ]uint32
scratchRelocEnt []RelocEnt
}
func (pr *PkgDecoder ) PkgPath () string { return pr .pkgPath }
func (pr *PkgDecoder ) SyncMarkers () bool { return pr .sync }
func NewPkgDecoder (pkgPath , input string ) PkgDecoder {
pr := PkgDecoder {
pkgPath : pkgPath ,
}
r := strings .NewReader (input )
assert (binary .Read (r , binary .LittleEndian , &pr .version ) == nil )
switch pr .version {
default :
panic (fmt .Errorf ("unsupported version: %v" , pr .version ))
case 0 :
case 1 :
var flags uint32
assert (binary .Read (r , binary .LittleEndian , &flags ) == nil )
pr .sync = flags &flagSyncMarkers != 0
}
assert (binary .Read (r , binary .LittleEndian , pr .elemEndsEnds [:]) == nil )
pr .elemEnds = make ([]uint32 , pr .elemEndsEnds [len (pr .elemEndsEnds )-1 ])
assert (binary .Read (r , binary .LittleEndian , pr .elemEnds [:]) == nil )
pos , err := r .Seek (0 , io .SeekCurrent )
assert (err == nil )
pr .elemData = input [pos :]
assert (len (pr .elemData )-8 == int (pr .elemEnds [len (pr .elemEnds )-1 ]))
return pr
}
func (pr *PkgDecoder ) NumElems (k RelocKind ) int {
count := int (pr .elemEndsEnds [k ])
if k > 0 {
count -= int (pr .elemEndsEnds [k -1 ])
}
return count
}
func (pr *PkgDecoder ) TotalElems () int {
return len (pr .elemEnds )
}
func (pr *PkgDecoder ) Fingerprint () [8 ]byte {
var fp [8 ]byte
copy (fp [:], pr .elemData [len (pr .elemData )-8 :])
return fp
}
func (pr *PkgDecoder ) AbsIdx (k RelocKind , idx Index ) int {
absIdx := int (idx )
if k > 0 {
absIdx += int (pr .elemEndsEnds [k -1 ])
}
if absIdx >= int (pr .elemEndsEnds [k ]) {
errorf ("%v:%v is out of bounds; %v" , k , idx , pr .elemEndsEnds )
}
return absIdx
}
func (pr *PkgDecoder ) DataIdx (k RelocKind , idx Index ) string {
absIdx := pr .AbsIdx (k , idx )
var start uint32
if absIdx > 0 {
start = pr .elemEnds [absIdx -1 ]
}
end := pr .elemEnds [absIdx ]
return pr .elemData [start :end ]
}
func (pr *PkgDecoder ) StringIdx (idx Index ) string {
return pr .DataIdx (RelocString , idx )
}
func (pr *PkgDecoder ) NewDecoder (k RelocKind , idx Index , marker SyncMarker ) Decoder {
r := pr .NewDecoderRaw (k , idx )
r .Sync (marker )
return r
}
func (pr *PkgDecoder ) TempDecoder (k RelocKind , idx Index , marker SyncMarker ) Decoder {
r := pr .TempDecoderRaw (k , idx )
r .Sync (marker )
return r
}
func (pr *PkgDecoder ) RetireDecoder (d *Decoder ) {
pr .scratchRelocEnt = d .Relocs
d .Relocs = nil
}
func (pr *PkgDecoder ) NewDecoderRaw (k RelocKind , idx Index ) Decoder {
r := Decoder {
common : pr ,
k : k ,
Idx : idx ,
}
r .Data .Reset (pr .DataIdx (k , idx ))
r .Sync (SyncRelocs )
r .Relocs = make ([]RelocEnt , r .Len ())
for i := range r .Relocs {
r .Sync (SyncReloc )
r .Relocs [i ] = RelocEnt {RelocKind (r .Len ()), Index (r .Len ())}
}
return r
}
func (pr *PkgDecoder ) TempDecoderRaw (k RelocKind , idx Index ) Decoder {
r := Decoder {
common : pr ,
k : k ,
Idx : idx ,
}
r .Data .Reset (pr .DataIdx (k , idx ))
r .Sync (SyncRelocs )
l := r .Len ()
if cap (pr .scratchRelocEnt ) >= l {
r .Relocs = pr .scratchRelocEnt [:l ]
pr .scratchRelocEnt = nil
} else {
r .Relocs = make ([]RelocEnt , l )
}
for i := range r .Relocs {
r .Sync (SyncReloc )
r .Relocs [i ] = RelocEnt {RelocKind (r .Len ()), Index (r .Len ())}
}
return r
}
type Decoder struct {
common *PkgDecoder
Relocs []RelocEnt
Data strings .Reader
k RelocKind
Idx Index
}
func (r *Decoder ) checkErr (err error ) {
if err != nil {
errorf ("unexpected decoding error: %w" , err )
}
}
func (r *Decoder ) rawUvarint () uint64 {
x , err := readUvarint (&r .Data )
r .checkErr (err )
return x
}
func readUvarint(r *strings .Reader ) (uint64 , error ) {
var x uint64
var s uint
for i := 0 ; i < binary .MaxVarintLen64 ; i ++ {
b , err := r .ReadByte ()
if err != nil {
if i > 0 && err == io .EOF {
err = io .ErrUnexpectedEOF
}
return x , err
}
if b < 0x80 {
if i == binary .MaxVarintLen64 -1 && b > 1 {
return x , overflow
}
return x | uint64 (b )<<s , nil
}
x |= uint64 (b &0x7f ) << s
s += 7
}
return x , overflow
}
var overflow = errors .New ("pkgbits: readUvarint overflows a 64-bit integer" )
func (r *Decoder ) rawVarint () int64 {
ux := r .rawUvarint ()
x := int64 (ux >> 1 )
if ux &1 != 0 {
x = ^x
}
return x
}
func (r *Decoder ) rawReloc (k RelocKind , idx int ) Index {
e := r .Relocs [idx ]
assert (e .Kind == k )
return e .Idx
}
func (r *Decoder ) Sync (mWant SyncMarker ) {
if !r .common .sync {
return
}
pos , _ := r .Data .Seek (0 , io .SeekCurrent )
mHave := SyncMarker (r .rawUvarint ())
writerPCs := make ([]int , r .rawUvarint ())
for i := range writerPCs {
writerPCs [i ] = int (r .rawUvarint ())
}
if mHave == mWant {
return
}
fmt .Printf ("export data desync: package %q, section %v, index %v, offset %v\n" , r .common .pkgPath , r .k , r .Idx , pos )
fmt .Printf ("\nfound %v, written at:\n" , mHave )
if len (writerPCs ) == 0 {
fmt .Printf ("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n" , r .common .pkgPath )
}
for _ , pc := range writerPCs {
fmt .Printf ("\t%s\n" , r .common .StringIdx (r .rawReloc (RelocString , pc )))
}
fmt .Printf ("\nexpected %v, reading at:\n" , mWant )
var readerPCs [32 ]uintptr
n := runtime .Callers (2 , readerPCs [:])
for _ , pc := range fmtFrames (readerPCs [:n ]...) {
fmt .Printf ("\t%s\n" , pc )
}
os .Exit (1 )
}
func (r *Decoder ) Bool () bool {
r .Sync (SyncBool )
x , err := r .Data .ReadByte ()
r .checkErr (err )
assert (x < 2 )
return x != 0
}
func (r *Decoder ) Int64 () int64 {
r .Sync (SyncInt64 )
return r .rawVarint ()
}
func (r *Decoder ) Uint64 () uint64 {
r .Sync (SyncUint64 )
return r .rawUvarint ()
}
func (r *Decoder ) Len () int { x := r .Uint64 (); v := int (x ); assert (uint64 (v ) == x ); return v }
func (r *Decoder ) Int () int { x := r .Int64 (); v := int (x ); assert (int64 (v ) == x ); return v }
func (r *Decoder ) Uint () uint { x := r .Uint64 (); v := uint (x ); assert (uint64 (v ) == x ); return v }
func (r *Decoder ) Code (mark SyncMarker ) int {
r .Sync (mark )
return r .Len ()
}
func (r *Decoder ) Reloc (k RelocKind ) Index {
r .Sync (SyncUseReloc )
return r .rawReloc (k , r .Len ())
}
func (r *Decoder ) String () string {
r .Sync (SyncString )
return r .common .StringIdx (r .Reloc (RelocString ))
}
func (r *Decoder ) Strings () []string {
res := make ([]string , r .Len ())
for i := range res {
res [i ] = r .String ()
}
return res
}
func (r *Decoder ) Value () constant .Value {
r .Sync (SyncValue )
isComplex := r .Bool ()
val := r .scalar ()
if isComplex {
val = constant .BinaryOp (val , token .ADD , constant .MakeImag (r .scalar ()))
}
return val
}
func (r *Decoder ) scalar () constant .Value {
switch tag := CodeVal (r .Code (SyncVal )); tag {
default :
panic (fmt .Errorf ("unexpected scalar tag: %v" , tag ))
case ValBool :
return constant .MakeBool (r .Bool ())
case ValString :
return constant .MakeString (r .String ())
case ValInt64 :
return constant .MakeInt64 (r .Int64 ())
case ValBigInt :
return constant .Make (r .bigInt ())
case ValBigRat :
num := r .bigInt ()
denom := r .bigInt ()
return constant .Make (new (big .Rat ).SetFrac (num , denom ))
case ValBigFloat :
return constant .Make (r .bigFloat ())
}
}
func (r *Decoder ) bigInt () *big .Int {
v := new (big .Int ).SetBytes ([]byte (r .String ()))
if r .Bool () {
v .Neg (v )
}
return v
}
func (r *Decoder ) bigFloat () *big .Float {
v := new (big .Float ).SetPrec (512 )
assert (v .UnmarshalText ([]byte (r .String ())) == nil )
return v
}
func (pr *PkgDecoder ) PeekPkgPath (idx Index ) string {
var path string
{
r := pr .TempDecoder (RelocPkg , idx , SyncPkgDef )
path = r .String ()
pr .RetireDecoder (&r )
}
if path == "" {
path = pr .pkgPath
}
return path
}
func (pr *PkgDecoder ) PeekObj (idx Index ) (string , string , CodeObj ) {
var ridx Index
var name string
var rcode int
{
r := pr .TempDecoder (RelocName , idx , SyncObject1 )
r .Sync (SyncSym )
r .Sync (SyncPkg )
ridx = r .Reloc (RelocPkg )
name = r .String ()
rcode = r .Code (SyncCodeObj )
pr .RetireDecoder (&r )
}
path := pr .PeekPkgPath (ridx )
assert (name != "" )
tag := CodeObj (rcode )
return path , name , tag
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .