diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index d70bde43f..33ee4d086 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -951,6 +951,8 @@ class basic_json_document std::string arena(shrink_arena ? d.arena : std::string()); const bool shrink_tape = d.tape != d.inline_tape && d.tape_size != d.tape_cap; const bool into_header = d.tape_size <= d.inline_cap; + std::vector indexes(d.indexes.capacity() > d.indexes.size() ? d.indexes : std::vector()); + std::vector index_slots(d.index_slots.capacity() > d.index_slots.size() ? d.index_slots : std::vector()); node* fresh = (shrink_tape && !into_header) ? static_cast(::operator new (d.tape_size * sizeof(node))) : d.inline_tape; if (shrink_tape) @@ -965,6 +967,14 @@ class basic_json_document d.arena.swap(arena); d.base[1] = d.arena.data(); } + if (d.indexes.capacity() > d.indexes.size()) + { + d.indexes.swap(indexes); + } + if (d.index_slots.capacity() > d.index_slots.size()) + { + d.index_slots.swap(index_slots); + } } private: @@ -1019,9 +1029,11 @@ class basic_json_document d.base[0] = d.src; d.base[1] = d.arena.data(); detail::view::build_object_indexes(d); + std::vector().swap(d.large_objects); // (only needed while parsing) d.discarded = false; return; } + std::vector().swap(d.large_objects); if (allow_exceptions) { detail::view::throw_parse_failure(failure, src, size, comments, trailing_commas); diff --git a/tests/src/unit-json_view.cpp b/tests/src/unit-json_view.cpp index a4fb9dcba..1711275eb 100644 --- a/tests/src/unit-json_view.cpp +++ b/tests/src/unit-json_view.cpp @@ -1381,6 +1381,48 @@ TEST_CASE("json_view large objects") CHECK(without_collisions.memory_usage() >= with_collisions.memory_usage() + (slots * sizeof(std::uint32_t))); } + SECTION("shrink_to_fit releases the tables' spare capacity") + { + const auto make_text = [](int objects, int members) + { + std::string text = "["; + for (int object = 0; object < objects; ++object) + { + text += object != 0 ? ",{" : "{"; + for (int i = 0; i < members + object; ++i) + { + text += (i != 0 ? ",\"" : "\"") + std::to_string(i) + "\":" + std::to_string(i); + } + text += '}'; + } + return text + "]"; + }; + const std::string small_text = make_text(5, 150); + const std::string big_text = make_text(40, 400); + + // reading a big text, and then a small one, leaves the spare capacity + // of the big one: shrink_to_fit() brings the document to the size of + // one parsed from the small text alone + json_document d = json_document::parse(big_text); + const std::size_t big = d.memory_usage(); + d.read(small_text); + CHECK(d.memory_usage() >= big); + d.shrink_to_fit(); + json_document fresh = json_document::parse(small_text); + fresh.shrink_to_fit(); + CHECK(d.memory_usage() == fresh.memory_usage()); + CHECK(d.memory_usage() < big / 2); + CHECK(d.root() == json::parse(small_text)); + for (int object = 0; object < 5; ++object) + { + const json_view v = d.root()[static_cast(object)]; + for (int i = 0; i < 150 + object; ++i) + { + CHECK(v[std::to_string(i)].get() == i); + } + } + } + SECTION("nested, reused, and in arrays") { std::string inner = "{";