242 lines · cpp
1//===-- 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-exception6//7//===----------------------------------------------------------------------===//8 9#include "string_utils.h"10#include "common.h"11 12#include <stdarg.h>13#include <string.h>14 15namespace scudo {16 17// Appends number in a given Base to buffer. If its length is less than18// |MinNumberLength|, it is padded with leading zeroes or spaces, depending19// on the value of |PadWithZero|.20void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,21 bool PadWithZero, bool Negative, bool Upper) {22 constexpr uptr MaxLen = 30;23 RAW_CHECK(Base == 10 || Base == 16);24 RAW_CHECK(Base == 10 || !Negative);25 RAW_CHECK(AbsoluteValue || !Negative);26 RAW_CHECK(MinNumberLength < MaxLen);27 if (Negative && MinNumberLength)28 --MinNumberLength;29 if (Negative && PadWithZero) {30 String.push_back('-');31 }32 uptr NumBuffer[MaxLen];33 int Pos = 0;34 do {35 RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,36 "appendNumber buffer overflow");37 NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);38 AbsoluteValue /= Base;39 } while (AbsoluteValue > 0);40 if (Pos < MinNumberLength) {41 memset(&NumBuffer[Pos], 0,42 sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));43 Pos = MinNumberLength;44 }45 RAW_CHECK(Pos > 0);46 Pos--;47 for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {48 char c = (PadWithZero || Pos == 0) ? '0' : ' ';49 String.push_back(c);50 }51 if (Negative && !PadWithZero)52 String.push_back('-');53 for (; Pos >= 0; Pos--) {54 char Digit = static_cast<char>(NumBuffer[Pos]);55 Digit = static_cast<char>((Digit < 10) ? '0' + Digit56 : (Upper ? 'A' : 'a') + Digit - 10);57 String.push_back(Digit);58 }59}60 61void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength,62 bool PadWithZero, bool Upper) {63 appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false,64 Upper);65}66 67void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength,68 bool PadWithZero) {69 const bool Negative = (Num < 0);70 const u64 UnsignedNum = (Num == INT64_MIN)71 ? static_cast<u64>(INT64_MAX) + 172 : static_cast<u64>(Negative ? -Num : Num);73 appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative,74 /*Upper=*/false);75}76 77// Use the fact that explicitly requesting 0 Width (%0s) results in UB and78// interpret Width == 0 as "no Width requested":79// Width == 0 - no Width requested80// Width < 0 - left-justify S within and pad it to -Width chars, if necessary81// Width > 0 - right-justify S, not implemented yet82void ScopedString::appendString(int Width, int MaxChars, const char *S) {83 if (!S)84 S = "<null>";85 int NumChars = 0;86 for (; *S; S++) {87 if (MaxChars >= 0 && NumChars >= MaxChars)88 break;89 String.push_back(*S);90 NumChars++;91 }92 if (Width < 0) {93 // Only left justification supported.94 Width = -Width - NumChars;95 while (Width-- > 0)96 String.push_back(' ');97 }98}99 100void ScopedString::appendPointer(u64 ptr_value) {101 appendString(0, -1, "0x");102 appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH,103 /*PadWithZero=*/true,104 /*Upper=*/false);105}106 107void ScopedString::vappend(const char *Format, va_list &Args) {108 // Since the string contains the '\0' terminator, put our size before it109 // so that push_back calls work correctly.110 DCHECK(String.size() > 0);111 String.resize(String.size() - 1);112 113 static const char *PrintfFormatsHelp =114 "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "115 "%[-]([0-9]*)?(\\.\\*)?s; %c\n";116 RAW_CHECK(Format);117 const char *Cur = Format;118 for (; *Cur; Cur++) {119 if (*Cur != '%') {120 String.push_back(*Cur);121 continue;122 }123 Cur++;124 const bool LeftJustified = *Cur == '-';125 if (LeftJustified)126 Cur++;127 bool HaveWidth = (*Cur >= '0' && *Cur <= '9');128 const bool PadWithZero = (*Cur == '0');129 u8 Width = 0;130 if (HaveWidth) {131 while (*Cur >= '0' && *Cur <= '9')132 Width = static_cast<u8>(Width * 10 + *Cur++ - '0');133 }134 const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');135 int Precision = -1;136 if (HavePrecision) {137 Cur += 2;138 Precision = va_arg(Args, int);139 }140 const bool HaveZ = (*Cur == 'z');141 Cur += HaveZ;142 const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');143 Cur += HaveLL * 2;144 s64 DVal;145 u64 UVal;146 const bool HaveLength = HaveZ || HaveLL;147 const bool HaveFlags = HaveWidth || HaveLength;148 // At the moment only %s supports precision and left-justification.149 CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));150 switch (*Cur) {151 case 'd': {152 DVal = HaveLL ? va_arg(Args, s64)153 : HaveZ ? va_arg(Args, sptr)154 : va_arg(Args, int);155 appendSignedDecimal(DVal, Width, PadWithZero);156 break;157 }158 case 'u':159 case 'x':160 case 'X': {161 UVal = HaveLL ? va_arg(Args, u64)162 : HaveZ ? va_arg(Args, uptr)163 : va_arg(Args, unsigned);164 const bool Upper = (*Cur == 'X');165 appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper);166 break;167 }168 case 'p': {169 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);170 appendPointer(va_arg(Args, uptr));171 break;172 }173 case 's': {174 RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);175 // Only left-justified Width is supported.176 CHECK(!HaveWidth || LeftJustified);177 appendString(LeftJustified ? -Width : Width, Precision,178 va_arg(Args, char *));179 break;180 }181 case 'c': {182 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);183 String.push_back(static_cast<char>(va_arg(Args, int)));184 break;185 }186 // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.187 // However, `-Wformat` doesn't know we have a different parser for those188 // placeholders and it keeps complaining the type mismatch on 64-bit189 // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to190 // silence the warning, we turn to use `PRId64`/`PRIu64` for printing191 // `s64`/`u64` and handle the `ld`/`lu` here.192 case 'l': {193 ++Cur;194 RAW_CHECK(*Cur == 'd' || *Cur == 'u');195 196 if (*Cur == 'd') {197 DVal = va_arg(Args, s64);198 appendSignedDecimal(DVal, Width, PadWithZero);199 } else {200 UVal = va_arg(Args, u64);201 appendUnsigned(UVal, 10, Width, PadWithZero, false);202 }203 204 break;205 }206 case '%': {207 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);208 String.push_back('%');209 break;210 }211 default: {212 RAW_CHECK_MSG(false, PrintfFormatsHelp);213 }214 }215 }216 String.push_back('\0');217 if (String.back() != '\0') {218 // String truncated, make sure the string is terminated properly.219 // This can happen if there is no more memory when trying to resize220 // the string.221 String.back() = '\0';222 }223}224 225void ScopedString::append(const char *Format, ...) {226 va_list Args;227 va_start(Args, Format);228 vappend(Format, Args);229 va_end(Args);230}231 232void Printf(const char *Format, ...) {233 va_list Args;234 va_start(Args, Format);235 ScopedString Msg;236 Msg.vappend(Format, Args);237 outputRaw(Msg.data());238 va_end(Args);239}240 241} // namespace scudo242