diff --git a/format.h b/format.h index 3bbf0c3ad009..5439435b2071 100644 --- a/format.h +++ b/format.h @@ -743,9 +743,7 @@ struct Value { StringValue wstring; CustomValue custom; }; -}; -struct Arg : Value { enum Type { NONE, // Integer types should go first, @@ -757,6 +755,8 @@ struct Arg : Value { Type type; }; +typedef struct Value Arg; // TODO: remove + template struct None {}; @@ -1058,58 +1058,52 @@ class RuntimeError : public std::runtime_error { template class ArgFormatter; - -/** - A type list utility class storing packed type data. - */ -class TypeList { - private: - const uint64_t *types_; - unsigned count_; - - public: - TypeList(const uint64_t *types, unsigned count) - : types_(types), count_(count) {} - - /** - Returns the argument type at specified index. - */ - Arg::Type operator[](unsigned index) const { - if (index >= count_) - return Arg::NONE; - unsigned shift = (index & 0xf) << 2; // (index % 16) * 4 - uint64_t mask = 0xf; - uint64_t type = (types_[index >> 4] >> shift) & mask; - return static_cast(type); - } -}; } // namespace internal -/** - An argument list. - */ +/** An argument list. */ class ArgList { private: - internal::TypeList types_; + // To reduce compiled code size per formatting function call, types of first + // MAX_PACKED_ARGS arguments are passed in the types_ field. + uint64_t types_; const internal::Value *values_; + internal::Arg::Type type(unsigned index) const { + unsigned shift = index * 4; + uint64_t mask = 0xf; + return static_cast( + (types_ & (mask << shift)) >> shift); + } + public: - ArgList() : types_(NULL, 0) {} - ArgList(const internal::TypeList &types, const internal::Value *values) + // Maximum number of arguments with packed types. + enum { MAX_PACKED_ARGS = 16 }; + + ArgList() : types_(0) {} + ArgList(ULongLong types, const internal::Value *values) : types_(types), values_(values) {} - /** - Returns the argument at specified index. - */ + /** Returns the argument at specified index. */ internal::Arg operator[](unsigned index) const { using internal::Arg; Arg arg; - Arg::Type type = types_[index]; - arg.type = type; - if (type != Arg::NONE) { + if (index >= MAX_PACKED_ARGS) { + if (type(MAX_PACKED_ARGS - 1) == Arg::NONE) { + arg.type = Arg::NONE; + return arg; + } + for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) { + if (values_[i].type == Arg::NONE) + return values_[i]; + } + return values_[index]; + } + Arg::Type arg_type = type(index); + if (arg_type != Arg::NONE) { internal::Value &value = arg; value = values_[index]; } + arg.type = arg_type; return arg; } }; @@ -1426,18 +1420,15 @@ inline StrFormatSpec pad( # define FMT_GEN15(f) FMT_GEN14(f), f(14) namespace internal { -inline void make_type(uint64_t* out, unsigned index) {} +inline uint64_t make_type() { return 0; } template -inline void make_type(uint64_t* out, unsigned index, const T &arg) { - out[index >> 4] |= MakeValue::type(arg) << ((index & 0xf) << 2); -} +inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } #if FMT_USE_VARIADIC_TEMPLATES template -inline void make_type(uint64_t* out, unsigned index, const Arg &first, const Args & ... tail) { - make_type(out, index, first); - make_type(out, index + 1, tail...); +inline uint64_t make_type(const Arg &first, const Args & ... tail) { + return make_type(first) | (make_type(tail...) << 4); } #else @@ -1447,13 +1438,13 @@ struct ArgType { ArgType() : type(0) {} template - ArgType(const T &arg) : type(0) { make_type(&type, 0, arg); } + ArgType(const T &arg) : type(make_type(arg)) {} }; # define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() -inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { - *out = t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | +inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { + return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | (t12.type << 48) | (t13.type << 52) | (t14.type << 56); @@ -1476,9 +1467,7 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_ fmt::internal::NonZero::VALUE] = { \ fmt::internal::MakeValue(args)... \ }; \ - uint64_t types[fmt::internal::NonZero<((sizeof...(Args)) + 15) / 16>::VALUE] = { }; \ - fmt::internal::make_type(types, 0, args...); \ - func(arg1, ArgList(fmt::internal::TypeList(types, sizeof...(Args)), values)); \ + func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ } // Defines a variadic constructor. @@ -1490,16 +1479,13 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_ fmt::internal::NonZero::VALUE] = { \ MakeValue(args)... \ }; \ - uint64_t types[fmt::internal::NonZero<((sizeof...(Args)) + 15) / 16>::VALUE] = {}; \ - fmt::internal::make_type(types, 0, args...); \ - func(arg0, arg1, ArgList(fmt::internal::TypeList(types, sizeof...(Args)), values)); \ + func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ } #else # define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) # define FMT_MAKE_REF2(n) v##n -# define FMT_MAKE_ZERO(n) 0 // Defines a wrapper for a function taking one argument of type arg_type // and n additional arguments of arbitrary types. @@ -1507,10 +1493,8 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_ template \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - uint64_t types = 0; \ - fmt::internal::make_type(&types, 0, FMT_GEN(n, FMT_MAKE_REF2)); \ func(arg1, fmt::ArgList( \ - fmt::internal::TypeList(&types, n), vals)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ } // Emulates a variadic function returning void on a pre-C++11 compiler. @@ -1526,10 +1510,8 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_ template \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - uint64_t types = 0; \ - fmt::internal::make_type(&types, 0, FMT_GEN(n, FMT_MAKE_REF2)); \ func(arg0, arg1, fmt::ArgList( \ - fmt::internal::TypeList(&types, n), vals)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ } // Emulates a variadic constructor on a pre-C++11 compiler. @@ -2622,18 +2604,33 @@ inline void format_decimal(char *&buffer, T value) { #define FMT_GET_ARG_NAME(type, index) arg##index #if FMT_USE_VARIADIC_TEMPLATES + +namespace fmt { +namespace internal { +inline void set_types(Value *) {} + +template +inline void set_types(Value *values, const T &arg, const Args & ... tail) { + values->type = static_cast(MakeValue::type(arg)); + set_types(values + 1, tail...); +} +} +} + # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ template \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ - using fmt::internal::Value; \ - const Value values[fmt::internal::NonZero::VALUE] = { \ + using fmt::internal::Arg; \ + Arg array[sizeof...(Args) + 1] = { \ fmt::internal::MakeValue(args)... \ }; \ - uint64_t types[fmt::internal::NonZero<((sizeof...(Args)) + 15) / 16>::VALUE] = {}; \ - fmt::internal::make_type(types, 0, args...); \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::TypeList(types, sizeof...(Args)), values)); \ + if (sizeof...(Args) > fmt::ArgList::MAX_PACKED_ARGS) { \ + set_types(array, args...); \ + array[sizeof...(Args)].type = Arg::NONE; \ + } \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ + fmt::ArgList(fmt::internal::make_type(args...), array)); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments @@ -2643,10 +2640,8 @@ inline void format_decimal(char *&buffer, T value) { inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ FMT_GEN(n, FMT_MAKE_ARG)) { \ const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ - uint64_t types = 0; \ - fmt::internal::make_type(&types, 0, FMT_GEN(n, FMT_MAKE_REF2)); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::TypeList(&types, n), vals)); \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ } # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ diff --git a/test/format-test.cc b/test/format-test.cc index 148526c23f9a..d3bb023b5281 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -551,6 +551,32 @@ TEST(FormatterTest, ArgErrors) { EXPECT_THROW_MSG(format(format_str), FormatError, "number is too big"); } +#if FMT_USE_VARIADIC_TEMPLATES +template +struct TestFormat { + template + static std::string format(fmt::StringRef format_str, const Args & ... args) { + return TestFormat::format(format_str, N - 1, args...); + } +}; + +template <> +struct TestFormat<0> { + template + static std::string format(fmt::StringRef format_str, const Args & ... args) { + return fmt::format(format_str, args...); + } +}; + +TEST(FormatterTest, ManyArgs) { + EXPECT_EQ("19", TestFormat<20>::format("{19}")); + EXPECT_THROW_MSG(TestFormat<20>::format("{20}"), + FormatError, "argument index out of range"); + EXPECT_THROW_MSG(TestFormat<21>::format("{21}"), + FormatError, "argument index out of range"); +} +#endif + TEST(FormatterTest, AutoArgIndex) { EXPECT_EQ("abc", format("{}{}{}", 'a', 'b', 'c')); EXPECT_THROW_MSG(format("{0}{}", 'a', 'b'), diff --git a/test/printf-test.cc b/test/printf-test.cc index 1cf1327199ec..f174577c63ef 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -436,26 +436,6 @@ TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); } -#if FMT_USE_VARIADIC_TEMPLATES -TEST(PrintfTest, ManyArgs) { - EXPECT_EQ("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 " - "21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 " - "41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 " - "61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 " - "81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99", - fmt::sprintf("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d " - "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d " - "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d " - "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d " - "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, - 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99)); -} -#endif - #if FMT_USE_FILE_DESCRIPTORS TEST(PrintfTest, Examples) { const char *weekday = "Thursday"; diff --git a/test/util-test.cc b/test/util-test.cc index 2a76b9c7634e..3bae27984d79 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -567,20 +567,6 @@ TEST(ArgTest, MakeArg) { EXPECT_EQ("test", w.str()); } -TEST(UtilTest, TypeList) { - uint64_t types[] = {0}; - int a; - char* b; - double c; - fmt::internal::make_type(types, 0, a, b, c); - - fmt::internal::TypeList typeList(types, 3); - EXPECT_EQ(typeList[0], Arg::INT); - EXPECT_EQ(typeList[1], Arg::CSTRING); - EXPECT_EQ(typeList[2], Arg::DOUBLE); - EXPECT_EQ(typeList[3], Arg::NONE); -} - TEST(UtilTest, ArgList) { fmt::ArgList args; EXPECT_EQ(Arg::NONE, args[1].type);