#include #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { template 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(v))) { return std::string(1, static_cast(v)); } char buf[64]; int n = snprintf(buf, sizeof(buf), "\\%#.2x", static_cast(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 std::string Esc(const T &v) { std::ostringstream oss; oss << v; return oss.str(); } void StrAppend(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; } std::string StrPrint(const char *format, ...) { va_list ap; va_start(ap, format); std::string result; StrAppend(&result, format, ap); va_end(ap); return result; } class FormatConvertTest : public ::testing::Test { }; template 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("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(fp))); EXPECT_THAT( FormatPack(UntypedFormatSpecImpl("%8$p"), args), MatchesPointerString(reinterpret_cast(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 class TypedFormatConvertTest : public FormatConvertTest { }; TYPED_TEST_SUITE_P(TypedFormatConvertTest); std::vector AllFlagCombinations() { const char kFlags[] = {'-', '#', '0', '+', ' '}; std::vector 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::type UnsignedT; using remove_volatile_t = typename std::remove_volatile::type; const T kMin = std::numeric_limits::min(); const T kMax = std::numeric_limits::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 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::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::type; static const T kMin = std::numeric_limits::min(); static const T kMax = std::numeric_limits::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's values behave as bools. std::vector v = {true, false}; const std::vector cv = {true, false}; EXPECT_EQ("1,0,1,0", FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"), absl::Span( {FormatArgImpl(v[0]), FormatArgImpl(v[1]), FormatArgImpl(cv[0]), FormatArgImpl(cv[1])}))); } TEST_F(FormatConvertTest, Int128) { absl::int128 positive = static_cast(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(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", "%9", "%.60", "%.30", "%03", "%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"}; std::vector doubles = {0.0, -0.0, .99999999999999, 99999999999999., std::numeric_limits::max(), -std::numeric_limits::max(), std::numeric_limits::min(), -std::numeric_limits::min(), std::numeric_limits::lowest(), -std::numeric_limits::lowest(), std::numeric_limits::epsilon(), std::numeric_limits::epsilon() + 1, std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; #ifndef __APPLE__ // Apple formats NaN differently (+nan) vs. (nan) doubles.push_back(std::nan("")); #endif // Some regression tests. doubles.push_back(0.99999999999999989); if (std::numeric_limits::has_denorm != std::denorm_absent) { doubles.push_back(std::numeric_limits::denorm_min()); doubles.push_back(-std::numeric_limits::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)); } } } for (const char *fmt : kFormats) { for (char f : {'f', 'F', // 'g', 'G', // 'a', 'A', // 'e', 'E'}) { std::string fmt_str = std::string(fmt) + f; for (double d : doubles) { int i = -10; FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)}; 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, i), FormatPack(format, absl::MakeSpan(args))) << fmt_str << " " << StrPrint("%.18g", d) << " " << StrPrint("%.999f", d); } } } } TEST_F(FormatConvertTest, LongDouble) { const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.60", "%+", "% ", "%-10"}; // 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. long double very_precise = 10000000000000000.25L; std::vector doubles = { 0.0, -0.0, very_precise, 1 / very_precise, std::numeric_limits::max(), -std::numeric_limits::max(), std::numeric_limits::min(), -std::numeric_limits::min(), std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; 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; 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("%.999Lf", d); } } } } TEST_F(FormatConvertTest, IntAsFloat) { const int kMin = std::numeric_limits::min(); const int kMax = std::numeric_limits::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(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 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