github.com/teleloco/go-ethereum@v1.9.7/rlp/typecache.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rlp 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 "sync" 24 ) 25 26 var ( 27 typeCacheMutex sync.RWMutex 28 typeCache = make(map[typekey]*typeinfo) 29 ) 30 31 type typeinfo struct { 32 decoder decoder 33 decoderErr error // error from makeDecoder 34 writer writer 35 writerErr error // error from makeWriter 36 } 37 38 // tags represents struct tags. 39 type tags struct { 40 // rlp:"nil" controls whether empty input results in a nil pointer. 41 nilOK bool 42 43 // This controls whether nil pointers are encoded/decoded as empty strings 44 // or empty lists. 45 nilKind Kind 46 47 // rlp:"tail" controls whether this field swallows additional list 48 // elements. It can only be set for the last field, which must be 49 // of slice type. 50 tail bool 51 52 // rlp:"-" ignores fields. 53 ignored bool 54 } 55 56 // typekey is the key of a type in typeCache. It includes the struct tags because 57 // they might generate a different decoder. 58 type typekey struct { 59 reflect.Type 60 tags 61 } 62 63 type decoder func(*Stream, reflect.Value) error 64 65 type writer func(reflect.Value, *encbuf) error 66 67 func cachedDecoder(typ reflect.Type) (decoder, error) { 68 info := cachedTypeInfo(typ, tags{}) 69 return info.decoder, info.decoderErr 70 } 71 72 func cachedWriter(typ reflect.Type) (writer, error) { 73 info := cachedTypeInfo(typ, tags{}) 74 return info.writer, info.writerErr 75 } 76 77 func cachedTypeInfo(typ reflect.Type, tags tags) *typeinfo { 78 typeCacheMutex.RLock() 79 info := typeCache[typekey{typ, tags}] 80 typeCacheMutex.RUnlock() 81 if info != nil { 82 return info 83 } 84 // not in the cache, need to generate info for this type. 85 typeCacheMutex.Lock() 86 defer typeCacheMutex.Unlock() 87 return cachedTypeInfo1(typ, tags) 88 } 89 90 func cachedTypeInfo1(typ reflect.Type, tags tags) *typeinfo { 91 key := typekey{typ, tags} 92 info := typeCache[key] 93 if info != nil { 94 // another goroutine got the write lock first 95 return info 96 } 97 // put a dummy value into the cache before generating. 98 // if the generator tries to lookup itself, it will get 99 // the dummy value and won't call itself recursively. 100 info = new(typeinfo) 101 typeCache[key] = info 102 info.generate(typ, tags) 103 return info 104 } 105 106 type field struct { 107 index int 108 info *typeinfo 109 } 110 111 func structFields(typ reflect.Type) (fields []field, err error) { 112 lastPublic := lastPublicField(typ) 113 for i := 0; i < typ.NumField(); i++ { 114 if f := typ.Field(i); f.PkgPath == "" { // exported 115 tags, err := parseStructTag(typ, i, lastPublic) 116 if err != nil { 117 return nil, err 118 } 119 if tags.ignored { 120 continue 121 } 122 info := cachedTypeInfo1(f.Type, tags) 123 fields = append(fields, field{i, info}) 124 } 125 } 126 return fields, nil 127 } 128 129 type structFieldError struct { 130 typ reflect.Type 131 field int 132 err error 133 } 134 135 func (e structFieldError) Error() string { 136 return fmt.Sprintf("%v (struct field %v.%s)", e.err, e.typ, e.typ.Field(e.field).Name) 137 } 138 139 type structTagError struct { 140 typ reflect.Type 141 field, tag, err string 142 } 143 144 func (e structTagError) Error() string { 145 return fmt.Sprintf("rlp: invalid struct tag %q for %v.%s (%s)", e.tag, e.typ, e.field, e.err) 146 } 147 148 func parseStructTag(typ reflect.Type, fi, lastPublic int) (tags, error) { 149 f := typ.Field(fi) 150 var ts tags 151 for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { 152 switch t = strings.TrimSpace(t); t { 153 case "": 154 case "-": 155 ts.ignored = true 156 case "nil", "nilString", "nilList": 157 ts.nilOK = true 158 if f.Type.Kind() != reflect.Ptr { 159 return ts, structTagError{typ, f.Name, t, "field is not a pointer"} 160 } 161 switch t { 162 case "nil": 163 ts.nilKind = defaultNilKind(f.Type.Elem()) 164 case "nilString": 165 ts.nilKind = String 166 case "nilList": 167 ts.nilKind = List 168 } 169 case "tail": 170 ts.tail = true 171 if fi != lastPublic { 172 return ts, structTagError{typ, f.Name, t, "must be on last field"} 173 } 174 if f.Type.Kind() != reflect.Slice { 175 return ts, structTagError{typ, f.Name, t, "field type is not slice"} 176 } 177 default: 178 return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name) 179 } 180 } 181 return ts, nil 182 } 183 184 func lastPublicField(typ reflect.Type) int { 185 last := 0 186 for i := 0; i < typ.NumField(); i++ { 187 if typ.Field(i).PkgPath == "" { 188 last = i 189 } 190 } 191 return last 192 } 193 194 func (i *typeinfo) generate(typ reflect.Type, tags tags) { 195 i.decoder, i.decoderErr = makeDecoder(typ, tags) 196 i.writer, i.writerErr = makeWriter(typ, tags) 197 } 198 199 // defaultNilKind determines whether a nil pointer to typ encodes/decodes 200 // as an empty string or empty list. 201 func defaultNilKind(typ reflect.Type) Kind { 202 k := typ.Kind() 203 if isUint(k) || k == reflect.String || k == reflect.Bool || isByteArray(typ) { 204 return String 205 } 206 return List 207 } 208 209 func isUint(k reflect.Kind) bool { 210 return k >= reflect.Uint && k <= reflect.Uintptr 211 } 212 213 func isByteArray(typ reflect.Type) bool { 214 return (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array) && isByte(typ.Elem()) 215 }