Skip to content

Commit 86fe359

Browse files
authored
trie: simplify StackTrie implementation (ethereum#23950)
Trim the search key from head as it's being pushed deeper into the trie. Previously the search key was never modified but each node kept information how to slice and compare it in keyOffset. Now the keyOffset is not needed as this information is included in the slice of the search key. This way the keyOffset can be removed and key manipulation simplified.
1 parent c10a0a6 commit 86fe359

File tree

1 file changed

+32
-40
lines changed

1 file changed

+32
-40
lines changed

trie/stacktrie.go

+32-40
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,11 @@ func returnToPool(st *StackTrie) {
5454
// in order. Once it determines that a subtree will no longer be inserted
5555
// into, it will hash it and free up the memory it uses.
5656
type StackTrie struct {
57-
nodeType uint8 // node type (as in branch, ext, leaf)
58-
val []byte // value contained by this node if it's a leaf
59-
key []byte // key chunk covered by this (full|ext) node
60-
keyOffset int // offset of the key chunk inside a full key
61-
children [16]*StackTrie // list of children (for fullnodes and exts)
62-
db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
57+
nodeType uint8 // node type (as in branch, ext, leaf)
58+
val []byte // value contained by this node if it's a leaf
59+
key []byte // key chunk covered by this (leaf|ext) node
60+
children [16]*StackTrie // list of children (for branch and exts)
61+
db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
6362
}
6463

6564
// NewStackTrie allocates and initializes an empty trie.
@@ -90,15 +89,13 @@ func (st *StackTrie) MarshalBinary() (data []byte, err error) {
9089
w = bufio.NewWriter(&b)
9190
)
9291
if err := gob.NewEncoder(w).Encode(struct {
93-
Nodetype uint8
94-
Val []byte
95-
Key []byte
96-
KeyOffset uint8
92+
Nodetype uint8
93+
Val []byte
94+
Key []byte
9795
}{
9896
st.nodeType,
9997
st.val,
10098
st.key,
101-
uint8(st.keyOffset),
10299
}); err != nil {
103100
return nil, err
104101
}
@@ -126,16 +123,14 @@ func (st *StackTrie) UnmarshalBinary(data []byte) error {
126123

127124
func (st *StackTrie) unmarshalBinary(r io.Reader) error {
128125
var dec struct {
129-
Nodetype uint8
130-
Val []byte
131-
Key []byte
132-
KeyOffset uint8
126+
Nodetype uint8
127+
Val []byte
128+
Key []byte
133129
}
134130
gob.NewDecoder(r).Decode(&dec)
135131
st.nodeType = dec.Nodetype
136132
st.val = dec.Val
137133
st.key = dec.Key
138-
st.keyOffset = int(dec.KeyOffset)
139134

140135
var hasChild = make([]byte, 1)
141136
for i := range st.children {
@@ -160,20 +155,18 @@ func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
160155
}
161156
}
162157

163-
func newLeaf(ko int, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
158+
func newLeaf(key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
164159
st := stackTrieFromPool(db)
165160
st.nodeType = leafNode
166-
st.keyOffset = ko
167-
st.key = append(st.key, key[ko:]...)
161+
st.key = append(st.key, key...)
168162
st.val = val
169163
return st
170164
}
171165

172-
func newExt(ko int, key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
166+
func newExt(key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
173167
st := stackTrieFromPool(db)
174168
st.nodeType = extNode
175-
st.keyOffset = ko
176-
st.key = append(st.key, key[ko:]...)
169+
st.key = append(st.key, key...)
177170
st.children[0] = child
178171
return st
179172
}
@@ -211,25 +204,26 @@ func (st *StackTrie) Reset() {
211204
st.children[i] = nil
212205
}
213206
st.nodeType = emptyNode
214-
st.keyOffset = 0
215207
}
216208

217209
// Helper function that, given a full key, determines the index
218210
// at which the chunk pointed by st.keyOffset is different from
219211
// the same chunk in the full key.
220212
func (st *StackTrie) getDiffIndex(key []byte) int {
221-
diffindex := 0
222-
for ; diffindex < len(st.key) && st.key[diffindex] == key[st.keyOffset+diffindex]; diffindex++ {
213+
for idx, nibble := range st.key {
214+
if nibble != key[idx] {
215+
return idx
216+
}
223217
}
224-
return diffindex
218+
return len(st.key)
225219
}
226220

227221
// Helper function to that inserts a (key, value) pair into
228222
// the trie.
229223
func (st *StackTrie) insert(key, value []byte) {
230224
switch st.nodeType {
231225
case branchNode: /* Branch */
232-
idx := int(key[st.keyOffset])
226+
idx := int(key[0])
233227
// Unresolve elder siblings
234228
for i := idx - 1; i >= 0; i-- {
235229
if st.children[i] != nil {
@@ -241,10 +235,10 @@ func (st *StackTrie) insert(key, value []byte) {
241235
}
242236
// Add new child
243237
if st.children[idx] == nil {
244-
st.children[idx] = stackTrieFromPool(st.db)
245-
st.children[idx].keyOffset = st.keyOffset + 1
238+
st.children[idx] = newLeaf(key[1:], value, st.db)
239+
} else {
240+
st.children[idx].insert(key[1:], value)
246241
}
247-
st.children[idx].insert(key, value)
248242
case extNode: /* Ext */
249243
// Compare both key chunks and see where they differ
250244
diffidx := st.getDiffIndex(key)
@@ -257,7 +251,7 @@ func (st *StackTrie) insert(key, value []byte) {
257251
if diffidx == len(st.key) {
258252
// Ext key and key segment are identical, recurse into
259253
// the child node.
260-
st.children[0].insert(key, value)
254+
st.children[0].insert(key[diffidx:], value)
261255
return
262256
}
263257
// Save the original part. Depending if the break is
@@ -266,7 +260,7 @@ func (st *StackTrie) insert(key, value []byte) {
266260
// node directly.
267261
var n *StackTrie
268262
if diffidx < len(st.key)-1 {
269-
n = newExt(diffidx+1, st.key, st.children[0], st.db)
263+
n = newExt(st.key[diffidx+1:], st.children[0], st.db)
270264
} else {
271265
// Break on the last byte, no need to insert
272266
// an extension node: reuse the current node
@@ -288,15 +282,14 @@ func (st *StackTrie) insert(key, value []byte) {
288282
// node.
289283
st.children[0] = stackTrieFromPool(st.db)
290284
st.children[0].nodeType = branchNode
291-
st.children[0].keyOffset = st.keyOffset + diffidx
292285
p = st.children[0]
293286
}
294287
// Create a leaf for the inserted part
295-
o := newLeaf(st.keyOffset+diffidx+1, key, value, st.db)
288+
o := newLeaf(key[diffidx+1:], value, st.db)
296289

297290
// Insert both child leaves where they belong:
298291
origIdx := st.key[diffidx]
299-
newIdx := key[diffidx+st.keyOffset]
292+
newIdx := key[diffidx]
300293
p.children[origIdx] = n
301294
p.children[newIdx] = o
302295
st.key = st.key[:diffidx]
@@ -330,7 +323,6 @@ func (st *StackTrie) insert(key, value []byte) {
330323
st.nodeType = extNode
331324
st.children[0] = NewStackTrie(st.db)
332325
st.children[0].nodeType = branchNode
333-
st.children[0].keyOffset = st.keyOffset + diffidx
334326
p = st.children[0]
335327
}
336328

@@ -339,19 +331,19 @@ func (st *StackTrie) insert(key, value []byte) {
339331
// The child leave will be hashed directly in order to
340332
// free up some memory.
341333
origIdx := st.key[diffidx]
342-
p.children[origIdx] = newLeaf(diffidx+1, st.key, st.val, st.db)
334+
p.children[origIdx] = newLeaf(st.key[diffidx+1:], st.val, st.db)
343335
p.children[origIdx].hash()
344336

345-
newIdx := key[diffidx+st.keyOffset]
346-
p.children[newIdx] = newLeaf(p.keyOffset+1, key, value, st.db)
337+
newIdx := key[diffidx]
338+
p.children[newIdx] = newLeaf(key[diffidx+1:], value, st.db)
347339

348340
// Finally, cut off the key part that has been passed
349341
// over to the children.
350342
st.key = st.key[:diffidx]
351343
st.val = nil
352344
case emptyNode: /* Empty */
353345
st.nodeType = leafNode
354-
st.key = key[st.keyOffset:]
346+
st.key = key
355347
st.val = value
356348
case hashedNode:
357349
panic("trying to insert into hash")

0 commit comments

Comments
 (0)