package pkgbits
import (
"bytes"
"crypto/md5"
"encoding/binary"
"go/constant"
"io"
"math/big"
"runtime"
"strings"
)
type PkgEncoder struct {
version Version
elems [numRelocs ][]string
stringsIdx map [string ]Index
syncFrames int
}
func (pw *PkgEncoder ) SyncMarkers () bool { return pw .syncFrames >= 0 }
func NewPkgEncoder (version Version , syncFrames int ) PkgEncoder {
return PkgEncoder {
version : version ,
stringsIdx : make (map [string ]Index ),
syncFrames : syncFrames ,
}
}
func (pw *PkgEncoder ) DumpTo (out0 io .Writer ) (fingerprint [8 ]byte ) {
h := md5 .New ()
out := io .MultiWriter (out0 , h )
writeUint32 := func (x uint32 ) {
assert (binary .Write (out , binary .LittleEndian , x ) == nil )
}
writeUint32 (uint32 (pw .version ))
if pw .version .Has (Flags ) {
var flags uint32
if pw .SyncMarkers () {
flags |= flagSyncMarkers
}
writeUint32 (flags )
}
var sum uint32
for _ , elems := range &pw .elems {
sum += uint32 (len (elems ))
writeUint32 (sum )
}
sum = 0
for _ , elems := range &pw .elems {
for _ , elem := range elems {
sum += uint32 (len (elem ))
writeUint32 (sum )
}
}
for _ , elems := range &pw .elems {
for _ , elem := range elems {
_ , err := io .WriteString (out , elem )
assert (err == nil )
}
}
copy (fingerprint [:], h .Sum (nil ))
_ , err := out0 .Write (fingerprint [:])
assert (err == nil )
return
}
func (pw *PkgEncoder ) StringIdx (s string ) Index {
if idx , ok := pw .stringsIdx [s ]; ok {
assert (pw .elems [RelocString ][idx ] == s )
return idx
}
idx := Index (len (pw .elems [RelocString ]))
pw .elems [RelocString ] = append (pw .elems [RelocString ], s )
pw .stringsIdx [s ] = idx
return idx
}
func (pw *PkgEncoder ) NewEncoder (k RelocKind , marker SyncMarker ) Encoder {
e := pw .NewEncoderRaw (k )
e .Sync (marker )
return e
}
func (pw *PkgEncoder ) NewEncoderRaw (k RelocKind ) Encoder {
idx := Index (len (pw .elems [k ]))
pw .elems [k ] = append (pw .elems [k ], "" )
return Encoder {
p : pw ,
k : k ,
Idx : idx ,
}
}
type Encoder struct {
p *PkgEncoder
Relocs []RelocEnt
RelocMap map [RelocEnt ]uint32
Data bytes .Buffer
encodingRelocHeader bool
k RelocKind
Idx Index
}
func (w *Encoder ) Flush () Index {
var sb strings .Builder
var tmp bytes .Buffer
io .Copy (&tmp , &w .Data )
if w .encodingRelocHeader {
panic ("encodingRelocHeader already true; recursive flush?" )
}
w .encodingRelocHeader = true
w .Sync (SyncRelocs )
w .Len (len (w .Relocs ))
for _ , rEnt := range w .Relocs {
w .Sync (SyncReloc )
w .Len (int (rEnt .Kind ))
w .Len (int (rEnt .Idx ))
}
io .Copy (&sb , &w .Data )
io .Copy (&sb , &tmp )
w .p .elems [w .k ][w .Idx ] = sb .String ()
return w .Idx
}
func (w *Encoder ) checkErr (err error ) {
if err != nil {
panicf ("unexpected encoding error: %v" , err )
}
}
func (w *Encoder ) rawUvarint (x uint64 ) {
var buf [binary .MaxVarintLen64 ]byte
n := binary .PutUvarint (buf [:], x )
_ , err := w .Data .Write (buf [:n ])
w .checkErr (err )
}
func (w *Encoder ) rawVarint (x int64 ) {
ux := uint64 (x ) << 1
if x < 0 {
ux = ^ux
}
w .rawUvarint (ux )
}
func (w *Encoder ) rawReloc (r RelocKind , idx Index ) int {
e := RelocEnt {r , idx }
if w .RelocMap != nil {
if i , ok := w .RelocMap [e ]; ok {
return int (i )
}
} else {
w .RelocMap = make (map [RelocEnt ]uint32 )
}
i := len (w .Relocs )
w .RelocMap [e ] = uint32 (i )
w .Relocs = append (w .Relocs , e )
return i
}
func (w *Encoder ) Sync (m SyncMarker ) {
if !w .p .SyncMarkers () {
return
}
var frames []string
if !w .encodingRelocHeader && w .p .syncFrames > 0 {
pcs := make ([]uintptr , w .p .syncFrames )
n := runtime .Callers (2 , pcs )
frames = fmtFrames (pcs [:n ]...)
}
w .rawUvarint (uint64 (m ))
w .rawUvarint (uint64 (len (frames )))
for _ , frame := range frames {
w .rawUvarint (uint64 (w .rawReloc (RelocString , w .p .StringIdx (frame ))))
}
}
func (w *Encoder ) Bool (b bool ) bool {
w .Sync (SyncBool )
var x byte
if b {
x = 1
}
err := w .Data .WriteByte (x )
w .checkErr (err )
return b
}
func (w *Encoder ) Int64 (x int64 ) {
w .Sync (SyncInt64 )
w .rawVarint (x )
}
func (w *Encoder ) Uint64 (x uint64 ) {
w .Sync (SyncUint64 )
w .rawUvarint (x )
}
func (w *Encoder ) Len (x int ) { assert (x >= 0 ); w .Uint64 (uint64 (x )) }
func (w *Encoder ) Int (x int ) { w .Int64 (int64 (x )) }
func (w *Encoder ) Uint (x uint ) { w .Uint64 (uint64 (x )) }
func (w *Encoder ) Reloc (r RelocKind , idx Index ) {
w .Sync (SyncUseReloc )
w .Len (w .rawReloc (r , idx ))
}
func (w *Encoder ) Code (c Code ) {
w .Sync (c .Marker ())
w .Len (c .Value ())
}
func (w *Encoder ) String (s string ) {
w .StringRef (w .p .StringIdx (s ))
}
func (w *Encoder ) StringRef (idx Index ) {
w .Sync (SyncString )
w .Reloc (RelocString , idx )
}
func (w *Encoder ) Strings (ss []string ) {
w .Len (len (ss ))
for _ , s := range ss {
w .String (s )
}
}
func (w *Encoder ) Value (val constant .Value ) {
w .Sync (SyncValue )
if w .Bool (val .Kind () == constant .Complex ) {
w .scalar (constant .Real (val ))
w .scalar (constant .Imag (val ))
} else {
w .scalar (val )
}
}
func (w *Encoder ) scalar (val constant .Value ) {
switch v := constant .Val (val ).(type ) {
default :
panicf ("unhandled %v (%v)" , val , val .Kind ())
case bool :
w .Code (ValBool )
w .Bool (v )
case string :
w .Code (ValString )
w .String (v )
case int64 :
w .Code (ValInt64 )
w .Int64 (v )
case *big .Int :
w .Code (ValBigInt )
w .bigInt (v )
case *big .Rat :
w .Code (ValBigRat )
w .bigInt (v .Num ())
w .bigInt (v .Denom ())
case *big .Float :
w .Code (ValBigFloat )
w .bigFloat (v )
}
}
func (w *Encoder ) bigInt (v *big .Int ) {
b := v .Bytes ()
w .String (string (b ))
w .Bool (v .Sign () < 0 )
}
func (w *Encoder ) bigFloat (v *big .Float ) {
b := v .Append (nil , 'p' , -1 )
w .String (string (b ))
}
func (w *Encoder ) Version () Version { return w .p .version }
The pages are generated with Golds v0.7.3 . (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 .