github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/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 "reflect" 21 "sync" 22 ) 23 24 var ( 25 typeCacheMutex sync.RWMutex 26 typeCache = make(map[typekey]*typeinfo) 27 ) 28 29 type typeinfo struct { 30 decoder 31 writer 32 } 33 34 // represents struct tags 35 type tags struct { 36 nilOK bool 37 } 38 39 type typekey struct { 40 reflect.Type 41 // the key must include the struct tags because they 42 // might generate a different decoder. 43 tags 44 } 45 46 type decoder func(*Stream, reflect.Value) error 47 48 type writer func(reflect.Value, *encbuf) error 49 50 func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) { 51 typeCacheMutex.RLock() 52 info := typeCache[typekey{typ, tags}] 53 typeCacheMutex.RUnlock() 54 if info != nil { 55 return info, nil 56 } 57 // not in the cache, need to generate info for this type. 58 typeCacheMutex.Lock() 59 defer typeCacheMutex.Unlock() 60 return cachedTypeInfo1(typ, tags) 61 } 62 63 func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) { 64 key := typekey{typ, tags} 65 info := typeCache[key] 66 if info != nil { 67 // another goroutine got the write lock first 68 return info, nil 69 } 70 // put a dummmy value into the cache before generating. 71 // if the generator tries to lookup itself, it will get 72 // the dummy value and won't call itself recursively. 73 typeCache[key] = new(typeinfo) 74 info, err := genTypeInfo(typ, tags) 75 if err != nil { 76 // remove the dummy value if the generator fails 77 delete(typeCache, key) 78 return nil, err 79 } 80 *typeCache[key] = *info 81 return typeCache[key], err 82 } 83 84 type field struct { 85 index int 86 info *typeinfo 87 } 88 89 func structFields(typ reflect.Type) (fields []field, err error) { 90 for i := 0; i < typ.NumField(); i++ { 91 if f := typ.Field(i); f.PkgPath == "" { // exported 92 tags := parseStructTag(f.Tag.Get("rlp")) 93 info, err := cachedTypeInfo1(f.Type, tags) 94 if err != nil { 95 return nil, err 96 } 97 fields = append(fields, field{i, info}) 98 } 99 } 100 return fields, nil 101 } 102 103 func parseStructTag(tag string) tags { 104 return tags{nilOK: tag == "nil"} 105 } 106 107 func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) { 108 info = new(typeinfo) 109 if info.decoder, err = makeDecoder(typ, tags); err != nil { 110 return nil, err 111 } 112 if info.writer, err = makeWriter(typ); err != nil { 113 return nil, err 114 } 115 return info, nil 116 } 117 118 func isUint(k reflect.Kind) bool { 119 return k >= reflect.Uint && k <= reflect.Uintptr 120 }