Path: blob/main/contrib/llvm-project/clang/lib/Rewrite/DeltaTree.cpp
35233 views
//===- DeltaTree.cpp - B-Tree for Rewrite Delta tracking ------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the DeltaTree and related classes.9//10//===----------------------------------------------------------------------===//1112#include "clang/Rewrite/Core/DeltaTree.h"13#include "clang/Basic/LLVM.h"14#include "llvm/Support/Casting.h"15#include <cassert>16#include <cstring>1718using namespace clang;1920/// The DeltaTree class is a multiway search tree (BTree) structure with some21/// fancy features. B-Trees are generally more memory and cache efficient22/// than binary trees, because they store multiple keys/values in each node.23///24/// DeltaTree implements a key/value mapping from FileIndex to Delta, allowing25/// fast lookup by FileIndex. However, an added (important) bonus is that it26/// can also efficiently tell us the full accumulated delta for a specific27/// file offset as well, without traversing the whole tree.28///29/// The nodes of the tree are made up of instances of two classes:30/// DeltaTreeNode and DeltaTreeInteriorNode. The later subclasses the31/// former and adds children pointers. Each node knows the full delta of all32/// entries (recursively) contained inside of it, which allows us to get the33/// full delta implied by a whole subtree in constant time.3435namespace {3637/// SourceDelta - As code in the original input buffer is added and deleted,38/// SourceDelta records are used to keep track of how the input SourceLocation39/// object is mapped into the output buffer.40struct SourceDelta {41unsigned FileLoc;42int Delta;4344static SourceDelta get(unsigned Loc, int D) {45SourceDelta Delta;46Delta.FileLoc = Loc;47Delta.Delta = D;48return Delta;49}50};5152/// DeltaTreeNode - The common part of all nodes.53///54class DeltaTreeNode {55public:56struct InsertResult {57DeltaTreeNode *LHS, *RHS;58SourceDelta Split;59};6061private:62friend class DeltaTreeInteriorNode;6364/// WidthFactor - This controls the number of K/V slots held in the BTree:65/// how wide it is. Each level of the BTree is guaranteed to have at least66/// WidthFactor-1 K/V pairs (except the root) and may have at most67/// 2*WidthFactor-1 K/V pairs.68enum { WidthFactor = 8 };6970/// Values - This tracks the SourceDelta's currently in this node.71SourceDelta Values[2*WidthFactor-1];7273/// NumValuesUsed - This tracks the number of values this node currently74/// holds.75unsigned char NumValuesUsed = 0;7677/// IsLeaf - This is true if this is a leaf of the btree. If false, this is78/// an interior node, and is actually an instance of DeltaTreeInteriorNode.79bool IsLeaf;8081/// FullDelta - This is the full delta of all the values in this node and82/// all children nodes.83int FullDelta = 0;8485public:86DeltaTreeNode(bool isLeaf = true) : IsLeaf(isLeaf) {}8788bool isLeaf() const { return IsLeaf; }89int getFullDelta() const { return FullDelta; }90bool isFull() const { return NumValuesUsed == 2*WidthFactor-1; }9192unsigned getNumValuesUsed() const { return NumValuesUsed; }9394const SourceDelta &getValue(unsigned i) const {95assert(i < NumValuesUsed && "Invalid value #");96return Values[i];97}9899SourceDelta &getValue(unsigned i) {100assert(i < NumValuesUsed && "Invalid value #");101return Values[i];102}103104/// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into105/// this node. If insertion is easy, do it and return false. Otherwise,106/// split the node, populate InsertRes with info about the split, and return107/// true.108bool DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes);109110void DoSplit(InsertResult &InsertRes);111112113/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a114/// local walk over our contained deltas.115void RecomputeFullDeltaLocally();116117void Destroy();118};119120/// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers.121/// This class tracks them.122class DeltaTreeInteriorNode : public DeltaTreeNode {123friend class DeltaTreeNode;124125DeltaTreeNode *Children[2*WidthFactor];126127~DeltaTreeInteriorNode() {128for (unsigned i = 0, e = NumValuesUsed+1; i != e; ++i)129Children[i]->Destroy();130}131132public:133DeltaTreeInteriorNode() : DeltaTreeNode(false /*nonleaf*/) {}134135DeltaTreeInteriorNode(const InsertResult &IR)136: DeltaTreeNode(false /*nonleaf*/) {137Children[0] = IR.LHS;138Children[1] = IR.RHS;139Values[0] = IR.Split;140FullDelta = IR.LHS->getFullDelta()+IR.RHS->getFullDelta()+IR.Split.Delta;141NumValuesUsed = 1;142}143144const DeltaTreeNode *getChild(unsigned i) const {145assert(i < getNumValuesUsed()+1 && "Invalid child");146return Children[i];147}148149DeltaTreeNode *getChild(unsigned i) {150assert(i < getNumValuesUsed()+1 && "Invalid child");151return Children[i];152}153154static bool classof(const DeltaTreeNode *N) { return !N->isLeaf(); }155};156157} // namespace158159/// Destroy - A 'virtual' destructor.160void DeltaTreeNode::Destroy() {161if (isLeaf())162delete this;163else164delete cast<DeltaTreeInteriorNode>(this);165}166167/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a168/// local walk over our contained deltas.169void DeltaTreeNode::RecomputeFullDeltaLocally() {170int NewFullDelta = 0;171for (unsigned i = 0, e = getNumValuesUsed(); i != e; ++i)172NewFullDelta += Values[i].Delta;173if (auto *IN = dyn_cast<DeltaTreeInteriorNode>(this))174for (unsigned i = 0, e = getNumValuesUsed()+1; i != e; ++i)175NewFullDelta += IN->getChild(i)->getFullDelta();176FullDelta = NewFullDelta;177}178179/// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into180/// this node. If insertion is easy, do it and return false. Otherwise,181/// split the node, populate InsertRes with info about the split, and return182/// true.183bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta,184InsertResult *InsertRes) {185// Maintain full delta for this node.186FullDelta += Delta;187188// Find the insertion point, the first delta whose index is >= FileIndex.189unsigned i = 0, e = getNumValuesUsed();190while (i != e && FileIndex > getValue(i).FileLoc)191++i;192193// If we found an a record for exactly this file index, just merge this194// value into the pre-existing record and finish early.195if (i != e && getValue(i).FileLoc == FileIndex) {196// NOTE: Delta could drop to zero here. This means that the delta entry is197// useless and could be removed. Supporting erases is more complex than198// leaving an entry with Delta=0, so we just leave an entry with Delta=0 in199// the tree.200Values[i].Delta += Delta;201return false;202}203204// Otherwise, we found an insertion point, and we know that the value at the205// specified index is > FileIndex. Handle the leaf case first.206if (isLeaf()) {207if (!isFull()) {208// For an insertion into a non-full leaf node, just insert the value in209// its sorted position. This requires moving later values over.210if (i != e)211memmove(&Values[i+1], &Values[i], sizeof(Values[0])*(e-i));212Values[i] = SourceDelta::get(FileIndex, Delta);213++NumValuesUsed;214return false;215}216217// Otherwise, if this is leaf is full, split the node at its median, insert218// the value into one of the children, and return the result.219assert(InsertRes && "No result location specified");220DoSplit(*InsertRes);221222if (InsertRes->Split.FileLoc > FileIndex)223InsertRes->LHS->DoInsertion(FileIndex, Delta, nullptr /*can't fail*/);224else225InsertRes->RHS->DoInsertion(FileIndex, Delta, nullptr /*can't fail*/);226return true;227}228229// Otherwise, this is an interior node. Send the request down the tree.230auto *IN = cast<DeltaTreeInteriorNode>(this);231if (!IN->Children[i]->DoInsertion(FileIndex, Delta, InsertRes))232return false; // If there was space in the child, just return.233234// Okay, this split the subtree, producing a new value and two children to235// insert here. If this node is non-full, we can just insert it directly.236if (!isFull()) {237// Now that we have two nodes and a new element, insert the perclated value238// into ourself by moving all the later values/children down, then inserting239// the new one.240if (i != e)241memmove(&IN->Children[i+2], &IN->Children[i+1],242(e-i)*sizeof(IN->Children[0]));243IN->Children[i] = InsertRes->LHS;244IN->Children[i+1] = InsertRes->RHS;245246if (e != i)247memmove(&Values[i+1], &Values[i], (e-i)*sizeof(Values[0]));248Values[i] = InsertRes->Split;249++NumValuesUsed;250return false;251}252253// Finally, if this interior node was full and a node is percolated up, split254// ourself and return that up the chain. Start by saving all our info to255// avoid having the split clobber it.256IN->Children[i] = InsertRes->LHS;257DeltaTreeNode *SubRHS = InsertRes->RHS;258SourceDelta SubSplit = InsertRes->Split;259260// Do the split.261DoSplit(*InsertRes);262263// Figure out where to insert SubRHS/NewSplit.264DeltaTreeInteriorNode *InsertSide;265if (SubSplit.FileLoc < InsertRes->Split.FileLoc)266InsertSide = cast<DeltaTreeInteriorNode>(InsertRes->LHS);267else268InsertSide = cast<DeltaTreeInteriorNode>(InsertRes->RHS);269270// We now have a non-empty interior node 'InsertSide' to insert271// SubRHS/SubSplit into. Find out where to insert SubSplit.272273// Find the insertion point, the first delta whose index is >SubSplit.FileLoc.274i = 0; e = InsertSide->getNumValuesUsed();275while (i != e && SubSplit.FileLoc > InsertSide->getValue(i).FileLoc)276++i;277278// Now we know that i is the place to insert the split value into. Insert it279// and the child right after it.280if (i != e)281memmove(&InsertSide->Children[i+2], &InsertSide->Children[i+1],282(e-i)*sizeof(IN->Children[0]));283InsertSide->Children[i+1] = SubRHS;284285if (e != i)286memmove(&InsertSide->Values[i+1], &InsertSide->Values[i],287(e-i)*sizeof(Values[0]));288InsertSide->Values[i] = SubSplit;289++InsertSide->NumValuesUsed;290InsertSide->FullDelta += SubSplit.Delta + SubRHS->getFullDelta();291return true;292}293294/// DoSplit - Split the currently full node (which has 2*WidthFactor-1 values)295/// into two subtrees each with "WidthFactor-1" values and a pivot value.296/// Return the pieces in InsertRes.297void DeltaTreeNode::DoSplit(InsertResult &InsertRes) {298assert(isFull() && "Why split a non-full node?");299300// Since this node is full, it contains 2*WidthFactor-1 values. We move301// the first 'WidthFactor-1' values to the LHS child (which we leave in this302// node), propagate one value up, and move the last 'WidthFactor-1' values303// into the RHS child.304305// Create the new child node.306DeltaTreeNode *NewNode;307if (auto *IN = dyn_cast<DeltaTreeInteriorNode>(this)) {308// If this is an interior node, also move over 'WidthFactor' children309// into the new node.310DeltaTreeInteriorNode *New = new DeltaTreeInteriorNode();311memcpy(&New->Children[0], &IN->Children[WidthFactor],312WidthFactor*sizeof(IN->Children[0]));313NewNode = New;314} else {315// Just create the new leaf node.316NewNode = new DeltaTreeNode();317}318319// Move over the last 'WidthFactor-1' values from here to NewNode.320memcpy(&NewNode->Values[0], &Values[WidthFactor],321(WidthFactor-1)*sizeof(Values[0]));322323// Decrease the number of values in the two nodes.324NewNode->NumValuesUsed = NumValuesUsed = WidthFactor-1;325326// Recompute the two nodes' full delta.327NewNode->RecomputeFullDeltaLocally();328RecomputeFullDeltaLocally();329330InsertRes.LHS = this;331InsertRes.RHS = NewNode;332InsertRes.Split = Values[WidthFactor-1];333}334335//===----------------------------------------------------------------------===//336// DeltaTree Implementation337//===----------------------------------------------------------------------===//338339//#define VERIFY_TREE340341#ifdef VERIFY_TREE342/// VerifyTree - Walk the btree performing assertions on various properties to343/// verify consistency. This is useful for debugging new changes to the tree.344static void VerifyTree(const DeltaTreeNode *N) {345const auto *IN = dyn_cast<DeltaTreeInteriorNode>(N);346if (IN == 0) {347// Verify leaves, just ensure that FullDelta matches up and the elements348// are in proper order.349int FullDelta = 0;350for (unsigned i = 0, e = N->getNumValuesUsed(); i != e; ++i) {351if (i)352assert(N->getValue(i-1).FileLoc < N->getValue(i).FileLoc);353FullDelta += N->getValue(i).Delta;354}355assert(FullDelta == N->getFullDelta());356return;357}358359// Verify interior nodes: Ensure that FullDelta matches up and the360// elements are in proper order and the children are in proper order.361int FullDelta = 0;362for (unsigned i = 0, e = IN->getNumValuesUsed(); i != e; ++i) {363const SourceDelta &IVal = N->getValue(i);364const DeltaTreeNode *IChild = IN->getChild(i);365if (i)366assert(IN->getValue(i-1).FileLoc < IVal.FileLoc);367FullDelta += IVal.Delta;368FullDelta += IChild->getFullDelta();369370// The largest value in child #i should be smaller than FileLoc.371assert(IChild->getValue(IChild->getNumValuesUsed()-1).FileLoc <372IVal.FileLoc);373374// The smallest value in child #i+1 should be larger than FileLoc.375assert(IN->getChild(i+1)->getValue(0).FileLoc > IVal.FileLoc);376VerifyTree(IChild);377}378379FullDelta += IN->getChild(IN->getNumValuesUsed())->getFullDelta();380381assert(FullDelta == N->getFullDelta());382}383#endif // VERIFY_TREE384385static DeltaTreeNode *getRoot(void *Root) {386return (DeltaTreeNode*)Root;387}388389DeltaTree::DeltaTree() {390Root = new DeltaTreeNode();391}392393DeltaTree::DeltaTree(const DeltaTree &RHS) {394// Currently we only support copying when the RHS is empty.395assert(getRoot(RHS.Root)->getNumValuesUsed() == 0 &&396"Can only copy empty tree");397Root = new DeltaTreeNode();398}399400DeltaTree::~DeltaTree() {401getRoot(Root)->Destroy();402}403404/// getDeltaAt - Return the accumulated delta at the specified file offset.405/// This includes all insertions or delections that occurred *before* the406/// specified file index.407int DeltaTree::getDeltaAt(unsigned FileIndex) const {408const DeltaTreeNode *Node = getRoot(Root);409410int Result = 0;411412// Walk down the tree.413while (true) {414// For all nodes, include any local deltas before the specified file415// index by summing them up directly. Keep track of how many were416// included.417unsigned NumValsGreater = 0;418for (unsigned e = Node->getNumValuesUsed(); NumValsGreater != e;419++NumValsGreater) {420const SourceDelta &Val = Node->getValue(NumValsGreater);421422if (Val.FileLoc >= FileIndex)423break;424Result += Val.Delta;425}426427// If we have an interior node, include information about children and428// recurse. Otherwise, if we have a leaf, we're done.429const auto *IN = dyn_cast<DeltaTreeInteriorNode>(Node);430if (!IN) return Result;431432// Include any children to the left of the values we skipped, all of433// their deltas should be included as well.434for (unsigned i = 0; i != NumValsGreater; ++i)435Result += IN->getChild(i)->getFullDelta();436437// If we found exactly the value we were looking for, break off the438// search early. There is no need to search the RHS of the value for439// partial results.440if (NumValsGreater != Node->getNumValuesUsed() &&441Node->getValue(NumValsGreater).FileLoc == FileIndex)442return Result+IN->getChild(NumValsGreater)->getFullDelta();443444// Otherwise, traverse down the tree. The selected subtree may be445// partially included in the range.446Node = IN->getChild(NumValsGreater);447}448// NOT REACHED.449}450451/// AddDelta - When a change is made that shifts around the text buffer,452/// this method is used to record that info. It inserts a delta of 'Delta'453/// into the current DeltaTree at offset FileIndex.454void DeltaTree::AddDelta(unsigned FileIndex, int Delta) {455assert(Delta && "Adding a noop?");456DeltaTreeNode *MyRoot = getRoot(Root);457458DeltaTreeNode::InsertResult InsertRes;459if (MyRoot->DoInsertion(FileIndex, Delta, &InsertRes)) {460Root = new DeltaTreeInteriorNode(InsertRes);461#ifdef VERIFY_TREE462MyRoot = Root;463#endif464}465466#ifdef VERIFY_TREE467VerifyTree(MyRoot);468#endif469}470471472