13cab2bb3Spatrick //===-- string_utils.cpp ----------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick
93cab2bb3Spatrick #include "string_utils.h"
103cab2bb3Spatrick #include "common.h"
113cab2bb3Spatrick
123cab2bb3Spatrick #include <stdarg.h>
133cab2bb3Spatrick #include <string.h>
143cab2bb3Spatrick
153cab2bb3Spatrick namespace scudo {
163cab2bb3Spatrick
appendChar(char ** Buffer,const char * BufferEnd,char C)173cab2bb3Spatrick static int appendChar(char **Buffer, const char *BufferEnd, char C) {
183cab2bb3Spatrick if (*Buffer < BufferEnd) {
193cab2bb3Spatrick **Buffer = C;
203cab2bb3Spatrick (*Buffer)++;
213cab2bb3Spatrick }
223cab2bb3Spatrick return 1;
233cab2bb3Spatrick }
243cab2bb3Spatrick
253cab2bb3Spatrick // Appends number in a given Base to buffer. If its length is less than
263cab2bb3Spatrick // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
273cab2bb3Spatrick // on the value of |PadWithZero|.
appendNumber(char ** Buffer,const char * BufferEnd,u64 AbsoluteValue,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Negative,bool Upper)283cab2bb3Spatrick static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
293cab2bb3Spatrick u8 Base, u8 MinNumberLength, bool PadWithZero,
303cab2bb3Spatrick bool Negative, bool Upper) {
313cab2bb3Spatrick constexpr uptr MaxLen = 30;
323cab2bb3Spatrick RAW_CHECK(Base == 10 || Base == 16);
333cab2bb3Spatrick RAW_CHECK(Base == 10 || !Negative);
343cab2bb3Spatrick RAW_CHECK(AbsoluteValue || !Negative);
353cab2bb3Spatrick RAW_CHECK(MinNumberLength < MaxLen);
363cab2bb3Spatrick int Res = 0;
373cab2bb3Spatrick if (Negative && MinNumberLength)
383cab2bb3Spatrick --MinNumberLength;
393cab2bb3Spatrick if (Negative && PadWithZero)
403cab2bb3Spatrick Res += appendChar(Buffer, BufferEnd, '-');
413cab2bb3Spatrick uptr NumBuffer[MaxLen];
423cab2bb3Spatrick int Pos = 0;
433cab2bb3Spatrick do {
443cab2bb3Spatrick RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
453cab2bb3Spatrick "appendNumber buffer overflow");
463cab2bb3Spatrick NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
473cab2bb3Spatrick AbsoluteValue /= Base;
483cab2bb3Spatrick } while (AbsoluteValue > 0);
493cab2bb3Spatrick if (Pos < MinNumberLength) {
503cab2bb3Spatrick memset(&NumBuffer[Pos], 0,
513cab2bb3Spatrick sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
523cab2bb3Spatrick Pos = MinNumberLength;
533cab2bb3Spatrick }
543cab2bb3Spatrick RAW_CHECK(Pos > 0);
553cab2bb3Spatrick Pos--;
563cab2bb3Spatrick for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
573cab2bb3Spatrick char c = (PadWithZero || Pos == 0) ? '0' : ' ';
583cab2bb3Spatrick Res += appendChar(Buffer, BufferEnd, c);
593cab2bb3Spatrick }
603cab2bb3Spatrick if (Negative && !PadWithZero)
613cab2bb3Spatrick Res += appendChar(Buffer, BufferEnd, '-');
623cab2bb3Spatrick for (; Pos >= 0; Pos--) {
633cab2bb3Spatrick char Digit = static_cast<char>(NumBuffer[Pos]);
643cab2bb3Spatrick Digit = static_cast<char>((Digit < 10) ? '0' + Digit
653cab2bb3Spatrick : (Upper ? 'A' : 'a') + Digit - 10);
663cab2bb3Spatrick Res += appendChar(Buffer, BufferEnd, Digit);
673cab2bb3Spatrick }
683cab2bb3Spatrick return Res;
693cab2bb3Spatrick }
703cab2bb3Spatrick
appendUnsigned(char ** Buffer,const char * BufferEnd,u64 Num,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Upper)713cab2bb3Spatrick static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
723cab2bb3Spatrick u8 Base, u8 MinNumberLength, bool PadWithZero,
733cab2bb3Spatrick bool Upper) {
743cab2bb3Spatrick return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
753cab2bb3Spatrick PadWithZero, /*Negative=*/false, Upper);
763cab2bb3Spatrick }
773cab2bb3Spatrick
appendSignedDecimal(char ** Buffer,const char * BufferEnd,s64 Num,u8 MinNumberLength,bool PadWithZero)783cab2bb3Spatrick static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
793cab2bb3Spatrick u8 MinNumberLength, bool PadWithZero) {
803cab2bb3Spatrick const bool Negative = (Num < 0);
81*d89ec533Spatrick const u64 UnsignedNum = (Num == INT64_MIN)
82*d89ec533Spatrick ? static_cast<u64>(INT64_MAX) + 1
83*d89ec533Spatrick : static_cast<u64>(Negative ? -Num : Num);
84*d89ec533Spatrick return appendNumber(Buffer, BufferEnd, UnsignedNum, 10, MinNumberLength,
85*d89ec533Spatrick PadWithZero, Negative, /*Upper=*/false);
863cab2bb3Spatrick }
873cab2bb3Spatrick
883cab2bb3Spatrick // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
893cab2bb3Spatrick // interpret Width == 0 as "no Width requested":
903cab2bb3Spatrick // Width == 0 - no Width requested
913cab2bb3Spatrick // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
923cab2bb3Spatrick // Width > 0 - right-justify S, not implemented yet
appendString(char ** Buffer,const char * BufferEnd,int Width,int MaxChars,const char * S)933cab2bb3Spatrick static int appendString(char **Buffer, const char *BufferEnd, int Width,
943cab2bb3Spatrick int MaxChars, const char *S) {
953cab2bb3Spatrick if (!S)
963cab2bb3Spatrick S = "<null>";
973cab2bb3Spatrick int Res = 0;
983cab2bb3Spatrick for (; *S; S++) {
993cab2bb3Spatrick if (MaxChars >= 0 && Res >= MaxChars)
1003cab2bb3Spatrick break;
1013cab2bb3Spatrick Res += appendChar(Buffer, BufferEnd, *S);
1023cab2bb3Spatrick }
1033cab2bb3Spatrick // Only the left justified strings are supported.
1043cab2bb3Spatrick while (Width < -Res)
1053cab2bb3Spatrick Res += appendChar(Buffer, BufferEnd, ' ');
1063cab2bb3Spatrick return Res;
1073cab2bb3Spatrick }
1083cab2bb3Spatrick
appendPointer(char ** Buffer,const char * BufferEnd,u64 ptr_value)1093cab2bb3Spatrick static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
1103cab2bb3Spatrick int Res = 0;
1113cab2bb3Spatrick Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
1123cab2bb3Spatrick Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
1133cab2bb3Spatrick SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
1143cab2bb3Spatrick /*Upper=*/false);
1153cab2bb3Spatrick return Res;
1163cab2bb3Spatrick }
1173cab2bb3Spatrick
formatString(char * Buffer,uptr BufferLength,const char * Format,va_list Args)118*d89ec533Spatrick static int formatString(char *Buffer, uptr BufferLength, const char *Format,
1193cab2bb3Spatrick va_list Args) {
1203cab2bb3Spatrick static const char *PrintfFormatsHelp =
1213cab2bb3Spatrick "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
1223cab2bb3Spatrick "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
1233cab2bb3Spatrick RAW_CHECK(Format);
1243cab2bb3Spatrick RAW_CHECK(BufferLength > 0);
1253cab2bb3Spatrick const char *BufferEnd = &Buffer[BufferLength - 1];
1263cab2bb3Spatrick const char *Cur = Format;
1273cab2bb3Spatrick int Res = 0;
1283cab2bb3Spatrick for (; *Cur; Cur++) {
1293cab2bb3Spatrick if (*Cur != '%') {
1303cab2bb3Spatrick Res += appendChar(&Buffer, BufferEnd, *Cur);
1313cab2bb3Spatrick continue;
1323cab2bb3Spatrick }
1333cab2bb3Spatrick Cur++;
1343cab2bb3Spatrick const bool LeftJustified = *Cur == '-';
1353cab2bb3Spatrick if (LeftJustified)
1363cab2bb3Spatrick Cur++;
1373cab2bb3Spatrick bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
1383cab2bb3Spatrick const bool PadWithZero = (*Cur == '0');
1393cab2bb3Spatrick u8 Width = 0;
1403cab2bb3Spatrick if (HaveWidth) {
1413cab2bb3Spatrick while (*Cur >= '0' && *Cur <= '9')
1423cab2bb3Spatrick Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
1433cab2bb3Spatrick }
1443cab2bb3Spatrick const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
1453cab2bb3Spatrick int Precision = -1;
1463cab2bb3Spatrick if (HavePrecision) {
1473cab2bb3Spatrick Cur += 2;
1483cab2bb3Spatrick Precision = va_arg(Args, int);
1493cab2bb3Spatrick }
1503cab2bb3Spatrick const bool HaveZ = (*Cur == 'z');
1513cab2bb3Spatrick Cur += HaveZ;
1523cab2bb3Spatrick const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
1533cab2bb3Spatrick Cur += HaveLL * 2;
1543cab2bb3Spatrick s64 DVal;
1553cab2bb3Spatrick u64 UVal;
1563cab2bb3Spatrick const bool HaveLength = HaveZ || HaveLL;
1573cab2bb3Spatrick const bool HaveFlags = HaveWidth || HaveLength;
1583cab2bb3Spatrick // At the moment only %s supports precision and left-justification.
1593cab2bb3Spatrick CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
1603cab2bb3Spatrick switch (*Cur) {
1613cab2bb3Spatrick case 'd': {
1623cab2bb3Spatrick DVal = HaveLL ? va_arg(Args, s64)
163*d89ec533Spatrick : HaveZ ? va_arg(Args, sptr)
164*d89ec533Spatrick : va_arg(Args, int);
1653cab2bb3Spatrick Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
1663cab2bb3Spatrick break;
1673cab2bb3Spatrick }
1683cab2bb3Spatrick case 'u':
1693cab2bb3Spatrick case 'x':
1703cab2bb3Spatrick case 'X': {
1713cab2bb3Spatrick UVal = HaveLL ? va_arg(Args, u64)
172*d89ec533Spatrick : HaveZ ? va_arg(Args, uptr)
173*d89ec533Spatrick : va_arg(Args, unsigned);
1743cab2bb3Spatrick const bool Upper = (*Cur == 'X');
1753cab2bb3Spatrick Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
1763cab2bb3Spatrick Width, PadWithZero, Upper);
1773cab2bb3Spatrick break;
1783cab2bb3Spatrick }
1793cab2bb3Spatrick case 'p': {
1803cab2bb3Spatrick RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
1813cab2bb3Spatrick Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
1823cab2bb3Spatrick break;
1833cab2bb3Spatrick }
1843cab2bb3Spatrick case 's': {
1853cab2bb3Spatrick RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
1863cab2bb3Spatrick // Only left-justified Width is supported.
1873cab2bb3Spatrick CHECK(!HaveWidth || LeftJustified);
1883cab2bb3Spatrick Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
1893cab2bb3Spatrick Precision, va_arg(Args, char *));
1903cab2bb3Spatrick break;
1913cab2bb3Spatrick }
1923cab2bb3Spatrick case 'c': {
1933cab2bb3Spatrick RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
1943cab2bb3Spatrick Res +=
1953cab2bb3Spatrick appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
1963cab2bb3Spatrick break;
1973cab2bb3Spatrick }
1983cab2bb3Spatrick case '%': {
1993cab2bb3Spatrick RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
2003cab2bb3Spatrick Res += appendChar(&Buffer, BufferEnd, '%');
2013cab2bb3Spatrick break;
2023cab2bb3Spatrick }
2033cab2bb3Spatrick default: {
2043cab2bb3Spatrick RAW_CHECK_MSG(false, PrintfFormatsHelp);
2053cab2bb3Spatrick }
2063cab2bb3Spatrick }
2073cab2bb3Spatrick }
2083cab2bb3Spatrick RAW_CHECK(Buffer <= BufferEnd);
2093cab2bb3Spatrick appendChar(&Buffer, BufferEnd + 1, '\0');
2103cab2bb3Spatrick return Res;
2113cab2bb3Spatrick }
2123cab2bb3Spatrick
formatString(char * Buffer,uptr BufferLength,const char * Format,...)213*d89ec533Spatrick int formatString(char *Buffer, uptr BufferLength, const char *Format, ...) {
214*d89ec533Spatrick va_list Args;
215*d89ec533Spatrick va_start(Args, Format);
216*d89ec533Spatrick int Res = formatString(Buffer, BufferLength, Format, Args);
217*d89ec533Spatrick va_end(Args);
218*d89ec533Spatrick return Res;
219*d89ec533Spatrick }
220*d89ec533Spatrick
append(const char * Format,va_list Args)2213cab2bb3Spatrick void ScopedString::append(const char *Format, va_list Args) {
2223cab2bb3Spatrick va_list ArgsCopy;
2233cab2bb3Spatrick va_copy(ArgsCopy, Args);
2243cab2bb3Spatrick // formatString doesn't currently support a null buffer or zero buffer length,
2253cab2bb3Spatrick // so in order to get the resulting formatted string length, we use a one-char
2263cab2bb3Spatrick // buffer.
2273cab2bb3Spatrick char C[1];
2283cab2bb3Spatrick const uptr AdditionalLength =
2293cab2bb3Spatrick static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1;
230*d89ec533Spatrick const uptr Length = length();
2313cab2bb3Spatrick String.resize(Length + AdditionalLength);
232*d89ec533Spatrick const uptr FormattedLength = static_cast<uptr>(formatString(
233*d89ec533Spatrick String.data() + Length, String.size() - Length, Format, ArgsCopy));
234*d89ec533Spatrick RAW_CHECK(data()[length()] == '\0');
235*d89ec533Spatrick RAW_CHECK(FormattedLength + 1 == AdditionalLength);
236*d89ec533Spatrick va_end(ArgsCopy);
2373cab2bb3Spatrick }
2383cab2bb3Spatrick
append(const char * Format,...)2393cab2bb3Spatrick void ScopedString::append(const char *Format, ...) {
2403cab2bb3Spatrick va_list Args;
2413cab2bb3Spatrick va_start(Args, Format);
2423cab2bb3Spatrick append(Format, Args);
2433cab2bb3Spatrick va_end(Args);
2443cab2bb3Spatrick }
2453cab2bb3Spatrick
Printf(const char * Format,...)2463cab2bb3Spatrick void Printf(const char *Format, ...) {
2473cab2bb3Spatrick va_list Args;
2483cab2bb3Spatrick va_start(Args, Format);
249*d89ec533Spatrick ScopedString Msg;
2503cab2bb3Spatrick Msg.append(Format, Args);
2513cab2bb3Spatrick outputRaw(Msg.data());
2523cab2bb3Spatrick va_end(Args);
2533cab2bb3Spatrick }
2543cab2bb3Spatrick
2553cab2bb3Spatrick } // namespace scudo
256