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