1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[Support] Add type-safe alternative to llvm::format()

llvm::format() is somewhat unsafe. The compiler does not check that integer
parameter size matches the %x or %d size and it does not complain when a 
StringRef is passed for a %s.  And correctly using a StringRef with format() is  
ugly because you have to convert it to a std::string then call c_str().
 
The cases where llvm::format() is useful is controlling how numbers and
strings are printed, especially when you want fixed width output.  This
patch adds some new formatting functions to raw_streams to format numbers
and StringRefs in a type safe manner. Some examples:

   OS << format_hex(255, 6)        => "0x00ff"
   OS << format_hex(255, 4)        => "0xff"
   OS << format_decimal(0, 5)      => "    0"
   OS << format_decimal(255, 5)    => "  255"
   OS << right_justify(Str, 5)     => "  foo"
   OS << left_justify(Str, 5)      => "foo  "

llvm-svn: 218463
This commit is contained in:
Nick Kledzik 2014-09-25 20:30:58 +00:00
parent ab00cb374c
commit 2ac9102fff
4 changed files with 164 additions and 0 deletions

View File

@ -23,6 +23,8 @@
#ifndef LLVM_SUPPORT_FORMAT_H
#define LLVM_SUPPORT_FORMAT_H
#include "llvm/ADT/StringRef.h"
#include <cassert>
#include <cstdio>
#ifdef _MSC_VER
@ -225,6 +227,66 @@ format(const char *Fmt, const T1 &Val1, const T2 &Val2, const T3 &Val3,
Val5, Val6);
}
/// This is a helper class used for left_justify() and right_justify().
class FormattedString {
StringRef Str;
unsigned Width;
bool RightJustify;
friend class raw_ostream;
public:
FormattedString(StringRef S, unsigned W, bool R)
: Str(S), Width(W), RightJustify(R) { }
};
/// left_justify - append spaces after string so total output is
/// \p Width characters. If \p Str is larger that \p Width, full string
/// is written with no padding.
inline FormattedString left_justify(StringRef Str, unsigned Width) {
return FormattedString(Str, Width, false);
}
/// right_justify - add spaces before string so total output is
/// \p Width characters. If \p Str is larger that \p Width, full string
/// is written with no padding.
inline FormattedString right_justify(StringRef Str, unsigned Width) {
return FormattedString(Str, Width, true);
}
/// This is a helper class used for format_hex() and format_decimal().
class FormattedNumber {
uint64_t HexValue;
int64_t DecValue;
unsigned Width;
bool Hex;
bool Upper;
friend class raw_ostream;
public:
FormattedNumber(uint64_t HV, int64_t DV, unsigned W, bool H, bool U)
: HexValue(HV), DecValue(DV), Width(W), Hex(H), Upper(U) { }
};
/// format_hex - Output \p N as a fixed width hexadecimal. If number will not
/// fit in width, full number is still printed. Examples:
/// OS << format_hex(255, 4) => 0xff
/// OS << format_hex(255, 4, true) => 0xFF
/// OS << format_hex(255, 6) => 0x00ff
/// OS << format_hex(255, 2) => 0xff
inline FormattedNumber format_hex(uint64_t N, unsigned Width, bool Upper=false) {
assert(Width <= 18 && "hex width must be <= 18");
return FormattedNumber(N, 0, Width, true, Upper);
}
/// format_decimal - Output \p N as a right justified, fixed-width decimal. If
/// number will not fit in width, full number is still printed. Examples:
/// OS << format_decimal(0, 5) => " 0"
/// OS << format_decimal(255, 5) => " 255"
/// OS << format_decimal(-1, 3) => " -1"
/// OS << format_decimal(12345, 3) => "12345"
inline FormattedNumber format_decimal(int64_t N, unsigned Width) {
return FormattedNumber(0, N, Width, false, false);
}
} // end namespace llvm
#endif

View File

@ -21,6 +21,8 @@
namespace llvm {
class format_object_base;
class FormattedString;
class FormattedNumber;
template <typename T>
class SmallVectorImpl;
@ -211,6 +213,12 @@ public:
// Formatted output, see the format() function in Support/Format.h.
raw_ostream &operator<<(const format_object_base &Fmt);
// Formatted output, see the leftJustify() function in Support/Format.h.
raw_ostream &operator<<(const FormattedString &);
// Formatted output, see the formatHex() function in Support/Format.h.
raw_ostream &operator<<(const FormattedNumber &);
/// indent - Insert 'NumSpaces' spaces.
raw_ostream &indent(unsigned NumSpaces);

View File

@ -20,6 +20,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include <cctype>
@ -394,6 +395,62 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
}
}
raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
unsigned Len = FS.Str.size();
int PadAmount = FS.Width - Len;
if (FS.RightJustify && (PadAmount > 0))
this->indent(PadAmount);
this->operator<<(FS.Str);
if (!FS.RightJustify && (PadAmount > 0))
this->indent(PadAmount);
return *this;
}
raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
if (FN.Hex) {
unsigned Nibbles = (64 - countLeadingZeros(FN.HexValue)+3)/4;
unsigned Width = (FN.Width > Nibbles+2) ? FN.Width : Nibbles+2;
char NumberBuffer[20] = "0x0000000000000000";
char *EndPtr = NumberBuffer+Width;
char *CurPtr = EndPtr;
const char A = FN.Upper ? 'A' : 'a';
unsigned long long N = FN.HexValue;
while (N) {
uintptr_t x = N % 16;
*--CurPtr = (x < 10 ? '0' + x : A + x - 10);
N /= 16;
}
return write(NumberBuffer, Width);
} else {
// Zero is a special case.
if (FN.DecValue == 0) {
this->indent(FN.Width-1);
return *this << '0';
}
char NumberBuffer[32];
char *EndPtr = NumberBuffer+sizeof(NumberBuffer);
char *CurPtr = EndPtr;
bool Neg = (FN.DecValue < 0);
uint64_t N = Neg ? -FN.DecValue : FN.DecValue;
while (N) {
*--CurPtr = '0' + char(N % 10);
N /= 10;
}
int Len = EndPtr - CurPtr;
int Pad = FN.Width - Len;
if (Neg)
--Pad;
if (Pad > 0)
this->indent(Pad);
if (Neg)
*this << '-';
return write(CurPtr, Len);
}
}
/// indent - Insert 'NumSpaces' spaces.
raw_ostream &raw_ostream::indent(unsigned NumSpaces) {
static const char Spaces[] = " "

View File

@ -143,4 +143,41 @@ TEST(raw_ostreamTest, WriteEscaped) {
EXPECT_EQ("\\001\\010\\200", Str);
}
TEST(raw_ostreamTest, Justify) {
EXPECT_EQ("xyz ", printToString(left_justify("xyz", 6), 6));
EXPECT_EQ("abc", printToString(left_justify("abc", 3), 3));
EXPECT_EQ("big", printToString(left_justify("big", 1), 3));
EXPECT_EQ(" xyz", printToString(right_justify("xyz", 6), 6));
EXPECT_EQ("abc", printToString(right_justify("abc", 3), 3));
EXPECT_EQ("big", printToString(right_justify("big", 1), 3));
}
TEST(raw_ostreamTest, FormatHex) {
EXPECT_EQ("0x1234", printToString(format_hex(0x1234, 6), 6));
EXPECT_EQ("0x001234", printToString(format_hex(0x1234, 8), 8));
EXPECT_EQ("0x00001234", printToString(format_hex(0x1234, 10), 10));
EXPECT_EQ("0x1234", printToString(format_hex(0x1234, 4), 6));
EXPECT_EQ("0xff", printToString(format_hex(255, 4), 4));
EXPECT_EQ("0xFF", printToString(format_hex(255, 4, true), 4));
EXPECT_EQ("0x1", printToString(format_hex(1, 3), 3));
EXPECT_EQ("0x12", printToString(format_hex(0x12, 3), 4));
EXPECT_EQ("0x123", printToString(format_hex(0x123, 3), 5));
EXPECT_EQ("0xffffffffffffffff",
printToString(format_hex(UINT64_MAX, 18), 18));
EXPECT_EQ("0x8000000000000000",
printToString(format_hex((INT64_MIN), 18), 18));
}
TEST(raw_ostreamTest, FormatDecimal) {
EXPECT_EQ(" 0", printToString(format_decimal(0, 4), 4));
EXPECT_EQ(" -1", printToString(format_decimal(-1, 4), 4));
EXPECT_EQ(" -1", printToString(format_decimal(-1, 6), 6));
EXPECT_EQ("1234567890", printToString(format_decimal(1234567890, 10), 10));
EXPECT_EQ(" 9223372036854775807",
printToString(format_decimal(INT64_MAX, 21), 21));
EXPECT_EQ(" -9223372036854775808",
printToString(format_decimal(INT64_MIN, 21), 21));
}
}