github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/iavl/immutable_tree.go (about)

     1  package iavl
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	dbm "github.com/gnolang/gno/tm2/pkg/db"
     8  )
     9  
    10  // ImmutableTree is a container for an immutable AVL+ ImmutableTree. Changes are performed by
    11  // swapping the internal root with a new one, while the container is mutable.
    12  // Note that this tree is not thread-safe.
    13  type ImmutableTree struct {
    14  	root    *Node
    15  	ndb     *nodeDB
    16  	version int64
    17  }
    18  
    19  // NewImmutableTree creates both in-memory and persistent instances
    20  func NewImmutableTree(db dbm.DB, cacheSize int) *ImmutableTree {
    21  	if db == nil {
    22  		// In-memory Tree.
    23  		return &ImmutableTree{}
    24  	}
    25  	return &ImmutableTree{
    26  		// NodeDB-backed Tree.
    27  		ndb: newNodeDB(db, cacheSize),
    28  	}
    29  }
    30  
    31  // String returns a string representation of Tree.
    32  func (t *ImmutableTree) String() string {
    33  	leaves := []string{}
    34  	t.Iterate(func(key []byte, val []byte) (stop bool) {
    35  		leaves = append(leaves, fmt.Sprintf("%x: %x", key, val))
    36  		return false
    37  	})
    38  	return "Tree{" + strings.Join(leaves, ", ") + "}"
    39  }
    40  
    41  // RenderShape provides a nested tree shape, ident is prepended in each level
    42  // Returns an array of strings, one per line, to join with "\n" or display otherwise
    43  func (t *ImmutableTree) RenderShape(indent string, encoder NodeEncoder) []string {
    44  	if encoder == nil {
    45  		encoder = defaultNodeEncoder
    46  	}
    47  	return t.renderNode(t.root, indent, 0, encoder)
    48  }
    49  
    50  // NodeEncoder will take an id (hash, or key for leaf nodes), the depth of the node,
    51  // and whether or not this is a leaf node.
    52  // It returns the string we wish to print, for iaviwer
    53  type NodeEncoder func(id []byte, depth int, isLeaf bool) string
    54  
    55  // defaultNodeEncoder can encode any node unless the client overrides it
    56  func defaultNodeEncoder(id []byte, depth int, isLeaf bool) string {
    57  	prefix := "- "
    58  	if isLeaf {
    59  		prefix = "* "
    60  	}
    61  	if len(id) == 0 {
    62  		return fmt.Sprintf("%s<nil>", prefix)
    63  	}
    64  	return fmt.Sprintf("%s%X", prefix, id)
    65  }
    66  
    67  func (t *ImmutableTree) renderNode(node *Node, indent string, depth int, encoder func([]byte, int, bool) string) []string {
    68  	prefix := strings.Repeat(indent, depth)
    69  	// handle nil
    70  	if node == nil {
    71  		return []string{fmt.Sprintf("%s<nil>", prefix)}
    72  	}
    73  	// handle leaf
    74  	if node.isLeaf() {
    75  		here := fmt.Sprintf("%s%s", prefix, encoder(node.key, depth, true))
    76  		return []string{here}
    77  	}
    78  
    79  	// recurse on inner node
    80  	here := fmt.Sprintf("%s%s", prefix, encoder(node.hash, depth, false))
    81  	left := t.renderNode(node.getLeftNode(t), indent, depth+1, encoder)
    82  	right := t.renderNode(node.getRightNode(t), indent, depth+1, encoder)
    83  	result := append(left, here)
    84  	result = append(result, right...)
    85  	return result
    86  }
    87  
    88  // Size returns the number of leaf nodes in the tree.
    89  func (t *ImmutableTree) Size() int64 {
    90  	if t.root == nil {
    91  		return 0
    92  	}
    93  	return t.root.size
    94  }
    95  
    96  // Version returns the version of the tree.
    97  func (t *ImmutableTree) Version() int64 {
    98  	return t.version
    99  }
   100  
   101  // Height returns the height of the tree.
   102  func (t *ImmutableTree) Height() int8 {
   103  	if t.root == nil {
   104  		return 0
   105  	}
   106  	return t.root.height
   107  }
   108  
   109  // Has returns whether or not a key exists.
   110  func (t *ImmutableTree) Has(key []byte) bool {
   111  	if t.root == nil {
   112  		return false
   113  	}
   114  	return t.root.has(t, key)
   115  }
   116  
   117  // Hash returns the root hash.
   118  func (t *ImmutableTree) Hash() []byte {
   119  	if t.root == nil {
   120  		return nil
   121  	}
   122  	hash, _ := t.root.hashWithCount()
   123  	return hash
   124  }
   125  
   126  // hashWithCount returns the root hash and hash count.
   127  func (t *ImmutableTree) hashWithCount() ([]byte, int64) {
   128  	if t.root == nil {
   129  		return nil, 0
   130  	}
   131  	return t.root.hashWithCount()
   132  }
   133  
   134  // Get returns the index and value of the specified key if it exists, or nil
   135  // and the next index, if it doesn't.
   136  func (t *ImmutableTree) Get(key []byte) (index int64, value []byte) {
   137  	if t.root == nil {
   138  		return 0, nil
   139  	}
   140  	return t.root.get(t, key)
   141  }
   142  
   143  // GetByIndex gets the key and value at the specified index.
   144  func (t *ImmutableTree) GetByIndex(index int64) (key []byte, value []byte) {
   145  	if t.root == nil {
   146  		return nil, nil
   147  	}
   148  	return t.root.getByIndex(t, index)
   149  }
   150  
   151  // Iterate iterates over all keys of the tree, in order.
   152  func (t *ImmutableTree) Iterate(fn func(key []byte, value []byte) bool) (stopped bool) {
   153  	if t.root == nil {
   154  		return false
   155  	}
   156  	return t.root.traverse(t, true, func(node *Node) bool {
   157  		if node.height == 0 {
   158  			return fn(node.key, node.value)
   159  		}
   160  		return false
   161  	})
   162  }
   163  
   164  // IterateRange makes a callback for all nodes with key between start and end non-inclusive.
   165  // If either are nil, then it is open on that side (nil, nil is the same as Iterate)
   166  func (t *ImmutableTree) IterateRange(start, end []byte, ascending bool, fn func(key []byte, value []byte) bool) (stopped bool) {
   167  	if t.root == nil {
   168  		return false
   169  	}
   170  	return t.root.traverseInRange(t, start, end, ascending, false, 0, func(node *Node, _ uint8) bool {
   171  		if node.height == 0 {
   172  			return fn(node.key, node.value)
   173  		}
   174  		return false
   175  	})
   176  }
   177  
   178  // IterateRangeInclusive makes a callback for all nodes with key between start and end inclusive.
   179  // If either are nil, then it is open on that side (nil, nil is the same as Iterate)
   180  func (t *ImmutableTree) IterateRangeInclusive(start, end []byte, ascending bool, fn func(key, value []byte, version int64) bool) (stopped bool) {
   181  	if t.root == nil {
   182  		return false
   183  	}
   184  	return t.root.traverseInRange(t, start, end, ascending, true, 0, func(node *Node, _ uint8) bool {
   185  		if node.height == 0 {
   186  			return fn(node.key, node.value, node.version)
   187  		}
   188  		return false
   189  	})
   190  }
   191  
   192  // Clone creates a clone of the tree.
   193  // Used internally by MutableTree.
   194  func (t *ImmutableTree) clone() *ImmutableTree {
   195  	return &ImmutableTree{
   196  		root:    t.root,
   197  		ndb:     t.ndb,
   198  		version: t.version,
   199  	}
   200  }
   201  
   202  // nodeSize is like Size, but includes inner nodes too.
   203  func (t *ImmutableTree) nodeSize() int {
   204  	size := 0
   205  	t.root.traverse(t, true, func(n *Node) bool {
   206  		size++
   207  		return false
   208  	})
   209  	return size
   210  }