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(s).
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef DEMANGLE_UTILITY_H
14#define DEMANGLE_UTILITY_H
15
16#include "StringView.h"
17#include <cstdint>
18#include <cstdlib>
19#include <cstring>
20#include <iterator>
21#include <limits>
22
23DEMANGLE_NAMESPACE_BEGIN
24
25// Stream that AST nodes write their string representation into after the AST
26// has been parsed.
27class OutputStream {
28  char *Buffer;
29  size_t CurrentPosition;
30  size_t BufferCapacity;
31
32  // Ensure there is at least n more positions in buffer.
33  void grow(size_t N) {
34    if (N + CurrentPosition >= BufferCapacity) {
35      BufferCapacity *= 2;
36      if (BufferCapacity < N + CurrentPosition)
37        BufferCapacity = N + CurrentPosition;
38      Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
39      if (Buffer == nullptr)
40        std::terminate();
41    }
42  }
43
44  void writeUnsigned(uint64_t N, bool isNeg = false) {
45    // Handle special case...
46    if (N == 0) {
47      *this << '0';
48      return;
49    }
50
51    char Temp[21];
52    char *TempPtr = std::end(Temp);
53
54    while (N) {
55      *--TempPtr = '0' + char(N % 10);
56      N /= 10;
57    }
58
59    // Add negative sign...
60    if (isNeg)
61      *--TempPtr = '-';
62    this->operator<<(StringView(TempPtr, std::end(Temp)));
63  }
64
65public:
66  OutputStream(char *StartBuf, size_t Size)
67      : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
68  OutputStream() = default;
69  void reset(char *Buffer_, size_t BufferCapacity_) {
70    CurrentPosition = 0;
71    Buffer = Buffer_;
72    BufferCapacity = BufferCapacity_;
73  }
74
75  /// If a ParameterPackExpansion (or similar type) is encountered, the offset
76  /// into the pack that we're currently printing.
77  unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
78  unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
79
80  OutputStream &operator+=(StringView R) {
81    size_t Size = R.size();
82    if (Size == 0)
83      return *this;
84    grow(Size);
85    std::memmove(Buffer + CurrentPosition, R.begin(), Size);
86    CurrentPosition += Size;
87    return *this;
88  }
89
90  OutputStream &operator+=(char C) {
91    grow(1);
92    Buffer[CurrentPosition++] = C;
93    return *this;
94  }
95
96  OutputStream &operator<<(StringView R) { return (*this += R); }
97
98  OutputStream &operator<<(char C) { return (*this += C); }
99
100  OutputStream &operator<<(long long N) {
101    if (N < 0)
102      writeUnsigned(static_cast<unsigned long long>(-N), true);
103    else
104      writeUnsigned(static_cast<unsigned long long>(N));
105    return *this;
106  }
107
108  OutputStream &operator<<(unsigned long long N) {
109    writeUnsigned(N, false);
110    return *this;
111  }
112
113  OutputStream &operator<<(long N) {
114    return this->operator<<(static_cast<long long>(N));
115  }
116
117  OutputStream &operator<<(unsigned long N) {
118    return this->operator<<(static_cast<unsigned long long>(N));
119  }
120
121  OutputStream &operator<<(int N) {
122    return this->operator<<(static_cast<long long>(N));
123  }
124
125  OutputStream &operator<<(unsigned int N) {
126    return this->operator<<(static_cast<unsigned long long>(N));
127  }
128
129  size_t getCurrentPosition() const { return CurrentPosition; }
130  void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
131
132  char back() const {
133    return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0';
134  }
135
136  bool empty() const { return CurrentPosition == 0; }
137
138  char *getBuffer() { return Buffer; }
139  char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
140  size_t getBufferCapacity() { return BufferCapacity; }
141};
142
143template <class T> class SwapAndRestore {
144  T &Restore;
145  T OriginalValue;
146  bool ShouldRestore = true;
147
148public:
149  SwapAndRestore(T &Restore_) : SwapAndRestore(Restore_, Restore_) {}
150
151  SwapAndRestore(T &Restore_, T NewVal)
152      : Restore(Restore_), OriginalValue(Restore) {
153    Restore = std::move(NewVal);
154  }
155  ~SwapAndRestore() {
156    if (ShouldRestore)
157      Restore = std::move(OriginalValue);
158  }
159
160  void shouldRestore(bool ShouldRestore_) { ShouldRestore = ShouldRestore_; }
161
162  void restoreNow(bool Force) {
163    if (!Force && !ShouldRestore)
164      return;
165
166    Restore = std::move(OriginalValue);
167    ShouldRestore = false;
168  }
169
170  SwapAndRestore(const SwapAndRestore &) = delete;
171  SwapAndRestore &operator=(const SwapAndRestore &) = delete;
172};
173
174inline bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S,
175                                   size_t InitSize) {
176  size_t BufferSize;
177  if (Buf == nullptr) {
178    Buf = static_cast<char *>(std::malloc(InitSize));
179    if (Buf == nullptr)
180      return false;
181    BufferSize = InitSize;
182  } else
183    BufferSize = *N;
184
185  S.reset(Buf, BufferSize);
186  return true;
187}
188
189DEMANGLE_NAMESPACE_END
190
191#endif
192