package struc import ( "bytes" "encoding/binary" "fmt" "math" "reflect" ) type Field struct { Name string Ptr bool Index int Type Type defType Type Array bool Slice bool Len int Order binary.ByteOrder Sizeof []int Sizefrom []int Fields Fields kind reflect.Kind } func (f *Field) String() string { var out string if f.Type == Pad { return fmt.Sprintf("{type: Pad, len: %d}", f.Len) } else { out = fmt.Sprintf("type: %s, order: %v", f.Type.String(), f.Order) } if f.Sizefrom != nil { out += fmt.Sprintf(", sizefrom: %v", f.Sizefrom) } else if f.Len > 0 { out += fmt.Sprintf(", len: %d", f.Len) } if f.Sizeof != nil { out += fmt.Sprintf(", sizeof: %v", f.Sizeof) } return "{" + out + "}" } func (f *Field) Size(val reflect.Value, options *Options) int { typ := f.Type.Resolve(options) size := 0 if typ == Struct { vals := []reflect.Value{val} if f.Slice { vals = make([]reflect.Value, val.Len()) for i := 0; i < val.Len(); i++ { vals[i] = val.Index(i) } } for _, val := range vals { size += f.Fields.Sizeof(val, options) } } else if typ == Pad { size = f.Len } else if f.Slice || f.kind == reflect.String { length := val.Len() if f.Len > 1 { length = f.Len } size = length * typ.Size() } else if typ == CustomType { return val.Addr().Interface().(Custom).Size(options) } else { size = typ.Size() } align := options.ByteAlign if align > 0 && size < align { size = align } return size } func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Options) (size int, err error) { order := f.Order if options.Order != nil { order = options.Order } if f.Ptr { val = val.Elem() } typ := f.Type.Resolve(options) switch typ { case Struct: return f.Fields.Pack(buf, val, options) case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64: size = typ.Size() var n uint64 switch f.kind { case reflect.Bool: if val.Bool() { n = 1 } else { n = 0 } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: n = uint64(val.Int()) default: n = val.Uint() } switch typ { case Bool: if n != 0 { buf[0] = 1 } else { buf[0] = 0 } case Int8, Uint8: buf[0] = byte(n) case Int16, Uint16: order.PutUint16(buf, uint16(n)) case Int32, Uint32: order.PutUint32(buf, uint32(n)) case Int64, Uint64: order.PutUint64(buf, uint64(n)) } case Float32, Float64: size = typ.Size() n := val.Float() switch typ { case Float32: order.PutUint32(buf, math.Float32bits(float32(n))) case Float64: order.PutUint64(buf, math.Float64bits(n)) } case String: switch f.kind { case reflect.String: size = val.Len() copy(buf, []byte(val.String())) default: // TODO: handle kind != bytes here size = val.Len() copy(buf, val.Bytes()) } case CustomType: return val.Addr().Interface().(Custom).Pack(buf, options) default: panic(fmt.Sprintf("no pack handler for type: %s", typ)) } return } func (f *Field) Pack(buf []byte, val reflect.Value, length int, options *Options) (int, error) { typ := f.Type.Resolve(options) if typ == Pad { for i := 0; i < length; i++ { buf[i] = 0 } return length, nil } if f.Slice { // special case strings and byte slices for performance end := val.Len() if !f.Array && typ == Uint8 && (f.defType == Uint8 || f.kind == reflect.String) { var tmp []byte if f.kind == reflect.String { tmp = []byte(val.String()) } else { tmp = val.Bytes() } copy(buf, tmp) if end < length { // TODO: allow configuring pad byte? rep := bytes.Repeat([]byte{0}, length-end) copy(buf[end:], rep) return length, nil } return val.Len(), nil } pos := 0 var zero reflect.Value if end < length { zero = reflect.Zero(val.Type().Elem()) } for i := 0; i < length; i++ { cur := zero if i < end { cur = val.Index(i) } if n, err := f.packVal(buf[pos:], cur, 1, options); err != nil { return pos, err } else { pos += n } } return pos, nil } else { return f.packVal(buf, val, length, options) } } func (f *Field) unpackVal(buf []byte, val reflect.Value, length int, options *Options) error { order := f.Order if options.Order != nil { order = options.Order } if f.Ptr { val = val.Elem() } typ := f.Type.Resolve(options) switch typ { case Float32, Float64: var n float64 switch typ { case Float32: n = float64(math.Float32frombits(order.Uint32(buf))) case Float64: n = math.Float64frombits(order.Uint64(buf)) } switch f.kind { case reflect.Float32, reflect.Float64: val.SetFloat(n) default: return fmt.Errorf("struc: refusing to unpack float into field %s of type %s", f.Name, f.kind.String()) } case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64: var n uint64 switch typ { case Int8: n = uint64(int64(int8(buf[0]))) case Int16: n = uint64(int64(int16(order.Uint16(buf)))) case Int32: n = uint64(int64(int32(order.Uint32(buf)))) case Int64: n = uint64(int64(order.Uint64(buf))) case Bool, Uint8: n = uint64(buf[0]) case Uint16: n = uint64(order.Uint16(buf)) case Uint32: n = uint64(order.Uint32(buf)) case Uint64: n = uint64(order.Uint64(buf)) } switch f.kind { case reflect.Bool: val.SetBool(n != 0) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: val.SetInt(int64(n)) default: val.SetUint(n) } default: panic(fmt.Sprintf("no unpack handler for type: %s", typ)) } return nil } func (f *Field) Unpack(buf []byte, val reflect.Value, length int, options *Options) error { typ := f.Type.Resolve(options) if typ == Pad || f.kind == reflect.String { if typ == Pad { return nil } else { val.SetString(string(buf)) return nil } } else if f.Slice { if val.Cap() < length { val.Set(reflect.MakeSlice(val.Type(), length, length)) } else if val.Len() < length { val.Set(val.Slice(0, length)) } // special case byte slices for performance if !f.Array && typ == Uint8 && f.defType == Uint8 { copy(val.Bytes(), buf[:length]) return nil } pos := 0 size := typ.Size() for i := 0; i < length; i++ { if err := f.unpackVal(buf[pos:pos+size], val.Index(i), 1, options); err != nil { return err } pos += size } return nil } else { return f.unpackVal(buf, val, length, options) } }