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(ptr: Buffer, size: 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 R: 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() = default;
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 struct {
108 /// The depth of '(' and ')' inside the currently printed template
109 /// arguments.
110 unsigned ParenDepth = 0;
111
112 /// True if we're currently printing a template argument.
113 bool InsideTemplate = false;
114 } TemplateTracker;
115
116 /// Returns true if we're currently between a '(' and ')' when printing
117 /// template args.
118 bool isInParensInTemplateArgs() const {
119 return TemplateTracker.ParenDepth > 0;
120 }
121
122 /// Returns true if we're printing template args.
123 bool isInsideTemplateArgs() const { return TemplateTracker.InsideTemplate; }
124
125 void printOpen(char Open = '(') {
126 if (isInsideTemplateArgs())
127 TemplateTracker.ParenDepth++;
128 *this += Open;
129 }
130 void printClose(char Close = ')') {
131 if (isInsideTemplateArgs())
132 TemplateTracker.ParenDepth--;
133 *this += Close;
134 }
135
136 OutputBuffer &operator+=(std::string_view R) {
137 if (size_t Size = R.size()) {
138 grow(N: Size);
139 std::memcpy(dest: Buffer + CurrentPosition, src: &*R.begin(), n: Size);
140 CurrentPosition += Size;
141 }
142 return *this;
143 }
144
145 OutputBuffer &operator+=(char C) {
146 grow(N: 1);
147 Buffer[CurrentPosition++] = C;
148 return *this;
149 }
150
151 OutputBuffer &prepend(std::string_view R) {
152 size_t Size = R.size();
153 if (!Size)
154 return *this;
155
156 grow(N: Size);
157 std::memmove(dest: Buffer + Size, src: Buffer, n: CurrentPosition);
158 std::memcpy(dest: Buffer, src: &*R.begin(), n: Size);
159 CurrentPosition += Size;
160
161 notifyInsertion(/*Position=*/0, /*Count=*/Size);
162
163 return *this;
164 }
165
166 OutputBuffer &operator<<(std::string_view R) { return (*this += R); }
167
168 OutputBuffer &operator<<(char C) { return (*this += C); }
169
170 OutputBuffer &operator<<(long long N) {
171 return writeUnsigned(N: static_cast<unsigned long long>(std::abs(x: N)), isNeg: N < 0);
172 }
173
174 OutputBuffer &operator<<(unsigned long long N) {
175 return writeUnsigned(N, isNeg: false);
176 }
177
178 OutputBuffer &operator<<(long N) {
179 return this->operator<<(N: static_cast<long long>(N));
180 }
181
182 OutputBuffer &operator<<(unsigned long N) {
183 return this->operator<<(N: static_cast<unsigned long long>(N));
184 }
185
186 OutputBuffer &operator<<(int N) {
187 return this->operator<<(N: static_cast<long long>(N));
188 }
189
190 OutputBuffer &operator<<(unsigned int N) {
191 return this->operator<<(N: static_cast<unsigned long long>(N));
192 }
193
194 void insert(size_t Pos, const char *S, size_t N) {
195 DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
196 if (N == 0)
197 return;
198
199 grow(N);
200 std::memmove(dest: Buffer + Pos + N, src: Buffer + Pos, n: CurrentPosition - Pos);
201 std::memcpy(dest: Buffer + Pos, src: S, n: N);
202 CurrentPosition += N;
203
204 notifyInsertion(Pos, N);
205 }
206
207 size_t getCurrentPosition() const { return CurrentPosition; }
208 void setCurrentPosition(size_t NewPos) {
209 notifyDeletion(CurrentPosition, NewPos);
210 CurrentPosition = NewPos;
211 }
212
213 char back() const {
214 DEMANGLE_ASSERT(CurrentPosition, "");
215 return Buffer[CurrentPosition - 1];
216 }
217
218 bool empty() const { return CurrentPosition == 0; }
219
220 char *getBuffer() { return Buffer; }
221 char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
222 size_t getBufferCapacity() const { return BufferCapacity; }
223};
224
225template <class T> class ScopedOverride {
226 T &Loc;
227 T Original;
228
229public:
230 ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {}
231
232 ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
233 Loc_ = std::move(NewVal);
234 }
235 ~ScopedOverride() { Loc = std::move(Original); }
236
237 ScopedOverride(const ScopedOverride &) = delete;
238 ScopedOverride &operator=(const ScopedOverride &) = delete;
239};
240
241DEMANGLE_NAMESPACE_END
242
243#endif
244