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  }