1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
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
}
|