Append to the output vector through a position-tracking sink

The vector-returning to_cbor(), to_msgpack(), ... wrote through
output_vector_sink, which appended each write with push_back() or
insert(). A number's 1 to 8 payload bytes went through vector::insert(),
which libc++ and libstdc++ both run as an out-of-line insert-anywhere
routine; on integer arrays that call cost more than encoding the number,
so the devirtualization of #5286 brought nothing there, and with GCC the
writer was slower than in 3.12.0.

output_vector_sink now keeps its own write position: it grows the vector
geometrically ahead of the position (resize(), using any capacity the
vector already has), copies each write there, and shrinks the vector to
the written length in its destructor, also when a write throws. Writes of
256 characters or more that do not fit are appended with insert(), so a
large string or binary value is copied once instead of zero-filled and
copied. The sink is move-only, as it owns that trim.

output_vector_adapter (the to_*(j, vector) overloads) appends with
push_back()/insert() again, so a caller holding the adapter never sees
the space kept ahead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-14 13:47:36 +02:00
1 parent 7461a199b5
commit e26e1ad312
3 files changed
+257 -69

No files matched your search

+13 -1
View File
@@ -35,6 +35,17 @@ std::vector<json> test_values()
big_object[std::to_string(i)] = i;
}
// large strings and binary values between small ones: a write too large
// for the space the vector sink has ahead takes a different path than a
// small one, and the small writes after it must still land behind it
json large_payloads = json::array();
for (int i = 0; i < 50; ++i)
{
large_payloads.push_back(std::string(300, static_cast<char>('a' + (i % 26))));
large_payloads.push_back(i);
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
}
return
{
json(nullptr), json(true), json(false),
@@ -46,7 +57,8 @@ std::vector<json> test_values()
json::array(), json::object(),
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
big_array, big_object
big_array, big_object, large_payloads,
json::binary(std::vector<std::uint8_t>(5000, 0x42))
};
}