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