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