summaryrefslogtreecommitdiff
path: root/pkg/binstruct
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/binstruct')
-rw-r--r--pkg/binstruct/binint.go29
-rw-r--r--pkg/binstruct/binint/builtins.go241
-rw-r--r--pkg/binstruct/binstruct_test.go50
-rw-r--r--pkg/binstruct/builtins.go241
-rw-r--r--pkg/binstruct/l1.go202
-rw-r--r--pkg/binstruct/l2.go161
-rw-r--r--pkg/binstruct/l3.go66
-rw-r--r--pkg/binstruct/marshal.go15
-rw-r--r--pkg/binstruct/size.go29
-rw-r--r--pkg/binstruct/structs.go166
-rw-r--r--pkg/binstruct/unmarshal.go22
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()))
}
}