gitee.com/quant1x/gox@v1.21.2/encoding/binary/struc/parse.go (about) 1 package struc 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "reflect" 8 "regexp" 9 "strconv" 10 "strings" 11 "sync" 12 ) 13 14 // struc:"int32,big,sizeof=Data,skip,sizefrom=Len" 15 16 type strucTag struct { 17 Type string 18 Order binary.ByteOrder 19 Sizeof string 20 Skip bool 21 Sizefrom string 22 } 23 24 func parseStrucTag(tag reflect.StructTag) *strucTag { 25 t := &strucTag{ 26 Order: binary.BigEndian, 27 } 28 tagStr := tag.Get("struc") 29 if tagStr == "" { 30 // someone's going to typo this (I already did once) 31 // sorry if you made a module actually using this tag 32 // and you're mad at me now 33 tagStr = tag.Get("struct") 34 } 35 for _, s := range strings.Split(tagStr, ",") { 36 if strings.HasPrefix(s, "sizeof=") { 37 tmp := strings.SplitN(s, "=", 2) 38 t.Sizeof = tmp[1] 39 } else if strings.HasPrefix(s, "sizefrom=") { 40 tmp := strings.SplitN(s, "=", 2) 41 t.Sizefrom = tmp[1] 42 } else if s == "big" { 43 t.Order = binary.BigEndian 44 } else if s == "little" { 45 t.Order = binary.LittleEndian 46 } else if s == "skip" { 47 t.Skip = true 48 } else { 49 t.Type = s 50 } 51 } 52 return t 53 } 54 55 var typeLenRe = regexp.MustCompile(`^\[(\d*)\]`) 56 57 func parseField(f reflect.StructField) (fd *Field, tag *strucTag, err error) { 58 tag = parseStrucTag(f.Tag) 59 var ok bool 60 fd = &Field{ 61 Name: f.Name, 62 Len: 1, 63 Order: tag.Order, 64 Slice: false, 65 kind: f.Type.Kind(), 66 } 67 switch fd.kind { 68 case reflect.Array: 69 fd.Slice = true 70 fd.Array = true 71 fd.Len = f.Type.Len() 72 fd.kind = f.Type.Elem().Kind() 73 case reflect.Slice: 74 fd.Slice = true 75 fd.Len = -1 76 fd.kind = f.Type.Elem().Kind() 77 case reflect.Ptr: 78 fd.Ptr = true 79 fd.kind = f.Type.Elem().Kind() 80 } 81 // check for custom types 82 tmp := reflect.New(f.Type) 83 if _, ok := tmp.Interface().(Custom); ok { 84 fd.Type = CustomType 85 return 86 } 87 var defTypeOk bool 88 fd.defType, defTypeOk = reflectTypeMap[fd.kind] 89 // find a type in the struct tag 90 pureType := typeLenRe.ReplaceAllLiteralString(tag.Type, "") 91 if fd.Type, ok = typeLookup[pureType]; ok { 92 fd.Len = 1 93 match := typeLenRe.FindAllStringSubmatch(tag.Type, -1) 94 if len(match) > 0 && len(match[0]) > 1 { 95 fd.Slice = true 96 first := match[0][1] 97 // Field.Len = -1 indicates a []slice 98 if first == "" { 99 fd.Len = -1 100 } else { 101 fd.Len, err = strconv.Atoi(first) 102 } 103 } 104 return 105 } 106 // the user didn't specify a type 107 switch f.Type { 108 case reflect.TypeOf(Size_t(0)): 109 fd.Type = SizeType 110 case reflect.TypeOf(Off_t(0)): 111 fd.Type = OffType 112 default: 113 if defTypeOk { 114 fd.Type = fd.defType 115 } else { 116 err = errors.New(fmt.Sprintf("struc: Could not resolve field '%v' type '%v'.", f.Name, f.Type)) 117 } 118 } 119 return 120 } 121 122 func parseFieldsLocked(v reflect.Value) (Fields, error) { 123 // we need to repeat this logic because parseFields() below can't be recursively called due to locking 124 for v.Kind() == reflect.Ptr { 125 v = v.Elem() 126 } 127 t := v.Type() 128 if v.NumField() < 1 { 129 return nil, errors.New("struc: Struct has no fields.") 130 } 131 sizeofMap := make(map[string][]int) 132 fields := make(Fields, v.NumField()) 133 for i := 0; i < t.NumField(); i++ { 134 field := t.Field(i) 135 f, tag, err := parseField(field) 136 if tag.Skip { 137 continue 138 } 139 if err != nil { 140 return nil, err 141 } 142 if !v.Field(i).CanSet() { 143 continue 144 } 145 f.Index = i 146 if tag.Sizeof != "" { 147 target, ok := t.FieldByName(tag.Sizeof) 148 if !ok { 149 return nil, fmt.Errorf("struc: `sizeof=%s` field does not exist", tag.Sizeof) 150 } 151 f.Sizeof = target.Index 152 sizeofMap[tag.Sizeof] = field.Index 153 } 154 if sizefrom, ok := sizeofMap[field.Name]; ok { 155 f.Sizefrom = sizefrom 156 } 157 if tag.Sizefrom != "" { 158 source, ok := t.FieldByName(tag.Sizefrom) 159 if !ok { 160 return nil, fmt.Errorf("struc: `sizefrom=%s` field does not exist", tag.Sizefrom) 161 } 162 f.Sizefrom = source.Index 163 } 164 if f.Len == -1 && f.Sizefrom == nil { 165 return nil, fmt.Errorf("struc: field `%s` is a slice with no length or sizeof field", field.Name) 166 } 167 // recurse into nested structs 168 // TODO: handle loops (probably by indirecting the []Field and putting pointer in cache) 169 if f.Type == Struct { 170 typ := field.Type 171 if f.Ptr { 172 typ = typ.Elem() 173 } 174 if f.Slice { 175 typ = typ.Elem() 176 } 177 f.Fields, err = parseFieldsLocked(reflect.New(typ)) 178 if err != nil { 179 return nil, err 180 } 181 } 182 fields[i] = f 183 } 184 return fields, nil 185 } 186 187 var fieldCache = make(map[reflect.Type]Fields) 188 var fieldCacheLock sync.RWMutex 189 var parseLock sync.Mutex 190 191 func fieldCacheLookup(t reflect.Type) Fields { 192 fieldCacheLock.RLock() 193 defer fieldCacheLock.RUnlock() 194 if cached, ok := fieldCache[t]; ok { 195 return cached 196 } 197 return nil 198 } 199 200 func parseFields(v reflect.Value) (Fields, error) { 201 for v.Kind() == reflect.Ptr { 202 v = v.Elem() 203 } 204 t := v.Type() 205 206 // fast path: hopefully the field parsing is already cached 207 if cached := fieldCacheLookup(t); cached != nil { 208 return cached, nil 209 } 210 211 // hold a global lock so multiple goroutines can't parse (the same) fields at once 212 parseLock.Lock() 213 defer parseLock.Unlock() 214 215 // check cache a second time, in case parseLock was just released by 216 // another thread who filled the cache for us 217 if cached := fieldCacheLookup(t); cached != nil { 218 return cached, nil 219 } 220 221 // no luck, time to parse and fill the cache ourselves 222 fields, err := parseFieldsLocked(v) 223 if err != nil { 224 return nil, err 225 } 226 fieldCacheLock.Lock() 227 fieldCache[t] = fields 228 fieldCacheLock.Unlock() 229 return fields, nil 230 }