Fix unit tests
[govpp.git] / vendor / github.com / lunixbochs / struc / fields.go
1 package struc
2
3 import (
4         "encoding/binary"
5         "fmt"
6         "io"
7         "reflect"
8         "strings"
9 )
10
11 type Fields []*Field
12
13 func (f Fields) SetByteOrder(order binary.ByteOrder) {
14         for _, field := range f {
15                 if field != nil {
16                         field.Order = order
17                 }
18         }
19 }
20
21 func (f Fields) String() string {
22         fields := make([]string, len(f))
23         for i, field := range f {
24                 if field != nil {
25                         fields[i] = field.String()
26                 }
27         }
28         return "{" + strings.Join(fields, ", ") + "}"
29 }
30
31 func (f Fields) Sizeof(val reflect.Value, options *Options) int {
32         for val.Kind() == reflect.Ptr {
33                 val = val.Elem()
34         }
35         size := 0
36         for i, field := range f {
37                 if field != nil {
38                         size += field.Size(val.Field(i), options)
39                 }
40         }
41         return size
42 }
43
44 func (f Fields) sizefrom(val reflect.Value, index []int) int {
45         field := val.FieldByIndex(index)
46         switch field.Kind() {
47         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
48                 return int(field.Int())
49         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
50                 n := int(field.Uint())
51                 // all the builtin array length types are native int
52                 // so this guards against weird truncation
53                 if n < 0 {
54                         return 0
55                 }
56                 return n
57         default:
58                 name := val.Type().FieldByIndex(index).Name
59                 panic(fmt.Sprintf("sizeof field %T.%s not an integer type", val.Interface(), name))
60         }
61 }
62
63 func (f Fields) Pack(buf []byte, val reflect.Value, options *Options) (int, error) {
64         for val.Kind() == reflect.Ptr {
65                 val = val.Elem()
66         }
67         pos := 0
68         for i, field := range f {
69                 if field == nil {
70                         continue
71                 }
72                 v := val.Field(i)
73                 length := field.Len
74                 if field.Sizefrom != nil {
75                         length = f.sizefrom(val, field.Sizefrom)
76                 }
77                 if length <= 0 && field.Slice {
78                         length = v.Len()
79                 }
80                 if field.Sizeof != nil {
81                         length := val.FieldByIndex(field.Sizeof).Len()
82                         switch field.kind {
83                         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
84                                 // allocating a new int here has fewer side effects (doesn't update the original struct)
85                                 // but it's a wasteful allocation
86                                 // the old method might work if we just cast the temporary int/uint to the target type
87                                 v = reflect.New(v.Type()).Elem()
88                                 v.SetInt(int64(length))
89                         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
90                                 v = reflect.New(v.Type()).Elem()
91                                 v.SetUint(uint64(length))
92                         default:
93                                 panic(fmt.Sprintf("sizeof field is not int or uint type: %s, %s", field.Name, v.Type()))
94                         }
95                 }
96                 if n, err := field.Pack(buf[pos:], v, length, options); err != nil {
97                         return n, err
98                 } else {
99                         pos += n
100                 }
101         }
102         return pos, nil
103 }
104
105 func (f Fields) Unpack(r io.Reader, val reflect.Value, options *Options) error {
106         for val.Kind() == reflect.Ptr {
107                 val = val.Elem()
108         }
109         var tmp [8]byte
110         var buf []byte
111         for i, field := range f {
112                 if field == nil {
113                         continue
114                 }
115                 v := val.Field(i)
116                 length := field.Len
117                 if field.Sizefrom != nil {
118                         length = f.sizefrom(val, field.Sizefrom)
119                 }
120                 if v.Kind() == reflect.Ptr && !v.Elem().IsValid() {
121                         v.Set(reflect.New(v.Type().Elem()))
122                 }
123                 if field.Type == Struct {
124                         if field.Slice {
125                                 vals := reflect.MakeSlice(v.Type(), length, length)
126                                 for i := 0; i < length; i++ {
127                                         v := vals.Index(i)
128                                         fields, err := parseFields(v)
129                                         if err != nil {
130                                                 return err
131                                         }
132                                         if err := fields.Unpack(r, v, options); err != nil {
133                                                 return err
134                                         }
135                                 }
136                                 v.Set(vals)
137                         } else {
138                                 // TODO: DRY (we repeat the inner loop above)
139                                 fields, err := parseFields(v)
140                                 if err != nil {
141                                         return err
142                                 }
143                                 if err := fields.Unpack(r, v, options); err != nil {
144                                         return err
145                                 }
146                         }
147                         continue
148                 } else {
149                         typ := field.Type.Resolve(options)
150                         if typ == CustomType {
151                                 if err := v.Addr().Interface().(Custom).Unpack(r, length, options); err != nil {
152                                         return err
153                                 }
154                         } else {
155                                 size := length * field.Type.Resolve(options).Size()
156                                 if size < 8 {
157                                         buf = tmp[:size]
158                                 } else {
159                                         buf = make([]byte, size)
160                                 }
161                                 if _, err := io.ReadFull(r, buf); err != nil {
162                                         return err
163                                 }
164                                 err := field.Unpack(buf[:size], v, length, options)
165                                 if err != nil {
166                                         return err
167                                 }
168                         }
169                 }
170         }
171         return nil
172 }