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