1 //===-- string_utils.cc -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "string_utils.h"
18 static int appendChar(char **Buffer, const char *BufferEnd, char C) {
19 if (*Buffer < BufferEnd) {
26 // Appends number in a given Base to buffer. If its length is less than
27 // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
28 // on the value of |PadWithZero|.
29 static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
30 u8 Base, u8 MinNumberLength, bool PadWithZero,
31 bool Negative, bool Upper) {
32 constexpr uptr MaxLen = 30;
33 RAW_CHECK(Base == 10 || Base == 16);
34 RAW_CHECK(Base == 10 || !Negative);
35 RAW_CHECK(AbsoluteValue || !Negative);
36 RAW_CHECK(MinNumberLength < MaxLen);
38 if (Negative && MinNumberLength)
40 if (Negative && PadWithZero)
41 Res += appendChar(Buffer, BufferEnd, '-');
42 uptr NumBuffer[MaxLen];
45 RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
46 "appendNumber buffer overflow");
47 NumBuffer[Pos++] = AbsoluteValue % Base;
48 AbsoluteValue /= Base;
49 } while (AbsoluteValue > 0);
50 if (Pos < MinNumberLength) {
51 memset(&NumBuffer[Pos], 0,
52 sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
53 Pos = MinNumberLength;
57 for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
58 char c = (PadWithZero || Pos == 0) ? '0' : ' ';
59 Res += appendChar(Buffer, BufferEnd, c);
61 if (Negative && !PadWithZero)
62 Res += appendChar(Buffer, BufferEnd, '-');
63 for (; Pos >= 0; Pos--) {
64 char Digit = static_cast<char>(NumBuffer[Pos]);
65 Digit = static_cast<char>((Digit < 10) ? '0' + Digit
66 : (Upper ? 'A' : 'a') + Digit - 10);
67 Res += appendChar(Buffer, BufferEnd, Digit);
72 static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
73 u8 Base, u8 MinNumberLength, bool PadWithZero,
75 return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
76 PadWithZero, /*Negative=*/false, Upper);
79 static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
80 u8 MinNumberLength, bool PadWithZero) {
81 const bool Negative = (Num < 0);
82 return appendNumber(Buffer, BufferEnd,
83 static_cast<u64>(Negative ? -Num : Num), 10,
84 MinNumberLength, PadWithZero, Negative,
88 // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
89 // interpret Width == 0 as "no Width requested":
90 // Width == 0 - no Width requested
91 // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
92 // Width > 0 - right-justify S, not implemented yet
93 static int appendString(char **Buffer, const char *BufferEnd, int Width,
94 int MaxChars, const char *S) {
99 if (MaxChars >= 0 && Res >= MaxChars)
101 Res += appendChar(Buffer, BufferEnd, *S);
103 // Only the left justified strings are supported.
105 Res += appendChar(Buffer, BufferEnd, ' ');
109 static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
111 Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
112 Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
113 SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
118 int formatString(char *Buffer, uptr BufferLength, const char *Format,
120 UNUSED static const char *PrintfFormatsHelp =
121 "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
122 "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
124 RAW_CHECK(BufferLength > 0);
125 const char *BufferEnd = &Buffer[BufferLength - 1];
126 const char *Cur = Format;
128 for (; *Cur; Cur++) {
130 Res += appendChar(&Buffer, BufferEnd, *Cur);
134 const bool LeftJustified = *Cur == '-';
137 bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
138 const bool PadWithZero = (*Cur == '0');
141 while (*Cur >= '0' && *Cur <= '9')
142 Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
144 const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
148 Precision = va_arg(Args, int);
150 const bool HaveZ = (*Cur == 'z');
152 const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
156 const bool HaveLength = HaveZ || HaveLL;
157 const bool HaveFlags = HaveWidth || HaveLength;
158 // At the moment only %s supports precision and left-justification.
159 CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
162 DVal = HaveLL ? va_arg(Args, s64)
163 : HaveZ ? va_arg(Args, sptr) : va_arg(Args, int);
164 Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
170 UVal = HaveLL ? va_arg(Args, u64)
171 : HaveZ ? va_arg(Args, uptr) : va_arg(Args, unsigned);
172 const bool Upper = (*Cur == 'X');
173 Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
174 Width, PadWithZero, Upper);
178 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
179 Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
183 RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
184 // Only left-justified Width is supported.
185 CHECK(!HaveWidth || LeftJustified);
186 Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
187 Precision, va_arg(Args, char *));
191 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
193 appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
197 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
198 Res += appendChar(&Buffer, BufferEnd, '%');
202 RAW_CHECK_MSG(false, PrintfFormatsHelp);
206 RAW_CHECK(Buffer <= BufferEnd);
207 appendChar(&Buffer, BufferEnd + 1, '\0');
211 void ScopedString::append(const char *Format, va_list Args) {
212 CHECK_LT(Length, String.size());
213 formatString(String.data() + Length, String.size() - Length, Format, Args);
214 Length += strlen(String.data() + Length);
215 CHECK_LT(Length, String.size());
219 void ScopedString::append(const char *Format, ...) {
221 va_start(Args, Format);
222 append(Format, Args);
227 void Printf(const char *Format, ...) {
229 va_start(Args, Format);
230 ScopedString Msg(512);
231 Msg.append(Format, Args);
232 outputRaw(Msg.data());