package gob
import (
"encoding"
"errors"
"fmt"
"os"
"reflect"
"sync"
"sync/atomic"
"unicode"
"unicode/utf8"
)
type userTypeInfo struct {
user reflect .Type
base reflect .Type
indir int
externalEnc int
externalDec int
encIndir int8
decIndir int8
}
const (
xGob = 1 + iota
xBinary
xText
)
var userTypeCache sync .Map
func validUserType(rt reflect .Type ) (*userTypeInfo , error ) {
if ui , ok := userTypeCache .Load (rt ); ok {
return ui .(*userTypeInfo ), nil
}
ut := new (userTypeInfo )
ut .base = rt
ut .user = rt
slowpoke := ut .base
for {
pt := ut .base
if pt .Kind () != reflect .Pointer {
break
}
ut .base = pt .Elem ()
if ut .base == slowpoke {
return nil , errors .New ("can't represent recursive pointer type " + ut .base .String ())
}
if ut .indir %2 == 0 {
slowpoke = slowpoke .Elem ()
}
ut .indir ++
}
if ok , indir := implementsInterface (ut .user , gobEncoderInterfaceType ); ok {
ut .externalEnc , ut .encIndir = xGob , indir
} else if ok , indir := implementsInterface (ut .user , binaryMarshalerInterfaceType ); ok {
ut .externalEnc , ut .encIndir = xBinary , indir
}
if ok , indir := implementsInterface (ut .user , gobDecoderInterfaceType ); ok {
ut .externalDec , ut .decIndir = xGob , indir
} else if ok , indir := implementsInterface (ut .user , binaryUnmarshalerInterfaceType ); ok {
ut .externalDec , ut .decIndir = xBinary , indir
}
ui , _ := userTypeCache .LoadOrStore (rt , ut )
return ui .(*userTypeInfo ), nil
}
var (
gobEncoderInterfaceType = reflect .TypeFor [GobEncoder ]()
gobDecoderInterfaceType = reflect .TypeFor [GobDecoder ]()
binaryMarshalerInterfaceType = reflect .TypeFor [encoding .BinaryMarshaler ]()
binaryUnmarshalerInterfaceType = reflect .TypeFor [encoding .BinaryUnmarshaler ]()
textMarshalerInterfaceType = reflect .TypeFor [encoding .TextMarshaler ]()
textUnmarshalerInterfaceType = reflect .TypeFor [encoding .TextUnmarshaler ]()
wireTypeType = reflect .TypeFor [wireType ]()
)
func implementsInterface(typ , gobEncDecType reflect .Type ) (success bool , indir int8 ) {
if typ == nil {
return
}
rt := typ
for {
if rt .Implements (gobEncDecType ) {
return true , indir
}
if p := rt ; p .Kind () == reflect .Pointer {
indir ++
if indir > 100 {
return false , 0
}
rt = p .Elem ()
continue
}
break
}
if typ .Kind () != reflect .Pointer {
if reflect .PointerTo (typ ).Implements (gobEncDecType ) {
return true , -1
}
}
return false , 0
}
func userType(rt reflect .Type ) *userTypeInfo {
ut , err := validUserType (rt )
if err != nil {
error_ (err )
}
return ut
}
type typeId int32
var typeLock sync .Mutex
const firstUserId = 64
type gobType interface {
id() typeId
setId(id typeId )
name() string
string() string
safeString(seen map [typeId ]bool ) string
}
var (
types = make (map [reflect .Type ]gobType , 32 )
idToTypeSlice = make ([]gobType , 1 , firstUserId )
builtinIdToTypeSlice [firstUserId ]gobType
)
func idToType(id typeId ) gobType {
if id < 0 || int (id ) >= len (idToTypeSlice ) {
return nil
}
return idToTypeSlice [id ]
}
func builtinIdToType(id typeId ) gobType {
if id < 0 || int (id ) >= len (builtinIdToTypeSlice ) {
return nil
}
return builtinIdToTypeSlice [id ]
}
func setTypeId(typ gobType ) {
if typ .id () != 0 {
return
}
nextId := typeId (len (idToTypeSlice ))
typ .setId (nextId )
idToTypeSlice = append (idToTypeSlice , typ )
}
func (t typeId ) gobType () gobType {
if t == 0 {
return nil
}
return idToType (t )
}
func (t typeId ) string () string {
if t .gobType () == nil {
return "<nil>"
}
return t .gobType ().string ()
}
func (t typeId ) name () string {
if t .gobType () == nil {
return "<nil>"
}
return t .gobType ().name ()
}
type CommonType struct {
Name string
Id typeId
}
func (t *CommonType ) id () typeId { return t .Id }
func (t *CommonType ) setId (id typeId ) { t .Id = id }
func (t *CommonType ) string () string { return t .Name }
func (t *CommonType ) safeString (seen map [typeId ]bool ) string {
return t .Name
}
func (t *CommonType ) name () string { return t .Name }
var (
tBool = bootstrapType ("bool" , (*bool )(nil ))
tInt = bootstrapType ("int" , (*int )(nil ))
tUint = bootstrapType ("uint" , (*uint )(nil ))
tFloat = bootstrapType ("float" , (*float64 )(nil ))
tBytes = bootstrapType ("bytes" , (*[]byte )(nil ))
tString = bootstrapType ("string" , (*string )(nil ))
tComplex = bootstrapType ("complex" , (*complex128 )(nil ))
tInterface = bootstrapType ("interface" , (*any )(nil ))
tReserved7 = bootstrapType ("_reserved1" , (*struct { r7 int })(nil ))
tReserved6 = bootstrapType ("_reserved1" , (*struct { r6 int })(nil ))
tReserved5 = bootstrapType ("_reserved1" , (*struct { r5 int })(nil ))
tReserved4 = bootstrapType ("_reserved1" , (*struct { r4 int })(nil ))
tReserved3 = bootstrapType ("_reserved1" , (*struct { r3 int })(nil ))
tReserved2 = bootstrapType ("_reserved1" , (*struct { r2 int })(nil ))
tReserved1 = bootstrapType ("_reserved1" , (*struct { r1 int })(nil ))
)
var tWireType = mustGetTypeInfo (wireTypeType ).id
var wireTypeUserInfo *userTypeInfo
func init() {
checkId (16 , tWireType )
checkId (17 , mustGetTypeInfo (reflect .TypeFor [arrayType ]()).id )
checkId (18 , mustGetTypeInfo (reflect .TypeFor [CommonType ]()).id )
checkId (19 , mustGetTypeInfo (reflect .TypeFor [sliceType ]()).id )
checkId (20 , mustGetTypeInfo (reflect .TypeFor [structType ]()).id )
checkId (21 , mustGetTypeInfo (reflect .TypeFor [fieldType ]()).id )
checkId (23 , mustGetTypeInfo (reflect .TypeFor [mapType ]()).id )
copy (builtinIdToTypeSlice [:], idToTypeSlice )
if nextId := len (idToTypeSlice ); nextId > firstUserId {
panic (fmt .Sprintln ("nextId too large:" , nextId ))
}
idToTypeSlice = idToTypeSlice [:firstUserId ]
registerBasics ()
wireTypeUserInfo = userType (wireTypeType )
}
type arrayType struct {
CommonType
Elem typeId
Len int
}
func newArrayType(name string ) *arrayType {
a := &arrayType {CommonType {Name : name }, 0 , 0 }
return a
}
func (a *arrayType ) init (elem gobType , len int ) {
setTypeId (a )
a .Elem = elem .id ()
a .Len = len
}
func (a *arrayType ) safeString (seen map [typeId ]bool ) string {
if seen [a .Id ] {
return a .Name
}
seen [a .Id ] = true
return fmt .Sprintf ("[%d]%s" , a .Len , a .Elem .gobType ().safeString (seen ))
}
func (a *arrayType ) string () string { return a .safeString (make (map [typeId ]bool )) }
type gobEncoderType struct {
CommonType
}
func newGobEncoderType(name string ) *gobEncoderType {
g := &gobEncoderType {CommonType {Name : name }}
setTypeId (g )
return g
}
func (g *gobEncoderType ) safeString (seen map [typeId ]bool ) string {
return g .Name
}
func (g *gobEncoderType ) string () string { return g .Name }
type mapType struct {
CommonType
Key typeId
Elem typeId
}
func newMapType(name string ) *mapType {
m := &mapType {CommonType {Name : name }, 0 , 0 }
return m
}
func (m *mapType ) init (key , elem gobType ) {
setTypeId (m )
m .Key = key .id ()
m .Elem = elem .id ()
}
func (m *mapType ) safeString (seen map [typeId ]bool ) string {
if seen [m .Id ] {
return m .Name
}
seen [m .Id ] = true
key := m .Key .gobType ().safeString (seen )
elem := m .Elem .gobType ().safeString (seen )
return fmt .Sprintf ("map[%s]%s" , key , elem )
}
func (m *mapType ) string () string { return m .safeString (make (map [typeId ]bool )) }
type sliceType struct {
CommonType
Elem typeId
}
func newSliceType(name string ) *sliceType {
s := &sliceType {CommonType {Name : name }, 0 }
return s
}
func (s *sliceType ) init (elem gobType ) {
setTypeId (s )
if elem .id () == 0 {
setTypeId (elem )
}
s .Elem = elem .id ()
}
func (s *sliceType ) safeString (seen map [typeId ]bool ) string {
if seen [s .Id ] {
return s .Name
}
seen [s .Id ] = true
return fmt .Sprintf ("[]%s" , s .Elem .gobType ().safeString (seen ))
}
func (s *sliceType ) string () string { return s .safeString (make (map [typeId ]bool )) }
type fieldType struct {
Name string
Id typeId
}
type structType struct {
CommonType
Field []fieldType
}
func (s *structType ) safeString (seen map [typeId ]bool ) string {
if s == nil {
return "<nil>"
}
if _ , ok := seen [s .Id ]; ok {
return s .Name
}
seen [s .Id ] = true
str := s .Name + " = struct { "
for _ , f := range s .Field {
str += fmt .Sprintf ("%s %s; " , f .Name , f .Id .gobType ().safeString (seen ))
}
str += "}"
return str
}
func (s *structType ) string () string { return s .safeString (make (map [typeId ]bool )) }
func newStructType(name string ) *structType {
s := &structType {CommonType {Name : name }, nil }
setTypeId (s )
return s
}
func newTypeObject(name string , ut *userTypeInfo , rt reflect .Type ) (gobType , error ) {
if ut .externalEnc != 0 {
return newGobEncoderType (name ), nil
}
var err error
var type0 , type1 gobType
defer func () {
if err != nil {
delete (types , rt )
}
}()
switch t := rt ; t .Kind () {
case reflect .Bool :
return tBool .gobType (), nil
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
return tInt .gobType (), nil
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
return tUint .gobType (), nil
case reflect .Float32 , reflect .Float64 :
return tFloat .gobType (), nil
case reflect .Complex64 , reflect .Complex128 :
return tComplex .gobType (), nil
case reflect .String :
return tString .gobType (), nil
case reflect .Interface :
return tInterface .gobType (), nil
case reflect .Array :
at := newArrayType (name )
types [rt ] = at
type0 , err = getBaseType ("" , t .Elem ())
if err != nil {
return nil , err
}
at .init (type0 , t .Len ())
return at , nil
case reflect .Map :
mt := newMapType (name )
types [rt ] = mt
type0 , err = getBaseType ("" , t .Key ())
if err != nil {
return nil , err
}
type1 , err = getBaseType ("" , t .Elem ())
if err != nil {
return nil , err
}
mt .init (type0 , type1 )
return mt , nil
case reflect .Slice :
if t .Elem ().Kind () == reflect .Uint8 {
return tBytes .gobType (), nil
}
st := newSliceType (name )
types [rt ] = st
type0 , err = getBaseType (t .Elem ().Name (), t .Elem ())
if err != nil {
return nil , err
}
st .init (type0 )
return st , nil
case reflect .Struct :
st := newStructType (name )
types [rt ] = st
idToTypeSlice [st .id ()] = st
for i := 0 ; i < t .NumField (); i ++ {
f := t .Field (i )
if !isSent (&f ) {
continue
}
typ := userType (f .Type ).base
tname := typ .Name ()
if tname == "" {
t := userType (f .Type ).base
tname = t .String ()
}
gt , err := getBaseType (tname , f .Type )
if err != nil {
return nil , err
}
if gt .id () == 0 {
setTypeId (gt )
}
st .Field = append (st .Field , fieldType {f .Name , gt .id ()})
}
return st , nil
default :
return nil , errors .New ("gob NewTypeObject can't handle type: " + rt .String ())
}
}
func isExported(name string ) bool {
rune , _ := utf8 .DecodeRuneInString (name )
return unicode .IsUpper (rune )
}
func isSent(field *reflect .StructField ) bool {
if !isExported (field .Name ) {
return false
}
typ := field .Type
for typ .Kind () == reflect .Pointer {
typ = typ .Elem ()
}
if typ .Kind () == reflect .Chan || typ .Kind () == reflect .Func {
return false
}
return true
}
func getBaseType(name string , rt reflect .Type ) (gobType , error ) {
ut := userType (rt )
return getType (name , ut , ut .base )
}
func getType(name string , ut *userTypeInfo , rt reflect .Type ) (gobType , error ) {
typ , present := types [rt ]
if present {
return typ , nil
}
typ , err := newTypeObject (name , ut , rt )
if err == nil {
types [rt ] = typ
}
return typ , err
}
func checkId(want , got typeId ) {
if want != got {
fmt .Fprintf (os .Stderr , "checkId: %d should be %d\n" , int (got ), int (want ))
panic ("bootstrap type wrong id: " + got .name () + " " + got .string () + " not " + want .string ())
}
}
func bootstrapType(name string , e any ) typeId {
rt := reflect .TypeOf (e ).Elem ()
_ , present := types [rt ]
if present {
panic ("bootstrap type already present: " + name + ", " + rt .String ())
}
typ := &CommonType {Name : name }
types [rt ] = typ
setTypeId (typ )
return typ .id ()
}
type wireType struct {
ArrayT *arrayType
SliceT *sliceType
StructT *structType
MapT *mapType
GobEncoderT *gobEncoderType
BinaryMarshalerT *gobEncoderType
TextMarshalerT *gobEncoderType
}
func (w *wireType ) string () string {
const unknown = "unknown type"
if w == nil {
return unknown
}
switch {
case w .ArrayT != nil :
return w .ArrayT .Name
case w .SliceT != nil :
return w .SliceT .Name
case w .StructT != nil :
return w .StructT .Name
case w .MapT != nil :
return w .MapT .Name
case w .GobEncoderT != nil :
return w .GobEncoderT .Name
case w .BinaryMarshalerT != nil :
return w .BinaryMarshalerT .Name
case w .TextMarshalerT != nil :
return w .TextMarshalerT .Name
}
return unknown
}
type typeInfo struct {
id typeId
encInit sync .Mutex
encoder atomic .Pointer [encEngine ]
wire wireType
}
var typeInfoMap atomic .Value
var typeInfoMapInit = make (map [reflect .Type ]*typeInfo , 16 )
func lookupTypeInfo(rt reflect .Type ) *typeInfo {
if m := typeInfoMapInit ; m != nil {
return m [rt ]
}
m , _ := typeInfoMap .Load ().(map [reflect .Type ]*typeInfo )
return m [rt ]
}
func getTypeInfo(ut *userTypeInfo ) (*typeInfo , error ) {
rt := ut .base
if ut .externalEnc != 0 {
rt = ut .user
}
if info := lookupTypeInfo (rt ); info != nil {
return info , nil
}
return buildTypeInfo (ut , rt )
}
func buildTypeInfo(ut *userTypeInfo , rt reflect .Type ) (*typeInfo , error ) {
typeLock .Lock ()
defer typeLock .Unlock ()
if info := lookupTypeInfo (rt ); info != nil {
return info , nil
}
gt , err := getBaseType (rt .Name (), rt )
if err != nil {
return nil , err
}
info := &typeInfo {id : gt .id ()}
if ut .externalEnc != 0 {
userType , err := getType (rt .Name (), ut , rt )
if err != nil {
return nil , err
}
gt := userType .id ().gobType ().(*gobEncoderType )
switch ut .externalEnc {
case xGob :
info .wire .GobEncoderT = gt
case xBinary :
info .wire .BinaryMarshalerT = gt
case xText :
info .wire .TextMarshalerT = gt
}
rt = ut .user
} else {
t := info .id .gobType ()
switch typ := rt ; typ .Kind () {
case reflect .Array :
info .wire .ArrayT = t .(*arrayType )
case reflect .Map :
info .wire .MapT = t .(*mapType )
case reflect .Slice :
if typ .Elem ().Kind () != reflect .Uint8 {
info .wire .SliceT = t .(*sliceType )
}
case reflect .Struct :
info .wire .StructT = t .(*structType )
}
}
if m := typeInfoMapInit ; m != nil {
m [rt ] = info
return info , nil
}
m , _ := typeInfoMap .Load ().(map [reflect .Type ]*typeInfo )
newm := make (map [reflect .Type ]*typeInfo , len (m ))
for k , v := range m {
newm [k ] = v
}
newm [rt ] = info
typeInfoMap .Store (newm )
return info , nil
}
func mustGetTypeInfo(rt reflect .Type ) *typeInfo {
t , err := getTypeInfo (userType (rt ))
if err != nil {
panic ("getTypeInfo: " + err .Error())
}
return t
}
type GobEncoder interface {
GobEncode () ([]byte , error )
}
type GobDecoder interface {
GobDecode ([]byte ) error
}
var (
nameToConcreteType sync .Map
concreteTypeToName sync .Map
)
func RegisterName (name string , value any ) {
if name == "" {
panic ("attempt to register empty name" )
}
ut := userType (reflect .TypeOf (value ))
if t , dup := nameToConcreteType .LoadOrStore (name , reflect .TypeOf (value )); dup && t != ut .user {
panic (fmt .Sprintf ("gob: registering duplicate types for %q: %s != %s" , name , t , ut .user ))
}
if n , dup := concreteTypeToName .LoadOrStore (ut .base , name ); dup && n != name {
nameToConcreteType .Delete (name )
panic (fmt .Sprintf ("gob: registering duplicate names for %s: %q != %q" , ut .user , n , name ))
}
}
func Register (value any ) {
rt := reflect .TypeOf (value )
name := rt .String ()
star := ""
if rt .Name () == "" {
if pt := rt ; pt .Kind () == reflect .Pointer {
star = "*"
rt = pt
}
}
if rt .Name () != "" {
if rt .PkgPath () == "" {
name = star + rt .Name ()
} else {
name = star + rt .PkgPath () + "." + rt .Name ()
}
}
RegisterName (name , value )
}
func registerBasics() {
Register (int (0 ))
Register (int8 (0 ))
Register (int16 (0 ))
Register (int32 (0 ))
Register (int64 (0 ))
Register (uint (0 ))
Register (uint8 (0 ))
Register (uint16 (0 ))
Register (uint32 (0 ))
Register (uint64 (0 ))
Register (float32 (0 ))
Register (float64 (0 ))
Register (complex64 (0i ))
Register (complex128 (0i ))
Register (uintptr (0 ))
Register (false )
Register ("" )
Register ([]byte (nil ))
Register ([]int (nil ))
Register ([]int8 (nil ))
Register ([]int16 (nil ))
Register ([]int32 (nil ))
Register ([]int64 (nil ))
Register ([]uint (nil ))
Register ([]uint8 (nil ))
Register ([]uint16 (nil ))
Register ([]uint32 (nil ))
Register ([]uint64 (nil ))
Register ([]float32 (nil ))
Register ([]float64 (nil ))
Register ([]complex64 (nil ))
Register ([]complex128 (nil ))
Register ([]uintptr (nil ))
Register ([]bool (nil ))
Register ([]string (nil ))
}
func init() {
typeInfoMap .Store (typeInfoMapInit )
typeInfoMapInit = nil
}
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 .