xref: /openbsd-src/gnu/llvm/compiler-rt/lib/scudo/standalone/string_utils.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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