trie: simplify StackTrie implementation (#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.
This commit is contained in:
parent
c10a0a62c3
commit
86fe359a56
@ -56,9 +56,8 @@ func returnToPool(st *StackTrie) {
|
|||||||
type StackTrie struct {
|
type StackTrie struct {
|
||||||
nodeType uint8 // node type (as in branch, ext, leaf)
|
nodeType uint8 // node type (as in branch, ext, leaf)
|
||||||
val []byte // value contained by this node if it's a leaf
|
val []byte // value contained by this node if it's a leaf
|
||||||
key []byte // key chunk covered by this (full|ext) node
|
key []byte // key chunk covered by this (leaf|ext) node
|
||||||
keyOffset int // offset of the key chunk inside a full key
|
children [16]*StackTrie // list of children (for branch and exts)
|
||||||
children [16]*StackTrie // list of children (for fullnodes and exts)
|
|
||||||
db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
|
db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,12 +92,10 @@ func (st *StackTrie) MarshalBinary() (data []byte, err error) {
|
|||||||
Nodetype uint8
|
Nodetype uint8
|
||||||
Val []byte
|
Val []byte
|
||||||
Key []byte
|
Key []byte
|
||||||
KeyOffset uint8
|
|
||||||
}{
|
}{
|
||||||
st.nodeType,
|
st.nodeType,
|
||||||
st.val,
|
st.val,
|
||||||
st.key,
|
st.key,
|
||||||
uint8(st.keyOffset),
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -129,13 +126,11 @@ func (st *StackTrie) unmarshalBinary(r io.Reader) error {
|
|||||||
Nodetype uint8
|
Nodetype uint8
|
||||||
Val []byte
|
Val []byte
|
||||||
Key []byte
|
Key []byte
|
||||||
KeyOffset uint8
|
|
||||||
}
|
}
|
||||||
gob.NewDecoder(r).Decode(&dec)
|
gob.NewDecoder(r).Decode(&dec)
|
||||||
st.nodeType = dec.Nodetype
|
st.nodeType = dec.Nodetype
|
||||||
st.val = dec.Val
|
st.val = dec.Val
|
||||||
st.key = dec.Key
|
st.key = dec.Key
|
||||||
st.keyOffset = int(dec.KeyOffset)
|
|
||||||
|
|
||||||
var hasChild = make([]byte, 1)
|
var hasChild = make([]byte, 1)
|
||||||
for i := range st.children {
|
for i := range st.children {
|
||||||
@ -160,20 +155,18 @@ func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLeaf(ko int, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
|
func newLeaf(key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
|
||||||
st := stackTrieFromPool(db)
|
st := stackTrieFromPool(db)
|
||||||
st.nodeType = leafNode
|
st.nodeType = leafNode
|
||||||
st.keyOffset = ko
|
st.key = append(st.key, key...)
|
||||||
st.key = append(st.key, key[ko:]...)
|
|
||||||
st.val = val
|
st.val = val
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExt(ko int, key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
|
func newExt(key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
|
||||||
st := stackTrieFromPool(db)
|
st := stackTrieFromPool(db)
|
||||||
st.nodeType = extNode
|
st.nodeType = extNode
|
||||||
st.keyOffset = ko
|
st.key = append(st.key, key...)
|
||||||
st.key = append(st.key, key[ko:]...)
|
|
||||||
st.children[0] = child
|
st.children[0] = child
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
@ -211,17 +204,18 @@ func (st *StackTrie) Reset() {
|
|||||||
st.children[i] = nil
|
st.children[i] = nil
|
||||||
}
|
}
|
||||||
st.nodeType = emptyNode
|
st.nodeType = emptyNode
|
||||||
st.keyOffset = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function that, given a full key, determines the index
|
// Helper function that, given a full key, determines the index
|
||||||
// at which the chunk pointed by st.keyOffset is different from
|
// at which the chunk pointed by st.keyOffset is different from
|
||||||
// the same chunk in the full key.
|
// the same chunk in the full key.
|
||||||
func (st *StackTrie) getDiffIndex(key []byte) int {
|
func (st *StackTrie) getDiffIndex(key []byte) int {
|
||||||
diffindex := 0
|
for idx, nibble := range st.key {
|
||||||
for ; diffindex < len(st.key) && st.key[diffindex] == key[st.keyOffset+diffindex]; diffindex++ {
|
if nibble != key[idx] {
|
||||||
|
return idx
|
||||||
}
|
}
|
||||||
return diffindex
|
}
|
||||||
|
return len(st.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to that inserts a (key, value) pair into
|
// Helper function to that inserts a (key, value) pair into
|
||||||
@ -229,7 +223,7 @@ func (st *StackTrie) getDiffIndex(key []byte) int {
|
|||||||
func (st *StackTrie) insert(key, value []byte) {
|
func (st *StackTrie) insert(key, value []byte) {
|
||||||
switch st.nodeType {
|
switch st.nodeType {
|
||||||
case branchNode: /* Branch */
|
case branchNode: /* Branch */
|
||||||
idx := int(key[st.keyOffset])
|
idx := int(key[0])
|
||||||
// Unresolve elder siblings
|
// Unresolve elder siblings
|
||||||
for i := idx - 1; i >= 0; i-- {
|
for i := idx - 1; i >= 0; i-- {
|
||||||
if st.children[i] != nil {
|
if st.children[i] != nil {
|
||||||
@ -241,10 +235,10 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
}
|
}
|
||||||
// Add new child
|
// Add new child
|
||||||
if st.children[idx] == nil {
|
if st.children[idx] == nil {
|
||||||
st.children[idx] = stackTrieFromPool(st.db)
|
st.children[idx] = newLeaf(key[1:], value, st.db)
|
||||||
st.children[idx].keyOffset = st.keyOffset + 1
|
} else {
|
||||||
|
st.children[idx].insert(key[1:], value)
|
||||||
}
|
}
|
||||||
st.children[idx].insert(key, value)
|
|
||||||
case extNode: /* Ext */
|
case extNode: /* Ext */
|
||||||
// Compare both key chunks and see where they differ
|
// Compare both key chunks and see where they differ
|
||||||
diffidx := st.getDiffIndex(key)
|
diffidx := st.getDiffIndex(key)
|
||||||
@ -257,7 +251,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
if diffidx == len(st.key) {
|
if diffidx == len(st.key) {
|
||||||
// Ext key and key segment are identical, recurse into
|
// Ext key and key segment are identical, recurse into
|
||||||
// the child node.
|
// the child node.
|
||||||
st.children[0].insert(key, value)
|
st.children[0].insert(key[diffidx:], value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Save the original part. Depending if the break is
|
// Save the original part. Depending if the break is
|
||||||
@ -266,7 +260,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
// node directly.
|
// node directly.
|
||||||
var n *StackTrie
|
var n *StackTrie
|
||||||
if diffidx < len(st.key)-1 {
|
if diffidx < len(st.key)-1 {
|
||||||
n = newExt(diffidx+1, st.key, st.children[0], st.db)
|
n = newExt(st.key[diffidx+1:], st.children[0], st.db)
|
||||||
} else {
|
} else {
|
||||||
// Break on the last byte, no need to insert
|
// Break on the last byte, no need to insert
|
||||||
// an extension node: reuse the current node
|
// an extension node: reuse the current node
|
||||||
@ -288,15 +282,14 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
// node.
|
// node.
|
||||||
st.children[0] = stackTrieFromPool(st.db)
|
st.children[0] = stackTrieFromPool(st.db)
|
||||||
st.children[0].nodeType = branchNode
|
st.children[0].nodeType = branchNode
|
||||||
st.children[0].keyOffset = st.keyOffset + diffidx
|
|
||||||
p = st.children[0]
|
p = st.children[0]
|
||||||
}
|
}
|
||||||
// Create a leaf for the inserted part
|
// Create a leaf for the inserted part
|
||||||
o := newLeaf(st.keyOffset+diffidx+1, key, value, st.db)
|
o := newLeaf(key[diffidx+1:], value, st.db)
|
||||||
|
|
||||||
// Insert both child leaves where they belong:
|
// Insert both child leaves where they belong:
|
||||||
origIdx := st.key[diffidx]
|
origIdx := st.key[diffidx]
|
||||||
newIdx := key[diffidx+st.keyOffset]
|
newIdx := key[diffidx]
|
||||||
p.children[origIdx] = n
|
p.children[origIdx] = n
|
||||||
p.children[newIdx] = o
|
p.children[newIdx] = o
|
||||||
st.key = st.key[:diffidx]
|
st.key = st.key[:diffidx]
|
||||||
@ -330,7 +323,6 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
st.nodeType = extNode
|
st.nodeType = extNode
|
||||||
st.children[0] = NewStackTrie(st.db)
|
st.children[0] = NewStackTrie(st.db)
|
||||||
st.children[0].nodeType = branchNode
|
st.children[0].nodeType = branchNode
|
||||||
st.children[0].keyOffset = st.keyOffset + diffidx
|
|
||||||
p = st.children[0]
|
p = st.children[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,11 +331,11 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
// The child leave will be hashed directly in order to
|
// The child leave will be hashed directly in order to
|
||||||
// free up some memory.
|
// free up some memory.
|
||||||
origIdx := st.key[diffidx]
|
origIdx := st.key[diffidx]
|
||||||
p.children[origIdx] = newLeaf(diffidx+1, st.key, st.val, st.db)
|
p.children[origIdx] = newLeaf(st.key[diffidx+1:], st.val, st.db)
|
||||||
p.children[origIdx].hash()
|
p.children[origIdx].hash()
|
||||||
|
|
||||||
newIdx := key[diffidx+st.keyOffset]
|
newIdx := key[diffidx]
|
||||||
p.children[newIdx] = newLeaf(p.keyOffset+1, key, value, st.db)
|
p.children[newIdx] = newLeaf(key[diffidx+1:], value, st.db)
|
||||||
|
|
||||||
// Finally, cut off the key part that has been passed
|
// Finally, cut off the key part that has been passed
|
||||||
// over to the children.
|
// over to the children.
|
||||||
@ -351,7 +343,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||||||
st.val = nil
|
st.val = nil
|
||||||
case emptyNode: /* Empty */
|
case emptyNode: /* Empty */
|
||||||
st.nodeType = leafNode
|
st.nodeType = leafNode
|
||||||
st.key = key[st.keyOffset:]
|
st.key = key
|
||||||
st.val = value
|
st.val = value
|
||||||
case hashedNode:
|
case hashedNode:
|
||||||
panic("trying to insert into hash")
|
panic("trying to insert into hash")
|
||||||
|
Loading…
Reference in New Issue
Block a user