blob: 6e0fa38632c89f60933e4c53de347c9ee0481762 [file] [log] [blame]
//===--- Utility.h ----------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// Provide some utility classes for use in the demangler.
// There are two copies of this file in the source tree. The one in libcxxabi
// is the original and the one in llvm is the copy. Use cp-to-llvm.sh to update
// the copy. See README.txt for more details.
//
//===----------------------------------------------------------------------===//
#ifndef DEMANGLE_UTILITY_H
#define DEMANGLE_UTILITY_H
#include "StringView.h"
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <limits>
DEMANGLE_NAMESPACE_BEGIN
// Stream that AST nodes write their string representation into after the AST
// has been parsed.
class OutputBuffer {
char *Buffer = nullptr;
size_t CurrentPosition = 0;
size_t BufferCapacity = 0;
// Ensure there are at least N more positions in the buffer.
void grow(size_t N) {
size_t Need = N + CurrentPosition;
if (Need > BufferCapacity) {
// Reduce the number of reallocations, with a bit of hysteresis. The
// number here is chosen so the first allocation will more-than-likely not
// allocate more than 1K.
Need += 1024 - 32;
BufferCapacity *= 2;
if (BufferCapacity < Need)
BufferCapacity = Need;
Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
if (Buffer == nullptr)
std::terminate();
}
}
OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) {
std::array<char, 21> Temp;
char *TempPtr = Temp.data() + Temp.size();
// Output at least one character.
do {
*--TempPtr = char('0' + N % 10);
N /= 10;
} while (N);
// Add negative sign.
if (isNeg)
*--TempPtr = '-';
return operator+=(StringView(TempPtr, Temp.data() + Temp.size()));
}
public:
OutputBuffer(char *StartBuf, size_t Size)
: Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
OutputBuffer() = default;
// Non-copyable
OutputBuffer(const OutputBuffer &) = delete;
OutputBuffer &operator=(const OutputBuffer &) = delete;
operator StringView() const { return StringView(Buffer, CurrentPosition); }
void reset(char *Buffer_, size_t BufferCapacity_) {
CurrentPosition = 0;
Buffer = Buffer_;
BufferCapacity = BufferCapacity_;
}
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
/// into the pack that we're currently printing.
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
/// When zero, we're printing template args and '>' needs to be parenthesized.
/// Use a counter so we can simply increment inside parentheses.
unsigned GtIsGt = 1;
bool isGtInsideTemplateArgs() const { return GtIsGt == 0; }
void printOpen(char Open = '(') {
GtIsGt++;
*this += Open;
}
void printClose(char Close = ')') {
GtIsGt--;
*this += Close;
}
OutputBuffer &operator+=(StringView R) {
if (size_t Size = R.size()) {
grow(Size);
std::memcpy(Buffer + CurrentPosition, R.begin(), Size);
CurrentPosition += Size;
}
return *this;
}
OutputBuffer &operator+=(char C) {
grow(1);
Buffer[CurrentPosition++] = C;
return *this;
}
OutputBuffer &prepend(StringView R) {
size_t Size = R.size();
grow(Size);
std::memmove(Buffer + Size, Buffer, CurrentPosition);
std::memcpy(Buffer, R.begin(), Size);
CurrentPosition += Size;
return *this;
}
OutputBuffer &operator<<(StringView R) { return (*this += R); }
OutputBuffer &operator<<(char C) { return (*this += C); }
OutputBuffer &operator<<(long long N) {
return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0);
}
OutputBuffer &operator<<(unsigned long long N) {
return writeUnsigned(N, false);
}
OutputBuffer &operator<<(long N) {
return this->operator<<(static_cast<long long>(N));
}
OutputBuffer &operator<<(unsigned long N) {
return this->operator<<(static_cast<unsigned long long>(N));
}
OutputBuffer &operator<<(int N) {
return this->operator<<(static_cast<long long>(N));
}
OutputBuffer &operator<<(unsigned int N) {
return this->operator<<(static_cast<unsigned long long>(N));
}
void insert(size_t Pos, const char *S, size_t N) {
assert(Pos <= CurrentPosition);
if (N == 0)
return;
grow(N);
std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
std::memcpy(Buffer + Pos, S, N);
CurrentPosition += N;
}
size_t getCurrentPosition() const { return CurrentPosition; }
void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
char back() const {
return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0';
}
bool empty() const { return CurrentPosition == 0; }
char *getBuffer() { return Buffer; }
char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
size_t getBufferCapacity() const { return BufferCapacity; }
};
template <class T> class ScopedOverride {
T &Loc;
T Original;
public:
ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {}
ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
Loc_ = std::move(NewVal);
}
~ScopedOverride() { Loc = std::move(Original); }
ScopedOverride(const ScopedOverride &) = delete;
ScopedOverride &operator=(const ScopedOverride &) = delete;
};
inline bool initializeOutputBuffer(char *Buf, size_t *N, OutputBuffer &OB,
size_t InitSize) {
size_t BufferSize;
if (Buf == nullptr) {
Buf = static_cast<char *>(std::malloc(InitSize));
if (Buf == nullptr)
return false;
BufferSize = InitSize;
} else
BufferSize = *N;
OB.reset(Buf, BufferSize);
return true;
}
DEMANGLE_NAMESPACE_END
#endif