mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
trie: polish the code
This commit is contained in:
parent
bfc3aaee4c
commit
140be4e19e
5 changed files with 63 additions and 39 deletions
|
|
@ -42,6 +42,15 @@ func (bp *bytesPool) Get() []byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetWithSize returns a slice with specified byte slice size.
|
||||||
|
func (bp *bytesPool) GetWithSize(s int) []byte {
|
||||||
|
b := bp.Get()
|
||||||
|
if cap(b) < s {
|
||||||
|
return make([]byte, s)
|
||||||
|
}
|
||||||
|
return b[:s]
|
||||||
|
}
|
||||||
|
|
||||||
// Put returns a slice to the pool. Safe for concurrent use. This method
|
// Put returns a slice to the pool. Safe for concurrent use. This method
|
||||||
// will ignore slices that are too small or too large (>3x the cap)
|
// will ignore slices that are too small or too large (>3x the cap)
|
||||||
func (bp *bytesPool) Put(b []byte) {
|
func (bp *bytesPool) Put(b []byte) {
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,13 @@ func keybytesToHex(str []byte) []byte {
|
||||||
// writeHexKey writes the hexkey into the given slice.
|
// writeHexKey writes the hexkey into the given slice.
|
||||||
// OBS! This method omits the termination flag.
|
// OBS! This method omits the termination flag.
|
||||||
// OBS! The dst slice must be at least 2x as large as the key
|
// OBS! The dst slice must be at least 2x as large as the key
|
||||||
func writeHexKey(dst []byte, key []byte) {
|
func writeHexKey(dst []byte, key []byte) []byte {
|
||||||
_ = dst[2*len(key)-1]
|
_ = dst[2*len(key)-1]
|
||||||
for i, b := range key {
|
for i, b := range key {
|
||||||
dst[i*2] = b / 16
|
dst[i*2] = b / 16
|
||||||
dst[i*2+1] = b % 16
|
dst[i*2+1] = b % 16
|
||||||
}
|
}
|
||||||
|
return dst[:2*len(key)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// hexToKeybytes turns hex nibbles into key bytes.
|
// hexToKeybytes turns hex nibbles into key bytes.
|
||||||
|
|
|
||||||
20
trie/node.go
20
trie/node.go
|
|
@ -46,17 +46,23 @@ type (
|
||||||
hashNode []byte
|
hashNode []byte
|
||||||
valueNode []byte
|
valueNode []byte
|
||||||
|
|
||||||
//fullnodeEncoder is a type used exclusively for encoding. Briefly instantiating
|
// fullnodeEncoder is a type used exclusively for encoding fullNode.
|
||||||
// a fullnodeEncoder and initializing with existing slices is less memory
|
// Briefly instantiating a fullnodeEncoder and initializing with
|
||||||
// intense than using the fullNode type.
|
// existing slices is less memory intense than using the fullNode type.
|
||||||
fullnodeEncoder struct {
|
fullnodeEncoder struct {
|
||||||
Children [17][]byte
|
Children [17][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
//shortNodeEncoder is a type used exclusively for encoding. Briefly instantiating
|
// extNodeEncoder is a type used exclusively for encoding extension node.
|
||||||
// a shortNodeEncoder and initializing with existing slices is less memory
|
// Briefly instantiating a extNodeEncoder and initializing with existing
|
||||||
// intense than using the shortNode type.
|
// slices is less memory intense than using the shortNode type.
|
||||||
shortNodeEncoder struct {
|
extNodeEncoder struct {
|
||||||
|
Key []byte
|
||||||
|
Val []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// leafNodeEncoder is a type used exclusively for encoding leaf node.
|
||||||
|
leafNodeEncoder struct {
|
||||||
Key []byte
|
Key []byte
|
||||||
Val []byte
|
Val []byte
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (n *shortNode) encode(w rlp.EncoderBuffer) {
|
||||||
w.ListEnd(offset)
|
w.ListEnd(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *shortNodeEncoder) encode(w rlp.EncoderBuffer) {
|
func (n *extNodeEncoder) encode(w rlp.EncoderBuffer) {
|
||||||
offset := w.List()
|
offset := w.List()
|
||||||
w.WriteBytes(n.Key)
|
w.WriteBytes(n.Key)
|
||||||
|
|
||||||
|
|
@ -79,6 +79,19 @@ func (n *shortNodeEncoder) encode(w rlp.EncoderBuffer) {
|
||||||
w.ListEnd(offset)
|
w.ListEnd(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *leafNodeEncoder) encode(w rlp.EncoderBuffer) {
|
||||||
|
offset := w.List()
|
||||||
|
|
||||||
|
// Encode the key to Compact format. The assumption is held
|
||||||
|
// that it's safe to modify the key in place.
|
||||||
|
n.Key = append(n.Key, 16) // termination flag
|
||||||
|
n.Key = hexToCompactInPlace(n.Key)
|
||||||
|
|
||||||
|
w.WriteBytes(n.Key) // Compact format key
|
||||||
|
w.WriteBytes(n.Val) // Value node, must be non-nil
|
||||||
|
w.ListEnd(offset)
|
||||||
|
}
|
||||||
|
|
||||||
func (n hashNode) encode(w rlp.EncoderBuffer) {
|
func (n hashNode) encode(w rlp.EncoderBuffer) {
|
||||||
w.WriteBytes(n)
|
w.WriteBytes(n)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,17 @@ func NewStackTrie(onTrieNode OnTrieNode) *StackTrie {
|
||||||
root: stPool.Get().(*stNode),
|
root: stPool.Get().(*stNode),
|
||||||
h: newHasher(false),
|
h: newHasher(false),
|
||||||
onTrieNode: onTrieNode,
|
onTrieNode: onTrieNode,
|
||||||
kBuf: make([]byte, 0, 64),
|
kBuf: make([]byte, 64),
|
||||||
pBuf: make([]byte, 0, 32),
|
pBuf: make([]byte, 64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *StackTrie) grow(key []byte) {
|
||||||
|
if cap(t.kBuf) < 2*len(key) {
|
||||||
|
t.kBuf = make([]byte, 2*len(key))
|
||||||
|
}
|
||||||
|
if cap(t.pBuf) < len(key) {
|
||||||
|
t.pBuf = make([]byte, 2*len(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,16 +78,8 @@ func (t *StackTrie) Update(key, value []byte) error {
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
return errors.New("trying to insert empty (deletion)")
|
return errors.New("trying to insert empty (deletion)")
|
||||||
}
|
}
|
||||||
var k []byte
|
t.grow(key)
|
||||||
{ // Need to expand the 'key' into hex-form. We use the dedicated buf for that.
|
k := writeHexKey(t.kBuf, key)
|
||||||
if cap(t.kBuf) < 2*len(key) { // realloc to ensure sufficient cap
|
|
||||||
t.kBuf = make([]byte, 2*len(key))
|
|
||||||
}
|
|
||||||
// resize to ensure correct size
|
|
||||||
t.kBuf = t.kBuf[:2*len(key)]
|
|
||||||
writeHexKey(t.kBuf, key)
|
|
||||||
k = t.kBuf
|
|
||||||
}
|
|
||||||
if bytes.Compare(t.last, k) >= 0 {
|
if bytes.Compare(t.last, k) >= 0 {
|
||||||
return errors.New("non-ascending key order")
|
return errors.New("non-ascending key order")
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +172,7 @@ func (n *stNode) getDiffIndex(key []byte) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to that inserts a (key, value) pair into the trie.
|
// Helper function to that inserts a (key, value) pair into the trie.
|
||||||
|
//
|
||||||
// - The key is not retained by this method, but always copied if needed.
|
// - The key is not retained by this method, but always copied if needed.
|
||||||
// - The value is retained by this method, as long as the leaf that it represents
|
// - The value is retained by this method, as long as the leaf that it represents
|
||||||
// remains unhashed. However: it is never modified.
|
// remains unhashed. However: it is never modified.
|
||||||
|
|
@ -306,7 +308,7 @@ func (t *StackTrie) insert(st *stNode, key, value []byte, path []byte) {
|
||||||
|
|
||||||
case emptyNode: /* Empty */
|
case emptyNode: /* Empty */
|
||||||
st.typ = leafNode
|
st.typ = leafNode
|
||||||
st.key = append(st.key, key...)
|
st.key = append(st.key, key...) // deep-copy the key as it's volatile
|
||||||
st.val = value
|
st.val = value
|
||||||
|
|
||||||
case hashedNode:
|
case hashedNode:
|
||||||
|
|
@ -364,7 +366,7 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
t.hash(st.children[0], append(path, st.key...))
|
t.hash(st.children[0], append(path, st.key...))
|
||||||
|
|
||||||
// encode the extension node
|
// encode the extension node
|
||||||
n := shortNodeEncoder{
|
n := extNodeEncoder{
|
||||||
Key: hexToCompactInPlace(st.key),
|
Key: hexToCompactInPlace(st.key),
|
||||||
Val: st.children[0].val,
|
Val: st.children[0].val,
|
||||||
}
|
}
|
||||||
|
|
@ -375,14 +377,11 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
st.children[0] = nil
|
st.children[0] = nil
|
||||||
|
|
||||||
case leafNode:
|
case leafNode:
|
||||||
st.key = append(st.key, byte(16))
|
n := leafNodeEncoder{
|
||||||
{
|
Key: st.key,
|
||||||
w := t.h.encbuf
|
Val: st.val,
|
||||||
offset := w.List()
|
|
||||||
w.WriteBytes(hexToCompactInPlace(st.key))
|
|
||||||
w.WriteBytes(st.val)
|
|
||||||
w.ListEnd(offset)
|
|
||||||
}
|
}
|
||||||
|
n.encode(t.h.encbuf)
|
||||||
blob = t.h.encodedBytes()
|
blob = t.h.encodedBytes()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -397,18 +396,14 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
// Skip committing the non-root node if the size is smaller than 32 bytes
|
// Skip committing the non-root node if the size is smaller than 32 bytes
|
||||||
// as tiny nodes are always embedded in their parent except root node.
|
// as tiny nodes are always embedded in their parent except root node.
|
||||||
if len(blob) < 32 && len(path) > 0 {
|
if len(blob) < 32 && len(path) > 0 {
|
||||||
val := bPool.Get()
|
st.val = bPool.GetWithSize(len(blob))
|
||||||
val = val[:len(blob)]
|
copy(st.val, blob)
|
||||||
copy(val, blob)
|
|
||||||
st.val = val
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
||||||
// input values.
|
// input values.
|
||||||
val := bPool.Get()
|
st.val = bPool.GetWithSize(32)
|
||||||
val = val[:32]
|
t.h.hashDataTo(st.val, blob)
|
||||||
t.h.hashDataTo(val, blob)
|
|
||||||
st.val = val
|
|
||||||
|
|
||||||
// Invoke the callback it's provided. Notably, the path and blob slices are
|
// Invoke the callback it's provided. Notably, the path and blob slices are
|
||||||
// volatile, please deep-copy the slices in callback if the contents need
|
// volatile, please deep-copy the slices in callback if the contents need
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue