[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:
Jacques Pienaar
2024-08-18 09:46:51 -07:00
committed by GitHub
parent bcbe9d6c77
commit 0d150db214
26 changed files with 803 additions and 790 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -36,6 +36,7 @@
using namespace clang;
using namespace arcmt;
using namespace ento;
using llvm::RewriteBuffer;
namespace {

View File

@@ -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,

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 {

View File

@@ -32,6 +32,7 @@
#if CLANG_ENABLE_OBJC_REWRITER
using namespace clang;
using llvm::RewriteBuffer;
using llvm::utostr;
namespace {

View File

@@ -3,9 +3,7 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangRewrite
DeltaTree.cpp
HTMLRewrite.cpp
RewriteRope.cpp
Rewriter.cpp
TokenRewriter.cpp

View File

@@ -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"

View File

@@ -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,

View File

@@ -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.

View File

@@ -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"

View File

@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_unittest(RewriteTests
RewriteBufferTest.cpp
RewriterTest.cpp
)
clang_target_link_libraries(RewriteTests

View 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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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.

View 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);
}

View File

@@ -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

View File

@@ -63,6 +63,7 @@ add_llvm_unittest(ADTTests
PostOrderIteratorTest.cpp
PriorityWorklistTest.cpp
RangeAdapterTest.cpp
RewriteBufferTest.cpp
SCCIteratorTest.cpp
STLExtrasTest.cpp
STLForwardCompatTest.cpp

View File

@@ -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) {