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