diff options
Diffstat (limited to 'vendor/github.com/lunixbochs/struc/fields.go')
-rw-r--r-- | vendor/github.com/lunixbochs/struc/fields.go | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/vendor/github.com/lunixbochs/struc/fields.go b/vendor/github.com/lunixbochs/struc/fields.go new file mode 100644 index 0000000..80e2045 --- /dev/null +++ b/vendor/github.com/lunixbochs/struc/fields.go @@ -0,0 +1,169 @@ +package struc + +import ( + "encoding/binary" + "fmt" + "io" + "reflect" + "strings" +) + +type Fields []*Field + +func (f Fields) SetByteOrder(order binary.ByteOrder) { + for _, field := range f { + field.Order = order + } +} + +func (f Fields) String() string { + fields := make([]string, len(f)) + for i, field := range f { + fields[i] = field.String() + } + return "{" + strings.Join(fields, ", ") + "}" +} + +func (f Fields) Sizeof(val reflect.Value, options *Options) int { + for val.Kind() == reflect.Ptr { + val = val.Elem() + } + size := 0 + for i, field := range f { + v := val.Field(i) + if v.CanSet() { + size += field.Size(v, options) + } + } + return size +} + +func (f Fields) sizefrom(val reflect.Value, index []int) int { + field := val.FieldByIndex(index) + switch field.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return int(field.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + n := int(field.Uint()) + // all the builtin array length types are native int + // so this guards against weird truncation + if n < 0 { + return 0 + } + return n + default: + name := val.Type().FieldByIndex(index).Name + panic(fmt.Sprintf("sizeof field %T.%s not an integer type", val.Interface(), name)) + } +} + +func (f Fields) Pack(buf []byte, val reflect.Value, options *Options) (int, error) { + for val.Kind() == reflect.Ptr { + val = val.Elem() + } + pos := 0 + for i, field := range f { + if !field.CanSet { + continue + } + v := val.Field(i) + length := field.Len + if field.Sizefrom != nil { + length = f.sizefrom(val, field.Sizefrom) + } + if length <= 0 && field.Slice { + length = v.Len() + } + if field.Sizeof != nil { + length := val.FieldByIndex(field.Sizeof).Len() + switch field.kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + // allocating a new int here has fewer side effects (doesn't update the original struct) + // but it's a wasteful allocation + // the old method might work if we just cast the temporary int/uint to the target type + v = reflect.New(v.Type()).Elem() + v.SetInt(int64(length)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = reflect.New(v.Type()).Elem() + v.SetUint(uint64(length)) + default: + panic(fmt.Sprintf("sizeof field is not int or uint type: %s, %s", field.Name, v.Type())) + } + } + if n, err := field.Pack(buf[pos:], v, length, options); err != nil { + return n, err + } else { + pos += n + } + } + return pos, nil +} + +func (f Fields) Unpack(r io.Reader, val reflect.Value, options *Options) error { + for val.Kind() == reflect.Ptr { + val = val.Elem() + } + var tmp [8]byte + var buf []byte + for i, field := range f { + if !field.CanSet { + continue + } + v := val.Field(i) + length := field.Len + if field.Sizefrom != nil { + length = f.sizefrom(val, field.Sizefrom) + } + if v.Kind() == reflect.Ptr && !v.Elem().IsValid() { + v.Set(reflect.New(v.Type().Elem())) + } + if field.Type == Struct { + if field.Slice { + vals := reflect.MakeSlice(v.Type(), length, length) + for i := 0; i < length; i++ { + v := vals.Index(i) + fields, err := parseFields(v) + if err != nil { + return err + } + if err := fields.Unpack(r, v, options); err != nil { + return err + } + } + v.Set(vals) + } else { + // TODO: DRY (we repeat the inner loop above) + fields, err := parseFields(v) + if err != nil { + return err + } + if err := fields.Unpack(r, v, options); err != nil { + return err + } + } + continue + } else { + typ := field.Type.Resolve(options) + if typ == CustomType { + if err := v.Addr().Interface().(Custom).Unpack(r, length, options); err != nil { + return err + } + } else { + size := length * field.Type.Resolve(options).Size() + if size < 8 { + buf = tmp[:size] + } else { + buf = make([]byte, size) + } + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + err := field.Unpack(buf[:size], v, length, options) + if err != nil { + return err + } + } + } + } + return nil +} |