Path: blob/main/contrib/llvm-project/clang/lib/Rewrite/RewriteRope.cpp
35233 views
//===- RewriteRope.cpp - Rope specialized for rewriter --------------------===//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 RewriteRope class, which is a powerful string.9//10//===----------------------------------------------------------------------===//1112#include "clang/Rewrite/Core/RewriteRope.h"13#include "clang/Basic/LLVM.h"14#include "llvm/Support/Casting.h"15#include <algorithm>16#include <cassert>17#include <cstring>1819using namespace clang;2021/// RewriteRope is a "strong" string class, designed to make insertions and22/// deletions in the middle of the string nearly constant time (really, they are23/// O(log N), but with a very low constant factor).24///25/// The implementation of this datastructure is a conceptual linear sequence of26/// RopePiece elements. Each RopePiece represents a view on a separately27/// allocated and reference counted string. This means that splitting a very28/// long string can be done in constant time by splitting a RopePiece that29/// references the whole string into two rope pieces that reference each half.30/// Once split, another string can be inserted in between the two halves by31/// inserting a RopePiece in between the two others. All of this is very32/// inexpensive: it takes time proportional to the number of RopePieces, not the33/// length of the strings they represent.34///35/// While a linear sequences of RopePieces is the conceptual model, the actual36/// implementation captures them in an adapted B+ Tree. Using a B+ tree (which37/// is a tree that keeps the values in the leaves and has where each node38/// contains a reasonable number of pointers to children/values) allows us to39/// maintain efficient operation when the RewriteRope contains a *huge* number40/// of RopePieces. The basic idea of the B+ Tree is that it allows us to find41/// the RopePiece corresponding to some offset very efficiently, and it42/// automatically balances itself on insertions of RopePieces (which can happen43/// for both insertions and erases of string ranges).44///45/// The one wrinkle on the theory is that we don't attempt to keep the tree46/// properly balanced when erases happen. Erases of string data can both insert47/// new RopePieces (e.g. when the middle of some other rope piece is deleted,48/// which results in two rope pieces, which is just like an insert) or it can49/// reduce the number of RopePieces maintained by the B+Tree. In the case when50/// the number of RopePieces is reduced, we don't attempt to maintain the51/// standard 'invariant' that each node in the tree contains at least52/// 'WidthFactor' children/values. For our use cases, this doesn't seem to53/// matter.54///55/// The implementation below is primarily implemented in terms of three classes:56/// RopePieceBTreeNode - Common base class for:57///58/// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece59/// nodes. This directly represents a chunk of the string with those60/// RopePieces concatenated.61/// RopePieceBTreeInterior - An interior node in the B+ Tree, which manages62/// up to '2*WidthFactor' other nodes in the tree.6364namespace {6566//===----------------------------------------------------------------------===//67// RopePieceBTreeNode Class68//===----------------------------------------------------------------------===//6970/// RopePieceBTreeNode - Common base class of RopePieceBTreeLeaf and71/// RopePieceBTreeInterior. This provides some 'virtual' dispatching methods72/// and a flag that determines which subclass the instance is. Also73/// important, this node knows the full extend of the node, including any74/// children that it has. This allows efficient skipping over entire subtrees75/// when looking for an offset in the BTree.76class RopePieceBTreeNode {77protected:78/// WidthFactor - This controls the number of K/V slots held in the BTree:79/// how wide it is. Each level of the BTree is guaranteed to have at least80/// 'WidthFactor' elements in it (either ropepieces or children), (except81/// the root, which may have less) and may have at most 2*WidthFactor82/// elements.83enum { WidthFactor = 8 };8485/// Size - This is the number of bytes of file this node (including any86/// potential children) covers.87unsigned Size = 0;8889/// IsLeaf - True if this is an instance of RopePieceBTreeLeaf, false if it90/// is an instance of RopePieceBTreeInterior.91bool IsLeaf;9293RopePieceBTreeNode(bool isLeaf) : IsLeaf(isLeaf) {}94~RopePieceBTreeNode() = default;9596public:97bool isLeaf() const { return IsLeaf; }98unsigned size() const { return Size; }99100void Destroy();101102/// split - Split the range containing the specified offset so that we are103/// guaranteed that there is a place to do an insertion at the specified104/// offset. The offset is relative, so "0" is the start of the node.105///106/// If there is no space in this subtree for the extra piece, the extra tree107/// node is returned and must be inserted into a parent.108RopePieceBTreeNode *split(unsigned Offset);109110/// insert - Insert the specified ropepiece into this tree node at the111/// specified offset. The offset is relative, so "0" is the start of the112/// node.113///114/// If there is no space in this subtree for the extra piece, the extra tree115/// node is returned and must be inserted into a parent.116RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);117118/// erase - Remove NumBytes from this node at the specified offset. We are119/// guaranteed that there is a split at Offset.120void erase(unsigned Offset, unsigned NumBytes);121};122123//===----------------------------------------------------------------------===//124// RopePieceBTreeLeaf Class125//===----------------------------------------------------------------------===//126127/// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece128/// nodes. This directly represents a chunk of the string with those129/// RopePieces concatenated. Since this is a B+Tree, all values (in this case130/// instances of RopePiece) are stored in leaves like this. To make iteration131/// over the leaves efficient, they maintain a singly linked list through the132/// NextLeaf field. This allows the B+Tree forward iterator to be constant133/// time for all increments.134class RopePieceBTreeLeaf : public RopePieceBTreeNode {135/// NumPieces - This holds the number of rope pieces currently active in the136/// Pieces array.137unsigned char NumPieces = 0;138139/// Pieces - This tracks the file chunks currently in this leaf.140RopePiece Pieces[2*WidthFactor];141142/// NextLeaf - This is a pointer to the next leaf in the tree, allowing143/// efficient in-order forward iteration of the tree without traversal.144RopePieceBTreeLeaf **PrevLeaf = nullptr;145RopePieceBTreeLeaf *NextLeaf = nullptr;146147public:148RopePieceBTreeLeaf() : RopePieceBTreeNode(true) {}149150~RopePieceBTreeLeaf() {151if (PrevLeaf || NextLeaf)152removeFromLeafInOrder();153clear();154}155156bool isFull() const { return NumPieces == 2*WidthFactor; }157158/// clear - Remove all rope pieces from this leaf.159void clear() {160while (NumPieces)161Pieces[--NumPieces] = RopePiece();162Size = 0;163}164165unsigned getNumPieces() const { return NumPieces; }166167const RopePiece &getPiece(unsigned i) const {168assert(i < getNumPieces() && "Invalid piece ID");169return Pieces[i];170}171172const RopePieceBTreeLeaf *getNextLeafInOrder() const { return NextLeaf; }173174void insertAfterLeafInOrder(RopePieceBTreeLeaf *Node) {175assert(!PrevLeaf && !NextLeaf && "Already in ordering");176177NextLeaf = Node->NextLeaf;178if (NextLeaf)179NextLeaf->PrevLeaf = &NextLeaf;180PrevLeaf = &Node->NextLeaf;181Node->NextLeaf = this;182}183184void removeFromLeafInOrder() {185if (PrevLeaf) {186*PrevLeaf = NextLeaf;187if (NextLeaf)188NextLeaf->PrevLeaf = PrevLeaf;189} else if (NextLeaf) {190NextLeaf->PrevLeaf = nullptr;191}192}193194/// FullRecomputeSizeLocally - This method recomputes the 'Size' field by195/// summing the size of all RopePieces.196void FullRecomputeSizeLocally() {197Size = 0;198for (unsigned i = 0, e = getNumPieces(); i != e; ++i)199Size += getPiece(i).size();200}201202/// split - Split the range containing the specified offset so that we are203/// guaranteed that there is a place to do an insertion at the specified204/// offset. The offset is relative, so "0" is the start of the node.205///206/// If there is no space in this subtree for the extra piece, the extra tree207/// node is returned and must be inserted into a parent.208RopePieceBTreeNode *split(unsigned Offset);209210/// insert - Insert the specified ropepiece into this tree node at the211/// specified offset. The offset is relative, so "0" is the start of the212/// node.213///214/// If there is no space in this subtree for the extra piece, the extra tree215/// node is returned and must be inserted into a parent.216RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);217218/// erase - Remove NumBytes from this node at the specified offset. We are219/// guaranteed that there is a split at Offset.220void erase(unsigned Offset, unsigned NumBytes);221222static bool classof(const RopePieceBTreeNode *N) {223return N->isLeaf();224}225};226227} // namespace228229/// split - Split the range containing the specified offset so that we are230/// guaranteed that there is a place to do an insertion at the specified231/// offset. The offset is relative, so "0" is the start of the node.232///233/// If there is no space in this subtree for the extra piece, the extra tree234/// node is returned and must be inserted into a parent.235RopePieceBTreeNode *RopePieceBTreeLeaf::split(unsigned Offset) {236// Find the insertion point. We are guaranteed that there is a split at the237// specified offset so find it.238if (Offset == 0 || Offset == size()) {239// Fastpath for a common case. There is already a splitpoint at the end.240return nullptr;241}242243// Find the piece that this offset lands in.244unsigned PieceOffs = 0;245unsigned i = 0;246while (Offset >= PieceOffs+Pieces[i].size()) {247PieceOffs += Pieces[i].size();248++i;249}250251// If there is already a split point at the specified offset, just return252// success.253if (PieceOffs == Offset)254return nullptr;255256// Otherwise, we need to split piece 'i' at Offset-PieceOffs. Convert Offset257// to being Piece relative.258unsigned IntraPieceOffset = Offset-PieceOffs;259260// We do this by shrinking the RopePiece and then doing an insert of the tail.261RopePiece Tail(Pieces[i].StrData, Pieces[i].StartOffs+IntraPieceOffset,262Pieces[i].EndOffs);263Size -= Pieces[i].size();264Pieces[i].EndOffs = Pieces[i].StartOffs+IntraPieceOffset;265Size += Pieces[i].size();266267return insert(Offset, Tail);268}269270/// insert - Insert the specified RopePiece into this tree node at the271/// specified offset. The offset is relative, so "0" is the start of the node.272///273/// If there is no space in this subtree for the extra piece, the extra tree274/// node is returned and must be inserted into a parent.275RopePieceBTreeNode *RopePieceBTreeLeaf::insert(unsigned Offset,276const RopePiece &R) {277// If this node is not full, insert the piece.278if (!isFull()) {279// Find the insertion point. We are guaranteed that there is a split at the280// specified offset so find it.281unsigned i = 0, e = getNumPieces();282if (Offset == size()) {283// Fastpath for a common case.284i = e;285} else {286unsigned SlotOffs = 0;287for (; Offset > SlotOffs; ++i)288SlotOffs += getPiece(i).size();289assert(SlotOffs == Offset && "Split didn't occur before insertion!");290}291292// For an insertion into a non-full leaf node, just insert the value in293// its sorted position. This requires moving later values over.294for (; i != e; --e)295Pieces[e] = Pieces[e-1];296Pieces[i] = R;297++NumPieces;298Size += R.size();299return nullptr;300}301302// Otherwise, if this is leaf is full, split it in two halves. Since this303// node is full, it contains 2*WidthFactor values. We move the first304// 'WidthFactor' values to the LHS child (which we leave in this node) and305// move the last 'WidthFactor' values into the RHS child.306307// Create the new node.308RopePieceBTreeLeaf *NewNode = new RopePieceBTreeLeaf();309310// Move over the last 'WidthFactor' values from here to NewNode.311std::copy(&Pieces[WidthFactor], &Pieces[2*WidthFactor],312&NewNode->Pieces[0]);313// Replace old pieces with null RopePieces to drop refcounts.314std::fill(&Pieces[WidthFactor], &Pieces[2*WidthFactor], RopePiece());315316// Decrease the number of values in the two nodes.317NewNode->NumPieces = NumPieces = WidthFactor;318319// Recompute the two nodes' size.320NewNode->FullRecomputeSizeLocally();321FullRecomputeSizeLocally();322323// Update the list of leaves.324NewNode->insertAfterLeafInOrder(this);325326// These insertions can't fail.327if (this->size() >= Offset)328this->insert(Offset, R);329else330NewNode->insert(Offset - this->size(), R);331return NewNode;332}333334/// erase - Remove NumBytes from this node at the specified offset. We are335/// guaranteed that there is a split at Offset.336void RopePieceBTreeLeaf::erase(unsigned Offset, unsigned NumBytes) {337// Since we are guaranteed that there is a split at Offset, we start by338// finding the Piece that starts there.339unsigned PieceOffs = 0;340unsigned i = 0;341for (; Offset > PieceOffs; ++i)342PieceOffs += getPiece(i).size();343assert(PieceOffs == Offset && "Split didn't occur before erase!");344345unsigned StartPiece = i;346347// Figure out how many pieces completely cover 'NumBytes'. We want to remove348// all of them.349for (; Offset+NumBytes > PieceOffs+getPiece(i).size(); ++i)350PieceOffs += getPiece(i).size();351352// If we exactly include the last one, include it in the region to delete.353if (Offset+NumBytes == PieceOffs+getPiece(i).size()) {354PieceOffs += getPiece(i).size();355++i;356}357358// If we completely cover some RopePieces, erase them now.359if (i != StartPiece) {360unsigned NumDeleted = i-StartPiece;361for (; i != getNumPieces(); ++i)362Pieces[i-NumDeleted] = Pieces[i];363364// Drop references to dead rope pieces.365std::fill(&Pieces[getNumPieces()-NumDeleted], &Pieces[getNumPieces()],366RopePiece());367NumPieces -= NumDeleted;368369unsigned CoverBytes = PieceOffs-Offset;370NumBytes -= CoverBytes;371Size -= CoverBytes;372}373374// If we completely removed some stuff, we could be done.375if (NumBytes == 0) return;376377// Okay, now might be erasing part of some Piece. If this is the case, then378// move the start point of the piece.379assert(getPiece(StartPiece).size() > NumBytes);380Pieces[StartPiece].StartOffs += NumBytes;381382// The size of this node just shrunk by NumBytes.383Size -= NumBytes;384}385386//===----------------------------------------------------------------------===//387// RopePieceBTreeInterior Class388//===----------------------------------------------------------------------===//389390namespace {391392/// RopePieceBTreeInterior - This represents an interior node in the B+Tree,393/// which holds up to 2*WidthFactor pointers to child nodes.394class RopePieceBTreeInterior : public RopePieceBTreeNode {395/// NumChildren - This holds the number of children currently active in the396/// Children array.397unsigned char NumChildren = 0;398399RopePieceBTreeNode *Children[2*WidthFactor];400401public:402RopePieceBTreeInterior() : RopePieceBTreeNode(false) {}403404RopePieceBTreeInterior(RopePieceBTreeNode *LHS, RopePieceBTreeNode *RHS)405: RopePieceBTreeNode(false) {406Children[0] = LHS;407Children[1] = RHS;408NumChildren = 2;409Size = LHS->size() + RHS->size();410}411412~RopePieceBTreeInterior() {413for (unsigned i = 0, e = getNumChildren(); i != e; ++i)414Children[i]->Destroy();415}416417bool isFull() const { return NumChildren == 2*WidthFactor; }418419unsigned getNumChildren() const { return NumChildren; }420421const RopePieceBTreeNode *getChild(unsigned i) const {422assert(i < NumChildren && "invalid child #");423return Children[i];424}425426RopePieceBTreeNode *getChild(unsigned i) {427assert(i < NumChildren && "invalid child #");428return Children[i];429}430431/// FullRecomputeSizeLocally - Recompute the Size field of this node by432/// summing up the sizes of the child nodes.433void FullRecomputeSizeLocally() {434Size = 0;435for (unsigned i = 0, e = getNumChildren(); i != e; ++i)436Size += getChild(i)->size();437}438439/// split - Split the range containing the specified offset so that we are440/// guaranteed that there is a place to do an insertion at the specified441/// offset. The offset is relative, so "0" is the start of the node.442///443/// If there is no space in this subtree for the extra piece, the extra tree444/// node is returned and must be inserted into a parent.445RopePieceBTreeNode *split(unsigned Offset);446447/// insert - Insert the specified ropepiece into this tree node at the448/// specified offset. The offset is relative, so "0" is the start of the449/// node.450///451/// If there is no space in this subtree for the extra piece, the extra tree452/// node is returned and must be inserted into a parent.453RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);454455/// HandleChildPiece - A child propagated an insertion result up to us.456/// Insert the new child, and/or propagate the result further up the tree.457RopePieceBTreeNode *HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS);458459/// erase - Remove NumBytes from this node at the specified offset. We are460/// guaranteed that there is a split at Offset.461void erase(unsigned Offset, unsigned NumBytes);462463static bool classof(const RopePieceBTreeNode *N) {464return !N->isLeaf();465}466};467468} // namespace469470/// split - Split the range containing the specified offset so that we are471/// guaranteed that there is a place to do an insertion at the specified472/// offset. The offset is relative, so "0" is the start of the node.473///474/// If there is no space in this subtree for the extra piece, the extra tree475/// node is returned and must be inserted into a parent.476RopePieceBTreeNode *RopePieceBTreeInterior::split(unsigned Offset) {477// Figure out which child to split.478if (Offset == 0 || Offset == size())479return nullptr; // If we have an exact offset, we're already split.480481unsigned ChildOffset = 0;482unsigned i = 0;483for (; Offset >= ChildOffset+getChild(i)->size(); ++i)484ChildOffset += getChild(i)->size();485486// If already split there, we're done.487if (ChildOffset == Offset)488return nullptr;489490// Otherwise, recursively split the child.491if (RopePieceBTreeNode *RHS = getChild(i)->split(Offset-ChildOffset))492return HandleChildPiece(i, RHS);493return nullptr; // Done!494}495496/// insert - Insert the specified ropepiece into this tree node at the497/// specified offset. The offset is relative, so "0" is the start of the498/// node.499///500/// If there is no space in this subtree for the extra piece, the extra tree501/// node is returned and must be inserted into a parent.502RopePieceBTreeNode *RopePieceBTreeInterior::insert(unsigned Offset,503const RopePiece &R) {504// Find the insertion point. We are guaranteed that there is a split at the505// specified offset so find it.506unsigned i = 0, e = getNumChildren();507508unsigned ChildOffs = 0;509if (Offset == size()) {510// Fastpath for a common case. Insert at end of last child.511i = e-1;512ChildOffs = size()-getChild(i)->size();513} else {514for (; Offset > ChildOffs+getChild(i)->size(); ++i)515ChildOffs += getChild(i)->size();516}517518Size += R.size();519520// Insert at the end of this child.521if (RopePieceBTreeNode *RHS = getChild(i)->insert(Offset-ChildOffs, R))522return HandleChildPiece(i, RHS);523524return nullptr;525}526527/// HandleChildPiece - A child propagated an insertion result up to us.528/// Insert the new child, and/or propagate the result further up the tree.529RopePieceBTreeNode *530RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) {531// Otherwise the child propagated a subtree up to us as a new child. See if532// we have space for it here.533if (!isFull()) {534// Insert RHS after child 'i'.535if (i + 1 != getNumChildren())536memmove(&Children[i+2], &Children[i+1],537(getNumChildren()-i-1)*sizeof(Children[0]));538Children[i+1] = RHS;539++NumChildren;540return nullptr;541}542543// Okay, this node is full. Split it in half, moving WidthFactor children to544// a newly allocated interior node.545546// Create the new node.547RopePieceBTreeInterior *NewNode = new RopePieceBTreeInterior();548549// Move over the last 'WidthFactor' values from here to NewNode.550memcpy(&NewNode->Children[0], &Children[WidthFactor],551WidthFactor*sizeof(Children[0]));552553// Decrease the number of values in the two nodes.554NewNode->NumChildren = NumChildren = WidthFactor;555556// Finally, insert the two new children in the side the can (now) hold them.557// These insertions can't fail.558if (i < WidthFactor)559this->HandleChildPiece(i, RHS);560else561NewNode->HandleChildPiece(i-WidthFactor, RHS);562563// Recompute the two nodes' size.564NewNode->FullRecomputeSizeLocally();565FullRecomputeSizeLocally();566return NewNode;567}568569/// erase - Remove NumBytes from this node at the specified offset. We are570/// guaranteed that there is a split at Offset.571void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) {572// This will shrink this node by NumBytes.573Size -= NumBytes;574575// Find the first child that overlaps with Offset.576unsigned i = 0;577for (; Offset >= getChild(i)->size(); ++i)578Offset -= getChild(i)->size();579580// Propagate the delete request into overlapping children, or completely581// delete the children as appropriate.582while (NumBytes) {583RopePieceBTreeNode *CurChild = getChild(i);584585// If we are deleting something contained entirely in the child, pass on the586// request.587if (Offset+NumBytes < CurChild->size()) {588CurChild->erase(Offset, NumBytes);589return;590}591592// If this deletion request starts somewhere in the middle of the child, it593// must be deleting to the end of the child.594if (Offset) {595unsigned BytesFromChild = CurChild->size()-Offset;596CurChild->erase(Offset, BytesFromChild);597NumBytes -= BytesFromChild;598// Start at the beginning of the next child.599Offset = 0;600++i;601continue;602}603604// If the deletion request completely covers the child, delete it and move605// the rest down.606NumBytes -= CurChild->size();607CurChild->Destroy();608--NumChildren;609if (i != getNumChildren())610memmove(&Children[i], &Children[i+1],611(getNumChildren()-i)*sizeof(Children[0]));612}613}614615//===----------------------------------------------------------------------===//616// RopePieceBTreeNode Implementation617//===----------------------------------------------------------------------===//618619void RopePieceBTreeNode::Destroy() {620if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(this))621delete Leaf;622else623delete cast<RopePieceBTreeInterior>(this);624}625626/// split - Split the range containing the specified offset so that we are627/// guaranteed that there is a place to do an insertion at the specified628/// offset. The offset is relative, so "0" is the start of the node.629///630/// If there is no space in this subtree for the extra piece, the extra tree631/// node is returned and must be inserted into a parent.632RopePieceBTreeNode *RopePieceBTreeNode::split(unsigned Offset) {633assert(Offset <= size() && "Invalid offset to split!");634if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(this))635return Leaf->split(Offset);636return cast<RopePieceBTreeInterior>(this)->split(Offset);637}638639/// insert - Insert the specified ropepiece into this tree node at the640/// specified offset. The offset is relative, so "0" is the start of the641/// node.642///643/// If there is no space in this subtree for the extra piece, the extra tree644/// node is returned and must be inserted into a parent.645RopePieceBTreeNode *RopePieceBTreeNode::insert(unsigned Offset,646const RopePiece &R) {647assert(Offset <= size() && "Invalid offset to insert!");648if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(this))649return Leaf->insert(Offset, R);650return cast<RopePieceBTreeInterior>(this)->insert(Offset, R);651}652653/// erase - Remove NumBytes from this node at the specified offset. We are654/// guaranteed that there is a split at Offset.655void RopePieceBTreeNode::erase(unsigned Offset, unsigned NumBytes) {656assert(Offset+NumBytes <= size() && "Invalid offset to erase!");657if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(this))658return Leaf->erase(Offset, NumBytes);659return cast<RopePieceBTreeInterior>(this)->erase(Offset, NumBytes);660}661662//===----------------------------------------------------------------------===//663// RopePieceBTreeIterator Implementation664//===----------------------------------------------------------------------===//665666static const RopePieceBTreeLeaf *getCN(const void *P) {667return static_cast<const RopePieceBTreeLeaf*>(P);668}669670// begin iterator.671RopePieceBTreeIterator::RopePieceBTreeIterator(const void *n) {672const auto *N = static_cast<const RopePieceBTreeNode *>(n);673674// Walk down the left side of the tree until we get to a leaf.675while (const auto *IN = dyn_cast<RopePieceBTreeInterior>(N))676N = IN->getChild(0);677678// We must have at least one leaf.679CurNode = cast<RopePieceBTreeLeaf>(N);680681// If we found a leaf that happens to be empty, skip over it until we get682// to something full.683while (CurNode && getCN(CurNode)->getNumPieces() == 0)684CurNode = getCN(CurNode)->getNextLeafInOrder();685686if (CurNode)687CurPiece = &getCN(CurNode)->getPiece(0);688else // Empty tree, this is an end() iterator.689CurPiece = nullptr;690CurChar = 0;691}692693void RopePieceBTreeIterator::MoveToNextPiece() {694if (CurPiece != &getCN(CurNode)->getPiece(getCN(CurNode)->getNumPieces()-1)) {695CurChar = 0;696++CurPiece;697return;698}699700// Find the next non-empty leaf node.701do702CurNode = getCN(CurNode)->getNextLeafInOrder();703while (CurNode && getCN(CurNode)->getNumPieces() == 0);704705if (CurNode)706CurPiece = &getCN(CurNode)->getPiece(0);707else // Hit end().708CurPiece = nullptr;709CurChar = 0;710}711712//===----------------------------------------------------------------------===//713// RopePieceBTree Implementation714//===----------------------------------------------------------------------===//715716static RopePieceBTreeNode *getRoot(void *P) {717return static_cast<RopePieceBTreeNode*>(P);718}719720RopePieceBTree::RopePieceBTree() {721Root = new RopePieceBTreeLeaf();722}723724RopePieceBTree::RopePieceBTree(const RopePieceBTree &RHS) {725assert(RHS.empty() && "Can't copy non-empty tree yet");726Root = new RopePieceBTreeLeaf();727}728729RopePieceBTree::~RopePieceBTree() {730getRoot(Root)->Destroy();731}732733unsigned RopePieceBTree::size() const {734return getRoot(Root)->size();735}736737void RopePieceBTree::clear() {738if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(getRoot(Root)))739Leaf->clear();740else {741getRoot(Root)->Destroy();742Root = new RopePieceBTreeLeaf();743}744}745746void RopePieceBTree::insert(unsigned Offset, const RopePiece &R) {747// #1. Split at Offset.748if (RopePieceBTreeNode *RHS = getRoot(Root)->split(Offset))749Root = new RopePieceBTreeInterior(getRoot(Root), RHS);750751// #2. Do the insertion.752if (RopePieceBTreeNode *RHS = getRoot(Root)->insert(Offset, R))753Root = new RopePieceBTreeInterior(getRoot(Root), RHS);754}755756void RopePieceBTree::erase(unsigned Offset, unsigned NumBytes) {757// #1. Split at Offset.758if (RopePieceBTreeNode *RHS = getRoot(Root)->split(Offset))759Root = new RopePieceBTreeInterior(getRoot(Root), RHS);760761// #2. Do the erasing.762getRoot(Root)->erase(Offset, NumBytes);763}764765//===----------------------------------------------------------------------===//766// RewriteRope Implementation767//===----------------------------------------------------------------------===//768769/// MakeRopeString - This copies the specified byte range into some instance of770/// RopeRefCountString, and return a RopePiece that represents it. This uses771/// the AllocBuffer object to aggregate requests for small strings into one772/// allocation instead of doing tons of tiny allocations.773RopePiece RewriteRope::MakeRopeString(const char *Start, const char *End) {774unsigned Len = End-Start;775assert(Len && "Zero length RopePiece is invalid!");776777// If we have space for this string in the current alloc buffer, use it.778if (AllocOffs+Len <= AllocChunkSize) {779memcpy(AllocBuffer->Data+AllocOffs, Start, Len);780AllocOffs += Len;781return RopePiece(AllocBuffer, AllocOffs-Len, AllocOffs);782}783784// If we don't have enough room because this specific allocation is huge,785// just allocate a new rope piece for it alone.786if (Len > AllocChunkSize) {787unsigned Size = End-Start+sizeof(RopeRefCountString)-1;788auto *Res = reinterpret_cast<RopeRefCountString *>(new char[Size]);789Res->RefCount = 0;790memcpy(Res->Data, Start, End-Start);791return RopePiece(Res, 0, End-Start);792}793794// Otherwise, this was a small request but we just don't have space for it795// Make a new chunk and share it with later allocations.796797unsigned AllocSize = offsetof(RopeRefCountString, Data) + AllocChunkSize;798auto *Res = reinterpret_cast<RopeRefCountString *>(new char[AllocSize]);799Res->RefCount = 0;800memcpy(Res->Data, Start, Len);801AllocBuffer = Res;802AllocOffs = Len;803804return RopePiece(AllocBuffer, 0, Len);805}806807808