blob: 5de8b57bfcd126f2577c52e2b3c1e9cdd0af3f28 [file] [log] [blame]
Dynamic Tools Team517193e2019-09-11 14:48:41 +00001//===-- 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
15namespace scudo {
16
17static int appendChar(char **Buffer, const char *BufferEnd, char C) {
18 if (*Buffer < BufferEnd) {
19 **Buffer = C;
20 (*Buffer)++;
21 }
22 return 1;
23}
24
25// Appends number in a given Base to buffer. If its length is less than
26// |MinNumberLength|, it is padded with leading zeroes or spaces, depending
27// on the value of |PadWithZero|.
28static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
29 u8 Base, u8 MinNumberLength, bool PadWithZero,
30 bool Negative, bool Upper) {
31 constexpr uptr MaxLen = 30;
32 RAW_CHECK(Base == 10 || Base == 16);
33 RAW_CHECK(Base == 10 || !Negative);
34 RAW_CHECK(AbsoluteValue || !Negative);
35 RAW_CHECK(MinNumberLength < MaxLen);
36 int Res = 0;
37 if (Negative && MinNumberLength)
38 --MinNumberLength;
39 if (Negative && PadWithZero)
40 Res += appendChar(Buffer, BufferEnd, '-');
41 uptr NumBuffer[MaxLen];
42 int Pos = 0;
43 do {
44 RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
45 "appendNumber buffer overflow");
46 NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
47 AbsoluteValue /= Base;
48 } while (AbsoluteValue > 0);
49 if (Pos < MinNumberLength) {
50 memset(&NumBuffer[Pos], 0,
51 sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
52 Pos = MinNumberLength;
53 }
54 RAW_CHECK(Pos > 0);
55 Pos--;
56 for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
57 char c = (PadWithZero || Pos == 0) ? '0' : ' ';
58 Res += appendChar(Buffer, BufferEnd, c);
59 }
60 if (Negative && !PadWithZero)
61 Res += appendChar(Buffer, BufferEnd, '-');
62 for (; Pos >= 0; Pos--) {
63 char Digit = static_cast<char>(NumBuffer[Pos]);
64 Digit = static_cast<char>((Digit < 10) ? '0' + Digit
65 : (Upper ? 'A' : 'a') + Digit - 10);
66 Res += appendChar(Buffer, BufferEnd, Digit);
67 }
68 return Res;
69}
70
71static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
72 u8 Base, u8 MinNumberLength, bool PadWithZero,
73 bool Upper) {
74 return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
75 PadWithZero, /*Negative=*/false, Upper);
76}
77
78static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
79 u8 MinNumberLength, bool PadWithZero) {
80 const bool Negative = (Num < 0);
81 return appendNumber(Buffer, BufferEnd,
82 static_cast<u64>(Negative ? -Num : Num), 10,
83 MinNumberLength, PadWithZero, Negative,
84 /*Upper=*/false);
85}
86
87// Use the fact that explicitly requesting 0 Width (%0s) results in UB and
88// interpret Width == 0 as "no Width requested":
89// Width == 0 - no Width requested
90// Width < 0 - left-justify S within and pad it to -Width chars, if necessary
91// Width > 0 - right-justify S, not implemented yet
92static int appendString(char **Buffer, const char *BufferEnd, int Width,
93 int MaxChars, const char *S) {
94 if (!S)
95 S = "<null>";
96 int Res = 0;
97 for (; *S; S++) {
98 if (MaxChars >= 0 && Res >= MaxChars)
99 break;
100 Res += appendChar(Buffer, BufferEnd, *S);
101 }
102 // Only the left justified strings are supported.
103 while (Width < -Res)
104 Res += appendChar(Buffer, BufferEnd, ' ');
105 return Res;
106}
107
108static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
109 int Res = 0;
110 Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
111 Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
112 SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
113 /*Upper=*/false);
114 return Res;
115}
116
117int formatString(char *Buffer, uptr BufferLength, const char *Format,
118 va_list Args) {
119 static const char *PrintfFormatsHelp =
120 "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
121 "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
122 RAW_CHECK(Format);
123 RAW_CHECK(BufferLength > 0);
124 const char *BufferEnd = &Buffer[BufferLength - 1];
125 const char *Cur = Format;
126 int Res = 0;
127 for (; *Cur; Cur++) {
128 if (*Cur != '%') {
129 Res += appendChar(&Buffer, BufferEnd, *Cur);
130 continue;
131 }
132 Cur++;
133 const bool LeftJustified = *Cur == '-';
134 if (LeftJustified)
135 Cur++;
136 bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
137 const bool PadWithZero = (*Cur == '0');
138 u8 Width = 0;
139 if (HaveWidth) {
140 while (*Cur >= '0' && *Cur <= '9')
141 Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
142 }
143 const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
144 int Precision = -1;
145 if (HavePrecision) {
146 Cur += 2;
147 Precision = va_arg(Args, int);
148 }
149 const bool HaveZ = (*Cur == 'z');
150 Cur += HaveZ;
151 const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
152 Cur += HaveLL * 2;
153 s64 DVal;
154 u64 UVal;
155 const bool HaveLength = HaveZ || HaveLL;
156 const bool HaveFlags = HaveWidth || HaveLength;
157 // At the moment only %s supports precision and left-justification.
158 CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
159 switch (*Cur) {
160 case 'd': {
161 DVal = HaveLL ? va_arg(Args, s64)
162 : HaveZ ? va_arg(Args, sptr) : va_arg(Args, int);
163 Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
164 break;
165 }
166 case 'u':
167 case 'x':
168 case 'X': {
169 UVal = HaveLL ? va_arg(Args, u64)
170 : HaveZ ? va_arg(Args, uptr) : va_arg(Args, unsigned);
171 const bool Upper = (*Cur == 'X');
172 Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
173 Width, PadWithZero, Upper);
174 break;
175 }
176 case 'p': {
177 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
178 Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
179 break;
180 }
181 case 's': {
182 RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
183 // Only left-justified Width is supported.
184 CHECK(!HaveWidth || LeftJustified);
185 Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
186 Precision, va_arg(Args, char *));
187 break;
188 }
189 case 'c': {
190 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
191 Res +=
192 appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
193 break;
194 }
195 case '%': {
196 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
197 Res += appendChar(&Buffer, BufferEnd, '%');
198 break;
199 }
200 default: {
201 RAW_CHECK_MSG(false, PrintfFormatsHelp);
202 }
203 }
204 }
205 RAW_CHECK(Buffer <= BufferEnd);
206 appendChar(&Buffer, BufferEnd + 1, '\0');
207 return Res;
208}
209
210void ScopedString::append(const char *Format, va_list Args) {
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000211 DCHECK_LT(Length, String.size());
212 va_list ArgsCopy;
213 va_copy(ArgsCopy, Args);
214 // formatString doesn't currently support a null buffer or zero buffer length,
215 // so in order to get the resulting formatted string length, we use a one-char
216 // buffer.
217 char C[1];
218 const uptr AdditionalLength =
219 static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1;
220 String.resize(Length + AdditionalLength);
221 formatString(String.data() + Length, AdditionalLength, Format, ArgsCopy);
222 Length = strlen(String.data());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000223 CHECK_LT(Length, String.size());
224}
225
226FORMAT(2, 3)
227void ScopedString::append(const char *Format, ...) {
228 va_list Args;
229 va_start(Args, Format);
230 append(Format, Args);
231 va_end(Args);
232}
233
234FORMAT(1, 2)
235void Printf(const char *Format, ...) {
236 va_list Args;
237 va_start(Args, Format);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000238 ScopedString Msg(1024);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000239 Msg.append(Format, Args);
240 outputRaw(Msg.data());
241 va_end(Args);
242}
243
244} // namespace scudo