forked from p9c/pod-archive
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simplebuffer.go
105 lines (97 loc) · 2.91 KB
/
simplebuffer.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
package simplebuffer
import (
"encoding/binary"
)
type Serializer interface {
// Encode returns the wire/storage form of the data
Encode() []byte
// Decode stores the decoded data from the head of the slice and returns the remainder
Decode(b []byte) []byte
}
type Serializers []Serializer
type Container struct {
Data []byte
}
// CreateContainer takes an array of serializer interface objects and renders the data into bytes
func (srs Serializers) CreateContainer(magic []byte) (out *Container) {
if len(magic) != 4 {
Error("magic must be 4 bytes")
return
}
out = &Container{}
var offset uint32
var length uint16
var nodes []uint32
for i := range srs {
b := srs[i].Encode()
// Debug(i, len(b), hex.EncodeToString(b))
length++
nodes = append(nodes, offset)
offset += uint32(len(b))
out.Data = append(out.Data, b...)
}
// L.Spew(out.Data)
// Debug(offset, len(out.Data))
nodeB := make([]byte, len(nodes)*4+2)
start := uint32(len(nodeB) + 8)
binary.BigEndian.PutUint16(nodeB[:2], length)
for i := range nodes {
b := nodeB[i*4+2 : i*4+4+2]
binary.BigEndian.PutUint32(b, nodes[i]+start)
// Debug(i, len(b), hex.EncodeToString(b))
}
// L.Spew(nodeB)
out.Data = append(nodeB, out.Data...)
size := offset + uint32(len(nodeB)) + 8
// Debug("size", size, len(out.Data))
sB := make([]byte, 4)
binary.BigEndian.PutUint32(sB, size)
out.Data = append(append(magic[:], sB...), out.Data...)
return
}
func (c *Container) Count() uint16 {
if len(c.Data) < 8 {
return 0
}
size := binary.BigEndian.Uint32(c.Data[4:8])
// Debug("size", size)
if len(c.Data) >= int(size) {
// we won't touch it if it's not at least as big so we don't get bounds errors
return binary.BigEndian.Uint16(c.Data[8:10])
}
return 0
}
func (c *Container) GetMagic() (out []byte) {
return c.Data[:4]
}
// Get returns the bytes that can be imported into an interface assuming the types are correct - field ordering is hard
// coded by the creation and identified by the magic.
//
// This is all read only and subslices so it should generate very little garbage or copy operations except as required
// for the output (we aren't going to go unsafe here, it isn't really necessary since already this library enables
// avoiding the decoding of values not being used from a message (or not used yet)
func (c *Container) Get(idx uint16) (out []byte) {
length := c.Count()
size := len(c.Data)
if length > idx {
// Debug("length", length, "idx", idx)
if idx < length {
offset := binary.BigEndian.Uint32(c.
Data[10+idx*4 : 10+idx*4+4])
// Debug("offset", offset)
if idx < length-1 {
nextOffset := binary.BigEndian.Uint32(c.
Data[10+((idx+1)*4) : 10+((idx+1)*4)+4])
// Debug("nextOffset", nextOffset)
out = c.Data[offset:nextOffset]
} else {
nextOffset := len(c.Data)
// Debug("last nextOffset", nextOffset)
out = c.Data[offset:nextOffset]
}
}
} else {
Error("size mismatch", length, size)
}
return
}