github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/immutable_tree.go (about) 1 package iavl 2 3 import ( 4 "fmt" 5 "strings" 6 7 dbm "github.com/fibonacci-chain/fbc/libs/tm-db" 8 ) 9 10 // ImmutableTree contains the immutable tree at a given version. It is typically created by calling 11 // MutableTree.GetImmutable(), in which case the returned tree is safe for concurrent access as 12 // long as the version is not deleted via DeleteVersion() or the tree's pruning settings. 13 // 14 // Returned key/value byte slices must not be modified, since they may point to data located inside 15 // IAVL which would also be modified. 16 type ImmutableTree struct { 17 root *Node 18 ndb *nodeDB 19 version int64 20 upgradeVersion int64 21 } 22 23 // NewImmutableTree creates both in-memory and persistent instances 24 func NewImmutableTree(db dbm.DB, cacheSize int) *ImmutableTree { 25 if db == nil { 26 // In-memory Tree. 27 return &ImmutableTree{} 28 } 29 return &ImmutableTree{ 30 // NodeDB-backed Tree. 31 ndb: newNodeDB(db, cacheSize, nil), 32 } 33 } 34 35 // NewImmutableTreeWithOpts creates an ImmutableTree with the given options. 36 func NewImmutableTreeWithOpts(db dbm.DB, cacheSize int, opts *Options) *ImmutableTree { 37 return &ImmutableTree{ 38 // NodeDB-backed Tree. 39 ndb: newNodeDB(db, cacheSize, opts), 40 } 41 } 42 43 // String returns a string representation of Tree. 44 func (t *ImmutableTree) String() string { 45 leaves := []string{} 46 t.Iterate(func(key []byte, val []byte) (stop bool) { 47 leaves = append(leaves, fmt.Sprintf("%x: %x", key, val)) 48 return false 49 }) 50 return "Tree{" + strings.Join(leaves, ", ") + "}" 51 } 52 53 // RenderShape provides a nested tree shape, ident is prepended in each level 54 // Returns an array of strings, one per line, to join with "\n" or display otherwise 55 func (t *ImmutableTree) RenderShape(indent string, encoder NodeEncoder) []string { 56 if encoder == nil { 57 encoder = defaultNodeEncoder 58 } 59 return t.renderNode(t.root, indent, 0, encoder) 60 } 61 62 // NodeEncoder will take an id (hash, or key for leaf nodes), the depth of the node, 63 // and whether or not this is a leaf node. 64 // It returns the string we wish to print, for iaviwer 65 type NodeEncoder func(id []byte, depth int, isLeaf bool) string 66 67 // defaultNodeEncoder can encode any node unless the client overrides it 68 func defaultNodeEncoder(id []byte, depth int, isLeaf bool) string { 69 prefix := "- " 70 if isLeaf { 71 prefix = "* " 72 } 73 if len(id) == 0 { 74 return fmt.Sprintf("%s<nil>", prefix) 75 } 76 return fmt.Sprintf("%s%X", prefix, id) 77 } 78 79 func (t *ImmutableTree) renderNode(node *Node, indent string, depth int, encoder func([]byte, int, bool) string) []string { 80 prefix := strings.Repeat(indent, depth) 81 // handle nil 82 if node == nil { 83 return []string{fmt.Sprintf("%s<nil>", prefix)} 84 } 85 // handle leaf 86 if node.isLeaf() { 87 here := fmt.Sprintf("%s%s", prefix, encoder(node.key, depth, true)) 88 return []string{here} 89 } 90 91 // recurse on inner node 92 here := fmt.Sprintf("%s%s", prefix, encoder(node.hash, depth, false)) 93 left := t.renderNode(node.getLeftNode(t), indent, depth+1, encoder) 94 right := t.renderNode(node.getRightNode(t), indent, depth+1, encoder) 95 result := append(left, here) 96 result = append(result, right...) 97 return result 98 } 99 100 // Size returns the number of leaf nodes in the tree. 101 func (t *ImmutableTree) Size() int64 { 102 if t.root == nil { 103 return 0 104 } 105 return t.root.size 106 } 107 108 // Version returns the version of the tree. 109 func (t *ImmutableTree) Version() int64 { 110 return t.version 111 } 112 113 // Height returns the height of the tree. 114 func (t *ImmutableTree) Height() int8 { 115 if t.root == nil { 116 return 0 117 } 118 return t.root.height 119 } 120 121 // Has returns whether or not a key exists. 122 func (t *ImmutableTree) Has(key []byte) bool { 123 if t.root == nil { 124 return false 125 } 126 return t.root.has(t, key) 127 } 128 129 // Hash returns the root hash. 130 func (t *ImmutableTree) Hash() []byte { 131 if t.root == nil { 132 return nil 133 } 134 hash, _ := t.root.hashWithCount() 135 return hash 136 } 137 138 // hashWithCount returns the root hash and hash count. 139 func (t *ImmutableTree) hashWithCount() ([]byte, int64) { 140 if t.root == nil { 141 return nil, 0 142 } 143 return t.root.hashWithCount() 144 } 145 146 // Export returns an iterator that exports tree nodes as ExportNodes. These nodes can be 147 // imported with MutableTree.Import() to recreate an identical tree. 148 func (t *ImmutableTree) Export() *Exporter { 149 return newExporter(t) 150 } 151 152 // GetWithIndex returns the index and value of the specified key if it exists, or nil and the next index 153 // otherwise. The returned value must not be modified, since it may point to data stored within 154 // IAVL. 155 func (t *ImmutableTree) GetWithIndex(key []byte) (index int64, value []byte) { 156 if t.root == nil { 157 return 0, nil 158 } 159 return t.root.get(t, key) 160 } 161 162 // Get returns the value of the specified key if it exists, or nil. 163 // The returned value must not be modified, since it may point to data stored within IAVL. 164 // Get potentially employs a more performant strategy than GetWithIndex for retrieving the value. 165 func (t *ImmutableTree) Get(key []byte) []byte { 166 if t.root == nil { 167 return nil 168 } 169 170 if GetEnableFastStorage() { 171 // attempt to get a FastNode directly from db/cache. 172 // if call fails, fall back to the original IAVL logic in place. 173 fastNode, err := t.ndb.GetFastNode(key) 174 if err != nil { 175 _, result := t.root.get(t, key) 176 return result 177 } 178 179 if fastNode == nil { 180 // If the tree is of the latest version and fast node is not in the tree 181 // then the regular node is not in the tree either because fast node 182 // represents live state. 183 if t.version == t.ndb.getLatestMemoryVersion() { 184 return nil 185 } 186 187 _, result := t.root.get(t, key) 188 return result 189 } 190 191 if fastNode.versionLastUpdatedAt <= t.version { 192 return fastNode.value 193 } 194 } 195 // Otherwise the cached node was updated later than the current tree. In this case, 196 // we need to use the regular stategy for reading from the current tree to avoid staleness. 197 _, result := t.root.get(t, key) 198 return result 199 } 200 201 // GetByIndex gets the key and value at the specified index. 202 func (t *ImmutableTree) GetByIndex(index int64) (key []byte, value []byte) { 203 if t.root == nil { 204 return nil, nil 205 } 206 return t.root.getByIndex(t, index) 207 } 208 209 // Iterate iterates over all keys of the tree. The keys and values must not be modified, 210 // since they may point to data stored within IAVL.Returns true if stopped by callback, false otherwise 211 func (t *ImmutableTree) Iterate(fn func(key []byte, value []byte) bool) (stopped bool) { 212 if t.root == nil { 213 return false 214 } 215 itr := t.Iterator(nil, nil, true) 216 defer itr.Close() 217 for ; itr.Valid(); itr.Next() { 218 if fn(itr.Key(), itr.Value()) { 219 return true 220 } 221 } 222 return false 223 } 224 225 // Iterator returns an iterator over the immutable tree. 226 func (t *ImmutableTree) Iterator(start, end []byte, ascending bool) dbm.Iterator { 227 if t.IsFastCacheEnabled() { 228 return NewFastIteratorWithCache(start, end, ascending, t.ndb) 229 } 230 return NewIterator(start, end, ascending, t) 231 } 232 233 // IterateRange makes a callback for all nodes with key between start and end non-inclusive. 234 // If either are nil, then it is open on that side (nil, nil is the same as Iterate). The keys and 235 // values must not be modified, since they may point to data stored within IAVL. 236 func (t *ImmutableTree) IterateRange(start, end []byte, ascending bool, fn func(key []byte, value []byte) bool) (stopped bool) { 237 if t.root == nil { 238 return false 239 } 240 return t.root.traverseInRange(t, start, end, ascending, false, 0, false, func(node *Node, _ uint8) bool { 241 if node.height == 0 { 242 return fn(node.key, node.value) 243 } 244 return false 245 }) 246 } 247 248 // IterateRangeInclusive makes a callback for all nodes with key between start and end inclusive. 249 // If either are nil, then it is open on that side (nil, nil is the same as Iterate). The keys and 250 // values must not be modified, since they may point to data stored within IAVL. 251 func (t *ImmutableTree) IterateRangeInclusive(start, end []byte, ascending bool, fn func(key, value []byte, version int64) bool) (stopped bool) { 252 if t.root == nil { 253 return false 254 } 255 return t.root.traverseInRange(t, start, end, ascending, true, 0, false, func(node *Node, _ uint8) bool { 256 if node.height == 0 { 257 return fn(node.key, node.value, node.version) 258 } 259 return false 260 }) 261 } 262 263 // IsFastCacheEnabled returns true if fast cache is enabled, false otherwise. 264 // For fast cache to be enabled, the following 2 conditions must be met: 265 // 1. The tree is of the latest version. 266 // 2. The underlying storage has been upgraded to fast cache 267 func (t *ImmutableTree) IsFastCacheEnabled() bool { 268 return GetEnableFastStorage() && t.isLatestTreeVersion() && t.ndb.hasUpgradedToFastStorage() 269 } 270 271 func (t *ImmutableTree) isLatestTreeVersion() bool { 272 return t.version == t.ndb.getLatestMemoryVersion() 273 } 274 275 // Clone creates a clone of the tree. 276 // Used internally by MutableTree. 277 func (t *ImmutableTree) clone() *ImmutableTree { 278 return &ImmutableTree{ 279 root: t.root, 280 ndb: t.ndb, 281 version: t.version, 282 upgradeVersion: -1, 283 } 284 } 285 286 // nodeSize is like Size, but includes inner nodes too. 287 func (t *ImmutableTree) nodeSize() int { 288 size := 0 289 t.root.traverse(t, true, func(n *Node) bool { 290 size++ 291 return false 292 }) 293 return size 294 } 295 296 func (t *ImmutableTree) SetUpgradeVersion(version int64) { 297 t.upgradeVersion = version 298 } 299 300 func (t *ImmutableTree) GetUpgradeVersion() int64 { 301 return t.upgradeVersion 302 } 303 304 // Only used for debug! 305 func (t *ImmutableTree) DebugGetNode(nodeHash []byte) *Node { 306 if string(t.Hash()) == string(nodeHash) { 307 return t.root 308 } 309 return t.ndb.GetNode(nodeHash) 310 } 311 312 // Only used for debug! 313 func (t *ImmutableTree) DebugFssVersion() ([]byte, error) { 314 return t.ndb.db.Get(metadataKeyFormat.Key([]byte(storageVersionKey))) 315 }