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 }