xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/string_utils.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
168d75effSDimitry Andric //===-- string_utils.cpp ----------------------------------------*- C++ -*-===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric 
968d75effSDimitry Andric #include "string_utils.h"
1068d75effSDimitry Andric #include "common.h"
1168d75effSDimitry Andric 
1268d75effSDimitry Andric #include <stdarg.h>
1368d75effSDimitry Andric #include <string.h>
1468d75effSDimitry Andric 
1568d75effSDimitry Andric namespace scudo {
1668d75effSDimitry Andric 
1768d75effSDimitry Andric // Appends number in a given Base to buffer. If its length is less than
1868d75effSDimitry Andric // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
1968d75effSDimitry Andric // on the value of |PadWithZero|.
20*0fca6ea1SDimitry Andric void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
21*0fca6ea1SDimitry Andric                                 bool PadWithZero, bool Negative, bool Upper) {
2268d75effSDimitry Andric   constexpr uptr MaxLen = 30;
2368d75effSDimitry Andric   RAW_CHECK(Base == 10 || Base == 16);
2468d75effSDimitry Andric   RAW_CHECK(Base == 10 || !Negative);
2568d75effSDimitry Andric   RAW_CHECK(AbsoluteValue || !Negative);
2668d75effSDimitry Andric   RAW_CHECK(MinNumberLength < MaxLen);
2768d75effSDimitry Andric   if (Negative && MinNumberLength)
2868d75effSDimitry Andric     --MinNumberLength;
29*0fca6ea1SDimitry Andric   if (Negative && PadWithZero) {
30*0fca6ea1SDimitry Andric     String.push_back('-');
31*0fca6ea1SDimitry Andric   }
3268d75effSDimitry Andric   uptr NumBuffer[MaxLen];
3368d75effSDimitry Andric   int Pos = 0;
3468d75effSDimitry Andric   do {
3568d75effSDimitry Andric     RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
3668d75effSDimitry Andric                   "appendNumber buffer overflow");
3768d75effSDimitry Andric     NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
3868d75effSDimitry Andric     AbsoluteValue /= Base;
3968d75effSDimitry Andric   } while (AbsoluteValue > 0);
4068d75effSDimitry Andric   if (Pos < MinNumberLength) {
4168d75effSDimitry Andric     memset(&NumBuffer[Pos], 0,
4268d75effSDimitry Andric            sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
4368d75effSDimitry Andric     Pos = MinNumberLength;
4468d75effSDimitry Andric   }
4568d75effSDimitry Andric   RAW_CHECK(Pos > 0);
4668d75effSDimitry Andric   Pos--;
4768d75effSDimitry Andric   for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
4868d75effSDimitry Andric     char c = (PadWithZero || Pos == 0) ? '0' : ' ';
49*0fca6ea1SDimitry Andric     String.push_back(c);
5068d75effSDimitry Andric   }
5168d75effSDimitry Andric   if (Negative && !PadWithZero)
52*0fca6ea1SDimitry Andric     String.push_back('-');
5368d75effSDimitry Andric   for (; Pos >= 0; Pos--) {
5468d75effSDimitry Andric     char Digit = static_cast<char>(NumBuffer[Pos]);
5568d75effSDimitry Andric     Digit = static_cast<char>((Digit < 10) ? '0' + Digit
5668d75effSDimitry Andric                                            : (Upper ? 'A' : 'a') + Digit - 10);
57*0fca6ea1SDimitry Andric     String.push_back(Digit);
5868d75effSDimitry Andric   }
5968d75effSDimitry Andric }
6068d75effSDimitry Andric 
61*0fca6ea1SDimitry Andric void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength,
62*0fca6ea1SDimitry Andric                                   bool PadWithZero, bool Upper) {
63*0fca6ea1SDimitry Andric   appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false,
64*0fca6ea1SDimitry Andric                Upper);
6568d75effSDimitry Andric }
6668d75effSDimitry Andric 
67*0fca6ea1SDimitry Andric void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength,
68*0fca6ea1SDimitry Andric                                        bool PadWithZero) {
6968d75effSDimitry Andric   const bool Negative = (Num < 0);
70e8d8bef9SDimitry Andric   const u64 UnsignedNum = (Num == INT64_MIN)
71e8d8bef9SDimitry Andric                               ? static_cast<u64>(INT64_MAX) + 1
72e8d8bef9SDimitry Andric                               : static_cast<u64>(Negative ? -Num : Num);
73*0fca6ea1SDimitry Andric   appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative,
74*0fca6ea1SDimitry Andric                /*Upper=*/false);
7568d75effSDimitry Andric }
7668d75effSDimitry Andric 
7768d75effSDimitry Andric // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
7868d75effSDimitry Andric // interpret Width == 0 as "no Width requested":
7968d75effSDimitry Andric // Width == 0 - no Width requested
8068d75effSDimitry Andric // Width  < 0 - left-justify S within and pad it to -Width chars, if necessary
8168d75effSDimitry Andric // Width  > 0 - right-justify S, not implemented yet
82*0fca6ea1SDimitry Andric void ScopedString::appendString(int Width, int MaxChars, const char *S) {
8368d75effSDimitry Andric   if (!S)
8468d75effSDimitry Andric     S = "<null>";
85*0fca6ea1SDimitry Andric   int NumChars = 0;
8668d75effSDimitry Andric   for (; *S; S++) {
87*0fca6ea1SDimitry Andric     if (MaxChars >= 0 && NumChars >= MaxChars)
8868d75effSDimitry Andric       break;
89*0fca6ea1SDimitry Andric     String.push_back(*S);
90*0fca6ea1SDimitry Andric     NumChars++;
9168d75effSDimitry Andric   }
92*0fca6ea1SDimitry Andric   if (Width < 0) {
93*0fca6ea1SDimitry Andric     // Only left justification supported.
94*0fca6ea1SDimitry Andric     Width = -Width - NumChars;
95*0fca6ea1SDimitry Andric     while (Width-- > 0)
96*0fca6ea1SDimitry Andric       String.push_back(' ');
97*0fca6ea1SDimitry Andric   }
9868d75effSDimitry Andric }
9968d75effSDimitry Andric 
100*0fca6ea1SDimitry Andric void ScopedString::appendPointer(u64 ptr_value) {
101*0fca6ea1SDimitry Andric   appendString(0, -1, "0x");
102*0fca6ea1SDimitry Andric   appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH,
103*0fca6ea1SDimitry Andric                  /*PadWithZero=*/true,
10468d75effSDimitry Andric                  /*Upper=*/false);
10568d75effSDimitry Andric }
10668d75effSDimitry Andric 
107*0fca6ea1SDimitry Andric void ScopedString::vappend(const char *Format, va_list &Args) {
108*0fca6ea1SDimitry Andric   // Since the string contains the '\0' terminator, put our size before it
109*0fca6ea1SDimitry Andric   // so that push_back calls work correctly.
110*0fca6ea1SDimitry Andric   DCHECK(String.size() > 0);
111*0fca6ea1SDimitry Andric   String.resize(String.size() - 1);
112*0fca6ea1SDimitry Andric 
11368d75effSDimitry Andric   static const char *PrintfFormatsHelp =
114*0fca6ea1SDimitry Andric       "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
11568d75effSDimitry Andric       "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
11668d75effSDimitry Andric   RAW_CHECK(Format);
11768d75effSDimitry Andric   const char *Cur = Format;
11868d75effSDimitry Andric   for (; *Cur; Cur++) {
11968d75effSDimitry Andric     if (*Cur != '%') {
120*0fca6ea1SDimitry Andric       String.push_back(*Cur);
12168d75effSDimitry Andric       continue;
12268d75effSDimitry Andric     }
12368d75effSDimitry Andric     Cur++;
12468d75effSDimitry Andric     const bool LeftJustified = *Cur == '-';
12568d75effSDimitry Andric     if (LeftJustified)
12668d75effSDimitry Andric       Cur++;
12768d75effSDimitry Andric     bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
12868d75effSDimitry Andric     const bool PadWithZero = (*Cur == '0');
12968d75effSDimitry Andric     u8 Width = 0;
13068d75effSDimitry Andric     if (HaveWidth) {
13168d75effSDimitry Andric       while (*Cur >= '0' && *Cur <= '9')
13268d75effSDimitry Andric         Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
13368d75effSDimitry Andric     }
13468d75effSDimitry Andric     const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
13568d75effSDimitry Andric     int Precision = -1;
13668d75effSDimitry Andric     if (HavePrecision) {
13768d75effSDimitry Andric       Cur += 2;
13868d75effSDimitry Andric       Precision = va_arg(Args, int);
13968d75effSDimitry Andric     }
14068d75effSDimitry Andric     const bool HaveZ = (*Cur == 'z');
14168d75effSDimitry Andric     Cur += HaveZ;
14268d75effSDimitry Andric     const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
14368d75effSDimitry Andric     Cur += HaveLL * 2;
14468d75effSDimitry Andric     s64 DVal;
14568d75effSDimitry Andric     u64 UVal;
14668d75effSDimitry Andric     const bool HaveLength = HaveZ || HaveLL;
14768d75effSDimitry Andric     const bool HaveFlags = HaveWidth || HaveLength;
14868d75effSDimitry Andric     // At the moment only %s supports precision and left-justification.
14968d75effSDimitry Andric     CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
15068d75effSDimitry Andric     switch (*Cur) {
15168d75effSDimitry Andric     case 'd': {
15268d75effSDimitry Andric       DVal = HaveLL  ? va_arg(Args, s64)
153e8d8bef9SDimitry Andric              : HaveZ ? va_arg(Args, sptr)
154e8d8bef9SDimitry Andric                      : va_arg(Args, int);
155*0fca6ea1SDimitry Andric       appendSignedDecimal(DVal, Width, PadWithZero);
15668d75effSDimitry Andric       break;
15768d75effSDimitry Andric     }
15868d75effSDimitry Andric     case 'u':
15968d75effSDimitry Andric     case 'x':
16068d75effSDimitry Andric     case 'X': {
16168d75effSDimitry Andric       UVal = HaveLL  ? va_arg(Args, u64)
162e8d8bef9SDimitry Andric              : HaveZ ? va_arg(Args, uptr)
163e8d8bef9SDimitry Andric                      : va_arg(Args, unsigned);
16468d75effSDimitry Andric       const bool Upper = (*Cur == 'X');
165*0fca6ea1SDimitry Andric       appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper);
16668d75effSDimitry Andric       break;
16768d75effSDimitry Andric     }
16868d75effSDimitry Andric     case 'p': {
16968d75effSDimitry Andric       RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
170*0fca6ea1SDimitry Andric       appendPointer(va_arg(Args, uptr));
17168d75effSDimitry Andric       break;
17268d75effSDimitry Andric     }
17368d75effSDimitry Andric     case 's': {
17468d75effSDimitry Andric       RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
17568d75effSDimitry Andric       // Only left-justified Width is supported.
17668d75effSDimitry Andric       CHECK(!HaveWidth || LeftJustified);
177*0fca6ea1SDimitry Andric       appendString(LeftJustified ? -Width : Width, Precision,
178*0fca6ea1SDimitry Andric                    va_arg(Args, char *));
17968d75effSDimitry Andric       break;
18068d75effSDimitry Andric     }
18168d75effSDimitry Andric     case 'c': {
18268d75effSDimitry Andric       RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
183*0fca6ea1SDimitry Andric       String.push_back(static_cast<char>(va_arg(Args, int)));
18468d75effSDimitry Andric       break;
18568d75effSDimitry Andric     }
18606c3fb27SDimitry Andric     // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
18706c3fb27SDimitry Andric     // However, `-Wformat` doesn't know we have a different parser for those
18806c3fb27SDimitry Andric     // placeholders and it keeps complaining the type mismatch on 64-bit
18906c3fb27SDimitry Andric     // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
19006c3fb27SDimitry Andric     // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
19106c3fb27SDimitry Andric     // `s64`/`u64` and handle the `ld`/`lu` here.
19206c3fb27SDimitry Andric     case 'l': {
19306c3fb27SDimitry Andric       ++Cur;
19406c3fb27SDimitry Andric       RAW_CHECK(*Cur == 'd' || *Cur == 'u');
19506c3fb27SDimitry Andric 
19606c3fb27SDimitry Andric       if (*Cur == 'd') {
19706c3fb27SDimitry Andric         DVal = va_arg(Args, s64);
198*0fca6ea1SDimitry Andric         appendSignedDecimal(DVal, Width, PadWithZero);
19906c3fb27SDimitry Andric       } else {
20006c3fb27SDimitry Andric         UVal = va_arg(Args, u64);
201*0fca6ea1SDimitry Andric         appendUnsigned(UVal, 10, Width, PadWithZero, false);
20206c3fb27SDimitry Andric       }
20306c3fb27SDimitry Andric 
20406c3fb27SDimitry Andric       break;
20506c3fb27SDimitry Andric     }
20668d75effSDimitry Andric     case '%': {
20768d75effSDimitry Andric       RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
208*0fca6ea1SDimitry Andric       String.push_back('%');
20968d75effSDimitry Andric       break;
21068d75effSDimitry Andric     }
21168d75effSDimitry Andric     default: {
21268d75effSDimitry Andric       RAW_CHECK_MSG(false, PrintfFormatsHelp);
21368d75effSDimitry Andric     }
21468d75effSDimitry Andric     }
21568d75effSDimitry Andric   }
216*0fca6ea1SDimitry Andric   String.push_back('\0');
217*0fca6ea1SDimitry Andric   if (String.back() != '\0') {
218*0fca6ea1SDimitry Andric     // String truncated, make sure the string is terminated properly.
219*0fca6ea1SDimitry Andric     // This can happen if there is no more memory when trying to resize
220*0fca6ea1SDimitry Andric     // the string.
221*0fca6ea1SDimitry Andric     String.back() = '\0';
22268d75effSDimitry Andric   }
22368d75effSDimitry Andric }
22468d75effSDimitry Andric 
22568d75effSDimitry Andric void ScopedString::append(const char *Format, ...) {
22668d75effSDimitry Andric   va_list Args;
22768d75effSDimitry Andric   va_start(Args, Format);
22806c3fb27SDimitry Andric   vappend(Format, Args);
22968d75effSDimitry Andric   va_end(Args);
23068d75effSDimitry Andric }
23168d75effSDimitry Andric 
23268d75effSDimitry Andric void Printf(const char *Format, ...) {
23368d75effSDimitry Andric   va_list Args;
23468d75effSDimitry Andric   va_start(Args, Format);
235fe6060f1SDimitry Andric   ScopedString Msg;
23606c3fb27SDimitry Andric   Msg.vappend(Format, Args);
23768d75effSDimitry Andric   outputRaw(Msg.data());
23868d75effSDimitry Andric   va_end(Args);
23968d75effSDimitry Andric }
24068d75effSDimitry Andric 
24168d75effSDimitry Andric } // namespace scudo
242