tree - mem.go
1 package tree
2
3 import (
4 "bytes"
5 "io"
6 "iter"
7 "slices"
8 "unsafe"
9
10 "vimagination.zapto.org/byteio"
11 )
12
13 // MemTree represents a tree backed by an in-memory byte slice.
14 type MemTree struct {
15 tree []byte
16 data []byte
17 names []string
18 ptrs [][]byte
19 }
20
21 // OpenMem opens a Tree from the given byte slice.
22 func OpenMem(data []byte) (*MemTree, error) {
23 return OpenMemAt(data, int64(len(data)))
24 }
25
26 // OpenMemAt opens a Tree from the given byte slice, using the given Node
27 // pointer instead of using the length of the data.
28 func OpenMemAt(data []byte, pos int64) (*MemTree, error) {
29 if pos <= 0 {
30 return &MemTree{}, nil
31 }
32
33 childrenSize, dataSize, sizes, err := readSizes(bytes.NewReader(data), pos)
34 if err != nil {
35 return nil, err
36 }
37
38 pos -= 1 + sizes
39 dataStart := pos - dataSize
40 m := &MemTree{
41 tree: data,
42 data: data[dataStart:pos],
43 }
44
45 if childrenSize > 0 {
46 if err := m.loadChildren(data, dataStart-childrenSize, childrenSize); err != nil {
47 return nil, err
48 }
49 }
50
51 return m, nil
52 }
53
54 func (m *MemTree) loadChildren(data []byte, start, length int64) error {
55 nameData, err := readChildNameSizes(bytes.NewReader(data[start:start+length]), length)
56 if err != nil {
57 return err
58 }
59
60 ptrs := start - int64(len(nameData))*8
61 lastName := nameData[len(nameData)-1]
62 namesStart := ptrs - lastName[0] - lastName[1]
63 m.names = make([]string, len(nameData))
64 m.ptrs = make([][]byte, len(nameData))
65
66 for n, name := range nameData {
67 m.names[n] = unsafe.String(&data[namesStart+name[0]], name[1])
68 m.ptrs[n] = data[ptrs : ptrs+8]
69 ptrs += 8
70 }
71
72 return nil
73 }
74
75 // WriteTo will pass the Nodes data to the given io.Writer as a single
76 // byte-slice.
77 func (m *MemTree) WriteTo(w io.Writer) (int64, error) {
78 n, err := w.Write(m.data)
79
80 return int64(n), err
81 }
82
83 // Data returns the Nodes data.
84 func (m *MemTree) Data() []byte {
85 return m.data
86 }
87
88 // Child attempts to retrieve a child Node corresponding to the given name.
89 //
90 // If no child matches the given name, the returned error will be of type
91 // ChildNotFoundError.
92 func (m *MemTree) Child(name string) (*MemTree, error) {
93 pos, found := slices.BinarySearch(m.names, name)
94 if !found {
95 return nil, ChildNotFoundError(name)
96 }
97
98 ptr, err := readPointer(m.ptrs[pos])
99 if err != nil {
100 return nil, err
101 }
102
103 return OpenMemAt(m.tree, ptr)
104 }
105
106 func readPointer(ptr []byte) (int64, error) {
107 ler := byteio.LittleEndianReader{Reader: bytes.NewReader(ptr)}
108 p, _, err := ler.ReadInt64()
109
110 return p, err
111 }
112
113 // Children returns an iterator that loops through all of the child Nodes.
114 //
115 // Read errors will be expressed with a final Node of underlying type
116 // ChildrenError.
117 func (m *MemTree) Children() iter.Seq2[string, Node] {
118 return func(yield func(string, Node) bool) {
119 for n, name := range m.names {
120 ptr, err := readPointer(m.ptrs[n])
121 if err != nil {
122 yield(name, ChildrenError{err})
123
124 return
125 }
126
127 tree, err := OpenMemAt(m.tree, ptr)
128 if err != nil {
129 yield(name, ChildrenError{err})
130
131 return
132 }
133
134 if !yield(name, tree) {
135 break
136 }
137 }
138 }
139 }
140
141 // DataLen returns the length of the data stored on this Node.
142 func (m *MemTree) DataLen() int64 {
143 return int64(len(m.data))
144 }
145
146 // NumChildren returns the number of child Nodes that are attached to this Node.
147 func (m *MemTree) NumChildren() int {
148 return len(m.names)
149 }
150