[llvm][clang] Move RewriterBuffer to ADT. (#99770)
These classes are not specific to clang and useful for other rewriter tools (flagged in previous review).
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
//===- DeltaTree.h - B-Tree for Rewrite Delta tracking ----------*- C++ -*-===//
|
||||
//
|
||||
// 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 defines the DeltaTree class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_REWRITE_CORE_DELTATREE_H
|
||||
#define LLVM_CLANG_REWRITE_CORE_DELTATREE_H
|
||||
|
||||
namespace clang {
|
||||
|
||||
/// DeltaTree - 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. This
|
||||
/// implements a key/value mapping from index to delta, and allows fast lookup
|
||||
/// on index. 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.
|
||||
class DeltaTree {
|
||||
void *Root; // "DeltaTreeNode *"
|
||||
|
||||
public:
|
||||
DeltaTree();
|
||||
|
||||
// Note: Currently we only support copying when the RHS is empty.
|
||||
DeltaTree(const DeltaTree &RHS);
|
||||
|
||||
DeltaTree &operator=(const DeltaTree &) = delete;
|
||||
~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 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 AddDelta(unsigned FileIndex, int Delta);
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_REWRITE_CORE_DELTATREE_H
|
||||
@@ -17,10 +17,13 @@
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include <string>
|
||||
|
||||
namespace llvm {
|
||||
class RewriteBuffer;
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
|
||||
class Rewriter;
|
||||
class RewriteBuffer;
|
||||
class Preprocessor;
|
||||
|
||||
namespace html {
|
||||
@@ -53,9 +56,9 @@ namespace html {
|
||||
|
||||
/// HighlightRange - This is the same as the above method, but takes
|
||||
/// decomposed file locations.
|
||||
void HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
|
||||
const char *BufferStart,
|
||||
const char *StartTag, const char *EndTag);
|
||||
void HighlightRange(llvm::RewriteBuffer &RB, unsigned B, unsigned E,
|
||||
const char *BufferStart, const char *StartTag,
|
||||
const char *EndTag);
|
||||
|
||||
/// EscapeText - HTMLize a specified file so that special characters are
|
||||
/// are translated so that they are not interpreted as HTML tags.
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
//===- RewriteRope.h - Rope specialized for rewriter ------------*- C++ -*-===//
|
||||
//
|
||||
// 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 defines the RewriteRope class, which is a powerful string class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_REWRITE_CORE_REWRITEROPE_H
|
||||
#define LLVM_CLANG_REWRITE_CORE_REWRITEROPE_H
|
||||
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopeRefCountString Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RopeRefCountString - This struct is allocated with 'new char[]' from the
|
||||
/// heap, and represents a reference counted chunk of string data. When its
|
||||
/// ref count drops to zero, it is delete[]'d. This is primarily managed
|
||||
/// through the RopePiece class below.
|
||||
struct RopeRefCountString {
|
||||
unsigned RefCount;
|
||||
char Data[1]; // Variable sized.
|
||||
|
||||
void Retain() { ++RefCount; }
|
||||
|
||||
void Release() {
|
||||
assert(RefCount > 0 && "Reference count is already zero.");
|
||||
if (--RefCount == 0)
|
||||
delete [] (char*)this;
|
||||
}
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopePiece Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RopePiece - This class represents a view into a RopeRefCountString object.
|
||||
/// This allows references to string data to be efficiently chopped up and
|
||||
/// moved around without having to push around the string data itself.
|
||||
///
|
||||
/// For example, we could have a 1M RopePiece and want to insert something
|
||||
/// into the middle of it. To do this, we split it into two RopePiece objects
|
||||
/// that both refer to the same underlying RopeRefCountString (just with
|
||||
/// different offsets) which is a nice constant time operation.
|
||||
struct RopePiece {
|
||||
llvm::IntrusiveRefCntPtr<RopeRefCountString> StrData;
|
||||
unsigned StartOffs = 0;
|
||||
unsigned EndOffs = 0;
|
||||
|
||||
RopePiece() = default;
|
||||
RopePiece(llvm::IntrusiveRefCntPtr<RopeRefCountString> Str, unsigned Start,
|
||||
unsigned End)
|
||||
: StrData(std::move(Str)), StartOffs(Start), EndOffs(End) {}
|
||||
|
||||
const char &operator[](unsigned Offset) const {
|
||||
return StrData->Data[Offset+StartOffs];
|
||||
}
|
||||
char &operator[](unsigned Offset) {
|
||||
return StrData->Data[Offset+StartOffs];
|
||||
}
|
||||
|
||||
unsigned size() const { return EndOffs-StartOffs; }
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopePieceBTreeIterator Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RopePieceBTreeIterator - This class provides read-only forward iteration
|
||||
/// over bytes that are in a RopePieceBTree. This first iterates over bytes
|
||||
/// in a RopePiece, then iterates over RopePiece's in a RopePieceBTreeLeaf,
|
||||
/// then iterates over RopePieceBTreeLeaf's in a RopePieceBTree.
|
||||
class RopePieceBTreeIterator {
|
||||
/// CurNode - The current B+Tree node that we are inspecting.
|
||||
const void /*RopePieceBTreeLeaf*/ *CurNode = nullptr;
|
||||
|
||||
/// CurPiece - The current RopePiece in the B+Tree node that we're
|
||||
/// inspecting.
|
||||
const RopePiece *CurPiece = nullptr;
|
||||
|
||||
/// CurChar - The current byte in the RopePiece we are pointing to.
|
||||
unsigned CurChar = 0;
|
||||
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const char;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
RopePieceBTreeIterator() = default;
|
||||
RopePieceBTreeIterator(const void /*RopePieceBTreeNode*/ *N);
|
||||
|
||||
char operator*() const {
|
||||
return (*CurPiece)[CurChar];
|
||||
}
|
||||
|
||||
bool operator==(const RopePieceBTreeIterator &RHS) const {
|
||||
return CurPiece == RHS.CurPiece && CurChar == RHS.CurChar;
|
||||
}
|
||||
bool operator!=(const RopePieceBTreeIterator &RHS) const {
|
||||
return !operator==(RHS);
|
||||
}
|
||||
|
||||
RopePieceBTreeIterator& operator++() { // Preincrement
|
||||
if (CurChar+1 < CurPiece->size())
|
||||
++CurChar;
|
||||
else
|
||||
MoveToNextPiece();
|
||||
return *this;
|
||||
}
|
||||
|
||||
RopePieceBTreeIterator operator++(int) { // Postincrement
|
||||
RopePieceBTreeIterator tmp = *this; ++*this; return tmp;
|
||||
}
|
||||
|
||||
llvm::StringRef piece() const {
|
||||
return llvm::StringRef(&(*CurPiece)[0], CurPiece->size());
|
||||
}
|
||||
|
||||
void MoveToNextPiece();
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopePieceBTree Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
class RopePieceBTree {
|
||||
void /*RopePieceBTreeNode*/ *Root;
|
||||
|
||||
public:
|
||||
RopePieceBTree();
|
||||
RopePieceBTree(const RopePieceBTree &RHS);
|
||||
RopePieceBTree &operator=(const RopePieceBTree &) = delete;
|
||||
~RopePieceBTree();
|
||||
|
||||
using iterator = RopePieceBTreeIterator;
|
||||
|
||||
iterator begin() const { return iterator(Root); }
|
||||
iterator end() const { return iterator(); }
|
||||
unsigned size() const;
|
||||
unsigned empty() const { return size() == 0; }
|
||||
|
||||
void clear();
|
||||
|
||||
void insert(unsigned Offset, const RopePiece &R);
|
||||
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RewriteRope Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RewriteRope - A powerful string class. This class supports extremely
|
||||
/// efficient insertions and deletions into the middle of it, even for
|
||||
/// ridiculously long strings.
|
||||
class RewriteRope {
|
||||
RopePieceBTree Chunks;
|
||||
|
||||
/// We allocate space for string data out of a buffer of size AllocChunkSize.
|
||||
/// This keeps track of how much space is left.
|
||||
llvm::IntrusiveRefCntPtr<RopeRefCountString> AllocBuffer;
|
||||
enum { AllocChunkSize = 4080 };
|
||||
unsigned AllocOffs = AllocChunkSize;
|
||||
|
||||
public:
|
||||
RewriteRope() = default;
|
||||
RewriteRope(const RewriteRope &RHS) : Chunks(RHS.Chunks) {}
|
||||
|
||||
// The copy assignment operator is defined as deleted pending further
|
||||
// motivation.
|
||||
RewriteRope &operator=(const RewriteRope &) = delete;
|
||||
|
||||
using iterator = RopePieceBTree::iterator;
|
||||
using const_iterator = RopePieceBTree::iterator;
|
||||
|
||||
iterator begin() const { return Chunks.begin(); }
|
||||
iterator end() const { return Chunks.end(); }
|
||||
unsigned size() const { return Chunks.size(); }
|
||||
|
||||
void clear() {
|
||||
Chunks.clear();
|
||||
}
|
||||
|
||||
void assign(const char *Start, const char *End) {
|
||||
clear();
|
||||
if (Start != End)
|
||||
Chunks.insert(0, MakeRopeString(Start, End));
|
||||
}
|
||||
|
||||
void insert(unsigned Offset, const char *Start, const char *End) {
|
||||
assert(Offset <= size() && "Invalid position to insert!");
|
||||
if (Start == End) return;
|
||||
Chunks.insert(Offset, MakeRopeString(Start, End));
|
||||
}
|
||||
|
||||
void erase(unsigned Offset, unsigned NumBytes) {
|
||||
assert(Offset+NumBytes <= size() && "Invalid region to erase!");
|
||||
if (NumBytes == 0) return;
|
||||
Chunks.erase(Offset, NumBytes);
|
||||
}
|
||||
|
||||
private:
|
||||
RopePiece MakeRopeString(const char *Start, const char *End);
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_REWRITE_CORE_REWRITEROPE_H
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Rewrite/Core/RewriteBuffer.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -32,7 +32,7 @@ class SourceManager;
|
||||
class Rewriter {
|
||||
SourceManager *SourceMgr = nullptr;
|
||||
const LangOptions *LangOpts = nullptr;
|
||||
std::map<FileID, RewriteBuffer> RewriteBuffers;
|
||||
std::map<FileID, llvm::RewriteBuffer> RewriteBuffers;
|
||||
|
||||
public:
|
||||
struct RewriteOptions {
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
///
|
||||
/// FIXME: This sometimes corrupts the file's rewrite buffer due to
|
||||
/// incorrect indexing in the implementation (see the FIXME in
|
||||
/// clang::RewriteBuffer::RemoveText). Moreover, it's inefficient because
|
||||
/// llvm::RewriteBuffer::RemoveText). Moreover, it's inefficient because
|
||||
/// it must scan the buffer from the beginning to find the start of the
|
||||
/// line. When feasible, it's better for the caller to check for a blank
|
||||
/// line and then, if found, expand the removal range to include it.
|
||||
@@ -62,8 +62,9 @@ public:
|
||||
RewriteOptions() {}
|
||||
};
|
||||
|
||||
using buffer_iterator = std::map<FileID, RewriteBuffer>::iterator;
|
||||
using const_buffer_iterator = std::map<FileID, RewriteBuffer>::const_iterator;
|
||||
using buffer_iterator = std::map<FileID, llvm::RewriteBuffer>::iterator;
|
||||
using const_buffer_iterator =
|
||||
std::map<FileID, llvm::RewriteBuffer>::const_iterator;
|
||||
|
||||
explicit Rewriter() = default;
|
||||
explicit Rewriter(SourceManager &SM, const LangOptions &LO)
|
||||
@@ -191,13 +192,13 @@ public:
|
||||
/// buffer, and allows you to write on it directly. This is useful if you
|
||||
/// want efficient low-level access to apis for scribbling on one specific
|
||||
/// FileID's buffer.
|
||||
RewriteBuffer &getEditBuffer(FileID FID);
|
||||
llvm::RewriteBuffer &getEditBuffer(FileID FID);
|
||||
|
||||
/// getRewriteBufferFor - Return the rewrite buffer for the specified FileID.
|
||||
/// If no modification has been made to it, return null.
|
||||
const RewriteBuffer *getRewriteBufferFor(FileID FID) const {
|
||||
std::map<FileID, RewriteBuffer>::const_iterator I =
|
||||
RewriteBuffers.find(FID);
|
||||
const llvm::RewriteBuffer *getRewriteBufferFor(FileID FID) const {
|
||||
std::map<FileID, llvm::RewriteBuffer>::const_iterator I =
|
||||
RewriteBuffers.find(FID);
|
||||
return I == RewriteBuffers.end() ? nullptr : &I->second;
|
||||
}
|
||||
|
||||
|
||||
@@ -596,7 +596,7 @@ bool MigrationProcess::applyTransform(TransformFn trans,
|
||||
for (Rewriter::buffer_iterator
|
||||
I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
|
||||
FileID FID = I->first;
|
||||
RewriteBuffer &buf = I->second;
|
||||
llvm::RewriteBuffer &buf = I->second;
|
||||
OptionalFileEntryRef file =
|
||||
Ctx.getSourceManager().getFileEntryRefForID(FID);
|
||||
assert(file);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
using namespace clang;
|
||||
using namespace arcmt;
|
||||
using namespace ento;
|
||||
using llvm::RewriteBuffer;
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#include "clang/Edit/Commit.h"
|
||||
#include "clang/Edit/EditsReceiver.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Rewrite/Core/RewriteBuffer.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <utility>
|
||||
|
||||
using namespace clang;
|
||||
using llvm::RewriteBuffer;
|
||||
|
||||
FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
|
||||
const LangOptions &LangOpts,
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
#include "clang/Rewrite/Core/HTMLRewrite.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Rewrite/Frontend/ASTConsumers.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
using namespace clang;
|
||||
using llvm::RewriteBuffer;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Functional HTML pretty-printing.
|
||||
|
||||
@@ -11,16 +11,18 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Rewrite/Frontend/Rewriters.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Rewrite/Frontend/Rewriters.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
using namespace clang;
|
||||
using llvm::RewriteBuffer;
|
||||
|
||||
/// isSameToken - Return true if the two specified tokens start have the same
|
||||
/// content.
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Rewrite/Frontend/ASTConsumers.h"
|
||||
#include "clang/AST/AST.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
@@ -23,6 +22,7 @@
|
||||
#include "clang/Config/config.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/Rewrite/Frontend/ASTConsumers.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
@@ -34,6 +34,7 @@
|
||||
#if CLANG_ENABLE_OBJC_REWRITER
|
||||
|
||||
using namespace clang;
|
||||
using llvm::RewriteBuffer;
|
||||
using llvm::utostr;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#if CLANG_ENABLE_OBJC_REWRITER
|
||||
|
||||
using namespace clang;
|
||||
using llvm::RewriteBuffer;
|
||||
using llvm::utostr;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -3,9 +3,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
)
|
||||
|
||||
add_clang_library(clangRewrite
|
||||
DeltaTree.cpp
|
||||
HTMLRewrite.cpp
|
||||
RewriteRope.cpp
|
||||
Rewriter.cpp
|
||||
TokenRewriter.cpp
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Lex/TokenConcatenation.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Rewrite/Core/RewriteBuffer.h"
|
||||
#include "clang/Rewrite/Core/RewriteRope.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/ADT/RewriteRope.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
@@ -29,113 +29,18 @@
|
||||
#include <utility>
|
||||
|
||||
using namespace clang;
|
||||
|
||||
raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
|
||||
// Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the
|
||||
// character iterator.
|
||||
for (RopePieceBTreeIterator I = begin(), E = end(); I != E;
|
||||
I.MoveToNextPiece())
|
||||
os << I.piece();
|
||||
return os;
|
||||
}
|
||||
|
||||
/// Return true if this character is non-new-line whitespace:
|
||||
/// ' ', '\\t', '\\f', '\\v', '\\r'.
|
||||
static inline bool isWhitespaceExceptNL(unsigned char c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\f':
|
||||
case '\v':
|
||||
case '\r':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
|
||||
bool removeLineIfEmpty) {
|
||||
// Nothing to remove, exit early.
|
||||
if (Size == 0) return;
|
||||
|
||||
unsigned RealOffset = getMappedOffset(OrigOffset, true);
|
||||
assert(RealOffset+Size <= Buffer.size() && "Invalid location");
|
||||
|
||||
// Remove the dead characters.
|
||||
Buffer.erase(RealOffset, Size);
|
||||
|
||||
// Add a delta so that future changes are offset correctly.
|
||||
AddReplaceDelta(OrigOffset, -Size);
|
||||
|
||||
if (removeLineIfEmpty) {
|
||||
// Find the line that the remove occurred and if it is completely empty
|
||||
// remove the line as well.
|
||||
|
||||
iterator curLineStart = begin();
|
||||
unsigned curLineStartOffs = 0;
|
||||
iterator posI = begin();
|
||||
for (unsigned i = 0; i != RealOffset; ++i) {
|
||||
if (*posI == '\n') {
|
||||
curLineStart = posI;
|
||||
++curLineStart;
|
||||
curLineStartOffs = i + 1;
|
||||
}
|
||||
++posI;
|
||||
}
|
||||
|
||||
unsigned lineSize = 0;
|
||||
posI = curLineStart;
|
||||
while (posI != end() && isWhitespaceExceptNL(*posI)) {
|
||||
++posI;
|
||||
++lineSize;
|
||||
}
|
||||
if (posI != end() && *posI == '\n') {
|
||||
Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
|
||||
// FIXME: Here, the offset of the start of the line is supposed to be
|
||||
// expressed in terms of the original input not the "real" rewrite
|
||||
// buffer. How do we compute that reliably? It might be tempting to use
|
||||
// curLineStartOffs + OrigOffset - RealOffset, but that assumes the
|
||||
// difference between the original and real offset is the same at the
|
||||
// removed text and at the start of the line, but that's not true if
|
||||
// edits were previously made earlier on the line. This bug is also
|
||||
// documented by a FIXME on the definition of
|
||||
// clang::Rewriter::RewriteOptions::RemoveLineIfEmpty. A reproducer for
|
||||
// the implementation below is the test RemoveLineIfEmpty in
|
||||
// clang/unittests/Rewrite/RewriteBufferTest.cpp.
|
||||
AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
|
||||
bool InsertAfter) {
|
||||
// Nothing to insert, exit early.
|
||||
if (Str.empty()) return;
|
||||
|
||||
unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
|
||||
Buffer.insert(RealOffset, Str.begin(), Str.end());
|
||||
|
||||
// Add a delta so that future changes are offset correctly.
|
||||
AddInsertDelta(OrigOffset, Str.size());
|
||||
}
|
||||
|
||||
/// ReplaceText - This method replaces a range of characters in the input
|
||||
/// buffer with a new string. This is effectively a combined "remove+insert"
|
||||
/// operation.
|
||||
void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
|
||||
StringRef NewStr) {
|
||||
unsigned RealOffset = getMappedOffset(OrigOffset, true);
|
||||
Buffer.erase(RealOffset, OrigLength);
|
||||
Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
|
||||
if (OrigLength != NewStr.size())
|
||||
AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
|
||||
}
|
||||
using llvm::RewriteBuffer;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Rewriter class
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Return true if this character is non-new-line whitespace:
|
||||
/// ' ', '\\t', '\\f', '\\v', '\\r'.
|
||||
static inline bool isWhitespaceExceptNL(unsigned char c) {
|
||||
return c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r';
|
||||
}
|
||||
|
||||
/// getRangeSize - Return the size in bytes of the specified range if they
|
||||
/// are in the same file. If not, this returns -1.
|
||||
int Rewriter::getRangeSize(const CharSourceRange &Range,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Sequence.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
@@ -52,6 +53,7 @@
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
using llvm::RewriteBuffer;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Boilerplate.
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Rewrite/Core/RewriteBuffer.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS
|
||||
)
|
||||
|
||||
add_clang_unittest(RewriteTests
|
||||
RewriteBufferTest.cpp
|
||||
RewriterTest.cpp
|
||||
)
|
||||
clang_target_link_libraries(RewriteTests
|
||||
|
||||
50
llvm/include/llvm/ADT/DeltaTree.h
Normal file
50
llvm/include/llvm/ADT/DeltaTree.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//===- DeltaTree.h - B-Tree for Rewrite Delta tracking ----------*- C++ -*-===//
|
||||
//
|
||||
// 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 defines the DeltaTree class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_ADT_DELTATREE_H
|
||||
#define LLVM_ADT_DELTATREE_H
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// DeltaTree - 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. This
|
||||
/// implements a key/value mapping from index to delta, and allows fast lookup
|
||||
/// on index. 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.
|
||||
class DeltaTree {
|
||||
void *Root; // "DeltaTreeNode *"
|
||||
|
||||
public:
|
||||
DeltaTree();
|
||||
|
||||
// Note: Currently we only support copying when the RHS is empty.
|
||||
DeltaTree(const DeltaTree &RHS);
|
||||
|
||||
DeltaTree &operator=(const DeltaTree &) = delete;
|
||||
~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 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 AddDelta(unsigned FileIndex, int Delta);
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_ADT_DELTATREE_H
|
||||
@@ -6,15 +6,20 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_REWRITE_CORE_REWRITEBUFFER_H
|
||||
#define LLVM_CLANG_REWRITE_CORE_REWRITEBUFFER_H
|
||||
#ifndef LLVM_ADT_REWRITEBUFFER_H
|
||||
#define LLVM_ADT_REWRITEBUFFER_H
|
||||
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Rewrite/Core/DeltaTree.h"
|
||||
#include "clang/Rewrite/Core/RewriteRope.h"
|
||||
#include "llvm/ADT/DeltaTree.h"
|
||||
#include "llvm/ADT/RewriteRope.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace clang {
|
||||
class Rewriter;
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class raw_ostream;
|
||||
|
||||
/// RewriteBuffer - As code is rewritten, SourceBuffer's from the original
|
||||
/// input with modifications get a new RewriteBuffer associated with them. The
|
||||
@@ -23,7 +28,7 @@ namespace clang {
|
||||
/// RewriteBuffer. For example, if text is inserted into the buffer, any
|
||||
/// locations after the insertion point have to be mapped.
|
||||
class RewriteBuffer {
|
||||
friend class Rewriter;
|
||||
friend class clang::Rewriter;
|
||||
|
||||
/// Deltas - Keep track of all the deltas in the source code due to insertions
|
||||
/// and deletions.
|
||||
@@ -43,9 +48,7 @@ public:
|
||||
void Initialize(const char *BufStart, const char *BufEnd) {
|
||||
Buffer.assign(BufStart, BufEnd);
|
||||
}
|
||||
void Initialize(StringRef Input) {
|
||||
Initialize(Input.begin(), Input.end());
|
||||
}
|
||||
void Initialize(StringRef Input) { Initialize(Input.begin(), Input.end()); }
|
||||
|
||||
/// Write to \p Stream the result of applying all changes to the
|
||||
/// original buffer.
|
||||
@@ -63,9 +66,7 @@ public:
|
||||
/// InsertText - Insert some text at the specified point, where the offset in
|
||||
/// the buffer is specified relative to the original SourceBuffer. The
|
||||
/// text is inserted after the specified location.
|
||||
void InsertText(unsigned OrigOffset, StringRef Str,
|
||||
bool InsertAfter = true);
|
||||
|
||||
void InsertText(unsigned OrigOffset, StringRef Str, bool InsertAfter = true);
|
||||
|
||||
/// InsertTextBefore - Insert some text before the specified point, where the
|
||||
/// offset in the buffer is specified relative to the original
|
||||
@@ -85,8 +86,7 @@ public:
|
||||
/// ReplaceText - This method replaces a range of characters in the input
|
||||
/// buffer with a new string. This is effectively a combined "remove/insert"
|
||||
/// operation.
|
||||
void ReplaceText(unsigned OrigOffset, unsigned OrigLength,
|
||||
StringRef NewStr);
|
||||
void ReplaceText(unsigned OrigOffset, unsigned OrigLength, StringRef NewStr);
|
||||
|
||||
private:
|
||||
/// getMappedOffset - Given an offset into the original SourceBuffer that this
|
||||
@@ -95,23 +95,23 @@ private:
|
||||
/// position where text is inserted, the location returned will be after any
|
||||
/// inserted text at the position.
|
||||
unsigned getMappedOffset(unsigned OrigOffset,
|
||||
bool AfterInserts = false) const{
|
||||
return Deltas.getDeltaAt(2*OrigOffset+AfterInserts)+OrigOffset;
|
||||
bool AfterInserts = false) const {
|
||||
return Deltas.getDeltaAt(2 * OrigOffset + AfterInserts) + OrigOffset;
|
||||
}
|
||||
|
||||
/// AddInsertDelta - When an insertion is made at a position, this
|
||||
/// method is used to record that information.
|
||||
void AddInsertDelta(unsigned OrigOffset, int Change) {
|
||||
return Deltas.AddDelta(2*OrigOffset, Change);
|
||||
return Deltas.AddDelta(2 * OrigOffset, Change);
|
||||
}
|
||||
|
||||
/// AddReplaceDelta - When a replacement/deletion is made at a position, this
|
||||
/// method is used to record that information.
|
||||
void AddReplaceDelta(unsigned OrigOffset, int Change) {
|
||||
return Deltas.AddDelta(2*OrigOffset+1, Change);
|
||||
return Deltas.AddDelta(2 * OrigOffset + 1, Change);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_CLANG_REWRITE_CORE_REWRITEBUFFER_H
|
||||
#endif // LLVM_ADT_REWRITEBUFFER_H
|
||||
223
llvm/include/llvm/ADT/RewriteRope.h
Normal file
223
llvm/include/llvm/ADT/RewriteRope.h
Normal file
@@ -0,0 +1,223 @@
|
||||
//===- RewriteRope.h - Rope specialized for rewriter ------------*- C++ -*-===//
|
||||
//
|
||||
// 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 defines the RewriteRope class, which is a powerful string class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_ADT_REWRITEROPE_H
|
||||
#define LLVM_ADT_REWRITEROPE_H
|
||||
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopeRefCountString Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RopeRefCountString - This struct is allocated with 'new char[]' from the
|
||||
/// heap, and represents a reference counted chunk of string data. When its
|
||||
/// ref count drops to zero, it is delete[]'d. This is primarily managed
|
||||
/// through the RopePiece class below.
|
||||
struct RopeRefCountString {
|
||||
unsigned RefCount;
|
||||
char Data[1]; // Variable sized.
|
||||
|
||||
void Retain() { ++RefCount; }
|
||||
|
||||
void Release() {
|
||||
assert(RefCount > 0 && "Reference count is already zero.");
|
||||
if (--RefCount == 0)
|
||||
delete[] (char *)this;
|
||||
}
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopePiece Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RopePiece - This class represents a view into a RopeRefCountString object.
|
||||
/// This allows references to string data to be efficiently chopped up and
|
||||
/// moved around without having to push around the string data itself.
|
||||
///
|
||||
/// For example, we could have a 1M RopePiece and want to insert something
|
||||
/// into the middle of it. To do this, we split it into two RopePiece objects
|
||||
/// that both refer to the same underlying RopeRefCountString (just with
|
||||
/// different offsets) which is a nice constant time operation.
|
||||
struct RopePiece {
|
||||
llvm::IntrusiveRefCntPtr<RopeRefCountString> StrData;
|
||||
unsigned StartOffs = 0;
|
||||
unsigned EndOffs = 0;
|
||||
|
||||
RopePiece() = default;
|
||||
RopePiece(llvm::IntrusiveRefCntPtr<RopeRefCountString> Str, unsigned Start,
|
||||
unsigned End)
|
||||
: StrData(std::move(Str)), StartOffs(Start), EndOffs(End) {}
|
||||
|
||||
const char &operator[](unsigned Offset) const {
|
||||
return StrData->Data[Offset + StartOffs];
|
||||
}
|
||||
char &operator[](unsigned Offset) {
|
||||
return StrData->Data[Offset + StartOffs];
|
||||
}
|
||||
|
||||
unsigned size() const { return EndOffs - StartOffs; }
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopePieceBTreeIterator Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RopePieceBTreeIterator - This class provides read-only forward iteration
|
||||
/// over bytes that are in a RopePieceBTree. This first iterates over bytes
|
||||
/// in a RopePiece, then iterates over RopePiece's in a RopePieceBTreeLeaf,
|
||||
/// then iterates over RopePieceBTreeLeaf's in a RopePieceBTree.
|
||||
class RopePieceBTreeIterator {
|
||||
/// CurNode - The current B+Tree node that we are inspecting.
|
||||
const void /*RopePieceBTreeLeaf*/ *CurNode = nullptr;
|
||||
|
||||
/// CurPiece - The current RopePiece in the B+Tree node that we're
|
||||
/// inspecting.
|
||||
const RopePiece *CurPiece = nullptr;
|
||||
|
||||
/// CurChar - The current byte in the RopePiece we are pointing to.
|
||||
unsigned CurChar = 0;
|
||||
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = const char;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
|
||||
RopePieceBTreeIterator() = default;
|
||||
RopePieceBTreeIterator(const void /*RopePieceBTreeNode*/ *N);
|
||||
|
||||
char operator*() const { return (*CurPiece)[CurChar]; }
|
||||
|
||||
bool operator==(const RopePieceBTreeIterator &RHS) const {
|
||||
return CurPiece == RHS.CurPiece && CurChar == RHS.CurChar;
|
||||
}
|
||||
bool operator!=(const RopePieceBTreeIterator &RHS) const {
|
||||
return !operator==(RHS);
|
||||
}
|
||||
|
||||
RopePieceBTreeIterator &operator++() { // Preincrement
|
||||
if (CurChar + 1 < CurPiece->size())
|
||||
++CurChar;
|
||||
else
|
||||
MoveToNextPiece();
|
||||
return *this;
|
||||
}
|
||||
|
||||
RopePieceBTreeIterator operator++(int) { // Postincrement
|
||||
RopePieceBTreeIterator tmp = *this;
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
llvm::StringRef piece() const {
|
||||
return llvm::StringRef(&(*CurPiece)[0], CurPiece->size());
|
||||
}
|
||||
|
||||
void MoveToNextPiece();
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RopePieceBTree Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
class RopePieceBTree {
|
||||
void /*RopePieceBTreeNode*/ *Root;
|
||||
|
||||
public:
|
||||
RopePieceBTree();
|
||||
RopePieceBTree(const RopePieceBTree &RHS);
|
||||
RopePieceBTree &operator=(const RopePieceBTree &) = delete;
|
||||
~RopePieceBTree();
|
||||
|
||||
using iterator = RopePieceBTreeIterator;
|
||||
|
||||
iterator begin() const { return iterator(Root); }
|
||||
iterator end() const { return iterator(); }
|
||||
unsigned size() const;
|
||||
unsigned empty() const { return size() == 0; }
|
||||
|
||||
void clear();
|
||||
|
||||
void insert(unsigned Offset, const RopePiece &R);
|
||||
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// RewriteRope Class
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
/// RewriteRope - A powerful string class. This class supports extremely
|
||||
/// efficient insertions and deletions into the middle of it, even for
|
||||
/// ridiculously long strings.
|
||||
class RewriteRope {
|
||||
RopePieceBTree Chunks;
|
||||
|
||||
/// We allocate space for string data out of a buffer of size AllocChunkSize.
|
||||
/// This keeps track of how much space is left.
|
||||
llvm::IntrusiveRefCntPtr<RopeRefCountString> AllocBuffer;
|
||||
enum { AllocChunkSize = 4080 };
|
||||
unsigned AllocOffs = AllocChunkSize;
|
||||
|
||||
public:
|
||||
RewriteRope() = default;
|
||||
RewriteRope(const RewriteRope &RHS) : Chunks(RHS.Chunks) {}
|
||||
|
||||
// The copy assignment operator is defined as deleted pending further
|
||||
// motivation.
|
||||
RewriteRope &operator=(const RewriteRope &) = delete;
|
||||
|
||||
using iterator = RopePieceBTree::iterator;
|
||||
using const_iterator = RopePieceBTree::iterator;
|
||||
|
||||
iterator begin() const { return Chunks.begin(); }
|
||||
iterator end() const { return Chunks.end(); }
|
||||
unsigned size() const { return Chunks.size(); }
|
||||
|
||||
void clear() { Chunks.clear(); }
|
||||
|
||||
void assign(const char *Start, const char *End) {
|
||||
clear();
|
||||
if (Start != End)
|
||||
Chunks.insert(0, MakeRopeString(Start, End));
|
||||
}
|
||||
|
||||
void insert(unsigned Offset, const char *Start, const char *End) {
|
||||
assert(Offset <= size() && "Invalid position to insert!");
|
||||
if (Start == End)
|
||||
return;
|
||||
Chunks.insert(Offset, MakeRopeString(Start, End));
|
||||
}
|
||||
|
||||
void erase(unsigned Offset, unsigned NumBytes) {
|
||||
assert(Offset + NumBytes <= size() && "Invalid region to erase!");
|
||||
if (NumBytes == 0)
|
||||
return;
|
||||
Chunks.erase(Offset, NumBytes);
|
||||
}
|
||||
|
||||
private:
|
||||
RopePiece MakeRopeString(const char *Start, const char *End);
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_ADT_REWRITEROPE_H
|
||||
@@ -168,6 +168,7 @@ add_llvm_component_library(LLVMSupport
|
||||
Debug.cpp
|
||||
DebugCounter.cpp
|
||||
DeltaAlgorithm.cpp
|
||||
DeltaTree.cpp
|
||||
DivisionByConstantInfo.cpp
|
||||
DAGDeltaAlgorithm.cpp
|
||||
DJB.cpp
|
||||
@@ -216,6 +217,8 @@ add_llvm_component_library(LLVMSupport
|
||||
PrettyStackTrace.cpp
|
||||
RandomNumberGenerator.cpp
|
||||
Regex.cpp
|
||||
RewriteBuffer.cpp
|
||||
RewriteRope.cpp
|
||||
RISCVAttributes.cpp
|
||||
RISCVAttributeParser.cpp
|
||||
RISCVISAUtils.cpp
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Rewrite/Core/DeltaTree.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/DeltaTree.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
/// The DeltaTree class is a multiway search tree (BTree) structure with some
|
||||
/// fancy features. B-Trees are generally more memory and cache efficient
|
||||
@@ -35,125 +34,125 @@ using namespace clang;
|
||||
|
||||
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 {
|
||||
unsigned FileLoc;
|
||||
int Delta;
|
||||
/// 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 {
|
||||
unsigned FileLoc;
|
||||
int Delta;
|
||||
|
||||
static SourceDelta get(unsigned Loc, int D) {
|
||||
SourceDelta Delta;
|
||||
Delta.FileLoc = Loc;
|
||||
Delta.Delta = D;
|
||||
return Delta;
|
||||
}
|
||||
static SourceDelta get(unsigned Loc, int D) {
|
||||
SourceDelta Delta;
|
||||
Delta.FileLoc = Loc;
|
||||
Delta.Delta = D;
|
||||
return Delta;
|
||||
}
|
||||
};
|
||||
|
||||
/// DeltaTreeNode - The common part of all nodes.
|
||||
///
|
||||
class DeltaTreeNode {
|
||||
public:
|
||||
struct InsertResult {
|
||||
DeltaTreeNode *LHS, *RHS;
|
||||
SourceDelta Split;
|
||||
};
|
||||
|
||||
/// DeltaTreeNode - The common part of all nodes.
|
||||
///
|
||||
class DeltaTreeNode {
|
||||
public:
|
||||
struct InsertResult {
|
||||
DeltaTreeNode *LHS, *RHS;
|
||||
SourceDelta Split;
|
||||
};
|
||||
private:
|
||||
friend class DeltaTreeInteriorNode;
|
||||
|
||||
private:
|
||||
friend class DeltaTreeInteriorNode;
|
||||
/// WidthFactor - This controls the number of K/V slots held in the BTree:
|
||||
/// how wide it is. Each level of the BTree is guaranteed to have at least
|
||||
/// WidthFactor-1 K/V pairs (except the root) and may have at most
|
||||
/// 2*WidthFactor-1 K/V pairs.
|
||||
enum { WidthFactor = 8 };
|
||||
|
||||
/// WidthFactor - This controls the number of K/V slots held in the BTree:
|
||||
/// how wide it is. Each level of the BTree is guaranteed to have at least
|
||||
/// WidthFactor-1 K/V pairs (except the root) and may have at most
|
||||
/// 2*WidthFactor-1 K/V pairs.
|
||||
enum { WidthFactor = 8 };
|
||||
/// Values - This tracks the SourceDelta's currently in this node.
|
||||
SourceDelta Values[2 * WidthFactor - 1];
|
||||
|
||||
/// Values - This tracks the SourceDelta's currently in this node.
|
||||
SourceDelta Values[2*WidthFactor-1];
|
||||
/// NumValuesUsed - This tracks the number of values this node currently
|
||||
/// holds.
|
||||
unsigned char NumValuesUsed = 0;
|
||||
|
||||
/// NumValuesUsed - This tracks the number of values this node currently
|
||||
/// holds.
|
||||
unsigned char NumValuesUsed = 0;
|
||||
/// IsLeaf - This is true if this is a leaf of the btree. If false, this is
|
||||
/// an interior node, and is actually an instance of DeltaTreeInteriorNode.
|
||||
bool IsLeaf;
|
||||
|
||||
/// IsLeaf - This is true if this is a leaf of the btree. If false, this is
|
||||
/// an interior node, and is actually an instance of DeltaTreeInteriorNode.
|
||||
bool IsLeaf;
|
||||
/// FullDelta - This is the full delta of all the values in this node and
|
||||
/// all children nodes.
|
||||
int FullDelta = 0;
|
||||
|
||||
/// FullDelta - This is the full delta of all the values in this node and
|
||||
/// all children nodes.
|
||||
int FullDelta = 0;
|
||||
public:
|
||||
DeltaTreeNode(bool isLeaf = true) : IsLeaf(isLeaf) {}
|
||||
|
||||
public:
|
||||
DeltaTreeNode(bool isLeaf = true) : IsLeaf(isLeaf) {}
|
||||
bool isLeaf() const { return IsLeaf; }
|
||||
int getFullDelta() const { return FullDelta; }
|
||||
bool isFull() const { return NumValuesUsed == 2 * WidthFactor - 1; }
|
||||
|
||||
bool isLeaf() const { return IsLeaf; }
|
||||
int getFullDelta() const { return FullDelta; }
|
||||
bool isFull() const { return NumValuesUsed == 2*WidthFactor-1; }
|
||||
unsigned getNumValuesUsed() const { return NumValuesUsed; }
|
||||
|
||||
unsigned getNumValuesUsed() const { return NumValuesUsed; }
|
||||
const SourceDelta &getValue(unsigned i) const {
|
||||
assert(i < NumValuesUsed && "Invalid value #");
|
||||
return Values[i];
|
||||
}
|
||||
|
||||
const SourceDelta &getValue(unsigned i) const {
|
||||
assert(i < NumValuesUsed && "Invalid value #");
|
||||
return Values[i];
|
||||
}
|
||||
SourceDelta &getValue(unsigned i) {
|
||||
assert(i < NumValuesUsed && "Invalid value #");
|
||||
return Values[i];
|
||||
}
|
||||
|
||||
SourceDelta &getValue(unsigned i) {
|
||||
assert(i < NumValuesUsed && "Invalid value #");
|
||||
return Values[i];
|
||||
}
|
||||
/// 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 DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes);
|
||||
|
||||
/// 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 DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes);
|
||||
void DoSplit(InsertResult &InsertRes);
|
||||
|
||||
void DoSplit(InsertResult &InsertRes);
|
||||
/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a
|
||||
/// local walk over our contained deltas.
|
||||
void RecomputeFullDeltaLocally();
|
||||
|
||||
void Destroy();
|
||||
};
|
||||
|
||||
/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a
|
||||
/// local walk over our contained deltas.
|
||||
void RecomputeFullDeltaLocally();
|
||||
/// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers.
|
||||
/// This class tracks them.
|
||||
class DeltaTreeInteriorNode : public DeltaTreeNode {
|
||||
friend class DeltaTreeNode;
|
||||
|
||||
void Destroy();
|
||||
};
|
||||
DeltaTreeNode *Children[2 * WidthFactor];
|
||||
|
||||
/// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers.
|
||||
/// This class tracks them.
|
||||
class DeltaTreeInteriorNode : public DeltaTreeNode {
|
||||
friend class DeltaTreeNode;
|
||||
~DeltaTreeInteriorNode() {
|
||||
for (unsigned i = 0, e = NumValuesUsed + 1; i != e; ++i)
|
||||
Children[i]->Destroy();
|
||||
}
|
||||
|
||||
DeltaTreeNode *Children[2*WidthFactor];
|
||||
public:
|
||||
DeltaTreeInteriorNode() : DeltaTreeNode(false /*nonleaf*/) {}
|
||||
|
||||
~DeltaTreeInteriorNode() {
|
||||
for (unsigned i = 0, e = NumValuesUsed+1; i != e; ++i)
|
||||
Children[i]->Destroy();
|
||||
}
|
||||
DeltaTreeInteriorNode(const InsertResult &IR)
|
||||
: DeltaTreeNode(false /*nonleaf*/) {
|
||||
Children[0] = IR.LHS;
|
||||
Children[1] = IR.RHS;
|
||||
Values[0] = IR.Split;
|
||||
FullDelta =
|
||||
IR.LHS->getFullDelta() + IR.RHS->getFullDelta() + IR.Split.Delta;
|
||||
NumValuesUsed = 1;
|
||||
}
|
||||
|
||||
public:
|
||||
DeltaTreeInteriorNode() : DeltaTreeNode(false /*nonleaf*/) {}
|
||||
const DeltaTreeNode *getChild(unsigned i) const {
|
||||
assert(i < getNumValuesUsed() + 1 && "Invalid child");
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
DeltaTreeInteriorNode(const InsertResult &IR)
|
||||
: DeltaTreeNode(false /*nonleaf*/) {
|
||||
Children[0] = IR.LHS;
|
||||
Children[1] = IR.RHS;
|
||||
Values[0] = IR.Split;
|
||||
FullDelta = IR.LHS->getFullDelta()+IR.RHS->getFullDelta()+IR.Split.Delta;
|
||||
NumValuesUsed = 1;
|
||||
}
|
||||
DeltaTreeNode *getChild(unsigned i) {
|
||||
assert(i < getNumValuesUsed() + 1 && "Invalid child");
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
const DeltaTreeNode *getChild(unsigned i) const {
|
||||
assert(i < getNumValuesUsed()+1 && "Invalid child");
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
DeltaTreeNode *getChild(unsigned i) {
|
||||
assert(i < getNumValuesUsed()+1 && "Invalid child");
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
static bool classof(const DeltaTreeNode *N) { return !N->isLeaf(); }
|
||||
};
|
||||
static bool classof(const DeltaTreeNode *N) { return !N->isLeaf(); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -172,7 +171,7 @@ void DeltaTreeNode::RecomputeFullDeltaLocally() {
|
||||
for (unsigned i = 0, e = getNumValuesUsed(); i != e; ++i)
|
||||
NewFullDelta += Values[i].Delta;
|
||||
if (auto *IN = dyn_cast<DeltaTreeInteriorNode>(this))
|
||||
for (unsigned i = 0, e = getNumValuesUsed()+1; i != e; ++i)
|
||||
for (unsigned i = 0, e = getNumValuesUsed() + 1; i != e; ++i)
|
||||
NewFullDelta += IN->getChild(i)->getFullDelta();
|
||||
FullDelta = NewFullDelta;
|
||||
}
|
||||
@@ -209,7 +208,7 @@ bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta,
|
||||
// For an insertion into a non-full leaf node, just insert the value in
|
||||
// its sorted position. This requires moving later values over.
|
||||
if (i != e)
|
||||
memmove(&Values[i+1], &Values[i], sizeof(Values[0])*(e-i));
|
||||
memmove(&Values[i + 1], &Values[i], sizeof(Values[0]) * (e - i));
|
||||
Values[i] = SourceDelta::get(FileIndex, Delta);
|
||||
++NumValuesUsed;
|
||||
return false;
|
||||
@@ -239,13 +238,13 @@ bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta,
|
||||
// into ourself by moving all the later values/children down, then inserting
|
||||
// the new one.
|
||||
if (i != e)
|
||||
memmove(&IN->Children[i+2], &IN->Children[i+1],
|
||||
(e-i)*sizeof(IN->Children[0]));
|
||||
memmove(&IN->Children[i + 2], &IN->Children[i + 1],
|
||||
(e - i) * sizeof(IN->Children[0]));
|
||||
IN->Children[i] = InsertRes->LHS;
|
||||
IN->Children[i+1] = InsertRes->RHS;
|
||||
IN->Children[i + 1] = InsertRes->RHS;
|
||||
|
||||
if (e != i)
|
||||
memmove(&Values[i+1], &Values[i], (e-i)*sizeof(Values[0]));
|
||||
memmove(&Values[i + 1], &Values[i], (e - i) * sizeof(Values[0]));
|
||||
Values[i] = InsertRes->Split;
|
||||
++NumValuesUsed;
|
||||
return false;
|
||||
@@ -272,20 +271,21 @@ bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta,
|
||||
// SubRHS/SubSplit into. Find out where to insert SubSplit.
|
||||
|
||||
// Find the insertion point, the first delta whose index is >SubSplit.FileLoc.
|
||||
i = 0; e = InsertSide->getNumValuesUsed();
|
||||
i = 0;
|
||||
e = InsertSide->getNumValuesUsed();
|
||||
while (i != e && SubSplit.FileLoc > InsertSide->getValue(i).FileLoc)
|
||||
++i;
|
||||
|
||||
// Now we know that i is the place to insert the split value into. Insert it
|
||||
// and the child right after it.
|
||||
if (i != e)
|
||||
memmove(&InsertSide->Children[i+2], &InsertSide->Children[i+1],
|
||||
(e-i)*sizeof(IN->Children[0]));
|
||||
InsertSide->Children[i+1] = SubRHS;
|
||||
memmove(&InsertSide->Children[i + 2], &InsertSide->Children[i + 1],
|
||||
(e - i) * sizeof(IN->Children[0]));
|
||||
InsertSide->Children[i + 1] = SubRHS;
|
||||
|
||||
if (e != i)
|
||||
memmove(&InsertSide->Values[i+1], &InsertSide->Values[i],
|
||||
(e-i)*sizeof(Values[0]));
|
||||
memmove(&InsertSide->Values[i + 1], &InsertSide->Values[i],
|
||||
(e - i) * sizeof(Values[0]));
|
||||
InsertSide->Values[i] = SubSplit;
|
||||
++InsertSide->NumValuesUsed;
|
||||
InsertSide->FullDelta += SubSplit.Delta + SubRHS->getFullDelta();
|
||||
@@ -310,7 +310,7 @@ void DeltaTreeNode::DoSplit(InsertResult &InsertRes) {
|
||||
// into the new node.
|
||||
DeltaTreeInteriorNode *New = new DeltaTreeInteriorNode();
|
||||
memcpy(&New->Children[0], &IN->Children[WidthFactor],
|
||||
WidthFactor*sizeof(IN->Children[0]));
|
||||
WidthFactor * sizeof(IN->Children[0]));
|
||||
NewNode = New;
|
||||
} else {
|
||||
// Just create the new leaf node.
|
||||
@@ -319,10 +319,10 @@ void DeltaTreeNode::DoSplit(InsertResult &InsertRes) {
|
||||
|
||||
// Move over the last 'WidthFactor-1' values from here to NewNode.
|
||||
memcpy(&NewNode->Values[0], &Values[WidthFactor],
|
||||
(WidthFactor-1)*sizeof(Values[0]));
|
||||
(WidthFactor - 1) * sizeof(Values[0]));
|
||||
|
||||
// Decrease the number of values in the two nodes.
|
||||
NewNode->NumValuesUsed = NumValuesUsed = WidthFactor-1;
|
||||
NewNode->NumValuesUsed = NumValuesUsed = WidthFactor - 1;
|
||||
|
||||
// Recompute the two nodes' full delta.
|
||||
NewNode->RecomputeFullDeltaLocally();
|
||||
@@ -330,14 +330,14 @@ void DeltaTreeNode::DoSplit(InsertResult &InsertRes) {
|
||||
|
||||
InsertRes.LHS = this;
|
||||
InsertRes.RHS = NewNode;
|
||||
InsertRes.Split = Values[WidthFactor-1];
|
||||
InsertRes.Split = Values[WidthFactor - 1];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DeltaTree Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//#define VERIFY_TREE
|
||||
// #define VERIFY_TREE
|
||||
|
||||
#ifdef VERIFY_TREE
|
||||
/// VerifyTree - Walk the btree performing assertions on various properties to
|
||||
@@ -350,7 +350,7 @@ static void VerifyTree(const DeltaTreeNode *N) {
|
||||
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);
|
||||
assert(N->getValue(i - 1).FileLoc < N->getValue(i).FileLoc);
|
||||
FullDelta += N->getValue(i).Delta;
|
||||
}
|
||||
assert(FullDelta == N->getFullDelta());
|
||||
@@ -364,16 +364,16 @@ static void VerifyTree(const DeltaTreeNode *N) {
|
||||
const SourceDelta &IVal = N->getValue(i);
|
||||
const DeltaTreeNode *IChild = IN->getChild(i);
|
||||
if (i)
|
||||
assert(IN->getValue(i-1).FileLoc < IVal.FileLoc);
|
||||
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 <
|
||||
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);
|
||||
assert(IN->getChild(i + 1)->getValue(0).FileLoc > IVal.FileLoc);
|
||||
VerifyTree(IChild);
|
||||
}
|
||||
|
||||
@@ -381,15 +381,11 @@ static void VerifyTree(const DeltaTreeNode *N) {
|
||||
|
||||
assert(FullDelta == N->getFullDelta());
|
||||
}
|
||||
#endif // VERIFY_TREE
|
||||
#endif // VERIFY_TREE
|
||||
|
||||
static DeltaTreeNode *getRoot(void *Root) {
|
||||
return (DeltaTreeNode*)Root;
|
||||
}
|
||||
static DeltaTreeNode *getRoot(void *Root) { return (DeltaTreeNode *)Root; }
|
||||
|
||||
DeltaTree::DeltaTree() {
|
||||
Root = new DeltaTreeNode();
|
||||
}
|
||||
DeltaTree::DeltaTree() { Root = new DeltaTreeNode(); }
|
||||
|
||||
DeltaTree::DeltaTree(const DeltaTree &RHS) {
|
||||
// Currently we only support copying when the RHS is empty.
|
||||
@@ -398,9 +394,7 @@ DeltaTree::DeltaTree(const DeltaTree &RHS) {
|
||||
Root = new DeltaTreeNode();
|
||||
}
|
||||
|
||||
DeltaTree::~DeltaTree() {
|
||||
getRoot(Root)->Destroy();
|
||||
}
|
||||
DeltaTree::~DeltaTree() { getRoot(Root)->Destroy(); }
|
||||
|
||||
/// getDeltaAt - Return the accumulated delta at the specified file offset.
|
||||
/// This includes all insertions or delections that occurred *before* the
|
||||
@@ -428,7 +422,8 @@ int DeltaTree::getDeltaAt(unsigned FileIndex) const {
|
||||
// If we have an interior node, include information about children and
|
||||
// recurse. Otherwise, if we have a leaf, we're done.
|
||||
const auto *IN = dyn_cast<DeltaTreeInteriorNode>(Node);
|
||||
if (!IN) return Result;
|
||||
if (!IN)
|
||||
return Result;
|
||||
|
||||
// Include any children to the left of the values we skipped, all of
|
||||
// their deltas should be included as well.
|
||||
@@ -440,7 +435,7 @@ int DeltaTree::getDeltaAt(unsigned FileIndex) const {
|
||||
// partial results.
|
||||
if (NumValsGreater != Node->getNumValuesUsed() &&
|
||||
Node->getValue(NumValsGreater).FileLoc == FileIndex)
|
||||
return Result+IN->getChild(NumValsGreater)->getFullDelta();
|
||||
return Result + IN->getChild(NumValsGreater)->getFullDelta();
|
||||
|
||||
// Otherwise, traverse down the tree. The selected subtree may be
|
||||
// partially included in the range.
|
||||
107
llvm/lib/Support/RewriteBuffer.cpp
Normal file
107
llvm/lib/Support/RewriteBuffer.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
//===- RewriteBuffer.h - Buffer rewriting interface -----------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
raw_ostream &RewriteBuffer::write(raw_ostream &Stream) const {
|
||||
// Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the
|
||||
// character iterator.
|
||||
for (RopePieceBTreeIterator I = begin(), E = end(); I != E;
|
||||
I.MoveToNextPiece())
|
||||
Stream << I.piece();
|
||||
return Stream;
|
||||
}
|
||||
|
||||
/// Return true if this character is non-new-line whitespace:
|
||||
/// ' ', '\\t', '\\f', '\\v', '\\r'.
|
||||
static inline bool isWhitespaceExceptNL(unsigned char c) {
|
||||
return c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r';
|
||||
}
|
||||
|
||||
void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
|
||||
bool removeLineIfEmpty) {
|
||||
// Nothing to remove, exit early.
|
||||
if (Size == 0)
|
||||
return;
|
||||
|
||||
unsigned RealOffset = getMappedOffset(OrigOffset, true);
|
||||
assert(RealOffset + Size <= Buffer.size() && "Invalid location");
|
||||
|
||||
// Remove the dead characters.
|
||||
Buffer.erase(RealOffset, Size);
|
||||
|
||||
// Add a delta so that future changes are offset correctly.
|
||||
AddReplaceDelta(OrigOffset, -Size);
|
||||
|
||||
if (removeLineIfEmpty) {
|
||||
// Find the line that the remove occurred and if it is completely empty
|
||||
// remove the line as well.
|
||||
|
||||
iterator curLineStart = begin();
|
||||
unsigned curLineStartOffs = 0;
|
||||
iterator posI = begin();
|
||||
for (unsigned i = 0; i != RealOffset; ++i) {
|
||||
if (*posI == '\n') {
|
||||
curLineStart = posI;
|
||||
++curLineStart;
|
||||
curLineStartOffs = i + 1;
|
||||
}
|
||||
++posI;
|
||||
}
|
||||
|
||||
unsigned lineSize = 0;
|
||||
posI = curLineStart;
|
||||
while (posI != end() && isWhitespaceExceptNL(*posI)) {
|
||||
++posI;
|
||||
++lineSize;
|
||||
}
|
||||
if (posI != end() && *posI == '\n') {
|
||||
Buffer.erase(curLineStartOffs, lineSize + 1 /* + '\n'*/);
|
||||
// FIXME: Here, the offset of the start of the line is supposed to be
|
||||
// expressed in terms of the original input not the "real" rewrite
|
||||
// buffer. How do we compute that reliably? It might be tempting to use
|
||||
// curLineStartOffs + OrigOffset - RealOffset, but that assumes the
|
||||
// difference between the original and real offset is the same at the
|
||||
// removed text and at the start of the line, but that's not true if
|
||||
// edits were previously made earlier on the line. This bug is also
|
||||
// documented by a FIXME on the definition of
|
||||
// clang::Rewriter::RewriteOptions::RemoveLineIfEmpty. A reproducer for
|
||||
// the implementation below is the test RemoveLineIfEmpty in
|
||||
// clang/unittests/Rewrite/RewriteBufferTest.cpp.
|
||||
AddReplaceDelta(curLineStartOffs, -(lineSize + 1 /* + '\n'*/));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
|
||||
bool InsertAfter) {
|
||||
// Nothing to insert, exit early.
|
||||
if (Str.empty())
|
||||
return;
|
||||
|
||||
unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
|
||||
Buffer.insert(RealOffset, Str.begin(), Str.end());
|
||||
|
||||
// Add a delta so that future changes are offset correctly.
|
||||
AddInsertDelta(OrigOffset, Str.size());
|
||||
}
|
||||
|
||||
/// ReplaceText - This method replaces a range of characters in the input
|
||||
/// buffer with a new string. This is effectively a combined "remove+insert"
|
||||
/// operation.
|
||||
void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
|
||||
StringRef NewStr) {
|
||||
unsigned RealOffset = getMappedOffset(OrigOffset, true);
|
||||
Buffer.erase(RealOffset, OrigLength);
|
||||
Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
|
||||
if (OrigLength != NewStr.size())
|
||||
AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
|
||||
}
|
||||
@@ -10,14 +10,13 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Rewrite/Core/RewriteRope.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/RewriteRope.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
using namespace clang;
|
||||
using namespace llvm;
|
||||
|
||||
/// RewriteRope is a "strong" string class, designed to make insertions and
|
||||
/// deletions in the middle of the string nearly constant time (really, they are
|
||||
@@ -68,162 +67,160 @@ namespace {
|
||||
// RopePieceBTreeNode Class
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// RopePieceBTreeNode - Common base class of RopePieceBTreeLeaf and
|
||||
/// RopePieceBTreeInterior. This provides some 'virtual' dispatching methods
|
||||
/// and a flag that determines which subclass the instance is. Also
|
||||
/// important, this node knows the full extend of the node, including any
|
||||
/// children that it has. This allows efficient skipping over entire subtrees
|
||||
/// when looking for an offset in the BTree.
|
||||
class RopePieceBTreeNode {
|
||||
protected:
|
||||
/// WidthFactor - This controls the number of K/V slots held in the BTree:
|
||||
/// how wide it is. Each level of the BTree is guaranteed to have at least
|
||||
/// 'WidthFactor' elements in it (either ropepieces or children), (except
|
||||
/// the root, which may have less) and may have at most 2*WidthFactor
|
||||
/// elements.
|
||||
enum { WidthFactor = 8 };
|
||||
/// RopePieceBTreeNode - Common base class of RopePieceBTreeLeaf and
|
||||
/// RopePieceBTreeInterior. This provides some 'virtual' dispatching methods
|
||||
/// and a flag that determines which subclass the instance is. Also
|
||||
/// important, this node knows the full extend of the node, including any
|
||||
/// children that it has. This allows efficient skipping over entire subtrees
|
||||
/// when looking for an offset in the BTree.
|
||||
class RopePieceBTreeNode {
|
||||
protected:
|
||||
/// WidthFactor - This controls the number of K/V slots held in the BTree:
|
||||
/// how wide it is. Each level of the BTree is guaranteed to have at least
|
||||
/// 'WidthFactor' elements in it (either ropepieces or children), (except
|
||||
/// the root, which may have less) and may have at most 2*WidthFactor
|
||||
/// elements.
|
||||
enum { WidthFactor = 8 };
|
||||
|
||||
/// Size - This is the number of bytes of file this node (including any
|
||||
/// potential children) covers.
|
||||
unsigned Size = 0;
|
||||
/// Size - This is the number of bytes of file this node (including any
|
||||
/// potential children) covers.
|
||||
unsigned Size = 0;
|
||||
|
||||
/// IsLeaf - True if this is an instance of RopePieceBTreeLeaf, false if it
|
||||
/// is an instance of RopePieceBTreeInterior.
|
||||
bool IsLeaf;
|
||||
/// IsLeaf - True if this is an instance of RopePieceBTreeLeaf, false if it
|
||||
/// is an instance of RopePieceBTreeInterior.
|
||||
bool IsLeaf;
|
||||
|
||||
RopePieceBTreeNode(bool isLeaf) : IsLeaf(isLeaf) {}
|
||||
~RopePieceBTreeNode() = default;
|
||||
RopePieceBTreeNode(bool isLeaf) : IsLeaf(isLeaf) {}
|
||||
~RopePieceBTreeNode() = default;
|
||||
|
||||
public:
|
||||
bool isLeaf() const { return IsLeaf; }
|
||||
unsigned size() const { return Size; }
|
||||
public:
|
||||
bool isLeaf() const { return IsLeaf; }
|
||||
unsigned size() const { return Size; }
|
||||
|
||||
void Destroy();
|
||||
void Destroy();
|
||||
|
||||
/// split - Split the range containing the specified offset so that we are
|
||||
/// guaranteed that there is a place to do an insertion at the specified
|
||||
/// offset. The offset is relative, so "0" is the start of the node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *split(unsigned Offset);
|
||||
/// split - Split the range containing the specified offset so that we are
|
||||
/// guaranteed that there is a place to do an insertion at the specified
|
||||
/// offset. The offset is relative, so "0" is the start of the node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *split(unsigned Offset);
|
||||
|
||||
/// insert - Insert the specified ropepiece into this tree node at the
|
||||
/// specified offset. The offset is relative, so "0" is the start of the
|
||||
/// node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);
|
||||
/// insert - Insert the specified ropepiece into this tree node at the
|
||||
/// specified offset. The offset is relative, so "0" is the start of the
|
||||
/// node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);
|
||||
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
};
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// RopePieceBTreeLeaf Class
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece
|
||||
/// nodes. This directly represents a chunk of the string with those
|
||||
/// RopePieces concatenated. Since this is a B+Tree, all values (in this case
|
||||
/// instances of RopePiece) are stored in leaves like this. To make iteration
|
||||
/// over the leaves efficient, they maintain a singly linked list through the
|
||||
/// NextLeaf field. This allows the B+Tree forward iterator to be constant
|
||||
/// time for all increments.
|
||||
class RopePieceBTreeLeaf : public RopePieceBTreeNode {
|
||||
/// NumPieces - This holds the number of rope pieces currently active in the
|
||||
/// Pieces array.
|
||||
unsigned char NumPieces = 0;
|
||||
/// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece
|
||||
/// nodes. This directly represents a chunk of the string with those
|
||||
/// RopePieces concatenated. Since this is a B+Tree, all values (in this case
|
||||
/// instances of RopePiece) are stored in leaves like this. To make iteration
|
||||
/// over the leaves efficient, they maintain a singly linked list through the
|
||||
/// NextLeaf field. This allows the B+Tree forward iterator to be constant
|
||||
/// time for all increments.
|
||||
class RopePieceBTreeLeaf : public RopePieceBTreeNode {
|
||||
/// NumPieces - This holds the number of rope pieces currently active in the
|
||||
/// Pieces array.
|
||||
unsigned char NumPieces = 0;
|
||||
|
||||
/// Pieces - This tracks the file chunks currently in this leaf.
|
||||
RopePiece Pieces[2*WidthFactor];
|
||||
/// Pieces - This tracks the file chunks currently in this leaf.
|
||||
RopePiece Pieces[2 * WidthFactor];
|
||||
|
||||
/// NextLeaf - This is a pointer to the next leaf in the tree, allowing
|
||||
/// efficient in-order forward iteration of the tree without traversal.
|
||||
RopePieceBTreeLeaf **PrevLeaf = nullptr;
|
||||
RopePieceBTreeLeaf *NextLeaf = nullptr;
|
||||
/// NextLeaf - This is a pointer to the next leaf in the tree, allowing
|
||||
/// efficient in-order forward iteration of the tree without traversal.
|
||||
RopePieceBTreeLeaf **PrevLeaf = nullptr;
|
||||
RopePieceBTreeLeaf *NextLeaf = nullptr;
|
||||
|
||||
public:
|
||||
RopePieceBTreeLeaf() : RopePieceBTreeNode(true) {}
|
||||
public:
|
||||
RopePieceBTreeLeaf() : RopePieceBTreeNode(true) {}
|
||||
|
||||
~RopePieceBTreeLeaf() {
|
||||
if (PrevLeaf || NextLeaf)
|
||||
removeFromLeafInOrder();
|
||||
clear();
|
||||
}
|
||||
~RopePieceBTreeLeaf() {
|
||||
if (PrevLeaf || NextLeaf)
|
||||
removeFromLeafInOrder();
|
||||
clear();
|
||||
}
|
||||
|
||||
bool isFull() const { return NumPieces == 2*WidthFactor; }
|
||||
bool isFull() const { return NumPieces == 2 * WidthFactor; }
|
||||
|
||||
/// clear - Remove all rope pieces from this leaf.
|
||||
void clear() {
|
||||
while (NumPieces)
|
||||
Pieces[--NumPieces] = RopePiece();
|
||||
Size = 0;
|
||||
}
|
||||
/// clear - Remove all rope pieces from this leaf.
|
||||
void clear() {
|
||||
while (NumPieces)
|
||||
Pieces[--NumPieces] = RopePiece();
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
unsigned getNumPieces() const { return NumPieces; }
|
||||
unsigned getNumPieces() const { return NumPieces; }
|
||||
|
||||
const RopePiece &getPiece(unsigned i) const {
|
||||
assert(i < getNumPieces() && "Invalid piece ID");
|
||||
return Pieces[i];
|
||||
}
|
||||
const RopePiece &getPiece(unsigned i) const {
|
||||
assert(i < getNumPieces() && "Invalid piece ID");
|
||||
return Pieces[i];
|
||||
}
|
||||
|
||||
const RopePieceBTreeLeaf *getNextLeafInOrder() const { return NextLeaf; }
|
||||
const RopePieceBTreeLeaf *getNextLeafInOrder() const { return NextLeaf; }
|
||||
|
||||
void insertAfterLeafInOrder(RopePieceBTreeLeaf *Node) {
|
||||
assert(!PrevLeaf && !NextLeaf && "Already in ordering");
|
||||
void insertAfterLeafInOrder(RopePieceBTreeLeaf *Node) {
|
||||
assert(!PrevLeaf && !NextLeaf && "Already in ordering");
|
||||
|
||||
NextLeaf = Node->NextLeaf;
|
||||
NextLeaf = Node->NextLeaf;
|
||||
if (NextLeaf)
|
||||
NextLeaf->PrevLeaf = &NextLeaf;
|
||||
PrevLeaf = &Node->NextLeaf;
|
||||
Node->NextLeaf = this;
|
||||
}
|
||||
|
||||
void removeFromLeafInOrder() {
|
||||
if (PrevLeaf) {
|
||||
*PrevLeaf = NextLeaf;
|
||||
if (NextLeaf)
|
||||
NextLeaf->PrevLeaf = &NextLeaf;
|
||||
PrevLeaf = &Node->NextLeaf;
|
||||
Node->NextLeaf = this;
|
||||
NextLeaf->PrevLeaf = PrevLeaf;
|
||||
} else if (NextLeaf) {
|
||||
NextLeaf->PrevLeaf = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromLeafInOrder() {
|
||||
if (PrevLeaf) {
|
||||
*PrevLeaf = NextLeaf;
|
||||
if (NextLeaf)
|
||||
NextLeaf->PrevLeaf = PrevLeaf;
|
||||
} else if (NextLeaf) {
|
||||
NextLeaf->PrevLeaf = nullptr;
|
||||
}
|
||||
}
|
||||
/// FullRecomputeSizeLocally - This method recomputes the 'Size' field by
|
||||
/// summing the size of all RopePieces.
|
||||
void FullRecomputeSizeLocally() {
|
||||
Size = 0;
|
||||
for (unsigned i = 0, e = getNumPieces(); i != e; ++i)
|
||||
Size += getPiece(i).size();
|
||||
}
|
||||
|
||||
/// FullRecomputeSizeLocally - This method recomputes the 'Size' field by
|
||||
/// summing the size of all RopePieces.
|
||||
void FullRecomputeSizeLocally() {
|
||||
Size = 0;
|
||||
for (unsigned i = 0, e = getNumPieces(); i != e; ++i)
|
||||
Size += getPiece(i).size();
|
||||
}
|
||||
/// split - Split the range containing the specified offset so that we are
|
||||
/// guaranteed that there is a place to do an insertion at the specified
|
||||
/// offset. The offset is relative, so "0" is the start of the node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *split(unsigned Offset);
|
||||
|
||||
/// split - Split the range containing the specified offset so that we are
|
||||
/// guaranteed that there is a place to do an insertion at the specified
|
||||
/// offset. The offset is relative, so "0" is the start of the node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *split(unsigned Offset);
|
||||
/// insert - Insert the specified ropepiece into this tree node at the
|
||||
/// specified offset. The offset is relative, so "0" is the start of the
|
||||
/// node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);
|
||||
|
||||
/// insert - Insert the specified ropepiece into this tree node at the
|
||||
/// specified offset. The offset is relative, so "0" is the start of the
|
||||
/// node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
|
||||
static bool classof(const RopePieceBTreeNode *N) {
|
||||
return N->isLeaf();
|
||||
}
|
||||
};
|
||||
static bool classof(const RopePieceBTreeNode *N) { return N->isLeaf(); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -244,7 +241,7 @@ RopePieceBTreeNode *RopePieceBTreeLeaf::split(unsigned Offset) {
|
||||
// Find the piece that this offset lands in.
|
||||
unsigned PieceOffs = 0;
|
||||
unsigned i = 0;
|
||||
while (Offset >= PieceOffs+Pieces[i].size()) {
|
||||
while (Offset >= PieceOffs + Pieces[i].size()) {
|
||||
PieceOffs += Pieces[i].size();
|
||||
++i;
|
||||
}
|
||||
@@ -256,13 +253,13 @@ RopePieceBTreeNode *RopePieceBTreeLeaf::split(unsigned Offset) {
|
||||
|
||||
// Otherwise, we need to split piece 'i' at Offset-PieceOffs. Convert Offset
|
||||
// to being Piece relative.
|
||||
unsigned IntraPieceOffset = Offset-PieceOffs;
|
||||
unsigned IntraPieceOffset = Offset - PieceOffs;
|
||||
|
||||
// We do this by shrinking the RopePiece and then doing an insert of the tail.
|
||||
RopePiece Tail(Pieces[i].StrData, Pieces[i].StartOffs+IntraPieceOffset,
|
||||
RopePiece Tail(Pieces[i].StrData, Pieces[i].StartOffs + IntraPieceOffset,
|
||||
Pieces[i].EndOffs);
|
||||
Size -= Pieces[i].size();
|
||||
Pieces[i].EndOffs = Pieces[i].StartOffs+IntraPieceOffset;
|
||||
Pieces[i].EndOffs = Pieces[i].StartOffs + IntraPieceOffset;
|
||||
Size += Pieces[i].size();
|
||||
|
||||
return insert(Offset, Tail);
|
||||
@@ -293,7 +290,7 @@ RopePieceBTreeNode *RopePieceBTreeLeaf::insert(unsigned Offset,
|
||||
// For an insertion into a non-full leaf node, just insert the value in
|
||||
// its sorted position. This requires moving later values over.
|
||||
for (; i != e; --e)
|
||||
Pieces[e] = Pieces[e-1];
|
||||
Pieces[e] = Pieces[e - 1];
|
||||
Pieces[i] = R;
|
||||
++NumPieces;
|
||||
Size += R.size();
|
||||
@@ -309,10 +306,10 @@ RopePieceBTreeNode *RopePieceBTreeLeaf::insert(unsigned Offset,
|
||||
RopePieceBTreeLeaf *NewNode = new RopePieceBTreeLeaf();
|
||||
|
||||
// Move over the last 'WidthFactor' values from here to NewNode.
|
||||
std::copy(&Pieces[WidthFactor], &Pieces[2*WidthFactor],
|
||||
std::copy(&Pieces[WidthFactor], &Pieces[2 * WidthFactor],
|
||||
&NewNode->Pieces[0]);
|
||||
// Replace old pieces with null RopePieces to drop refcounts.
|
||||
std::fill(&Pieces[WidthFactor], &Pieces[2*WidthFactor], RopePiece());
|
||||
std::fill(&Pieces[WidthFactor], &Pieces[2 * WidthFactor], RopePiece());
|
||||
|
||||
// Decrease the number of values in the two nodes.
|
||||
NewNode->NumPieces = NumPieces = WidthFactor;
|
||||
@@ -347,33 +344,34 @@ void RopePieceBTreeLeaf::erase(unsigned Offset, unsigned NumBytes) {
|
||||
|
||||
// Figure out how many pieces completely cover 'NumBytes'. We want to remove
|
||||
// all of them.
|
||||
for (; Offset+NumBytes > PieceOffs+getPiece(i).size(); ++i)
|
||||
for (; Offset + NumBytes > PieceOffs + getPiece(i).size(); ++i)
|
||||
PieceOffs += getPiece(i).size();
|
||||
|
||||
// If we exactly include the last one, include it in the region to delete.
|
||||
if (Offset+NumBytes == PieceOffs+getPiece(i).size()) {
|
||||
if (Offset + NumBytes == PieceOffs + getPiece(i).size()) {
|
||||
PieceOffs += getPiece(i).size();
|
||||
++i;
|
||||
}
|
||||
|
||||
// If we completely cover some RopePieces, erase them now.
|
||||
if (i != StartPiece) {
|
||||
unsigned NumDeleted = i-StartPiece;
|
||||
unsigned NumDeleted = i - StartPiece;
|
||||
for (; i != getNumPieces(); ++i)
|
||||
Pieces[i-NumDeleted] = Pieces[i];
|
||||
Pieces[i - NumDeleted] = Pieces[i];
|
||||
|
||||
// Drop references to dead rope pieces.
|
||||
std::fill(&Pieces[getNumPieces()-NumDeleted], &Pieces[getNumPieces()],
|
||||
std::fill(&Pieces[getNumPieces() - NumDeleted], &Pieces[getNumPieces()],
|
||||
RopePiece());
|
||||
NumPieces -= NumDeleted;
|
||||
|
||||
unsigned CoverBytes = PieceOffs-Offset;
|
||||
unsigned CoverBytes = PieceOffs - Offset;
|
||||
NumBytes -= CoverBytes;
|
||||
Size -= CoverBytes;
|
||||
}
|
||||
|
||||
// If we completely removed some stuff, we could be done.
|
||||
if (NumBytes == 0) return;
|
||||
if (NumBytes == 0)
|
||||
return;
|
||||
|
||||
// Okay, now might be erasing part of some Piece. If this is the case, then
|
||||
// move the start point of the piece.
|
||||
@@ -390,81 +388,79 @@ void RopePieceBTreeLeaf::erase(unsigned Offset, unsigned NumBytes) {
|
||||
|
||||
namespace {
|
||||
|
||||
/// RopePieceBTreeInterior - This represents an interior node in the B+Tree,
|
||||
/// which holds up to 2*WidthFactor pointers to child nodes.
|
||||
class RopePieceBTreeInterior : public RopePieceBTreeNode {
|
||||
/// NumChildren - This holds the number of children currently active in the
|
||||
/// Children array.
|
||||
unsigned char NumChildren = 0;
|
||||
/// RopePieceBTreeInterior - This represents an interior node in the B+Tree,
|
||||
/// which holds up to 2*WidthFactor pointers to child nodes.
|
||||
class RopePieceBTreeInterior : public RopePieceBTreeNode {
|
||||
/// NumChildren - This holds the number of children currently active in the
|
||||
/// Children array.
|
||||
unsigned char NumChildren = 0;
|
||||
|
||||
RopePieceBTreeNode *Children[2*WidthFactor];
|
||||
RopePieceBTreeNode *Children[2 * WidthFactor];
|
||||
|
||||
public:
|
||||
RopePieceBTreeInterior() : RopePieceBTreeNode(false) {}
|
||||
public:
|
||||
RopePieceBTreeInterior() : RopePieceBTreeNode(false) {}
|
||||
|
||||
RopePieceBTreeInterior(RopePieceBTreeNode *LHS, RopePieceBTreeNode *RHS)
|
||||
: RopePieceBTreeNode(false) {
|
||||
Children[0] = LHS;
|
||||
Children[1] = RHS;
|
||||
NumChildren = 2;
|
||||
Size = LHS->size() + RHS->size();
|
||||
}
|
||||
RopePieceBTreeInterior(RopePieceBTreeNode *LHS, RopePieceBTreeNode *RHS)
|
||||
: RopePieceBTreeNode(false) {
|
||||
Children[0] = LHS;
|
||||
Children[1] = RHS;
|
||||
NumChildren = 2;
|
||||
Size = LHS->size() + RHS->size();
|
||||
}
|
||||
|
||||
~RopePieceBTreeInterior() {
|
||||
for (unsigned i = 0, e = getNumChildren(); i != e; ++i)
|
||||
Children[i]->Destroy();
|
||||
}
|
||||
~RopePieceBTreeInterior() {
|
||||
for (unsigned i = 0, e = getNumChildren(); i != e; ++i)
|
||||
Children[i]->Destroy();
|
||||
}
|
||||
|
||||
bool isFull() const { return NumChildren == 2*WidthFactor; }
|
||||
bool isFull() const { return NumChildren == 2 * WidthFactor; }
|
||||
|
||||
unsigned getNumChildren() const { return NumChildren; }
|
||||
unsigned getNumChildren() const { return NumChildren; }
|
||||
|
||||
const RopePieceBTreeNode *getChild(unsigned i) const {
|
||||
assert(i < NumChildren && "invalid child #");
|
||||
return Children[i];
|
||||
}
|
||||
const RopePieceBTreeNode *getChild(unsigned i) const {
|
||||
assert(i < NumChildren && "invalid child #");
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
RopePieceBTreeNode *getChild(unsigned i) {
|
||||
assert(i < NumChildren && "invalid child #");
|
||||
return Children[i];
|
||||
}
|
||||
RopePieceBTreeNode *getChild(unsigned i) {
|
||||
assert(i < NumChildren && "invalid child #");
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
/// FullRecomputeSizeLocally - Recompute the Size field of this node by
|
||||
/// summing up the sizes of the child nodes.
|
||||
void FullRecomputeSizeLocally() {
|
||||
Size = 0;
|
||||
for (unsigned i = 0, e = getNumChildren(); i != e; ++i)
|
||||
Size += getChild(i)->size();
|
||||
}
|
||||
/// FullRecomputeSizeLocally - Recompute the Size field of this node by
|
||||
/// summing up the sizes of the child nodes.
|
||||
void FullRecomputeSizeLocally() {
|
||||
Size = 0;
|
||||
for (unsigned i = 0, e = getNumChildren(); i != e; ++i)
|
||||
Size += getChild(i)->size();
|
||||
}
|
||||
|
||||
/// split - Split the range containing the specified offset so that we are
|
||||
/// guaranteed that there is a place to do an insertion at the specified
|
||||
/// offset. The offset is relative, so "0" is the start of the node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *split(unsigned Offset);
|
||||
/// split - Split the range containing the specified offset so that we are
|
||||
/// guaranteed that there is a place to do an insertion at the specified
|
||||
/// offset. The offset is relative, so "0" is the start of the node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *split(unsigned Offset);
|
||||
|
||||
/// insert - Insert the specified ropepiece into this tree node at the
|
||||
/// specified offset. The offset is relative, so "0" is the start of the
|
||||
/// node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);
|
||||
/// insert - Insert the specified ropepiece into this tree node at the
|
||||
/// specified offset. The offset is relative, so "0" is the start of the
|
||||
/// node.
|
||||
///
|
||||
/// If there is no space in this subtree for the extra piece, the extra tree
|
||||
/// node is returned and must be inserted into a parent.
|
||||
RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R);
|
||||
|
||||
/// HandleChildPiece - A child propagated an insertion result up to us.
|
||||
/// Insert the new child, and/or propagate the result further up the tree.
|
||||
RopePieceBTreeNode *HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS);
|
||||
/// HandleChildPiece - A child propagated an insertion result up to us.
|
||||
/// Insert the new child, and/or propagate the result further up the tree.
|
||||
RopePieceBTreeNode *HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS);
|
||||
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void erase(unsigned Offset, unsigned NumBytes);
|
||||
|
||||
static bool classof(const RopePieceBTreeNode *N) {
|
||||
return !N->isLeaf();
|
||||
}
|
||||
};
|
||||
static bool classof(const RopePieceBTreeNode *N) { return !N->isLeaf(); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -481,7 +477,7 @@ RopePieceBTreeNode *RopePieceBTreeInterior::split(unsigned Offset) {
|
||||
|
||||
unsigned ChildOffset = 0;
|
||||
unsigned i = 0;
|
||||
for (; Offset >= ChildOffset+getChild(i)->size(); ++i)
|
||||
for (; Offset >= ChildOffset + getChild(i)->size(); ++i)
|
||||
ChildOffset += getChild(i)->size();
|
||||
|
||||
// If already split there, we're done.
|
||||
@@ -489,7 +485,7 @@ RopePieceBTreeNode *RopePieceBTreeInterior::split(unsigned Offset) {
|
||||
return nullptr;
|
||||
|
||||
// Otherwise, recursively split the child.
|
||||
if (RopePieceBTreeNode *RHS = getChild(i)->split(Offset-ChildOffset))
|
||||
if (RopePieceBTreeNode *RHS = getChild(i)->split(Offset - ChildOffset))
|
||||
return HandleChildPiece(i, RHS);
|
||||
return nullptr; // Done!
|
||||
}
|
||||
@@ -509,17 +505,17 @@ RopePieceBTreeNode *RopePieceBTreeInterior::insert(unsigned Offset,
|
||||
unsigned ChildOffs = 0;
|
||||
if (Offset == size()) {
|
||||
// Fastpath for a common case. Insert at end of last child.
|
||||
i = e-1;
|
||||
ChildOffs = size()-getChild(i)->size();
|
||||
i = e - 1;
|
||||
ChildOffs = size() - getChild(i)->size();
|
||||
} else {
|
||||
for (; Offset > ChildOffs+getChild(i)->size(); ++i)
|
||||
for (; Offset > ChildOffs + getChild(i)->size(); ++i)
|
||||
ChildOffs += getChild(i)->size();
|
||||
}
|
||||
|
||||
Size += R.size();
|
||||
|
||||
// Insert at the end of this child.
|
||||
if (RopePieceBTreeNode *RHS = getChild(i)->insert(Offset-ChildOffs, R))
|
||||
if (RopePieceBTreeNode *RHS = getChild(i)->insert(Offset - ChildOffs, R))
|
||||
return HandleChildPiece(i, RHS);
|
||||
|
||||
return nullptr;
|
||||
@@ -534,9 +530,9 @@ RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) {
|
||||
if (!isFull()) {
|
||||
// Insert RHS after child 'i'.
|
||||
if (i + 1 != getNumChildren())
|
||||
memmove(&Children[i+2], &Children[i+1],
|
||||
(getNumChildren()-i-1)*sizeof(Children[0]));
|
||||
Children[i+1] = RHS;
|
||||
memmove(&Children[i + 2], &Children[i + 1],
|
||||
(getNumChildren() - i - 1) * sizeof(Children[0]));
|
||||
Children[i + 1] = RHS;
|
||||
++NumChildren;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -549,7 +545,7 @@ RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) {
|
||||
|
||||
// Move over the last 'WidthFactor' values from here to NewNode.
|
||||
memcpy(&NewNode->Children[0], &Children[WidthFactor],
|
||||
WidthFactor*sizeof(Children[0]));
|
||||
WidthFactor * sizeof(Children[0]));
|
||||
|
||||
// Decrease the number of values in the two nodes.
|
||||
NewNode->NumChildren = NumChildren = WidthFactor;
|
||||
@@ -559,7 +555,7 @@ RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) {
|
||||
if (i < WidthFactor)
|
||||
this->HandleChildPiece(i, RHS);
|
||||
else
|
||||
NewNode->HandleChildPiece(i-WidthFactor, RHS);
|
||||
NewNode->HandleChildPiece(i - WidthFactor, RHS);
|
||||
|
||||
// Recompute the two nodes' size.
|
||||
NewNode->FullRecomputeSizeLocally();
|
||||
@@ -585,7 +581,7 @@ void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) {
|
||||
|
||||
// If we are deleting something contained entirely in the child, pass on the
|
||||
// request.
|
||||
if (Offset+NumBytes < CurChild->size()) {
|
||||
if (Offset + NumBytes < CurChild->size()) {
|
||||
CurChild->erase(Offset, NumBytes);
|
||||
return;
|
||||
}
|
||||
@@ -593,7 +589,7 @@ void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) {
|
||||
// If this deletion request starts somewhere in the middle of the child, it
|
||||
// must be deleting to the end of the child.
|
||||
if (Offset) {
|
||||
unsigned BytesFromChild = CurChild->size()-Offset;
|
||||
unsigned BytesFromChild = CurChild->size() - Offset;
|
||||
CurChild->erase(Offset, BytesFromChild);
|
||||
NumBytes -= BytesFromChild;
|
||||
// Start at the beginning of the next child.
|
||||
@@ -608,8 +604,8 @@ void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) {
|
||||
CurChild->Destroy();
|
||||
--NumChildren;
|
||||
if (i != getNumChildren())
|
||||
memmove(&Children[i], &Children[i+1],
|
||||
(getNumChildren()-i)*sizeof(Children[0]));
|
||||
memmove(&Children[i], &Children[i + 1],
|
||||
(getNumChildren() - i) * sizeof(Children[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +650,7 @@ RopePieceBTreeNode *RopePieceBTreeNode::insert(unsigned Offset,
|
||||
/// erase - Remove NumBytes from this node at the specified offset. We are
|
||||
/// guaranteed that there is a split at Offset.
|
||||
void RopePieceBTreeNode::erase(unsigned Offset, unsigned NumBytes) {
|
||||
assert(Offset+NumBytes <= size() && "Invalid offset to erase!");
|
||||
assert(Offset + NumBytes <= size() && "Invalid offset to erase!");
|
||||
if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(this))
|
||||
return Leaf->erase(Offset, NumBytes);
|
||||
return cast<RopePieceBTreeInterior>(this)->erase(Offset, NumBytes);
|
||||
@@ -665,7 +661,7 @@ void RopePieceBTreeNode::erase(unsigned Offset, unsigned NumBytes) {
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static const RopePieceBTreeLeaf *getCN(const void *P) {
|
||||
return static_cast<const RopePieceBTreeLeaf*>(P);
|
||||
return static_cast<const RopePieceBTreeLeaf *>(P);
|
||||
}
|
||||
|
||||
// begin iterator.
|
||||
@@ -686,13 +682,14 @@ RopePieceBTreeIterator::RopePieceBTreeIterator(const void *n) {
|
||||
|
||||
if (CurNode)
|
||||
CurPiece = &getCN(CurNode)->getPiece(0);
|
||||
else // Empty tree, this is an end() iterator.
|
||||
else // Empty tree, this is an end() iterator.
|
||||
CurPiece = nullptr;
|
||||
CurChar = 0;
|
||||
}
|
||||
|
||||
void RopePieceBTreeIterator::MoveToNextPiece() {
|
||||
if (CurPiece != &getCN(CurNode)->getPiece(getCN(CurNode)->getNumPieces()-1)) {
|
||||
if (CurPiece !=
|
||||
&getCN(CurNode)->getPiece(getCN(CurNode)->getNumPieces() - 1)) {
|
||||
CurChar = 0;
|
||||
++CurPiece;
|
||||
return;
|
||||
@@ -715,25 +712,19 @@ void RopePieceBTreeIterator::MoveToNextPiece() {
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static RopePieceBTreeNode *getRoot(void *P) {
|
||||
return static_cast<RopePieceBTreeNode*>(P);
|
||||
return static_cast<RopePieceBTreeNode *>(P);
|
||||
}
|
||||
|
||||
RopePieceBTree::RopePieceBTree() {
|
||||
Root = new RopePieceBTreeLeaf();
|
||||
}
|
||||
RopePieceBTree::RopePieceBTree() { Root = new RopePieceBTreeLeaf(); }
|
||||
|
||||
RopePieceBTree::RopePieceBTree(const RopePieceBTree &RHS) {
|
||||
assert(RHS.empty() && "Can't copy non-empty tree yet");
|
||||
Root = new RopePieceBTreeLeaf();
|
||||
}
|
||||
|
||||
RopePieceBTree::~RopePieceBTree() {
|
||||
getRoot(Root)->Destroy();
|
||||
}
|
||||
RopePieceBTree::~RopePieceBTree() { getRoot(Root)->Destroy(); }
|
||||
|
||||
unsigned RopePieceBTree::size() const {
|
||||
return getRoot(Root)->size();
|
||||
}
|
||||
unsigned RopePieceBTree::size() const { return getRoot(Root)->size(); }
|
||||
|
||||
void RopePieceBTree::clear() {
|
||||
if (auto *Leaf = dyn_cast<RopePieceBTreeLeaf>(getRoot(Root)))
|
||||
@@ -772,24 +763,24 @@ void RopePieceBTree::erase(unsigned Offset, unsigned NumBytes) {
|
||||
/// the AllocBuffer object to aggregate requests for small strings into one
|
||||
/// allocation instead of doing tons of tiny allocations.
|
||||
RopePiece RewriteRope::MakeRopeString(const char *Start, const char *End) {
|
||||
unsigned Len = End-Start;
|
||||
unsigned Len = End - Start;
|
||||
assert(Len && "Zero length RopePiece is invalid!");
|
||||
|
||||
// If we have space for this string in the current alloc buffer, use it.
|
||||
if (AllocOffs+Len <= AllocChunkSize) {
|
||||
memcpy(AllocBuffer->Data+AllocOffs, Start, Len);
|
||||
if (AllocOffs + Len <= AllocChunkSize) {
|
||||
memcpy(AllocBuffer->Data + AllocOffs, Start, Len);
|
||||
AllocOffs += Len;
|
||||
return RopePiece(AllocBuffer, AllocOffs-Len, AllocOffs);
|
||||
return RopePiece(AllocBuffer, AllocOffs - Len, AllocOffs);
|
||||
}
|
||||
|
||||
// If we don't have enough room because this specific allocation is huge,
|
||||
// just allocate a new rope piece for it alone.
|
||||
if (Len > AllocChunkSize) {
|
||||
unsigned Size = End-Start+sizeof(RopeRefCountString)-1;
|
||||
unsigned Size = End - Start + sizeof(RopeRefCountString) - 1;
|
||||
auto *Res = reinterpret_cast<RopeRefCountString *>(new char[Size]);
|
||||
Res->RefCount = 0;
|
||||
memcpy(Res->Data, Start, End-Start);
|
||||
return RopePiece(Res, 0, End-Start);
|
||||
memcpy(Res->Data, Start, End - Start);
|
||||
return RopePiece(Res, 0, End - Start);
|
||||
}
|
||||
|
||||
// Otherwise, this was a small request but we just don't have space for it
|
||||
@@ -63,6 +63,7 @@ add_llvm_unittest(ADTTests
|
||||
PostOrderIteratorTest.cpp
|
||||
PriorityWorklistTest.cpp
|
||||
RangeAdapterTest.cpp
|
||||
RewriteBufferTest.cpp
|
||||
SCCIteratorTest.cpp
|
||||
STLExtrasTest.cpp
|
||||
STLForwardCompatTest.cpp
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Rewrite/Core/RewriteBuffer.h"
|
||||
#include "llvm/ADT/RewriteBuffer.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -32,7 +31,7 @@ static void tagRange(unsigned Offset, unsigned Len, StringRef tagName,
|
||||
raw_string_ostream(EndTag) << "</" << tagName << '>';
|
||||
|
||||
Buf.InsertTextAfter(Offset, BeginTag);
|
||||
Buf.InsertTextBefore(Offset+Len, EndTag);
|
||||
Buf.InsertTextBefore(Offset + Len, EndTag);
|
||||
}
|
||||
|
||||
TEST(RewriteBuffer, TagRanges) {
|
||||
Reference in New Issue
Block a user