1 //===-- string_utils.cpp ----------------------------------------*- 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"
17 static int appendChar(char **Buffer
, const char *BufferEnd
, char C
) {
18 if (*Buffer
< BufferEnd
) {
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|.
28 static 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
);
37 if (Negative
&& MinNumberLength
)
39 if (Negative
&& PadWithZero
)
40 Res
+= appendChar(Buffer
, BufferEnd
, '-');
41 uptr NumBuffer
[MaxLen
];
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
;
56 for (; Pos
>= 0 && NumBuffer
[Pos
] == 0; Pos
--) {
57 char c
= (PadWithZero
|| Pos
== 0) ? '0' : ' ';
58 Res
+= appendChar(Buffer
, BufferEnd
, c
);
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
);
71 static int appendUnsigned(char **Buffer
, const char *BufferEnd
, u64 Num
,
72 u8 Base
, u8 MinNumberLength
, bool PadWithZero
,
74 return appendNumber(Buffer
, BufferEnd
, Num
, Base
, MinNumberLength
,
75 PadWithZero
, /*Negative=*/false, Upper
);
78 static int appendSignedDecimal(char **Buffer
, const char *BufferEnd
, s64 Num
,
79 u8 MinNumberLength
, bool PadWithZero
) {
80 const bool Negative
= (Num
< 0);
81 const u64 UnsignedNum
= (Num
== INT64_MIN
)
82 ? static_cast<u64
>(INT64_MAX
) + 1
83 : static_cast<u64
>(Negative
? -Num
: Num
);
84 return appendNumber(Buffer
, BufferEnd
, UnsignedNum
, 10, MinNumberLength
,
85 PadWithZero
, Negative
, /*Upper=*/false);
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 static int formatString(char *Buffer
, uptr BufferLength
, const char *Format
,
120 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
)
165 Res
+= appendSignedDecimal(&Buffer
, BufferEnd
, DVal
, Width
, PadWithZero
);
171 UVal
= HaveLL
? va_arg(Args
, u64
)
172 : HaveZ
? va_arg(Args
, uptr
)
173 : va_arg(Args
, unsigned);
174 const bool Upper
= (*Cur
== 'X');
175 Res
+= appendUnsigned(&Buffer
, BufferEnd
, UVal
, (*Cur
== 'u') ? 10 : 16,
176 Width
, PadWithZero
, Upper
);
180 RAW_CHECK_MSG(!HaveFlags
, PrintfFormatsHelp
);
181 Res
+= appendPointer(&Buffer
, BufferEnd
, va_arg(Args
, uptr
));
185 RAW_CHECK_MSG(!HaveLength
, PrintfFormatsHelp
);
186 // Only left-justified Width is supported.
187 CHECK(!HaveWidth
|| LeftJustified
);
188 Res
+= appendString(&Buffer
, BufferEnd
, LeftJustified
? -Width
: Width
,
189 Precision
, va_arg(Args
, char *));
193 RAW_CHECK_MSG(!HaveFlags
, PrintfFormatsHelp
);
195 appendChar(&Buffer
, BufferEnd
, static_cast<char>(va_arg(Args
, int)));
198 // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
199 // However, `-Wformat` doesn't know we have a different parser for those
200 // placeholders and it keeps complaining the type mismatch on 64-bit
201 // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
202 // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
203 // `s64`/`u64` and handle the `ld`/`lu` here.
206 RAW_CHECK(*Cur
== 'd' || *Cur
== 'u');
209 DVal
= va_arg(Args
, s64
);
211 appendSignedDecimal(&Buffer
, BufferEnd
, DVal
, Width
, PadWithZero
);
213 UVal
= va_arg(Args
, u64
);
214 Res
+= appendUnsigned(&Buffer
, BufferEnd
, UVal
, 10, Width
, PadWithZero
,
221 RAW_CHECK_MSG(!HaveFlags
, PrintfFormatsHelp
);
222 Res
+= appendChar(&Buffer
, BufferEnd
, '%');
226 RAW_CHECK_MSG(false, PrintfFormatsHelp
);
230 RAW_CHECK(Buffer
<= BufferEnd
);
231 appendChar(&Buffer
, BufferEnd
+ 1, '\0');
235 int formatString(char *Buffer
, uptr BufferLength
, const char *Format
, ...) {
237 va_start(Args
, Format
);
238 int Res
= formatString(Buffer
, BufferLength
, Format
, Args
);
243 void ScopedString::vappend(const char *Format
, va_list Args
) {
245 va_copy(ArgsCopy
, Args
);
246 // formatString doesn't currently support a null buffer or zero buffer length,
247 // so in order to get the resulting formatted string length, we use a one-char
250 const uptr AdditionalLength
=
251 static_cast<uptr
>(formatString(C
, sizeof(C
), Format
, Args
)) + 1;
252 const uptr Length
= length();
253 String
.resize(Length
+ AdditionalLength
);
254 const uptr FormattedLength
= static_cast<uptr
>(formatString(
255 String
.data() + Length
, String
.size() - Length
, Format
, ArgsCopy
));
256 RAW_CHECK(data()[length()] == '\0');
257 RAW_CHECK(FormattedLength
+ 1 == AdditionalLength
);
261 void ScopedString::append(const char *Format
, ...) {
263 va_start(Args
, Format
);
264 vappend(Format
, Args
);
268 void Printf(const char *Format
, ...) {
270 va_start(Args
, Format
);
272 Msg
.vappend(Format
, Args
);
273 outputRaw(Msg
.data());