package gob
import (
"encoding"
"encoding/binary"
"math"
"math/bits"
"reflect"
"sync"
)
const uint64Size = 8
type encHelper func (state *encoderState , v reflect .Value ) bool
type encoderState struct {
enc *Encoder
b *encBuffer
sendZero bool
fieldnum int
buf [1 + uint64Size ]byte
next *encoderState
}
type encBuffer struct {
data []byte
scratch [64 ]byte
}
var encBufferPool = sync .Pool {
New : func () any {
e := new (encBuffer )
e .data = e .scratch [0 :0 ]
return e
},
}
func (e *encBuffer ) writeByte (c byte ) {
e .data = append (e .data , c )
}
func (e *encBuffer ) Write (p []byte ) (int , error ) {
e .data = append (e .data , p ...)
return len (p ), nil
}
func (e *encBuffer ) WriteString (s string ) {
e .data = append (e .data , s ...)
}
func (e *encBuffer ) Len () int {
return len (e .data )
}
func (e *encBuffer ) Bytes () []byte {
return e .data
}
func (e *encBuffer ) Reset () {
if len (e .data ) >= tooBig {
e .data = e .scratch [0 :0 ]
} else {
e .data = e .data [0 :0 ]
}
}
func (enc *Encoder ) newEncoderState (b *encBuffer ) *encoderState {
e := enc .freeList
if e == nil {
e = new (encoderState )
e .enc = enc
} else {
enc .freeList = e .next
}
e .sendZero = false
e .fieldnum = 0
e .b = b
if len (b .data ) == 0 {
b .data = b .scratch [0 :0 ]
}
return e
}
func (enc *Encoder ) freeEncoderState (e *encoderState ) {
e .next = enc .freeList
enc .freeList = e
}
func (state *encoderState ) encodeUint (x uint64 ) {
if x <= 0x7F {
state .b .writeByte (uint8 (x ))
return
}
binary .BigEndian .PutUint64 (state .buf [1 :], x )
bc := bits .LeadingZeros64 (x ) >> 3
state .buf [bc ] = uint8 (bc - uint64Size )
state .b .Write (state .buf [bc : uint64Size +1 ])
}
func (state *encoderState ) encodeInt (i int64 ) {
var x uint64
if i < 0 {
x = uint64 (^i <<1 ) | 1
} else {
x = uint64 (i << 1 )
}
state .encodeUint (x )
}
type encOp func (i *encInstr , state *encoderState , v reflect .Value )
type encInstr struct {
op encOp
field int
index []int
indir int
}
func (state *encoderState ) update (instr *encInstr ) {
if instr != nil {
state .encodeUint (uint64 (instr .field - state .fieldnum ))
state .fieldnum = instr .field
}
}
func encIndirect(pv reflect .Value , indir int ) reflect .Value {
for ; indir > 0 ; indir -- {
if pv .IsNil () {
break
}
pv = pv .Elem ()
}
return pv
}
func encBool(i *encInstr , state *encoderState , v reflect .Value ) {
b := v .Bool ()
if b || state .sendZero {
state .update (i )
if b {
state .encodeUint (1 )
} else {
state .encodeUint (0 )
}
}
}
func encInt(i *encInstr , state *encoderState , v reflect .Value ) {
value := v .Int ()
if value != 0 || state .sendZero {
state .update (i )
state .encodeInt (value )
}
}
func encUint(i *encInstr , state *encoderState , v reflect .Value ) {
value := v .Uint ()
if value != 0 || state .sendZero {
state .update (i )
state .encodeUint (value )
}
}
func floatBits(f float64 ) uint64 {
u := math .Float64bits (f )
return bits .ReverseBytes64 (u )
}
func encFloat(i *encInstr , state *encoderState , v reflect .Value ) {
f := v .Float ()
if f != 0 || state .sendZero {
bits := floatBits (f )
state .update (i )
state .encodeUint (bits )
}
}
func encComplex(i *encInstr , state *encoderState , v reflect .Value ) {
c := v .Complex ()
if c != 0 +0i || state .sendZero {
rpart := floatBits (real (c ))
ipart := floatBits (imag (c ))
state .update (i )
state .encodeUint (rpart )
state .encodeUint (ipart )
}
}
func encUint8Array(i *encInstr , state *encoderState , v reflect .Value ) {
b := v .Bytes ()
if len (b ) > 0 || state .sendZero {
state .update (i )
state .encodeUint (uint64 (len (b )))
state .b .Write (b )
}
}
func encString(i *encInstr , state *encoderState , v reflect .Value ) {
s := v .String ()
if len (s ) > 0 || state .sendZero {
state .update (i )
state .encodeUint (uint64 (len (s )))
state .b .WriteString (s )
}
}
func encStructTerminator(i *encInstr , state *encoderState , v reflect .Value ) {
state .encodeUint (0 )
}
type encEngine struct {
instr []encInstr
}
const singletonField = 0
func valid(v reflect .Value ) bool {
switch v .Kind () {
case reflect .Invalid :
return false
case reflect .Pointer :
return !v .IsNil ()
}
return true
}
func (enc *Encoder ) encodeSingle (b *encBuffer , engine *encEngine , value reflect .Value ) {
state := enc .newEncoderState (b )
defer enc .freeEncoderState (state )
state .fieldnum = singletonField
state .sendZero = true
instr := &engine .instr [singletonField ]
if instr .indir > 0 {
value = encIndirect (value , instr .indir )
}
if valid (value ) {
instr .op (instr , state , value )
}
}
func (enc *Encoder ) encodeStruct (b *encBuffer , engine *encEngine , value reflect .Value ) {
if !valid (value ) {
return
}
state := enc .newEncoderState (b )
defer enc .freeEncoderState (state )
state .fieldnum = -1
for i := 0 ; i < len (engine .instr ); i ++ {
instr := &engine .instr [i ]
if i >= value .NumField () {
instr .op (instr , state , reflect .Value {})
break
}
field := value .FieldByIndex (instr .index )
if instr .indir > 0 {
field = encIndirect (field , instr .indir )
if !valid (field ) {
continue
}
}
instr .op (instr , state , field )
}
}
func (enc *Encoder ) encodeArray (b *encBuffer , value reflect .Value , op encOp , elemIndir int , length int , helper encHelper ) {
state := enc .newEncoderState (b )
defer enc .freeEncoderState (state )
state .fieldnum = -1
state .sendZero = true
state .encodeUint (uint64 (length ))
if helper != nil && helper (state , value ) {
return
}
for i := 0 ; i < length ; i ++ {
elem := value .Index (i )
if elemIndir > 0 {
elem = encIndirect (elem , elemIndir )
if !valid (elem ) {
errorf ("encodeArray: nil element" )
}
}
op (nil , state , elem )
}
}
func encodeReflectValue(state *encoderState , v reflect .Value , op encOp , indir int ) {
for i := 0 ; i < indir && v .IsValid (); i ++ {
v = reflect .Indirect (v )
}
if !v .IsValid () {
errorf ("encodeReflectValue: nil element" )
}
op (nil , state , v )
}
func (enc *Encoder ) encodeMap (b *encBuffer , mv reflect .Value , keyOp , elemOp encOp , keyIndir , elemIndir int ) {
state := enc .newEncoderState (b )
state .fieldnum = -1
state .sendZero = true
state .encodeUint (uint64 (mv .Len ()))
mi := mv .MapRange ()
for mi .Next () {
encodeReflectValue (state , mi .Key (), keyOp , keyIndir )
encodeReflectValue (state , mi .Value (), elemOp , elemIndir )
}
enc .freeEncoderState (state )
}
func (enc *Encoder ) encodeInterface (b *encBuffer , iv reflect .Value ) {
elem := iv .Elem ()
if elem .Kind () == reflect .Pointer && elem .IsNil () {
errorf ("gob: cannot encode nil pointer of type %s inside interface" , iv .Elem ().Type ())
}
state := enc .newEncoderState (b )
state .fieldnum = -1
state .sendZero = true
if iv .IsNil () {
state .encodeUint (0 )
return
}
ut := userType (iv .Elem ().Type ())
namei , ok := concreteTypeToName .Load (ut .base )
if !ok {
errorf ("type not registered for interface: %s" , ut .base )
}
name := namei .(string )
state .encodeUint (uint64 (len (name )))
state .b .WriteString (name )
enc .sendTypeDescriptor (enc .writer (), state , ut )
enc .sendTypeId (state , ut )
enc .pushWriter (b )
data := encBufferPool .Get ().(*encBuffer )
data .Write (spaceForLength )
enc .encode (data , elem , ut )
if enc .err != nil {
error_ (enc .err )
}
enc .popWriter ()
enc .writeMessage (b , data )
data .Reset ()
encBufferPool .Put (data )
if enc .err != nil {
error_ (enc .err )
}
enc .freeEncoderState (state )
}
func (enc *Encoder ) encodeGobEncoder (b *encBuffer , ut *userTypeInfo , v reflect .Value ) {
var data []byte
var err error
switch ut .externalEnc {
case xGob :
data , err = v .Interface ().(GobEncoder ).GobEncode ()
case xBinary :
data , err = v .Interface ().(encoding .BinaryMarshaler ).MarshalBinary ()
case xText :
data , err = v .Interface ().(encoding .TextMarshaler ).MarshalText ()
}
if err != nil {
error_ (err )
}
state := enc .newEncoderState (b )
state .fieldnum = -1
state .encodeUint (uint64 (len (data )))
state .b .Write (data )
enc .freeEncoderState (state )
}
var encOpTable = [...]encOp {
reflect .Bool : encBool ,
reflect .Int : encInt ,
reflect .Int8 : encInt ,
reflect .Int16 : encInt ,
reflect .Int32 : encInt ,
reflect .Int64 : encInt ,
reflect .Uint : encUint ,
reflect .Uint8 : encUint ,
reflect .Uint16 : encUint ,
reflect .Uint32 : encUint ,
reflect .Uint64 : encUint ,
reflect .Uintptr : encUint ,
reflect .Float32 : encFloat ,
reflect .Float64 : encFloat ,
reflect .Complex64 : encComplex ,
reflect .Complex128 : encComplex ,
reflect .String : encString ,
}
func encOpFor(rt reflect .Type , inProgress map [reflect .Type ]*encOp , building map [*typeInfo ]bool ) (*encOp , int ) {
ut := userType (rt )
if ut .externalEnc != 0 {
return gobEncodeOpFor (ut )
}
if opPtr := inProgress [rt ]; opPtr != nil {
return opPtr , ut .indir
}
typ := ut .base
indir := ut .indir
k := typ .Kind ()
var op encOp
if int (k ) < len (encOpTable ) {
op = encOpTable [k ]
}
if op == nil {
inProgress [rt ] = &op
switch t := typ ; t .Kind () {
case reflect .Slice :
if t .Elem ().Kind () == reflect .Uint8 {
op = encUint8Array
break
}
elemOp , elemIndir := encOpFor (t .Elem (), inProgress , building )
helper := encSliceHelper [t .Elem ().Kind ()]
op = func (i *encInstr , state *encoderState , slice reflect .Value ) {
if !state .sendZero && slice .Len () == 0 {
return
}
state .update (i )
state .enc .encodeArray (state .b , slice , *elemOp , elemIndir , slice .Len (), helper )
}
case reflect .Array :
elemOp , elemIndir := encOpFor (t .Elem (), inProgress , building )
helper := encArrayHelper [t .Elem ().Kind ()]
op = func (i *encInstr , state *encoderState , array reflect .Value ) {
state .update (i )
state .enc .encodeArray (state .b , array , *elemOp , elemIndir , array .Len (), helper )
}
case reflect .Map :
keyOp , keyIndir := encOpFor (t .Key (), inProgress , building )
elemOp , elemIndir := encOpFor (t .Elem (), inProgress , building )
op = func (i *encInstr , state *encoderState , mv reflect .Value ) {
if !state .sendZero && mv .IsNil () {
return
}
state .update (i )
state .enc .encodeMap (state .b , mv , *keyOp , *elemOp , keyIndir , elemIndir )
}
case reflect .Struct :
getEncEngine (userType (typ ), building )
info := mustGetTypeInfo (typ )
op = func (i *encInstr , state *encoderState , sv reflect .Value ) {
state .update (i )
enc := info .encoder .Load ()
state .enc .encodeStruct (state .b , enc , sv )
}
case reflect .Interface :
op = func (i *encInstr , state *encoderState , iv reflect .Value ) {
if !state .sendZero && (!iv .IsValid () || iv .IsNil ()) {
return
}
state .update (i )
state .enc .encodeInterface (state .b , iv )
}
}
}
if op == nil {
errorf ("can't happen: encode type %s" , rt )
}
return &op , indir
}
func gobEncodeOpFor(ut *userTypeInfo ) (*encOp , int ) {
rt := ut .user
if ut .encIndir == -1 {
rt = reflect .PointerTo (rt )
} else if ut .encIndir > 0 {
for i := int8 (0 ); i < ut .encIndir ; i ++ {
rt = rt .Elem ()
}
}
var op encOp
op = func (i *encInstr , state *encoderState , v reflect .Value ) {
if ut .encIndir == -1 {
if !v .CanAddr () {
errorf ("unaddressable value of type %s" , rt )
}
v = v .Addr ()
}
if !state .sendZero && v .IsZero () {
return
}
state .update (i )
state .enc .encodeGobEncoder (state .b , ut , v )
}
return &op , int (ut .encIndir )
}
func compileEnc(ut *userTypeInfo , building map [*typeInfo ]bool ) *encEngine {
srt := ut .base
engine := new (encEngine )
seen := make (map [reflect .Type ]*encOp )
rt := ut .base
if ut .externalEnc != 0 {
rt = ut .user
}
if ut .externalEnc == 0 && srt .Kind () == reflect .Struct {
for fieldNum , wireFieldNum := 0 , 0 ; fieldNum < srt .NumField (); fieldNum ++ {
f := srt .Field (fieldNum )
if !isSent (&f ) {
continue
}
op , indir := encOpFor (f .Type , seen , building )
engine .instr = append (engine .instr , encInstr {*op , wireFieldNum , f .Index , indir })
wireFieldNum ++
}
if srt .NumField () > 0 && len (engine .instr ) == 0 {
errorf ("type %s has no exported fields" , rt )
}
engine .instr = append (engine .instr , encInstr {encStructTerminator , 0 , nil , 0 })
} else {
engine .instr = make ([]encInstr , 1 )
op , indir := encOpFor (rt , seen , building )
engine .instr [0 ] = encInstr {*op , singletonField , nil , indir }
}
return engine
}
func getEncEngine(ut *userTypeInfo , building map [*typeInfo ]bool ) *encEngine {
info , err := getTypeInfo (ut )
if err != nil {
error_ (err )
}
enc := info .encoder .Load ()
if enc == nil {
enc = buildEncEngine (info , ut , building )
}
return enc
}
func buildEncEngine(info *typeInfo , ut *userTypeInfo , building map [*typeInfo ]bool ) *encEngine {
if building != nil && building [info ] {
return nil
}
info .encInit .Lock ()
defer info .encInit .Unlock ()
enc := info .encoder .Load ()
if enc == nil {
if building == nil {
building = make (map [*typeInfo ]bool )
}
building [info ] = true
enc = compileEnc (ut , building )
info .encoder .Store (enc )
}
return enc
}
func (enc *Encoder ) encode (b *encBuffer , value reflect .Value , ut *userTypeInfo ) {
defer catchError (&enc .err )
engine := getEncEngine (ut , nil )
indir := ut .indir
if ut .externalEnc != 0 {
indir = int (ut .encIndir )
}
for i := 0 ; i < indir ; i ++ {
value = reflect .Indirect (value )
}
if ut .externalEnc == 0 && value .Type ().Kind () == reflect .Struct {
enc .encodeStruct (b , engine , value )
} else {
enc .encodeSingle (b , engine , value )
}
}
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 .