//===- DeltaTree.cpp - B-Tree for Rewrite Delta tracking ------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the DeltaTree and related classes. // //===----------------------------------------------------------------------===// #include "llvm/ADT/DeltaTree.h" #include "llvm/Support/Casting.h" #include <cassert> #include <cstring> usingnamespacellvm; /// The DeltaTree class is a multiway search tree (BTree) structure with some /// fancy features. B-Trees are generally more memory and cache efficient /// than binary trees, because they store multiple keys/values in each node. /// /// DeltaTree implements a key/value mapping from FileIndex to Delta, allowing /// fast lookup by FileIndex. However, an added (important) bonus is that it /// can also efficiently tell us the full accumulated delta for a specific /// file offset as well, without traversing the whole tree. /// /// The nodes of the tree are made up of instances of two classes: /// DeltaTreeNode and DeltaTreeInteriorNode. The later subclasses the /// former and adds children pointers. Each node knows the full delta of all /// entries (recursively) contained inside of it, which allows us to get the /// full delta implied by a whole subtree in constant time. namespace { /// SourceDelta - As code in the original input buffer is added and deleted, /// SourceDelta records are used to keep track of how the input SourceLocation /// object is mapped into the output buffer. struct SourceDelta { … }; /// DeltaTreeNode - The common part of all nodes. /// class DeltaTreeNode { … }; /// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers. /// This class tracks them. class DeltaTreeInteriorNode : public DeltaTreeNode { … }; } // namespace /// Destroy - A 'virtual' destructor. void DeltaTreeNode::Destroy() { … } /// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a /// local walk over our contained deltas. void DeltaTreeNode::RecomputeFullDeltaLocally() { … } /// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into /// this node. If insertion is easy, do it and return false. Otherwise, /// split the node, populate InsertRes with info about the split, and return /// true. bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes) { … } /// DoSplit - Split the currently full node (which has 2*WidthFactor-1 values) /// into two subtrees each with "WidthFactor-1" values and a pivot value. /// Return the pieces in InsertRes. void DeltaTreeNode::DoSplit(InsertResult &InsertRes) { … } //===----------------------------------------------------------------------===// // DeltaTree Implementation //===----------------------------------------------------------------------===// // #define VERIFY_TREE #ifdef VERIFY_TREE /// VerifyTree - Walk the btree performing assertions on various properties to /// verify consistency. This is useful for debugging new changes to the tree. static void VerifyTree(const DeltaTreeNode *N) { const auto *IN = dyn_cast<DeltaTreeInteriorNode>(N); if (IN == 0) { // Verify leaves, just ensure that FullDelta matches up and the elements // are in proper order. int FullDelta = 0; for (unsigned i = 0, e = N->getNumValuesUsed(); i != e; ++i) { if (i) assert(N->getValue(i - 1).FileLoc < N->getValue(i).FileLoc); FullDelta += N->getValue(i).Delta; } assert(FullDelta == N->getFullDelta()); return; } // Verify interior nodes: Ensure that FullDelta matches up and the // elements are in proper order and the children are in proper order. int FullDelta = 0; for (unsigned i = 0, e = IN->getNumValuesUsed(); i != e; ++i) { const SourceDelta &IVal = N->getValue(i); const DeltaTreeNode *IChild = IN->getChild(i); if (i) assert(IN->getValue(i - 1).FileLoc < IVal.FileLoc); FullDelta += IVal.Delta; FullDelta += IChild->getFullDelta(); // The largest value in child #i should be smaller than FileLoc. assert(IChild->getValue(IChild->getNumValuesUsed() - 1).FileLoc < IVal.FileLoc); // The smallest value in child #i+1 should be larger than FileLoc. assert(IN->getChild(i + 1)->getValue(0).FileLoc > IVal.FileLoc); VerifyTree(IChild); } FullDelta += IN->getChild(IN->getNumValuesUsed())->getFullDelta(); assert(FullDelta == N->getFullDelta()); } #endif // VERIFY_TREE static DeltaTreeNode *getRoot(void *Root) { … } DeltaTree::DeltaTree() { … } DeltaTree::DeltaTree(const DeltaTree &RHS) { … } DeltaTree::~DeltaTree() { … } /// getDeltaAt - Return the accumulated delta at the specified file offset. /// This includes all insertions or delections that occurred *before* the /// specified file index. int DeltaTree::getDeltaAt(unsigned FileIndex) const { … } /// AddDelta - When a change is made that shifts around the text buffer, /// this method is used to record that info. It inserts a delta of 'Delta' /// into the current DeltaTree at offset FileIndex. void DeltaTree::AddDelta(unsigned FileIndex, int Delta) { … }