forked from lunixbochs/struc
/
fields.go
114 lines (107 loc) · 2.11 KB
/
fields.go
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
package struc
import (
"encoding/binary"
"io"
"reflect"
"strings"
)
type Fields []*Field
func (f Fields) SetByteOrder(order binary.ByteOrder) {
for _, field := range f {
field.Order = order
}
}
func (f Fields) String() string {
fields := make([]string, len(f))
for i, field := range f {
fields[i] = field.String()
}
return "{" + strings.Join(fields, ", ") + "}"
}
func (f Fields) Sizeof(val reflect.Value) int {
for val.Kind() == reflect.Ptr {
val = val.Elem()
}
size := 0
for i, field := range f {
v := val.Field(i)
if v.CanSet() {
size += field.Size(v)
}
}
return size
}
func (f Fields) Pack(buf []byte, val reflect.Value) error {
for val.Kind() == reflect.Ptr {
val = val.Elem()
}
pos := 0
for i, field := range f {
if !field.CanSet {
continue
}
v := val.Field(i)
length := field.Len
if field.Sizefrom != nil {
length = int(val.FieldByIndex(field.Sizefrom).Int())
}
if length <= 0 && field.Slice {
length = field.Size(v)
}
if field.Sizeof != nil {
length := val.FieldByIndex(field.Sizeof).Len()
v = reflect.ValueOf(length)
}
err := field.Pack(buf[pos:], v, length)
if err != nil {
return err
}
pos += field.Size(v)
}
return nil
}
func (f Fields) Unpack(r io.Reader, val reflect.Value) error {
for val.Kind() == reflect.Ptr {
val = val.Elem()
}
var tmp [8]byte
var buf []byte
for i, field := range f {
if !field.CanSet {
continue
}
v := val.Field(i)
length := field.Len
if field.Sizefrom != nil {
length = int(val.FieldByIndex(field.Sizefrom).Int())
}
if v.Kind() == reflect.Ptr && !v.Elem().IsValid() {
v.Set(reflect.New(v.Type().Elem()))
}
if field.Type == Struct {
fields, err := parseFields(v)
if err != nil {
return err
}
if err := fields.Unpack(r, v); err != nil {
return err
}
continue
} else {
size := length * field.Type.Size()
if size < 8 {
buf = tmp[:size]
} else {
buf = make([]byte, size)
}
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
err := field.Unpack(buf[:size], v, length)
if err != nil {
return err
}
}
}
return nil
}