[Clang][SME2] Enable multi-vector loads & stores for SME2 (#75821)
[llvm-project.git] / compiler-rt / lib / scudo / standalone / string_utils.cpp
blobd4e4e3becd0e1afca6aac57657fd7104a72f7cea
1 //===-- string_utils.cpp ----------------------------------------*- 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 //===----------------------------------------------------------------------===//
9 #include "string_utils.h"
10 #include "common.h"
12 #include <stdarg.h>
13 #include <string.h>
15 namespace scudo {
17 static int appendChar(char **Buffer, const char *BufferEnd, char C) {
18 if (*Buffer < BufferEnd) {
19 **Buffer = C;
20 (*Buffer)++;
22 return 1;
25 // Appends number in a given Base to buffer. If its length is less than
26 // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
27 // on the value of |PadWithZero|.
28 static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
29 u8 Base, u8 MinNumberLength, bool PadWithZero,
30 bool Negative, bool Upper) {
31 constexpr uptr MaxLen = 30;
32 RAW_CHECK(Base == 10 || Base == 16);
33 RAW_CHECK(Base == 10 || !Negative);
34 RAW_CHECK(AbsoluteValue || !Negative);
35 RAW_CHECK(MinNumberLength < MaxLen);
36 int Res = 0;
37 if (Negative && MinNumberLength)
38 --MinNumberLength;
39 if (Negative && PadWithZero)
40 Res += appendChar(Buffer, BufferEnd, '-');
41 uptr NumBuffer[MaxLen];
42 int Pos = 0;
43 do {
44 RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
45 "appendNumber buffer overflow");
46 NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
47 AbsoluteValue /= Base;
48 } while (AbsoluteValue > 0);
49 if (Pos < MinNumberLength) {
50 memset(&NumBuffer[Pos], 0,
51 sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
52 Pos = MinNumberLength;
54 RAW_CHECK(Pos > 0);
55 Pos--;
56 for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
57 char c = (PadWithZero || Pos == 0) ? '0' : ' ';
58 Res += appendChar(Buffer, BufferEnd, c);
60 if (Negative && !PadWithZero)
61 Res += appendChar(Buffer, BufferEnd, '-');
62 for (; Pos >= 0; Pos--) {
63 char Digit = static_cast<char>(NumBuffer[Pos]);
64 Digit = static_cast<char>((Digit < 10) ? '0' + Digit
65 : (Upper ? 'A' : 'a') + Digit - 10);
66 Res += appendChar(Buffer, BufferEnd, Digit);
68 return Res;
71 static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
72 u8 Base, u8 MinNumberLength, bool PadWithZero,
73 bool Upper) {
74 return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
75 PadWithZero, /*Negative=*/false, Upper);
78 static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
79 u8 MinNumberLength, bool PadWithZero) {
80 const bool Negative = (Num < 0);
81 const u64 UnsignedNum = (Num == INT64_MIN)
82 ? static_cast<u64>(INT64_MAX) + 1
83 : static_cast<u64>(Negative ? -Num : Num);
84 return appendNumber(Buffer, BufferEnd, UnsignedNum, 10, MinNumberLength,
85 PadWithZero, Negative, /*Upper=*/false);
88 // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
89 // interpret Width == 0 as "no Width requested":
90 // Width == 0 - no Width requested
91 // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
92 // Width > 0 - right-justify S, not implemented yet
93 static int appendString(char **Buffer, const char *BufferEnd, int Width,
94 int MaxChars, const char *S) {
95 if (!S)
96 S = "<null>";
97 int Res = 0;
98 for (; *S; S++) {
99 if (MaxChars >= 0 && Res >= MaxChars)
100 break;
101 Res += appendChar(Buffer, BufferEnd, *S);
103 // Only the left justified strings are supported.
104 while (Width < -Res)
105 Res += appendChar(Buffer, BufferEnd, ' ');
106 return Res;
109 static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
110 int Res = 0;
111 Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
112 Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
113 SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
114 /*Upper=*/false);
115 return Res;
118 static int formatString(char *Buffer, uptr BufferLength, const char *Format,
119 va_list Args) {
120 static const char *PrintfFormatsHelp =
121 "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
122 "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
123 RAW_CHECK(Format);
124 RAW_CHECK(BufferLength > 0);
125 const char *BufferEnd = &Buffer[BufferLength - 1];
126 const char *Cur = Format;
127 int Res = 0;
128 for (; *Cur; Cur++) {
129 if (*Cur != '%') {
130 Res += appendChar(&Buffer, BufferEnd, *Cur);
131 continue;
133 Cur++;
134 const bool LeftJustified = *Cur == '-';
135 if (LeftJustified)
136 Cur++;
137 bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
138 const bool PadWithZero = (*Cur == '0');
139 u8 Width = 0;
140 if (HaveWidth) {
141 while (*Cur >= '0' && *Cur <= '9')
142 Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
144 const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
145 int Precision = -1;
146 if (HavePrecision) {
147 Cur += 2;
148 Precision = va_arg(Args, int);
150 const bool HaveZ = (*Cur == 'z');
151 Cur += HaveZ;
152 const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
153 Cur += HaveLL * 2;
154 s64 DVal;
155 u64 UVal;
156 const bool HaveLength = HaveZ || HaveLL;
157 const bool HaveFlags = HaveWidth || HaveLength;
158 // At the moment only %s supports precision and left-justification.
159 CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
160 switch (*Cur) {
161 case 'd': {
162 DVal = HaveLL ? va_arg(Args, s64)
163 : HaveZ ? va_arg(Args, sptr)
164 : va_arg(Args, int);
165 Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
166 break;
168 case 'u':
169 case 'x':
170 case 'X': {
171 UVal = HaveLL ? va_arg(Args, u64)
172 : HaveZ ? va_arg(Args, uptr)
173 : va_arg(Args, unsigned);
174 const bool Upper = (*Cur == 'X');
175 Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
176 Width, PadWithZero, Upper);
177 break;
179 case 'p': {
180 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
181 Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
182 break;
184 case 's': {
185 RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
186 // Only left-justified Width is supported.
187 CHECK(!HaveWidth || LeftJustified);
188 Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
189 Precision, va_arg(Args, char *));
190 break;
192 case 'c': {
193 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
194 Res +=
195 appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
196 break;
198 // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
199 // However, `-Wformat` doesn't know we have a different parser for those
200 // placeholders and it keeps complaining the type mismatch on 64-bit
201 // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
202 // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
203 // `s64`/`u64` and handle the `ld`/`lu` here.
204 case 'l': {
205 ++Cur;
206 RAW_CHECK(*Cur == 'd' || *Cur == 'u');
208 if (*Cur == 'd') {
209 DVal = va_arg(Args, s64);
210 Res +=
211 appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
212 } else {
213 UVal = va_arg(Args, u64);
214 Res += appendUnsigned(&Buffer, BufferEnd, UVal, 10, Width, PadWithZero,
215 false);
218 break;
220 case '%': {
221 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
222 Res += appendChar(&Buffer, BufferEnd, '%');
223 break;
225 default: {
226 RAW_CHECK_MSG(false, PrintfFormatsHelp);
230 RAW_CHECK(Buffer <= BufferEnd);
231 appendChar(&Buffer, BufferEnd + 1, '\0');
232 return Res;
235 int formatString(char *Buffer, uptr BufferLength, const char *Format, ...) {
236 va_list Args;
237 va_start(Args, Format);
238 int Res = formatString(Buffer, BufferLength, Format, Args);
239 va_end(Args);
240 return Res;
243 void ScopedString::vappend(const char *Format, va_list Args) {
244 va_list ArgsCopy;
245 va_copy(ArgsCopy, Args);
246 // formatString doesn't currently support a null buffer or zero buffer length,
247 // so in order to get the resulting formatted string length, we use a one-char
248 // buffer.
249 char C[1];
250 const uptr AdditionalLength =
251 static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1;
252 const uptr Length = length();
253 String.resize(Length + AdditionalLength);
254 const uptr FormattedLength = static_cast<uptr>(formatString(
255 String.data() + Length, String.size() - Length, Format, ArgsCopy));
256 RAW_CHECK(data()[length()] == '\0');
257 RAW_CHECK(FormattedLength + 1 == AdditionalLength);
258 va_end(ArgsCopy);
261 void ScopedString::append(const char *Format, ...) {
262 va_list Args;
263 va_start(Args, Format);
264 vappend(Format, Args);
265 va_end(Args);
268 void Printf(const char *Format, ...) {
269 va_list Args;
270 va_start(Args, Format);
271 ScopedString Msg;
272 Msg.vappend(Format, Args);
273 outputRaw(Msg.data());
274 va_end(Args);
277 } // namespace scudo