diff options
Diffstat (limited to 'pkg/binstruct')
-rw-r--r-- | pkg/binstruct/binint.go | 29 | ||||
-rw-r--r-- | pkg/binstruct/binint/builtins.go | 241 | ||||
-rw-r--r-- | pkg/binstruct/binstruct_test.go | 50 | ||||
-rw-r--r-- | pkg/binstruct/builtins.go | 241 | ||||
-rw-r--r-- | pkg/binstruct/l1.go | 202 | ||||
-rw-r--r-- | pkg/binstruct/l2.go | 161 | ||||
-rw-r--r-- | pkg/binstruct/l3.go | 66 | ||||
-rw-r--r-- | pkg/binstruct/marshal.go | 15 | ||||
-rw-r--r-- | pkg/binstruct/size.go | 29 | ||||
-rw-r--r-- | pkg/binstruct/structs.go | 166 | ||||
-rw-r--r-- | pkg/binstruct/unmarshal.go | 22 |
11 files changed, 511 insertions, 711 deletions
diff --git a/pkg/binstruct/binint.go b/pkg/binstruct/binint.go new file mode 100644 index 0000000..fb6374c --- /dev/null +++ b/pkg/binstruct/binint.go @@ -0,0 +1,29 @@ +package binstruct + +import ( + "reflect" + + "lukeshu.com/btrfs-tools/pkg/binstruct/binint" +) + +type ( + U8 = binint.U8 + U16le = binint.U16le + U32le = binint.U32le + U64le = binint.U64le + U16be = binint.U16be + U32be = binint.U32be + U64be = binint.U64be + I8 = binint.I8 + I16le = binint.I16le + I32le = binint.I32le + I64le = binint.I64le + I16be = binint.I16be + I32be = binint.I32be + I64be = binint.I64be +) + +var ( + u8Type = reflect.TypeOf(U8(0)) + i8Type = reflect.TypeOf(I8(0)) +) diff --git a/pkg/binstruct/binint/builtins.go b/pkg/binstruct/binint/builtins.go new file mode 100644 index 0000000..3272ed9 --- /dev/null +++ b/pkg/binstruct/binint/builtins.go @@ -0,0 +1,241 @@ +package binint + +import ( + "encoding/binary" + "fmt" +) + +func needNBytes(t interface{}, dat []byte, n int) error { + if len(dat) < n { + return fmt.Errorf("%T.UnmarshalBinary: need at least %d bytes, only have %d", t, n, len(dat)) + } + return nil +} + +// unsigned + +type U8 uint8 + +func (U8) BinaryStaticSize() int { return 1 } +func (x U8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil } +func (x *U8) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 1); err != nil { + return 0, err + } + *x = U8(dat[0]) + return 1, nil +} + +// unsigned little endian + +type U16le uint16 + +func (U16le) BinaryStaticSize() int { return 2 } +func (x U16le) MarshalBinary() ([]byte, error) { + var buf [2]byte + binary.LittleEndian.PutUint16(buf[:], uint16(x)) + return buf[:], nil +} +func (x *U16le) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 2); err != nil { + return 0, err + } + *x = U16le(binary.LittleEndian.Uint16(dat)) + return 2, nil +} + +type U32le uint32 + +func (U32le) BinaryStaticSize() int { return 4 } +func (x U32le) MarshalBinary() ([]byte, error) { + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], uint32(x)) + return buf[:], nil +} +func (x *U32le) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 4); err != nil { + return 0, err + } + *x = U32le(binary.LittleEndian.Uint32(dat)) + return 2, nil +} + +type U64le uint64 + +func (U64le) BinaryStaticSize() int { return 8 } +func (x U64le) MarshalBinary() ([]byte, error) { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], uint64(x)) + return buf[:], nil +} +func (x *U64le) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 8); err != nil { + return 0, err + } + *x = U64le(binary.LittleEndian.Uint64(dat)) + return 2, nil +} + +// unsigned big endian + +type U16be uint16 + +func (U16be) BinaryStaticSize() int { return 2 } +func (x U16be) MarshalBinary() ([]byte, error) { + var buf [2]byte + binary.BigEndian.PutUint16(buf[:], uint16(x)) + return buf[:], nil +} +func (x *U16be) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 2); err != nil { + return 0, err + } + *x = U16be(binary.BigEndian.Uint16(dat)) + return 2, nil +} + +type U32be uint32 + +func (U32be) BinaryStaticSize() int { return 4 } +func (x U32be) MarshalBinary() ([]byte, error) { + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], uint32(x)) + return buf[:], nil +} +func (x *U32be) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 4); err != nil { + return 0, err + } + *x = U32be(binary.BigEndian.Uint32(dat)) + return 2, nil +} + +type U64be uint64 + +func (U64be) BinaryStaticSize() int { return 8 } +func (x U64be) MarshalBinary() ([]byte, error) { + var buf [8]byte + binary.BigEndian.PutUint64(buf[:], uint64(x)) + return buf[:], nil +} +func (x *U64be) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 8); err != nil { + return 0, err + } + *x = U64be(binary.BigEndian.Uint64(dat)) + return 2, nil +} + +// signed + +type I8 int8 + +func (I8) BinaryStaticSize() int { return 1 } +func (x I8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil } +func (x *I8) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 1); err != nil { + return 0, err + } + *x = I8(dat[0]) + return 1, nil +} + +// signed little endian + +type I16le int16 + +func (I16le) BinaryStaticSize() int { return 2 } +func (x I16le) MarshalBinary() ([]byte, error) { + var buf [2]byte + binary.LittleEndian.PutUint16(buf[:], uint16(x)) + return buf[:], nil +} +func (x *I16le) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 2); err != nil { + return 0, err + } + *x = I16le(binary.LittleEndian.Uint16(dat)) + return 2, nil +} + +type I32le int32 + +func (I32le) BinaryStaticSize() int { return 4 } +func (x I32le) MarshalBinary() ([]byte, error) { + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], uint32(x)) + return buf[:], nil +} +func (x *I32le) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 4); err != nil { + return 0, err + } + *x = I32le(binary.LittleEndian.Uint32(dat)) + return 2, nil +} + +type I64le int64 + +func (I64le) BinaryStaticSize() int { return 8 } +func (x I64le) MarshalBinary() ([]byte, error) { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], uint64(x)) + return buf[:], nil +} +func (x *I64le) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 8); err != nil { + return 0, err + } + *x = I64le(binary.LittleEndian.Uint64(dat)) + return 2, nil +} + +// signed big endian + +type I16be int16 + +func (I16be) BinaryStaticSize() int { return 2 } +func (x I16be) MarshalBinary() ([]byte, error) { + var buf [2]byte + binary.BigEndian.PutUint16(buf[:], uint16(x)) + return buf[:], nil +} +func (x *I16be) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 2); err != nil { + return 0, err + } + *x = I16be(binary.BigEndian.Uint16(dat)) + return 2, nil +} + +type I32be int32 + +func (I32be) BinaryStaticSize() int { return 4 } +func (x I32be) MarshalBinary() ([]byte, error) { + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], uint32(x)) + return buf[:], nil +} +func (x *I32be) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 4); err != nil { + return 0, err + } + *x = I32be(binary.BigEndian.Uint32(dat)) + return 2, nil +} + +type I64be int64 + +func (I64be) BinaryStaticSize() int { return 8 } +func (x I64be) MarshalBinary() ([]byte, error) { + var buf [8]byte + binary.BigEndian.PutUint64(buf[:], uint64(x)) + return buf[:], nil +} +func (x *I64be) UnmarshalBinary(dat []byte) (int, error) { + if err := needNBytes(*x, dat, 8); err != nil { + return 0, err + } + *x = I64be(binary.BigEndian.Uint64(dat)) + return 2, nil +} diff --git a/pkg/binstruct/binstruct_test.go b/pkg/binstruct/binstruct_test.go index 382a200..1070d06 100644 --- a/pkg/binstruct/binstruct_test.go +++ b/pkg/binstruct/binstruct_test.go @@ -6,39 +6,43 @@ import ( "github.com/stretchr/testify/assert" "lukeshu.com/btrfs-tools/pkg/binstruct" + . "lukeshu.com/btrfs-tools/pkg/binstruct/binint" ) func TestSmoke(t *testing.T) { - type UUID [16]byte - type PhysicalAddr int64 + type UUID [16]U8 + type PhysicalAddr I64le + func (PhysicalAddr) BinaryStaticSize() int { return I64le(0).BinaryStaticSize() } + func (x PhysicalAddr) MarshalBinary() ([]byte, error) { return I64le(x).MarshalBinary() } + func (x *PhysicalAddr) UnmarshalBinary([]byte) (int, error) { return I64le(x).UnmarshalBinary(dat) } type DevItem struct { - DeviceID uint64 `bin:"off=0, siz=8"` // device id + DeviceID U64le `bin:"off=0x0, siz=0x8"` // device id - NumBytes uint64 `bin:"off=8, siz=8"` // number of bytes - NumBytesUsed uint64 `bin:"off=10, siz=8"` // number of bytes used + NumBytes U64le `bin:"off=0x8, siz=0x8"` // number of bytes + NumBytesUsed U64le `bin:"off=0x10, siz=0x8"` // number of bytes used - IOOptimalAlign uint32 `bin:"off=18, siz=4"` // optimal I/O align - IOOptimalWidth uint32 `bin:"off=1c, siz=4"` // optimal I/O width - IOMinSize uint32 `bin:"off=20, siz=4"` // minimal I/O size (sector size) + IOOptimalAlign U32le `bin:"off=0x18, siz=0x4"` // optimal I/O align + IOOptimalWidth U32le `bin:"off=0x1c, siz=0x4"` // optimal I/O width + IOMinSize U32le `bin:"off=0x20, siz=0x4"` // minimal I/O size (sector size) - Type uint64 `bin:"off=24, siz=8"` // type - Generation uint64 `bin:"off=2c, siz=8"` // generation - StartOffset uint64 `bin:"off=34, siz=8"` // start offset - DevGroup uint32 `bin:"off=3c, siz=4"` // dev group - SeekSpeed uint8 `bin:"off=40, siz=1"` // seek speed - Bandwidth uint8 `bin:"off=41, siz=1"` // bandwidth + Type U64le `bin:"off=0x24, siz=0x8"` // type + Generation U64le `bin:"off=0x2c, siz=0x8"` // generation + StartOffset U64le `bin:"off=0x34, siz=0x8"` // start offset + DevGroup U32le `bin:"off=0x3c, siz=0x4"` // dev group + SeekSpeed U8 `bin:"off=0x40, siz=0x1"` // seek speed + Bandwidth U8 `bin:"off=0x41, siz=0x1"` // bandwidth - DevUUID UUID `bin:"off=42, siz=10"` // device UUID - FSUUID UUID `bin:"off=52, siz=10"` // FS UUID + DevUUID UUID `bin:"off=0x42, siz=0x10"` // device UUID + FSUUID UUID `bin:"off=0x52, siz=0x10"` // FS UUID - binstruct.End `bin:"off=62"` + binstruct.End `bin:"off=0x62"` } type TestType struct { - Magic [5]byte `bin:"off=0,siz=5"` - Dev DevItem `bin:"off=5,siz=62"` - Addr PhysicalAddr `bin:"off=67, siz=8"` + Magic [5]byte `bin:"off=0x0,siz=0x5"` + Dev DevItem `bin:"off=0x5,siz=0x62"` + Addr PhysicalAddr `bin:"off=0x67, siz=0x8"` - binstruct.End `bin:"off=6F"` + binstruct.End `bin:"off=0x6F"` } input := TestType{} @@ -51,6 +55,8 @@ func TestSmoke(t *testing.T) { assert.True(t, len(bs) == 0x6F, "len(bs)=0x%x", len(bs)) var output TestType - assert.NoError(t, binstruct.Unmarshal(bs, &output)) + n, err := binstruct.Unmarshal(bs, &output) + assert.NoError(t, err) + assert.Equal(t, 0x6F, n) assert.Equal(t, input, output) } diff --git a/pkg/binstruct/builtins.go b/pkg/binstruct/builtins.go deleted file mode 100644 index c791640..0000000 --- a/pkg/binstruct/builtins.go +++ /dev/null @@ -1,241 +0,0 @@ -package binstruct - -import ( - "encoding/binary" - "fmt" -) - -func needNBytes(t interface{}, dat []byte, n int) error { - if len(dat) < n { - return fmt.Errorf("%T.UnmarshalBinary: need at least %d bytes, only have %d", t, n, len(dat)) - } - return nil -} - -// unsigned - -type u8 uint8 - -func (u8) BinaryStaticSize() int { return 1 } -func (x u8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil } -func (x *u8) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 1); err != nil { - return 0, err - } - *x = u8(dat[0]) - return 1, nil -} - -// unsigned little endian - -type u16le uint16 - -func (u16le) BinaryStaticSize() int { return 2 } -func (x u16le) MarshalBinary() ([]byte, error) { - var buf [2]byte - binary.LittleEndian.PutUint16(buf[:], uint16(x)) - return buf[:], nil -} -func (x *u16le) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 2); err != nil { - return 0, err - } - *x = u16le(binary.LittleEndian.Uint16(dat)) - return 2, nil -} - -type u32le uint32 - -func (u32le) BinaryStaticSize() int { return 4 } -func (x u32le) MarshalBinary() ([]byte, error) { - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], uint32(x)) - return buf[:], nil -} -func (x *u32le) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 4); err != nil { - return 0, err - } - *x = u32le(binary.LittleEndian.Uint32(dat)) - return 2, nil -} - -type u64le uint64 - -func (u64le) BinaryStaticSize() int { return 8 } -func (x u64le) MarshalBinary() ([]byte, error) { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], uint64(x)) - return buf[:], nil -} -func (x *u64le) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 8); err != nil { - return 0, err - } - *x = u64le(binary.LittleEndian.Uint64(dat)) - return 2, nil -} - -// unsigned big endian - -type u16be uint16 - -func (u16be) BinaryStaticSize() int { return 2 } -func (x u16be) MarshalBinary() ([]byte, error) { - var buf [2]byte - binary.BigEndian.PutUint16(buf[:], uint16(x)) - return buf[:], nil -} -func (x *u16be) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 2); err != nil { - return 0, err - } - *x = u16be(binary.BigEndian.Uint16(dat)) - return 2, nil -} - -type u32be uint32 - -func (u32be) BinaryStaticSize() int { return 4 } -func (x u32be) MarshalBinary() ([]byte, error) { - var buf [4]byte - binary.BigEndian.PutUint32(buf[:], uint32(x)) - return buf[:], nil -} -func (x *u32be) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 4); err != nil { - return 0, err - } - *x = u32be(binary.BigEndian.Uint32(dat)) - return 2, nil -} - -type u64be uint64 - -func (u64be) BinaryStaticSize() int { return 8 } -func (x u64be) MarshalBinary() ([]byte, error) { - var buf [8]byte - binary.BigEndian.PutUint64(buf[:], uint64(x)) - return buf[:], nil -} -func (x *u64be) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 8); err != nil { - return 0, err - } - *x = u64be(binary.BigEndian.Uint64(dat)) - return 2, nil -} - -// signed - -type i8 int8 - -func (i8) BinaryStaticSize() int { return 1 } -func (x i8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil } -func (x *i8) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 1); err != nil { - return 0, err - } - *x = i8(dat[0]) - return 1, nil -} - -// signed little endian - -type i16le int16 - -func (i16le) BinaryStaticSize() int { return 2 } -func (x i16le) MarshalBinary() ([]byte, error) { - var buf [2]byte - binary.LittleEndian.PutUint16(buf[:], uint16(x)) - return buf[:], nil -} -func (x *i16le) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 2); err != nil { - return 0, err - } - *x = i16le(binary.LittleEndian.Uint16(dat)) - return 2, nil -} - -type i32le int32 - -func (i32le) BinaryStaticSize() int { return 4 } -func (x i32le) MarshalBinary() ([]byte, error) { - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], uint32(x)) - return buf[:], nil -} -func (x *i32le) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 4); err != nil { - return 0, err - } - *x = i32le(binary.LittleEndian.Uint32(dat)) - return 2, nil -} - -type i64le int64 - -func (i64le) BinaryStaticSize() int { return 8 } -func (x i64le) MarshalBinary() ([]byte, error) { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], uint64(x)) - return buf[:], nil -} -func (x *i64le) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 8); err != nil { - return 0, err - } - *x = i64le(binary.LittleEndian.Uint64(dat)) - return 2, nil -} - -// signed big endian - -type i16be int16 - -func (i16be) BinaryStaticSize() int { return 2 } -func (x i16be) MarshalBinary() ([]byte, error) { - var buf [2]byte - binary.BigEndian.PutUint16(buf[:], uint16(x)) - return buf[:], nil -} -func (x *i16be) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 2); err != nil { - return 0, err - } - *x = i16be(binary.BigEndian.Uint16(dat)) - return 2, nil -} - -type i32be int32 - -func (i32be) BinaryStaticSize() int { return 4 } -func (x i32be) MarshalBinary() ([]byte, error) { - var buf [4]byte - binary.BigEndian.PutUint32(buf[:], uint32(x)) - return buf[:], nil -} -func (x *i32be) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 4); err != nil { - return 0, err - } - *x = i32be(binary.BigEndian.Uint32(dat)) - return 2, nil -} - -type i64be int64 - -func (i64be) BinaryStaticSize() int { return 8 } -func (x i64be) MarshalBinary() ([]byte, error) { - var buf [8]byte - binary.BigEndian.PutUint64(buf[:], uint64(x)) - return buf[:], nil -} -func (x *i64be) UnmarshalBinary(dat []byte) (int, error) { - if err := needNBytes(*x, dat, 8); err != nil { - return 0, err - } - *x = i64be(binary.BigEndian.Uint64(dat)) - return 2, nil -} diff --git a/pkg/binstruct/l1.go b/pkg/binstruct/l1.go deleted file mode 100644 index 4464f51..0000000 --- a/pkg/binstruct/l1.go +++ /dev/null @@ -1,202 +0,0 @@ -//go:build old -// +build old - -package binstruct - -import ( - "encoding/binary" - "fmt" - "reflect" -) - -type Marshaler interface { - MarshalBinary() []byte -} - -type Unmarshaler interface { - UnmarshalBinary([]byte) -} - -type handler interface { - Unmarshal(dat []byte) interface{} - Marshal(val interface{}) []byte -} - -type extHandler struct { - typ reflect.Type -} - -func (_ extHandler) Marshal(val interface{}) []byte { - return val.(Marshaler).MarshalBinary() -} -func (e extHandler) Unmarshal(dat []byte) interface{} { - valPtr := reflect.New(e.typ).Interface().(Unmarshaler) - valPtr.UnmarshalBinary(dat) - return reflect.ValueOf(valPtr).Elem().Interface() -} -func (e extHandler) Size() int64 { - val := reflect.New(e.typ).Elem().Interface().(Marshaler) - return val.BinarySize() -} - -type primitive struct { - unmarshal func(dat []byte) interface{} - marshal func(val interface{}) []byte - size int64 -} - -func (p primitive) Unmarshal(dat []byte) interface{} { return p.unmarshal(dat) } -func (p primitive) Marshal(val interface{}) []byte { return p.marshal(val) } -func (p primitive) Size() int64 { return p.size } - -var _ handler = primitive{} - -func convert[T any](in interface{}) T { - var dstTyp T - return reflect.ValueOf(in).Convert(reflect.TypeOf(dstTyp)).Interface().(T) -} - -func genHandler(typ reflect.Type) (handler, error) { - _, marOK := reflect.New(typ).Elem().Interface().(Marshaler) - _, unmarOK := reflect.New(typ).Interface().(Unmarshaler) - if marOK && unmarOK { - return extHandler{ - typ: typ, - }, nil - } - switch typ.Kind() { - case reflect.Invalid: // invalid - return nil, fmt.Errorf("unsupported kind: %s: %v", typ.Kind(), typ) - case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: // I don't wanna - return nil, fmt.Errorf("unsupported kind: %s: %v", typ.Kind(), typ) - case reflect.Int, reflect.Uint, reflect.Uintptr: // platform specific - return nil, fmt.Errorf("unsupported kind: %s: %v", typ.Kind(), typ) - case reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer: // runtime - return nil, fmt.Errorf("unsupported kind: %s: %v", typ.Kind(), typ) - case reflect.Map, reflect.Slice, reflect.String: // dynamic size - return nil, fmt.Errorf("unsupported kind: %s: %v", typ.Kind(), typ) - - // uint //////////////////////////////////////////////////////////////// - case reflect.Uint8: - return primitive{ - unmarshal: func(dat []byte) interface{} { return dat[0] }, - marshal: func(val interface{}) []byte { return []byte{convert[uint8](val)} }, - size: 1, - }, nil - case reflect.Uint16: - return primitive{ - unmarshal: func(dat []byte) interface{} { return binary.LittleEndian.Uint16(dat) }, - marshal: func(val interface{}) []byte { - var buf [2]byte - binary.LittleEndian.PutUint16(buf[:], convert[uint16](val)) - return buf[:] - }, - size: 2, - }, nil - case reflect.Uint32: - return primitive{ - unmarshal: func(dat []byte) interface{} { return binary.LittleEndian.Uint32(dat) }, - marshal: func(val interface{}) []byte { - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], convert[uint32](val)) - return buf[:] - }, - size: 4, - }, nil - case reflect.Uint64: - return primitive{ - unmarshal: func(dat []byte) interface{} { return binary.LittleEndian.Uint64(dat) }, - marshal: func(val interface{}) []byte { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], convert[uint64](val)) - return buf[:] - }, - size: 8, - }, nil - - // int ///////////////////////////////////////////////////////////////// - case reflect.Int8: - return primitive{ - unmarshal: func(dat []byte) interface{} { return int8(dat[0]) }, - marshal: func(val interface{}) []byte { return []byte{uint8(convert[int8](val))} }, - size: 1, - }, nil - case reflect.Int16: - return primitive{ - unmarshal: func(dat []byte) interface{} { return int16(binary.LittleEndian.Uint16(dat)) }, - marshal: func(val interface{}) []byte { - var buf [2]byte - binary.LittleEndian.PutUint16(buf[:], uint16(convert[int16](val))) - return buf[:] - }, - size: 2, - }, nil - case reflect.Int32: - return primitive{ - unmarshal: func(dat []byte) interface{} { return int32(binary.LittleEndian.Uint32(dat)) }, - marshal: func(val interface{}) []byte { - var buf [4]byte - binary.LittleEndian.PutUint32(buf[:], uint32(convert[int32](val))) - return buf[:] - }, - size: 4, - }, nil - case reflect.Int64: - return primitive{ - unmarshal: func(dat []byte) interface{} { return int64(binary.LittleEndian.Uint64(dat)) }, - marshal: func(val interface{}) []byte { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], uint64(convert[int64](val))) - return buf[:] - }, - size: 8, - }, nil - - // composite /////////////////////////////////////////////////////////// - - case reflect.Ptr: - inner, err := getHandler(typ.Elem()) - if err != nil { - return nil, fmt.Errorf("%v: %w", typ, err) - } - return primitive{ - unmarshal: func(dat []byte) interface{} { - return reflect.ValueOf(inner.Unmarshal(dat)).Addr().Interface() - }, - marshal: func(val interface{}) []byte { - return inner.Marshal(reflect.ValueOf(val).Elem().Interface()) - }, - size: inner.Size(), - }, nil - case reflect.Array: - inner, err := getHandler(typ.Elem()) - if err != nil { - return nil, fmt.Errorf("%v: %w", typ, err) - } - return primitive{ - unmarshal: func(dat []byte) interface{} { - val := reflect.New(typ).Elem() - for i := 0; i < typ.Len(); i++ { - fieldVal := inner.Unmarshal(dat[i*int(inner.Size()):]) - val.Index(i).Set(reflect.ValueOf(fieldVal).Convert(typ.Elem())) - } - return val.Interface() - }, - marshal: func(val interface{}) []byte { - _val := reflect.ValueOf(val) - var ret []byte - for i := 0; i < typ.Len(); i++ { - ret = append(ret, inner.Marshal(_val.Index(i).Interface())...) - } - return ret - }, - size: inner.Size() * int64(typ.Len()), - }, nil - case reflect.Struct: - return genStructHandler(typ) - - // end ///////////////////////////////////////////////////////////////// - default: - panic(fmt.Errorf("unknown kind: %v: %v", typ.Kind(), typ)) - } -} diff --git a/pkg/binstruct/l2.go b/pkg/binstruct/l2.go deleted file mode 100644 index 4f05d96..0000000 --- a/pkg/binstruct/l2.go +++ /dev/null @@ -1,161 +0,0 @@ -//go:build old -// +build old - -package binstruct - -import ( - "fmt" - "reflect" - "strconv" - "strings" -) - -type End struct{} - -type structHandler struct { - typ reflect.Type - fields []structField - size int64 -} - -type structField struct { - typ reflect.Type - tag - handler - name string -} - -func (sh structHandler) Unmarshal(dat []byte) interface{} { - val := reflect.New(sh.typ).Elem() - for i, field := range sh.fields { - if field.skip { - continue - } - fieldVal := field.Unmarshal(dat[field.off:]) - val.Field(i).Set(reflect.ValueOf(fieldVal).Convert(field.typ)) - } - return val.Interface() -} -func (sh structHandler) Marshal(_val interface{}) []byte { - val := reflect.ValueOf(_val) - ret := make([]byte, 0, sh.size) - for i, field := range sh.fields { - if field.skip { - continue - } - if int64(len(ret)) != field.off { - panic(fmt.Errorf("field %d %q: len(ret)=0x%x but field.off=0x%x", i, field.name, len(ret), field.off)) - } - ret = append(ret, field.Marshal(val.Field(i).Interface())...) - } - return ret -} -func (sh structHandler) Size() int64 { - return sh.size -} - -var _ handler = structHandler{} - -func genStructHandler(structInfo reflect.Type) (handler, error) { - ret := structHandler{ - typ: structInfo, - } - - var curOffset, endOffset int64 - for i := 0; i < structInfo.NumField(); i++ { - var fieldInfo reflect.StructField = structInfo.Field(i) - - fieldTag, err := parseStructTag(fieldInfo.Tag.Get("bin")) - if err != nil { - return nil, fmt.Errorf("%v: field %q: %w", - structInfo, fieldInfo.Name, err) - } - if fieldTag.skip { - ret.fields = append(ret.fields, structField{ - tag: fieldTag, - name: fieldInfo.Name, - }) - continue - } - - if fieldTag.off != curOffset { - err := fmt.Errorf("tag says off=0x%x but curOffset=0x%x", fieldTag.off, curOffset) - return nil, fmt.Errorf("%v: field %q: %w", - structInfo, fieldInfo.Name, err) - } - if fieldInfo.Type == reflect.TypeOf(End{}) { - endOffset = curOffset - } - - fieldHandler, err := getHandler(fieldInfo.Type) - if err != nil { - return nil, fmt.Errorf("%v: field %q: %w", - structInfo, fieldInfo.Name, err) - } - - if fieldTag.siz != fieldHandler.Size() { - err := fmt.Errorf("tag says siz=0x%x but handler.Size()=0x%x", fieldTag.siz, fieldHandler.Size()) - return nil, fmt.Errorf("%v: field %q: %w", - structInfo, fieldInfo.Name, err) - } - curOffset += fieldTag.siz - - ret.fields = append(ret.fields, structField{ - typ: fieldInfo.Type, - tag: fieldTag, - handler: fieldHandler, - name: fieldInfo.Name, - }) - } - ret.size = curOffset - - if ret.size != endOffset { - return nil, fmt.Errorf("%v: .size=%v but endOffset=%v", - structInfo, ret.size, endOffset) - } - - return ret, nil -} - -type tag struct { - skip bool - - off int64 - siz int64 -} - -func parseStructTag(str string) (tag, error) { - var ret tag - for _, part := range strings.Split(str, ",") { - part = strings.TrimSpace(part) - if part == "" { - continue - } - if part == "-" { - return tag{skip: true}, nil - } - keyval := strings.SplitN(part, "=", 2) - if len(keyval) != 2 { - return tag{}, fmt.Errorf("option is not a key=value pair: %q", part) - } - key := keyval[0] - val := keyval[1] - switch key { - case "off": - vint, err := strconv.ParseInt(val, 16, 64) - if err != nil { - return tag{}, err - } - ret.off = vint - case "siz": - vint, err := strconv.ParseInt(val, 16, 64) - if err != nil { - return tag{}, err - } - ret.siz = vint - default: - return tag{}, fmt.Errorf("unrecognized option %q", key) - } - } - return ret, nil -} diff --git a/pkg/binstruct/l3.go b/pkg/binstruct/l3.go deleted file mode 100644 index 93919d8..0000000 --- a/pkg/binstruct/l3.go +++ /dev/null @@ -1,66 +0,0 @@ -//go:build old -// +build old - -package binstruct - -import ( - "fmt" - "reflect" -) - -var handlerCache = make(map[reflect.Type]handler) - -func getHandler(typ reflect.Type) (handler, error) { - h, ok := handlerCache[typ] - if ok { - return h, nil - } - - h, err := genHandler(typ) - if err != nil { - return nil, err - } - handlerCache[typ] = h - return h, nil -} - -func Unmarshal(dat []byte, dstPtr interface{}) error { - _dstPtr := reflect.ValueOf(dstPtr) - if _dstPtr.Kind() != reflect.Ptr { - return fmt.Errorf("not a pointer: %v", _dstPtr.Type()) - } - - dst := _dstPtr.Elem() - handler, err := getHandler(dst.Type()) - if err != nil { - return err - } - if int64(len(dat)) < handler.Size() { - return fmt.Errorf("need at least %d bytes of data, only have %d", - handler.Size(), len(dat)) - } - val := handler.Unmarshal(dat[:handler.Size()]) - dst.Set(reflect.ValueOf(val).Convert(dst.Type())) - return nil -} - -func Marshal(val interface{}) ([]byte, error) { - handler, err := getHandler(reflect.TypeOf(val)) - if err != nil { - return nil, err - } - bs := handler.Marshal(val) - if int64(len(bs)) != handler.Size() { - return nil, fmt.Errorf("got %d bytes but expected %d bytes", - len(bs), handler.Size()) - } - return bs, nil -} - -func Size(val interface{}) (int64, error) { - handler, err := getHandler(reflect.TypeOf(val)) - if err != nil { - return 0, err - } - return handler.Size(), nil -} diff --git a/pkg/binstruct/marshal.go b/pkg/binstruct/marshal.go index c8158a4..90f30b6 100644 --- a/pkg/binstruct/marshal.go +++ b/pkg/binstruct/marshal.go @@ -1,20 +1,27 @@ package binstruct import ( + "encoding" "fmt" "reflect" ) -type Marshaler interface { - MarshalBinary() ([]byte, error) -} +type Marshaler = encoding.BinaryMarshaler func Marshal(obj any) ([]byte, error) { if mar, ok := obj.(Marshaler); ok { return mar.MarshalBinary() } + return MarshalWithoutInterface(obj) +} + +func MarshalWithoutInterface(obj any) ([]byte, error) { val := reflect.ValueOf(obj) switch val.Kind() { + case reflect.Uint8: + return val.Convert(u8Type).Interface().(Marshaler).MarshalBinary() + case reflect.Int8: + return val.Convert(i8Type).Interface().(Marshaler).MarshalBinary() case reflect.Ptr: return Marshal(val.Elem().Interface()) case reflect.Array: @@ -28,7 +35,7 @@ func Marshal(obj any) ([]byte, error) { } return ret, nil case reflect.Struct: - // TODO + return getStructHandler(val.Type()).Marshal(val) default: panic(fmt.Errorf("type=%v does not implement binfmt.Marshaler and kind=%v is not a supported statically-sized kind", val.Type(), val.Kind())) diff --git a/pkg/binstruct/size.go b/pkg/binstruct/size.go index 519c2fe..f6b3241 100644 --- a/pkg/binstruct/size.go +++ b/pkg/binstruct/size.go @@ -10,27 +10,34 @@ type StaticSizer interface { } func StaticSize(obj any) int { - return staticSize(reflect.TypeOf(obj)) + sz, err := staticSize(reflect.TypeOf(obj)) + if err != nil { + panic(err) + } + return sz } var staticSizerType = reflect.TypeOf((*StaticSizer)(nil)).Elem() -func staticSize(typ reflect.Type) int { +func staticSize(typ reflect.Type) (int, error) { if typ.Implements(staticSizerType) { - return reflect.New(typ).Elem().Interface().(StaticSizer).BinaryStaticSize() - } - if szer, ok := obj.(StaticSizer); ok { - return szer.BinaryStaticSize() + return reflect.New(typ).Elem().Interface().(StaticSizer).BinaryStaticSize(), nil } switch typ.Kind() { + case reflect.Uint8, reflect.Int8: + return 1, nil case reflect.Ptr: - return StaticSize(typ.Elem()) + return staticSize(typ.Elem()) case reflect.Array: - return StaticSize(typ.Elem()) * typ.Len() + elemSize, err := staticSize(typ.Elem()) + if err != nil { + return 0, err + } + return elemSize * typ.Len(), nil case reflect.Struct: - // TODO + return getStructHandler(typ).Size, nil default: - panic(fmt.Errorf("type=%v does not implement binfmt.StaticSizer and kind=%v is not a supported statically-sized kind", - typ, typ.Kind())) + return 0, fmt.Errorf("type=%v does not implement binfmt.StaticSizer and kind=%v is not a supported statically-sized kind", + typ, typ.Kind()) } } diff --git a/pkg/binstruct/structs.go b/pkg/binstruct/structs.go index 6f971d5..6e9f02f 100644 --- a/pkg/binstruct/structs.go +++ b/pkg/binstruct/structs.go @@ -1,11 +1,177 @@ package binstruct import ( + "fmt" "reflect" + "strconv" + "strings" ) +type End struct{} + +var endType = reflect.TypeOf(End{}) + +type tag struct { + skip bool + + off int + siz int +} + +func parseStructTag(str string) (tag, error) { + var ret tag + for _, part := range strings.Split(str, ",") { + part = strings.TrimSpace(part) + if part == "" { + continue + } + if part == "-" { + return tag{skip: true}, nil + } + keyval := strings.SplitN(part, "=", 2) + if len(keyval) != 2 { + return tag{}, fmt.Errorf("option is not a key=value pair: %q", part) + } + key := keyval[0] + val := keyval[1] + switch key { + case "off": + vint, err := strconv.ParseInt(val, 0, 0) + if err != nil { + return tag{}, err + } + ret.off = int(vint) + case "siz": + vint, err := strconv.ParseInt(val, 0, 0) + if err != nil { + return tag{}, err + } + ret.siz = int(vint) + default: + return tag{}, fmt.Errorf("unrecognized option %q", key) + } + } + return ret, nil +} + type structHandler struct { + Size int + fields []structField +} + +type structField struct { + name string + tag +} + +func (sh structHandler) Unmarshal(dat []byte, dst reflect.Value) (int, error) { + var n int + for i, field := range sh.fields { + if field.skip { + continue + } + _n, err := Unmarshal(dat[n:], dst.Field(i).Addr().Interface()) + if err != nil { + if _n >= 0 { + n += _n + } + return n, fmt.Errorf("field %d %q: %w", + i, field.name, err) + } + if _n != field.siz { + return n, fmt.Errorf("field %d %q: consumed %d bytes but should have consumed %d bytes", + i, field.name, _n, field.siz) + } + n += _n + } + return n, nil +} + +func (sh structHandler) Marshal(val reflect.Value) ([]byte, error) { + ret := make([]byte, 0, sh.Size) + for i, field := range sh.fields { + if field.skip { + continue + } + bs, err := Marshal(val.Field(i).Interface()) + ret = append(ret, bs...) + if err != nil { + return ret, fmt.Errorf("field %d %q: %w", + i, field.name, err) + } + } + return ret, nil } +func genStructHandler(structInfo reflect.Type) (structHandler, error) { + var ret structHandler + + var curOffset, endOffset int + for i := 0; i < structInfo.NumField(); i++ { + var fieldInfo reflect.StructField = structInfo.Field(i) + + fieldTag, err := parseStructTag(fieldInfo.Tag.Get("bin")) + if err != nil { + return ret, fmt.Errorf("%v: field %q: %w", + structInfo, fieldInfo.Name, err) + } + if fieldTag.skip { + ret.fields = append(ret.fields, structField{ + tag: fieldTag, + name: fieldInfo.Name, + }) + continue + } + + if fieldTag.off != curOffset { + err := fmt.Errorf("tag says off=0x%x but curOffset=0x%x", fieldTag.off, curOffset) + return ret, fmt.Errorf("%v: field %q: %w", + structInfo, fieldInfo.Name, err) + } + if fieldInfo.Type == endType { + endOffset = curOffset + } + + fieldSize, err := staticSize(fieldInfo.Type) + if err != nil { + return ret, fmt.Errorf("%v: field %q: %w", + structInfo, fieldInfo.Name, err) + } + + if fieldTag.siz != fieldSize { + err := fmt.Errorf("tag says siz=0x%x but StaticSize(typ)=0x%x", fieldTag.siz, fieldSize) + return ret, fmt.Errorf("%v: field %q: %w", + structInfo, fieldInfo.Name, err) + } + curOffset += fieldTag.siz + + ret.fields = append(ret.fields, structField{ + name: fieldInfo.Name, + tag: fieldTag, + }) + } + ret.Size = curOffset + + if ret.Size != endOffset { + return ret, fmt.Errorf("%v: .Size=%v but endOffset=%v", + structInfo, ret.Size, endOffset) + } + + return ret, nil +} + +var structCache = make(map[reflect.Type]structHandler) + func getStructHandler(typ reflect.Type) structHandler { + h, ok := structCache[typ] + if ok { + return h + } + + h, err := genStructHandler(typ) + if err != nil { + panic(err) + } + structCache[typ] = h + return h } diff --git a/pkg/binstruct/unmarshal.go b/pkg/binstruct/unmarshal.go index 4810d78..ca2f4b2 100644 --- a/pkg/binstruct/unmarshal.go +++ b/pkg/binstruct/unmarshal.go @@ -13,6 +13,10 @@ func Unmarshal(dat []byte, dstPtr any) (int, error) { if unmar, ok := dstPtr.(Unmarshaler); ok { return unmar.UnmarshalBinary(dat) } + return UnmarshalWithoutInterface(dat, dstPtr) +} + +func UnmarshalWithoutInterface(dat []byte, dstPtr any) (int, error) { _dstPtr := reflect.ValueOf(dstPtr) if _dstPtr.Kind() != reflect.Ptr { return 0, fmt.Errorf("not a pointer: %v", _dstPtr.Type()) @@ -20,9 +24,19 @@ func Unmarshal(dat []byte, dstPtr any) (int, error) { dst := _dstPtr.Elem() switch dst.Kind() { + case reflect.Uint8: + newDstPtr := reflect.New(u8Type) + n, err := Unmarshal(dat, newDstPtr.Interface()) + dst.Set(newDstPtr.Elem().Convert(dst.Type())) + return n, err + case reflect.Int8: + newDstPtr := reflect.New(i8Type) + n, err := Unmarshal(dat, newDstPtr.Interface()) + dst.Set(newDstPtr.Elem().Convert(dst.Type())) + return n, err case reflect.Ptr: - elemPtr := reflect.New(dst.Type().Elem()).Interface() - n, err := Unmarshal(dat, elemPtr) + elemPtr := reflect.New(dst.Type().Elem()) + n, err := Unmarshal(dat, elemPtr.Interface()) dst.Set(elemPtr.Convert(dst.Type())) return n, err case reflect.Array: @@ -36,9 +50,9 @@ func Unmarshal(dat []byte, dstPtr any) (int, error) { } return n, nil case reflect.Struct: - // TODO + return getStructHandler(dst.Type()).Unmarshal(dat, dst) default: panic(fmt.Errorf("type=%v does not implement binfmt.Unmarshaler and kind=%v is not a supported statically-sized kind", - val.Type(), val.Kind())) + dst.Type(), dst.Kind())) } } |