break
}
var elemId typeId
- if tt, ok := builtinIdToType[wireId]; ok {
+ if tt := builtinIdToType(wireId); tt != nil {
elemId = tt.(*sliceType).Elem
} else {
elemId = dec.wireType[wireId].SliceT.Elem
}
// Extract and compare element types.
var sw *sliceType
- if tt, ok := builtinIdToType[fw]; ok {
+ if tt := builtinIdToType(fw); tt != nil {
sw, _ = tt.(*sliceType)
} else if wire != nil {
sw = wire.SliceT
var wireStruct *structType
// Builtin types can come from global pool; the rest must be defined by the decoder.
// Also we know we're decoding a struct now, so the client must have sent one.
- if t, ok := builtinIdToType[remoteId]; ok {
+ if t := builtinIdToType(remoteId); t != nil {
wireStruct, _ = t.(*structType)
} else {
wire := dec.wireType[remoteId]
// emptyStruct is the type we compile into when ignoring a struct value.
type emptyStruct struct{}
-var emptyStructType = reflect.TypeOf(emptyStruct{})
+var emptyStructType = reflect.TypeOf((*emptyStruct)(nil)).Elem()
// getIgnoreEnginePtr returns the engine for the specified type when the value is to be discarded.
func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err error) {
// Internally, typeIds are used as keys to a map to recover the underlying type info.
type typeId int32
-var nextId typeId // incremented for each new type we build
var typeLock sync.Mutex // set while building a type
const firstUserId = 64 // lowest id number granted to user
safeString(seen map[typeId]bool) string
}
-var types = make(map[reflect.Type]gobType)
-var idToType = make(map[typeId]gobType)
-var builtinIdToType map[typeId]gobType // set in init() after builtins are established
+var types = make(map[reflect.Type]gobType, 32)
+var idToType = make([]gobType, 1, firstUserId)
+var builtinIdToTypeSlice [firstUserId]gobType // set in init() after builtins are established
+
+func builtinIdToType(id typeId) gobType {
+ if id < 0 || int(id) >= len(builtinIdToTypeSlice) {
+ return nil
+ }
+ return builtinIdToTypeSlice[id]
+}
func setTypeId(typ gobType) {
// When building recursive types, someone may get there before us.
if typ.id() != 0 {
return
}
- nextId++
+ nextId := typeId(len(idToType))
typ.setId(nextId)
- idToType[nextId] = typ
+ idToType = append(idToType, typ)
}
func (t typeId) gobType() gobType {
)
// Predefined because it's needed by the Decoder
-var tWireType = mustGetTypeInfo(reflect.TypeOf(wireType{})).id
+var tWireType = mustGetTypeInfo(reflect.TypeOf((*wireType)(nil)).Elem()).id
var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType)
func init() {
// Some magic numbers to make sure there are no surprises.
checkId(16, tWireType)
- checkId(17, mustGetTypeInfo(reflect.TypeOf(arrayType{})).id)
- checkId(18, mustGetTypeInfo(reflect.TypeOf(CommonType{})).id)
- checkId(19, mustGetTypeInfo(reflect.TypeOf(sliceType{})).id)
- checkId(20, mustGetTypeInfo(reflect.TypeOf(structType{})).id)
- checkId(21, mustGetTypeInfo(reflect.TypeOf(fieldType{})).id)
- checkId(23, mustGetTypeInfo(reflect.TypeOf(mapType{})).id)
-
- builtinIdToType = make(map[typeId]gobType)
+ checkId(17, mustGetTypeInfo(reflect.TypeOf((*arrayType)(nil)).Elem()).id)
+ checkId(18, mustGetTypeInfo(reflect.TypeOf((*CommonType)(nil)).Elem()).id)
+ checkId(19, mustGetTypeInfo(reflect.TypeOf((*sliceType)(nil)).Elem()).id)
+ checkId(20, mustGetTypeInfo(reflect.TypeOf((*structType)(nil)).Elem()).id)
+ checkId(21, mustGetTypeInfo(reflect.TypeOf((*fieldType)(nil)).Elem()).id)
+ checkId(23, mustGetTypeInfo(reflect.TypeOf((*mapType)(nil)).Elem()).id)
+
for k, v := range idToType {
- builtinIdToType[k] = v
+ builtinIdToTypeSlice[k] = v
}
// Move the id space upwards to allow for growth in the predefined world
// without breaking existing files.
- if nextId > firstUserId {
+ if nextId := len(idToType); nextId > firstUserId {
panic(fmt.Sprintln("nextId too large:", nextId))
}
- nextId = firstUserId
+ idToType = idToType[:firstUserId]
registerBasics()
wireTypeUserInfo = userType(reflect.TypeOf((*wireType)(nil)))
}
typ := &CommonType{Name: name}
types[rt] = typ
setTypeId(typ)
- checkId(expect, nextId)
userType(rt) // might as well cache it now
- return nextId
+ return typ.id()
}
// Representation of the information we send and receive about this type.
// protected by a mutex.
var typeInfoMap atomic.Value
+// typeInfoMapInit is used instead of typeInfoMap during init time,
+// as types are registered sequentially during init and we can save
+// the overhead of making map copies.
+// It is saved to typeInfoMap and set to nil before init finishes.
+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]
}
}
}
+ if m := typeInfoMapInit; m != nil {
+ m[rt] = info
+ return info, nil
+ }
+
// Create new map with old contents plus new entry.
- newm := make(map[reflect.Type]*typeInfo)
m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo)
+ newm := make(map[reflect.Type]*typeInfo, len(m))
for k, v := range m {
newm[k] = v
}
Register([]bool(nil))
Register([]string(nil))
}
+
+func init() {
+ typeInfoMap.Store(typeInfoMapInit)
+ typeInfoMapInit = nil
+}