github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/rlp/typecache.go (about) 1 package rlp 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "sync" 8 ) 9 10 var ( 11 typeCacheMutex sync.RWMutex 12 typeCache = make(map[typekey]*typeinfo) 13 ) 14 15 type typeinfo struct { 16 decoder 17 writer 18 } 19 20 // represents struct tags 21 type tags struct { 22 // rlp:"nil" controls whether empty input results in a nil pointer. 23 nilOK bool 24 // rlp:"tail" controls whether this field swallows additional list 25 // elements. It can only be set for the last field, which must be 26 // of slice type. 27 tail bool 28 // rlp:"-" ignores fields. 29 ignored bool 30 } 31 32 type typekey struct { 33 reflect.Type 34 // the key must include the struct tags because they 35 // might generate a different decoder. 36 tags 37 } 38 39 type decoder func(*Stream, reflect.Value) error 40 41 type writer func(reflect.Value, *encbuf) error 42 43 func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) { 44 typeCacheMutex.RLock() 45 info := typeCache[typekey{typ, tags}] 46 typeCacheMutex.RUnlock() 47 if info != nil { 48 return info, nil 49 } 50 // not in the cache, need to generate info for this type. 51 typeCacheMutex.Lock() 52 defer typeCacheMutex.Unlock() 53 return cachedTypeInfo1(typ, tags) 54 } 55 56 func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) { 57 key := typekey{typ, tags} 58 info := typeCache[key] 59 if info != nil { 60 // another goroutine got the write lock first 61 return info, nil 62 } 63 // put a dummmy value into the cache before generating. 64 // if the generator tries to lookup itself, it will get 65 // the dummy value and won't call itself recursively. 66 typeCache[key] = new(typeinfo) 67 info, err := genTypeInfo(typ, tags) 68 if err != nil { 69 // remove the dummy value if the generator fails 70 delete(typeCache, key) 71 return nil, err 72 } 73 *typeCache[key] = *info 74 return typeCache[key], err 75 } 76 77 type field struct { 78 index int 79 info *typeinfo 80 } 81 82 func structFields(typ reflect.Type) (fields []field, err error) { 83 for i := 0; i < typ.NumField(); i++ { 84 if f := typ.Field(i); f.PkgPath == "" { // exported 85 tags, err := parseStructTag(typ, i) 86 if err != nil { 87 return nil, err 88 } 89 if tags.ignored { 90 continue 91 } 92 info, err := cachedTypeInfo1(f.Type, tags) 93 if err != nil { 94 return nil, err 95 } 96 fields = append(fields, field{i, info}) 97 } 98 } 99 return fields, nil 100 } 101 102 func parseStructTag(typ reflect.Type, fi int) (tags, error) { 103 f := typ.Field(fi) 104 var ts tags 105 for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { 106 switch t = strings.TrimSpace(t); t { 107 case "": 108 case "-": 109 ts.ignored = true 110 case "nil": 111 ts.nilOK = true 112 case "tail": 113 ts.tail = true 114 if fi != typ.NumField()-1 { 115 return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (must be on last field)`, typ, f.Name) 116 } 117 if f.Type.Kind() != reflect.Slice { 118 return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (field type is not slice)`, typ, f.Name) 119 } 120 default: 121 return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name) 122 } 123 } 124 return ts, nil 125 } 126 127 func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) { 128 info = new(typeinfo) 129 if info.decoder, err = makeDecoder(typ, tags); err != nil { 130 return nil, err 131 } 132 if info.writer, err = makeWriter(typ, tags); err != nil { 133 return nil, err 134 } 135 return info, nil 136 } 137 138 func isUint(k reflect.Kind) bool { 139 return k >= reflect.Uint && k <= reflect.Uintptr 140 }