github.com/lbryio/lbcd@v0.22.119/claimtrie/merkletrie/collapsedtrie.go (about)

     1  package merkletrie
     2  
     3  import (
     4  	"github.com/lbryio/lbcd/chaincfg/chainhash"
     5  )
     6  
     7  type KeyType []byte
     8  
     9  type collapsedVertex struct {
    10  	children   []*collapsedVertex
    11  	key        KeyType
    12  	merkleHash *chainhash.Hash
    13  	claimHash  *chainhash.Hash
    14  }
    15  
    16  // insertAt inserts v into s at index i and returns the new slice.
    17  // https://stackoverflow.com/questions/42746972/golang-insert-to-a-sorted-slice
    18  func insertAt(data []*collapsedVertex, i int, v *collapsedVertex) []*collapsedVertex {
    19  	if i == len(data) {
    20  		// Insert at end is the easy case.
    21  		return append(data, v)
    22  	}
    23  
    24  	// Make space for the inserted element by shifting
    25  	// values at the insertion index up one index. The call
    26  	// to append does not allocate memory when cap(data) is
    27  	// greater than len(data).
    28  	data = append(data[:i+1], data[i:]...)
    29  	data[i] = v
    30  	return data
    31  }
    32  
    33  func (ptn *collapsedVertex) Insert(value *collapsedVertex) *collapsedVertex {
    34  	// keep it sorted (and sort.Sort is too slow)
    35  	index := sortSearch(ptn.children, value.key[0])
    36  	ptn.children = insertAt(ptn.children, index, value)
    37  
    38  	return value
    39  }
    40  
    41  // this sort.Search is stolen shamelessly from search.go,
    42  // and modified for performance to not need a closure
    43  func sortSearch(nodes []*collapsedVertex, b byte) int {
    44  	i, j := 0, len(nodes)
    45  	for i < j {
    46  		h := int(uint(i+j) >> 1) // avoid overflow when computing h
    47  		// i ≤ h < j
    48  		if nodes[h].key[0] < b {
    49  			i = h + 1 // preserves f(i-1) == false
    50  		} else {
    51  			j = h // preserves f(j) == true
    52  		}
    53  	}
    54  	// i == j, f(i-1) == false, and f(j) (= f(i)) == true  =>  answer is i.
    55  	return i
    56  }
    57  
    58  func (ptn *collapsedVertex) findNearest(key KeyType) (int, *collapsedVertex) {
    59  	// none of the children overlap on the first char or we would have a parent node with that char
    60  	index := sortSearch(ptn.children, key[0])
    61  	hits := ptn.children[index:]
    62  	if len(hits) > 0 {
    63  		return index, hits[0]
    64  	}
    65  	return -1, nil
    66  }
    67  
    68  type collapsedTrie struct {
    69  	Root  *collapsedVertex
    70  	Nodes int
    71  }
    72  
    73  func NewCollapsedTrie() *collapsedTrie {
    74  	// we never delete the Root node
    75  	return &collapsedTrie{Root: &collapsedVertex{key: make(KeyType, 0)}, Nodes: 1}
    76  }
    77  
    78  func (pt *collapsedTrie) NodeCount() int {
    79  	return pt.Nodes
    80  }
    81  
    82  func matchLength(a, b KeyType) int {
    83  	minLen := len(a)
    84  	if len(b) < minLen {
    85  		minLen = len(b)
    86  	}
    87  	for i := 0; i < minLen; i++ {
    88  		if a[i] != b[i] {
    89  			return i
    90  		}
    91  	}
    92  	return minLen
    93  }
    94  
    95  func (pt *collapsedTrie) insert(value KeyType, node *collapsedVertex) (bool, *collapsedVertex) {
    96  	index, child := node.findNearest(value)
    97  	match := 0
    98  	if index >= 0 { // if we found a child
    99  		child.merkleHash = nil
   100  		match = matchLength(value, child.key)
   101  		if len(value) == match && len(child.key) == match {
   102  			return false, child
   103  		}
   104  	}
   105  	if match <= 0 {
   106  		pt.Nodes++
   107  		return true, node.Insert(&collapsedVertex{key: value})
   108  	}
   109  	if match < len(child.key) {
   110  		grandChild := collapsedVertex{key: child.key[match:], children: child.children,
   111  			claimHash: child.claimHash, merkleHash: child.merkleHash}
   112  		newChild := collapsedVertex{key: child.key[0:match], children: []*collapsedVertex{&grandChild}}
   113  		child = &newChild
   114  		node.children[index] = child
   115  		pt.Nodes++
   116  		if len(value) == match {
   117  			return true, child
   118  		}
   119  	}
   120  	return pt.insert(value[match:], child)
   121  }
   122  
   123  func (pt *collapsedTrie) InsertOrFind(value KeyType) (bool, *collapsedVertex) {
   124  	pt.Root.merkleHash = nil
   125  	if len(value) <= 0 {
   126  		return false, pt.Root
   127  	}
   128  
   129  	// we store the name so we need to make our own copy of it
   130  	// this avoids errors where this function is called via the DB iterator
   131  	v2 := make([]byte, len(value))
   132  	copy(v2, value)
   133  	return pt.insert(v2, pt.Root)
   134  }
   135  
   136  func find(value KeyType, node *collapsedVertex, pathIndexes *[]int, path *[]*collapsedVertex) *collapsedVertex {
   137  	index, child := node.findNearest(value)
   138  	if index < 0 {
   139  		return nil
   140  	}
   141  	match := matchLength(value, child.key)
   142  	if len(value) == match && len(child.key) == match {
   143  		if pathIndexes != nil {
   144  			*pathIndexes = append(*pathIndexes, index)
   145  		}
   146  		if path != nil {
   147  			*path = append(*path, child)
   148  		}
   149  		return child
   150  	}
   151  	if match < len(child.key) || match == len(value) {
   152  		return nil
   153  	}
   154  	if pathIndexes != nil {
   155  		*pathIndexes = append(*pathIndexes, index)
   156  	}
   157  	if path != nil {
   158  		*path = append(*path, child)
   159  	}
   160  	return find(value[match:], child, pathIndexes, path)
   161  }
   162  
   163  func (pt *collapsedTrie) Find(value KeyType) *collapsedVertex {
   164  	if len(value) <= 0 {
   165  		return pt.Root
   166  	}
   167  	return find(value, pt.Root, nil, nil)
   168  }
   169  
   170  func (pt *collapsedTrie) FindPath(value KeyType) ([]int, []*collapsedVertex) {
   171  	pathIndexes := []int{-1}
   172  	path := []*collapsedVertex{pt.Root}
   173  	if len(value) > 0 {
   174  		result := find(value, pt.Root, &pathIndexes, &path)
   175  		if result == nil { // not sure I want this line
   176  			return nil, nil
   177  		}
   178  	}
   179  	return pathIndexes, path
   180  }
   181  
   182  // IterateFrom can be used to find a value and run a function on that value.
   183  // If the handler returns true it continues to iterate through the children of value.
   184  func (pt *collapsedTrie) IterateFrom(start KeyType, handler func(name KeyType, value *collapsedVertex) bool) {
   185  	node := find(start, pt.Root, nil, nil)
   186  	if node == nil {
   187  		return
   188  	}
   189  	iterateFrom(start, node, handler)
   190  }
   191  
   192  func iterateFrom(name KeyType, node *collapsedVertex, handler func(name KeyType, value *collapsedVertex) bool) {
   193  	for handler(name, node) {
   194  		for _, child := range node.children {
   195  			iterateFrom(append(name, child.key...), child, handler)
   196  		}
   197  	}
   198  }
   199  
   200  func (pt *collapsedTrie) Erase(value KeyType) bool {
   201  	indexes, path := pt.FindPath(value)
   202  	if path == nil || len(path) <= 1 {
   203  		if len(path) == 1 {
   204  			path[0].merkleHash = nil
   205  			path[0].claimHash = nil
   206  		}
   207  		return false
   208  	}
   209  	nodes := pt.Nodes
   210  	i := len(path) - 1
   211  	path[i].claimHash = nil // this is the thing we are erasing; the rest is book-keeping
   212  	for ; i > 0; i-- {
   213  		childCount := len(path[i].children)
   214  		noClaimData := path[i].claimHash == nil
   215  		path[i].merkleHash = nil
   216  		if childCount == 1 && noClaimData {
   217  			path[i].key = append(path[i].key, path[i].children[0].key...)
   218  			path[i].claimHash = path[i].children[0].claimHash
   219  			path[i].children = path[i].children[0].children
   220  			pt.Nodes--
   221  			continue
   222  		}
   223  		if childCount == 0 && noClaimData {
   224  			index := indexes[i]
   225  			path[i-1].children = append(path[i-1].children[:index], path[i-1].children[index+1:]...)
   226  			pt.Nodes--
   227  			continue
   228  		}
   229  		break
   230  	}
   231  	for ; i >= 0; i-- {
   232  		path[i].merkleHash = nil
   233  	}
   234  	return nodes > pt.Nodes
   235  }