123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- //===-- string_utils.cpp ----------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "string_utils.h"
- #include "common.h"
- #include <stdarg.h>
- #include <string.h>
- namespace scudo {
- static int appendChar(char **Buffer, const char *BufferEnd, char C) {
- if (*Buffer < BufferEnd) {
- **Buffer = C;
- (*Buffer)++;
- }
- return 1;
- }
- // Appends number in a given Base to buffer. If its length is less than
- // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
- // on the value of |PadWithZero|.
- static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
- u8 Base, u8 MinNumberLength, bool PadWithZero,
- bool Negative, bool Upper) {
- constexpr uptr MaxLen = 30;
- RAW_CHECK(Base == 10 || Base == 16);
- RAW_CHECK(Base == 10 || !Negative);
- RAW_CHECK(AbsoluteValue || !Negative);
- RAW_CHECK(MinNumberLength < MaxLen);
- int Res = 0;
- if (Negative && MinNumberLength)
- --MinNumberLength;
- if (Negative && PadWithZero)
- Res += appendChar(Buffer, BufferEnd, '-');
- uptr NumBuffer[MaxLen];
- int Pos = 0;
- do {
- RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
- "appendNumber buffer overflow");
- NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
- AbsoluteValue /= Base;
- } while (AbsoluteValue > 0);
- if (Pos < MinNumberLength) {
- memset(&NumBuffer[Pos], 0,
- sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
- Pos = MinNumberLength;
- }
- RAW_CHECK(Pos > 0);
- Pos--;
- for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
- char c = (PadWithZero || Pos == 0) ? '0' : ' ';
- Res += appendChar(Buffer, BufferEnd, c);
- }
- if (Negative && !PadWithZero)
- Res += appendChar(Buffer, BufferEnd, '-');
- for (; Pos >= 0; Pos--) {
- char Digit = static_cast<char>(NumBuffer[Pos]);
- Digit = static_cast<char>((Digit < 10) ? '0' + Digit
- : (Upper ? 'A' : 'a') + Digit - 10);
- Res += appendChar(Buffer, BufferEnd, Digit);
- }
- return Res;
- }
- static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
- u8 Base, u8 MinNumberLength, bool PadWithZero,
- bool Upper) {
- return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
- PadWithZero, /*Negative=*/false, Upper);
- }
- static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
- u8 MinNumberLength, bool PadWithZero) {
- const bool Negative = (Num < 0);
- const u64 UnsignedNum = (Num == INT64_MIN)
- ? static_cast<u64>(INT64_MAX) + 1
- : static_cast<u64>(Negative ? -Num : Num);
- return appendNumber(Buffer, BufferEnd, UnsignedNum, 10, MinNumberLength,
- PadWithZero, Negative, /*Upper=*/false);
- }
- // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
- // interpret Width == 0 as "no Width requested":
- // Width == 0 - no Width requested
- // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
- // Width > 0 - right-justify S, not implemented yet
- static int appendString(char **Buffer, const char *BufferEnd, int Width,
- int MaxChars, const char *S) {
- if (!S)
- S = "<null>";
- int Res = 0;
- for (; *S; S++) {
- if (MaxChars >= 0 && Res >= MaxChars)
- break;
- Res += appendChar(Buffer, BufferEnd, *S);
- }
- // Only the left justified strings are supported.
- while (Width < -Res)
- Res += appendChar(Buffer, BufferEnd, ' ');
- return Res;
- }
- static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
- int Res = 0;
- Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
- Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
- SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
- /*Upper=*/false);
- return Res;
- }
- static int formatString(char *Buffer, uptr BufferLength, const char *Format,
- va_list Args) {
- static const char *PrintfFormatsHelp =
- "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
- "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
- RAW_CHECK(Format);
- RAW_CHECK(BufferLength > 0);
- const char *BufferEnd = &Buffer[BufferLength - 1];
- const char *Cur = Format;
- int Res = 0;
- for (; *Cur; Cur++) {
- if (*Cur != '%') {
- Res += appendChar(&Buffer, BufferEnd, *Cur);
- continue;
- }
- Cur++;
- const bool LeftJustified = *Cur == '-';
- if (LeftJustified)
- Cur++;
- bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
- const bool PadWithZero = (*Cur == '0');
- u8 Width = 0;
- if (HaveWidth) {
- while (*Cur >= '0' && *Cur <= '9')
- Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
- }
- const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
- int Precision = -1;
- if (HavePrecision) {
- Cur += 2;
- Precision = va_arg(Args, int);
- }
- const bool HaveZ = (*Cur == 'z');
- Cur += HaveZ;
- const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
- Cur += HaveLL * 2;
- s64 DVal;
- u64 UVal;
- const bool HaveLength = HaveZ || HaveLL;
- const bool HaveFlags = HaveWidth || HaveLength;
- // At the moment only %s supports precision and left-justification.
- CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
- switch (*Cur) {
- case 'd': {
- DVal = HaveLL ? va_arg(Args, s64)
- : HaveZ ? va_arg(Args, sptr)
- : va_arg(Args, int);
- Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
- break;
- }
- case 'u':
- case 'x':
- case 'X': {
- UVal = HaveLL ? va_arg(Args, u64)
- : HaveZ ? va_arg(Args, uptr)
- : va_arg(Args, unsigned);
- const bool Upper = (*Cur == 'X');
- Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
- Width, PadWithZero, Upper);
- break;
- }
- case 'p': {
- RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
- Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
- break;
- }
- case 's': {
- RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
- // Only left-justified Width is supported.
- CHECK(!HaveWidth || LeftJustified);
- Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
- Precision, va_arg(Args, char *));
- break;
- }
- case 'c': {
- RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
- Res +=
- appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
- break;
- }
- // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
- // However, `-Wformat` doesn't know we have a different parser for those
- // placeholders and it keeps complaining the type mismatch on 64-bit
- // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
- // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
- // `s64`/`u64` and handle the `ld`/`lu` here.
- case 'l': {
- ++Cur;
- RAW_CHECK(*Cur == 'd' || *Cur == 'u');
- if (*Cur == 'd') {
- DVal = va_arg(Args, s64);
- Res +=
- appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
- } else {
- UVal = va_arg(Args, u64);
- Res += appendUnsigned(&Buffer, BufferEnd, UVal, 10, Width, PadWithZero,
- false);
- }
- break;
- }
- case '%': {
- RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
- Res += appendChar(&Buffer, BufferEnd, '%');
- break;
- }
- default: {
- RAW_CHECK_MSG(false, PrintfFormatsHelp);
- }
- }
- }
- RAW_CHECK(Buffer <= BufferEnd);
- appendChar(&Buffer, BufferEnd + 1, '\0');
- return Res;
- }
- int formatString(char *Buffer, uptr BufferLength, const char *Format, ...) {
- va_list Args;
- va_start(Args, Format);
- int Res = formatString(Buffer, BufferLength, Format, Args);
- va_end(Args);
- return Res;
- }
- void ScopedString::vappend(const char *Format, va_list Args) {
- va_list ArgsCopy;
- va_copy(ArgsCopy, Args);
- // formatString doesn't currently support a null buffer or zero buffer length,
- // so in order to get the resulting formatted string length, we use a one-char
- // buffer.
- char C[1];
- const uptr AdditionalLength =
- static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1;
- const uptr Length = length();
- String.resize(Length + AdditionalLength);
- const uptr FormattedLength = static_cast<uptr>(formatString(
- String.data() + Length, String.size() - Length, Format, ArgsCopy));
- RAW_CHECK(data()[length()] == '\0');
- RAW_CHECK(FormattedLength + 1 == AdditionalLength);
- va_end(ArgsCopy);
- }
- void ScopedString::append(const char *Format, ...) {
- va_list Args;
- va_start(Args, Format);
- vappend(Format, Args);
- va_end(Args);
- }
- void Printf(const char *Format, ...) {
- va_list Args;
- va_start(Args, Format);
- ScopedString Msg;
- Msg.vappend(Format, Args);
- outputRaw(Msg.data());
- va_end(Args);
- }
- } // namespace scudo
|