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

David Majnemer david.majnemer at gmail.com
Thu Sep 25 19:58:19 PDT 2014


On Thu, Sep 25, 2014 at 1:30 PM, Nick Kledzik <kledzik at apple.com> wrote:

> Author: kledzik
> Date: Thu Sep 25 15:30:58 2014
> New Revision: 218463
>
> URL: http://llvm.org/viewvc/llvm-project?rev=218463&view=rev
> Log:
> [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  "
>
>
> Modified:
>     llvm/trunk/include/llvm/Support/Format.h
>     llvm/trunk/include/llvm/Support/raw_ostream.h
>     llvm/trunk/lib/Support/raw_ostream.cpp
>     llvm/trunk/unittests/Support/raw_ostream_test.cpp
>
> Modified: llvm/trunk/include/llvm/Support/Format.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Format.h?rev=218463&r1=218462&r2=218463&view=diff
>
> ==============================================================================
> --- llvm/trunk/include/llvm/Support/Format.h (original)
> +++ llvm/trunk/include/llvm/Support/Format.h Thu Sep 25 15:30:58 2014
> @@ -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,
>                                                  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
>
> Modified: llvm/trunk/include/llvm/Support/raw_ostream.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/raw_ostream.h?rev=218463&r1=218462&r2=218463&view=diff
>
> ==============================================================================
> --- llvm/trunk/include/llvm/Support/raw_ostream.h (original)
> +++ llvm/trunk/include/llvm/Support/raw_ostream.h Thu Sep 25 15:30:58 2014
> @@ -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);
>
>
> Modified: llvm/trunk/lib/Support/raw_ostream.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/raw_ostream.cpp?rev=218463&r1=218462&r2=218463&view=diff
>
> ==============================================================================
> --- llvm/trunk/lib/Support/raw_ostream.cpp (original)
> +++ llvm/trunk/lib/Support/raw_ostream.cpp Thu Sep 25 15:30:58 2014
> @@ -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<<(con
>    }
>  }
>
> +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;
>

This has undefined behavior if FN.DecValue is INT64_MIN, fixed in r218496.


> +    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[] = "                                "
>
> Modified: llvm/trunk/unittests/Support/raw_ostream_test.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/raw_ostream_test.cpp?rev=218463&r1=218462&r2=218463&view=diff
>
> ==============================================================================
> --- llvm/trunk/unittests/Support/raw_ostream_test.cpp (original)
> +++ llvm/trunk/unittests/Support/raw_ostream_test.cpp Thu Sep 25 15:30:58
> 2014
> @@ -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));
> +}
> +
> +
>  }
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20140925/fd7cdb8e/attachment.html>


More information about the llvm-commits mailing list