diff --git a/fmt/format.h b/fmt/format.h index 0f471cc4..1ea52b85 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -1004,6 +1004,7 @@ struct Value { void *formatter, const void *arg, void *format_str_ptr); struct CustomValue { + std::size_t size; const void *value; FormatFunc format; }; @@ -1303,6 +1304,7 @@ class MakeValue : public Arg { MakeValue(const T &value, typename EnableIf::value>::value, int>::type = 0) { + custom.size = sizeof(T); custom.value = &value; custom.format = &format_custom_arg; } @@ -1377,10 +1379,13 @@ class ArgList { }; internal::Arg::Type type(unsigned index) const { + return type(types_, index); + } + static internal::Arg::Type type(uint64_t types, unsigned index) { unsigned shift = index * 4; uint64_t mask = 0xf; return static_cast( - (types_ & (mask << shift)) >> shift); + (types & (mask << shift)) >> shift); } template @@ -1397,6 +1402,8 @@ class ArgList { ArgList(ULongLong types, const internal::Arg *args) : types_(types), args_(args) {} + uint64_t types() const { return types_; } + /** Returns the argument at specified index. */ internal::Arg operator[](unsigned index) const { using internal::Arg; @@ -1422,8 +1429,231 @@ class ArgList { } return args_[index]; } + + // Cross-thread serialization facility + + template + void serialize(std::vector& buffer) const; + template + static ArgList deserialize(std::vector& buffer); + +private: + template + static std::size_t calculate_extra_size(const internal::Arg& arg); + template + static void serialize_extra_data(uint8_t*& data_buffer, internal::Arg::Type type, const internal::Value& arg); + template + static void deserialize_extra_data(uint8_t*& data_buffer, internal::Arg::Type type, internal::Value& arg); }; +// Cross-thread serialization facility + +template +inline std::size_t ArgList::calculate_extra_size(const internal::Arg& arg) +{ + std::size_t result = 0; + + if (arg.type == internal::Arg::NAMED_ARG) + { + const fmt::internal::NamedArg* named = static_cast*>(arg.pointer); + result = sizeof(fmt::internal::NamedArg) + sizeof(std::size_t) + named->name.size() * sizeof(Char) + calculate_extra_size(*named); + } + else if (arg.type == internal::Arg::CSTRING) + result = sizeof(std::size_t) + (std::strlen(arg.string.value) + 1) * sizeof(char); + else if (arg.type == internal::Arg::STRING) + result = sizeof(std::size_t) + arg.string.size * sizeof(char); + else if (arg.type == internal::Arg::WSTRING) + result = sizeof(std::size_t) + arg.wstring.size * sizeof(wchar_t); + else if (arg.type == internal::Arg::CUSTOM) + result = arg.custom.size; + + return result; +} + +template +inline void ArgList::serialize_extra_data(uint8_t*& data_buffer, internal::Arg::Type type, const internal::Value& value) +{ + // Serialize extra data + if (type == internal::Arg::NAMED_ARG) { + const fmt::internal::NamedArg* named = static_cast*>(value.pointer); + std::memcpy(data_buffer, named, sizeof(fmt::internal::NamedArg)); + data_buffer += sizeof(fmt::internal::NamedArg); + std::size_t size = named->name.size() * sizeof(Char); + std::memcpy(data_buffer, &size, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + std::memcpy(data_buffer, named->name.data(), size); + data_buffer += size; + serialize_extra_data(data_buffer, named->type, *named); + } + else if (type == internal::Arg::CSTRING) { + std::size_t size = (std::strlen(value.string.value) + 1) * sizeof(char); + std::memcpy(data_buffer, &size, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + std::memcpy(data_buffer, value.string.value, size); + data_buffer += size; + } + else if (type == internal::Arg::STRING) { + std::size_t size = value.string.size * sizeof(char); + std::memcpy(data_buffer, &size, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + std::memcpy(data_buffer, value.string.value, size); + data_buffer += size; + } + else if (type == internal::Arg::WSTRING) { + std::size_t size = value.wstring.size * sizeof(wchar_t); + std::memcpy(data_buffer, &size, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + std::memcpy(data_buffer, value.wstring.value, size); + data_buffer += size; + } + else if (type == internal::Arg::CUSTOM) { + std::memcpy(data_buffer, value.custom.value, value.custom.size); + data_buffer += value.custom.size; + } +} + +template +inline void ArgList::deserialize_extra_data(uint8_t*& data_buffer, internal::Arg::Type type, internal::Value& value) +{ + // Deserialize extra data + if (type == internal::Arg::NAMED_ARG) { + fmt::internal::NamedArg* named = reinterpret_cast*>(data_buffer); + value.pointer = named; + data_buffer += sizeof(fmt::internal::NamedArg); + std::size_t size; + std::memcpy(&size, data_buffer, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + named->name = BasicStringRef(reinterpret_cast(data_buffer), size); + data_buffer += size; + deserialize_extra_data(data_buffer, named->type, *named); + } + else if (type == internal::Arg::CSTRING) { + std::size_t size; + std::memcpy(&size, data_buffer, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + value.string.value = reinterpret_cast(data_buffer); + data_buffer += size; + } + else if (type == internal::Arg::STRING) { + std::size_t size; + std::memcpy(&size, data_buffer, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + value.string.value = reinterpret_cast(data_buffer); + value.string.size = size / sizeof(char); + data_buffer += size; + } + else if (type == internal::Arg::WSTRING) { + std::size_t size; + std::memcpy(&size, data_buffer, sizeof(std::size_t)); + data_buffer += sizeof(std::size_t); + value.wstring.value = reinterpret_cast(data_buffer); + value.wstring.size = size / sizeof(wchar_t); + data_buffer += size; + } + else if (type == internal::Arg::CUSTOM) { + value.custom.value = data_buffer; + data_buffer += value.custom.size; + } +} + +template +inline void ArgList::serialize(std::vector& buffer) const +{ + const ArgList& args = *this; + + // Calculate the count of format arguments + unsigned count = 1; + while (args[count - 1].type != internal::Arg::NONE) + ++count; + + // Special check for none format arguments + if (count == 1) + return; + + // Caclulate base & full buffer sizes + std::size_t item_size = (count > MAX_PACKED_ARGS) ? sizeof(internal::Arg) : sizeof(internal::Value); + std::size_t base_size = sizeof(count) + sizeof(std::size_t) + sizeof(ULongLong) + count * item_size; + std::size_t full_size = base_size; + for (unsigned i = 0; i < count; ++i) + full_size += calculate_extra_size(args[i]); + + // Resize buffer to fit all format arguments + buffer.resize(full_size); + + uint8_t* base_buffer = buffer.data(); + uint8_t* data_buffer = base_buffer + base_size; + + // Serialize the count of format arguments + std::memcpy(base_buffer, &count, sizeof(unsigned)); + base_buffer += sizeof(unsigned); + + // Serialize the base buffer size + std::memcpy(base_buffer, &base_size, sizeof(std::size_t)); + base_buffer += sizeof(std::size_t); + + // Serialize types of format arguments + ULongLong types = args.types(); + std::memcpy(base_buffer, &types, sizeof(ULongLong)); + base_buffer += sizeof(ULongLong); + + // Serialize values of format arguments + for (unsigned i = 0; i < count; ++i) { + // Serialize argument + internal::Arg arg = args[i]; + std::memcpy(base_buffer, &arg, item_size); + base_buffer += item_size; + // Serialize extra data + serialize_extra_data(data_buffer, arg.type, arg); + } +} + +template +inline ArgList ArgList::deserialize(std::vector& buffer) +{ + // Special check for empty format arguments list + if (buffer.empty()) + return ArgList(); + + uint8_t* base_buffer = buffer.data(); + uint8_t* data_buffer = base_buffer; + + // Deserialize the count of format arguments + unsigned count; + std::memcpy(&count, base_buffer, sizeof(unsigned)); + base_buffer += sizeof(unsigned); + + // Deserialize the base buffer size + std::size_t base_size; + std::memcpy(&base_size, base_buffer, sizeof(std::size_t)); + base_buffer += sizeof(std::size_t); + + // Update the data buffer offset + data_buffer += base_size; + + // Deserialize types of format arguments + ULongLong types; + std::memcpy(&types, base_buffer, sizeof(ULongLong)); + base_buffer += sizeof(ULongLong); + + // Calculate the item size + std::size_t item_size = (count > MAX_PACKED_ARGS) ? sizeof(internal::Arg) : sizeof(internal::Value); + + // Deserialize values of format arguments + uint8_t* local_buffer = base_buffer; + for (unsigned i = 0; i < count; ++i) { + // Deserialize argument + internal::Value* value = reinterpret_cast(local_buffer); + local_buffer += item_size; + // Deserialize extra data + deserialize_extra_data(data_buffer, type(types, i), *value); + } + + // Prepare and return arguments list stored in the provided buffer + return (count > MAX_PACKED_ARGS) ? + ArgList(types, reinterpret_cast(base_buffer)) : + ArgList(types, reinterpret_cast(base_buffer)); +} + #define FMT_DISPATCH(call) static_cast(this)->call /** diff --git a/test/format-test.cc b/test/format-test.cc index 6d246b93..8d841330 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1653,3 +1653,32 @@ FMT_VARIADIC(void, custom_format, const char *) TEST(FormatTest, CustomArgFormatter) { custom_format("{}", 42); } + +std::string serialize_deserialize(const char *format_str, fmt::ArgList args) { + std::vector buffer; + args.serialize(buffer); + args = args.deserialize(buffer); + std::string result = fmt::format(format_str, args); + buffer.clear(); + return result; +} +FMT_VARIADIC(std::string, serialize_deserialize, const char *) + +TEST(FormatTest, Serialization) { + EXPECT_EQ(serialize_deserialize("{0}, {1}, {2}", -1, 0, 1), "-1, 0, 1"); + EXPECT_EQ(serialize_deserialize("{0}, {1}, {2}", 'a', 'b', 'c'), "a, b, c"); + EXPECT_EQ(serialize_deserialize("{}, {}, {}", 'a', 'b', 'c'), "a, b, c"); + EXPECT_EQ(serialize_deserialize("{2}, {1}, {0}", 'a', 'b', 'c'), "c, b, a"); + EXPECT_EQ(serialize_deserialize("{0}{1}{0}", "abra", "cad"), "abracadabra"); + EXPECT_EQ(serialize_deserialize("{:<30}", "left aligned"), "left aligned "); + EXPECT_EQ(serialize_deserialize("{:>30}", "right aligned"), " right aligned"); + EXPECT_EQ(serialize_deserialize("{:^30}", "centered"), " centered "); + EXPECT_EQ(serialize_deserialize("{:*^30}", "centered"), "***********centered***********"); + EXPECT_EQ(serialize_deserialize("{:+f}; {:+f}", 3.14, -3.14), "+3.140000; -3.140000"); + EXPECT_EQ(serialize_deserialize("{: f}; {: f}", 3.14, -3.14), " 3.140000; -3.140000"); + EXPECT_EQ(serialize_deserialize("{:-f}; {:-f}", 3.14, -3.14), "3.140000; -3.140000"); + EXPECT_EQ(serialize_deserialize("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42), "int: 42; hex: 2a; oct: 52; bin: 101010"); + EXPECT_EQ(serialize_deserialize("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42), "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"); + EXPECT_EQ(serialize_deserialize("The date is {}", Date(2012, 12, 9)), "The date is 2012-12-9"); + EXPECT_EQ(serialize_deserialize("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)), "Elapsed time: 1.23 seconds"); +}