-
Notifications
You must be signed in to change notification settings - Fork 0
/
types.go
146 lines (129 loc) · 3.83 KB
/
types.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
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
package rifx
import (
"bytes"
"encoding/binary"
"fmt"
"strings"
)
// Block represents a single block of binary data
type Block struct {
Type string
Size uint32
Data interface{}
}
// ToStruct de-serializes block data into the provided struct pointer
func (b *Block) ToStruct(ptr interface{}) error {
return binary.Read(bytes.NewReader(b.Data.([]byte)), binary.BigEndian, ptr)
}
// ToString returns block data as a string
func (b *Block) ToString() string {
if data, ok := b.Data.([]byte); ok {
// Check for embedded block type
if len(data) >= 4 && strings.ToLower(fmt.Sprintf("%s", data[:4])) == "utf8" {
strLen := binary.BigEndian.Uint32(data[4:8])
if strLen+8 > b.Size {
return fmt.Sprintf("%s", bytes.Trim(data, "\x00"))
}
return fmt.Sprintf("%s", bytes.Trim(data[8:8+strLen], "\x00"))
}
return fmt.Sprintf("%s", bytes.Trim(data, "\x00"))
}
return ""
}
// ToUint8 returns block data as uint8
func (b *Block) ToUint8() uint8 {
return uint8(b.Data.([]byte)[0])
}
// ToUint16 returns block data as uint16
func (b *Block) ToUint16() uint16 {
return binary.BigEndian.Uint16(b.Data.([]byte))
}
// ToUint32 returns block data as uint32
func (b *Block) ToUint32() uint32 {
return binary.BigEndian.Uint32(b.Data.([]byte))
}
// ToUint64 returns block data as uint64
func (b *Block) ToUint64() uint64 {
return binary.BigEndian.Uint64(b.Data.([]byte))
}
// List represents a collection of binary blocks
type List struct {
Identifier string
NumBlocks int
Blocks []*Block
}
// ForEach iterates over the list's blocks and invokes a callback for each
func (l *List) ForEach(cb func(*Block)) {
for _, block := range l.Blocks {
cb(block)
}
}
// Map performs a basic mapping operator over a list's blocks
func (l *List) Map(cb func(*Block) interface{}) []interface{} {
var ret []interface{}
l.ForEach(func(b *Block) {
ret = append(ret, cb(b))
})
return ret
}
// Filter performs a basic filtering operator over a list's blocks
func (l *List) Filter(cb func(*Block) bool) *List {
ret := &List{Identifier: l.Identifier}
l.ForEach(func(b *Block) {
if cb(b) {
ret.Blocks = append(ret.Blocks, b)
}
})
ret.NumBlocks = len(ret.Blocks)
return ret
}
// SublistFilter returns a slice of child lists that have the provided identifier
func (l *List) SublistFilter(identifier string) []*List {
filtered := l.Filter(func(b *Block) bool {
return b.Type == "LIST" && b.Data.(*List).Identifier == identifier
}).Map(func(b *Block) interface{} {
return b.Data
})
ret := make([]*List, len(filtered))
for idx, block := range filtered {
ret[idx] = block.(*List)
}
return ret
}
// Find performs a basic find operation over a list's blocks
func (l *List) Find(cb func(*Block) bool) (*Block, error) {
for _, block := range l.Blocks {
if cb(block) {
return block, nil
}
}
return nil, fmt.Errorf("ENOTFOUND")
}
// FindByType performs a find operation over a list's blocks predicated upon block type
func (l *List) FindByType(blockType string) (*Block, error) {
return l.Find(func(b *Block) bool {
return b.Type == blockType
})
}
// SublistMerge filters sublists with the specified identifier and concatenates their blocks in a new list
func (l *List) SublistMerge(identifier string) *List {
newList := &List{
Identifier: identifier,
Blocks: make([]*Block, 0),
NumBlocks: 0,
}
for _, sublist := range l.SublistFilter(identifier) {
newList.Blocks = append(newList.Blocks, sublist.Blocks...)
newList.NumBlocks += sublist.NumBlocks
}
return newList
}
// SublistFind performs a basic find operation over a list's child list elements based on identifer
func (l *List) SublistFind(identifier string) (*List, error) {
for _, block := range l.Blocks {
if block.Type == "LIST" && block.Data.(*List).Identifier == identifier {
return block.Data.(*List), nil
}
}
return nil, fmt.Errorf("No sublist with identifier %s exists", identifier)
}