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|.
appendNumber(u64 AbsoluteValue,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Negative,bool Upper)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
appendUnsigned(u64 Num,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Upper)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
appendSignedDecimal(s64 Num,u8 MinNumberLength,bool PadWithZero)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
appendString(int Width,int MaxChars,const char * S)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
appendPointer(u64 ptr_value)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
vappend(const char * Format,va_list & Args)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
append(const char * Format,...)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
Printf(const char * Format,...)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