From 1fc4d2afb31271f382e4218ffd451bc2161fdab3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 23 Oct 2026 06:45:13 +0200 Subject: [PATCH] Clarify the DOM parser's collection stacks and rename reenter_enclosing_container() Address review comments: explain why a closed container erases only the top of the shared stack, set the payload before the type, rename the helper that makes the enclosing container current again, and declare the parser's state stack right before reserving it. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/input/json_sax.hpp | 20 ++++++++++------- include/nlohmann/detail/input/parser.hpp | 6 ++--- single_include/nlohmann/json.hpp | 26 +++++++++++++--------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index d1ab4f83a..db95685f0 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -569,16 +569,18 @@ class json_sax_dom_parser JSON_ASSERT(frames.back().object == nullptr); const frame f = frames.back(); frames.pop_back(); - enter_enclosing_container(); + reenter_enclosing_container(); if (f.array == nullptr) { - // the elements are on the stack: make them the array + // the elements are the top of the shared elements stack, from + // f.start on (the elements below belong to enclosing arrays that + // are still open): move them into the array and pop them const auto first = elements.begin() + static_cast(f.start); auto* const arr = BasicJsonType::template create(std::make_move_iterator(first), std::make_move_iterator(elements.end())); BasicJsonType value; - value.m_data.m_type = BasicJsonType::value_t::array; value.m_data.m_value.array = arr; + value.m_data.m_type = BasicJsonType::value_t::array; elements.erase(first, elements.end()); place_container(f, frames.empty() ? nullptr : &frames.back(), std::move(value), true); @@ -1033,13 +1035,15 @@ class json_sax_dom_parser return true; } - /// the members of an object that is collected on the stack as an object + /// the members of an object that is collected on the stack as an object; + /// they are the top of the shared member stack, from f.start on (the + /// members below belong to enclosing objects that are still open) object_t* members_to_object(const frame& f, BasicJsonType& value) { const auto first = ordered_data.members.begin() + static_cast(f.start); auto* const obj = BasicJsonType::template create(std::make_move_iterator(first), std::make_move_iterator(ordered_data.members.end())); - value.m_data.m_type = BasicJsonType::value_t::object; value.m_data.m_value.object = obj; + value.m_data.m_type = BasicJsonType::value_t::object; ordered_data.members.erase(first, ordered_data.members.end()); return obj; } @@ -1073,7 +1077,7 @@ class json_sax_dom_parser // the members are on the stack: make them the object const frame f = frames.back(); frames.pop_back(); - enter_enclosing_container(); + reenter_enclosing_container(); BasicJsonType value; members_to_object(f, value); @@ -1098,7 +1102,7 @@ class json_sax_dom_parser release_key_index(frames.back(), std::integral_constant {}); frames.pop_back(); - enter_enclosing_container(); + reenter_enclosing_container(); return true; } @@ -1269,7 +1273,7 @@ class json_sax_dom_parser } /// after a container was closed: the one it was in is the one that gets the next value - void enter_enclosing_container() + void reenter_enclosing_container() { if (frames.empty()) { diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index bfa70b01c..3017a29fd 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -243,13 +243,13 @@ class parser JSON_HEDLEY_NON_NULL(2) bool sax_parse_internal(SAX* sax) { + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + // stack to remember the hierarchy of structured values we are parsing // true = array; false = object (bytes, not std::vector, whose // push_back() is a call with GCC) std::vector states; - // value to avoid a goto (see comment where set to true) - bool skip_to_state_evaluation = false; - // a document that starts with a container pushes onto the stack at // once: allocate it in one step instead of growing it from 1 byte if (last_token == token_type::begin_object || last_token == token_type::begin_array) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 27f8b8a3c..f2b921cc0 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -15502,16 +15502,18 @@ class json_sax_dom_parser JSON_ASSERT(frames.back().object == nullptr); const frame f = frames.back(); frames.pop_back(); - enter_enclosing_container(); + reenter_enclosing_container(); if (f.array == nullptr) { - // the elements are on the stack: make them the array + // the elements are the top of the shared elements stack, from + // f.start on (the elements below belong to enclosing arrays that + // are still open): move them into the array and pop them const auto first = elements.begin() + static_cast(f.start); auto* const arr = BasicJsonType::template create(std::make_move_iterator(first), std::make_move_iterator(elements.end())); BasicJsonType value; - value.m_data.m_type = BasicJsonType::value_t::array; value.m_data.m_value.array = arr; + value.m_data.m_type = BasicJsonType::value_t::array; elements.erase(first, elements.end()); place_container(f, frames.empty() ? nullptr : &frames.back(), std::move(value), true); @@ -15966,13 +15968,15 @@ class json_sax_dom_parser return true; } - /// the members of an object that is collected on the stack as an object + /// the members of an object that is collected on the stack as an object; + /// they are the top of the shared member stack, from f.start on (the + /// members below belong to enclosing objects that are still open) object_t* members_to_object(const frame& f, BasicJsonType& value) { const auto first = ordered_data.members.begin() + static_cast(f.start); auto* const obj = BasicJsonType::template create(std::make_move_iterator(first), std::make_move_iterator(ordered_data.members.end())); - value.m_data.m_type = BasicJsonType::value_t::object; value.m_data.m_value.object = obj; + value.m_data.m_type = BasicJsonType::value_t::object; ordered_data.members.erase(first, ordered_data.members.end()); return obj; } @@ -16006,7 +16010,7 @@ class json_sax_dom_parser // the members are on the stack: make them the object const frame f = frames.back(); frames.pop_back(); - enter_enclosing_container(); + reenter_enclosing_container(); BasicJsonType value; members_to_object(f, value); @@ -16031,7 +16035,7 @@ class json_sax_dom_parser release_key_index(frames.back(), std::integral_constant {}); frames.pop_back(); - enter_enclosing_container(); + reenter_enclosing_container(); return true; } @@ -16202,7 +16206,7 @@ class json_sax_dom_parser } /// after a container was closed: the one it was in is the one that gets the next value - void enter_enclosing_container() + void reenter_enclosing_container() { if (frames.empty()) { @@ -22007,13 +22011,13 @@ class parser JSON_HEDLEY_NON_NULL(2) bool sax_parse_internal(SAX* sax) { + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + // stack to remember the hierarchy of structured values we are parsing // true = array; false = object (bytes, not std::vector, whose // push_back() is a call with GCC) std::vector states; - // value to avoid a goto (see comment where set to true) - bool skip_to_state_evaluation = false; - // a document that starts with a container pushes onto the stack at // once: allocate it in one step instead of growing it from 1 byte if (last_token == token_type::begin_object || last_token == token_type::begin_array)