summaryrefslogtreecommitdiff
path: root/lib/binstruct/size.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/binstruct/size.go')
-rw-r--r--lib/binstruct/size.go57
1 files changed, 57 insertions, 0 deletions
diff --git a/lib/binstruct/size.go b/lib/binstruct/size.go
new file mode 100644
index 0000000..6563455
--- /dev/null
+++ b/lib/binstruct/size.go
@@ -0,0 +1,57 @@
+package binstruct
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type StaticSizer interface {
+ BinaryStaticSize() int
+}
+
+func StaticSize(obj any) int {
+ sz, err := staticSize(reflect.TypeOf(obj))
+ if err != nil {
+ panic(err)
+ }
+ return sz
+}
+
+var (
+ staticSizerType = reflect.TypeOf((*StaticSizer)(nil)).Elem()
+ marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
+ unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
+)
+
+func staticSize(typ reflect.Type) (int, error) {
+ if typ.Implements(staticSizerType) {
+ return reflect.New(typ).Elem().Interface().(StaticSizer).BinaryStaticSize(), nil
+ }
+ switch typ.Kind() {
+ case reflect.Uint8, reflect.Int8:
+ return 1, nil
+ case reflect.Uint16, reflect.Int16:
+ return 2, nil
+ case reflect.Uint32, reflect.Int32:
+ return 4, nil
+ case reflect.Uint64, reflect.Int64:
+ return 8, nil
+ case reflect.Ptr:
+ return staticSize(typ.Elem())
+ case reflect.Array:
+ elemSize, err := staticSize(typ.Elem())
+ if err != nil {
+ return 0, err
+ }
+ return elemSize * typ.Len(), nil
+ case reflect.Struct:
+ if !(typ.Implements(marshalerType) || typ.Implements(unmarshalerType)) {
+ return getStructHandler(typ).Size, nil
+ }
+ return 0, fmt.Errorf("type=%v (kind=%v) does not implement binfmt.StaticSizer but does implement binfmt.Marshaler or binfmt.Unmarshaler",
+ typ, typ.Kind())
+ default:
+ return 0, fmt.Errorf("type=%v does not implement binfmt.StaticSizer and kind=%v is not a supported statically-sized kind",
+ typ, typ.Kind())
+ }
+}