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