2020-05-22 07:37:07 +00:00
|
|
|
package mpt
|
|
|
|
|
2024-08-24 10:07:53 +00:00
|
|
|
import (
|
|
|
|
"slices"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
)
|
2021-07-30 13:57:42 +00:00
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// lcp returns the longest common prefix of a and b.
|
2020-05-22 07:37:07 +00:00
|
|
|
// 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]
|
|
|
|
}
|
|
|
|
|
2020-12-26 10:27:59 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// toNibbles mangles the path by splitting every byte into 2 containing low- and high- 4-byte part.
|
2020-05-22 07:37:07 +00:00
|
|
|
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
|
|
|
|
}
|
2021-07-29 15:00:07 +00:00
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// strToNibbles mangles the path by splitting every byte into 2 containing low- and high- 4-byte part,
|
2022-02-17 09:11:59 +00:00
|
|
|
// ignoring the first byte (prefix).
|
|
|
|
func strToNibbles(path string) []byte {
|
|
|
|
result := make([]byte, (len(path)-1)*2)
|
2024-08-30 18:41:02 +00:00
|
|
|
for i := range len(path) - 1 {
|
2022-02-17 09:11:59 +00:00
|
|
|
result[i*2] = path[i+1] >> 4
|
|
|
|
result[i*2+1] = path[i+1] & 0x0F
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// fromNibbles performs an operation opposite to toNibbles and runs no path validity checks.
|
2021-07-29 15:00:07 +00:00
|
|
|
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
|
|
|
|
}
|
2021-07-30 13:57:42 +00:00
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// GetChildrenPaths returns a set of paths to the node's children who are non-empty HashNodes
|
2021-07-30 13:57:42 +00:00
|
|
|
// 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 {
|
2024-08-24 10:07:53 +00:00
|
|
|
cPath := slices.Concat(path, n.key)
|
2021-07-30 13:57:42 +00:00
|
|
|
res[n.next.Hash()] = [][]byte{cPath}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
panic("unknown Node type")
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|