xref: /llvm-project/compiler-rt/lib/scudo/standalone/string_utils.cpp (revision 1949f7d6c9bd59172c01c7933e1c558797c47eac)
16d46ebefSNico Weber //===-- string_utils.cpp ----------------------------------------*- C++ -*-===//
26d46ebefSNico Weber //
36d46ebefSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46d46ebefSNico Weber // See https://llvm.org/LICENSE.txt for license information.
56d46ebefSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66d46ebefSNico Weber //
76d46ebefSNico Weber //===----------------------------------------------------------------------===//
86d46ebefSNico Weber 
96d46ebefSNico Weber #include "string_utils.h"
106d46ebefSNico Weber #include "common.h"
116d46ebefSNico Weber 
126d46ebefSNico Weber #include <stdarg.h>
136d46ebefSNico Weber #include <string.h>
146d46ebefSNico Weber 
156d46ebefSNico Weber namespace scudo {
166d46ebefSNico Weber 
176d46ebefSNico Weber // Appends number in a given Base to buffer. If its length is less than
186d46ebefSNico Weber // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
196d46ebefSNico Weber // on the value of |PadWithZero|.
appendNumber(u64 AbsoluteValue,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Negative,bool Upper)20*1949f7d6SChristopher Ferris void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
21*1949f7d6SChristopher Ferris                                 bool PadWithZero, bool Negative, bool Upper) {
226d46ebefSNico Weber   constexpr uptr MaxLen = 30;
236d46ebefSNico Weber   RAW_CHECK(Base == 10 || Base == 16);
246d46ebefSNico Weber   RAW_CHECK(Base == 10 || !Negative);
256d46ebefSNico Weber   RAW_CHECK(AbsoluteValue || !Negative);
266d46ebefSNico Weber   RAW_CHECK(MinNumberLength < MaxLen);
276d46ebefSNico Weber   if (Negative && MinNumberLength)
286d46ebefSNico Weber     --MinNumberLength;
29*1949f7d6SChristopher Ferris   if (Negative && PadWithZero) {
30*1949f7d6SChristopher Ferris     String.push_back('-');
31*1949f7d6SChristopher Ferris   }
326d46ebefSNico Weber   uptr NumBuffer[MaxLen];
336d46ebefSNico Weber   int Pos = 0;
346d46ebefSNico Weber   do {
356d46ebefSNico Weber     RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
366d46ebefSNico Weber                   "appendNumber buffer overflow");
376d46ebefSNico Weber     NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
386d46ebefSNico Weber     AbsoluteValue /= Base;
396d46ebefSNico Weber   } while (AbsoluteValue > 0);
406d46ebefSNico Weber   if (Pos < MinNumberLength) {
416d46ebefSNico Weber     memset(&NumBuffer[Pos], 0,
426d46ebefSNico Weber            sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
436d46ebefSNico Weber     Pos = MinNumberLength;
446d46ebefSNico Weber   }
456d46ebefSNico Weber   RAW_CHECK(Pos > 0);
466d46ebefSNico Weber   Pos--;
476d46ebefSNico Weber   for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
486d46ebefSNico Weber     char c = (PadWithZero || Pos == 0) ? '0' : ' ';
49*1949f7d6SChristopher Ferris     String.push_back(c);
506d46ebefSNico Weber   }
516d46ebefSNico Weber   if (Negative && !PadWithZero)
52*1949f7d6SChristopher Ferris     String.push_back('-');
536d46ebefSNico Weber   for (; Pos >= 0; Pos--) {
546d46ebefSNico Weber     char Digit = static_cast<char>(NumBuffer[Pos]);
556d46ebefSNico Weber     Digit = static_cast<char>((Digit < 10) ? '0' + Digit
566d46ebefSNico Weber                                            : (Upper ? 'A' : 'a') + Digit - 10);
57*1949f7d6SChristopher Ferris     String.push_back(Digit);
586d46ebefSNico Weber   }
596d46ebefSNico Weber }
606d46ebefSNico Weber 
appendUnsigned(u64 Num,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Upper)61*1949f7d6SChristopher Ferris void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength,
62*1949f7d6SChristopher Ferris                                   bool PadWithZero, bool Upper) {
63*1949f7d6SChristopher Ferris   appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false,
64*1949f7d6SChristopher Ferris                Upper);
656d46ebefSNico Weber }
666d46ebefSNico Weber 
appendSignedDecimal(s64 Num,u8 MinNumberLength,bool PadWithZero)67*1949f7d6SChristopher Ferris void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength,
68*1949f7d6SChristopher Ferris                                        bool PadWithZero) {
696d46ebefSNico Weber   const bool Negative = (Num < 0);
702efc09c9SKostya Kortchinsky   const u64 UnsignedNum = (Num == INT64_MIN)
712efc09c9SKostya Kortchinsky                               ? static_cast<u64>(INT64_MAX) + 1
722efc09c9SKostya Kortchinsky                               : static_cast<u64>(Negative ? -Num : Num);
73*1949f7d6SChristopher Ferris   appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative,
74*1949f7d6SChristopher Ferris                /*Upper=*/false);
756d46ebefSNico Weber }
766d46ebefSNico Weber 
776d46ebefSNico Weber // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
786d46ebefSNico Weber // interpret Width == 0 as "no Width requested":
796d46ebefSNico Weber // Width == 0 - no Width requested
806d46ebefSNico Weber // Width  < 0 - left-justify S within and pad it to -Width chars, if necessary
816d46ebefSNico Weber // Width  > 0 - right-justify S, not implemented yet
appendString(int Width,int MaxChars,const char * S)82*1949f7d6SChristopher Ferris void ScopedString::appendString(int Width, int MaxChars, const char *S) {
836d46ebefSNico Weber   if (!S)
846d46ebefSNico Weber     S = "<null>";
85*1949f7d6SChristopher Ferris   int NumChars = 0;
866d46ebefSNico Weber   for (; *S; S++) {
87*1949f7d6SChristopher Ferris     if (MaxChars >= 0 && NumChars >= MaxChars)
886d46ebefSNico Weber       break;
89*1949f7d6SChristopher Ferris     String.push_back(*S);
90*1949f7d6SChristopher Ferris     NumChars++;
916d46ebefSNico Weber   }
92*1949f7d6SChristopher Ferris   if (Width < 0) {
93*1949f7d6SChristopher Ferris     // Only left justification supported.
94*1949f7d6SChristopher Ferris     Width = -Width - NumChars;
95*1949f7d6SChristopher Ferris     while (Width-- > 0)
96*1949f7d6SChristopher Ferris       String.push_back(' ');
97*1949f7d6SChristopher Ferris   }
986d46ebefSNico Weber }
996d46ebefSNico Weber 
appendPointer(u64 ptr_value)100*1949f7d6SChristopher Ferris void ScopedString::appendPointer(u64 ptr_value) {
101*1949f7d6SChristopher Ferris   appendString(0, -1, "0x");
102*1949f7d6SChristopher Ferris   appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH,
103*1949f7d6SChristopher Ferris                  /*PadWithZero=*/true,
1046d46ebefSNico Weber                  /*Upper=*/false);
1056d46ebefSNico Weber }
1066d46ebefSNico Weber 
vappend(const char * Format,va_list & Args)107*1949f7d6SChristopher Ferris void ScopedString::vappend(const char *Format, va_list &Args) {
108*1949f7d6SChristopher Ferris   // Since the string contains the '\0' terminator, put our size before it
109*1949f7d6SChristopher Ferris   // so that push_back calls work correctly.
110*1949f7d6SChristopher Ferris   DCHECK(String.size() > 0);
111*1949f7d6SChristopher Ferris   String.resize(String.size() - 1);
112*1949f7d6SChristopher Ferris 
1136d46ebefSNico Weber   static const char *PrintfFormatsHelp =
114*1949f7d6SChristopher Ferris       "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
1156d46ebefSNico Weber       "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
1166d46ebefSNico Weber   RAW_CHECK(Format);
1176d46ebefSNico Weber   const char *Cur = Format;
1186d46ebefSNico Weber   for (; *Cur; Cur++) {
1196d46ebefSNico Weber     if (*Cur != '%') {
120*1949f7d6SChristopher Ferris       String.push_back(*Cur);
1216d46ebefSNico Weber       continue;
1226d46ebefSNico Weber     }
1236d46ebefSNico Weber     Cur++;
1246d46ebefSNico Weber     const bool LeftJustified = *Cur == '-';
1256d46ebefSNico Weber     if (LeftJustified)
1266d46ebefSNico Weber       Cur++;
1276d46ebefSNico Weber     bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
1286d46ebefSNico Weber     const bool PadWithZero = (*Cur == '0');
1296d46ebefSNico Weber     u8 Width = 0;
1306d46ebefSNico Weber     if (HaveWidth) {
1316d46ebefSNico Weber       while (*Cur >= '0' && *Cur <= '9')
1326d46ebefSNico Weber         Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
1336d46ebefSNico Weber     }
1346d46ebefSNico Weber     const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
1356d46ebefSNico Weber     int Precision = -1;
1366d46ebefSNico Weber     if (HavePrecision) {
1376d46ebefSNico Weber       Cur += 2;
1386d46ebefSNico Weber       Precision = va_arg(Args, int);
1396d46ebefSNico Weber     }
1406d46ebefSNico Weber     const bool HaveZ = (*Cur == 'z');
1416d46ebefSNico Weber     Cur += HaveZ;
1426d46ebefSNico Weber     const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
1436d46ebefSNico Weber     Cur += HaveLL * 2;
1446d46ebefSNico Weber     s64 DVal;
1456d46ebefSNico Weber     u64 UVal;
1466d46ebefSNico Weber     const bool HaveLength = HaveZ || HaveLL;
1476d46ebefSNico Weber     const bool HaveFlags = HaveWidth || HaveLength;
1486d46ebefSNico Weber     // At the moment only %s supports precision and left-justification.
1496d46ebefSNico Weber     CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
1506d46ebefSNico Weber     switch (*Cur) {
1516d46ebefSNico Weber     case 'd': {
1526d46ebefSNico Weber       DVal = HaveLL  ? va_arg(Args, s64)
1532efc09c9SKostya Kortchinsky              : HaveZ ? va_arg(Args, sptr)
1542efc09c9SKostya Kortchinsky                      : va_arg(Args, int);
155*1949f7d6SChristopher Ferris       appendSignedDecimal(DVal, Width, PadWithZero);
1566d46ebefSNico Weber       break;
1576d46ebefSNico Weber     }
1586d46ebefSNico Weber     case 'u':
1596d46ebefSNico Weber     case 'x':
1606d46ebefSNico Weber     case 'X': {
1616d46ebefSNico Weber       UVal = HaveLL  ? va_arg(Args, u64)
1622efc09c9SKostya Kortchinsky              : HaveZ ? va_arg(Args, uptr)
1632efc09c9SKostya Kortchinsky                      : va_arg(Args, unsigned);
1646d46ebefSNico Weber       const bool Upper = (*Cur == 'X');
165*1949f7d6SChristopher Ferris       appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper);
1666d46ebefSNico Weber       break;
1676d46ebefSNico Weber     }
1686d46ebefSNico Weber     case 'p': {
1696d46ebefSNico Weber       RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
170*1949f7d6SChristopher Ferris       appendPointer(va_arg(Args, uptr));
1716d46ebefSNico Weber       break;
1726d46ebefSNico Weber     }
1736d46ebefSNico Weber     case 's': {
1746d46ebefSNico Weber       RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
1756d46ebefSNico Weber       // Only left-justified Width is supported.
1766d46ebefSNico Weber       CHECK(!HaveWidth || LeftJustified);
177*1949f7d6SChristopher Ferris       appendString(LeftJustified ? -Width : Width, Precision,
178*1949f7d6SChristopher Ferris                    va_arg(Args, char *));
1796d46ebefSNico Weber       break;
1806d46ebefSNico Weber     }
1816d46ebefSNico Weber     case 'c': {
1826d46ebefSNico Weber       RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
183*1949f7d6SChristopher Ferris       String.push_back(static_cast<char>(va_arg(Args, int)));
1846d46ebefSNico Weber       break;
1856d46ebefSNico Weber     }
1861e36156aSChia-hung Duan     // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
1871e36156aSChia-hung Duan     // However, `-Wformat` doesn't know we have a different parser for those
1881e36156aSChia-hung Duan     // placeholders and it keeps complaining the type mismatch on 64-bit
1891e36156aSChia-hung Duan     // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
1901e36156aSChia-hung Duan     // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
1911e36156aSChia-hung Duan     // `s64`/`u64` and handle the `ld`/`lu` here.
1921e36156aSChia-hung Duan     case 'l': {
1931e36156aSChia-hung Duan       ++Cur;
1941e36156aSChia-hung Duan       RAW_CHECK(*Cur == 'd' || *Cur == 'u');
1951e36156aSChia-hung Duan 
1961e36156aSChia-hung Duan       if (*Cur == 'd') {
1971e36156aSChia-hung Duan         DVal = va_arg(Args, s64);
198*1949f7d6SChristopher Ferris         appendSignedDecimal(DVal, Width, PadWithZero);
1991e36156aSChia-hung Duan       } else {
2001e36156aSChia-hung Duan         UVal = va_arg(Args, u64);
201*1949f7d6SChristopher Ferris         appendUnsigned(UVal, 10, Width, PadWithZero, false);
2021e36156aSChia-hung Duan       }
2031e36156aSChia-hung Duan 
2041e36156aSChia-hung Duan       break;
2051e36156aSChia-hung Duan     }
2066d46ebefSNico Weber     case '%': {
2076d46ebefSNico Weber       RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
208*1949f7d6SChristopher Ferris       String.push_back('%');
2096d46ebefSNico Weber       break;
2106d46ebefSNico Weber     }
2116d46ebefSNico Weber     default: {
2126d46ebefSNico Weber       RAW_CHECK_MSG(false, PrintfFormatsHelp);
2136d46ebefSNico Weber     }
2146d46ebefSNico Weber     }
2156d46ebefSNico Weber   }
216*1949f7d6SChristopher Ferris   String.push_back('\0');
217*1949f7d6SChristopher Ferris   if (String.back() != '\0') {
218*1949f7d6SChristopher Ferris     // String truncated, make sure the string is terminated properly.
219*1949f7d6SChristopher Ferris     // This can happen if there is no more memory when trying to resize
220*1949f7d6SChristopher Ferris     // the string.
221*1949f7d6SChristopher Ferris     String.back() = '\0';
2226d46ebefSNico Weber   }
2236d46ebefSNico Weber }
2246d46ebefSNico Weber 
append(const char * Format,...)2256d46ebefSNico Weber void ScopedString::append(const char *Format, ...) {
2266d46ebefSNico Weber   va_list Args;
2276d46ebefSNico Weber   va_start(Args, Format);
228a27c416bSChristopher Ferris   vappend(Format, Args);
2296d46ebefSNico Weber   va_end(Args);
2306d46ebefSNico Weber }
2316d46ebefSNico Weber 
Printf(const char * Format,...)2326d46ebefSNico Weber void Printf(const char *Format, ...) {
2336d46ebefSNico Weber   va_list Args;
2346d46ebefSNico Weber   va_start(Args, Format);
235868317b3SKostya Kortchinsky   ScopedString Msg;
236a27c416bSChristopher Ferris   Msg.vappend(Format, Args);
2376d46ebefSNico Weber   outputRaw(Msg.data());
2386d46ebefSNico Weber   va_end(Args);
2396d46ebefSNico Weber }
2406d46ebefSNico Weber 
2416d46ebefSNico Weber } // namespace scudo
242