411 changes: 411 additions & 0 deletions llvm/include/llvm/Support/FormatProviders.h

Large diffs are not rendered by default.

246 changes: 246 additions & 0 deletions llvm/include/llvm/Support/FormatVariadic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
//===- FormatVariadic.h - Efficient type-safe string formatting --*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the formatv() function which can be used with other LLVM
// subsystems to provide printf-like formatting, but with improved safety and
// flexibility. The result of `formatv` is an object which can be streamed to
// a raw_ostream or converted to a std::string or llvm::SmallString.
//
// // Convert to std::string.
// std::string S = formatv("{0} {1}", 1234.412, "test").str();
//
// // Convert to llvm::SmallString
// SmallString<8> S = formatv("{0} {1}", 1234.412, "test").sstr<8>();
//
// // Stream to an existing raw_ostream.
// OS << formatv("{0} {1}", 1234.412, "test");
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_FORMATVARIADIC_H
#define LLVM_SUPPORT_FORMATVARIADIC_H

#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/FormatCommon.h"
#include "llvm/Support/FormatProviders.h"
#include "llvm/Support/FormatVariadicDetails.h"
#include "llvm/Support/raw_ostream.h"

#include <string>
#include <tuple>
#include <vector>

namespace llvm {

enum class ReplacementType { Empty, Format, Literal };

struct ReplacementItem {
ReplacementItem() {}
explicit ReplacementItem(StringRef Literal)
: Type(ReplacementType::Literal), Spec(Literal) {}
ReplacementItem(StringRef Spec, size_t Index, size_t Align, AlignStyle Where,
char Pad, StringRef Options)
: Type(ReplacementType::Format), Spec(Spec), Index(Index), Align(Align),
Where(Where), Pad(Pad), Options(Options) {}
ReplacementType Type = ReplacementType::Empty;
StringRef Spec;
size_t Index = 0;
size_t Align = 0;
AlignStyle Where = AlignStyle::Right;
char Pad;
StringRef Options;
};

class formatv_object_base {
protected:
// The parameters are stored in a std::tuple, which does not provide runtime
// indexing capabilities. In order to enable runtime indexing, we use this
// structure to put the parameters into a std::vector. Since the parameters
// are not all the same type, we use some type-erasure by wrapping the
// parameters in a template class that derives from a non-template superclass.
// Essentially, we are converting a std::tuple<Derived<Ts...>> to a
// std::vector<Base*>.
struct create_wrappers {
template <typename... Ts>
std::vector<detail::format_wrapper *> operator()(Ts &... Items) {
return std::vector<detail::format_wrapper *>{&Items...};
}
};

StringRef Fmt;
std::vector<detail::format_wrapper *> Wrappers;
std::vector<ReplacementItem> Replacements;

static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
size_t &Align, char &Pad);

static std::pair<ReplacementItem, StringRef>
splitLiteralAndReplacement(StringRef Fmt);

public:
formatv_object_base(StringRef Fmt, std::size_t ParamCount)
: Fmt(Fmt), Replacements(parseFormatString(Fmt)) {
Wrappers.reserve(ParamCount);
return;
}

void format(raw_ostream &S) const {
for (auto &R : Replacements) {
if (R.Type == ReplacementType::Empty)
continue;
if (R.Type == ReplacementType::Literal) {
S << R.Spec;
continue;
}
if (R.Index >= Wrappers.size()) {
S << R.Spec;
continue;
}

auto W = Wrappers[R.Index];

FmtAlign Align(*W, R.Where, R.Align);
Align.format(S, R.Options);
}
}
static std::vector<ReplacementItem> parseFormatString(StringRef Fmt);

static Optional<ReplacementItem> parseReplacementItem(StringRef Spec);

std::string str() const {
std::string Result;
raw_string_ostream Stream(Result);
Stream << *this;
Stream.flush();
return Result;
}

template <size_t N> llvm::SmallString<N> sstr() const {
SmallString<N> Result;
raw_svector_ostream Stream(Result);
Stream << *this;
return Result;
}

template <size_t N> operator SmallString<N>() const { return sstr<N>(); }

operator std::string() const { return str(); }
};

template <typename Tuple> class formatv_object : public formatv_object_base {
// Storage for the parameter wrappers. Since the base class erases the type
// of the parameters, we have to own the storage for the parameters here, and
// have the base class store type-erased pointers into this tuple.
Tuple Parameters;

public:
formatv_object(StringRef Fmt, Tuple &&Params)
: formatv_object_base(Fmt, std::tuple_size<Tuple>::value),
Parameters(std::move(Params)) {
Wrappers = apply_tuple(create_wrappers(), Parameters);
}
};

// \brief Format text given a format string and replacement parameters.
//
// ===General Description===
//
// Formats textual output. `Fmt` is a string consisting of one or more
// replacement sequences with the following grammar:
//
// rep_field ::= "{" [index] ["," layout] [":" format] "}"
// index ::= <non-negative integer>
// layout ::= [[[char]loc]width]
// format ::= <any string not containing "{" or "}">
// char ::= <any character except "{" or "}">
// loc ::= "-" | "=" | "+"
// width ::= <positive integer>
//
// index - A non-negative integer specifying the index of the item in the
// parameter pack to print. Any other value is invalid.
// layout - A string controlling how the field is laid out within the available
// space.
// format - A type-dependent string used to provide additional options to
// the formatting operation. Refer to the documentation of the
// various individual format providers for per-type options.
// char - The padding character. Defaults to ' ' (space). Only valid if
// `loc` is also specified.
// loc - Where to print the formatted text within the field. Only valid if
// `width` is also specified.
// '-' : The field is left aligned within the available space.
// '=' : The field is centered within the available space.
// '+' : The field is right aligned within the available space (this
// is the default).
// width - The width of the field within which to print the formatted text.
// If this is less than the required length then the `char` and `loc`
// fields are ignored, and the field is printed with no leading or
// trailing padding. If this is greater than the required length,
// then the text is output according to the value of `loc`, and padded
// as appropriate on the left and/or right by `char`.
//
// ===Special Characters===
//
// The characters '{' and '}' are reserved and cannot appear anywhere within a
// replacement sequence. Outside of a replacement sequence, in order to print
// a literal '{' or '}' it must be doubled -- "{{" to print a literal '{' and
// "}}" to print a literal '}'.
//
// ===Parameter Indexing===
// `index` specifies the index of the paramter in the parameter pack to format
// into the output. Note that it is possible to refer to the same parameter
// index multiple times in a given format string. This makes it possible to
// output the same value multiple times without passing it multiple times to the
// function. For example:
//
// formatv("{0} {1} {0}", "a", "bb")
//
// would yield the string "abba". This can be convenient when it is expensive
// to compute the value of the parameter, and you would otherwise have had to
// save it to a temporary.
//
// ===Formatter Search===
//
// For a given parameter of type T, the following steps are executed in order
// until a match is found:
//
// 1. If the parameter is of class type, and contains a method
// void format(raw_ostream &Stream, StringRef Options)
// Then this method is invoked to produce the formatted output. The
// implementation should write the formatted text into `Stream`.
// 2. If there is a suitable template specialization of format_provider<>
// for type T containing a method whose signature is:
// void format(const T &Obj, raw_ostream &Stream, StringRef Options)
// Then this method is invoked as described in Step 1.
//
// If a match cannot be found through either of the above methods, a compiler
// error is generated.
//
// ===Invalid Format String Handling===
//
// In the case of a format string which does not match the grammar described
// above, the output is undefined. With asserts enabled, LLVM will trigger an
// assertion. Otherwise, it will try to do something reasonable, but in general
// the details of what that is are undefined.
//
template <typename... Ts>
inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object<decltype(
std::make_tuple(detail::build_format_wrapper(std::forward<Ts>(Vals))...))> {
using ParamTuple = decltype(
std::make_tuple(detail::build_format_wrapper(std::forward<Ts>(Vals))...));
return formatv_object<ParamTuple>(
Fmt,
std::make_tuple(detail::build_format_wrapper(std::forward<Ts>(Vals))...));
}

} // end namespace llvm

#endif
149 changes: 149 additions & 0 deletions llvm/include/llvm/Support/FormatVariadicDetails.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//===- FormatVariadicDetails.h - Helpers for FormatVariadic.h ----*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H
#define LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

#include <type_traits>

namespace llvm {
template <typename T, typename Enable = void> struct format_provider {};

namespace detail {

class format_wrapper {
protected:
~format_wrapper() {}

public:
virtual void format(llvm::raw_ostream &S, StringRef Options) = 0;
};

template <typename T> class member_format_wrapper : public format_wrapper {
T Item;

public:
explicit member_format_wrapper(T &&Item) : Item(Item) {}

void format(llvm::raw_ostream &S, StringRef Options) override {
Item.format(S, Options);
}
};

template <typename T> class provider_format_wrapper : public format_wrapper {
T Item;

public:
explicit provider_format_wrapper(T &&Item) : Item(Item) {}

void format(llvm::raw_ostream &S, StringRef Options) override {
format_provider<typename std::decay<T>::type>::format(Item, S, Options);
}
};

template <typename T> class missing_format_wrapper : public format_wrapper {
public:
missing_format_wrapper() {
static_assert(false, "T does not have a format_provider");
}
void format(llvm::raw_ostream &S, StringRef Options) override {}
};

// Test if T is a class that contains a member function with the signature:
//
// void format(raw_ostream &, StringRef);
//
template <class T, class Enable = void> class has_FormatMember {
public:
static bool const value = false;
};

template <class T>
class has_FormatMember<T,
typename std::enable_if<std::is_class<T>::value>::type> {
using Signature_format = void (T::*)(llvm::raw_ostream &S, StringRef Options);

template <typename U>
static char test2(SameType<Signature_format, &U::format> *);

template <typename U> static double test2(...);

public:
static bool const value = (sizeof(test2<T>(nullptr)) == 1);
};

// Test if format_provider<T> is defined on T and contains a member function
// with the signature:
// static void format(const T&, raw_stream &, StringRef);
//
template <class T> class has_FormatProvider {
public:
using Decayed = typename std::decay<T>::type;
typedef void (*Signature_format)(const Decayed &, llvm::raw_ostream &,
StringRef);

template <typename U>
static char test(SameType<Signature_format, &U::format> *);

template <typename U> static double test(...);

static bool const value =
(sizeof(test<llvm::format_provider<Decayed>>(nullptr)) == 1);
};

// Simple template that decides whether a type T should use the member-function
// based format() invocation.
template <typename T>
struct uses_format_member
: public std::integral_constant<bool, has_FormatMember<T>::value> {};

// Simple template that decides whether a type T should use the format_provider
// based format() invocation. The member function takes priority, so this test
// will only be true if there is not ALSO a format member.
template <typename T>
struct uses_format_provider
: public std::integral_constant<bool, !has_FormatMember<T>::value &&
has_FormatProvider<T>::value> {};

// Simple template that decides whether a type T has neither a member-function
// nor format_provider based implementation that it can use. Mostly used so
// that the compiler spits out a nice diagnostic when a type with no format
// implementation can be located.
template <typename T>
struct uses_missing_provider
: public std::integral_constant<bool, !has_FormatMember<T>::value &&
!has_FormatProvider<T>::value> {};

template <typename T>
typename std::enable_if<uses_format_member<T>::value,
member_format_wrapper<T>>::type
build_format_wrapper(T &&Item) {
return member_format_wrapper<T>(std::forward<T>(Item));
}

template <typename T>
typename std::enable_if<uses_format_provider<T>::value,
provider_format_wrapper<T>>::type
build_format_wrapper(T &&Item) {
return provider_format_wrapper<T>(std::forward<T>(Item));
}

template <typename T>
typename std::enable_if<uses_missing_provider<T>::value,
missing_format_wrapper<T>>::type
build_format_wrapper(T &&Item) {
return missing_format_wrapper<T>();
}
}
}

#endif
19 changes: 13 additions & 6 deletions llvm/include/llvm/Support/NativeFormatting.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,19 @@ enum class HexPrintStyle { Upper, Lower, PrefixUpper, PrefixLower };

size_t getDefaultPrecision(FloatStyle Style);

void write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style);
void write_integer(raw_ostream &S, int N, IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style);
void write_integer(raw_ostream &S, long N, IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long long N, IntegerStyle Style);
void write_integer(raw_ostream &S, long long N, IntegerStyle Style);
bool isPrefixedHexStyle(HexPrintStyle S);

void write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, int N, size_t MinDigits, IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, long N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, long long N, size_t MinDigits,
IntegerStyle Style);

void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
Optional<size_t> Width = None);
Expand Down
5 changes: 1 addition & 4 deletions llvm/include/llvm/Support/YAMLTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define LLVM_SUPPORT_YAMLTRAITS_H

#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
Expand Down Expand Up @@ -201,10 +202,6 @@ struct DocumentListTraits {
// static T::value_type& element(IO &io, T &seq, size_t index);
};

// Only used by compiler if both template types are the same
template <typename T, T>
struct SameType;

// Only used for better diagnostics of missing traits
template <typename T>
struct MissingTrait;
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/Support/raw_ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <system_error>

namespace llvm {
class formatv_object_base;
class format_object_base;
class FormattedString;
class FormattedNumber;
Expand Down Expand Up @@ -223,6 +224,9 @@ class raw_ostream {
// Formatted output, see the formatHex() function in Support/Format.h.
raw_ostream &operator<<(const FormattedNumber &);

// Formatted output, see the formatv() function in Support/FormatVariadic.h.
raw_ostream &operator<<(const formatv_object_base &);

// Formatted output, see the format_bytes() function in Support/Format.h.
raw_ostream &operator<<(const FormattedBytes &);

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_llvm_library(LLVMSupport
FileOutputBuffer.cpp
FoldingSet.cpp
FormattedStream.cpp
FormatVariadic.cpp
GraphWriter.cpp
Hashing.cpp
IntEqClasses.cpp
Expand Down
156 changes: 156 additions & 0 deletions llvm/lib/Support/FormatVariadic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//===- FormatVariadic.cpp - Format string parsing and analysis ----*-C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//

#include "llvm/Support/FormatVariadic.h"

using namespace llvm;

static Optional<AlignStyle> translateLocChar(char C) {
switch (C) {
case '-':
return AlignStyle::Left;
case '=':
return AlignStyle::Center;
case '+':
return AlignStyle::Right;
default:
return None;
}
LLVM_BUILTIN_UNREACHABLE;
}

bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
size_t &Align, char &Pad) {
Where = AlignStyle::Right;
Align = 0;
Pad = ' ';
if (Spec.empty())
return true;

if (Spec.size() > 1) {
// A maximum of 2 characters at the beginning can be used for something
// other
// than the width.
// If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
// contains the width.
// Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
// Otherwise, Spec[0:...] contains the width.
if (auto Loc = translateLocChar(Spec[1])) {
Pad = Spec[0];
Where = *Loc;
Spec = Spec.drop_front(2);
} else if (auto Loc = translateLocChar(Spec[0])) {
Where = *Loc;
Spec = Spec.drop_front(1);
}
}

bool Failed = Spec.consumeInteger(0, Align);
return !Failed;
}

Optional<ReplacementItem>
formatv_object_base::parseReplacementItem(StringRef Spec) {
StringRef RepString = Spec.trim("{}");

// If the replacement sequence does not start with a non-negative integer,
// this is an error.
char Pad = ' ';
std::size_t Align = 0;
AlignStyle Where = AlignStyle::Right;
StringRef Options;
size_t Index = 0;
RepString = RepString.trim();
if (RepString.consumeInteger(0, Index)) {
assert(false && "Invalid replacement sequence index!");
return ReplacementItem{};
}
RepString = RepString.trim();
if (!RepString.empty() && RepString.front() == ',') {
RepString = RepString.drop_front();
if (!consumeFieldLayout(RepString, Where, Align, Pad))
assert(false && "Invalid replacement field layout specification!");
}
RepString = RepString.trim();
if (!RepString.empty() && RepString.front() == ':') {
Options = RepString.drop_front().trim();
RepString = StringRef();
}
RepString = RepString.trim();
if (!RepString.empty()) {
assert(false && "Unexpected characters found in replacement string!");
}

return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
}

std::pair<ReplacementItem, StringRef>
formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) {
StringRef Rep;
StringRef Remainder;
std::size_t From = 0;
while (From < Fmt.size() && From != StringRef::npos) {
std::size_t BO = Fmt.find_first_of('{', From);
// Everything up until the first brace is a literal.
if (BO != 0)
return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO));

StringRef Braces =
Fmt.drop_front(BO).take_while([](char C) { return C == '{'; });
// If there is more than one brace, then some of them are escaped. Treat
// these as replacements.
if (Braces.size() > 1) {
size_t NumEscapedBraces = Braces.size() / 2;
StringRef Middle = Fmt.substr(BO, NumEscapedBraces);
StringRef Right = Fmt.drop_front(BO + NumEscapedBraces * 2);
return std::make_pair(ReplacementItem{Middle}, Right);
}
// An unterminated open brace is undefined. We treat the rest of the string
// as a literal replacement, but we assert to indicate that this is
// undefined and that we consider it an error.
std::size_t BC = Fmt.find_first_of('}', BO);
if (BC == StringRef::npos) {
assert(
false &&
"Unterminated brace sequence. Escape with {{ for a literal brace.");
return std::make_pair(ReplacementItem{Fmt}, StringRef());
}

// Even if there is a closing brace, if there is another open brace before
// this closing brace, treat this portion as literal, and try again with the
// next one.
std::size_t BO2 = Fmt.find_first_of('{', BO + 1);
if (BO2 < BC)
return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)},
Fmt.substr(BO2));

StringRef Spec = Fmt.slice(BO + 1, BC);
StringRef Right = Fmt.substr(BC + 1);

auto RI = parseReplacementItem(Spec);
if (RI.hasValue())
return std::make_pair(*RI, Right);

// If there was an error parsing the replacement item, treat it as an
// invalid replacement spec, and just continue.
From = BC + 1;
}
return std::make_pair(ReplacementItem{Fmt}, StringRef());
}

std::vector<ReplacementItem>
formatv_object_base::parseFormatString(StringRef Fmt) {
std::vector<ReplacementItem> Replacements;
ReplacementItem I;
while (!Fmt.empty()) {
std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt);
if (I.Type != ReplacementType::Empty)
Replacements.push_back(I);
}
return Replacements;
}
67 changes: 44 additions & 23 deletions llvm/lib/Support/NativeFormatting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ static void writeWithCommas(raw_ostream &S, ArrayRef<char> Buffer) {
}

template <typename T>
static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,
bool IsNegative) {
static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
IntegerStyle Style, bool IsNegative) {
static_assert(std::is_unsigned<T>::value, "Value is not unsigned!");

char NumberBuffer[128];
Expand All @@ -59,6 +59,12 @@ static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,

if (IsNegative)
S << '-';

if (Len < MinDigits && Style != IntegerStyle::Number) {
for (size_t I = Len; I < MinDigits; ++I)
S << '0';
}

if (Style == IntegerStyle::Number) {
writeWithCommas(S, ArrayRef<char>(std::end(NumberBuffer) - Len, Len));
} else {
Expand All @@ -67,53 +73,60 @@ static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,
}

template <typename T>
static void write_unsigned(raw_ostream &S, T N, IntegerStyle Style,
bool IsNegative = false) {
static void write_unsigned(raw_ostream &S, T N, size_t MinDigits,
IntegerStyle Style, bool IsNegative = false) {
// Output using 32-bit div/mod if possible.
if (N == static_cast<uint32_t>(N))
write_unsigned_impl(S, static_cast<uint32_t>(N), Style, IsNegative);
write_unsigned_impl(S, static_cast<uint32_t>(N), MinDigits, Style,
IsNegative);
else
write_unsigned_impl(S, N, Style, IsNegative);
write_unsigned_impl(S, N, MinDigits, Style, IsNegative);
}

template <typename T>
static void write_signed(raw_ostream &S, T N, IntegerStyle Style) {
static void write_signed(raw_ostream &S, T N, size_t MinDigits,
IntegerStyle Style) {
static_assert(std::is_signed<T>::value, "Value is not signed!");

using UnsignedT = typename std::make_unsigned<T>::type;

if (N >= 0) {
write_unsigned(S, static_cast<UnsignedT>(N), Style);
write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style);
return;
}

UnsignedT UN = -(UnsignedT)N;
write_unsigned(S, UN, Style, true);
write_unsigned(S, UN, MinDigits, Style, true);
}

void llvm::write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style) {
write_unsigned(S, N, Style);
void llvm::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
IntegerStyle Style) {
write_unsigned(S, N, MinDigits, Style);
}

void llvm::write_integer(raw_ostream &S, int N, IntegerStyle Style) {
write_signed(S, N, Style);
void llvm::write_integer(raw_ostream &S, int N, size_t MinDigits,
IntegerStyle Style) {
write_signed(S, N, MinDigits, Style);
}

void llvm::write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style) {
write_unsigned(S, N, Style);
void llvm::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
IntegerStyle Style) {
write_unsigned(S, N, MinDigits, Style);
}

void llvm::write_integer(raw_ostream &S, long N, IntegerStyle Style) {
write_signed(S, N, Style);
void llvm::write_integer(raw_ostream &S, long N, size_t MinDigits,
IntegerStyle Style) {
write_signed(S, N, MinDigits, Style);
}

void llvm::write_integer(raw_ostream &S, unsigned long long N,
void llvm::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
IntegerStyle Style) {
write_unsigned(S, N, Style);
write_unsigned(S, N, MinDigits, Style);
}

void llvm::write_integer(raw_ostream &S, long long N, IntegerStyle Style) {
write_signed(S, N, Style);
void llvm::write_integer(raw_ostream &S, long long N, size_t MinDigits,
IntegerStyle Style) {
write_signed(S, N, MinDigits, Style);
}

void llvm::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
Expand Down Expand Up @@ -178,7 +191,9 @@ void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,
#if defined(__MINGW32__)
// FIXME: It should be generic to C++11.
if (N == 0.0 && std::signbit(N)) {
const char *NegativeZero = "-0.000000e+00";
char NegativeZero[] = "-0.000000e+00";
if (Style == FloatStyle::ExponentUpper)
NegativeZero[strlen(NegativeZero) - 4] = 'E';
S << NegativeZero;
return;
}
Expand All @@ -187,7 +202,9 @@ void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,

// negative zero
if (fpcl == _FPCLASS_NZ) {
const char *NegativeZero = "-0.000000e+00";
char NegativeZero[] = "-0.000000e+00";
if (Style == FloatStyle::ExponentUpper)
NegativeZero[strlen(NegativeZero) - 4] = 'E';
S << NegativeZero;
return;
}
Expand Down Expand Up @@ -231,6 +248,10 @@ void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,
S << '%';
}

bool llvm::isPrefixedHexStyle(HexPrintStyle S) {
return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper);
}

size_t llvm::getDefaultPrecision(FloatStyle Style) {
switch (Style) {
case FloatStyle::Exponent:
Expand Down
17 changes: 12 additions & 5 deletions llvm/lib/Support/raw_ostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/NativeFormatting.h"
#include "llvm/Support/Process.h"
Expand Down Expand Up @@ -114,22 +115,22 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
}

raw_ostream &raw_ostream::operator<<(unsigned long N) {
write_integer(*this, static_cast<uint64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
return *this;
}

raw_ostream &raw_ostream::operator<<(long N) {
write_integer(*this, static_cast<int64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
return *this;
}

raw_ostream &raw_ostream::operator<<(unsigned long long N) {
write_integer(*this, static_cast<uint64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
return *this;
}

raw_ostream &raw_ostream::operator<<(long long N) {
write_integer(*this, static_cast<int64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
return *this;
}

Expand Down Expand Up @@ -318,6 +319,12 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
}
}

raw_ostream &raw_ostream::operator<<(const formatv_object_base &Obj) {
SmallString<128> S;
Obj.format(*this);
return *this;
}

raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
unsigned Len = FS.Str.size();
int PadAmount = FS.Width - Len;
Expand All @@ -344,7 +351,7 @@ raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
} else {
llvm::SmallString<16> Buffer;
llvm::raw_svector_ostream Stream(Buffer);
llvm::write_integer(Stream, FN.DecValue, IntegerStyle::Integer);
llvm::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer);
if (Buffer.size() < FN.Width)
indent(FN.Width - Buffer.size());
(*this) << Buffer;
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_llvm_unittest(SupportTests
ErrorTest.cpp
ErrorOrTest.cpp
FileOutputBufferTest.cpp
FormatVariadicTest.cpp
Host.cpp
LEB128Test.cpp
LineIteratorTest.cpp
Expand Down
538 changes: 538 additions & 0 deletions llvm/unittests/Support/FormatVariadicTest.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion llvm/unittests/Support/NativeFormatTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace {
template <typename T> std::string format_number(T N, IntegerStyle Style) {
std::string S;
llvm::raw_string_ostream Str(S);
write_integer(Str, N, Style);
write_integer(Str, N, 0, Style);
Str.flush();
return S;
}
Expand Down