1*76559068SAndroid Build Coastguard Worker //===-- string_utils.cpp ----------------------------------------*- C++ -*-===//
2*76559068SAndroid Build Coastguard Worker //
3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*76559068SAndroid Build Coastguard Worker //
7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*76559068SAndroid Build Coastguard Worker
9*76559068SAndroid Build Coastguard Worker #include "string_utils.h"
10*76559068SAndroid Build Coastguard Worker #include "common.h"
11*76559068SAndroid Build Coastguard Worker
12*76559068SAndroid Build Coastguard Worker #include <stdarg.h>
13*76559068SAndroid Build Coastguard Worker #include <string.h>
14*76559068SAndroid Build Coastguard Worker
15*76559068SAndroid Build Coastguard Worker namespace scudo {
16*76559068SAndroid Build Coastguard Worker
17*76559068SAndroid Build Coastguard Worker // Appends number in a given Base to buffer. If its length is less than
18*76559068SAndroid Build Coastguard Worker // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
19*76559068SAndroid Build Coastguard Worker // on the value of |PadWithZero|.
appendNumber(u64 AbsoluteValue,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Negative,bool Upper)20*76559068SAndroid Build Coastguard Worker void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
21*76559068SAndroid Build Coastguard Worker bool PadWithZero, bool Negative, bool Upper) {
22*76559068SAndroid Build Coastguard Worker constexpr uptr MaxLen = 30;
23*76559068SAndroid Build Coastguard Worker RAW_CHECK(Base == 10 || Base == 16);
24*76559068SAndroid Build Coastguard Worker RAW_CHECK(Base == 10 || !Negative);
25*76559068SAndroid Build Coastguard Worker RAW_CHECK(AbsoluteValue || !Negative);
26*76559068SAndroid Build Coastguard Worker RAW_CHECK(MinNumberLength < MaxLen);
27*76559068SAndroid Build Coastguard Worker if (Negative && MinNumberLength)
28*76559068SAndroid Build Coastguard Worker --MinNumberLength;
29*76559068SAndroid Build Coastguard Worker if (Negative && PadWithZero) {
30*76559068SAndroid Build Coastguard Worker String.push_back('-');
31*76559068SAndroid Build Coastguard Worker }
32*76559068SAndroid Build Coastguard Worker uptr NumBuffer[MaxLen];
33*76559068SAndroid Build Coastguard Worker int Pos = 0;
34*76559068SAndroid Build Coastguard Worker do {
35*76559068SAndroid Build Coastguard Worker RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
36*76559068SAndroid Build Coastguard Worker "appendNumber buffer overflow");
37*76559068SAndroid Build Coastguard Worker NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
38*76559068SAndroid Build Coastguard Worker AbsoluteValue /= Base;
39*76559068SAndroid Build Coastguard Worker } while (AbsoluteValue > 0);
40*76559068SAndroid Build Coastguard Worker if (Pos < MinNumberLength) {
41*76559068SAndroid Build Coastguard Worker memset(&NumBuffer[Pos], 0,
42*76559068SAndroid Build Coastguard Worker sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
43*76559068SAndroid Build Coastguard Worker Pos = MinNumberLength;
44*76559068SAndroid Build Coastguard Worker }
45*76559068SAndroid Build Coastguard Worker RAW_CHECK(Pos > 0);
46*76559068SAndroid Build Coastguard Worker Pos--;
47*76559068SAndroid Build Coastguard Worker for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
48*76559068SAndroid Build Coastguard Worker char c = (PadWithZero || Pos == 0) ? '0' : ' ';
49*76559068SAndroid Build Coastguard Worker String.push_back(c);
50*76559068SAndroid Build Coastguard Worker }
51*76559068SAndroid Build Coastguard Worker if (Negative && !PadWithZero)
52*76559068SAndroid Build Coastguard Worker String.push_back('-');
53*76559068SAndroid Build Coastguard Worker for (; Pos >= 0; Pos--) {
54*76559068SAndroid Build Coastguard Worker char Digit = static_cast<char>(NumBuffer[Pos]);
55*76559068SAndroid Build Coastguard Worker Digit = static_cast<char>((Digit < 10) ? '0' + Digit
56*76559068SAndroid Build Coastguard Worker : (Upper ? 'A' : 'a') + Digit - 10);
57*76559068SAndroid Build Coastguard Worker String.push_back(Digit);
58*76559068SAndroid Build Coastguard Worker }
59*76559068SAndroid Build Coastguard Worker }
60*76559068SAndroid Build Coastguard Worker
appendUnsigned(u64 Num,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Upper)61*76559068SAndroid Build Coastguard Worker void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength,
62*76559068SAndroid Build Coastguard Worker bool PadWithZero, bool Upper) {
63*76559068SAndroid Build Coastguard Worker appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false,
64*76559068SAndroid Build Coastguard Worker Upper);
65*76559068SAndroid Build Coastguard Worker }
66*76559068SAndroid Build Coastguard Worker
appendSignedDecimal(s64 Num,u8 MinNumberLength,bool PadWithZero)67*76559068SAndroid Build Coastguard Worker void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength,
68*76559068SAndroid Build Coastguard Worker bool PadWithZero) {
69*76559068SAndroid Build Coastguard Worker const bool Negative = (Num < 0);
70*76559068SAndroid Build Coastguard Worker const u64 UnsignedNum = (Num == INT64_MIN)
71*76559068SAndroid Build Coastguard Worker ? static_cast<u64>(INT64_MAX) + 1
72*76559068SAndroid Build Coastguard Worker : static_cast<u64>(Negative ? -Num : Num);
73*76559068SAndroid Build Coastguard Worker appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative,
74*76559068SAndroid Build Coastguard Worker /*Upper=*/false);
75*76559068SAndroid Build Coastguard Worker }
76*76559068SAndroid Build Coastguard Worker
77*76559068SAndroid Build Coastguard Worker // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
78*76559068SAndroid Build Coastguard Worker // interpret Width == 0 as "no Width requested":
79*76559068SAndroid Build Coastguard Worker // Width == 0 - no Width requested
80*76559068SAndroid Build Coastguard Worker // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
81*76559068SAndroid Build Coastguard Worker // Width > 0 - right-justify S, not implemented yet
appendString(int Width,int MaxChars,const char * S)82*76559068SAndroid Build Coastguard Worker void ScopedString::appendString(int Width, int MaxChars, const char *S) {
83*76559068SAndroid Build Coastguard Worker if (!S)
84*76559068SAndroid Build Coastguard Worker S = "<null>";
85*76559068SAndroid Build Coastguard Worker int NumChars = 0;
86*76559068SAndroid Build Coastguard Worker for (; *S; S++) {
87*76559068SAndroid Build Coastguard Worker if (MaxChars >= 0 && NumChars >= MaxChars)
88*76559068SAndroid Build Coastguard Worker break;
89*76559068SAndroid Build Coastguard Worker String.push_back(*S);
90*76559068SAndroid Build Coastguard Worker NumChars++;
91*76559068SAndroid Build Coastguard Worker }
92*76559068SAndroid Build Coastguard Worker if (Width < 0) {
93*76559068SAndroid Build Coastguard Worker // Only left justification supported.
94*76559068SAndroid Build Coastguard Worker Width = -Width - NumChars;
95*76559068SAndroid Build Coastguard Worker while (Width-- > 0)
96*76559068SAndroid Build Coastguard Worker String.push_back(' ');
97*76559068SAndroid Build Coastguard Worker }
98*76559068SAndroid Build Coastguard Worker }
99*76559068SAndroid Build Coastguard Worker
appendPointer(u64 ptr_value)100*76559068SAndroid Build Coastguard Worker void ScopedString::appendPointer(u64 ptr_value) {
101*76559068SAndroid Build Coastguard Worker appendString(0, -1, "0x");
102*76559068SAndroid Build Coastguard Worker appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH,
103*76559068SAndroid Build Coastguard Worker /*PadWithZero=*/true,
104*76559068SAndroid Build Coastguard Worker /*Upper=*/false);
105*76559068SAndroid Build Coastguard Worker }
106*76559068SAndroid Build Coastguard Worker
vappend(const char * Format,va_list & Args)107*76559068SAndroid Build Coastguard Worker void ScopedString::vappend(const char *Format, va_list &Args) {
108*76559068SAndroid Build Coastguard Worker // Since the string contains the '\0' terminator, put our size before it
109*76559068SAndroid Build Coastguard Worker // so that push_back calls work correctly.
110*76559068SAndroid Build Coastguard Worker DCHECK(String.size() > 0);
111*76559068SAndroid Build Coastguard Worker String.resize(String.size() - 1);
112*76559068SAndroid Build Coastguard Worker
113*76559068SAndroid Build Coastguard Worker static const char *PrintfFormatsHelp =
114*76559068SAndroid Build Coastguard Worker "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
115*76559068SAndroid Build Coastguard Worker "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
116*76559068SAndroid Build Coastguard Worker RAW_CHECK(Format);
117*76559068SAndroid Build Coastguard Worker const char *Cur = Format;
118*76559068SAndroid Build Coastguard Worker for (; *Cur; Cur++) {
119*76559068SAndroid Build Coastguard Worker if (*Cur != '%') {
120*76559068SAndroid Build Coastguard Worker String.push_back(*Cur);
121*76559068SAndroid Build Coastguard Worker continue;
122*76559068SAndroid Build Coastguard Worker }
123*76559068SAndroid Build Coastguard Worker Cur++;
124*76559068SAndroid Build Coastguard Worker const bool LeftJustified = *Cur == '-';
125*76559068SAndroid Build Coastguard Worker if (LeftJustified)
126*76559068SAndroid Build Coastguard Worker Cur++;
127*76559068SAndroid Build Coastguard Worker bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
128*76559068SAndroid Build Coastguard Worker const bool PadWithZero = (*Cur == '0');
129*76559068SAndroid Build Coastguard Worker u8 Width = 0;
130*76559068SAndroid Build Coastguard Worker if (HaveWidth) {
131*76559068SAndroid Build Coastguard Worker while (*Cur >= '0' && *Cur <= '9')
132*76559068SAndroid Build Coastguard Worker Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
133*76559068SAndroid Build Coastguard Worker }
134*76559068SAndroid Build Coastguard Worker const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
135*76559068SAndroid Build Coastguard Worker int Precision = -1;
136*76559068SAndroid Build Coastguard Worker if (HavePrecision) {
137*76559068SAndroid Build Coastguard Worker Cur += 2;
138*76559068SAndroid Build Coastguard Worker Precision = va_arg(Args, int);
139*76559068SAndroid Build Coastguard Worker }
140*76559068SAndroid Build Coastguard Worker const bool HaveZ = (*Cur == 'z');
141*76559068SAndroid Build Coastguard Worker Cur += HaveZ;
142*76559068SAndroid Build Coastguard Worker const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
143*76559068SAndroid Build Coastguard Worker Cur += HaveLL * 2;
144*76559068SAndroid Build Coastguard Worker s64 DVal;
145*76559068SAndroid Build Coastguard Worker u64 UVal;
146*76559068SAndroid Build Coastguard Worker const bool HaveLength = HaveZ || HaveLL;
147*76559068SAndroid Build Coastguard Worker const bool HaveFlags = HaveWidth || HaveLength;
148*76559068SAndroid Build Coastguard Worker // At the moment only %s supports precision and left-justification.
149*76559068SAndroid Build Coastguard Worker CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
150*76559068SAndroid Build Coastguard Worker switch (*Cur) {
151*76559068SAndroid Build Coastguard Worker case 'd': {
152*76559068SAndroid Build Coastguard Worker DVal = HaveLL ? va_arg(Args, s64)
153*76559068SAndroid Build Coastguard Worker : HaveZ ? va_arg(Args, sptr)
154*76559068SAndroid Build Coastguard Worker : va_arg(Args, int);
155*76559068SAndroid Build Coastguard Worker appendSignedDecimal(DVal, Width, PadWithZero);
156*76559068SAndroid Build Coastguard Worker break;
157*76559068SAndroid Build Coastguard Worker }
158*76559068SAndroid Build Coastguard Worker case 'u':
159*76559068SAndroid Build Coastguard Worker case 'x':
160*76559068SAndroid Build Coastguard Worker case 'X': {
161*76559068SAndroid Build Coastguard Worker UVal = HaveLL ? va_arg(Args, u64)
162*76559068SAndroid Build Coastguard Worker : HaveZ ? va_arg(Args, uptr)
163*76559068SAndroid Build Coastguard Worker : va_arg(Args, unsigned);
164*76559068SAndroid Build Coastguard Worker const bool Upper = (*Cur == 'X');
165*76559068SAndroid Build Coastguard Worker appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper);
166*76559068SAndroid Build Coastguard Worker break;
167*76559068SAndroid Build Coastguard Worker }
168*76559068SAndroid Build Coastguard Worker case 'p': {
169*76559068SAndroid Build Coastguard Worker RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
170*76559068SAndroid Build Coastguard Worker appendPointer(va_arg(Args, uptr));
171*76559068SAndroid Build Coastguard Worker break;
172*76559068SAndroid Build Coastguard Worker }
173*76559068SAndroid Build Coastguard Worker case 's': {
174*76559068SAndroid Build Coastguard Worker RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
175*76559068SAndroid Build Coastguard Worker // Only left-justified Width is supported.
176*76559068SAndroid Build Coastguard Worker CHECK(!HaveWidth || LeftJustified);
177*76559068SAndroid Build Coastguard Worker appendString(LeftJustified ? -Width : Width, Precision,
178*76559068SAndroid Build Coastguard Worker va_arg(Args, char *));
179*76559068SAndroid Build Coastguard Worker break;
180*76559068SAndroid Build Coastguard Worker }
181*76559068SAndroid Build Coastguard Worker case 'c': {
182*76559068SAndroid Build Coastguard Worker RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
183*76559068SAndroid Build Coastguard Worker String.push_back(static_cast<char>(va_arg(Args, int)));
184*76559068SAndroid Build Coastguard Worker break;
185*76559068SAndroid Build Coastguard Worker }
186*76559068SAndroid Build Coastguard Worker // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
187*76559068SAndroid Build Coastguard Worker // However, `-Wformat` doesn't know we have a different parser for those
188*76559068SAndroid Build Coastguard Worker // placeholders and it keeps complaining the type mismatch on 64-bit
189*76559068SAndroid Build Coastguard Worker // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
190*76559068SAndroid Build Coastguard Worker // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
191*76559068SAndroid Build Coastguard Worker // `s64`/`u64` and handle the `ld`/`lu` here.
192*76559068SAndroid Build Coastguard Worker case 'l': {
193*76559068SAndroid Build Coastguard Worker ++Cur;
194*76559068SAndroid Build Coastguard Worker RAW_CHECK(*Cur == 'd' || *Cur == 'u');
195*76559068SAndroid Build Coastguard Worker
196*76559068SAndroid Build Coastguard Worker if (*Cur == 'd') {
197*76559068SAndroid Build Coastguard Worker DVal = va_arg(Args, s64);
198*76559068SAndroid Build Coastguard Worker appendSignedDecimal(DVal, Width, PadWithZero);
199*76559068SAndroid Build Coastguard Worker } else {
200*76559068SAndroid Build Coastguard Worker UVal = va_arg(Args, u64);
201*76559068SAndroid Build Coastguard Worker appendUnsigned(UVal, 10, Width, PadWithZero, false);
202*76559068SAndroid Build Coastguard Worker }
203*76559068SAndroid Build Coastguard Worker
204*76559068SAndroid Build Coastguard Worker break;
205*76559068SAndroid Build Coastguard Worker }
206*76559068SAndroid Build Coastguard Worker case '%': {
207*76559068SAndroid Build Coastguard Worker RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
208*76559068SAndroid Build Coastguard Worker String.push_back('%');
209*76559068SAndroid Build Coastguard Worker break;
210*76559068SAndroid Build Coastguard Worker }
211*76559068SAndroid Build Coastguard Worker default: {
212*76559068SAndroid Build Coastguard Worker RAW_CHECK_MSG(false, PrintfFormatsHelp);
213*76559068SAndroid Build Coastguard Worker }
214*76559068SAndroid Build Coastguard Worker }
215*76559068SAndroid Build Coastguard Worker }
216*76559068SAndroid Build Coastguard Worker String.push_back('\0');
217*76559068SAndroid Build Coastguard Worker if (String.back() != '\0') {
218*76559068SAndroid Build Coastguard Worker // String truncated, make sure the string is terminated properly.
219*76559068SAndroid Build Coastguard Worker // This can happen if there is no more memory when trying to resize
220*76559068SAndroid Build Coastguard Worker // the string.
221*76559068SAndroid Build Coastguard Worker String.back() = '\0';
222*76559068SAndroid Build Coastguard Worker }
223*76559068SAndroid Build Coastguard Worker }
224*76559068SAndroid Build Coastguard Worker
append(const char * Format,...)225*76559068SAndroid Build Coastguard Worker void ScopedString::append(const char *Format, ...) {
226*76559068SAndroid Build Coastguard Worker va_list Args;
227*76559068SAndroid Build Coastguard Worker va_start(Args, Format);
228*76559068SAndroid Build Coastguard Worker vappend(Format, Args);
229*76559068SAndroid Build Coastguard Worker va_end(Args);
230*76559068SAndroid Build Coastguard Worker }
231*76559068SAndroid Build Coastguard Worker
Printf(const char * Format,...)232*76559068SAndroid Build Coastguard Worker void Printf(const char *Format, ...) {
233*76559068SAndroid Build Coastguard Worker va_list Args;
234*76559068SAndroid Build Coastguard Worker va_start(Args, Format);
235*76559068SAndroid Build Coastguard Worker ScopedString Msg;
236*76559068SAndroid Build Coastguard Worker Msg.vappend(Format, Args);
237*76559068SAndroid Build Coastguard Worker outputRaw(Msg.data());
238*76559068SAndroid Build Coastguard Worker va_end(Args);
239*76559068SAndroid Build Coastguard Worker }
240*76559068SAndroid Build Coastguard Worker
241*76559068SAndroid Build Coastguard Worker } // namespace scudo
242