xref: /aosp_15_r20/external/scudo/standalone/string_utils.cpp (revision 76559068c068bd27e82aff38fac3bfc865233bca)
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