Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[scudo][standalone] Add string utility functions
Summary: Add some string utility functions, notably to format strings, get lengths, convert a string to a number. Those functions will be used in reports and flags (coming up next). They were mostly borrowed from sanitizer_common. Make use of the string length function in a couple places in the platform code that was checked in with inlined version of it. Add some tests. Reviewers: morehouse, eugenis, vitalybuka, hctim Reviewed By: morehouse, vitalybuka Subscribers: mgorny, delcypher, jdoerfert, #sanitizers, llvm-commits Tags: #llvm, #sanitizers Differential Revision: https://reviews.llvm.org/D59262 llvm-svn: 356457
- Loading branch information
Kostya Kortchinsky
committed
Mar 19, 2019
1 parent
8ee477a
commit 7045c6f
Showing
7 changed files
with
385 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
//===-- string_utils.cc -----------------------------------------*- 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 <ctype.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++] = 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); | ||
return appendNumber(Buffer, BufferEnd, | ||
static_cast<u64>(Negative ? -Num : Num), 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; | ||
} | ||
|
||
int formatString(char *Buffer, uptr BufferLength, const char *Format, | ||
va_list Args) { | ||
UNUSED 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; | ||
} | ||
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; | ||
} | ||
|
||
void ScopedString::append(const char *Format, va_list Args) { | ||
CHECK_LT(Length, String.size()); | ||
formatString(String.data() + Length, String.size() - Length, Format, Args); | ||
Length += strlen(String.data() + Length); | ||
CHECK_LT(Length, String.size()); | ||
} | ||
|
||
FORMAT(2, 3) | ||
void ScopedString::append(const char *Format, ...) { | ||
va_list Args; | ||
va_start(Args, Format); | ||
append(Format, Args); | ||
va_end(Args); | ||
} | ||
|
||
FORMAT(1, 2) | ||
void Printf(const char *Format, ...) { | ||
va_list Args; | ||
va_start(Args, Format); | ||
ScopedString Msg(512); | ||
Msg.append(Format, Args); | ||
outputRaw(Msg.data()); | ||
va_end(Args); | ||
} | ||
|
||
} // namespace scudo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
//===-- string_utils.h ------------------------------------------*- 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef SCUDO_STRING_UTILS_H_ | ||
#define SCUDO_STRING_UTILS_H_ | ||
|
||
#include "internal_defs.h" | ||
#include "vector.h" | ||
|
||
#include <stdarg.h> | ||
|
||
namespace scudo { | ||
|
||
class ScopedString { | ||
public: | ||
explicit ScopedString(uptr MaxLength) : String(MaxLength), Length(0) { | ||
String[0] = '\0'; | ||
} | ||
uptr length() { return Length; } | ||
const char *data() { return String.data(); } | ||
void clear() { | ||
String[0] = '\0'; | ||
Length = 0; | ||
} | ||
void append(const char *Format, va_list Args); | ||
void append(const char *Format, ...); | ||
|
||
private: | ||
Vector<char> String; | ||
uptr Length; | ||
}; | ||
|
||
void Printf(const char *Format, ...); | ||
|
||
} // namespace scudo | ||
|
||
#endif // SCUDO_STRING_UTILS_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.