From 965a70e38d666520bdbcbf3aafb01312ba7bc833 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 10 Jan 2018 11:22:19 +0100 Subject: [PATCH] :hammer: optimized output format --- src/json.hpp | 148 +++++++++++++++++++++++++++++++-------- test/src/unit-ubjson.cpp | 14 ++-- 2 files changed, 125 insertions(+), 37 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index e7465e29..9dc0b24a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6189,48 +6189,61 @@ class binary_writer } /*! - @brief[in] j JSON value to serialize + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value */ - void write_ubjson(const BasicJsonType& j, const bool use_count = false) + void write_ubjson(const BasicJsonType& j, + const bool use_count = false, + const bool use_type = false, + const bool add_prefix = true) { switch (j.type()) { case value_t::null: { - oa->write_character(static_cast('Z')); + if (add_prefix) + { + oa->write_character(static_cast('Z')); + } break; } case value_t::boolean: { - oa->write_character(j.m_value.boolean - ? static_cast('T') - : static_cast('F')); + if (add_prefix) + oa->write_character(j.m_value.boolean + ? static_cast('T') + : static_cast('F')); break; } case value_t::number_integer: { - write_number_with_ubjson_prefix(j.m_value.number_integer); + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); break; } case value_t::number_unsigned: { - write_number_with_ubjson_prefix(j.m_value.number_unsigned); + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); break; } case value_t::number_float: { - write_number_with_ubjson_prefix(j.m_value.number_float); + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); break; } case value_t::string: { - oa->write_character(static_cast('S')); - write_number_with_ubjson_prefix(j.m_value.string->size()); + if (add_prefix) + { + oa->write_character(static_cast('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); oa->write_characters( reinterpret_cast(j.m_value.string->c_str()), j.m_value.string->size()); @@ -6239,17 +6252,38 @@ class binary_writer case value_t::array: { - oa->write_character(static_cast('[')); + if (add_prefix) + { + oa->write_character(static_cast('[')); + } + + bool prefix_required = true; + if (use_type and not j.m_value.array->empty()) + { + const char first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(static_cast('$')); + oa->write_character(static_cast(first_prefix)); + } + } if (use_count) { oa->write_character(static_cast('#')); - write_number_with_ubjson_prefix(j.m_value.array->size()); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); } for (const auto& el : *j.m_value.array) { - write_ubjson(el, use_count); + write_ubjson(el, use_count, use_type, prefix_required); } if (not use_count) @@ -6262,21 +6296,42 @@ class binary_writer case value_t::object: { - oa->write_character(static_cast('{')); + if (add_prefix) + { + oa->write_character(static_cast('{')); + } + + bool prefix_required = true; + if (use_type and not j.m_value.object->empty()) + { + const char first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(static_cast('$')); + oa->write_character(static_cast(first_prefix)); + } + } if (use_count) { oa->write_character(static_cast('#')); - write_number_with_ubjson_prefix(j.m_value.object->size()); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); } for (const auto& el : *j.m_value.object) { - write_number_with_ubjson_prefix(el.first.size()); + write_number_with_ubjson_prefix(el.first.size(), true); oa->write_characters( reinterpret_cast(el.first.c_str()), el.first.size()); - write_ubjson(el.second, use_count); + write_ubjson(el.second, use_count, use_type, prefix_required); } if (not use_count) @@ -6320,38 +6375,56 @@ class binary_writer } template - void write_number_with_ubjson_prefix(const NumberType n) + void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { if (std::is_floating_point::value) { - oa->write_character(static_cast('D')); // float64 + if (add_prefix) + { + oa->write_character(static_cast('D')); // float64 + } write_number(n); } else if (std::is_unsigned::value) { if (n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('i')); // uint8 + if (add_prefix) + { + oa->write_character(static_cast('i')); // uint8 + } write_number(static_cast(n)); } else if (n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('U')); // uint8 + if (add_prefix) + { + oa->write_character(static_cast('U')); // uint8 + } write_number(static_cast(n)); } else if (n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('I')); // int16 + if (add_prefix) + { + oa->write_character(static_cast('I')); // int16 + } write_number(static_cast(n)); } else if (n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('l')); // int32 + if (add_prefix) + { + oa->write_character(static_cast('l')); // int32 + } write_number(static_cast(n)); } else if (n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('L')); // int64 + if (add_prefix) + { + oa->write_character(static_cast('L')); // int64 + } write_number(static_cast(n)); } else @@ -6364,27 +6437,42 @@ class binary_writer { if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('i')); // int8 + if (add_prefix) + { + oa->write_character(static_cast('i')); // int8 + } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('U')); // uint8 + if (add_prefix) + { + oa->write_character(static_cast('U')); // uint8 + } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('I')); // int16 + if (add_prefix) + { + oa->write_character(static_cast('I')); // int16 + } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('l')); // int32 + if (add_prefix) + { + oa->write_character(static_cast('l')); // int32 + } write_number(static_cast(n)); } else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) { - oa->write_character(static_cast('L')); // int64 + if (add_prefix) + { + oa->write_character(static_cast('L')); // int64 + } write_number(static_cast(n)); } else diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp index bda8224a..43c461cc 100644 --- a/test/src/unit-ubjson.cpp +++ b/test/src/unit-ubjson.cpp @@ -129,13 +129,13 @@ TEST_CASE("UBJSON") // check individual bytes CHECK(result[0] == 'L'); int64_t restored = (static_cast(result[1]) << 070) + - (static_cast(result[2]) << 060) + - (static_cast(result[3]) << 050) + - (static_cast(result[4]) << 040) + - (static_cast(result[5]) << 030) + - (static_cast(result[6]) << 020) + - (static_cast(result[7]) << 010) + - static_cast(result[8]); + (static_cast(result[2]) << 060) + + (static_cast(result[3]) << 050) + + (static_cast(result[4]) << 040) + + (static_cast(result[5]) << 030) + + (static_cast(result[6]) << 020) + + (static_cast(result[7]) << 010) + + static_cast(result[8]); CHECK(restored == i); // roundtrip