master
  1//===--- Utility.h ----------------------------------------------*- C++ -*-===//
  2//
  3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4// See https://llvm.org/LICENSE.txt for license information.
  5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6//
  7//===----------------------------------------------------------------------===//
  8//
  9// Provide some utility classes for use in the demangler.
 10// There are two copies of this file in the source tree.  The one in libcxxabi
 11// is the original and the one in llvm is the copy.  Use cp-to-llvm.sh to update
 12// the copy.  See README.txt for more details.
 13//
 14//===----------------------------------------------------------------------===//
 15
 16#ifndef DEMANGLE_UTILITY_H
 17#define DEMANGLE_UTILITY_H
 18
 19#include "DemangleConfig.h"
 20
 21#include <array>
 22#include <cstdint>
 23#include <cstdlib>
 24#include <cstring>
 25#include <limits>
 26#include <string_view>
 27
 28DEMANGLE_NAMESPACE_BEGIN
 29
 30class Node;
 31
 32// Stream that AST nodes write their string representation into after the AST
 33// has been parsed.
 34class OutputBuffer {
 35  char *Buffer = nullptr;
 36  size_t CurrentPosition = 0;
 37  size_t BufferCapacity = 0;
 38
 39  // Ensure there are at least N more positions in the buffer.
 40  void grow(size_t N) {
 41    size_t Need = N + CurrentPosition;
 42    if (Need > BufferCapacity) {
 43      // Reduce the number of reallocations, with a bit of hysteresis. The
 44      // number here is chosen so the first allocation will more-than-likely not
 45      // allocate more than 1K.
 46      Need += 1024 - 32;
 47      BufferCapacity *= 2;
 48      if (BufferCapacity < Need)
 49        BufferCapacity = Need;
 50      Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
 51      if (Buffer == nullptr)
 52        std::abort();
 53    }
 54  }
 55
 56  OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) {
 57    std::array<char, 21> Temp;
 58    char *TempPtr = Temp.data() + Temp.size();
 59
 60    // Output at least one character.
 61    do {
 62      *--TempPtr = char('0' + N % 10);
 63      N /= 10;
 64    } while (N);
 65
 66    // Add negative sign.
 67    if (isNeg)
 68      *--TempPtr = '-';
 69
 70    return operator+=(
 71        std::string_view(TempPtr, Temp.data() + Temp.size() - TempPtr));
 72  }
 73
 74public:
 75  OutputBuffer(char *StartBuf, size_t Size)
 76      : Buffer(StartBuf), BufferCapacity(Size) {}
 77  OutputBuffer(char *StartBuf, size_t *SizePtr)
 78      : OutputBuffer(StartBuf, StartBuf ? *SizePtr : 0) {}
 79  OutputBuffer() = default;
 80  // Non-copyable
 81  OutputBuffer(const OutputBuffer &) = delete;
 82  OutputBuffer &operator=(const OutputBuffer &) = delete;
 83
 84  virtual ~OutputBuffer() {}
 85
 86  operator std::string_view() const {
 87    return std::string_view(Buffer, CurrentPosition);
 88  }
 89
 90  /// Called by the demangler when printing the demangle tree. By
 91  /// default calls into \c Node::print{Left|Right} but can be overriden
 92  /// by clients to track additional state when printing the demangled name.
 93  virtual void printLeft(const Node &N);
 94  virtual void printRight(const Node &N);
 95
 96  /// Called when we write to this object anywhere other than the end.
 97  virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {}
 98
 99  /// Called when we make the \c CurrentPosition of this object smaller.
100  virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {}
101
102  /// If a ParameterPackExpansion (or similar type) is encountered, the offset
103  /// into the pack that we're currently printing.
104  unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
105  unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
106
107  /// When zero, we're printing template args and '>' needs to be parenthesized.
108  /// Use a counter so we can simply increment inside parentheses.
109  unsigned GtIsGt = 1;
110
111  bool isGtInsideTemplateArgs() const { return GtIsGt == 0; }
112
113  void printOpen(char Open = '(') {
114    GtIsGt++;
115    *this += Open;
116  }
117  void printClose(char Close = ')') {
118    GtIsGt--;
119    *this += Close;
120  }
121
122  OutputBuffer &operator+=(std::string_view R) {
123    if (size_t Size = R.size()) {
124      grow(Size);
125      std::memcpy(Buffer + CurrentPosition, &*R.begin(), Size);
126      CurrentPosition += Size;
127    }
128    return *this;
129  }
130
131  OutputBuffer &operator+=(char C) {
132    grow(1);
133    Buffer[CurrentPosition++] = C;
134    return *this;
135  }
136
137  OutputBuffer &prepend(std::string_view R) {
138    size_t Size = R.size();
139    if (!Size)
140      return *this;
141
142    grow(Size);
143    std::memmove(Buffer + Size, Buffer, CurrentPosition);
144    std::memcpy(Buffer, &*R.begin(), Size);
145    CurrentPosition += Size;
146
147    notifyInsertion(/*Position=*/0, /*Count=*/Size);
148
149    return *this;
150  }
151
152  OutputBuffer &operator<<(std::string_view R) { return (*this += R); }
153
154  OutputBuffer &operator<<(char C) { return (*this += C); }
155
156  OutputBuffer &operator<<(long long N) {
157    return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0);
158  }
159
160  OutputBuffer &operator<<(unsigned long long N) {
161    return writeUnsigned(N, false);
162  }
163
164  OutputBuffer &operator<<(long N) {
165    return this->operator<<(static_cast<long long>(N));
166  }
167
168  OutputBuffer &operator<<(unsigned long N) {
169    return this->operator<<(static_cast<unsigned long long>(N));
170  }
171
172  OutputBuffer &operator<<(int N) {
173    return this->operator<<(static_cast<long long>(N));
174  }
175
176  OutputBuffer &operator<<(unsigned int N) {
177    return this->operator<<(static_cast<unsigned long long>(N));
178  }
179
180  void insert(size_t Pos, const char *S, size_t N) {
181    DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
182    if (N == 0)
183      return;
184
185    grow(N);
186    std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
187    std::memcpy(Buffer + Pos, S, N);
188    CurrentPosition += N;
189
190    notifyInsertion(Pos, N);
191  }
192
193  size_t getCurrentPosition() const { return CurrentPosition; }
194  void setCurrentPosition(size_t NewPos) {
195    notifyDeletion(CurrentPosition, NewPos);
196    CurrentPosition = NewPos;
197  }
198
199  char back() const {
200    DEMANGLE_ASSERT(CurrentPosition, "");
201    return Buffer[CurrentPosition - 1];
202  }
203
204  bool empty() const { return CurrentPosition == 0; }
205
206  char *getBuffer() { return Buffer; }
207  char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
208  size_t getBufferCapacity() const { return BufferCapacity; }
209};
210
211template <class T> class ScopedOverride {
212  T &Loc;
213  T Original;
214
215public:
216  ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {}
217
218  ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
219    Loc_ = std::move(NewVal);
220  }
221  ~ScopedOverride() { Loc = std::move(Original); }
222
223  ScopedOverride(const ScopedOverride &) = delete;
224  ScopedOverride &operator=(const ScopedOverride &) = delete;
225};
226
227DEMANGLE_NAMESPACE_END
228
229#endif