github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/mpt/helpers.go (about)

     1  package mpt
     2  
     3  import "github.com/nspcc-dev/neo-go/pkg/util"
     4  
     5  // lcp returns the longest common prefix of a and b.
     6  // Note: it does no allocations.
     7  func lcp(a, b []byte) []byte {
     8  	if len(a) < len(b) {
     9  		return lcp(b, a)
    10  	}
    11  
    12  	var i int
    13  	for i = 0; i < len(b); i++ {
    14  		if a[i] != b[i] {
    15  			break
    16  		}
    17  	}
    18  
    19  	return a[:i]
    20  }
    21  
    22  func lcpMany(kv []keyValue) []byte {
    23  	if len(kv) == 1 {
    24  		return kv[0].key
    25  	}
    26  	p := lcp(kv[0].key, kv[1].key)
    27  	if len(p) == 0 {
    28  		return p
    29  	}
    30  	for i := range kv[2:] {
    31  		p = lcp(p, kv[2+i].key)
    32  	}
    33  	return p
    34  }
    35  
    36  // toNibbles mangles the path by splitting every byte into 2 containing low- and high- 4-byte part.
    37  func toNibbles(path []byte) []byte {
    38  	result := make([]byte, len(path)*2)
    39  	for i := range path {
    40  		result[i*2] = path[i] >> 4
    41  		result[i*2+1] = path[i] & 0x0F
    42  	}
    43  	return result
    44  }
    45  
    46  // strToNibbles mangles the path by splitting every byte into 2 containing low- and high- 4-byte part,
    47  // ignoring the first byte (prefix).
    48  func strToNibbles(path string) []byte {
    49  	result := make([]byte, (len(path)-1)*2)
    50  	for i := 0; i < len(path)-1; i++ {
    51  		result[i*2] = path[i+1] >> 4
    52  		result[i*2+1] = path[i+1] & 0x0F
    53  	}
    54  	return result
    55  }
    56  
    57  // fromNibbles performs an operation opposite to toNibbles and runs no path validity checks.
    58  func fromNibbles(path []byte) []byte {
    59  	result := make([]byte, len(path)/2)
    60  	for i := range result {
    61  		result[i] = path[2*i]<<4 + path[2*i+1]
    62  	}
    63  	return result
    64  }
    65  
    66  // GetChildrenPaths returns a set of paths to the node's children who are non-empty HashNodes
    67  // based on the node's path.
    68  func GetChildrenPaths(path []byte, node Node) map[util.Uint256][][]byte {
    69  	res := make(map[util.Uint256][][]byte)
    70  	switch n := node.(type) {
    71  	case *LeafNode, *HashNode, EmptyNode:
    72  		return nil
    73  	case *BranchNode:
    74  		for i, child := range n.Children {
    75  			if child.Type() == HashT {
    76  				cPath := make([]byte, len(path), len(path)+1)
    77  				copy(cPath, path)
    78  				if i != lastChild {
    79  					cPath = append(cPath, byte(i))
    80  				}
    81  				paths := res[child.Hash()]
    82  				paths = append(paths, cPath)
    83  				res[child.Hash()] = paths
    84  			}
    85  		}
    86  	case *ExtensionNode:
    87  		if n.next.Type() == HashT {
    88  			cPath := make([]byte, len(path)+len(n.key))
    89  			copy(cPath, path)
    90  			copy(cPath[len(path):], n.key)
    91  			res[n.next.Hash()] = [][]byte{cPath}
    92  		}
    93  	default:
    94  		panic("unknown Node type")
    95  	}
    96  	return res
    97  }