mirror of
https://github.com/nlohmann/json.git
synced 2026-10-09 16:07:14 +00:00
Unflatten in time and memory linear in the pointer depth
#5443 made unflatten() decide between arrays and objects independently of the iteration order by collecting the pointer prefixes that have a reference token 0 below them in a std::set<std::vector<string_t>>. Every such prefix was stored as a copy of all its reference tokens, and get_and_create() compared whole prefix vectors at every step, so unflattening a pointer of depth d took time and memory quadratic in d: a 10,000-level array pointer took 18 s and 1.3 GB, a 100,000-level one did not finish. The prefixes are now numbered nodes of a tree, so each is stored once and get_and_create() follows the tree token by token. The result is unchanged, including its independence of the iteration order. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+129
-44
No files matched your search
@@ -969,21 +969,54 @@ TEST_CASE("flatten of structured values")
|
||||
CHECK(flat.begin().key() == path);
|
||||
CHECK(flat.begin().value() == 0);
|
||||
|
||||
// unflatten() is not iterative: it takes time and memory
|
||||
// quadratic in the depth, so it is only roundtripped for a
|
||||
// moderate depth
|
||||
std::string small_text;
|
||||
for (std::size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
small_text += objects ? "{\"a\":" : "[";
|
||||
}
|
||||
small_text += "0";
|
||||
small_text += std::string(500, objects ? '}' : ']');
|
||||
const auto small_value = json::parse(small_text);
|
||||
CHECK(small_value.flatten().unflatten() == small_value);
|
||||
// unflatten() is linear in the depth, so the value roundtrips
|
||||
CHECK(flat.unflatten() == value);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unflatten of a deeply nested pointer")
|
||||
{
|
||||
const std::size_t depth = 100000;
|
||||
for (const bool objects :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
CAPTURE(objects)
|
||||
std::string path;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
path += objects ? "/a" : "/0";
|
||||
}
|
||||
|
||||
json flat = json::object();
|
||||
flat[path] = 1;
|
||||
const json value = flat.unflatten();
|
||||
|
||||
// walk down iteratively
|
||||
std::size_t levels = 0;
|
||||
const json* current = &value;
|
||||
while (objects ? current->is_object() : current->is_array())
|
||||
{
|
||||
REQUIRE(current->size() == 1);
|
||||
current = objects ? ¤t->at("a") : ¤t->at(0);
|
||||
++levels;
|
||||
}
|
||||
CHECK(levels == depth);
|
||||
CHECK(*current == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("unflatten does not depend on the iteration order")
|
||||
{
|
||||
// the "0" key comes after its sibling in iteration order
|
||||
const nlohmann::ordered_json flat_array = nlohmann::ordered_json::parse(R"({"/a/1": 2, "/a/0": 1})");
|
||||
CHECK(flat_array.unflatten() == nlohmann::ordered_json::parse(R"({"a": [1, 2]})"));
|
||||
|
||||
const nlohmann::ordered_json flat_object = nlohmann::ordered_json::parse(R"({"/b/1": 2})");
|
||||
CHECK(flat_object.unflatten() == nlohmann::ordered_json::parse(R"({"b": {"1": 2}})"));
|
||||
}
|
||||
|
||||
SECTION("objects and arrays interleaved")
|
||||
{
|
||||
const json value =
|
||||
|
||||
Reference in new issue
Block a user