d85783fd0b
-- f34cd235a12ad0ee1fea3a1ee5a427272dc2b285 by Abseil Team <absl-team@google.com>: Migrates uses of deprecated map types to recommended types. PiperOrigin-RevId: 309945156 -- e3410a47ad32c0775b6911610bc47b22938decad by Matthew Brown <matthewbr@google.com>: Internal Change PiperOrigin-RevId: 309856021 -- a58cfa25e0bb59e7fa9647ac1aae65eaccff0086 by Greg Falcon <gfalcon@google.com>: Internal change. PiperOrigin-RevId: 309804612 -- cdc5ec310035fbe25f496bda283fe655d94d7769 by Mark Barolak <mbar@google.com>: Standardize the header comments for friend functions in cord.h PiperOrigin-RevId: 309779073 -- fe61602701be795e54477b0fdbf5ffc1df12a6b7 by Samuel Benzaquen <sbenza@google.com>: Implement %f natively for any input. It evaluates the input at runtime and allocates stack space accordingly. This removes a potential fallback into snprintf, improves performance, and removes all memory allocations in this formatting path. PiperOrigin-RevId: 309752501 -- 79e2a24f3f959e8b06ddf1d440bbabbd5f89b5b7 by Greg Falcon <gfalcon@google.com>: Add a Cord::swap() method. Many other Abseil types already provide this, but it was missing here. We already provided a two-argument free function form of `swap()`, but that API is better suited for generic code. The swap member function is a better API when the types are known. PiperOrigin-RevId: 309751740 -- 85cdf60024f153fb4fcb7fe68ed2b14b9faf119d by Derek Mauro <dmauro@google.com>: Cleanup uses of "linker initialized" SpinLocks PiperOrigin-RevId: 309581867 -- 9e5443bfcec4b94056b13c75326576e987ab88fb by Matt Kulukundis <kfm@google.com>: Clarify intended mixing properties of `absl::Hash` PiperOrigin-RevId: 309520174 -- a0630f0827b67f217aaeae68a448fe4c1101e17d by Greg Falcon <gfalcon@google.com>: Comment out a test in Emscripten to sidestep `long double` issues. PiperOrigin-RevId: 309482953 GitOrigin-RevId: f34cd235a12ad0ee1fea3a1ee5a427272dc2b285 Change-Id: Icce0c9d547117374d596b9d684e4054ddd118669
840 lines
30 KiB
C++
840 lines
30 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");
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|