package rlp import ( "reflect" "sync" ) var ( typeCacheMutex sync.RWMutex typeCache = make(map[typekey]*typeinfo) ) type typeinfo struct { decoder writer } // represents struct tags type tags struct { nilOK bool } type typekey struct { reflect.Type // the key must include the struct tags because they // might generate a different decoder. tags } type decoder func(*Stream, reflect.Value) error type writer func(reflect.Value, *encbuf) error func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) { typeCacheMutex.RLock() info := typeCache[typekey{typ, tags}] typeCacheMutex.RUnlock() if info != nil { return info, nil } // not in the cache, need to generate info for this type. typeCacheMutex.Lock() defer typeCacheMutex.Unlock() return cachedTypeInfo1(typ, tags) } func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) { key := typekey{typ, tags} info := typeCache[key] if info != nil { // another goroutine got the write lock first return info, nil } // put a dummmy value into the cache before generating. // if the generator tries to lookup itself, it will get // the dummy value and won't call itself recursively. typeCache[key] = new(typeinfo) info, err := genTypeInfo(typ, tags) if err != nil { // remove the dummy value if the generator fails delete(typeCache, key) return nil, err } *typeCache[key] = *info return typeCache[key], err } type field struct { index int info *typeinfo } func structFields(typ reflect.Type) (fields []field, err error) { for i := 0; i < typ.NumField(); i++ { if f := typ.Field(i); f.PkgPath == "" { // exported tags := parseStructTag(f.Tag.Get("rlp")) info, err := cachedTypeInfo1(f.Type, tags) if err != nil { return nil, err } fields = append(fields, field{i, info}) } } return fields, nil } func parseStructTag(tag string) tags { return tags{nilOK: tag == "nil"} } func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) { info = new(typeinfo) if info.decoder, err = makeDecoder(typ, tags); err != nil { return nil, err } if info.writer, err = makeWriter(typ); err != nil { return nil, err } return info, nil } func isUint(k reflect.Kind) bool { return k >= reflect.Uint && k <= reflect.Uintptr }