mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
trie: comments
This commit is contained in:
parent
312128384b
commit
0777a4e065
7 changed files with 46 additions and 18 deletions
|
|
@ -16,13 +16,20 @@
|
||||||
|
|
||||||
package trie
|
package trie
|
||||||
|
|
||||||
|
// This file implements a codec to convert between byte arrays
|
||||||
|
// and nibble (half-byte) arrays, where the latter are used for traversing the trie.
|
||||||
|
// Hence the "compact" encoded form for a key is as the byte array, which
|
||||||
|
// is (roughly) half the length of the decoded nibble array.
|
||||||
|
// Nibble arrays for keys that represent leaf nodes have a terminator flag (numerical 16) appended to the end.
|
||||||
|
// The compact encoded form uses Hex Prefix (HP) encoding, to encode the terminator status
|
||||||
|
// and whether the key length is even or odd in the first two bytes (note the original description uses only
|
||||||
|
// one byte, but that's inconvenient)
|
||||||
|
|
||||||
|
// Encode a slice of nibbles into a HP byte array
|
||||||
func CompactEncode(hexSlice []byte) []byte {
|
func CompactEncode(hexSlice []byte) []byte {
|
||||||
terminator := 0
|
terminator := 0
|
||||||
if hexSlice[len(hexSlice)-1] == 16 {
|
if hexSlice[len(hexSlice)-1] == 16 {
|
||||||
terminator = 1
|
terminator = 1
|
||||||
}
|
|
||||||
|
|
||||||
if terminator == 1 {
|
|
||||||
hexSlice = hexSlice[:len(hexSlice)-1]
|
hexSlice = hexSlice[:len(hexSlice)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,12 +49,16 @@ func CompactEncode(hexSlice []byte) []byte {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func CompactDecode(str []byte) []byte {
|
// Decode a HP encoded byte array into a nibble array
|
||||||
base := CompactHexDecode(str)
|
// with terminator flag if applicable
|
||||||
|
func CompactDecode(key []byte) []byte {
|
||||||
|
base := CompactHexDecode(key) // appends the terminator flag by default
|
||||||
|
if base[0] < 2 {
|
||||||
|
// remove the terminator flag if its not in the HP
|
||||||
base = base[:len(base)-1]
|
base = base[:len(base)-1]
|
||||||
if base[0] >= 2 {
|
|
||||||
base = append(base, 16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HP tells us if key length is even or odd
|
||||||
if base[0]%2 == 1 {
|
if base[0]%2 == 1 {
|
||||||
base = base[1:]
|
base = base[1:]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -57,10 +68,13 @@ func CompactDecode(str []byte) []byte {
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
func CompactHexDecode(str []byte) []byte {
|
// Decode a byte array into a nibble array.
|
||||||
l := len(str)*2 + 1
|
// Assumes the key coressponds to a terminator node (ie appends 16)
|
||||||
|
// CompactHexDecode is called immediately by the Get/Update/Remove functions.
|
||||||
|
func CompactHexDecode(key []byte) []byte {
|
||||||
|
l := len(key)*2 + 1
|
||||||
var nibbles = make([]byte, l)
|
var nibbles = make([]byte, l)
|
||||||
for i, b := range str {
|
for i, b := range key {
|
||||||
nibbles[i*2] = b / 16
|
nibbles[i*2] = b / 16
|
||||||
nibbles[i*2+1] = b % 16
|
nibbles[i*2+1] = b % 16
|
||||||
}
|
}
|
||||||
|
|
@ -68,12 +82,13 @@ func CompactHexDecode(str []byte) []byte {
|
||||||
return nibbles
|
return nibbles
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeCompact(key []byte) []byte {
|
// This is really a compact encoding of nibbles
|
||||||
l := len(key) / 2
|
// without hex-prefix
|
||||||
|
func DecodeCompact(nibbles []byte) []byte {
|
||||||
|
l := len(nibbles) / 2
|
||||||
var res = make([]byte, l)
|
var res = make([]byte, l)
|
||||||
for i := 0; i < l; i++ {
|
for i := 0; i < l; i++ {
|
||||||
v1, v0 := key[2*i], key[2*i+1]
|
res[i] = 16*nibbles[2*i] + nibbles[2*i+1]
|
||||||
res[i] = v1*16 + v0
|
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
package trie
|
package trie
|
||||||
|
|
||||||
|
// FullNode represents the main node type for a radix tree.
|
||||||
|
// The first 16 children are branches for each possible next letter in the key.
|
||||||
|
// The final slot is for a terminating node (a ValueNode), whose key ends at the FullNode.
|
||||||
type FullNode struct {
|
type FullNode struct {
|
||||||
trie *Trie
|
trie *Trie
|
||||||
nodes [17]Node
|
nodes [17]Node
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ package trie
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/common"
|
import "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
|
// HashNode represents a node that is too big to be a ShortNode.
|
||||||
|
// The actual node of interest (probably a FullNode) is rlp encoded and hashed,
|
||||||
|
// yielding the key of the HashNode. The key can be used to fetch the actual
|
||||||
|
// node from the database
|
||||||
type HashNode struct {
|
type HashNode struct {
|
||||||
key []byte
|
key []byte
|
||||||
trie *Trie
|
trie *Trie
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type Node interface {
|
||||||
Copy(*Trie) Node // All nodes, for now, return them self
|
Copy(*Trie) Node // All nodes, for now, return them self
|
||||||
Dirty() bool
|
Dirty() bool
|
||||||
fstring(string) string
|
fstring(string) string
|
||||||
Hash() interface{}
|
Hash() interface{} // only really a hash if size(node) > 32
|
||||||
RlpData() interface{}
|
RlpData() interface{}
|
||||||
setDirty(dirty bool)
|
setDirty(dirty bool)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,12 @@ package trie
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/common"
|
import "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
|
// ShortNode holds the nibble key for a ValueNode, HashNode, or FullNode.
|
||||||
|
// Note a ShortNode should never hold another ShortNode, as they can just
|
||||||
|
// be combined into one ShortNode
|
||||||
type ShortNode struct {
|
type ShortNode struct {
|
||||||
trie *Trie
|
trie *Trie
|
||||||
key []byte
|
key []byte // hex-prefixed bytes
|
||||||
value Node
|
value Node
|
||||||
dirty bool
|
dirty bool
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +33,6 @@ func NewShortNode(t *Trie, key []byte, value Node) *ShortNode {
|
||||||
}
|
}
|
||||||
func (self *ShortNode) Value() Node {
|
func (self *ShortNode) Value() Node {
|
||||||
self.value = self.trie.trans(self.value)
|
self.value = self.trie.trans(self.value)
|
||||||
|
|
||||||
return self.value
|
return self.value
|
||||||
}
|
}
|
||||||
func (self *ShortNode) Dirty() bool { return self.dirty }
|
func (self *ShortNode) Dirty() bool { return self.dirty }
|
||||||
|
|
@ -48,6 +50,7 @@ func (self *ShortNode) Hash() interface{} {
|
||||||
return self.trie.store(self)
|
return self.trie.store(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns nibbles
|
||||||
func (self *ShortNode) Key() []byte {
|
func (self *ShortNode) Key() []byte {
|
||||||
return CompactDecode(self.key)
|
return CompactDecode(self.key)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,7 @@ func (self *Trie) mknode(value *common.Value) Node {
|
||||||
return NewValueNode(self, value.Bytes())
|
return NewValueNode(self, value.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolve HashNodes by fetching from the db
|
||||||
func (self *Trie) trans(node Node) Node {
|
func (self *Trie) trans(node Node) Node {
|
||||||
switch node := node.(type) {
|
switch node := node.(type) {
|
||||||
case *HashNode:
|
case *HashNode:
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package trie
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/common"
|
import "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
|
// ValueNode represents a leaf node, a terminal point in the tree.
|
||||||
|
// It is implied that if you found the ValueNode, then you know its key
|
||||||
type ValueNode struct {
|
type ValueNode struct {
|
||||||
trie *Trie
|
trie *Trie
|
||||||
data []byte
|
data []byte
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue