-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstruct.go
More file actions
229 lines (210 loc) · 7.18 KB
/
struct.go
File metadata and controls
229 lines (210 loc) · 7.18 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package bmstruct
import (
"fmt"
)
//Struct is a Template with an associated Value. You can think of a Struct as an
//"object" where the Template is the "class".
type Struct struct {
*Template `json:"template"`
Value `json:"data"`
}
//New method instantiates a Struct object by mapping the given data to the
//Template.
//
//The size of the given data shall exactly match the Template size otherwise it
//will panic.
func (t *Template) New(data Valuable) *Struct {
value := data.GetValue()
if t.Size != len(value) {
panic("data bytes does not match the template size")
}
return &Struct{
Template: t,
Value: value,
}
}
//Empty method instantiates a Struct object with empty data, i.e. all bytes
//are zeroed out.
func (t *Template) Empty() *Struct {
return &Struct{
Template: t,
Value: make([]byte, t.Size),
}
}
//Clone method creates a new Struct object with the same template and the same
//cloned Value as the original Struct.
func (s *Struct) Clone() *Struct {
clone := &Struct{
Template: s.Template,
Value: s.Value.Clone(),
}
return clone
}
// Lookup method of Struct returns the Value if the field indicated by
// fieldName. A clone of the field is returned so modifying the returned value
// does not impact the Struct. For modifying the Struct object use the Update or
// UpdateFunc method.
//
// Lookup operation for a non-existing field name will panic.
func (s *Struct) Lookup(fieldName string) Value {
field, found := s.Template.Fields[fieldName]
if !found {
panic(fmt.Sprintf("field name %s not found in template", fieldName))
}
return field.copySlice(s.Value)
}
// LookupFunc method of Struct returns function, which looks up the Value of the
// field indicated by fieldName. The returned function returns a clone of the
// field so modifying the returned value does not impact the Struct.
// For modifying the Struct object use the Update or UpdateFunc method.
//
// LookupFunc operation for a non-existing field name will panic.
func (s *Struct) LookupFunc(fieldName string) func() Value {
field, found := s.Template.Fields[fieldName]
if !found {
panic(fmt.Sprintf("field name %s not found in template", fieldName))
}
return func() Value {
return field.copySlice(s.Value)
}
}
// Update method of Struct changes the field indicated by fieldName to the given
// Value.
//
// Update operation will panic for a non-existing field name or incorrect Value
// size.
func (s *Struct) Update(fieldName string, valuable Valuable) {
value := valuable.GetValue()
field, found := s.Template.Fields[fieldName]
if !found {
panic(fmt.Sprintf("field name %s not found in template", fieldName))
}
if uint64(len(value)) != field.Len {
panic(fmt.Sprintf("new value size (%d bytes) and field length (%d bytes) mismatch",
uint64(len(value)), field.Len))
}
field.updateSlice(s.Value, value)
}
// UpdateFunc method of Struct returns a function that can be used to modify the
// field indicated by fieldName.
//
// UpdateFunc operation will panic for a non-existing field name and the
// returned function will panic for incorrect value size.
func (s *Struct) UpdateFunc(fieldName string) func(valuable Valuable) {
field, found := s.Template.Fields[fieldName]
if !found {
panic(fmt.Sprintf("field name %s not found in template", fieldName))
}
return func(valuable Valuable) {
value := valuable.GetValue()
if uint64(len(value)) != field.Len {
panic(fmt.Sprintf("new value size (%d bytes) and field length (%d bytes) mismatch",
uint64(len(value)), field.Len))
}
field.updateSlice(s.Value, value)
}
}
//Structs represents an array of Struct objects over a Value.
type Structs struct {
*Template `json:"template"`
Value `json:"data"`
}
//Slice method creates a new Structs object. Slice will panic when the length of
//the given data does not align with the size of the Template.
func (t *Template) Slice(data Valuable) *Structs {
value := data.GetValue()
if len(value)%t.Size != 0 {
panic("data bytes does not align")
}
structs := &Structs{
Template: t,
Value: value,
}
return structs
}
//SliceE method creates a new Structs object. SliceE will return an error when
//the length of the given data does not align with the size of the Template.
func (t *Template) SliceE(data Valuable) (*Structs, error) {
value := data.GetValue()
if len(value)%t.Size != 0 {
return nil, fmt.Errorf("data bytes does not align")
}
structs := &Structs{
Template: t,
Value: value,
}
return structs, nil
}
//Count method returns the number of Struct objects in a Structs.
func (ss *Structs) Count() uint32 {
return uint32(len(ss.Value) / ss.Template.Size)
}
//At method returns the Struct object that starts at the given offset. For
//returning the nth Struct object use the 'Nth' method call.
//
//At method panics when the offset is invalid (too large or not aligned
//with the Template size).
//
//At method returns a copy of data. Any modification on the returned Struct does
//not impact the Structs object.
func (ss *Structs) At(offset uint64) *Struct {
if offset+uint64(ss.Template.Size) > uint64(len(ss.Value)) {
panic("offset out of bounds")
}
if offset%uint64(ss.Template.Size) != 0 {
panic("offset does not align with the Template size")
}
return ss.Template.New(ss.Value[offset : offset+uint64(ss.Template.Size)]).Clone()
}
//Nth method returns the nth Struct object. For returning the Struct object at a
//given offset use the 'At' method call.
//
//Nth method panics when n is invalid (too large or negative).
//
//Nth method returns a copy of data. Any modification on the returned Struct
//does not impact the Structs object.
func (ss *Structs) Nth(n int) *Struct {
if n < 0 {
panic("index out of bounds")
}
offset := uint64(n * ss.Template.Size)
if offset+uint64(ss.Template.Size) > uint64(len(ss.Value)) {
panic("index out of bounds")
}
return ss.Template.New(ss.Value[offset : offset+uint64(ss.Template.Size)]).Clone()
}
//Update method updates a Struct at the specified offset with the given Struct.
//
//Update panics if the offset is invalid (i.e. too large or does not align with
//the Template size) or the new Struct has a different kind of a Template.
func (ss *Structs) Update(offset uint64, s *Struct) {
if offset+uint64(ss.Template.Size) > uint64(len(ss.Value)) {
panic("offset out of bounds")
}
if offset%uint64(ss.Template.Size) != 0 {
panic("invalid offset, no struct found")
}
if !ss.Template.Equal(s.Template) {
panic("structs cannot be updated with different kind of struct")
}
copy(ss.Value[offset:offset+(uint64(ss.Template.Size))], s.Value)
}
//Clone method returns a new Structs that is the clone of the original Struct,
//i.e. the template is the same and the Value is cloned.
func (ss *Structs) Clone() *Structs {
clone := &Structs{
Value: ss.Value.Clone(),
Template: ss.Template,
}
return clone
}
//StructsIterFn is a function definition that is used by the Iter method if
//Structs.
type StructsIterFn func(offset uint64, s *Struct)
//Iter method iterates over the Struct objects stored in Structs. For each
//Struct the given 'fn' method is called with the offset and the Struct object.
func (ss *Structs) Iter(fn StructsIterFn) {
for offset := uint64(0); offset < uint64(len(ss.Value)); offset += uint64(ss.Template.Size) {
fn(offset, ss.At(offset))
}
}