43c5875f50e4534374d4925001ead59c57e91227
[govpp.git] / vendor / github.com / lunixbochs / struc / parse.go
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("struc: Could not find field 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 }