ccdbb5941 Export of internal Abseil changes 01f5f81f9 Export of internal Abseil changes 2c92bdc7c Export of internal Abseil changes e7ebf9803 Export of internal Abseil changes 2eba343b5 Export of internal Abseil changes a8b03d90e Export of internal Abseil changes 1d31b5c36 Export of internal Abseil changes da3a87690 Export of internal Abseil changes 8faf20461 Exclude empty directories (#697) 2069dc796 Export of internal Abseil changes 4832bf6bf Added a BUILD file in root to expose license. (#695) af8f994af Export of internal Abseil changes 33caf1097 Export of internal Abseil changes cf1a02e2d Export of internal Abseil changes git-subtree-dir: third_party/abseil_cpp git-subtree-split: ccdbb5941f992fabda7eae3ce72f55efc17c826a
871 lines
31 KiB
C++
871 lines
31 KiB
C++
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#include <cctype>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <thread> // NOLINT
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "absl/base/internal/raw_logging.h"
|
|
#include "absl/strings/internal/str_format/bind.h"
|
|
#include "absl/types/optional.h"
|
|
|
|
namespace absl {
|
|
ABSL_NAMESPACE_BEGIN
|
|
namespace str_format_internal {
|
|
namespace {
|
|
|
|
template <typename T, size_t N>
|
|
size_t ArraySize(T (&)[N]) {
|
|
return N;
|
|
}
|
|
|
|
std::string LengthModFor(float) { return ""; }
|
|
std::string LengthModFor(double) { return ""; }
|
|
std::string LengthModFor(long double) { return "L"; }
|
|
std::string LengthModFor(char) { return "hh"; }
|
|
std::string LengthModFor(signed char) { return "hh"; }
|
|
std::string LengthModFor(unsigned char) { return "hh"; }
|
|
std::string LengthModFor(short) { return "h"; } // NOLINT
|
|
std::string LengthModFor(unsigned short) { return "h"; } // NOLINT
|
|
std::string LengthModFor(int) { return ""; }
|
|
std::string LengthModFor(unsigned) { return ""; }
|
|
std::string LengthModFor(long) { return "l"; } // NOLINT
|
|
std::string LengthModFor(unsigned long) { return "l"; } // NOLINT
|
|
std::string LengthModFor(long long) { return "ll"; } // NOLINT
|
|
std::string LengthModFor(unsigned long long) { return "ll"; } // NOLINT
|
|
|
|
std::string EscCharImpl(int v) {
|
|
if (std::isprint(static_cast<unsigned char>(v))) {
|
|
return std::string(1, static_cast<char>(v));
|
|
}
|
|
char buf[64];
|
|
int n = snprintf(buf, sizeof(buf), "\\%#.2x",
|
|
static_cast<unsigned>(v & 0xff));
|
|
assert(n > 0 && n < sizeof(buf));
|
|
return std::string(buf, n);
|
|
}
|
|
|
|
std::string Esc(char v) { return EscCharImpl(v); }
|
|
std::string Esc(signed char v) { return EscCharImpl(v); }
|
|
std::string Esc(unsigned char v) { return EscCharImpl(v); }
|
|
|
|
template <typename T>
|
|
std::string Esc(const T &v) {
|
|
std::ostringstream oss;
|
|
oss << v;
|
|
return oss.str();
|
|
}
|
|
|
|
void StrAppendV(std::string *dst, const char *format, va_list ap) {
|
|
// First try with a small fixed size buffer
|
|
static const int kSpaceLength = 1024;
|
|
char space[kSpaceLength];
|
|
|
|
// It's possible for methods that use a va_list to invalidate
|
|
// the data in it upon use. The fix is to make a copy
|
|
// of the structure before using it and use that copy instead.
|
|
va_list backup_ap;
|
|
va_copy(backup_ap, ap);
|
|
int result = vsnprintf(space, kSpaceLength, format, backup_ap);
|
|
va_end(backup_ap);
|
|
if (result < kSpaceLength) {
|
|
if (result >= 0) {
|
|
// Normal case -- everything fit.
|
|
dst->append(space, result);
|
|
return;
|
|
}
|
|
if (result < 0) {
|
|
// Just an error.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Increase the buffer size to the size requested by vsnprintf,
|
|
// plus one for the closing \0.
|
|
int length = result + 1;
|
|
char *buf = new char[length];
|
|
|
|
// Restore the va_list before we use it again
|
|
va_copy(backup_ap, ap);
|
|
result = vsnprintf(buf, length, format, backup_ap);
|
|
va_end(backup_ap);
|
|
|
|
if (result >= 0 && result < length) {
|
|
// It fit
|
|
dst->append(buf, result);
|
|
}
|
|
delete[] buf;
|
|
}
|
|
|
|
void StrAppend(std::string *out, const char *format, ...) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
StrAppendV(out, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
std::string StrPrint(const char *format, ...) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
std::string result;
|
|
StrAppendV(&result, format, ap);
|
|
va_end(ap);
|
|
return result;
|
|
}
|
|
|
|
class FormatConvertTest : public ::testing::Test { };
|
|
|
|
template <typename T>
|
|
void TestStringConvert(const T& str) {
|
|
const FormatArgImpl args[] = {FormatArgImpl(str)};
|
|
struct Expectation {
|
|
const char *out;
|
|
const char *fmt;
|
|
};
|
|
const Expectation kExpect[] = {
|
|
{"hello", "%1$s" },
|
|
{"", "%1$.s" },
|
|
{"", "%1$.0s" },
|
|
{"h", "%1$.1s" },
|
|
{"he", "%1$.2s" },
|
|
{"hello", "%1$.10s" },
|
|
{" hello", "%1$6s" },
|
|
{" he", "%1$5.2s" },
|
|
{"he ", "%1$-5.2s" },
|
|
{"hello ", "%1$-6.10s" },
|
|
};
|
|
for (const Expectation &e : kExpect) {
|
|
UntypedFormatSpecImpl format(e.fmt);
|
|
EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
|
|
}
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, BasicString) {
|
|
TestStringConvert("hello"); // As char array.
|
|
TestStringConvert(static_cast<const char*>("hello"));
|
|
TestStringConvert(std::string("hello"));
|
|
TestStringConvert(string_view("hello"));
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, NullString) {
|
|
const char* p = nullptr;
|
|
UntypedFormatSpecImpl format("%s");
|
|
EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, StringPrecision) {
|
|
// We cap at the precision.
|
|
char c = 'a';
|
|
const char* p = &c;
|
|
UntypedFormatSpecImpl format("%.1s");
|
|
EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
|
|
|
|
// We cap at the NUL-terminator.
|
|
p = "ABC";
|
|
UntypedFormatSpecImpl format2("%.10s");
|
|
EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
|
|
}
|
|
|
|
// Pointer formatting is implementation defined. This checks that the argument
|
|
// can be matched to `ptr`.
|
|
MATCHER_P(MatchesPointerString, ptr, "") {
|
|
if (ptr == nullptr && arg == "(nil)") {
|
|
return true;
|
|
}
|
|
void* parsed = nullptr;
|
|
if (sscanf(arg.c_str(), "%p", &parsed) != 1) {
|
|
ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str());
|
|
}
|
|
return ptr == parsed;
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, Pointer) {
|
|
static int x = 0;
|
|
const int *xp = &x;
|
|
char c = 'h';
|
|
char *mcp = &c;
|
|
const char *cp = "hi";
|
|
const char *cnil = nullptr;
|
|
const int *inil = nullptr;
|
|
using VoidF = void (*)();
|
|
VoidF fp = [] {}, fnil = nullptr;
|
|
volatile char vc;
|
|
volatile char *vcp = &vc;
|
|
volatile char *vcnil = nullptr;
|
|
const FormatArgImpl args_array[] = {
|
|
FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil),
|
|
FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
|
|
FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
|
|
};
|
|
auto args = absl::MakeConstSpan(args_array);
|
|
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args),
|
|
MatchesPointerString(&x));
|
|
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
|
|
MatchesPointerString(&x));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
|
|
MatchesPointerString(&x));
|
|
|
|
// const char*
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
|
|
MatchesPointerString(cp));
|
|
// null const int*
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
|
|
MatchesPointerString(nullptr));
|
|
// null const char*
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
|
|
MatchesPointerString(nullptr));
|
|
// nonconst char*
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
|
|
MatchesPointerString(mcp));
|
|
|
|
// function pointers
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
|
|
MatchesPointerString(reinterpret_cast<const void*>(fp)));
|
|
EXPECT_THAT(
|
|
FormatPack(UntypedFormatSpecImpl("%8$p"), args),
|
|
MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
|
|
|
|
// null function pointers
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
|
|
MatchesPointerString(nullptr));
|
|
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
|
|
MatchesPointerString(nullptr));
|
|
}
|
|
|
|
struct Cardinal {
|
|
enum Pos { k1 = 1, k2 = 2, k3 = 3 };
|
|
enum Neg { kM1 = -1, kM2 = -2, kM3 = -3 };
|
|
};
|
|
|
|
TEST_F(FormatConvertTest, Enum) {
|
|
const Cardinal::Pos k3 = Cardinal::k3;
|
|
const Cardinal::Neg km3 = Cardinal::kM3;
|
|
const FormatArgImpl args[] = {FormatArgImpl(k3), FormatArgImpl(km3)};
|
|
UntypedFormatSpecImpl format("%1$d");
|
|
UntypedFormatSpecImpl format2("%2$d");
|
|
EXPECT_EQ("3", FormatPack(format, absl::MakeSpan(args)));
|
|
EXPECT_EQ("-3", FormatPack(format2, absl::MakeSpan(args)));
|
|
}
|
|
|
|
template <typename T>
|
|
class TypedFormatConvertTest : public FormatConvertTest { };
|
|
|
|
TYPED_TEST_SUITE_P(TypedFormatConvertTest);
|
|
|
|
std::vector<std::string> AllFlagCombinations() {
|
|
const char kFlags[] = {'-', '#', '0', '+', ' '};
|
|
std::vector<std::string> result;
|
|
for (size_t fsi = 0; fsi < (1ull << ArraySize(kFlags)); ++fsi) {
|
|
std::string flag_set;
|
|
for (size_t fi = 0; fi < ArraySize(kFlags); ++fi)
|
|
if (fsi & (1ull << fi))
|
|
flag_set += kFlags[fi];
|
|
result.push_back(flag_set);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
|
|
typedef TypeParam T;
|
|
typedef typename std::make_unsigned<T>::type UnsignedT;
|
|
using remove_volatile_t = typename std::remove_volatile<T>::type;
|
|
const T kMin = std::numeric_limits<remove_volatile_t>::min();
|
|
const T kMax = std::numeric_limits<remove_volatile_t>::max();
|
|
const T kVals[] = {
|
|
remove_volatile_t(1),
|
|
remove_volatile_t(2),
|
|
remove_volatile_t(3),
|
|
remove_volatile_t(123),
|
|
remove_volatile_t(-1),
|
|
remove_volatile_t(-2),
|
|
remove_volatile_t(-3),
|
|
remove_volatile_t(-123),
|
|
remove_volatile_t(0),
|
|
kMax - remove_volatile_t(1),
|
|
kMax,
|
|
kMin + remove_volatile_t(1),
|
|
kMin,
|
|
};
|
|
const char kConvChars[] = {'d', 'i', 'u', 'o', 'x', 'X'};
|
|
const std::string kWid[] = {"", "4", "10"};
|
|
const std::string kPrec[] = {"", ".", ".0", ".4", ".10"};
|
|
|
|
const std::vector<std::string> flag_sets = AllFlagCombinations();
|
|
|
|
for (size_t vi = 0; vi < ArraySize(kVals); ++vi) {
|
|
const T val = kVals[vi];
|
|
SCOPED_TRACE(Esc(val));
|
|
const FormatArgImpl args[] = {FormatArgImpl(val)};
|
|
for (size_t ci = 0; ci < ArraySize(kConvChars); ++ci) {
|
|
const char conv_char = kConvChars[ci];
|
|
for (size_t fsi = 0; fsi < flag_sets.size(); ++fsi) {
|
|
const std::string &flag_set = flag_sets[fsi];
|
|
for (size_t wi = 0; wi < ArraySize(kWid); ++wi) {
|
|
const std::string &wid = kWid[wi];
|
|
for (size_t pi = 0; pi < ArraySize(kPrec); ++pi) {
|
|
const std::string &prec = kPrec[pi];
|
|
|
|
const bool is_signed_conv = (conv_char == 'd' || conv_char == 'i');
|
|
const bool is_unsigned_to_signed =
|
|
!std::is_signed<T>::value && is_signed_conv;
|
|
// Don't consider sign-related flags '+' and ' ' when doing
|
|
// unsigned to signed conversions.
|
|
if (is_unsigned_to_signed &&
|
|
flag_set.find_first_of("+ ") != std::string::npos) {
|
|
continue;
|
|
}
|
|
|
|
std::string new_fmt("%");
|
|
new_fmt += flag_set;
|
|
new_fmt += wid;
|
|
new_fmt += prec;
|
|
// old and new always agree up to here.
|
|
std::string old_fmt = new_fmt;
|
|
new_fmt += conv_char;
|
|
std::string old_result;
|
|
if (is_unsigned_to_signed) {
|
|
// don't expect agreement on unsigned formatted as signed,
|
|
// as printf can't do that conversion properly. For those
|
|
// cases, we do expect agreement with printf with a "%u"
|
|
// and the unsigned equivalent of 'val'.
|
|
UnsignedT uval = val;
|
|
old_fmt += LengthModFor(uval);
|
|
old_fmt += "u";
|
|
old_result = StrPrint(old_fmt.c_str(), uval);
|
|
} else {
|
|
old_fmt += LengthModFor(val);
|
|
old_fmt += conv_char;
|
|
old_result = StrPrint(old_fmt.c_str(), val);
|
|
}
|
|
|
|
SCOPED_TRACE(std::string() + " old_fmt: \"" + old_fmt +
|
|
"\"'"
|
|
" new_fmt: \"" +
|
|
new_fmt + "\"");
|
|
UntypedFormatSpecImpl format(new_fmt);
|
|
EXPECT_EQ(old_result, FormatPack(format, absl::MakeSpan(args)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TYPED_TEST_P(TypedFormatConvertTest, Char) {
|
|
typedef TypeParam T;
|
|
using remove_volatile_t = typename std::remove_volatile<T>::type;
|
|
static const T kMin = std::numeric_limits<remove_volatile_t>::min();
|
|
static const T kMax = std::numeric_limits<remove_volatile_t>::max();
|
|
T kVals[] = {
|
|
remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10),
|
|
remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10),
|
|
remove_volatile_t(0),
|
|
kMin + remove_volatile_t(1), kMin,
|
|
kMax - remove_volatile_t(1), kMax
|
|
};
|
|
for (const T &c : kVals) {
|
|
const FormatArgImpl args[] = {FormatArgImpl(c)};
|
|
UntypedFormatSpecImpl format("%c");
|
|
EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args)));
|
|
}
|
|
}
|
|
|
|
REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);
|
|
|
|
typedef ::testing::Types<
|
|
int, unsigned, volatile int,
|
|
short, unsigned short,
|
|
long, unsigned long,
|
|
long long, unsigned long long,
|
|
signed char, unsigned char, char>
|
|
AllIntTypes;
|
|
INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
|
|
TypedFormatConvertTest, AllIntTypes);
|
|
TEST_F(FormatConvertTest, VectorBool) {
|
|
// Make sure vector<bool>'s values behave as bools.
|
|
std::vector<bool> v = {true, false};
|
|
const std::vector<bool> cv = {true, false};
|
|
EXPECT_EQ("1,0,1,0",
|
|
FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"),
|
|
absl::Span<const FormatArgImpl>(
|
|
{FormatArgImpl(v[0]), FormatArgImpl(v[1]),
|
|
FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
|
|
}
|
|
|
|
|
|
TEST_F(FormatConvertTest, Int128) {
|
|
absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979;
|
|
absl::int128 negative = -positive;
|
|
absl::int128 max = absl::Int128Max(), min = absl::Int128Min();
|
|
const FormatArgImpl args[] = {FormatArgImpl(positive),
|
|
FormatArgImpl(negative), FormatArgImpl(max),
|
|
FormatArgImpl(min)};
|
|
|
|
struct Case {
|
|
const char* format;
|
|
const char* expected;
|
|
} cases[] = {
|
|
{"%1$d", "2595989796776606496405"},
|
|
{"%1$30d", " 2595989796776606496405"},
|
|
{"%1$-30d", "2595989796776606496405 "},
|
|
{"%1$u", "2595989796776606496405"},
|
|
{"%1$x", "8cba9876066020f695"},
|
|
{"%2$d", "-2595989796776606496405"},
|
|
{"%2$30d", " -2595989796776606496405"},
|
|
{"%2$-30d", "-2595989796776606496405 "},
|
|
{"%2$u", "340282366920938460867384810655161715051"},
|
|
{"%2$x", "ffffffffffffff73456789f99fdf096b"},
|
|
{"%3$d", "170141183460469231731687303715884105727"},
|
|
{"%3$u", "170141183460469231731687303715884105727"},
|
|
{"%3$x", "7fffffffffffffffffffffffffffffff"},
|
|
{"%4$d", "-170141183460469231731687303715884105728"},
|
|
{"%4$x", "80000000000000000000000000000000"},
|
|
};
|
|
|
|
for (auto c : cases) {
|
|
UntypedFormatSpecImpl format(c.format);
|
|
EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
|
|
}
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, Uint128) {
|
|
absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
|
|
absl::uint128 max = absl::Uint128Max();
|
|
const FormatArgImpl args[] = {FormatArgImpl(v), FormatArgImpl(max)};
|
|
|
|
struct Case {
|
|
const char* format;
|
|
const char* expected;
|
|
} cases[] = {
|
|
{"%1$d", "2595989796776606496405"},
|
|
{"%1$30d", " 2595989796776606496405"},
|
|
{"%1$-30d", "2595989796776606496405 "},
|
|
{"%1$u", "2595989796776606496405"},
|
|
{"%1$x", "8cba9876066020f695"},
|
|
{"%2$d", "340282366920938463463374607431768211455"},
|
|
{"%2$u", "340282366920938463463374607431768211455"},
|
|
{"%2$x", "ffffffffffffffffffffffffffffffff"},
|
|
};
|
|
|
|
for (auto c : cases) {
|
|
UntypedFormatSpecImpl format(c.format);
|
|
EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
|
|
}
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, Float) {
|
|
#ifdef _MSC_VER
|
|
// MSVC has a different rounding policy than us so we can't test our
|
|
// implementation against the native one there.
|
|
return;
|
|
#endif // _MSC_VER
|
|
|
|
const char *const kFormats[] = {
|
|
"%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03",
|
|
"%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"};
|
|
|
|
std::vector<double> doubles = {0.0,
|
|
-0.0,
|
|
.99999999999999,
|
|
99999999999999.,
|
|
std::numeric_limits<double>::max(),
|
|
-std::numeric_limits<double>::max(),
|
|
std::numeric_limits<double>::min(),
|
|
-std::numeric_limits<double>::min(),
|
|
std::numeric_limits<double>::lowest(),
|
|
-std::numeric_limits<double>::lowest(),
|
|
std::numeric_limits<double>::epsilon(),
|
|
std::numeric_limits<double>::epsilon() + 1,
|
|
std::numeric_limits<double>::infinity(),
|
|
-std::numeric_limits<double>::infinity()};
|
|
|
|
// Some regression tests.
|
|
doubles.push_back(0.99999999999999989);
|
|
|
|
if (std::numeric_limits<double>::has_denorm != std::denorm_absent) {
|
|
doubles.push_back(std::numeric_limits<double>::denorm_min());
|
|
doubles.push_back(-std::numeric_limits<double>::denorm_min());
|
|
}
|
|
|
|
for (double base :
|
|
{1., 12., 123., 1234., 12345., 123456., 1234567., 12345678., 123456789.,
|
|
1234567890., 12345678901., 123456789012., 1234567890123.}) {
|
|
for (int exp = -123; exp <= 123; ++exp) {
|
|
for (int sign : {1, -1}) {
|
|
doubles.push_back(sign * std::ldexp(base, exp));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Workaround libc bug.
|
|
// https://sourceware.org/bugzilla/show_bug.cgi?id=22142
|
|
const bool gcc_bug_22142 =
|
|
StrPrint("%f", std::numeric_limits<double>::max()) !=
|
|
"1797693134862315708145274237317043567980705675258449965989174768031"
|
|
"5726078002853876058955863276687817154045895351438246423432132688946"
|
|
"4182768467546703537516986049910576551282076245490090389328944075868"
|
|
"5084551339423045832369032229481658085593321233482747978262041447231"
|
|
"68738177180919299881250404026184124858368.000000";
|
|
|
|
if (!gcc_bug_22142) {
|
|
for (int exp = -300; exp <= 300; ++exp) {
|
|
const double all_ones_mantissa = 0x1fffffffffffff;
|
|
doubles.push_back(std::ldexp(all_ones_mantissa, exp));
|
|
}
|
|
}
|
|
|
|
if (gcc_bug_22142) {
|
|
for (auto &d : doubles) {
|
|
using L = std::numeric_limits<double>;
|
|
double d2 = std::abs(d);
|
|
if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) {
|
|
d = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove duplicates to speed up the logic below.
|
|
std::sort(doubles.begin(), doubles.end());
|
|
doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
|
|
|
|
#ifndef __APPLE__
|
|
// Apple formats NaN differently (+nan) vs. (nan)
|
|
doubles.push_back(std::nan(""));
|
|
#endif
|
|
|
|
// Reserve the space to ensure we don't allocate memory in the output itself.
|
|
std::string str_format_result;
|
|
str_format_result.reserve(1 << 20);
|
|
std::string string_printf_result;
|
|
string_printf_result.reserve(1 << 20);
|
|
|
|
for (const char *fmt : kFormats) {
|
|
for (char f : {'f', 'F', //
|
|
'g', 'G', //
|
|
'a', 'A', //
|
|
'e', 'E'}) {
|
|
std::string fmt_str = std::string(fmt) + f;
|
|
|
|
if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
|
|
// This particular test takes way too long with snprintf.
|
|
// Disable for the case we are not implementing natively.
|
|
continue;
|
|
}
|
|
|
|
for (double d : doubles) {
|
|
int i = -10;
|
|
FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
|
|
UntypedFormatSpecImpl format(fmt_str);
|
|
|
|
string_printf_result.clear();
|
|
StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
|
|
str_format_result.clear();
|
|
|
|
{
|
|
AppendPack(&str_format_result, format, absl::MakeSpan(args));
|
|
}
|
|
|
|
if (string_printf_result != str_format_result) {
|
|
// We use ASSERT_EQ here because failures are usually correlated and a
|
|
// bug would print way too many failed expectations causing the test
|
|
// to time out.
|
|
ASSERT_EQ(string_printf_result, str_format_result)
|
|
<< fmt_str << " " << StrPrint("%.18g", d) << " "
|
|
<< StrPrint("%a", d) << " " << StrPrint("%.1080f", d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, FloatRound) {
|
|
std::string s;
|
|
const auto format = [&](const char *fmt, double d) -> std::string & {
|
|
s.clear();
|
|
FormatArgImpl args[1] = {FormatArgImpl(d)};
|
|
AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
|
|
#if !defined(_MSC_VER)
|
|
// MSVC has a different rounding policy than us so we can't test our
|
|
// implementation against the native one there.
|
|
EXPECT_EQ(StrPrint(fmt, d), s);
|
|
#endif // _MSC_VER
|
|
|
|
return s;
|
|
};
|
|
// All of these values have to be exactly represented.
|
|
// Otherwise we might not be testing what we think we are testing.
|
|
|
|
// These values can fit in a 64bit "fast" representation.
|
|
const double exact_value = 0.00000000000005684341886080801486968994140625;
|
|
assert(exact_value == std::pow(2, -44));
|
|
// Round up at a 5xx.
|
|
EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001");
|
|
// Round up at a >5
|
|
EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006");
|
|
// Round down at a <5
|
|
EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568");
|
|
// Nine handling
|
|
EXPECT_EQ(format("%.35f", exact_value),
|
|
"0.00000000000005684341886080801486969");
|
|
EXPECT_EQ(format("%.36f", exact_value),
|
|
"0.000000000000056843418860808014869690");
|
|
// Round down the last nine.
|
|
EXPECT_EQ(format("%.37f", exact_value),
|
|
"0.0000000000000568434188608080148696899");
|
|
EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147");
|
|
// Round up the last nine
|
|
EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470");
|
|
EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697");
|
|
|
|
// Round to even (down)
|
|
EXPECT_EQ(format("%.43f", exact_value),
|
|
"0.0000000000000568434188608080148696899414062");
|
|
// Exact
|
|
EXPECT_EQ(format("%.44f", exact_value),
|
|
"0.00000000000005684341886080801486968994140625");
|
|
// Round to even (up), let make the last digits 75 instead of 25
|
|
EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)),
|
|
"0.0000000000001705302565824240446090698242188");
|
|
// Exact, just to check.
|
|
EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)),
|
|
"0.00000000000017053025658242404460906982421875");
|
|
|
|
// This value has to be small enough that it won't fit in the uint128
|
|
// representation for printing.
|
|
const double small_exact_value =
|
|
0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625; // NOLINT
|
|
assert(small_exact_value == std::pow(2, -120));
|
|
// Round up at a 5xx.
|
|
EXPECT_EQ(format("%.37f", small_exact_value),
|
|
"0.0000000000000000000000000000000000008");
|
|
// Round down at a <5
|
|
EXPECT_EQ(format("%.38f", small_exact_value),
|
|
"0.00000000000000000000000000000000000075");
|
|
// Round up at a >5
|
|
EXPECT_EQ(format("%.41f", small_exact_value),
|
|
"0.00000000000000000000000000000000000075232");
|
|
// Nine handling
|
|
EXPECT_EQ(format("%.55f", small_exact_value),
|
|
"0.0000000000000000000000000000000000007523163845262640051");
|
|
EXPECT_EQ(format("%.56f", small_exact_value),
|
|
"0.00000000000000000000000000000000000075231638452626400510");
|
|
EXPECT_EQ(format("%.57f", small_exact_value),
|
|
"0.000000000000000000000000000000000000752316384526264005100");
|
|
EXPECT_EQ(format("%.58f", small_exact_value),
|
|
"0.0000000000000000000000000000000000007523163845262640051000");
|
|
// Round down the last nine
|
|
EXPECT_EQ(format("%.59f", small_exact_value),
|
|
"0.00000000000000000000000000000000000075231638452626400509999");
|
|
// Round up the last nine
|
|
EXPECT_EQ(format("%.79f", small_exact_value),
|
|
"0.000000000000000000000000000000000000"
|
|
"7523163845262640050999913838222372338039460");
|
|
|
|
// Round to even (down)
|
|
EXPECT_EQ(format("%.119f", small_exact_value),
|
|
"0.000000000000000000000000000000000000"
|
|
"75231638452626400509999138382223723380"
|
|
"394595633413601376560109201818704605102539062");
|
|
// Exact
|
|
EXPECT_EQ(format("%.120f", small_exact_value),
|
|
"0.000000000000000000000000000000000000"
|
|
"75231638452626400509999138382223723380"
|
|
"3945956334136013765601092018187046051025390625");
|
|
// Round to even (up), let make the last digits 75 instead of 25
|
|
EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)),
|
|
"0.000000000000000000000000000000000002"
|
|
"25694915357879201529997415146671170141"
|
|
"183786900240804129680327605456113815307617188");
|
|
// Exact, just to check.
|
|
EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)),
|
|
"0.000000000000000000000000000000000002"
|
|
"25694915357879201529997415146671170141"
|
|
"1837869002408041296803276054561138153076171875");
|
|
}
|
|
|
|
// We don't actually store the results. This is just to exercise the rest of the
|
|
// machinery.
|
|
struct NullSink {
|
|
friend void AbslFormatFlush(NullSink *sink, string_view str) {}
|
|
};
|
|
|
|
template <typename... T>
|
|
bool FormatWithNullSink(absl::string_view fmt, const T &... a) {
|
|
NullSink sink;
|
|
FormatArgImpl args[] = {FormatArgImpl(a)...};
|
|
return FormatUntyped(&sink, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args));
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, ExtremeWidthPrecision) {
|
|
for (const char *fmt : {"f"}) {
|
|
for (double d : {1e-100, 1.0, 1e100}) {
|
|
constexpr int max = std::numeric_limits<int>::max();
|
|
EXPECT_TRUE(FormatWithNullSink(std::string("%.*") + fmt, max, d));
|
|
EXPECT_TRUE(FormatWithNullSink(std::string("%1.*") + fmt, max, d));
|
|
EXPECT_TRUE(FormatWithNullSink(std::string("%*") + fmt, max, d));
|
|
EXPECT_TRUE(FormatWithNullSink(std::string("%*.*") + fmt, max, max, d));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, LongDouble) {
|
|
#ifdef _MSC_VER
|
|
// MSVC has a different rounding policy than us so we can't test our
|
|
// implementation against the native one there.
|
|
return;
|
|
#endif // _MSC_VER
|
|
const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000",
|
|
"%.60", "%+", "% ", "%-10"};
|
|
|
|
std::vector<long double> doubles = {
|
|
0.0,
|
|
-0.0,
|
|
std::numeric_limits<long double>::max(),
|
|
-std::numeric_limits<long double>::max(),
|
|
std::numeric_limits<long double>::min(),
|
|
-std::numeric_limits<long double>::min(),
|
|
std::numeric_limits<long double>::infinity(),
|
|
-std::numeric_limits<long double>::infinity()};
|
|
|
|
for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L,
|
|
1234567.L, 12345678.L, 123456789.L, 1234567890.L,
|
|
12345678901.L, 123456789012.L, 1234567890123.L,
|
|
// This value is not representable in double, but it
|
|
// is in long double that uses the extended format.
|
|
// This is to verify that we are not truncating the
|
|
// value mistakenly through a double.
|
|
10000000000000000.25L}) {
|
|
for (int exp : {-1000, -500, 0, 500, 1000}) {
|
|
for (int sign : {1, -1}) {
|
|
doubles.push_back(sign * std::ldexp(base, exp));
|
|
doubles.push_back(sign / std::ldexp(base, exp));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Regression tests
|
|
//
|
|
// Using a string literal because not all platforms support hex literals or it
|
|
// might be out of range.
|
|
doubles.push_back(std::strtold("-0xf.ffffffb5feafffbp-16324L", nullptr));
|
|
|
|
for (const char *fmt : kFormats) {
|
|
for (char f : {'f', 'F', //
|
|
'g', 'G', //
|
|
'a', 'A', //
|
|
'e', 'E'}) {
|
|
std::string fmt_str = std::string(fmt) + 'L' + f;
|
|
|
|
if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') {
|
|
// This particular test takes way too long with snprintf.
|
|
// Disable for the case we are not implementing natively.
|
|
continue;
|
|
}
|
|
|
|
for (auto d : doubles) {
|
|
FormatArgImpl arg(d);
|
|
UntypedFormatSpecImpl format(fmt_str);
|
|
// We use ASSERT_EQ here because failures are usually correlated and a
|
|
// bug would print way too many failed expectations causing the test to
|
|
// time out.
|
|
ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1}))
|
|
<< fmt_str << " " << StrPrint("%.18Lg", d) << " "
|
|
<< StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, IntAsFloat) {
|
|
const int kMin = std::numeric_limits<int>::min();
|
|
const int kMax = std::numeric_limits<int>::max();
|
|
const int ia[] = {
|
|
1, 2, 3, 123,
|
|
-1, -2, -3, -123,
|
|
0, kMax - 1, kMax, kMin + 1, kMin };
|
|
for (const int fx : ia) {
|
|
SCOPED_TRACE(fx);
|
|
const FormatArgImpl args[] = {FormatArgImpl(fx)};
|
|
struct Expectation {
|
|
int line;
|
|
std::string out;
|
|
const char *fmt;
|
|
};
|
|
const double dx = static_cast<double>(fx);
|
|
const Expectation kExpect[] = {
|
|
{ __LINE__, StrPrint("%f", dx), "%f" },
|
|
{ __LINE__, StrPrint("%12f", dx), "%12f" },
|
|
{ __LINE__, StrPrint("%.12f", dx), "%.12f" },
|
|
{ __LINE__, StrPrint("%12a", dx), "%12a" },
|
|
{ __LINE__, StrPrint("%.12a", dx), "%.12a" },
|
|
};
|
|
for (const Expectation &e : kExpect) {
|
|
SCOPED_TRACE(e.line);
|
|
SCOPED_TRACE(e.fmt);
|
|
UntypedFormatSpecImpl format(e.fmt);
|
|
EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args)));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
bool FormatFails(const char* test_format, T value) {
|
|
std::string format_string = std::string("<<") + test_format + ">>";
|
|
UntypedFormatSpecImpl format(format_string);
|
|
|
|
int one = 1;
|
|
const FormatArgImpl args[] = {FormatArgImpl(value), FormatArgImpl(one)};
|
|
EXPECT_EQ(FormatPack(format, absl::MakeSpan(args)), "")
|
|
<< "format=" << test_format << " value=" << value;
|
|
return FormatPack(format, absl::MakeSpan(args)).empty();
|
|
}
|
|
|
|
TEST_F(FormatConvertTest, ExpectedFailures) {
|
|
// Int input
|
|
EXPECT_TRUE(FormatFails("%p", 1));
|
|
EXPECT_TRUE(FormatFails("%s", 1));
|
|
EXPECT_TRUE(FormatFails("%n", 1));
|
|
|
|
// Double input
|
|
EXPECT_TRUE(FormatFails("%p", 1.));
|
|
EXPECT_TRUE(FormatFails("%s", 1.));
|
|
EXPECT_TRUE(FormatFails("%n", 1.));
|
|
EXPECT_TRUE(FormatFails("%c", 1.));
|
|
EXPECT_TRUE(FormatFails("%d", 1.));
|
|
EXPECT_TRUE(FormatFails("%x", 1.));
|
|
EXPECT_TRUE(FormatFails("%*d", 1.));
|
|
|
|
// String input
|
|
EXPECT_TRUE(FormatFails("%n", ""));
|
|
EXPECT_TRUE(FormatFails("%c", ""));
|
|
EXPECT_TRUE(FormatFails("%d", ""));
|
|
EXPECT_TRUE(FormatFails("%x", ""));
|
|
EXPECT_TRUE(FormatFails("%f", ""));
|
|
EXPECT_TRUE(FormatFails("%*d", ""));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace str_format_internal
|
|
ABSL_NAMESPACE_END
|
|
} // namespace absl
|