Path: blob/main/contrib/llvm-project/llvm/lib/Support/DeltaTree.cpp
213764 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 "llvm/ADT/DeltaTree.h"13#include "llvm/Support/Casting.h"14#include <cassert>15#include <cstring>1617using namespace llvm;1819/// The DeltaTree class is a multiway search tree (BTree) structure with some20/// fancy features. B-Trees are generally more memory and cache efficient21/// than binary trees, because they store multiple keys/values in each node.22///23/// DeltaTree implements a key/value mapping from FileIndex to Delta, allowing24/// fast lookup by FileIndex. However, an added (important) bonus is that it25/// can also efficiently tell us the full accumulated delta for a specific26/// file offset as well, without traversing the whole tree.27///28/// The nodes of the tree are made up of instances of two classes:29/// DeltaTreeNode and DeltaTreeInteriorNode. The later subclasses the30/// former and adds children pointers. Each node knows the full delta of all31/// entries (recursively) contained inside of it, which allows us to get the32/// full delta implied by a whole subtree in constant time.3334namespace {3536/// SourceDelta - As code in the original input buffer is added and deleted,37/// SourceDelta records are used to keep track of how the input SourceLocation38/// object is mapped into the output buffer.39struct SourceDelta {40unsigned FileLoc;41int Delta;4243static SourceDelta get(unsigned Loc, int D) {44SourceDelta Delta;45Delta.FileLoc = Loc;46Delta.Delta = D;47return Delta;48}49};5051/// DeltaTreeNode - The common part of all nodes.52///53class DeltaTreeNode {54public:55struct InsertResult {56DeltaTreeNode *LHS, *RHS;57SourceDelta Split;58};5960private:61friend class DeltaTreeInteriorNode;6263/// WidthFactor - This controls the number of K/V slots held in the BTree:64/// how wide it is. Each level of the BTree is guaranteed to have at least65/// WidthFactor-1 K/V pairs (except the root) and may have at most66/// 2*WidthFactor-1 K/V pairs.67enum { WidthFactor = 8 };6869/// Values - This tracks the SourceDelta's currently in this node.70SourceDelta Values[2 * WidthFactor - 1];7172/// NumValuesUsed - This tracks the number of values this node currently73/// holds.74unsigned char NumValuesUsed = 0;7576/// IsLeaf - This is true if this is a leaf of the btree. If false, this is77/// an interior node, and is actually an instance of DeltaTreeInteriorNode.78bool IsLeaf;7980/// FullDelta - This is the full delta of all the values in this node and81/// all children nodes.82int FullDelta = 0;8384public:85DeltaTreeNode(bool isLeaf = true) : IsLeaf(isLeaf) {}8687bool isLeaf() const { return IsLeaf; }88int getFullDelta() const { return FullDelta; }89bool isFull() const { return NumValuesUsed == 2 * WidthFactor - 1; }9091unsigned getNumValuesUsed() const { return NumValuesUsed; }9293const SourceDelta &getValue(unsigned i) const {94assert(i < NumValuesUsed && "Invalid value #");95return Values[i];96}9798SourceDelta &getValue(unsigned i) {99assert(i < NumValuesUsed && "Invalid value #");100return Values[i];101}102103/// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into104/// this node. If insertion is easy, do it and return false. Otherwise,105/// split the node, populate InsertRes with info about the split, and return106/// true.107bool DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes);108109void DoSplit(InsertResult &InsertRes);110111/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a112/// local walk over our contained deltas.113void RecomputeFullDeltaLocally();114115void Destroy();116};117118/// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers.119/// This class tracks them.120class DeltaTreeInteriorNode : public DeltaTreeNode {121friend class DeltaTreeNode;122123DeltaTreeNode *Children[2 * WidthFactor];124125~DeltaTreeInteriorNode() {126for (unsigned i = 0, e = NumValuesUsed + 1; i != e; ++i)127Children[i]->Destroy();128}129130public:131DeltaTreeInteriorNode() : DeltaTreeNode(false /*nonleaf*/) {}132133DeltaTreeInteriorNode(const InsertResult &IR)134: DeltaTreeNode(false /*nonleaf*/) {135Children[0] = IR.LHS;136Children[1] = IR.RHS;137Values[0] = IR.Split;138FullDelta =139IR.LHS->getFullDelta() + IR.RHS->getFullDelta() + IR.Split.Delta;140NumValuesUsed = 1;141}142143const DeltaTreeNode *getChild(unsigned i) const {144assert(i < getNumValuesUsed() + 1 && "Invalid child");145return Children[i];146}147148DeltaTreeNode *getChild(unsigned i) {149assert(i < getNumValuesUsed() + 1 && "Invalid child");150return Children[i];151}152153static bool classof(const DeltaTreeNode *N) { return !N->isLeaf(); }154};155156} // namespace157158/// Destroy - A 'virtual' destructor.159void DeltaTreeNode::Destroy() {160if (isLeaf())161delete this;162else163delete cast<DeltaTreeInteriorNode>(this);164}165166/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a167/// local walk over our contained deltas.168void DeltaTreeNode::RecomputeFullDeltaLocally() {169int NewFullDelta = 0;170for (unsigned i = 0, e = getNumValuesUsed(); i != e; ++i)171NewFullDelta += Values[i].Delta;172if (auto *IN = dyn_cast<DeltaTreeInteriorNode>(this))173for (unsigned i = 0, e = getNumValuesUsed() + 1; i != e; ++i)174NewFullDelta += IN->getChild(i)->getFullDelta();175FullDelta = NewFullDelta;176}177178/// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into179/// this node. If insertion is easy, do it and return false. Otherwise,180/// split the node, populate InsertRes with info about the split, and return181/// true.182bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta,183InsertResult *InsertRes) {184// Maintain full delta for this node.185FullDelta += Delta;186187// Find the insertion point, the first delta whose index is >= FileIndex.188unsigned i = 0, e = getNumValuesUsed();189while (i != e && FileIndex > getValue(i).FileLoc)190++i;191192// If we found an a record for exactly this file index, just merge this193// value into the pre-existing record and finish early.194if (i != e && getValue(i).FileLoc == FileIndex) {195// NOTE: Delta could drop to zero here. This means that the delta entry is196// useless and could be removed. Supporting erases is more complex than197// leaving an entry with Delta=0, so we just leave an entry with Delta=0 in198// the tree.199Values[i].Delta += Delta;200return false;201}202203// Otherwise, we found an insertion point, and we know that the value at the204// specified index is > FileIndex. Handle the leaf case first.205if (isLeaf()) {206if (!isFull()) {207// For an insertion into a non-full leaf node, just insert the value in208// its sorted position. This requires moving later values over.209if (i != e)210memmove(&Values[i + 1], &Values[i], sizeof(Values[0]) * (e - i));211Values[i] = SourceDelta::get(FileIndex, Delta);212++NumValuesUsed;213return false;214}215216// Otherwise, if this is leaf is full, split the node at its median, insert217// the value into one of the children, and return the result.218assert(InsertRes && "No result location specified");219DoSplit(*InsertRes);220221if (InsertRes->Split.FileLoc > FileIndex)222InsertRes->LHS->DoInsertion(FileIndex, Delta, nullptr /*can't fail*/);223else224InsertRes->RHS->DoInsertion(FileIndex, Delta, nullptr /*can't fail*/);225return true;226}227228// Otherwise, this is an interior node. Send the request down the tree.229auto *IN = cast<DeltaTreeInteriorNode>(this);230if (!IN->Children[i]->DoInsertion(FileIndex, Delta, InsertRes))231return false; // If there was space in the child, just return.232233// Okay, this split the subtree, producing a new value and two children to234// insert here. If this node is non-full, we can just insert it directly.235if (!isFull()) {236// Now that we have two nodes and a new element, insert the perclated value237// into ourself by moving all the later values/children down, then inserting238// the new one.239if (i != e)240memmove(&IN->Children[i + 2], &IN->Children[i + 1],241(e - i) * sizeof(IN->Children[0]));242IN->Children[i] = InsertRes->LHS;243IN->Children[i + 1] = InsertRes->RHS;244245if (e != i)246memmove(&Values[i + 1], &Values[i], (e - i) * sizeof(Values[0]));247Values[i] = InsertRes->Split;248++NumValuesUsed;249return false;250}251252// Finally, if this interior node was full and a node is percolated up, split253// ourself and return that up the chain. Start by saving all our info to254// avoid having the split clobber it.255IN->Children[i] = InsertRes->LHS;256DeltaTreeNode *SubRHS = InsertRes->RHS;257SourceDelta SubSplit = InsertRes->Split;258259// Do the split.260DoSplit(*InsertRes);261262// Figure out where to insert SubRHS/NewSplit.263DeltaTreeInteriorNode *InsertSide;264if (SubSplit.FileLoc < InsertRes->Split.FileLoc)265InsertSide = cast<DeltaTreeInteriorNode>(InsertRes->LHS);266else267InsertSide = cast<DeltaTreeInteriorNode>(InsertRes->RHS);268269// We now have a non-empty interior node 'InsertSide' to insert270// SubRHS/SubSplit into. Find out where to insert SubSplit.271272// Find the insertion point, the first delta whose index is >SubSplit.FileLoc.273i = 0;274e = 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) { return (DeltaTreeNode *)Root; }386387DeltaTree::DeltaTree() { Root = new DeltaTreeNode(); }388389DeltaTree::DeltaTree(const DeltaTree &RHS) {390// Currently we only support copying when the RHS is empty.391assert(getRoot(RHS.Root)->getNumValuesUsed() == 0 &&392"Can only copy empty tree");393Root = new DeltaTreeNode();394}395396DeltaTree::~DeltaTree() { getRoot(Root)->Destroy(); }397398/// getDeltaAt - Return the accumulated delta at the specified file offset.399/// This includes all insertions or delections that occurred *before* the400/// specified file index.401int DeltaTree::getDeltaAt(unsigned FileIndex) const {402const DeltaTreeNode *Node = getRoot(Root);403404int Result = 0;405406// Walk down the tree.407while (true) {408// For all nodes, include any local deltas before the specified file409// index by summing them up directly. Keep track of how many were410// included.411unsigned NumValsGreater = 0;412for (unsigned e = Node->getNumValuesUsed(); NumValsGreater != e;413++NumValsGreater) {414const SourceDelta &Val = Node->getValue(NumValsGreater);415416if (Val.FileLoc >= FileIndex)417break;418Result += Val.Delta;419}420421// If we have an interior node, include information about children and422// recurse. Otherwise, if we have a leaf, we're done.423const auto *IN = dyn_cast<DeltaTreeInteriorNode>(Node);424if (!IN)425return Result;426427// Include any children to the left of the values we skipped, all of428// their deltas should be included as well.429for (unsigned i = 0; i != NumValsGreater; ++i)430Result += IN->getChild(i)->getFullDelta();431432// If we found exactly the value we were looking for, break off the433// search early. There is no need to search the RHS of the value for434// partial results.435if (NumValsGreater != Node->getNumValuesUsed() &&436Node->getValue(NumValsGreater).FileLoc == FileIndex)437return Result + IN->getChild(NumValsGreater)->getFullDelta();438439// Otherwise, traverse down the tree. The selected subtree may be440// partially included in the range.441Node = IN->getChild(NumValsGreater);442}443// NOT REACHED.444}445446/// AddDelta - When a change is made that shifts around the text buffer,447/// this method is used to record that info. It inserts a delta of 'Delta'448/// into the current DeltaTree at offset FileIndex.449void DeltaTree::AddDelta(unsigned FileIndex, int Delta) {450assert(Delta && "Adding a noop?");451DeltaTreeNode *MyRoot = getRoot(Root);452453DeltaTreeNode::InsertResult InsertRes;454if (MyRoot->DoInsertion(FileIndex, Delta, &InsertRes)) {455Root = new DeltaTreeInteriorNode(InsertRes);456#ifdef VERIFY_TREE457MyRoot = Root;458#endif459}460461#ifdef VERIFY_TREE462VerifyTree(MyRoot);463#endif464}465466467