f80680187e
It couldn't be done previously with two maps and mixed storage, but now all of the storage changes are located in a single map, so it's trivial to do exact slice allocations and avoid string->[]byte conversions.
97 lines
2.2 KiB
Go
97 lines
2.2 KiB
Go
package mpt
|
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
// lcp returns longest common prefix of a and b.
|
|
// Note: it does no allocations.
|
|
func lcp(a, b []byte) []byte {
|
|
if len(a) < len(b) {
|
|
return lcp(b, a)
|
|
}
|
|
|
|
var i int
|
|
for i = 0; i < len(b); i++ {
|
|
if a[i] != b[i] {
|
|
break
|
|
}
|
|
}
|
|
|
|
return a[:i]
|
|
}
|
|
|
|
func lcpMany(kv []keyValue) []byte {
|
|
if len(kv) == 1 {
|
|
return kv[0].key
|
|
}
|
|
p := lcp(kv[0].key, kv[1].key)
|
|
if len(p) == 0 {
|
|
return p
|
|
}
|
|
for i := range kv[2:] {
|
|
p = lcp(p, kv[2+i].key)
|
|
}
|
|
return p
|
|
}
|
|
|
|
// toNibbles mangles path by splitting every byte into 2 containing low- and high- 4-byte part.
|
|
func toNibbles(path []byte) []byte {
|
|
result := make([]byte, len(path)*2)
|
|
for i := range path {
|
|
result[i*2] = path[i] >> 4
|
|
result[i*2+1] = path[i] & 0x0F
|
|
}
|
|
return result
|
|
}
|
|
|
|
// strToNibbles mangles path by splitting every byte into 2 containing low- and high- 4-byte part,
|
|
// ignoring the first byte (prefix).
|
|
func strToNibbles(path string) []byte {
|
|
result := make([]byte, (len(path)-1)*2)
|
|
for i := 0; i < len(path)-1; i++ {
|
|
result[i*2] = path[i+1] >> 4
|
|
result[i*2+1] = path[i+1] & 0x0F
|
|
}
|
|
return result
|
|
}
|
|
|
|
// fromNibbles performs operation opposite to toNibbles and does no path validity checks.
|
|
func fromNibbles(path []byte) []byte {
|
|
result := make([]byte, len(path)/2)
|
|
for i := range result {
|
|
result[i] = path[2*i]<<4 + path[2*i+1]
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetChildrenPaths returns a set of paths to node's children who are non-empty HashNodes
|
|
// based on the node's path.
|
|
func GetChildrenPaths(path []byte, node Node) map[util.Uint256][][]byte {
|
|
res := make(map[util.Uint256][][]byte)
|
|
switch n := node.(type) {
|
|
case *LeafNode, *HashNode, EmptyNode:
|
|
return nil
|
|
case *BranchNode:
|
|
for i, child := range n.Children {
|
|
if child.Type() == HashT {
|
|
cPath := make([]byte, len(path), len(path)+1)
|
|
copy(cPath, path)
|
|
if i != lastChild {
|
|
cPath = append(cPath, byte(i))
|
|
}
|
|
paths := res[child.Hash()]
|
|
paths = append(paths, cPath)
|
|
res[child.Hash()] = paths
|
|
}
|
|
}
|
|
case *ExtensionNode:
|
|
if n.next.Type() == HashT {
|
|
cPath := make([]byte, len(path)+len(n.key))
|
|
copy(cPath, path)
|
|
copy(cPath[len(path):], n.key)
|
|
res[n.next.Hash()] = [][]byte{cPath}
|
|
}
|
|
default:
|
|
panic("unknown Node type")
|
|
}
|
|
return res
|
|
}
|