mirror of
https://github.com/nlohmann/json.git
synced 2026-10-24 06:48:55 +00:00
2.1.2: one iterative walker for CBOR and MessagePack (#5891)
write_cbor_iterative and write_msgpack_iterative (and their *_value_or_push helpers) were identical but for the head, scalar, and key writers. They are now one template over a small policy struct, so everything inlines. BON8 keeps its own walker (open string run, closing marker), noted in a comment. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
1 file changed
+78
-88
@@ -1005,19 +1005,72 @@ class binary_writer
|
|||||||
typename BasicJsonType::array_t::const_iterator array_it{};
|
typename BasicJsonType::array_t::const_iterator array_it{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @brief the CBOR specifics of @ref write_container_iterative
|
||||||
|
struct cbor_walk
|
||||||
|
{
|
||||||
|
static void array_head(binary_writer& w, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
w.write_cbor_head(0x80, j.m_data.m_value.array->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void object_head(binary_writer& w, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
w.write_cbor_head(0xA0, j.m_data.m_value.object->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scalar(binary_writer& w, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
w.write_cbor(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KeyType>
|
||||||
|
static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object)
|
||||||
|
{
|
||||||
|
w.write_cbor_key(k, object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief the MessagePack specifics of @ref write_container_iterative
|
||||||
|
struct msgpack_walk
|
||||||
|
{
|
||||||
|
static void array_head(binary_writer& w, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
w.write_msgpack_array_prefix(j.m_data.m_value.array->size(), j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void object_head(binary_writer& w, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
w.write_msgpack_object_prefix(j.m_data.m_value.object->size(), j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scalar(binary_writer& w, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
w.write_msgpack(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KeyType>
|
||||||
|
static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object)
|
||||||
|
{
|
||||||
|
w.write_msgpack_key(k, object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write @a j with @ref write_cbor, or write its header and push a
|
@brief write @a j with @a Walk::scalar, or write its header and push a
|
||||||
frame for @ref write_cbor_iterative to continue with its elements
|
frame for @ref write_container_iterative to continue with its
|
||||||
|
elements
|
||||||
|
|
||||||
A scalar, and an empty array or object, are written out in full: there is
|
A scalar, and an empty array or object, are written out in full: there is
|
||||||
nothing below them for @ref write_cbor_iterative to come back to, so
|
nothing below them for @ref write_container_iterative to come back to, so
|
||||||
nothing is pushed for them.
|
nothing is pushed for them.
|
||||||
*/
|
*/
|
||||||
void write_cbor_value_or_push(const BasicJsonType& j, std::vector<binary_container_frame>& stack)
|
template<typename Walk>
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE
|
||||||
|
void write_container_value_or_push(const BasicJsonType& j, std::vector<binary_container_frame>& stack)
|
||||||
{
|
{
|
||||||
if (j.is_array())
|
if (j.is_array())
|
||||||
{
|
{
|
||||||
write_cbor_head(0x80, j.m_data.m_value.array->size());
|
Walk::array_head(*this, j);
|
||||||
if (!j.m_data.m_value.array->empty())
|
if (!j.m_data.m_value.array->empty())
|
||||||
{
|
{
|
||||||
stack.emplace_back(&j);
|
stack.emplace_back(&j);
|
||||||
@@ -1027,7 +1080,7 @@ class binary_writer
|
|||||||
|
|
||||||
if (j.is_object())
|
if (j.is_object())
|
||||||
{
|
{
|
||||||
write_cbor_head(0xA0, j.m_data.m_value.object->size());
|
Walk::object_head(*this, j);
|
||||||
if (!j.m_data.m_value.object->empty())
|
if (!j.m_data.m_value.object->empty())
|
||||||
{
|
{
|
||||||
stack.emplace_back(&j);
|
stack.emplace_back(&j);
|
||||||
@@ -1035,22 +1088,26 @@ class binary_writer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_cbor(j);
|
Walk::scalar(*this, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write out @a root and everything below it without the call stack
|
@brief write out @a root and everything below it without the call stack
|
||||||
|
|
||||||
Emits the same bytes as @ref write_cbor, keeping the containers it has
|
Emits the same bytes as the recursive writer, keeping the containers it has
|
||||||
entered on an explicit stack instead of descending into them. Only reached
|
entered on an explicit stack instead of descending into them. Only reached
|
||||||
for values nested deeper than @ref recursion_depth_limit, which is why it
|
for values nested deeper than @ref recursion_depth_limit, which is why it
|
||||||
is not written for speed.
|
is not written for speed. Shared by CBOR and MessagePack, which differ only
|
||||||
|
in the @a Walk policy (@ref cbor_walk, @ref msgpack_walk); BON8 has its own
|
||||||
|
walker, as it tracks an open string run and closes a container with a
|
||||||
|
marker.
|
||||||
*/
|
*/
|
||||||
void write_cbor_iterative(const BasicJsonType& root)
|
template<typename Walk>
|
||||||
|
void write_container_iterative(const BasicJsonType& root)
|
||||||
{
|
{
|
||||||
// only a container with elements is ever pushed; see write_cbor_value_or_push
|
// only a container with elements is ever pushed; see write_container_value_or_push
|
||||||
std::vector<binary_container_frame> stack;
|
std::vector<binary_container_frame> stack;
|
||||||
write_cbor_value_or_push(root, stack);
|
write_container_value_or_push<Walk>(root, stack);
|
||||||
|
|
||||||
while (!stack.empty())
|
while (!stack.empty())
|
||||||
{
|
{
|
||||||
@@ -1068,7 +1125,7 @@ class binary_writer
|
|||||||
// read the child before pushing: entering it can move every frame
|
// read the child before pushing: entering it can move every frame
|
||||||
const BasicJsonType* child = &(*current.array_it);
|
const BasicJsonType* child = &(*current.array_it);
|
||||||
++stack.back().array_it;
|
++stack.back().array_it;
|
||||||
write_cbor_value_or_push(*child, stack);
|
write_container_value_or_push<Walk>(*child, stack);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1080,90 +1137,23 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the key is written directly (not via a temporary basic_json),
|
// the key is written directly (not via a temporary basic_json),
|
||||||
// with the object as diagnostics context, as in write_cbor
|
// with the object as diagnostics context, as in the recursive writer
|
||||||
write_cbor_key(current.object_it->first, *current.value);
|
Walk::key(*this, current.object_it->first, *current.value);
|
||||||
const BasicJsonType* child = &(current.object_it->second);
|
const BasicJsonType* child = &(current.object_it->second);
|
||||||
++stack.back().object_it;
|
++stack.back().object_it;
|
||||||
write_cbor_value_or_push(*child, stack);
|
write_container_value_or_push<Walk>(*child, stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
void write_cbor_iterative(const BasicJsonType& root)
|
||||||
@brief write @a j with @ref write_msgpack, or write its header and push a
|
|
||||||
frame for @ref write_msgpack_iterative to continue with its elements
|
|
||||||
|
|
||||||
@sa @ref write_cbor_value_or_push
|
|
||||||
*/
|
|
||||||
void write_msgpack_value_or_push(const BasicJsonType& j, std::vector<binary_container_frame>& stack)
|
|
||||||
{
|
{
|
||||||
if (j.is_array())
|
write_container_iterative<cbor_walk>(root);
|
||||||
{
|
|
||||||
write_msgpack_array_prefix(j.m_data.m_value.array->size(), j);
|
|
||||||
if (!j.m_data.m_value.array->empty())
|
|
||||||
{
|
|
||||||
stack.emplace_back(&j);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.is_object())
|
|
||||||
{
|
|
||||||
write_msgpack_object_prefix(j.m_data.m_value.object->size(), j);
|
|
||||||
if (!j.m_data.m_value.object->empty())
|
|
||||||
{
|
|
||||||
stack.emplace_back(&j);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_msgpack(j);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief write out @a root and everything below it without the call stack
|
|
||||||
|
|
||||||
@sa @ref write_cbor_iterative
|
|
||||||
*/
|
|
||||||
void write_msgpack_iterative(const BasicJsonType& root)
|
void write_msgpack_iterative(const BasicJsonType& root)
|
||||||
{
|
{
|
||||||
std::vector<binary_container_frame> stack;
|
write_container_iterative<msgpack_walk>(root);
|
||||||
write_msgpack_value_or_push(root, stack);
|
|
||||||
|
|
||||||
while (!stack.empty())
|
|
||||||
{
|
|
||||||
const binary_container_frame current = stack.back();
|
|
||||||
|
|
||||||
if (current.value->is_array())
|
|
||||||
{
|
|
||||||
const auto& array = *current.value->m_data.m_value.array;
|
|
||||||
if (current.array_it == array.cend())
|
|
||||||
{
|
|
||||||
stack.pop_back();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BasicJsonType* child = &(*current.array_it);
|
|
||||||
++stack.back().array_it;
|
|
||||||
write_msgpack_value_or_push(*child, stack);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& object = *current.value->m_data.m_value.object;
|
|
||||||
if (current.object_it == object.cend())
|
|
||||||
{
|
|
||||||
stack.pop_back();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// as in write_cbor_iterative, the key is written directly with
|
|
||||||
// the object as diagnostics context
|
|
||||||
write_msgpack_key(current.object_it->first, *current.value);
|
|
||||||
const BasicJsonType* child = &(current.object_it->second);
|
|
||||||
++stack.back().object_it;
|
|
||||||
write_msgpack_value_or_push(*child, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return true when a closing ']' still has to be written after the elements
|
/// @return true when a closing ']' still has to be written after the elements
|
||||||
@@ -1319,7 +1309,7 @@ class binary_writer
|
|||||||
elements of an optimized container, and everything below the
|
elements of an optimized container, and everything below the
|
||||||
top level, never repeat it)
|
top level, never repeat it)
|
||||||
|
|
||||||
@sa @ref write_cbor_value_or_push
|
@sa @ref write_container_value_or_push
|
||||||
*/
|
*/
|
||||||
void write_ubjson_value_or_push(const BasicJsonType& j, const bool add_prefix, const bool use_count,
|
void write_ubjson_value_or_push(const BasicJsonType& j, const bool add_prefix, const bool use_count,
|
||||||
const bool use_type, const bool use_bjdata, const bjdata_version_t bjdata_version,
|
const bool use_type, const bool use_bjdata, const bjdata_version_t bjdata_version,
|
||||||
@@ -4418,7 +4408,7 @@ class binary_writer
|
|||||||
A scalar, and an empty array or object, are written out in full and not
|
A scalar, and an empty array or object, are written out in full and not
|
||||||
pushed.
|
pushed.
|
||||||
|
|
||||||
@sa @ref write_cbor_value_or_push
|
@sa @ref write_container_value_or_push
|
||||||
*/
|
*/
|
||||||
void write_bon8_value_or_push(const BasicJsonType& j, bool& string_open, std::vector<binary_container_frame>& stack)
|
void write_bon8_value_or_push(const BasicJsonType& j, bool& string_open, std::vector<binary_container_frame>& stack)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user