json/include/nlohmann/detail/output/binary_writer.hpp

1636 lines
60 KiB
C++
Raw Normal View History

2018-01-10 17:18:31 +08:00
#pragma once
2017-08-14 23:55:06 +08:00
#include <algorithm> // reverse
#include <array> // array
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
#include <cmath> // isnan, isinf
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
#include <cstring> // memcpy
#include <limits> // numeric_limits
2019-03-15 21:55:13 +08:00
#include <string> // string
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
#include <utility> // move
2017-08-14 23:55:06 +08:00
#include <nlohmann/detail/input/binary_reader.hpp>
2019-07-01 04:14:02 +08:00
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
2017-08-14 23:55:06 +08:00
namespace nlohmann
{
namespace detail
{
///////////////////
// binary writer //
///////////////////
/*!
@brief serialization to CBOR and MessagePack values
*/
template<typename BasicJsonType, typename CharType>
class binary_writer
{
2018-10-28 00:31:03 +08:00
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using number_float_t = typename BasicJsonType::number_float_t;
2018-10-28 00:31:03 +08:00
2017-08-14 23:55:06 +08:00
public:
/*!
@brief create a binary writer
@param[in] adapter output adapter to write to
*/
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
2017-08-14 23:55:06 +08:00
{
JSON_ASSERT(oa);
2017-08-14 23:55:06 +08:00
}
/*!
2018-10-25 05:39:30 +08:00
@param[in] j JSON value to serialize
@pre j.type() == value_t::object
*/
void write_bson(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::object:
{
write_bson_object(*j.m_value.object);
break;
}
case value_t::null:
case value_t::array:
case value_t::string:
case value_t::boolean:
case value_t::number_integer:
case value_t::number_unsigned:
case value_t::number_float:
case value_t::binary:
case value_t::discarded:
2018-10-25 05:39:30 +08:00
default:
{
2021-08-19 23:04:34 +08:00
JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j));
2018-10-25 05:39:30 +08:00
}
}
}
/*!
@param[in] j JSON value to serialize
2017-08-14 23:55:06 +08:00
*/
void write_cbor(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::null:
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xF6));
2017-08-14 23:55:06 +08:00
break;
}
case value_t::boolean:
{
oa->write_character(j.m_value.boolean
2018-10-28 00:31:03 +08:00
? to_char_type(0xF5)
: to_char_type(0xF4));
2017-08-14 23:55:06 +08:00
break;
}
case value_t::number_integer:
{
if (j.m_value.number_integer >= 0)
{
// CBOR does not differentiate between positive signed
// integers and unsigned integers. Therefore, we used the
// code from the value_t::number_unsigned case here.
if (j.m_value.number_integer <= 0x17)
{
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x18));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x19));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x1A));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
else
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x1B));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
}
else
{
// The conversions below encode the sign in the first
// byte, and the value is converted to a positive number.
const auto positive_number = -1 - j.m_value.number_integer;
if (j.m_value.number_integer >= -24)
{
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0x20 + positive_number));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x38));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(positive_number));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x39));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(positive_number));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x3A));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(positive_number));
2017-08-14 23:55:06 +08:00
}
else
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x3B));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(positive_number));
2017-08-14 23:55:06 +08:00
}
}
break;
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned <= 0x17)
{
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x18));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x19));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x1A));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));
2017-08-14 23:55:06 +08:00
}
else
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x1B));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));
2017-08-14 23:55:06 +08:00
}
break;
}
2018-03-29 00:20:55 +08:00
case value_t::number_float:
2017-08-14 23:55:06 +08:00
{
if (std::isnan(j.m_value.number_float))
{
// NaN is 0xf97e00 in CBOR
oa->write_character(to_char_type(0xF9));
oa->write_character(to_char_type(0x7E));
oa->write_character(to_char_type(0x00));
}
else if (std::isinf(j.m_value.number_float))
{
// Infinity is 0xf97c00, -Infinity is 0xf9fc00
oa->write_character(to_char_type(0xf9));
oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
oa->write_character(to_char_type(0x00));
}
else
{
write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);
}
2017-08-14 23:55:06 +08:00
break;
}
case value_t::string:
{
// step 1: write control byte and the string length
const auto N = j.m_value.string->size();
if (N <= 0x17)
{
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0x60 + N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x78));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x79));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x7A));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(N));
2017-08-14 23:55:06 +08:00
}
// LCOV_EXCL_START
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x7B));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(N));
2017-08-14 23:55:06 +08:00
}
// LCOV_EXCL_STOP
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
// step 1: write control byte and the array size
const auto N = j.m_value.array->size();
if (N <= 0x17)
{
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0x80 + N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x98));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x99));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x9A));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(N));
2017-08-14 23:55:06 +08:00
}
// LCOV_EXCL_START
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x9B));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(N));
2017-08-14 23:55:06 +08:00
}
// LCOV_EXCL_STOP
// step 2: write each element
for (const auto& el : *j.m_value.array)
{
write_cbor(el);
}
break;
}
case value_t::binary:
{
if (j.m_value.binary->has_subtype())
{
2021-08-06 20:36:38 +08:00
if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())
{
write_number(static_cast<std::uint8_t>(0xd8));
write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));
}
else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())
{
write_number(static_cast<std::uint8_t>(0xd9));
write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));
}
else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())
{
write_number(static_cast<std::uint8_t>(0xda));
write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));
}
else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())
{
write_number(static_cast<std::uint8_t>(0xdb));
write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));
}
}
// step 1: write control byte and the binary array size
const auto N = j.m_value.binary->size();
if (N <= 0x17)
{
write_number(static_cast<std::uint8_t>(0x40 + N));
}
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
oa->write_character(to_char_type(0x58));
write_number(static_cast<std::uint8_t>(N));
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
oa->write_character(to_char_type(0x59));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
oa->write_character(to_char_type(0x5A));
write_number(static_cast<std::uint32_t>(N));
}
// LCOV_EXCL_START
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
{
oa->write_character(to_char_type(0x5B));
write_number(static_cast<std::uint64_t>(N));
}
// LCOV_EXCL_STOP
// step 2: write each element
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.binary->data()),
N);
break;
}
2017-08-14 23:55:06 +08:00
case value_t::object:
{
// step 1: write control byte and the object size
const auto N = j.m_value.object->size();
if (N <= 0x17)
{
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0xA0 + N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xB8));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xB9));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xBA));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(N));
2017-08-14 23:55:06 +08:00
}
// LCOV_EXCL_START
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint64_t>::max)())
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xBB));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(N));
2017-08-14 23:55:06 +08:00
}
// LCOV_EXCL_STOP
// step 2: write each element
for (const auto& el : *j.m_value.object)
{
write_cbor(el.first);
write_cbor(el.second);
}
break;
}
case value_t::discarded:
2017-08-14 23:55:06 +08:00
default:
break;
}
}
/*!
2018-10-25 05:39:30 +08:00
@param[in] j JSON value to serialize
2017-08-14 23:55:06 +08:00
*/
void write_msgpack(const BasicJsonType& j)
{
switch (j.type())
{
case value_t::null: // nil
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xC0));
2017-08-14 23:55:06 +08:00
break;
}
case value_t::boolean: // true and false
{
oa->write_character(j.m_value.boolean
2018-10-28 00:31:03 +08:00
? to_char_type(0xC3)
: to_char_type(0xC2));
2017-08-14 23:55:06 +08:00
break;
}
case value_t::number_integer:
{
if (j.m_value.number_integer >= 0)
{
// MessagePack does not differentiate between positive
// signed integers and unsigned integers. Therefore, we used
// the code from the value_t::number_unsigned case here.
if (j.m_value.number_unsigned < 128)
{
// positive fixnum
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 8
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCC));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 16
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCD));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 32
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCE));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 64
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCF));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
}
else
{
if (j.m_value.number_integer >= -32)
{
// negative fixnum
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
2019-03-17 07:27:44 +08:00
j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
2017-08-14 23:55:06 +08:00
{
// int 8
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xD0));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
2019-03-17 07:27:44 +08:00
j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
2017-08-14 23:55:06 +08:00
{
// int 16
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xD1));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int16_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
2019-03-17 07:27:44 +08:00
j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
2017-08-14 23:55:06 +08:00
{
// int 32
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xD2));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int32_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
2019-03-17 07:27:44 +08:00
j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
2017-08-14 23:55:06 +08:00
{
// int 64
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xD3));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int64_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
}
break;
}
case value_t::number_unsigned:
{
if (j.m_value.number_unsigned < 128)
{
// positive fixnum
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 8
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCC));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 16
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCD));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 32
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCE));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
2017-08-14 23:55:06 +08:00
{
// uint 64
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xCF));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
2017-08-14 23:55:06 +08:00
}
break;
}
2018-03-29 00:20:55 +08:00
case value_t::number_float:
2017-08-14 23:55:06 +08:00
{
write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);
2017-08-14 23:55:06 +08:00
break;
}
case value_t::string:
{
// step 1: write control byte and the string length
const auto N = j.m_value.string->size();
if (N <= 31)
{
// fixstr
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0xA0 | N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint8_t>::max)())
2017-08-14 23:55:06 +08:00
{
// str 8
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xD9));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
// str 16
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xDA));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
// str 32
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xDB));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(N));
2017-08-14 23:55:06 +08:00
}
// step 2: write the string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
// step 1: write control byte and the array size
const auto N = j.m_value.array->size();
if (N <= 15)
{
// fixarray
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0x90 | N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
// array 16
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xDC));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
// array 32
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xDD));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(N));
2017-08-14 23:55:06 +08:00
}
// step 2: write each element
for (const auto& el : *j.m_value.array)
{
write_msgpack(el);
}
break;
}
case value_t::binary:
{
// step 0: determine if the binary type has a set subtype to
// determine whether or not to use the ext or fixext types
const bool use_ext = j.m_value.binary->has_subtype();
// step 1: write control byte and the byte string length
const auto N = j.m_value.binary->size();
if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
2020-06-23 04:32:21 +08:00
std::uint8_t output_type{};
bool fixed = true;
if (use_ext)
{
switch (N)
{
case 1:
output_type = 0xD4; // fixext 1
break;
case 2:
output_type = 0xD5; // fixext 2
break;
case 4:
output_type = 0xD6; // fixext 4
break;
case 8:
output_type = 0xD7; // fixext 8
break;
case 16:
output_type = 0xD8; // fixext 16
break;
default:
output_type = 0xC7; // ext 8
fixed = false;
break;
}
}
else
{
output_type = 0xC4; // bin 8
fixed = false;
}
oa->write_character(to_char_type(output_type));
if (!fixed)
{
write_number(static_cast<std::uint8_t>(N));
}
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
2020-06-23 04:32:21 +08:00
std::uint8_t output_type = use_ext
? 0xC8 // ext 16
: 0xC5; // bin 16
oa->write_character(to_char_type(output_type));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
2020-06-23 04:32:21 +08:00
std::uint8_t output_type = use_ext
? 0xC9 // ext 32
: 0xC6; // bin 32
oa->write_character(to_char_type(output_type));
write_number(static_cast<std::uint32_t>(N));
}
// step 1.5: if this is an ext type, write the subtype
if (use_ext)
{
2020-05-17 19:51:59 +08:00
write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));
}
// step 2: write the byte string
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.binary->data()),
N);
break;
}
2017-08-14 23:55:06 +08:00
case value_t::object:
{
// step 1: write control byte and the object size
const auto N = j.m_value.object->size();
if (N <= 15)
{
// fixmap
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
2017-08-14 23:55:06 +08:00
{
// map 16
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xDE));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint16_t>(N));
2017-08-14 23:55:06 +08:00
}
2019-03-17 07:27:44 +08:00
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
2017-08-14 23:55:06 +08:00
{
// map 32
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0xDF));
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint32_t>(N));
2017-08-14 23:55:06 +08:00
}
// step 2: write each element
for (const auto& el : *j.m_value.object)
{
write_msgpack(el.first);
write_msgpack(el.second);
}
break;
}
case value_t::discarded:
2017-08-14 23:55:06 +08:00
default:
break;
}
}
/*!
@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,
const bool use_type, const bool add_prefix = true)
{
switch (j.type())
{
case value_t::null:
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('Z'));
}
break;
}
case value_t::boolean:
{
if (add_prefix)
{
oa->write_character(j.m_value.boolean
2018-10-28 00:31:03 +08:00
? to_char_type('T')
: to_char_type('F'));
}
break;
}
case value_t::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, add_prefix);
break;
}
case value_t::number_float:
{
write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);
break;
}
case value_t::string:
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('S'));
}
write_number_with_ubjson_prefix(j.m_value.string->size(), true);
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
j.m_value.string->size());
break;
}
case value_t::array:
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('['));
}
bool prefix_required = true;
if (use_type && !j.m_value.array->empty())
{
JSON_ASSERT(use_count);
2018-03-29 01:37:21 +08:00
const CharType 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;
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('$'));
2018-03-29 01:37:21 +08:00
oa->write_character(first_prefix);
}
}
if (use_count)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('#'));
write_number_with_ubjson_prefix(j.m_value.array->size(), true);
}
for (const auto& el : *j.m_value.array)
{
write_ubjson(el, use_count, use_type, prefix_required);
}
if (!use_count)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(']'));
}
break;
}
case value_t::binary:
{
if (add_prefix)
{
oa->write_character(to_char_type('['));
}
if (use_type && !j.m_value.binary->empty())
{
JSON_ASSERT(use_count);
oa->write_character(to_char_type('$'));
oa->write_character('U');
}
if (use_count)
{
oa->write_character(to_char_type('#'));
write_number_with_ubjson_prefix(j.m_value.binary->size(), true);
}
if (use_type)
{
oa->write_characters(
reinterpret_cast<const CharType*>(j.m_value.binary->data()),
j.m_value.binary->size());
}
else
{
for (size_t i = 0; i < j.m_value.binary->size(); ++i)
{
oa->write_character(to_char_type('U'));
oa->write_character(j.m_value.binary->data()[i]);
}
}
if (!use_count)
{
oa->write_character(to_char_type(']'));
}
break;
}
case value_t::object:
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('{'));
}
bool prefix_required = true;
if (use_type && !j.m_value.object->empty())
{
JSON_ASSERT(use_count);
2018-03-29 01:37:21 +08:00
const CharType 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;
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('$'));
2018-03-29 01:37:21 +08:00
oa->write_character(first_prefix);
}
}
if (use_count)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('#'));
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(), true);
oa->write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required);
}
if (!use_count)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('}'));
}
break;
}
case value_t::discarded:
default:
break;
}
}
2018-10-25 05:39:30 +08:00
private:
//////////
// BSON //
//////////
/*!
2018-10-25 05:39:30 +08:00
@return The size of a BSON document entry header, including the id marker
and the entry name size (and its null-terminator).
*/
2021-01-02 23:13:04 +08:00
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
2018-09-15 09:08:50 +08:00
{
2018-10-28 00:31:03 +08:00
const auto it = name.find(static_cast<typename string_t::value_type>(0));
2019-07-02 04:37:30 +08:00
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
{
2021-01-25 20:47:50 +08:00
JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j));
2021-08-01 20:08:14 +08:00
static_cast<void>(j);
}
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
2018-09-15 09:08:50 +08:00
}
/*!
@brief Writes the given @a element_type and @a name to the output adapter
*/
2018-10-28 00:31:03 +08:00
void write_bson_entry_header(const string_t& name,
const std::uint8_t element_type)
2018-09-15 09:23:54 +08:00
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(element_type)); // boolean
2018-09-15 09:23:54 +08:00
oa->write_characters(
reinterpret_cast<const CharType*>(name.c_str()),
name.size() + 1u);
}
/*!
@brief Writes a BSON element with key @a name and boolean value @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_boolean(const string_t& name,
2018-10-25 05:39:30 +08:00
const bool value)
{
write_bson_entry_header(name, 0x08);
2018-10-28 00:31:03 +08:00
oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
}
/*!
@brief Writes a BSON element with key @a name and double value @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_double(const string_t& name,
2018-10-25 05:39:30 +08:00
const double value)
{
write_bson_entry_header(name, 0x01);
write_number<double, true>(value);
}
/*!
@return The size of the BSON-encoded string in @a value
*/
2018-10-28 00:31:03 +08:00
static std::size_t calc_bson_string_size(const string_t& value)
{
return sizeof(std::int32_t) + value.size() + 1ul;
}
/*!
@brief Writes a BSON element with key @a name and string value @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_string(const string_t& name,
const string_t& value)
{
write_bson_entry_header(name, 0x02);
write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));
oa->write_characters(
reinterpret_cast<const CharType*>(value.c_str()),
value.size() + 1);
}
/*!
@brief Writes a BSON element with key @a name and null value
*/
2018-10-28 00:31:03 +08:00
void write_bson_null(const string_t& name)
{
write_bson_entry_header(name, 0x0A);
}
/*!
@return The size of the BSON-encoded integer @a value
*/
static std::size_t calc_bson_integer_size(const std::int64_t value)
{
return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
2019-03-17 07:27:44 +08:00
? sizeof(std::int32_t)
: sizeof(std::int64_t);
2018-09-15 18:11:21 +08:00
}
/*!
@brief Writes a BSON element with key @a name and integer @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_integer(const string_t& name,
2018-10-25 05:39:30 +08:00
const std::int64_t value)
2018-09-15 18:11:21 +08:00
{
if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
2018-09-15 18:11:21 +08:00
{
write_bson_entry_header(name, 0x10); // int32
write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
}
else
{
write_bson_entry_header(name, 0x12); // int64
write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
}
}
/*!
@return The size of the BSON-encoded unsigned integer in @a j
*/
2018-10-28 00:31:03 +08:00
static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
{
2018-10-28 00:31:03 +08:00
return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
? sizeof(std::int32_t)
: sizeof(std::int64_t);
}
/*!
@brief Writes a BSON element with key @a name and unsigned @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_unsigned(const string_t& name,
2021-01-02 23:13:04 +08:00
const BasicJsonType& j)
{
2021-01-02 23:13:04 +08:00
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
2018-10-28 00:31:03 +08:00
write_bson_entry_header(name, 0x10 /* int32 */);
2021-01-02 23:13:04 +08:00
write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.number_unsigned));
}
2021-01-02 23:13:04 +08:00
else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
{
2018-10-28 00:31:03 +08:00
write_bson_entry_header(name, 0x12 /* int64 */);
2021-01-02 23:13:04 +08:00
write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_unsigned));
}
else
{
2021-01-25 20:47:50 +08:00
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j));
}
}
/*!
@brief Writes a BSON element with key @a name and object @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_object_entry(const string_t& name,
2018-10-25 05:39:30 +08:00
const typename BasicJsonType::object_t& value)
{
write_bson_entry_header(name, 0x03); // object
write_bson_object(value);
}
/*!
@return The size of the BSON-encoded array @a value
*/
static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
2018-09-15 19:54:08 +08:00
{
2018-10-25 19:01:18 +08:00
std::size_t array_index = 0ul;
2018-09-15 19:54:08 +08:00
const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
{
return result + calc_bson_element_size(std::to_string(array_index++), el);
});
2018-09-15 19:54:08 +08:00
return sizeof(std::int32_t) + embedded_document_size + 1ul;
}
2018-09-15 19:54:08 +08:00
/*!
@return The size of the BSON-encoded binary array @a value
*/
static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)
{
return sizeof(std::int32_t) + value.size() + 1ul;
}
/*!
@brief Writes a BSON element with key @a name and array @a value
*/
2018-10-28 00:31:03 +08:00
void write_bson_array(const string_t& name,
2018-10-25 05:39:30 +08:00
const typename BasicJsonType::array_t& value)
{
write_bson_entry_header(name, 0x04); // array
2018-10-25 05:39:30 +08:00
write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));
2018-10-25 19:01:18 +08:00
std::size_t array_index = 0ul;
for (const auto& el : value)
2018-09-15 19:54:08 +08:00
{
2018-10-25 19:01:18 +08:00
write_bson_element(std::to_string(array_index++), el);
2018-09-15 19:54:08 +08:00
}
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x00));
}
2018-09-15 19:54:08 +08:00
/*!
@brief Writes a BSON element with key @a name and binary value @a value
*/
void write_bson_binary(const string_t& name,
const binary_t& value)
{
write_bson_entry_header(name, 0x05);
write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size()));
2021-08-06 20:36:38 +08:00
write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : std::uint8_t(0x00));
oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
}
/*!
@brief Calculates the size necessary to serialize the JSON value @a j with its @a name
@return The calculated size for the BSON document entry for @a j with the given @a name.
*/
2018-10-28 00:31:03 +08:00
static std::size_t calc_bson_element_size(const string_t& name,
2018-10-25 05:39:30 +08:00
const BasicJsonType& j)
{
2021-01-02 23:13:04 +08:00
const auto header_size = calc_bson_entry_header_size(name, j);
switch (j.type())
{
case value_t::object:
return header_size + calc_bson_object_size(*j.m_value.object);
2018-10-25 05:39:30 +08:00
case value_t::array:
return header_size + calc_bson_array_size(*j.m_value.array);
2018-10-25 05:39:30 +08:00
case value_t::binary:
return header_size + calc_bson_binary_size(*j.m_value.binary);
case value_t::boolean:
return header_size + 1ul;
2018-10-25 05:39:30 +08:00
case value_t::number_float:
return header_size + 8ul;
2018-10-25 05:39:30 +08:00
case value_t::number_integer:
return header_size + calc_bson_integer_size(j.m_value.number_integer);
2018-10-25 05:39:30 +08:00
case value_t::number_unsigned:
return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);
2018-10-25 05:39:30 +08:00
case value_t::string:
return header_size + calc_bson_string_size(*j.m_value.string);
2018-10-25 05:39:30 +08:00
case value_t::null:
return header_size + 0ul;
2018-10-25 05:39:30 +08:00
// LCOV_EXCL_START
case value_t::discarded:
2018-10-25 05:39:30 +08:00
default:
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
2018-10-25 05:39:30 +08:00
return 0ul;
// LCOV_EXCL_STOP
2018-12-23 20:56:18 +08:00
}
2018-09-15 19:54:08 +08:00
}
/*!
2018-10-25 05:39:30 +08:00
@brief Serializes the JSON value @a j to BSON and associates it with the
key @a name.
@param name The name to associate with the JSON entity @a j within the
current BSON document
*/
2018-10-28 00:31:03 +08:00
void write_bson_element(const string_t& name,
2018-10-25 05:39:30 +08:00
const BasicJsonType& j)
2018-09-15 09:23:54 +08:00
{
switch (j.type())
{
case value_t::object:
return write_bson_object_entry(name, *j.m_value.object);
2018-10-25 05:39:30 +08:00
2018-09-15 19:54:08 +08:00
case value_t::array:
return write_bson_array(name, *j.m_value.array);
2018-10-25 05:39:30 +08:00
case value_t::binary:
return write_bson_binary(name, *j.m_value.binary);
2018-09-15 09:23:54 +08:00
case value_t::boolean:
return write_bson_boolean(name, j.m_value.boolean);
2018-10-25 05:39:30 +08:00
2018-09-15 09:23:54 +08:00
case value_t::number_float:
return write_bson_double(name, j.m_value.number_float);
2018-10-25 05:39:30 +08:00
case value_t::number_integer:
return write_bson_integer(name, j.m_value.number_integer);
2018-10-25 05:39:30 +08:00
2018-09-15 18:11:21 +08:00
case value_t::number_unsigned:
2021-01-02 23:13:04 +08:00
return write_bson_unsigned(name, j);
2018-10-25 05:39:30 +08:00
case value_t::string:
return write_bson_string(name, *j.m_value.string);
2018-10-25 05:39:30 +08:00
case value_t::null:
return write_bson_null(name);
2018-10-25 05:39:30 +08:00
// LCOV_EXCL_START
case value_t::discarded:
2018-10-25 05:39:30 +08:00
default:
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
2018-10-25 05:39:30 +08:00
return;
// LCOV_EXCL_STOP
2018-12-23 20:56:18 +08:00
}
}
2018-09-15 09:23:54 +08:00
/*!
@brief Calculates the size of the BSON serialization of the given
JSON-object @a j.
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
@param[in] value JSON value to serialize
@pre value.type() == value_t::object
*/
static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
{
std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0),
2018-10-28 00:31:03 +08:00
[](size_t result, const typename BasicJsonType::object_t::value_type & el)
{
2018-10-28 00:31:03 +08:00
return result += calc_bson_element_size(el.first, el.second);
});
return sizeof(std::int32_t) + document_size + 1ul;
2018-09-15 09:23:54 +08:00
}
/*!
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
@param[in] value JSON value to serialize
@pre value.type() == value_t::object
*/
void write_bson_object(const typename BasicJsonType::object_t& value)
{
2018-10-25 05:39:30 +08:00
write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));
2018-09-15 09:08:50 +08:00
for (const auto& el : value)
2018-09-15 09:08:50 +08:00
{
write_bson_element(el.first, el.second);
2018-09-15 09:08:50 +08:00
}
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type(0x00));
}
2018-10-25 05:39:30 +08:00
//////////
// CBOR //
//////////
2018-10-25 05:39:30 +08:00
static constexpr CharType get_cbor_float_prefix(float /*unused*/)
{
2018-10-28 00:31:03 +08:00
return to_char_type(0xFA); // Single-Precision Float
}
2018-10-25 05:39:30 +08:00
static constexpr CharType get_cbor_float_prefix(double /*unused*/)
{
2018-10-28 00:31:03 +08:00
return to_char_type(0xFB); // Double-Precision Float
2018-10-25 05:39:30 +08:00
}
2018-10-25 05:39:30 +08:00
/////////////
// MsgPack //
/////////////
2017-08-14 23:55:06 +08:00
2018-10-25 05:39:30 +08:00
static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
2017-08-14 23:55:06 +08:00
{
2018-10-28 00:31:03 +08:00
return to_char_type(0xCA); // float 32
2018-10-25 05:39:30 +08:00
}
2018-10-25 05:39:30 +08:00
static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
{
2018-10-28 00:31:03 +08:00
return to_char_type(0xCB); // float 64
}
2018-10-25 05:39:30 +08:00
////////////
// UBJSON //
////////////
// UBJSON: write number (floating point)
template<typename NumberType, typename std::enable_if<
std::is_floating_point<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if (add_prefix)
{
2018-03-29 01:37:21 +08:00
oa->write_character(get_ubjson_float_prefix(n));
}
write_number(n);
}
// UBJSON: write number (unsigned integer)
template<typename NumberType, typename std::enable_if<
std::is_unsigned<NumberType>::value, int>::type = 0>
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
2019-03-17 07:27:44 +08:00
if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('i')); // int8
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(n));
}
2019-03-17 07:27:44 +08:00
else if (n <= (std::numeric_limits<std::uint8_t>::max)())
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('U')); // uint8
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(n));
}
2019-03-17 07:27:44 +08:00
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('I')); // int16
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int16_t>(n));
}
2019-03-17 07:27:44 +08:00
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('l')); // int32
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int32_t>(n));
}
2019-03-17 07:27:44 +08:00
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('L')); // int64
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int64_t>(n));
}
else
{
if (add_prefix)
{
oa->write_character(to_char_type('H')); // high-precision number
}
const auto number = BasicJsonType(n).dump();
write_number_with_ubjson_prefix(number.size(), true);
for (std::size_t i = 0; i < number.size(); ++i)
{
2020-07-21 04:38:00 +08:00
oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
}
}
}
// UBJSON: write number (signed integer)
template < typename NumberType, typename std::enable_if <
std::is_signed<NumberType>::value&&
!std::is_floating_point<NumberType>::value, int >::type = 0 >
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('i')); // int8
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int8_t>(n));
}
else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('U')); // uint8
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::uint8_t>(n));
}
else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('I')); // int16
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int16_t>(n));
}
else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('l')); // int32
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int32_t>(n));
}
else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
{
if (add_prefix)
{
2018-10-28 00:31:03 +08:00
oa->write_character(to_char_type('L')); // int64
}
2019-03-17 07:27:44 +08:00
write_number(static_cast<std::int64_t>(n));
}
// LCOV_EXCL_START
else
{
if (add_prefix)
{
oa->write_character(to_char_type('H')); // high-precision number
}
const auto number = BasicJsonType(n).dump();
write_number_with_ubjson_prefix(number.size(), true);
for (std::size_t i = 0; i < number.size(); ++i)
{
2020-07-21 04:38:00 +08:00
oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
}
}
// LCOV_EXCL_STOP
}
/*!
@brief determine the type prefix of container values
*/
2018-03-29 01:37:21 +08:00
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
{
switch (j.type())
{
case value_t::null:
return 'Z';
case value_t::boolean:
return j.m_value.boolean ? 'T' : 'F';
case value_t::number_integer:
{
if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
{
return 'i';
}
if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
{
return 'U';
}
if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
{
return 'I';
}
if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
{
return 'l';
}
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
{
return 'L';
}
// anything else is treated as high-precision number
2020-07-24 15:32:03 +08:00
return 'H'; // LCOV_EXCL_LINE
}
case value_t::number_unsigned:
{
2019-07-13 03:05:16 +08:00
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
{
return 'i';
}
2019-07-13 03:05:16 +08:00
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))
{
return 'U';
}
2019-07-13 03:05:16 +08:00
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
{
return 'I';
}
2019-07-13 03:05:16 +08:00
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
return 'l';
}
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
{
return 'L';
}
// anything else is treated as high-precision number
2020-07-24 15:32:03 +08:00
return 'H'; // LCOV_EXCL_LINE
}
case value_t::number_float:
2018-03-29 01:37:21 +08:00
return get_ubjson_float_prefix(j.m_value.number_float);
case value_t::string:
return 'S';
case value_t::array: // fallthrough
case value_t::binary:
return '[';
case value_t::object:
return '{';
case value_t::discarded:
default: // discarded values
return 'N';
}
}
2018-10-25 05:39:30 +08:00
static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
2018-03-29 01:37:21 +08:00
{
2018-10-25 05:39:30 +08:00
return 'd'; // float 32
2018-03-29 01:37:21 +08:00
}
2018-10-25 05:39:30 +08:00
static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
2018-03-29 01:37:21 +08:00
{
2018-10-25 05:39:30 +08:00
return 'D'; // float 64
2018-03-29 01:37:21 +08:00
}
2018-10-25 05:39:30 +08:00
///////////////////////
// Utility functions //
///////////////////////
2018-03-29 01:37:21 +08:00
2018-10-25 05:39:30 +08:00
/*
@brief write a number to output input
@param[in] n number of type @a NumberType
@tparam NumberType the type of the number
@tparam OutputIsLittleEndian Set to true if output data is
required to be little endian
2018-03-29 01:37:21 +08:00
2021-11-09 21:46:58 +08:00
@note This function needs to respect the system's endianness, because bytes
2018-10-25 05:39:30 +08:00
in CBOR, MessagePack, and UBJSON are stored in network order (big
endian) and therefore need reordering on little endian systems.
*/
template<typename NumberType, bool OutputIsLittleEndian = false>
void write_number(const NumberType n)
2018-03-29 01:37:21 +08:00
{
2018-10-25 05:39:30 +08:00
// step 1: write number to array of length NumberType
:rotating_light: add new CI and fix warnings (#2561) * :alembic: move CI targets to CMake * :recycle: add target for cpplint * :recycle: add target for self-contained binaries * :recycle: add targets for iwyu and infer * :loud_sound: add version output * :recycle: add target for oclint * :rotating_light: fix warnings * :recycle: rename targets * :recycle: use iwyu properly * :rotating_light: fix warnings * :recycle: use iwyu properly * :recycle: add target for benchmarks * :recycle: add target for CMake flags * :construction_worker: use GitHub Actions * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: try to install Clang 11 * :alembic: try to install GCC 11 * :alembic: add clang analyze target * :fire: remove Google Benchmark * :arrow_up: Google Benchmark 1.5.2 * :fire: use fetchcontent * :penguin: add target to download a Linux version of CMake * :hammer: fix dependency * :rotating_light: fix includes * :rotating_light: fix comment * :wrench: adjust flags for GCC 11.0.0 20210110 (experimental) * :whale: user Docker image to run CI * :wrench: add target for Valgrind * :construction_worker: add target for Valgrind tests * :alembic: add Dart * :rewind: remove Dart * :alembic: do not call ctest in test subdirectory * :alembic: download test data explicitly * :alembic: only execute Valgrind tests * :alembic: fix labels * :fire: remove unneeded jobs * :hammer: cleanup * :bug: fix OCLint call * :white_check_mark: add targets for offline and git-independent tests * :white_check_mark: add targets for C++ language versions and reproducible tests * :hammer: clean up * :construction_worker: add CI steps for cppcheck and cpplint * :rotating_light: fix warnings from Clang-Tidy * :construction_worker: add CI steps for Clang-Tidy * :rotating_light: fix warnings * :wrench: select proper binary * :rotating_light: fix warnings * :rotating_light: suppress some unhelpful warnings * :rotating_light: fix warnings * :art: fix format * :rotating_light: fix warnings * :construction_worker: add CI steps for Sanitizers * :rotating_light: fix warnings * :zap: add optimization to sanitizer build * :rotating_light: fix warnings * :rotating_light: add missing header * :rotating_light: fix warnings * :construction_worker: add CI step for coverage * :construction_worker: add CI steps for disabled exceptions and implicit conversions * :rotating_light: fix warnings * :construction_worker: add CI steps for checking indentation * :bug: fix variable use * :green_heart: fix build * :heavy_minus_sign: remove CircleCI * :construction_worker: add CI step for diagnostics * :rotating_light: fix warning * :fire: clean Travis
2021-03-24 14:15:18 +08:00
std::array<CharType, sizeof(NumberType)> vec{};
2018-10-25 05:39:30 +08:00
std::memcpy(vec.data(), &n, sizeof(NumberType));
// step 2: write array to output (with possible reordering)
if (is_little_endian != OutputIsLittleEndian)
2018-10-25 05:39:30 +08:00
{
// reverse byte order prior to conversion if necessary
std::reverse(vec.begin(), vec.end());
}
oa->write_characters(vec.data(), sizeof(NumberType));
2018-03-29 01:37:21 +08:00
}
void write_compact_float(const number_float_t n, detail::input_format_t format)
{
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
{
oa->write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(static_cast<float>(n))
: get_msgpack_float_prefix(static_cast<float>(n)));
write_number(static_cast<float>(n));
}
else
{
oa->write_character(format == detail::input_format_t::cbor
? get_cbor_float_prefix(n)
: get_msgpack_float_prefix(n));
write_number(n);
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
2018-10-28 21:20:20 +08:00
public:
2018-10-28 00:31:03 +08:00
// The following to_char_type functions are implement the conversion
// between uint8_t and CharType. In case CharType is not unsigned,
// such a conversion is required to allow values greater than 128.
// See <https://github.com/nlohmann/json/issues/1286> for a discussion.
template < typename C = CharType,
enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
2018-10-28 00:31:03 +08:00
static constexpr CharType to_char_type(std::uint8_t x) noexcept
{
return *reinterpret_cast<char*>(&x);
}
template < typename C = CharType,
enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
2018-10-28 00:31:03 +08:00
static CharType to_char_type(std::uint8_t x) noexcept
{
static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
static_assert(std::is_trivial<CharType>::value, "CharType must be trivial");
2018-10-28 00:31:03 +08:00
CharType result;
std::memcpy(&result, &x, sizeof(x));
return result;
}
template<typename C = CharType,
enable_if_t<std::is_unsigned<C>::value>* = nullptr>
static constexpr CharType to_char_type(std::uint8_t x) noexcept
{
return x;
}
template < typename InputCharType, typename C = CharType,
enable_if_t <
std::is_signed<C>::value &&
std::is_signed<char>::value &&
2018-10-28 00:31:03 +08:00
std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
> * = nullptr >
static constexpr CharType to_char_type(InputCharType x) noexcept
{
return x;
}
2017-08-14 23:55:06 +08:00
private:
2021-11-09 21:46:58 +08:00
/// whether we can assume little endianness
const bool is_little_endian = little_endianness();
2017-08-14 23:55:06 +08:00
/// the output
output_adapter_t<CharType> oa = nullptr;
};
} // namespace detail
} // namespace nlohmann