| |
| |
| |
|
|
| package gob |
|
|
| import ( |
| "encoding" |
| "errors" |
| "fmt" |
| "maps" |
| "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 := maps.Clone(m) |
| 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 |
| } |
|
|