Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitHub Actions <action@github.com>2020-06-28 14:43:50 +0300
committerGitHub Actions <action@github.com>2020-06-28 14:43:50 +0300
commita7862d0ba7ec09baaf51b57d7e4405e3b4ba19c2 (patch)
tree3c08bec28545630c55f39d9946c979dfa70a4386
parentb4ec08af30ef7c60c5bde12487abcb68a44e2cab (diff)
Upstream release v3.1.0v3.1.0
-rw-r--r--README.md2
-rw-r--r--include/nlohmann/json.hpp5228
-rw-r--r--include/nlohmann/json_fwd.hpp64
3 files changed, 3827 insertions, 1467 deletions
diff --git a/README.md b/README.md
index eb30182..d44c1ca 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ include(FetchContent)
FetchContent_Declare(json
GIT_REPOSITORY https://github.com/ArthurSonzogni/nlohman_json
- GIT_TAG v3.0.1)
+ GIT_TAG v3.1.0)
FetchContent_GetProperties(json)
if(NOT json_POPULATED)
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 5b0b0ea..9b38efb 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -1,11 +1,11 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.0.1
+| | |__ | | | | | | version 3.1.0
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -29,34 +29,94 @@ SOFTWARE.
#ifndef NLOHMANN_JSON_HPP
#define NLOHMANN_JSON_HPP
-#include <algorithm> // all_of, copy, fill, find, for_each, generate_n, none_of, remove, reverse, transform
-#include <array> // array
+#define NLOHMANN_JSON_VERSION_MAJOR 3
+#define NLOHMANN_JSON_VERSION_MINOR 1
+#define NLOHMANN_JSON_VERSION_PATCH 0
+
+#include <algorithm> // all_of, find, for_each
#include <cassert> // assert
#include <ciso646> // and, not, or
-#include <clocale> // lconv, localeconv
-#include <cmath> // isfinite, labs, ldexp, signbit
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
-#include <cstdint> // int64_t, uint64_t
-#include <cstdlib> // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull
-#include <cstring> // memcpy, strlen
-#include <forward_list> // forward_list
-#include <functional> // function, hash, less
+#include <functional> // hash, less
#include <initializer_list> // initializer_list
-#include <iomanip> // hex
-#include <iosfwd> // istream, ostream
-#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
-#include <limits> // numeric_limits
-#include <locale> // locale
-#include <map> // map
-#include <memory> // addressof, allocator, allocator_traits, unique_ptr
+#include <iosfwd> // istream, ostream
+#include <iterator> // iterator_traits, random_access_iterator_tag
#include <numeric> // accumulate
-#include <sstream> // stringstream
-#include <string> // getline, stoi, string, to_string
-#include <type_traits> // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type
-#include <utility> // declval, forward, make_pair, move, pair, swap
-#include <valarray> // valarray
+#include <string> // string, stoi, to_string
+#include <utility> // declval, forward, move, pair, swap
+
+// #include <nlohmann/json_fwd.hpp>
+#ifndef NLOHMANN_JSON_FWD_HPP
+#define NLOHMANN_JSON_FWD_HPP
+
+#include <cstdint> // int64_t, uint64_t
+#include <map> // map
+#include <memory> // allocator
+#include <string> // string
#include <vector> // vector
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+namespace nlohmann
+{
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename = void, typename = void>
+struct adl_serializer;
+
+template<template<typename U, typename V, typename... Args> class ObjectType =
+ std::map,
+ template<typename U, typename... Args> class ArrayType = std::vector,
+ class StringType = std::string, class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename U> class AllocatorType = std::allocator,
+ template<typename T, typename SFINAE = void> class JSONSerializer =
+ adl_serializer>
+class basic_json;
+
+/*!
+@brief JSON Pointer
+
+A JSON pointer defines a string syntax for identifying a specific value
+within a JSON document. It can be used with functions `at` and
+`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+
+@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
+
+@since version 2.0.0
+*/
+template<typename BasicJsonType>
+class json_pointer;
+
+/*!
+@brief default JSON class
+
+This type is the default specialization of the @ref basic_json class which
+uses the standard template types.
+
+@since version 1.0.0
+*/
+using json = basic_json<>;
+}
+
+#endif
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+// This file contains all internal macro definitions
+// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
+
// exclude unsupported compilers
#if defined(__clang__)
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
@@ -90,7 +150,7 @@ SOFTWARE.
#endif
// allow to disable exceptions
-#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION)
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
#define JSON_THROW(exception) throw exception
#define JSON_TRY try
#define JSON_CATCH(exception) catch(exception)
@@ -100,6 +160,20 @@ SOFTWARE.
#define JSON_CATCH(exception) if(false)
#endif
+// override exception macros
+#if defined(JSON_THROW_USER)
+ #undef JSON_THROW
+ #define JSON_THROW JSON_THROW_USER
+#endif
+#if defined(JSON_TRY_USER)
+ #undef JSON_TRY
+ #define JSON_TRY JSON_TRY_USER
+#endif
+#if defined(JSON_CATCH_USER)
+ #undef JSON_CATCH
+ #define JSON_CATCH JSON_CATCH_USER
+#endif
+
// manual branch prediction
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#define JSON_LIKELY(x) __builtin_expect(!!(x), 1)
@@ -117,27 +191,6 @@ SOFTWARE.
#define JSON_HAS_CPP_14
#endif
-/*!
-@brief namespace for Niels Lohmann
-@see https://github.com/nlohmann
-@since version 1.0.0
-*/
-namespace nlohmann
-{
-template<typename = void, typename = void>
-struct adl_serializer;
-
-// forward declaration of basic_json (required to split the class)
-template<template<typename, typename, typename...> class ObjectType = std::map,
- template<typename, typename...> class ArrayType = std::vector,
- class StringType = std::string, class BooleanType = bool,
- class NumberIntegerType = std::int64_t,
- class NumberUnsignedType = std::uint64_t,
- class NumberFloatType = double,
- template<typename> class AllocatorType = std::allocator,
- template<typename, typename = void> class JSONSerializer = adl_serializer>
-class basic_json;
-
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
// may be removed in the future once the class is split.
@@ -154,17 +207,300 @@ class basic_json;
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
AllocatorType, JSONSerializer>
+/*!
+@brief Helper to determine whether there's a key_type for T.
+This helper is used to tell associative containers apart from other containers
+such as sequence containers. For instance, `std::map` passes the test as it
+contains a `mapped_type`, whereas `std::vector` fails the test.
+
+@sa http://stackoverflow.com/a/7728728/266378
+@since version 1.0.0, overworked in version 2.0.6
+*/
+#define NLOHMANN_JSON_HAS_HELPER(type) \
+ template<typename T> struct has_##type { \
+ private: \
+ template<typename U, typename = typename U::type> \
+ static int detect(U &&); \
+ static void detect(...); \
+ public: \
+ static constexpr bool value = \
+ std::is_integral<decltype(detect(std::declval<T>()))>::value; \
+ }
+
+// #include <nlohmann/detail/meta.hpp>
+
+
+#include <ciso646> // not
+#include <cstddef> // size_t
+#include <limits> // numeric_limits
+#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
+#include <utility> // declval
+
+// #include <nlohmann/json_fwd.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+namespace nlohmann
+{
/*!
-@brief unnamed namespace with internal helper functions
+@brief detail namespace with internal helper functions
-This namespace collects some functions that could not be defined inside the
-@ref basic_json class.
+This namespace collects functions that should not be exposed,
+implementations of some @ref basic_json methods, and meta-programming helpers.
@since version 2.1.0
*/
namespace detail
{
+/////////////
+// helpers //
+/////////////
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+// implementation of C++14 index_sequence and affiliates
+// source: https://stackoverflow.com/a/32223343
+template<std::size_t... Ints>
+struct index_sequence
+{
+ using type = index_sequence;
+ using value_type = std::size_t;
+ static constexpr std::size_t size() noexcept
+ {
+ return sizeof...(Ints);
+ }
+};
+
+template<class Sequence1, class Sequence2>
+struct merge_and_renumber;
+
+template<std::size_t... I1, std::size_t... I2>
+struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
+ : index_sequence < I1..., (sizeof...(I1) + I2)... > {};
+
+template<std::size_t N>
+struct make_index_sequence
+ : merge_and_renumber < typename make_index_sequence < N / 2 >::type,
+ typename make_index_sequence < N - N / 2 >::type > {};
+
+template<> struct make_index_sequence<0> : index_sequence<> {};
+template<> struct make_index_sequence<1> : index_sequence<0> {};
+
+template<typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+/*
+Implementation of two C++17 constructs: conjunction, negation. This is needed
+to avoid evaluating all the traits in a condition
+
+For example: not std::is_same<void, T>::value and has_value_type<T>::value
+will not compile when T = void (on MSVC at least). Whereas
+conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
+stop evaluating if negation<...>::value == false
+
+Please note that those constructs must be used with caution, since symbols can
+become very long quickly (which can slow down compilation and cause MSVC
+internal compiler errors). Only use it when you have to (see example ahead).
+*/
+template<class...> struct conjunction : std::true_type {};
+template<class B1> struct conjunction<B1> : B1 {};
+template<class B1, class... Bn>
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+
+template<class B> struct negation : std::integral_constant<bool, not B::value> {};
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+////////////////////////
+// has_/is_ functions //
+////////////////////////
+
+// source: https://stackoverflow.com/a/37193089/4116453
+
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+NLOHMANN_JSON_HAS_HELPER(mapped_type);
+NLOHMANN_JSON_HAS_HELPER(key_type);
+NLOHMANN_JSON_HAS_HELPER(value_type);
+NLOHMANN_JSON_HAS_HELPER(iterator);
+
+template<bool B, class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
+{
+ static constexpr auto value =
+ std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and
+ std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<class BasicJsonType, class CompatibleObjectType>
+struct is_compatible_object_type
+{
+ static auto constexpr value = is_compatible_object_type_impl <
+ conjunction<negation<std::is_same<void, CompatibleObjectType>>,
+ has_mapped_type<CompatibleObjectType>,
+ has_key_type<CompatibleObjectType>>::value,
+ typename BasicJsonType::object_t, CompatibleObjectType >::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct is_basic_json_nested_type
+{
+ static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
+ std::is_same<T, typename BasicJsonType::const_iterator>::value or
+ std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
+ std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
+};
+
+template<class BasicJsonType, class CompatibleArrayType>
+struct is_compatible_array_type
+{
+ static auto constexpr value =
+ conjunction<negation<std::is_same<void, CompatibleArrayType>>,
+ negation<is_compatible_object_type<
+ BasicJsonType, CompatibleArrayType>>,
+ negation<std::is_constructible<typename BasicJsonType::string_t,
+ CompatibleArrayType>>,
+ negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
+ has_value_type<CompatibleArrayType>,
+ has_iterator<CompatibleArrayType>>::value;
+};
+
+template<bool, typename, typename>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
+{
+ // is there an assert somewhere on overflows?
+ using RealLimits = std::numeric_limits<RealIntegerType>;
+ using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+ static constexpr auto value =
+ std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
+ CompatibleLimits::is_integer and
+ RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+{
+ static constexpr auto value =
+ is_compatible_integer_type_impl <
+ std::is_integral<CompatibleNumberIntegerType>::value and
+ not std::is_same<bool, CompatibleNumberIntegerType>::value,
+ RealIntegerType, CompatibleNumberIntegerType > ::value;
+};
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T>
+struct has_from_json
+{
+ private:
+ // also check the return type of from_json
+ template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
+ std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(
+ detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json
+{
+ private:
+ template <
+ typename U,
+ typename = enable_if_t<std::is_same<
+ T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(detect(
+ std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+template<typename BasicJsonType, typename T>
+struct has_to_json
+{
+ private:
+ template<typename U, typename = decltype(uncvref_t<U>::to_json(
+ std::declval<BasicJsonType&>(), std::declval<T>()))>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(detect(
+ std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+template <typename BasicJsonType, typename CompatibleCompleteType>
+struct is_compatible_complete_type
+{
+ static constexpr bool value =
+ not std::is_base_of<std::istream, CompatibleCompleteType>::value and
+ not std::is_same<BasicJsonType, CompatibleCompleteType>::value and
+ not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
+ has_to_json<BasicJsonType, CompatibleCompleteType>::value;
+};
+
+template <typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type
+ : conjunction<is_complete_type<CompatibleType>,
+ is_compatible_complete_type<BasicJsonType, CompatibleType>>
+{
+};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+ static constexpr T value{};
+};
+
+template<typename T>
+constexpr T static_const<T>::value;
+}
+}
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+
+#include <exception> // exception
+#include <stdexcept> // runtime_error
+#include <string> // to_string
+
+namespace nlohmann
+{
+namespace detail
+{
////////////////
// exceptions //
////////////////
@@ -419,6 +755,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
+json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. |
@liveexample{The following code shows how an `out_of_range` exception can be
caught.,out_of_range}
@@ -481,9 +818,21 @@ class other_error : public exception
private:
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
+}
+}
+// #include <nlohmann/detail/value_t.hpp>
+#include <array> // array
+#include <ciso646> // and
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+
+namespace nlohmann
+{
+namespace detail
+{
///////////////////////////
// JSON type enumeration //
///////////////////////////
@@ -512,7 +861,7 @@ value with the default value for a given type
@since version 1.0.0
*/
-enum class value_t : uint8_t
+enum class value_t : std::uint8_t
{
null, ///< null value
object, ///< object (unordered set of name/value pairs)
@@ -537,7 +886,7 @@ Returns an ordering that is similar to Python:
*/
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
- static constexpr std::array<uint8_t, 8> order = {{
+ static constexpr std::array<std::uint8_t, 8> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
}
@@ -547,80 +896,350 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept
const auto r_index = static_cast<std::size_t>(rhs);
return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
}
+}
+}
+// #include <nlohmann/detail/conversions/from_json.hpp>
-/////////////
-// helpers //
-/////////////
-template<typename> struct is_basic_json : std::false_type {};
+#include <algorithm> // transform
+#include <array> // array
+#include <ciso646> // and, not
+#include <forward_list> // forward_list
+#include <iterator> // inserter, front_inserter, end
+#include <string> // string
+#include <tuple> // tuple, make_tuple
+#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <utility> // pair, declval
+#include <valarray> // valarray
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+// #include <nlohmann/detail/exceptions.hpp>
-// alias templates to reduce boilerplate
-template<bool B, typename T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
+// #include <nlohmann/detail/macro_scope.hpp>
-template<typename T>
-using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+// #include <nlohmann/detail/meta.hpp>
-// implementation of C++14 index_sequence and affiliates
-// source: https://stackoverflow.com/a/32223343
-template<std::size_t... Ints>
-struct index_sequence
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
{
- using type = index_sequence;
- using value_type = std::size_t;
- static constexpr std::size_t size() noexcept
+namespace detail
+{
+// overloads for basic_json template parameters
+template<typename BasicJsonType, typename ArithmeticType,
+ enable_if_t<std::is_arithmetic<ArithmeticType>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int> = 0>
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
{
- return sizeof...(Ints);
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+
+ default:
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
}
-};
+}
-template<class Sequence1, class Sequence2>
-struct merge_and_renumber;
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+ if (JSON_UNLIKELY(not j.is_boolean()))
+ {
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
+ }
+ b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
-template<std::size_t... I1, std::size_t... I2>
-struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
- : index_sequence < I1..., (sizeof...(I1) + I2)... > {};
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+ if (JSON_UNLIKELY(not j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
+ }
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
-template<std::size_t N>
-struct make_index_sequence
- : merge_and_renumber < typename make_index_sequence < N / 2 >::type,
- typename make_index_sequence < N - N / 2 >::type > {};
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+ get_arithmetic_value(j, val);
+}
-template<> struct make_index_sequence<0> : index_sequence<> {};
-template<> struct make_index_sequence<1> : index_sequence<0> {};
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+ get_arithmetic_value(j, val);
+}
-template<typename... Ts>
-using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+ get_arithmetic_value(j, val);
+}
-/*
-Implementation of two C++17 constructs: conjunction, negation. This is needed
-to avoid evaluating all the traits in a condition
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+void from_json(const BasicJsonType& j, EnumType& e)
+{
+ typename std::underlying_type<EnumType>::type val;
+ get_arithmetic_value(j, val);
+ e = static_cast<EnumType>(val);
+}
-For example: not std::is_same<void, T>::value and has_value_type<T>::value
-will not compile when T = void (on MSVC at least). Whereas
-conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
-stop evaluating if negation<...>::value == false
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
-Please note that those constructs must be used with caution, since symbols can
-become very long quickly (which can slow down compilation and cause MSVC
-internal compiler errors). Only use it when you have to (see example ahead).
-*/
-template<class...> struct conjunction : std::true_type {};
-template<class B1> struct conjunction<B1> : B1 {};
-template<class B1, class... Bn>
-struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+ enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ std::transform(j.rbegin(), j.rend(),
+ std::front_inserter(l), [](const BasicJsonType & i)
+ {
+ return i.template get<T>();
+ });
+}
-template<class B> struct negation : std::integral_constant<bool, not B::value> {};
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ l.resize(j.size());
+ std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
+}
-// dispatch utility (taken from ranges-v3)
-template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
-template<> struct priority_tag<0> {};
+template<typename BasicJsonType, typename CompatibleArrayType>
+void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
+{
+ using std::end;
+ std::transform(j.begin(), j.end(),
+ std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename CompatibleArrayType::value_type>();
+ });
+}
+template<typename BasicJsonType, typename CompatibleArrayType>
+auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+ arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
+ void())
+{
+ using std::end;
+
+ arr.reserve(j.size());
+ std::transform(j.begin(), j.end(),
+ std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename CompatibleArrayType::value_type>();
+ });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
+ std::is_convertible<BasicJsonType, typename CompatibleArrayType::value_type>::value and
+ not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+
+ from_json_array_impl(j, arr, priority_tag<2> {});
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
+{
+ if (JSON_UNLIKELY(not j.is_object()))
+ {
+ JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
+ }
+
+ auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+ using value_type = typename CompatibleObjectType::value_type;
+ std::transform(
+ inner_object->begin(), inner_object->end(),
+ std::inserter(obj, obj.begin()),
+ [](typename BasicJsonType::object_t::value_type const & p)
+ {
+ return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>());
+ });
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template<typename BasicJsonType, typename ArithmeticType,
+ enable_if_t <
+ std::is_arithmetic<ArithmeticType>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int> = 0>
+void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+ case value_t::boolean:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+ break;
+ }
+
+ default:
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ }
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
+{
+ p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>)
+{
+ t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
+}
+
+template<typename BasicJsonType, typename... Args>
+void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
+{
+ from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
+}
+
+struct from_json_fn
+{
+ private:
+ template<typename BasicJsonType, typename T>
+ auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
+ noexcept(noexcept(from_json(j, val)))
+ -> decltype(from_json(j, val), void())
+ {
+ return from_json(j, val);
+ }
+
+ template<typename BasicJsonType, typename T>
+ void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
+ {
+ static_assert(sizeof(BasicJsonType) == 0,
+ "could not find from_json() method in T's namespace");
+#ifdef _MSC_VER
+ // MSVC does not show a stacktrace for the above assert
+ using decayed = uncvref_t<T>;
+ static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
+ "forcing MSVC stacktrace to show which T we're talking about.");
+#endif
+ }
+
+ public:
+ template<typename BasicJsonType, typename T>
+ void operator()(const BasicJsonType& j, T& val) const
+ noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
+ {
+ return call(j, val, priority_tag<1> {});
+ }
+};
+}
+
+/// namespace to hold default `from_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace
+{
+constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
+}
+}
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+
+
+#include <ciso646> // or, and, not
+#include <iterator> // begin, end
+#include <tuple> // tuple, get
+#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
+#include <utility> // move, forward, declval, pair
+#include <valarray> // valarray
+#include <vector> // vector
+
+// #include <nlohmann/detail/meta.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
//////////////////
// constructors //
//////////////////
@@ -783,159 +1402,6 @@ struct external_constructor<value_t::object>
}
};
-
-////////////////////////
-// has_/is_ functions //
-////////////////////////
-
-/*!
-@brief Helper to determine whether there's a key_type for T.
-
-This helper is used to tell associative containers apart from other containers
-such as sequence containers. For instance, `std::map` passes the test as it
-contains a `mapped_type`, whereas `std::vector` fails the test.
-
-@sa http://stackoverflow.com/a/7728728/266378
-@since version 1.0.0, overworked in version 2.0.6
-*/
-#define NLOHMANN_JSON_HAS_HELPER(type) \
- template<typename T> struct has_##type { \
- private: \
- template<typename U, typename = typename U::type> \
- static int detect(U &&); \
- static void detect(...); \
- public: \
- static constexpr bool value = \
- std::is_integral<decltype(detect(std::declval<T>()))>::value; \
- }
-
-NLOHMANN_JSON_HAS_HELPER(mapped_type);
-NLOHMANN_JSON_HAS_HELPER(key_type);
-NLOHMANN_JSON_HAS_HELPER(value_type);
-NLOHMANN_JSON_HAS_HELPER(iterator);
-
-#undef NLOHMANN_JSON_HAS_HELPER
-
-
-template<bool B, class RealType, class CompatibleObjectType>
-struct is_compatible_object_type_impl : std::false_type {};
-
-template<class RealType, class CompatibleObjectType>
-struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
-{
- static constexpr auto value =
- std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and
- std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
-};
-
-template<class BasicJsonType, class CompatibleObjectType>
-struct is_compatible_object_type
-{
- static auto constexpr value = is_compatible_object_type_impl <
- conjunction<negation<std::is_same<void, CompatibleObjectType>>,
- has_mapped_type<CompatibleObjectType>,
- has_key_type<CompatibleObjectType>>::value,
- typename BasicJsonType::object_t, CompatibleObjectType >::value;
-};
-
-template<typename BasicJsonType, typename T>
-struct is_basic_json_nested_type
-{
- static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
- std::is_same<T, typename BasicJsonType::const_iterator>::value or
- std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
- std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
-};
-
-template<class BasicJsonType, class CompatibleArrayType>
-struct is_compatible_array_type
-{
- static auto constexpr value =
- conjunction<negation<std::is_same<void, CompatibleArrayType>>,
- negation<is_compatible_object_type<
- BasicJsonType, CompatibleArrayType>>,
- negation<std::is_constructible<typename BasicJsonType::string_t,
- CompatibleArrayType>>,
- negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
- has_value_type<CompatibleArrayType>,
- has_iterator<CompatibleArrayType>>::value;
-};
-
-template<bool, typename, typename>
-struct is_compatible_integer_type_impl : std::false_type {};
-
-template<typename RealIntegerType, typename CompatibleNumberIntegerType>
-struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
-{
- // is there an assert somewhere on overflows?
- using RealLimits = std::numeric_limits<RealIntegerType>;
- using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
-
- static constexpr auto value =
- std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
- CompatibleLimits::is_integer and
- RealLimits::is_signed == CompatibleLimits::is_signed;
-};
-
-template<typename RealIntegerType, typename CompatibleNumberIntegerType>
-struct is_compatible_integer_type
-{
- static constexpr auto value =
- is_compatible_integer_type_impl <
- std::is_integral<CompatibleNumberIntegerType>::value and
- not std::is_same<bool, CompatibleNumberIntegerType>::value,
- RealIntegerType, CompatibleNumberIntegerType >::value;
-};
-
-
-// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
-template<typename BasicJsonType, typename T>
-struct has_from_json
-{
- private:
- // also check the return type of from_json
- template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
- std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
- static int detect(U&&);
- static void detect(...);
-
- public:
- static constexpr bool value = std::is_integral<decltype(
- detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
-};
-
-// This trait checks if JSONSerializer<T>::from_json(json const&) exists
-// this overload is used for non-default-constructible user-defined-types
-template<typename BasicJsonType, typename T>
-struct has_non_default_from_json
-{
- private:
- template<typename U, typename =
- enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value>>
- static int detect(U&&);
- static void detect(...);
-
- public:
- static constexpr bool value = std::is_integral<decltype(detect(
- std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
-};
-
-// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
-template<typename BasicJsonType, typename T>
-struct has_to_json
-{
- private:
- template<typename U, typename = decltype(uncvref_t<U>::to_json(
- std::declval<BasicJsonType&>(), std::declval<T>()))>
- static int detect(U&&);
- static void detect(...);
-
- public:
- static constexpr bool value = std::is_integral<decltype(detect(
- std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
-};
-
-
/////////////
// to_json //
/////////////
@@ -1055,261 +1521,6 @@ void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
to_json_tuple_impl(j, t, index_sequence_for<Args...> {});
}
-///////////////
-// from_json //
-///////////////
-
-// overloads for basic_json template parameters
-template<typename BasicJsonType, typename ArithmeticType,
- enable_if_t<std::is_arithmetic<ArithmeticType>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
- int> = 0>
-void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
-{
- switch (static_cast<value_t>(j))
- {
- case value_t::number_unsigned:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
- break;
- }
- case value_t::number_integer:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
- break;
- }
- case value_t::number_float:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
- break;
- }
-
- default:
- JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
- }
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
-{
- if (JSON_UNLIKELY(not j.is_boolean()))
- {
- JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
- }
- b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
-{
- if (JSON_UNLIKELY(not j.is_string()))
- {
- JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
- }
- s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
-{
- get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
-{
- get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
-{
- get_arithmetic_value(j, val);
-}
-
-template<typename BasicJsonType, typename EnumType,
- enable_if_t<std::is_enum<EnumType>::value, int> = 0>
-void from_json(const BasicJsonType& j, EnumType& e)
-{
- typename std::underlying_type<EnumType>::type val;
- get_arithmetic_value(j, val);
- e = static_cast<EnumType>(val);
-}
-
-template<typename BasicJsonType>
-void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
-{
- if (JSON_UNLIKELY(not j.is_array()))
- {
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
- }
- arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
-}
-
-// forward_list doesn't have an insert method
-template<typename BasicJsonType, typename T, typename Allocator,
- enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
-{
- if (JSON_UNLIKELY(not j.is_array()))
- {
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
- }
- std::transform(j.rbegin(), j.rend(),
- std::front_inserter(l), [](const BasicJsonType & i)
- {
- return i.template get<T>();
- });
-}
-
-// valarray doesn't have an insert method
-template<typename BasicJsonType, typename T,
- enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
-void from_json(const BasicJsonType& j, std::valarray<T>& l)
-{
- if (JSON_UNLIKELY(not j.is_array()))
- {
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
- }
- l.resize(j.size());
- std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
-}
-
-template<typename BasicJsonType, typename CompatibleArrayType>
-void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
-{
- using std::end;
-
- std::transform(j.begin(), j.end(),
- std::inserter(arr, end(arr)), [](const BasicJsonType & i)
- {
- // get<BasicJsonType>() returns *this, this won't call a from_json
- // method when value_type is BasicJsonType
- return i.template get<typename CompatibleArrayType::value_type>();
- });
-}
-
-template<typename BasicJsonType, typename CompatibleArrayType>
-auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
--> decltype(
- arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
- void())
-{
- using std::end;
-
- arr.reserve(j.size());
- std::transform(j.begin(), j.end(),
- std::inserter(arr, end(arr)), [](const BasicJsonType & i)
- {
- // get<BasicJsonType>() returns *this, this won't call a from_json
- // method when value_type is BasicJsonType
- return i.template get<typename CompatibleArrayType::value_type>();
- });
-}
-
-template<typename BasicJsonType, typename T, std::size_t N>
-void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
-{
- for (std::size_t i = 0; i < N; ++i)
- {
- arr[i] = j.at(i).template get<T>();
- }
-}
-
-template<typename BasicJsonType, typename CompatibleArrayType,
- enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
- std::is_convertible<BasicJsonType, typename CompatibleArrayType::value_type>::value and
- not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
-void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
-{
- if (JSON_UNLIKELY(not j.is_array()))
- {
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
- }
-
- from_json_array_impl(j, arr, priority_tag<2> {});
-}
-
-template<typename BasicJsonType, typename CompatibleObjectType,
- enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
-void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
-{
- if (JSON_UNLIKELY(not j.is_object()))
- {
- JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
- }
-
- auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
- using value_type = typename CompatibleObjectType::value_type;
- std::transform(
- inner_object->begin(), inner_object->end(),
- std::inserter(obj, obj.begin()),
- [](typename BasicJsonType::object_t::value_type const & p)
- {
- return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>());
- });
-}
-
-// overload for arithmetic types, not chosen for basic_json template arguments
-// (BooleanType, etc..); note: Is it really necessary to provide explicit
-// overloads for boolean_t etc. in case of a custom BooleanType which is not
-// an arithmetic type?
-template<typename BasicJsonType, typename ArithmeticType,
- enable_if_t <
- std::is_arithmetic<ArithmeticType>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
- int> = 0>
-void from_json(const BasicJsonType& j, ArithmeticType& val)
-{
- switch (static_cast<value_t>(j))
- {
- case value_t::number_unsigned:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
- break;
- }
- case value_t::number_integer:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
- break;
- }
- case value_t::number_float:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
- break;
- }
- case value_t::boolean:
- {
- val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
- break;
- }
-
- default:
- JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
- }
-}
-
-template<typename BasicJsonType, typename A1, typename A2>
-void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
-{
- p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
-}
-
-template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>)
-{
- t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
-}
-
-template<typename BasicJsonType, typename... Args>
-void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
-{
- from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
-}
-
struct to_json_fn
{
private:
@@ -1342,50 +1553,39 @@ struct to_json_fn
return call(j, std::forward<T>(val), priority_tag<1> {});
}
};
+}
-struct from_json_fn
+/// namespace to hold default `to_json` function
+namespace
{
- private:
- template<typename BasicJsonType, typename T>
- auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
- noexcept(noexcept(from_json(j, val)))
- -> decltype(from_json(j, val), void())
- {
- return from_json(j, val);
- }
+constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
+}
+}
- template<typename BasicJsonType, typename T>
- void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
- {
- static_assert(sizeof(BasicJsonType) == 0,
- "could not find from_json() method in T's namespace");
-#ifdef _MSC_VER
- // MSVC does not show a stacktrace for the above assert
- using decayed = uncvref_t<T>;
- static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
- "forcing MSVC stacktrace to show which T we're talking about.");
-#endif
- }
+// #include <nlohmann/detail/input/input_adapters.hpp>
- public:
- template<typename BasicJsonType, typename T>
- void operator()(const BasicJsonType& j, T& val) const
- noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
- {
- return call(j, val, priority_tag<1> {});
- }
-};
-// taken from ranges-v3
-template<typename T>
-struct static_const
-{
- static constexpr T value{};
-};
+#include <algorithm> // min
+#include <array> // array
+#include <cassert> // assert
+#include <cstddef> // size_t
+#include <cstring> // strlen
+#include <ios> // streamsize, streamoff, streampos
+#include <istream> // istream
+#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
+#include <memory> // shared_ptr, make_shared, addressof
+#include <numeric> // accumulate
+#include <string> // string, char_traits
+#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
+#include <utility> // pair, declval
+
+// #include <nlohmann/detail/macro_scope.hpp>
-template<typename T>
-constexpr T static_const<T>::value;
+namespace nlohmann
+{
+namespace detail
+{
////////////////////
// input adapters //
////////////////////
@@ -1624,10 +1824,34 @@ class input_adapter
/// the actual adapter
input_adapter_t ia = nullptr;
};
+}
+}
-//////////////////////
-// lexer and parser //
-//////////////////////
+// #include <nlohmann/detail/input/lexer.hpp>
+
+
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <ios> // hex, uppercase
+#include <iomanip> // setw, setfill
+#include <sstream> // stringstream
+#include <string> // char_traits, string
+#include <vector> // vector
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+///////////
+// lexer //
+///////////
/*!
@brief lexical analysis
@@ -1819,9 +2043,10 @@ class lexer
@brief scan a string literal
This function scans a string according to Sect. 7 of RFC 7159. While
- scanning, bytes are escaped and copied into buffer yytext. Then the function
- returns successfully, yytext is *not* null-terminated (as it may contain \0
- bytes), and yytext.size() is the number of bytes in the string.
+ scanning, bytes are escaped and copied into buffer token_buffer. Then the
+ function returns successfully, token_buffer is *not* null-terminated (as it
+ may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+ string.
@return token_type::value_string if string could be successfully scanned,
token_type::parse_error otherwise
@@ -1831,7 +2056,7 @@ class lexer
*/
token_type scan_string()
{
- // reset yytext (ignore opening quote)
+ // reset token_buffer (ignore opening quote)
reset();
// we entered the function by reading an open quote
@@ -2303,7 +2528,7 @@ class lexer
contains cycles, but any cycle can be left when EOF is read. Therefore,
the function is guaranteed to terminate.
- During scanning, the read bytes are stored in yytext. This string is
+ During scanning, the read bytes are stored in token_buffer. This string is
then converted to a signed integer, an unsigned integer, or a
floating-point number.
@@ -2317,7 +2542,7 @@ class lexer
*/
token_type scan_number()
{
- // reset yytext to store the number's bytes
+ // reset token_buffer to store the number's bytes
reset();
// the type of the parsed number; initially set to unsigned; will be
@@ -2601,10 +2826,10 @@ scan_number_done:
// try to parse integers first and fall back to floats
if (number_type == token_type::value_unsigned)
{
- const auto x = std::strtoull(yytext.data(), &endptr, 10);
+ const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
// we checked the number format before
- assert(endptr == yytext.data() + yytext.size());
+ assert(endptr == token_buffer.data() + token_buffer.size());
if (errno == 0)
{
@@ -2617,10 +2842,10 @@ scan_number_done:
}
else if (number_type == token_type::value_integer)
{
- const auto x = std::strtoll(yytext.data(), &endptr, 10);
+ const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
// we checked the number format before
- assert(endptr == yytext.data() + yytext.size());
+ assert(endptr == token_buffer.data() + token_buffer.size());
if (errno == 0)
{
@@ -2634,10 +2859,10 @@ scan_number_done:
// this code is reached if we parse a floating-point number or if an
// integer conversion above failed
- strtof(value_float, yytext.data(), &endptr);
+ strtof(value_float, token_buffer.data(), &endptr);
// we checked the number format before
- assert(endptr == yytext.data() + yytext.size());
+ assert(endptr == token_buffer.data() + token_buffer.size());
return token_type::value_float;
}
@@ -2666,10 +2891,10 @@ scan_number_done:
// input management
/////////////////////
- /// reset yytext; current character is beginning of token
+ /// reset token_buffer; current character is beginning of token
void reset() noexcept
{
- yytext.clear();
+ token_buffer.clear();
token_string.clear();
token_string.push_back(std::char_traits<char>::to_char_type(current));
}
@@ -2707,10 +2932,10 @@ scan_number_done:
}
}
- /// add a character to yytext
+ /// add a character to token_buffer
void add(int c)
{
- yytext.push_back(std::char_traits<char>::to_char_type(c));
+ token_buffer.push_back(std::char_traits<char>::to_char_type(c));
}
public:
@@ -2739,7 +2964,7 @@ scan_number_done:
/// return current string value (implicitly resets the token; useful only once)
std::string move_string()
{
- return std::move(yytext);
+ return std::move(token_buffer);
}
/////////////////////
@@ -2867,7 +3092,7 @@ scan_number_done:
std::vector<char> token_string {};
/// buffer for variable-length tokens (numbers, strings)
- std::string yytext {};
+ std::string token_buffer {};
/// a description of occurred lexer errors
const char* error_message = "";
@@ -2880,6 +3105,37 @@ scan_number_done:
/// the decimal point
const char decimal_point_char = '.';
};
+}
+}
+
+// #include <nlohmann/detail/input/parser.hpp>
+
+
+#include <cassert> // assert
+#include <cmath> // isfinite
+#include <cstdint> // uint8_t
+#include <functional> // function
+#include <string> // string
+#include <utility> // move
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+////////////
+// parser //
+////////////
/*!
@brief syntax analysis
@@ -3445,12 +3701,20 @@ class parser
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
+}
+}
-///////////////
-// iterators //
-///////////////
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
-/*!
+
+#include <cstddef> // ptrdiff_t
+#include <limits> // numeric_limits
+
+namespace nlohmann
+{
+namespace detail
+{
+/*
@brief an iterator for primitive JSON types
This class models an iterator for primitive JSON types (boolean, number,
@@ -3461,9 +3725,15 @@ end_value (`1`) models past the end.
*/
class primitive_iterator_t
{
- public:
+ private:
using difference_type = std::ptrdiff_t;
+ static constexpr difference_type begin_value = 0;
+ static constexpr difference_type end_value = begin_value + 1;
+ /// iterator as signed integer type
+ difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+
+ public:
constexpr difference_type get_value() const noexcept
{
return m_it;
@@ -3503,10 +3773,10 @@ class primitive_iterator_t
return lhs.m_it < rhs.m_it;
}
- primitive_iterator_t operator+(difference_type i)
+ primitive_iterator_t operator+(difference_type n) noexcept
{
auto result = *this;
- result += i;
+ result += n;
return result;
}
@@ -3515,57 +3785,57 @@ class primitive_iterator_t
return lhs.m_it - rhs.m_it;
}
- friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it)
- {
- return os << it.m_it;
- }
-
- primitive_iterator_t& operator++()
+ primitive_iterator_t& operator++() noexcept
{
++m_it;
return *this;
}
- primitive_iterator_t const operator++(int)
+ primitive_iterator_t const operator++(int) noexcept
{
auto result = *this;
m_it++;
return result;
}
- primitive_iterator_t& operator--()
+ primitive_iterator_t& operator--() noexcept
{
--m_it;
return *this;
}
- primitive_iterator_t const operator--(int)
+ primitive_iterator_t const operator--(int) noexcept
{
auto result = *this;
m_it--;
return result;
}
- primitive_iterator_t& operator+=(difference_type n)
+ primitive_iterator_t& operator+=(difference_type n) noexcept
{
m_it += n;
return *this;
}
- primitive_iterator_t& operator-=(difference_type n)
+ primitive_iterator_t& operator-=(difference_type n) noexcept
{
m_it -= n;
return *this;
}
+};
+}
+}
- private:
- static constexpr difference_type begin_value = 0;
- static constexpr difference_type end_value = begin_value + 1;
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
- /// iterator as signed integer type
- difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
-};
+namespace nlohmann
+{
+namespace detail
+{
/*!
@brief an iterator value
@@ -3581,7 +3851,34 @@ template<typename BasicJsonType> struct internal_iterator
/// generic iterator for all other types
primitive_iterator_t primitive_iterator {};
};
+}
+}
+
+// #include <nlohmann/detail/iterators/iter_impl.hpp>
+
+#include <ciso646> // not
+#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
+#include <type_traits> // conditional, is_const, remove_const
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+// forward declare, to be able to friend it later on
template<typename IteratorType> class iteration_proxy;
/*!
@@ -4174,10 +4471,25 @@ class iter_impl
/// associated JSON instance
pointer m_object = nullptr;
/// the actual iterator of the associated instance
- internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it = {};
+ internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it;
};
+}
+}
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+
-/// proxy class for the iterator_wrapper functions
+#include <cstddef> // size_t
+#include <string> // string, to_string
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+/// proxy class for the items() function
template<typename IteratorType> class iteration_proxy
{
private:
@@ -4247,7 +4559,7 @@ template<typename IteratorType> class iteration_proxy
public:
/// construct iteration proxy from a container
- explicit iteration_proxy(typename IteratorType::reference cont)
+ explicit iteration_proxy(typename IteratorType::reference cont) noexcept
: container(cont) {}
/// return iterator begin (needed for range-based for)
@@ -4262,6 +4574,23 @@ template<typename IteratorType> class iteration_proxy
return iteration_proxy_internal(container.end());
}
};
+}
+}
+
+// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
+
+
+#include <cstddef> // ptrdiff_t
+#include <iterator> // reverse_iterator
+#include <utility> // declval
+
+namespace nlohmann
+{
+namespace detail
+{
+//////////////////////
+// reverse_iterator //
+//////////////////////
/*!
@brief a template for a reverse iterator class
@@ -4366,11 +4695,25 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
return it.operator * ();
}
};
+}
+}
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
-/////////////////////
-// output adapters //
-/////////////////////
+#include <algorithm> // copy
+#include <cstddef> // size_t
+#include <ios> // streamsize
+#include <iterator> // back_inserter
+#include <memory> // shared_ptr, make_shared
+#include <ostream> // basic_ostream
+#include <string> // basic_string
+#include <vector> // vector
+
+namespace nlohmann
+{
+namespace detail
+{
/// abstract output adapter interface
template<typename CharType> struct output_adapter_protocol
{
@@ -4467,10 +4810,43 @@ class output_adapter
private:
output_adapter_t<CharType> oa = nullptr;
};
+}
+}
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+
+
+#include <algorithm> // generate_n
+#include <array> // array
+#include <cassert> // assert
+#include <cmath> // ldexp
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <iomanip> // setw, setfill
+#include <ios> // hex
+#include <iterator> // back_inserter
+#include <limits> // numeric_limits
+#include <sstream> // stringstream
+#include <string> // char_traits, string
+#include <utility> // make_pair, move
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
-//////////////////////////////
-// binary reader and writer //
-//////////////////////////////
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+///////////////////
+// binary reader //
+///////////////////
/*!
@brief deserialization of CBOR and MessagePack values
@@ -4480,6 +4856,7 @@ class binary_reader
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using string_t = typename BasicJsonType::string_t;
public:
/*!
@@ -4508,7 +4885,7 @@ class binary_reader
if (strict)
{
get();
- check_eof(true);
+ expect_eof();
}
return res;
}
@@ -4529,7 +4906,28 @@ class binary_reader
if (strict)
{
get();
- check_eof(true);
+ expect_eof();
+ }
+ return res;
+ }
+
+ /*!
+ @brief create a JSON value from UBJSON input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from UBJSON input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ BasicJsonType parse_ubjson(const bool strict)
+ {
+ const auto res = parse_ubjson_internal();
+ if (strict)
+ {
+ get_ignore_noop();
+ expect_eof();
}
return res;
}
@@ -4628,7 +5026,6 @@ class binary_reader
case 0x38: // Negative integer (one-byte uint8_t follows)
{
- // must be uint8_t !
return static_cast<number_integer_t>(-1) - get_number<uint8_t>();
}
@@ -4819,9 +5216,9 @@ class binary_reader
case 0xF9: // Half-Precision Float (two-byte IEEE 754)
{
const int byte1 = get();
- check_eof();
+ unexpect_eof();
const int byte2 = get();
- check_eof();
+ unexpect_eof();
// code from RFC 7049, Appendix D, Figure 3:
// As half-precision floating-point numbers were only added
@@ -5196,6 +5593,16 @@ class binary_reader
}
/*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+ */
+ BasicJsonType parse_ubjson_internal(const bool get_char = true)
+ {
+ return get_ubjson_value(get_char ? get_ignore_noop() : current);
+ }
+
+ /*!
@brief get next character from the input
This function provides the interface to the used input adapter. It does
@@ -5210,6 +5617,20 @@ class binary_reader
return (current = ia->get_character());
}
+ /*!
+ @return character read from the input after ignoring all 'N' entries
+ */
+ int get_ignore_noop()
+ {
+ do
+ {
+ get();
+ }
+ while (current == 'N');
+
+ return current;
+ }
+
/*
@brief read a number from the input
@@ -5230,7 +5651,7 @@ class binary_reader
for (std::size_t i = 0; i < sizeof(NumberType); ++i)
{
get();
- check_eof();
+ unexpect_eof();
// reverse byte order prior to conversion if necessary
if (is_little_endian)
@@ -5255,7 +5676,7 @@ class binary_reader
@param[in] len number of bytes to read
@note We can not reserve @a len bytes for the result, because @a len
- may be too large. Usually, @ref check_eof() detects the end of
+ may be too large. Usually, @ref unexpect_eof() detects the end of
the input before we run out of string memory.
@return string created by reading @a len bytes
@@ -5263,13 +5684,13 @@ class binary_reader
@throw parse_error.110 if input has less than @a len bytes
*/
template<typename NumberType>
- std::string get_string(const NumberType len)
+ string_t get_string(const NumberType len)
{
- std::string result;
+ string_t result;
std::generate_n(std::back_inserter(result), len, [this]()
{
get();
- check_eof();
+ unexpect_eof();
return static_cast<char>(current);
});
return result;
@@ -5287,9 +5708,9 @@ class binary_reader
@throw parse_error.110 if input ended
@throw parse_error.113 if an unexpected byte is read
*/
- std::string get_cbor_string()
+ string_t get_cbor_string()
{
- check_eof();
+ unexpect_eof();
switch (current)
{
@@ -5344,10 +5765,10 @@ class binary_reader
case 0x7F: // UTF-8 string (indefinite length)
{
- std::string result;
+ string_t result;
while (get() != 0xFF)
{
- check_eof();
+ unexpect_eof();
result.push_back(static_cast<char>(current));
}
return result;
@@ -5400,9 +5821,9 @@ class binary_reader
@throw parse_error.110 if input ended
@throw parse_error.113 if an unexpected byte is read
*/
- std::string get_msgpack_string()
+ string_t get_msgpack_string()
{
- check_eof();
+ unexpect_eof();
switch (current)
{
@@ -5496,25 +5917,251 @@ class binary_reader
}
/*!
- @brief check if input ended
+ @brief reads a UBJSON string
+
+ This function is either called after reading the 'S' byte explicitly
+ indicating a string, or in case of an object key where the 'S' byte can be
+ left out.
+
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+
+ @return string
+
@throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ string_t get_ubjson_string(const bool get_char = true)
+ {
+ if (get_char)
+ {
+ get(); // TODO: may we ignore N here?
+ }
+
+ unexpect_eof();
+
+ switch (current)
+ {
+ case 'U':
+ return get_string(get_number<uint8_t>());
+ case 'i':
+ return get_string(get_number<int8_t>());
+ case 'I':
+ return get_string(get_number<int16_t>());
+ case 'l':
+ return get_string(get_number<int32_t>());
+ case 'L':
+ return get_string(get_number<int64_t>());
+ default:
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(113, chars_read,
+ "expected a UBJSON string; last byte: 0x" + ss.str()));
+ }
+ }
+
+ /*!
+ @brief determine the type and size for a container
+
+ In the optimized UBJSON format, a type and a size can be provided to allow
+ for a more compact representation.
+
+ @return pair of the size and the type
*/
- void check_eof(const bool expect_eof = false) const
+ std::pair<std::size_t, int> get_ubjson_size_type()
+ {
+ std::size_t sz = string_t::npos;
+ int tc = 0;
+
+ get_ignore_noop();
+
+ if (current == '$')
+ {
+ tc = get(); // must not ignore 'N', because 'N' maybe the type
+ unexpect_eof();
+
+ get_ignore_noop();
+ if (current != '#')
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(112, chars_read,
+ "expected '#' after UBJSON type information; last byte: 0x" + ss.str()));
+ }
+ sz = parse_ubjson_internal();
+ }
+ else if (current == '#')
+ {
+ sz = parse_ubjson_internal();
+ }
+
+ return std::make_pair(sz, tc);
+ }
+
+ BasicJsonType get_ubjson_value(const int prefix)
+ {
+ switch (prefix)
+ {
+ case std::char_traits<char>::eof(): // EOF
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ case 'T': // true
+ return true;
+ case 'F': // false
+ return false;
+
+ case 'Z': // null
+ return nullptr;
+
+ case 'U':
+ return get_number<uint8_t>();
+ case 'i':
+ return get_number<int8_t>();
+ case 'I':
+ return get_number<int16_t>();
+ case 'l':
+ return get_number<int32_t>();
+ case 'L':
+ return get_number<int64_t>();
+ case 'd':
+ return get_number<float>();
+ case 'D':
+ return get_number<double>();
+
+ case 'C': // char
+ {
+ get();
+ unexpect_eof();
+ if (JSON_UNLIKELY(current > 127))
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(113, chars_read,
+ "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + ss.str()));
+ }
+ return string_t(1, static_cast<char>(current));
+ }
+
+ case 'S': // string
+ return get_ubjson_string();
+
+ case '[': // array
+ return get_ubjson_array();
+
+ case '{': // object
+ return get_ubjson_object();
+
+ default: // anything else
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(112, chars_read,
+ "error reading UBJSON; last byte: 0x" + ss.str()));
+ }
+ }
+
+ BasicJsonType get_ubjson_array()
{
- if (expect_eof)
+ BasicJsonType result = value_t::array;
+ const auto size_and_type = get_ubjson_size_type();
+
+ if (size_and_type.first != string_t::npos)
{
- if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
+ if (size_and_type.second != 0)
{
- JSON_THROW(parse_error::create(110, chars_read, "expected end of input"));
+ if (size_and_type.second != 'N')
+ std::generate_n(std::back_inserter(*result.m_value.array),
+ size_and_type.first, [this, size_and_type]()
+ {
+ return get_ubjson_value(size_and_type.second);
+ });
+ }
+ else
+ {
+ std::generate_n(std::back_inserter(*result.m_value.array),
+ size_and_type.first, [this]()
+ {
+ return parse_ubjson_internal();
+ });
}
}
else
{
- if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
+ while (current != ']')
{
- JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+ result.push_back(parse_ubjson_internal(false));
+ get_ignore_noop();
+ }
+ }
+
+ return result;
+ }
+
+ BasicJsonType get_ubjson_object()
+ {
+ BasicJsonType result = value_t::object;
+ const auto size_and_type = get_ubjson_size_type();
+
+ if (size_and_type.first != string_t::npos)
+ {
+ if (size_and_type.second != 0)
+ {
+ std::generate_n(std::inserter(*result.m_value.object,
+ result.m_value.object->end()),
+ size_and_type.first, [this, size_and_type]()
+ {
+ auto key = get_ubjson_string();
+ auto val = get_ubjson_value(size_and_type.second);
+ return std::make_pair(std::move(key), std::move(val));
+ });
+ }
+ else
+ {
+ std::generate_n(std::inserter(*result.m_value.object,
+ result.m_value.object->end()),
+ size_and_type.first, [this]()
+ {
+ auto key = get_ubjson_string();
+ auto val = parse_ubjson_internal();
+ return std::make_pair(std::move(key), std::move(val));
+ });
}
}
+ else
+ {
+ while (current != '}')
+ {
+ auto key = get_ubjson_string(false);
+ result[std::move(key)] = parse_ubjson_internal();
+ get_ignore_noop();
+ }
+ }
+
+ return result;
+ }
+
+ /*!
+ @brief throw if end of input is not reached
+ @throw parse_error.110 if input not ended
+ */
+ void expect_eof() const
+ {
+ if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "expected end of input"));
+ }
+ }
+
+ /*!
+ @briefthrow if end of input is reached
+ @throw parse_error.110 if input ended
+ */
+ void unexpect_eof() const
+ {
+ if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+ }
}
private:
@@ -5530,6 +6177,30 @@ class binary_reader
/// whether we can assume little endianess
const bool is_little_endian = little_endianess();
};
+}
+}
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+
+
+#include <algorithm> // reverse
+#include <array> // array
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <limits> // numeric_limits
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+///////////////////
+// binary writer //
+///////////////////
/*!
@brief serialization to CBOR and MessagePack values
@@ -5678,23 +6349,23 @@ class binary_writer
{
write_number(static_cast<uint8_t>(0x60 + N));
}
- else if (N <= 0xFF)
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x78));
write_number(static_cast<uint8_t>(N));
}
- else if (N <= 0xFFFF)
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x79));
write_number(static_cast<uint16_t>(N));
}
- else if (N <= 0xFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x7A));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
- else if (N <= 0xFFFFFFFFFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0x7B));
write_number(static_cast<uint64_t>(N));
@@ -5716,23 +6387,23 @@ class binary_writer
{
write_number(static_cast<uint8_t>(0x80 + N));
}
- else if (N <= 0xFF)
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0x98));
write_number(static_cast<uint8_t>(N));
}
- else if (N <= 0xFFFF)
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0x99));
write_number(static_cast<uint16_t>(N));
}
- else if (N <= 0xFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0x9A));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
- else if (N <= 0xFFFFFFFFFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0x9B));
write_number(static_cast<uint64_t>(N));
@@ -5755,23 +6426,23 @@ class binary_writer
{
write_number(static_cast<uint8_t>(0xA0 + N));
}
- else if (N <= 0xFF)
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
{
oa->write_character(static_cast<CharType>(0xB8));
write_number(static_cast<uint8_t>(N));
}
- else if (N <= 0xFFFF)
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
{
oa->write_character(static_cast<CharType>(0xB9));
write_number(static_cast<uint16_t>(N));
}
- else if (N <= 0xFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
{
oa->write_character(static_cast<CharType>(0xBA));
write_number(static_cast<uint32_t>(N));
}
// LCOV_EXCL_START
- else if (N <= 0xFFFFFFFFFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint64_t>::max)())
{
oa->write_character(static_cast<CharType>(0xBB));
write_number(static_cast<uint64_t>(N));
@@ -5939,19 +6610,19 @@ class binary_writer
// fixstr
write_number(static_cast<uint8_t>(0xA0 | N));
}
- else if (N <= 255)
+ else if (N <= (std::numeric_limits<uint8_t>::max)())
{
// str 8
oa->write_character(static_cast<CharType>(0xD9));
write_number(static_cast<uint8_t>(N));
}
- else if (N <= 65535)
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// str 16
oa->write_character(static_cast<CharType>(0xDA));
write_number(static_cast<uint16_t>(N));
}
- else if (N <= 4294967295)
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// str 32
oa->write_character(static_cast<CharType>(0xDB));
@@ -5974,13 +6645,13 @@ class binary_writer
// fixarray
write_number(static_cast<uint8_t>(0x90 | N));
}
- else if (N <= 0xFFFF)
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// array 16
oa->write_character(static_cast<CharType>(0xDC));
write_number(static_cast<uint16_t>(N));
}
- else if (N <= 0xFFFFFFFF)
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// array 32
oa->write_character(static_cast<CharType>(0xDD));
@@ -6004,13 +6675,13 @@ class binary_writer
// fixmap
write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
}
- else if (N <= 65535)
+ else if (N <= (std::numeric_limits<uint16_t>::max)())
{
// map 16
oa->write_character(static_cast<CharType>(0xDE));
write_number(static_cast<uint16_t>(N));
}
- else if (N <= 4294967295)
+ else if (N <= (std::numeric_limits<uint32_t>::max)())
{
// map 32
oa->write_character(static_cast<CharType>(0xDF));
@@ -6031,6 +6702,165 @@ class binary_writer
}
}
+ /*!
+ @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)
+ {
+ oa->write_character(static_cast<CharType>('Z'));
+ }
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ if (add_prefix)
+ oa->write_character(j.m_value.boolean
+ ? static_cast<CharType>('T')
+ : static_cast<CharType>('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)
+ {
+ oa->write_character(static_cast<CharType>('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)
+ {
+ oa->write_character(static_cast<CharType>('['));
+ }
+
+ bool prefix_required = true;
+ if (use_type and not j.m_value.array->empty())
+ {
+ assert(use_count);
+ 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<CharType>('$'));
+ oa->write_character(static_cast<CharType>(first_prefix));
+ }
+ }
+
+ if (use_count)
+ {
+ oa->write_character(static_cast<CharType>('#'));
+ 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 (not use_count)
+ {
+ oa->write_character(static_cast<CharType>(']'));
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('{'));
+ }
+
+ bool prefix_required = true;
+ if (use_type and not j.m_value.object->empty())
+ {
+ assert(use_count);
+ 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<CharType>('$'));
+ oa->write_character(static_cast<CharType>(first_prefix));
+ }
+ }
+
+ if (use_count)
+ {
+ oa->write_character(static_cast<CharType>('#'));
+ 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 (not use_count)
+ {
+ oa->write_character(static_cast<CharType>('}'));
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
private:
/*
@brief write a number to output input
@@ -6039,10 +6869,11 @@ class binary_writer
@tparam NumberType the type of the number
@note This function needs to respect the system's endianess, because bytes
- in CBOR and MessagePack are stored in network order (big endian) and
- therefore need reordering on little endian systems.
+ in CBOR, MessagePack, and UBJSON are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
*/
- template<typename NumberType> void write_number(NumberType n)
+ template<typename NumberType>
+ void write_number(const NumberType n)
{
// step 1: write number to array of length NumberType
std::array<CharType, sizeof(NumberType)> vec;
@@ -6058,6 +6889,200 @@ class binary_writer
oa->write_characters(vec.data(), sizeof(NumberType));
}
+ template<typename NumberType>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix)
+ {
+ if (std::is_floating_point<NumberType>::value)
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('D')); // float64
+ }
+ write_number(n);
+ }
+ else if (std::is_unsigned<NumberType>::value)
+ {
+ if (n <= (std::numeric_limits<int8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('i')); // int8
+ }
+ write_number(static_cast<uint8_t>(n));
+ }
+ else if (n <= (std::numeric_limits<uint8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('U')); // uint8
+ }
+ write_number(static_cast<uint8_t>(n));
+ }
+ else if (n <= (std::numeric_limits<int16_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('I')); // int16
+ }
+ write_number(static_cast<int16_t>(n));
+ }
+ else if (n <= (std::numeric_limits<int32_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('l')); // int32
+ }
+ write_number(static_cast<int32_t>(n));
+ }
+ else if (n <= (std::numeric_limits<int64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('L')); // int64
+ }
+ write_number(static_cast<int64_t>(n));
+ }
+ else
+ {
+ JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
+ }
+ }
+ else
+ {
+ if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('i')); // int8
+ }
+ write_number(static_cast<int8_t>(n));
+ }
+ else if ((std::numeric_limits<uint8_t>::min)() <= n and n <= (std::numeric_limits<uint8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('U')); // uint8
+ }
+ write_number(static_cast<uint8_t>(n));
+ }
+ else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('I')); // int16
+ }
+ write_number(static_cast<int16_t>(n));
+ }
+ else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('l')); // int32
+ }
+ write_number(static_cast<int32_t>(n));
+ }
+ else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(static_cast<CharType>('L')); // int64
+ }
+ write_number(static_cast<int64_t>(n));
+ }
+ // LCOV_EXCL_START
+ else
+ {
+ JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
+ }
+ // LCOV_EXCL_STOP
+ }
+ }
+
+ /*!
+ @brief determine the type prefix of container values
+
+ @note This function does not need to be 100% accurate when it comes to
+ integer limits. In case a number exceeds the limits of int64_t,
+ this will be detected by a later call to function
+ write_number_with_ubjson_prefix. Therefore, we return 'L' for any
+ value that does not fit the previous limits.
+ */
+ char 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<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
+ {
+ return 'i';
+ }
+ else if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
+ {
+ return 'U';
+ }
+ else if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
+ {
+ return 'I';
+ }
+ else if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
+ {
+ return 'l';
+ }
+ else // no check and assume int64_t (see note above)
+ {
+ return 'L';
+ }
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= (std::numeric_limits<int8_t>::max)())
+ {
+ return 'i';
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ return 'U';
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<int16_t>::max)())
+ {
+ return 'I';
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<int32_t>::max)())
+ {
+ return 'l';
+ }
+ else // no check and assume int64_t (see note above)
+ {
+ return 'L';
+ }
+ }
+
+ case value_t::number_float:
+ return 'D';
+
+ case value_t::string:
+ return 'S';
+
+ case value_t::array:
+ return '[';
+
+ case value_t::object:
+ return '{';
+
+ default: // discarded values
+ return 'N';
+ }
+ }
+
private:
/// whether we can assume little endianess
const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
@@ -6065,7 +7090,1140 @@ class binary_writer
/// the output
output_adapter_t<CharType> oa = nullptr;
};
+}
+}
+
+// #include <nlohmann/detail/output/serializer.hpp>
+
+
+#include <algorithm> // reverse, remove, fill, find, none_of
+#include <array> // array
+#include <cassert> // assert
+#include <ciso646> // and, or
+#include <clocale> // localeconv, lconv
+#include <cmath> // labs, isfinite, isnan, signbit
+#include <cstddef> // size_t, ptrdiff_t
+#include <cstdint> // uint8_t
+#include <cstdio> // snprintf
+#include <iomanip> // setfill
+#include <iterator> // next
+#include <limits> // numeric_limits
+#include <string> // string
+#include <sstream> // stringstream
+#include <type_traits> // is_same
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/conversions/to_chars.hpp>
+
+
+#include <cassert> // assert
+#include <ciso646> // or, and, not
+#include <cmath> // signbit, isfinite
+#include <cstdint> // intN_t, uintN_t
+#include <cstring> // memcpy, memmove
+
+namespace nlohmann
+{
+namespace detail
+{
+
+/*!
+@brief implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+
+The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
+
+For a detailed description of the algorithm see:
+
+[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
+ Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
+ Language Design and Implementation, PLDI 2010
+[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
+ Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
+ Design and Implementation, PLDI 1996
+*/
+namespace dtoa_impl
+{
+
+template <typename Target, typename Source>
+Target reinterpret_bits(const Source source)
+{
+ static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+ Target target;
+ std::memcpy(&target, &source, sizeof(Source));
+ return target;
+}
+
+struct diyfp // f * 2^e
+{
+ static constexpr int kPrecision = 64; // = q
+
+ uint64_t f;
+ int e;
+
+ constexpr diyfp() noexcept : f(0), e(0) {}
+ constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+ /*!
+ @brief returns x - y
+ @pre x.e == y.e and x.f >= y.f
+ */
+ static diyfp sub(const diyfp& x, const diyfp& y) noexcept
+ {
+ assert(x.e == y.e);
+ assert(x.f >= y.f);
+
+ return diyfp(x.f - y.f, x.e);
+ }
+
+ /*!
+ @brief returns x * y
+ @note The result is rounded. (Only the upper q bits are returned.)
+ */
+ static diyfp mul(const diyfp& x, const diyfp& y) noexcept
+ {
+ static_assert(kPrecision == 64, "internal error");
+
+ // Computes:
+ // f = round((x.f * y.f) / 2^q)
+ // e = x.e + y.e + q
+
+ // Emulate the 64-bit * 64-bit multiplication:
+ //
+ // p = u * v
+ // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+ // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi )
+ // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 )
+ // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 )
+ // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3)
+ // = (p0_lo ) + 2^32 (Q ) + 2^64 (H )
+ // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H )
+ //
+ // (Since Q might be larger than 2^32 - 1)
+ //
+ // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+ //
+ // (Q_hi + H does not overflow a 64-bit int)
+ //
+ // = p_lo + 2^64 p_hi
+
+ const uint64_t u_lo = x.f & 0xFFFFFFFF;
+ const uint64_t u_hi = x.f >> 32;
+ const uint64_t v_lo = y.f & 0xFFFFFFFF;
+ const uint64_t v_hi = y.f >> 32;
+
+ const uint64_t p0 = u_lo * v_lo;
+ const uint64_t p1 = u_lo * v_hi;
+ const uint64_t p2 = u_hi * v_lo;
+ const uint64_t p3 = u_hi * v_hi;
+
+ const uint64_t p0_hi = p0 >> 32;
+ const uint64_t p1_lo = p1 & 0xFFFFFFFF;
+ const uint64_t p1_hi = p1 >> 32;
+ const uint64_t p2_lo = p2 & 0xFFFFFFFF;
+ const uint64_t p2_hi = p2 >> 32;
+
+ uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+ // The full product might now be computed as
+ //
+ // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+ // p_lo = p0_lo + (Q << 32)
+ //
+ // But in this particular case here, the full p_lo is not required.
+ // Effectively we only need to add the highest bit in p_lo to p_hi (and
+ // Q_hi + 1 does not overflow).
+
+ Q += uint64_t{1} << (64 - 32 - 1); // round, ties up
+
+ const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);
+
+ return diyfp(h, x.e + y.e + 64);
+ }
+
+ /*!
+ @brief normalize x such that the significand is >= 2^(q-1)
+ @pre x.f != 0
+ */
+ static diyfp normalize(diyfp x) noexcept
+ {
+ assert(x.f != 0);
+
+ while ((x.f >> 63) == 0)
+ {
+ x.f <<= 1;
+ x.e--;
+ }
+
+ return x;
+ }
+
+ /*!
+ @brief normalize x such that the result has the exponent E
+ @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+ */
+ static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
+ {
+ const int delta = x.e - target_exponent;
+
+ assert(delta >= 0);
+ assert(((x.f << delta) >> delta) == x.f);
+
+ return diyfp(x.f << delta, target_exponent);
+ }
+};
+
+struct boundaries
+{
+ diyfp w;
+ diyfp minus;
+ diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+
+@pre value must be finite and positive
+*/
+template <typename FloatType>
+boundaries compute_boundaries(FloatType value)
+{
+ assert(std::isfinite(value));
+ assert(value > 0);
+
+ // Convert the IEEE representation into a diyfp.
+ //
+ // If v is denormal:
+ // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1))
+ // If v is normalized:
+ // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+ static_assert(std::numeric_limits<FloatType>::is_iec559,
+ "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
+
+ constexpr int kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+ constexpr int kBias = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+ constexpr int kMinExp = 1 - kBias;
+ constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+ using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type;
+
+ const uint64_t bits = reinterpret_bits<bits_type>(value);
+ const uint64_t E = bits >> (kPrecision - 1);
+ const uint64_t F = bits & (kHiddenBit - 1);
+
+ const bool is_denormal = (E == 0);
+ const diyfp v = is_denormal
+ ? diyfp(F, kMinExp)
+ : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+ // Compute the boundaries m- and m+ of the floating-point value
+ // v = f * 2^e.
+ //
+ // Determine v- and v+, the floating-point predecessor and successor if v,
+ // respectively.
+ //
+ // v- = v - 2^e if f != 2^(p-1) or e == e_min (A)
+ // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B)
+ //
+ // v+ = v + 2^e
+ //
+ // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+ // between m- and m+ round to v, regardless of how the input rounding
+ // algorithm breaks ties.
+ //
+ // ---+-------------+-------------+-------------+-------------+--- (A)
+ // v- m- v m+ v+
+ //
+ // -----------------+------+------+-------------+-------------+--- (B)
+ // v- m- v m+ v+
+
+ const bool lower_boundary_is_closer = (F == 0 and E > 1);
+ const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
+ const diyfp m_minus = lower_boundary_is_closer
+ ? diyfp(4 * v.f - 1, v.e - 2) // (B)
+ : diyfp(2 * v.f - 1, v.e - 1); // (A)
+
+ // Determine the normalized w+ = m+.
+ const diyfp w_plus = diyfp::normalize(m_plus);
+
+ // Determine w- = m- such that e_(w-) = e_(w+).
+ const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+ return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+// alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+// <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+// = (f div 2^-e) + (f mod 2^-e) * 2^e
+// = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+// -e >= 32 or e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+// (10 * p2) div 2^-e = d[-1]
+// (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+// -e <= 60 or e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+ uint64_t f;
+ int e;
+ int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+
+ alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e)
+{
+ // Now
+ //
+ // alpha <= e_c + e + q <= gamma (1)
+ // ==> f_c * 2^alpha <= c * 2^e * 2^q
+ //
+ // and since the c's are normalized, 2^(q-1) <= f_c,
+ //
+ // ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+ // ==> 2^(alpha - e - 1) <= c
+ //
+ // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as
+ //
+ // k = ceil( log_10( 2^(alpha - e - 1) ) )
+ // = ceil( (alpha - e - 1) * log_10(2) )
+ //
+ // From the paper:
+ // "In theory the result of the procedure could be wrong since c is rounded,
+ // and the computation itself is approximated [...]. In practice, however,
+ // this simple function is sufficient."
+ //
+ // For IEEE double precision floating-point numbers converted into
+ // normalized diyfp's w = f * 2^e, with q = 64,
+ //
+ // e >= -1022 (min IEEE exponent)
+ // -52 (p - 1)
+ // -52 (p - 1, possibly normalize denormal IEEE numbers)
+ // -11 (normalize the diyfp)
+ // = -1137
+ //
+ // and
+ //
+ // e <= +1023 (max IEEE exponent)
+ // -52 (p - 1)
+ // -11 (normalize the diyfp)
+ // = 960
+ //
+ // This binary exponent range [-1137,960] results in a decimal exponent
+ // range [-307,324]. One does not need to store a cached power for each
+ // k in this range. For each such k it suffices to find a cached power
+ // such that the exponent of the product lies in [alpha,gamma].
+ // This implies that the difference of the decimal exponents of adjacent
+ // table entries must be less than or equal to
+ //
+ // floor( (gamma - alpha) * log_10(2) ) = 8.
+ //
+ // (A smaller distance gamma-alpha would require a larger table.)
+
+ // NB:
+ // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+ constexpr int kCachedPowersSize = 79;
+ constexpr int kCachedPowersMinDecExp = -300;
+ constexpr int kCachedPowersDecStep = 8;
+
+ static constexpr cached_power kCachedPowers[] =
+ {
+ { 0xAB70FE17C79AC6CA, -1060, -300 },
+ { 0xFF77B1FCBEBCDC4F, -1034, -292 },
+ { 0xBE5691EF416BD60C, -1007, -284 },
+ { 0x8DD01FAD907FFC3C, -980, -276 },
+ { 0xD3515C2831559A83, -954, -268 },
+ { 0x9D71AC8FADA6C9B5, -927, -260 },
+ { 0xEA9C227723EE8BCB, -901, -252 },
+ { 0xAECC49914078536D, -874, -244 },
+ { 0x823C12795DB6CE57, -847, -236 },
+ { 0xC21094364DFB5637, -821, -228 },
+ { 0x9096EA6F3848984F, -794, -220 },
+ { 0xD77485CB25823AC7, -768, -212 },
+ { 0xA086CFCD97BF97F4, -741, -204 },
+ { 0xEF340A98172AACE5, -715, -196 },
+ { 0xB23867FB2A35B28E, -688, -188 },
+ { 0x84C8D4DFD2C63F3B, -661, -180 },
+ { 0xC5DD44271AD3CDBA, -635, -172 },
+ { 0x936B9FCEBB25C996, -608, -164 },
+ { 0xDBAC6C247D62A584, -582, -156 },
+ { 0xA3AB66580D5FDAF6, -555, -148 },
+ { 0xF3E2F893DEC3F126, -529, -140 },
+ { 0xB5B5ADA8AAFF80B8, -502, -132 },
+ { 0x87625F056C7C4A8B, -475, -124 },
+ { 0xC9BCFF6034C13053, -449, -116 },
+ { 0x964E858C91BA2655, -422, -108 },
+ { 0xDFF9772470297EBD, -396, -100 },
+ { 0xA6DFBD9FB8E5B88F, -369, -92 },
+ { 0xF8A95FCF88747D94, -343, -84 },
+ { 0xB94470938FA89BCF, -316, -76 },
+ { 0x8A08F0F8BF0F156B, -289, -68 },
+ { 0xCDB02555653131B6, -263, -60 },
+ { 0x993FE2C6D07B7FAC, -236, -52 },
+ { 0xE45C10C42A2B3B06, -210, -44 },
+ { 0xAA242499697392D3, -183, -36 },
+ { 0xFD87B5F28300CA0E, -157, -28 },
+ { 0xBCE5086492111AEB, -130, -20 },
+ { 0x8CBCCC096F5088CC, -103, -12 },
+ { 0xD1B71758E219652C, -77, -4 },
+ { 0x9C40000000000000, -50, 4 },
+ { 0xE8D4A51000000000, -24, 12 },
+ { 0xAD78EBC5AC620000, 3, 20 },
+ { 0x813F3978F8940984, 30, 28 },
+ { 0xC097CE7BC90715B3, 56, 36 },
+ { 0x8F7E32CE7BEA5C70, 83, 44 },
+ { 0xD5D238A4ABE98068, 109, 52 },
+ { 0x9F4F2726179A2245, 136, 60 },
+ { 0xED63A231D4C4FB27, 162, 68 },
+ { 0xB0DE65388CC8ADA8, 189, 76 },
+ { 0x83C7088E1AAB65DB, 216, 84 },
+ { 0xC45D1DF942711D9A, 242, 92 },
+ { 0x924D692CA61BE758, 269, 100 },
+ { 0xDA01EE641A708DEA, 295, 108 },
+ { 0xA26DA3999AEF774A, 322, 116 },
+ { 0xF209787BB47D6B85, 348, 124 },
+ { 0xB454E4A179DD1877, 375, 132 },
+ { 0x865B86925B9BC5C2, 402, 140 },
+ { 0xC83553C5C8965D3D, 428, 148 },
+ { 0x952AB45CFA97A0B3, 455, 156 },
+ { 0xDE469FBD99A05FE3, 481, 164 },
+ { 0xA59BC234DB398C25, 508, 172 },
+ { 0xF6C69A72A3989F5C, 534, 180 },
+ { 0xB7DCBF5354E9BECE, 561, 188 },
+ { 0x88FCF317F22241E2, 588, 196 },
+ { 0xCC20CE9BD35C78A5, 614, 204 },
+ { 0x98165AF37B2153DF, 641, 212 },
+ { 0xE2A0B5DC971F303A, 667, 220 },
+ { 0xA8D9D1535CE3B396, 694, 228 },
+ { 0xFB9B7CD9A4A7443C, 720, 236 },
+ { 0xBB764C4CA7A44410, 747, 244 },
+ { 0x8BAB8EEFB6409C1A, 774, 252 },
+ { 0xD01FEF10A657842C, 800, 260 },
+ { 0x9B10A4E5E9913129, 827, 268 },
+ { 0xE7109BFBA19C0C9D, 853, 276 },
+ { 0xAC2820D9623BF429, 880, 284 },
+ { 0x80444B5E7AA7CF85, 907, 292 },
+ { 0xBF21E44003ACDD2D, 933, 300 },
+ { 0x8E679C2F5E44FF8F, 960, 308 },
+ { 0xD433179D9C8CB841, 986, 316 },
+ { 0x9E19DB92B4E31BA9, 1013, 324 },
+ };
+
+ // This computation gives exactly the same results for k as
+ // k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+ // for |e| <= 1500, but doesn't require floating-point operations.
+ // NB: log_10(2) ~= 78913 / 2^18
+ assert(e >= -1500);
+ assert(e <= 1500);
+ const int f = kAlpha - e - 1;
+ const int k = (f * 78913) / (1 << 18) + (f > 0);
+
+ const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+ assert(index >= 0);
+ assert(index < kCachedPowersSize);
+ static_cast<void>(kCachedPowersSize); // Fix warning.
+
+ const cached_power cached = kCachedPowers[index];
+ assert(kAlpha <= cached.e + e + 64);
+ assert(kGamma >= cached.e + e + 64);
+
+ return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const uint32_t n, uint32_t& pow10)
+{
+ // LCOV_EXCL_START
+ if (n >= 1000000000)
+ {
+ pow10 = 1000000000;
+ return 10;
+ }
+ // LCOV_EXCL_STOP
+ else if (n >= 100000000)
+ {
+ pow10 = 100000000;
+ return 9;
+ }
+ else if (n >= 10000000)
+ {
+ pow10 = 10000000;
+ return 8;
+ }
+ else if (n >= 1000000)
+ {
+ pow10 = 1000000;
+ return 7;
+ }
+ else if (n >= 100000)
+ {
+ pow10 = 100000;
+ return 6;
+ }
+ else if (n >= 10000)
+ {
+ pow10 = 10000;
+ return 5;
+ }
+ else if (n >= 1000)
+ {
+ pow10 = 1000;
+ return 4;
+ }
+ else if (n >= 100)
+ {
+ pow10 = 100;
+ return 3;
+ }
+ else if (n >= 10)
+ {
+ pow10 = 10;
+ return 2;
+ }
+ else
+ {
+ pow10 = 1;
+ return 1;
+ }
+}
+
+inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,
+ uint64_t rest, uint64_t ten_k)
+{
+ assert(len >= 1);
+ assert(dist <= delta);
+ assert(rest <= delta);
+ assert(ten_k > 0);
+
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // ten_k
+ // <------>
+ // <---- rest ---->
+ // --------------[------------------+----+--------------]--------------
+ // w V
+ // = buf * 10^k
+ //
+ // ten_k represents a unit-in-the-last-place in the decimal representation
+ // stored in buf.
+ // Decrement buf by ten_k while this takes buf closer to w.
+
+ // The tests are written in this order to avoid overflow in unsigned
+ // integer arithmetic.
+
+ while (rest < dist
+ and delta - rest >= ten_k
+ and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
+ {
+ assert(buf[len - 1] != '0');
+ buf[len - 1]--;
+ rest += ten_k;
+ }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
+ diyfp M_minus, diyfp w, diyfp M_plus)
+{
+ static_assert(kAlpha >= -60, "internal error");
+ static_assert(kGamma <= -32, "internal error");
+
+ // Generates the digits (and the exponent) of a decimal floating-point
+ // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+ // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
+ //
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // Grisu2 generates the digits of M+ from left to right and stops as soon as
+ // V is in [M-,M+].
+
+ assert(M_plus.e >= kAlpha);
+ assert(M_plus.e <= kGamma);
+
+ uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+ uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e)
+
+ // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+ //
+ // M+ = f * 2^e
+ // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+ // = ((p1 ) * 2^-e + (p2 )) * 2^e
+ // = p1 + p2 * 2^e
+
+ const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e);
+
+ uint32_t p1 = static_cast<uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+ uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e
+
+ // 1)
+ //
+ // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+ assert(p1 > 0);
+
+ uint32_t pow10;
+ const int k = find_largest_pow10(p1, pow10);
+
+ // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+ //
+ // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+ // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1))
+ //
+ // M+ = p1 + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+ // = d[k-1] * 10^(k-1) + ( rest) * 2^e
+ //
+ // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+ //
+ // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+ //
+ // but stop as soon as
+ //
+ // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+ int n = k;
+ while (n > 0)
+ {
+ // Invariants:
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k)
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ //
+ const uint32_t d = p1 / pow10; // d = p1 div 10^(n-1)
+ const uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1)
+ //
+ // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+ // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+ //
+ assert(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+ //
+ p1 = r;
+ n--;
+ //
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e)
+ // pow10 = 10^n
+ //
+
+ // Now check if enough digits have been generated.
+ // Compute
+ //
+ // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+ //
+ // Note:
+ // Since rest and delta share the same exponent e, it suffices to
+ // compare the significands.
+ const uint64_t rest = (uint64_t{p1} << -one.e) + p2;
+ if (rest <= delta)
+ {
+ // V = buffer * 10^n, with M- <= V <= M+.
+
+ decimal_exponent += n;
+
+ // We may now just stop. But instead look if the buffer could be
+ // decremented to bring V closer to w.
+ //
+ // pow10 = 10^n is now 1 ulp in the decimal representation V.
+ // The rounding procedure works with diyfp's with an implicit
+ // exponent of e.
+ //
+ // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+ //
+ const uint64_t ten_n = uint64_t{pow10} << -one.e;
+ grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+ return;
+ }
+
+ pow10 /= 10;
+ //
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ // Invariants restored.
+ }
+
+ // 2)
+ //
+ // The digits of the integral part have been generated:
+ //
+ // M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+ // = buffer + p2 * 2^e
+ //
+ // Now generate the digits of the fractional part p2 * 2^e.
+ //
+ // Note:
+ // No decimal point is generated: the exponent is adjusted instead.
+ //
+ // p2 actually represents the fraction
+ //
+ // p2 * 2^e
+ // = p2 / 2^-e
+ // = d[-1] / 10^1 + d[-2] / 10^2 + ...
+ //
+ // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+ //
+ // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+ // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+ //
+ // using
+ //
+ // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+ // = ( d) * 2^-e + ( r)
+ //
+ // or
+ // 10^m * p2 * 2^e = d + r * 2^e
+ //
+ // i.e.
+ //
+ // M+ = buffer + p2 * 2^e
+ // = buffer + 10^-m * (d + r * 2^e)
+ // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+ //
+ // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+ assert(p2 > delta);
+
+ int m = 0;
+ for (;;)
+ {
+ // Invariant:
+ // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
+ // = buffer * 10^-m + 10^-m * (p2 ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
+ //
+ assert(p2 <= UINT64_MAX / 10);
+ p2 *= 10;
+ const uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e
+ const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+ //
+ // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+ // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ assert(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ p2 = r;
+ m++;
+ //
+ // M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+ // Invariant restored.
+
+ // Check if enough digits have been generated.
+ //
+ // 10^-m * p2 * 2^e <= delta * 2^e
+ // p2 * 2^e <= 10^m * delta * 2^e
+ // p2 <= 10^m * delta
+ delta *= 10;
+ dist *= 10;
+ if (p2 <= delta)
+ {
+ break;
+ }
+ }
+
+ // V = buffer * 10^-m, with M- <= V <= M+.
+
+ decimal_exponent -= m;
+
+ // 1 ulp in the decimal representation is now 10^-m.
+ // Since delta and dist are now scaled by 10^m, we need to do the
+ // same with ulp in order to keep the units in sync.
+ //
+ // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+ //
+ const uint64_t ten_m = one.f;
+ grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+ // By construction this algorithm generates the shortest possible decimal
+ // number (Loitsch, Theorem 6.2) which rounds back to w.
+ // For an input number of precision p, at least
+ //
+ // N = 1 + ceil(p * log_10(2))
+ //
+ // decimal digits are sufficient to identify all binary floating-point
+ // numbers (Matula, "In-and-Out conversions").
+ // This implies that the algorithm does not produce more than N decimal
+ // digits.
+ //
+ // N = 17 for p = 53 (IEEE double precision)
+ // N = 9 for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+ diyfp m_minus, diyfp v, diyfp m_plus)
+{
+ assert(m_plus.e == m_minus.e);
+ assert(m_plus.e == v.e);
+
+ // --------(-----------------------+-----------------------)-------- (A)
+ // m- v m+
+ //
+ // --------------------(-----------+-----------------------)-------- (B)
+ // m- v m+
+ //
+ // First scale v (and m- and m+) such that the exponent is in the range
+ // [alpha, gamma].
+
+ const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+ const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+ // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
+ const diyfp w = diyfp::mul(v, c_minus_k);
+ const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+ const diyfp w_plus = diyfp::mul(m_plus, c_minus_k);
+
+ // ----(---+---)---------------(---+---)---------------(---+---)----
+ // w- w w+
+ // = c*m- = c*v = c*m+
+ //
+ // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+ // w+ are now off by a small amount.
+ // In fact:
+ //
+ // w - v * 10^k < 1 ulp
+ //
+ // To account for this inaccuracy, add resp. subtract 1 ulp.
+ //
+ // --------+---[---------------(---+---)---------------]---+--------
+ // w- M- w M+ w+
+ //
+ // Now any number in [M-, M+] (bounds included) will round to w when input,
+ // regardless of how the input rounding algorithm breaks ties.
+ //
+ // And digit_gen generates the shortest possible such number in [M-, M+].
+ // Note that this does not mean that Grisu2 always generates the shortest
+ // possible number in the interval (m-, m+).
+ const diyfp M_minus(w_minus.f + 1, w_minus.e);
+ const diyfp M_plus (w_plus.f - 1, w_plus.e );
+
+ decimal_exponent = -cached.k; // = -(-k) = k
+
+ grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template <typename FloatType>
+void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
+{
+ static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+ "internal error: not enough precision");
+
+ assert(std::isfinite(value));
+ assert(value > 0);
+
+ // If the neighbors (and boundaries) of 'value' are always computed for double-precision
+ // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
+ // decimal representations are not exactly "short".
+ //
+ // The documentation for 'std::to_chars' (http://en.cppreference.com/w/cpp/utility/to_chars)
+ // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
+ // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'
+ // does.
+ // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
+ // representation using the corresponding std::from_chars function recovers value exactly". That
+ // indicates that single precision floating-point numbers should be recovered using
+ // 'std::strtof'.
+ //
+ // NB: If the neighbors are computed for single-precision numbers, there is a single float
+ // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
+ // value is off by 1 ulp.
+#if 0
+ const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+ const boundaries w = compute_boundaries(value);
+#endif
+
+ grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+inline char* append_exponent(char* buf, int e)
+{
+ assert(e > -1000);
+ assert(e < 1000);
+
+ if (e < 0)
+ {
+ e = -e;
+ *buf++ = '-';
+ }
+ else
+ {
+ *buf++ = '+';
+ }
+
+ uint32_t k = static_cast<uint32_t>(e);
+ if (k < 10)
+ {
+ // Always print at least two digits in the exponent.
+ // This is for compatibility with printf("%g").
+ *buf++ = '0';
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else if (k < 100)
+ {
+ *buf++ = static_cast<char>('0' + k / 10);
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else
+ {
+ *buf++ = static_cast<char>('0' + k / 100);
+ k %= 100;
+ *buf++ = static_cast<char>('0' + k / 10);
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+
+ return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+ int min_exp, int max_exp)
+{
+ assert(min_exp < 0);
+ assert(max_exp > 0);
+
+ const int k = len;
+ const int n = len + decimal_exponent;
+
+ // v = buf * 10^(n-k)
+ // k is the length of the buffer (number of decimal digits)
+ // n is the position of the decimal point relative to the start of the buffer.
+
+ if (k <= n and n <= max_exp)
+ {
+ // digits[000]
+ // len <= max_exp + 2
+
+ std::memset(buf + k, '0', static_cast<size_t>(n - k));
+ // Make it look like a floating-point number (#362, #378)
+ buf[n + 0] = '.';
+ buf[n + 1] = '0';
+ return buf + (n + 2);
+ }
+
+ if (0 < n and n <= max_exp)
+ {
+ // dig.its
+ // len <= max_digits10 + 1
+
+ assert(k > n);
+
+ std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n));
+ buf[n] = '.';
+ return buf + (k + 1);
+ }
+
+ if (min_exp < n and n <= 0)
+ {
+ // 0.[000]digits
+ // len <= 2 + (-min_exp - 1) + max_digits10
+
+ std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k));
+ buf[0] = '0';
+ buf[1] = '.';
+ std::memset(buf + 2, '0', static_cast<size_t>(-n));
+ return buf + (2 + (-n) + k);
+ }
+
+ if (k == 1)
+ {
+ // dE+123
+ // len <= 1 + 5
+
+ buf += 1;
+ }
+ else
+ {
+ // d.igitsE+123
+ // len <= max_digits10 + 1 + 5
+
+ std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1));
+ buf[1] = '.';
+ buf += 1 + k;
+ }
+
+ *buf++ = 'e';
+ return append_exponent(buf, n - 1);
+}
+
+} // namespace dtoa_impl
+
+/*!
+@brief generates a decimal representation of the floating-point number value in [first, last).
+
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template <typename FloatType>
+char* to_chars(char* first, char* last, FloatType value)
+{
+ static_cast<void>(last); // maybe unused - fix warning
+ assert(std::isfinite(value));
+
+ // Use signbit(value) instead of (value < 0) since signbit works for -0.
+ if (std::signbit(value))
+ {
+ value = -value;
+ *first++ = '-';
+ }
+
+ if (value == 0) // +-0
+ {
+ *first++ = '0';
+ // Make it look like a floating-point number (#362, #378)
+ *first++ = '.';
+ *first++ = '0';
+ return first;
+ }
+
+ assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
+
+ // Compute v = buffer * 10^decimal_exponent.
+ // The decimal digits are stored in the buffer, which needs to be interpreted
+ // as an unsigned decimal integer.
+ // len is the length of the buffer, i.e. the number of decimal digits.
+ int len = 0;
+ int decimal_exponent = 0;
+ dtoa_impl::grisu2(first, len, decimal_exponent, value);
+
+ assert(len <= std::numeric_limits<FloatType>::max_digits10);
+
+ // Format the buffer like printf("%.*g", prec, value)
+ constexpr int kMinExp = -4;
+ // Use digits10 here to increase compatibility with version 2.
+ constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
+
+ assert(last - first >= kMaxExp + 2);
+ assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+ assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+ return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+} // namespace detail
+} // namespace nlohmann
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
///////////////////
// serialization //
///////////////////
@@ -6077,6 +8235,9 @@ class serializer
using number_float_t = typename BasicJsonType::number_float_t;
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ static constexpr uint8_t UTF8_ACCEPT = 0;
+ static constexpr uint8_t UTF8_REJECT = 1;
+
public:
/*!
@param[in] s output stream to serialize to
@@ -6086,7 +8247,8 @@ class serializer
: o(std::move(s)), loc(std::localeconv()),
thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
- indent_char(ichar), indent_string(512, indent_char) {}
+ indent_char(ichar), indent_string(512, indent_char)
+ {}
// delete because of pointer members
serializer(const serializer&) = delete;
@@ -6303,171 +8465,6 @@ class serializer
private:
/*!
- @brief returns the number of expected bytes following in UTF-8 string
-
- @param[in] u the first byte of a UTF-8 string
- @return the number of expected bytes following
- */
- static constexpr std::size_t bytes_following(const uint8_t u)
- {
- return ((u <= 127) ? 0
- : ((192 <= u and u <= 223) ? 1
- : ((224 <= u and u <= 239) ? 2
- : ((240 <= u and u <= 247) ? 3 : std::string::npos))));
- }
-
- /*!
- @brief calculates the extra space to escape a JSON string
-
- @param[in] s the string to escape
- @param[in] ensure_ascii whether to escape non-ASCII characters with
- \uXXXX sequences
- @return the number of characters required to escape string @a s
-
- @complexity Linear in the length of string @a s.
- */
- static std::size_t extra_space(const string_t& s,
- const bool ensure_ascii) noexcept
- {
- std::size_t res = 0;
-
- for (std::size_t i = 0; i < s.size(); ++i)
- {
- switch (s[i])
- {
- // control characters that can be escaped with a backslash
- case '"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- {
- // from c (1 byte) to \x (2 bytes)
- res += 1;
- break;
- }
-
- // control characters that need \uxxxx escaping
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- case 0x0B:
- case 0x0E:
- case 0x0F:
- case 0x10:
- case 0x11:
- case 0x12:
- case 0x13:
- case 0x14:
- case 0x15:
- case 0x16:
- case 0x17:
- case 0x18:
- case 0x19:
- case 0x1A:
- case 0x1B:
- case 0x1C:
- case 0x1D:
- case 0x1E:
- case 0x1F:
- {
- // from c (1 byte) to \uxxxx (6 bytes)
- res += 5;
- break;
- }
-
- default:
- {
- if (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F))
- {
- const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
- // invalid characters will be detected by throw_if_invalid_utf8
- assert (bytes != std::string::npos);
-
- if (bytes == 3)
- {
- // codepoints that need 4 bytes (i.e., 3 additional
- // bytes) in UTF-8 need a surrogate pair when \u
- // escaping is used: from 4 bytes to \uxxxx\uxxxx
- // (12 bytes)
- res += (12 - bytes - 1);
- }
- else
- {
- // from x bytes to \uxxxx (6 bytes)
- res += (6 - bytes - 1);
- }
-
- // skip the additional bytes
- i += bytes;
- }
- break;
- }
- }
- }
-
- return res;
- }
-
- static void escape_codepoint(int codepoint, string_t& result, std::size_t& pos)
- {
- // expecting a proper codepoint
- assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
-
- // the last written character was the backslash before the 'u'
- assert(result[pos] == '\\');
-
- // write the 'u'
- result[++pos] = 'u';
-
- // convert a number 0..15 to its hex representation (0..f)
- static const std::array<char, 16> hexify =
- {
- {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
- }
- };
-
- if (codepoint < 0x10000)
- {
- // codepoints U+0000..U+FFFF can be represented as \uxxxx.
- result[++pos] = hexify[(codepoint >> 12) & 0x0F];
- result[++pos] = hexify[(codepoint >> 8) & 0x0F];
- result[++pos] = hexify[(codepoint >> 4) & 0x0F];
- result[++pos] = hexify[codepoint & 0x0F];
- }
- else
- {
- // codepoints U+10000..U+10FFFF need a surrogate pair to be
- // represented as \uxxxx\uxxxx.
- // http://www.unicode.org/faq/utf_bom.html#utf16-4
- codepoint -= 0x10000;
- const int high_surrogate = 0xD800 | ((codepoint >> 10) & 0x3FF);
- const int low_surrogate = 0xDC00 | (codepoint & 0x3FF);
- result[++pos] = hexify[(high_surrogate >> 12) & 0x0F];
- result[++pos] = hexify[(high_surrogate >> 8) & 0x0F];
- result[++pos] = hexify[(high_surrogate >> 4) & 0x0F];
- result[++pos] = hexify[high_surrogate & 0x0F];
- ++pos; // backslash is already in output
- result[++pos] = 'u';
- result[++pos] = hexify[(low_surrogate >> 12) & 0x0F];
- result[++pos] = hexify[(low_surrogate >> 8) & 0x0F];
- result[++pos] = hexify[(low_surrogate >> 4) & 0x0F];
- result[++pos] = hexify[low_surrogate & 0x0F];
- }
-
- ++pos;
- }
-
- /*!
@brief dump escaped string
Escape a string by replacing certain special characters by a sequence of an
@@ -6481,145 +8478,146 @@ class serializer
@complexity Linear in the length of string @a s.
*/
- void dump_escaped(const string_t& s, const bool ensure_ascii) const
+ void dump_escaped(const string_t& s, const bool ensure_ascii)
{
- throw_if_invalid_utf8(s);
-
- const auto space = extra_space(s, ensure_ascii);
- if (space == 0)
- {
- o->write_characters(s.c_str(), s.size());
- return;
- }
-
- // create a result string of necessary size
- string_t result(s.size() + space, '\\');
- std::size_t pos = 0;
+ uint32_t codepoint;
+ uint8_t state = UTF8_ACCEPT;
+ std::size_t bytes = 0; // number of bytes written to string_buffer
for (std::size_t i = 0; i < s.size(); ++i)
{
- switch (s[i])
- {
- case '"': // quotation mark (0x22)
- {
- result[pos + 1] = '"';
- pos += 2;
- break;
- }
-
- case '\\': // reverse solidus (0x5C)
- {
- // nothing to change
- pos += 2;
- break;
- }
-
- case '\b': // backspace (0x08)
- {
- result[pos + 1] = 'b';
- pos += 2;
- break;
- }
-
- case '\f': // formfeed (0x0C)
- {
- result[pos + 1] = 'f';
- pos += 2;
- break;
- }
-
- case '\n': // newline (0x0A)
- {
- result[pos + 1] = 'n';
- pos += 2;
- break;
- }
+ const auto byte = static_cast<uint8_t>(s[i]);
- case '\r': // carriage return (0x0D)
+ switch (decode(state, codepoint, byte))
+ {
+ case UTF8_ACCEPT: // decode found a new code point
{
- result[pos + 1] = 'r';
- pos += 2;
- break;
- }
+ switch (codepoint)
+ {
+ case 0x08: // backspace
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'b';
+ break;
+ }
- case '\t': // horizontal tab (0x09)
- {
- result[pos + 1] = 't';
- pos += 2;
- break;
- }
+ case 0x09: // horizontal tab
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 't';
+ break;
+ }
- default:
- {
- // escape control characters (0x00..0x1F) or, if
- // ensure_ascii parameter is used, non-ASCII characters
- if ((0x00 <= s[i] and s[i] <= 0x1F) or
- (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F)))
- {
- const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
- // invalid characters will be detected by throw_if_invalid_utf8
- assert (bytes != std::string::npos);
+ case 0x0A: // newline
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'n';
+ break;
+ }
- // check that the additional bytes are present
- assert(i + bytes < s.size());
+ case 0x0C: // formfeed
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'f';
+ break;
+ }
- // to use \uxxxx escaping, we first need to calculate
- // the codepoint from the UTF-8 bytes
- int codepoint = 0;
+ case 0x0D: // carriage return
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'r';
+ break;
+ }
- // bytes is unsigned type:
- assert(bytes <= 3);
- switch (bytes)
+ case 0x22: // quotation mark
{
- case 0:
- {
- codepoint = s[i] & 0xFF;
- break;
- }
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = '\"';
+ break;
+ }
- case 1:
- {
- codepoint = ((s[i] & 0x3F) << 6)
- + (s[i + 1] & 0x7F);
- break;
- }
+ case 0x5C: // reverse solidus
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = '\\';
+ break;
+ }
- case 2:
+ default:
+ {
+ // escape control characters (0x00..0x1F) or, if
+ // ensure_ascii parameter is used, non-ASCII characters
+ if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
{
- codepoint = ((s[i] & 0x1F) << 12)
- + ((s[i + 1] & 0x7F) << 6)
- + (s[i + 2] & 0x7F);
- break;
+ if (codepoint <= 0xFFFF)
+ {
+ std::snprintf(string_buffer.data() + bytes, 7, "\\u%04x",
+ static_cast<uint16_t>(codepoint));
+ bytes += 6;
+ }
+ else
+ {
+ std::snprintf(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
+ static_cast<uint16_t>(0xD7C0 + (codepoint >> 10)),
+ static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF)));
+ bytes += 12;
+ }
}
-
- case 3:
+ else
{
- codepoint = ((s[i] & 0xF) << 18)
- + ((s[i + 1] & 0x7F) << 12)
- + ((s[i + 2] & 0x7F) << 6)
- + (s[i + 3] & 0x7F);
- break;
+ // copy byte to buffer (all previous bytes
+ // been copied have in default case above)
+ string_buffer[bytes++] = s[i];
}
-
- default:
- break; // LCOV_EXCL_LINE
+ break;
}
+ }
- escape_codepoint(codepoint, result, pos);
- i += bytes;
+ // write buffer and reset index; there must be 13 bytes
+ // left, as this is the maximal number of bytes to be
+ // written ("\uxxxx\uxxxx\0") for one code point
+ if (string_buffer.size() - bytes < 13)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ bytes = 0;
}
- else
+ break;
+ }
+
+ case UTF8_REJECT: // decode found invalid UTF-8 byte
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(byte);
+ JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str()));
+ }
+
+ default: // decode found yet incomplete multi-byte code point
+ {
+ if (not ensure_ascii)
{
- // all other characters are added as-is
- result[pos++] = s[i];
+ // code point will not be escaped - copy byte to buffer
+ string_buffer[bytes++] = s[i];
}
break;
}
}
}
- assert(pos == result.size());
- o->write_characters(result.c_str(), result.size());
+ if (JSON_LIKELY(state == UTF8_ACCEPT))
+ {
+ // write buffer
+ if (bytes > 0)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ }
+ }
+ else
+ {
+ // we finish reading, but do not accept: string was incomplete
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(static_cast<uint8_t>(s.back()));
+ JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str()));
+ }
}
/*!
@@ -6679,14 +8677,36 @@ class serializer
void dump_float(number_float_t x)
{
// NaN / inf
- if (not std::isfinite(x) or std::isnan(x))
+ if (not std::isfinite(x))
{
o->write_characters("null", 4);
return;
}
- // get number of digits for a text -> float -> text round-trip
- static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
+ // If number_float_t is an IEEE-754 single or double precision number,
+ // use the Grisu2 algorithm to produce short numbers which are
+ // guaranteed to round-trip, using strtof and strtod, resp.
+ //
+ // NB: The test below works if <long double> == <double>.
+ static constexpr bool is_ieee_single_or_double
+ = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or
+ (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);
+
+ dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
+ }
+
+ void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
+ {
+ char* begin = number_buffer.data();
+ char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
+
+ o->write_characters(begin, static_cast<size_t>(end - begin));
+ }
+
+ void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
+ {
+ // get number of digits for a float -> text -> float round-trip
+ static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
// the actual conversion
std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
@@ -6744,15 +8764,16 @@ class serializer
followed.
@param[in,out] state the state of the decoding
+ @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
@param[in] byte next byte to decode
+ @return new state
- @note The function has been edited: a std::array is used and the code
- point is not calculated.
+ @note The function has been edited: a std::array is used.
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
*/
- static void decode(uint8_t& state, const uint8_t byte)
+ static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept
{
static const std::array<uint8_t, 400> utf8d =
{
@@ -6775,42 +8796,13 @@ class serializer
};
const uint8_t type = utf8d[byte];
- state = utf8d[256u + state * 16u + type];
- }
- /*!
- @brief throw an exception if a string is not UTF-8 encoded
-
- @param[in] str UTF-8 string to check
- @throw type_error.316 if passed string is not UTF-8 encoded
-
- @since version 3.0.0
- */
- static void throw_if_invalid_utf8(const std::string& str)
- {
- // start with state 0 (= accept)
- uint8_t state = 0;
+ codep = (state != UTF8_ACCEPT)
+ ? (byte & 0x3fu) | (codep << 6)
+ : static_cast<uint32_t>(0xff >> type) & (byte);
- for (size_t i = 0; i < str.size(); ++i)
- {
- const auto byte = static_cast<uint8_t>(str[i]);
- decode(state, byte);
- if (state == 1)
- {
- // state 1 means reject
- std::stringstream ss;
- ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(byte);
- JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str()));
- }
- }
-
- if (state != 0)
- {
- // we finish reading, but do not accept: string was incomplete
- std::stringstream ss;
- ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(static_cast<uint8_t>(str.back()));
- JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str()));
- }
+ state = utf8d[256u + state * 16u + type];
+ return state;
}
private:
@@ -6827,13 +8819,27 @@ class serializer
/// the locale's decimal point character
const char decimal_point = '\0';
+ /// string buffer
+ std::array<char, 512> string_buffer{{}};
+
/// the indentation character
const char indent_char;
-
/// the indentation string
string_t indent_string;
};
+}
+}
+
+// #include <nlohmann/detail/json_ref.hpp>
+
+#include <initializer_list>
+#include <utility>
+
+namespace nlohmann
+{
+namespace detail
+{
template<typename BasicJsonType>
class json_ref
{
@@ -6886,74 +8892,30 @@ class json_ref
value_type* value_ref = nullptr;
const bool is_rvalue;
};
-
-} // namespace detail
-
-/// namespace to hold default `to_json` / `from_json` functions
-namespace
-{
-constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
-constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
+}
}
+// #include <nlohmann/detail/json_pointer.hpp>
-/*!
-@brief default JSONSerializer template argument
-
-This serializer ignores the template arguments and uses ADL
-([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
-for serialization.
-*/
-template<typename, typename>
-struct adl_serializer
-{
- /*!
- @brief convert a JSON value to any value type
-
- This function is usually called by the `get()` function of the
- @ref basic_json class (either explicit or via conversion operators).
-
- @param[in] j JSON value to read from
- @param[in,out] val value to write to
- */
- template<typename BasicJsonType, typename ValueType>
- static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
- noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
- {
- ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
- }
-
- /*!
- @brief convert any value type to a JSON value
- This function is usually called by the constructors of the @ref basic_json
- class.
+#include <cassert> // assert
+#include <numeric> // accumulate
+#include <string> // string
+#include <vector> // vector
- @param[in,out] j JSON value to write to
- @param[in] val value to read from
- */
- template<typename BasicJsonType, typename ValueType>
- static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
- noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
- {
- ::nlohmann::to_json(j, std::forward<ValueType>(val));
- }
-};
+// #include <nlohmann/detail/macro_scope.hpp>
-/*!
-@brief JSON Pointer
+// #include <nlohmann/detail/exceptions.hpp>
-A JSON pointer defines a string syntax for identifying a specific value
-within a JSON document. It can be used with functions `at` and
-`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+// #include <nlohmann/detail/value_t.hpp>
-@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
-@since version 2.0.0
-*/
+namespace nlohmann
+{
+template<typename BasicJsonType>
class json_pointer
{
- /// allow basic_json to access private members
+ // allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json;
@@ -6967,19 +8929,21 @@ class json_pointer
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
- @throw parse_error.107 if the given JSON pointer @a s is nonempty and
- does not begin with a slash (`/`); see example below
+ @throw parse_error.107 if the given JSON pointer @a s is nonempty and does
+ not begin with a slash (`/`); see example below
- @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s
- is not followed by `0` (representing `~`) or `1` (representing `/`);
- see example below
+ @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
+ not followed by `0` (representing `~`) or `1` (representing `/`); see
+ example below
- @liveexample{The example shows the construction several valid JSON
- pointers as well as the exceptional behavior.,json_pointer}
+ @liveexample{The example shows the construction several valid JSON pointers
+ as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
- explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {}
+ explicit json_pointer(const std::string& s = "")
+ : reference_tokens(split(s))
+ {}
/*!
@brief return a string representation of the JSON pointer
@@ -7021,7 +8985,7 @@ class json_pointer
*/
static int array_index(const std::string& s)
{
- size_t processed_chars = 0;
+ std::size_t processed_chars = 0;
const int res = std::stoi(s, &processed_chars);
// check if the string was completely read
@@ -7076,8 +9040,66 @@ class json_pointer
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const;
+ BasicJsonType& get_and_create(BasicJsonType& j) const
+ {
+ using size_type = typename BasicJsonType::size_type;
+ auto result = &j;
+
+ // in case no reference tokens exist, return a reference to the JSON value
+ // j which will be overwritten by a primitive value
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (result->m_type)
+ {
+ case detail::value_t::null:
+ {
+ if (reference_token == "0")
+ {
+ // start a new array if reference token is 0
+ result = &result->operator[](0);
+ }
+ else
+ {
+ // start a new object otherwise
+ result = &result->operator[](reference_token);
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ // create an entry in the object
+ result = &result->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // create an entry in the array
+ JSON_TRY
+ {
+ result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ /*
+ The following code is only reached if there exists a reference
+ token _and_ the current value is primitive. In this case, we have
+ an error situation, because primitive values may only occur as
+ single value; that is, with an empty list of reference tokens.
+ */
+ default:
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
+ }
+ }
+
+ return *result;
+ }
/*!
@brief return a reference to the pointed to value
@@ -7098,8 +9120,75 @@ class json_pointer
@throw parse_error.109 if an array index was not a number
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
+ BasicJsonType& get_unchecked(BasicJsonType* ptr) const
+ {
+ using size_type = typename BasicJsonType::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ // convert null values to arrays or objects before continuing
+ if (ptr->m_type == detail::value_t::null)
+ {
+ // check if reference token is a number
+ const bool nums =
+ std::all_of(reference_token.begin(), reference_token.end(),
+ [](const char x)
+ {
+ return (x >= '0' and x <= '9');
+ });
+
+ // change value to array for numbers or "-" or to object otherwise
+ *ptr = (nums or reference_token == "-")
+ ? detail::value_t::array
+ : detail::value_t::object;
+ }
+
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ if (reference_token == "-")
+ {
+ // explicitly treat "-" as index beyond the end
+ ptr = &ptr->operator[](ptr->m_value.array->size());
+ }
+ else
+ {
+ // convert array index to number; unchecked access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+ }
/*!
@throw parse_error.106 if an array index begins with '0'
@@ -7107,8 +9196,57 @@ class json_pointer
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
+ BasicJsonType& get_checked(BasicJsonType* ptr) const
+ {
+ using size_type = typename BasicJsonType::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+ }
/*!
@brief return a const reference to the pointed to value
@@ -7123,8 +9261,58 @@ class json_pointer
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
+ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
+ {
+ using size_type = typename BasicJsonType::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" cannot be used for const access
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // use unchecked array access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+ }
/*!
@throw parse_error.106 if an array index begins with '0'
@@ -7132,8 +9320,57 @@ class json_pointer
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
+ const BasicJsonType& get_checked(const BasicJsonType* ptr) const
+ {
+ using size_type = typename BasicJsonType::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+ }
/*!
@brief split the string input to reference tokens
@@ -7252,10 +9489,57 @@ class json_pointer
@note Empty objects or arrays are flattened to `null`.
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
static void flatten(const std::string& reference_string,
- const NLOHMANN_BASIC_JSON_TPL& value,
- NLOHMANN_BASIC_JSON_TPL& result);
+ const BasicJsonType& value,
+ BasicJsonType& result)
+ {
+ switch (value.m_type)
+ {
+ case detail::value_t::array:
+ {
+ if (value.m_value.array->empty())
+ {
+ // flatten empty array as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate array and use index as reference string
+ for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+ {
+ flatten(reference_string + "/" + std::to_string(i),
+ value.m_value.array->operator[](i), result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ if (value.m_value.object->empty())
+ {
+ // flatten empty object as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate object and use keys as reference string
+ for (const auto& element : *value.m_value.object)
+ {
+ flatten(reference_string + "/" + escape(element.first), element.second, result);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // add primitive value with its reference string
+ result[reference_string] = value;
+ break;
+ }
+ }
+ }
/*!
@param[in] value flattened JSON
@@ -7267,19 +9551,108 @@ class json_pointer
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
- NLOHMANN_BASIC_JSON_TPL_DECLARATION
- static NLOHMANN_BASIC_JSON_TPL
- unflatten(const NLOHMANN_BASIC_JSON_TPL& value);
+ static BasicJsonType
+ unflatten(const BasicJsonType& value)
+ {
+ if (JSON_UNLIKELY(not value.is_object()))
+ {
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
+ }
+
+ BasicJsonType result;
+
+ // iterate the JSON object values
+ for (const auto& element : *value.m_value.object)
+ {
+ if (JSON_UNLIKELY(not element.second.is_primitive()))
+ {
+ JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
+ }
+
+ // assign value to reference pointed to by JSON pointer; Note that if
+ // the JSON pointer is "" (i.e., points to the whole value), function
+ // get_and_create returns a reference to result itself. An assignment
+ // will then create a primitive value.
+ json_pointer(element.first).get_and_create(result) = element.second;
+ }
+
+ return result;
+ }
friend bool operator==(json_pointer const& lhs,
- json_pointer const& rhs) noexcept;
+ json_pointer const& rhs) noexcept
+ {
+ return (lhs.reference_tokens == rhs.reference_tokens);
+ }
friend bool operator!=(json_pointer const& lhs,
- json_pointer const& rhs) noexcept;
+ json_pointer const& rhs) noexcept
+ {
+ return not (lhs == rhs);
+ }
/// the reference tokens
std::vector<std::string> reference_tokens;
};
+}
+
+// #include <nlohmann/adl_serializer.hpp>
+
+
+#include <utility>
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+
+
+namespace nlohmann
+{
+template<typename, typename>
+struct adl_serializer
+{
+ /*!
+ @brief convert a JSON value to any value type
+
+ This function is usually called by the `get()` function of the
+ @ref basic_json class (either explicit or via conversion operators).
+
+ @param[in] j JSON value to read from
+ @param[in,out] val value to write to
+ */
+ template<typename BasicJsonType, typename ValueType>
+ static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+ {
+ ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+ }
+
+ /*!
+ @brief convert any value type to a JSON value
+
+ This function is usually called by the constructors of the @ref basic_json
+ class.
+
+ @param[in,out] j JSON value to write to
+ @param[in] val value to read from
+ */
+ template<typename BasicJsonType, typename ValueType>
+ static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
+ noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
+ {
+ ::nlohmann::to_json(j, std::forward<ValueType>(val));
+ }
+};
+}
+
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+namespace nlohmann
+{
/*!
@brief a class to store JSON values
@@ -7367,7 +9740,7 @@ class basic_json
{
private:
template<detail::value_t> friend struct detail::external_constructor;
- friend ::nlohmann::json_pointer;
+ friend ::nlohmann::json_pointer<basic_json>;
friend ::nlohmann::detail::parser<basic_json>;
friend ::nlohmann::detail::serializer<basic_json>;
template<typename BasicJsonType>
@@ -7404,7 +9777,7 @@ class basic_json
public:
using value_t = detail::value_t;
/// @copydoc nlohmann::json_pointer
- using json_pointer = ::nlohmann::json_pointer;
+ using json_pointer = ::nlohmann::json_pointer<basic_json>;
template<typename T, typename SFINAE>
using json_serializer = JSONSerializer<T, SFINAE>;
/// helper type for initializer lists of basic_json values
@@ -7517,10 +9890,13 @@ class basic_json
result["copyright"] = "(C) 2013-2017 Niels Lohmann";
result["name"] = "JSON for Modern C++";
result["url"] = "https://github.com/nlohmann/json";
- result["version"] =
- {
- {"string", "3.0.1"}, {"major", 3}, {"minor", 0}, {"patch", 1}
- };
+ result["version"]["string"] =
+ std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." +
+ std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." +
+ std::to_string(NLOHMANN_JSON_VERSION_PATCH);
+ result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+ result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+ result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
#ifdef _WIN32
result["platform"] = "win32";
@@ -8139,7 +10515,7 @@ class basic_json
object = nullptr; // silence warning, see #821
if (JSON_UNLIKELY(t == value_t::null))
{
- JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.0.1")); // LCOV_EXCL_LINE
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.0")); // LCOV_EXCL_LINE
}
break;
}
@@ -8182,7 +10558,7 @@ class basic_json
array = create<array_t>(std::move(value));
}
- void destroy(value_t t)
+ void destroy(value_t t) noexcept
{
switch (t)
{
@@ -8227,7 +10603,7 @@ class basic_json
value is changed, because the invariant expresses a relationship between
@a m_type and @a m_value.
*/
- void assert_invariant() const
+ void assert_invariant() const noexcept
{
assert(m_type != value_t::object or m_value.object != nullptr);
assert(m_type != value_t::array or m_value.array != nullptr);
@@ -8432,15 +10808,13 @@ class basic_json
@since version 2.1.0
*/
- template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>,
- detail::enable_if_t<not std::is_base_of<std::istream, U>::value and
- not std::is_same<U, basic_json_t>::value and
- not detail::is_basic_json_nested_type<
- basic_json_t, U>::value and
- detail::has_to_json<basic_json, U>::value,
- int> = 0>
- basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json(
- std::declval<basic_json_t&>(), std::forward<CompatibleType>(val))))
+ template <typename CompatibleType,
+ typename U = detail::uncvref_t<CompatibleType>,
+ detail::enable_if_t<
+ detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
+ basic_json(CompatibleType && val) noexcept(noexcept(
+ JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
+ std::forward<CompatibleType>(val))))
{
JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
assert_invariant();
@@ -9021,7 +11395,7 @@ class basic_json
@since version 1.0.0
*/
- ~basic_json()
+ ~basic_json() noexcept
{
assert_invariant();
m_value.destroy(m_type);
@@ -11361,18 +13735,89 @@ class basic_json
@note The name of this function is not yet final and may change in the
future.
+
+ @deprecated This stream operator is deprecated and will be removed in
+ future 4.0.0 of the library. Please use @ref items() instead;
+ that is, replace `json::iterator_wrapper(j)` with `j.items()`.
*/
- static iteration_proxy<iterator> iterator_wrapper(reference ref)
+ JSON_DEPRECATED
+ static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
{
- return iteration_proxy<iterator>(ref);
+ return ref.items();
}
/*!
@copydoc iterator_wrapper(reference)
*/
- static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref)
+ JSON_DEPRECATED
+ static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
+ {
+ return ref.items();
+ }
+
+ /*!
+ @brief helper to access iterator member functions in range-based for
+
+ This function allows to access @ref iterator::key() and @ref
+ iterator::value() during range-based for loops. In these loops, a
+ reference to the JSON values is returned, so there is no access to the
+ underlying iterator.
+
+ For loop without `items()` function:
+
+ @code{cpp}
+ for (auto it = j_object.begin(); it != j_object.end(); ++it)
+ {
+ std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+ }
+ @endcode
+
+ Range-based for loop without `items()` function:
+
+ @code{cpp}
+ for (auto it : j_object)
+ {
+ // "it" is of type json::reference and has no key() member
+ std::cout << "value: " << it << '\n';
+ }
+ @endcode
+
+ Range-based for loop with `items()` function:
+
+ @code{cpp}
+ for (auto it : j_object.items())
{
- return iteration_proxy<const_iterator>(ref);
+ std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+ }
+ @endcode
+
+ @note When iterating over an array, `key()` will return the index of the
+ element as string (see example). For primitive types (e.g., numbers),
+ `key()` returns an empty string.
+
+ @return iteration proxy object wrapping @a ref with an interface to use in
+ range-based for loops
+
+ @liveexample{The following code shows how the function is used.,items}
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
+
+ @since version 3.x.x.
+ */
+ iteration_proxy<iterator> items() noexcept
+ {
+ return iteration_proxy<iterator>(*this);
+ }
+
+ /*!
+ @copydoc items()
+ */
+ iteration_proxy<const_iterator> items() const noexcept
+ {
+ return iteration_proxy<const_iterator>(*this);
}
/// @}
@@ -12943,8 +15388,8 @@ class basic_json
/*!
@brief serialize to stream
- @deprecated This stream operator is deprecated and will be removed in a
- future version of the library. Please use
+ @deprecated This stream operator is deprecated and will be removed in
+ future 4.0.0 of the library. Please use
@ref operator<<(std::ostream&, const basic_json&)
instead; that is, replace calls like `j >> o;` with `o << j;`.
@since version 1.0.0; deprecated since version 3.0.0
@@ -13129,8 +15574,8 @@ class basic_json
/*!
@brief deserialize from stream
- @deprecated This stream operator is deprecated and will be removed in a
- future version of the library. Please use
+ @deprecated This stream operator is deprecated and will be removed in
+ version 4.0.0 of the library. Please use
@ref operator>>(std::istream&, basic_json&)
instead; that is, replace calls like `j << i;` with `i >> j;`.
@since version 1.0.0; deprecated since version 3.0.0
@@ -13331,9 +15776,11 @@ class basic_json
vector in CBOR format.,to_cbor}
@sa http://cbor.io
- @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ @sa @ref from_cbor(detail::input_adapter, const bool strict) for the
analogous deserialization
@sa @ref to_msgpack(const basic_json&) for the related MessagePack format
+ @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ related UBJSON format
@since version 2.0.9
*/
@@ -13429,6 +15876,8 @@ class basic_json
@sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
analogous deserialization
@sa @ref to_cbor(const basic_json& for the related CBOR format
+ @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ related UBJSON format
@since version 2.0.9
*/
@@ -13450,6 +15899,107 @@ class basic_json
}
/*!
+ @brief create a UBJSON serialization of a given JSON value
+
+ Serializes a given JSON value @a j to a byte vector using the UBJSON
+ (Universal Binary JSON) serialization format. UBJSON aims to be more compact
+ than JSON itself, yet more efficient to parse.
+
+ The library uses the following mapping from JSON values types to
+ UBJSON types according to the UBJSON specification:
+
+ JSON value type | value/range | UBJSON type | marker
+ --------------- | --------------------------------- | ----------- | ------
+ null | `null` | null | `Z`
+ boolean | `true` | true | `T`
+ boolean | `false` | false | `F`
+ number_integer | -9223372036854775808..-2147483649 | int64 | `L`
+ number_integer | -2147483648..-32769 | int32 | `l`
+ number_integer | -32768..-129 | int16 | `I`
+ number_integer | -128..127 | int8 | `i`
+ number_integer | 128..255 | uint8 | `U`
+ number_integer | 256..32767 | int16 | `I`
+ number_integer | 32768..2147483647 | int32 | `l`
+ number_integer | 2147483648..9223372036854775807 | int64 | `L`
+ number_unsigned | 0..127 | int8 | `i`
+ number_unsigned | 128..255 | uint8 | `U`
+ number_unsigned | 256..32767 | int16 | `I`
+ number_unsigned | 32768..2147483647 | int32 | `l`
+ number_unsigned | 2147483648..9223372036854775807 | int64 | `L`
+ number_float | *any value* | float64 | `D`
+ string | *with shortest length indicator* | string | `S`
+ array | *see notes on optimized format* | array | `[`
+ object | *see notes on optimized format* | map | `{`
+
+ @note The mapping is **complete** in the sense that any JSON value type
+ can be converted to a UBJSON value.
+
+ @note The following values can **not** be converted to a UBJSON value:
+ - strings with more than 9223372036854775807 bytes (theoretical)
+ - unsigned integer numbers above 9223372036854775807
+
+ @note The following markers are not used in the conversion:
+ - `Z`: no-op values are not created.
+ - `C`: single-byte strings are serialized with `S` markers.
+
+ @note Any UBJSON output created @ref to_ubjson can be successfully parsed
+ by @ref from_ubjson.
+
+ @note If NaN or Infinity are stored inside a JSON number, they are
+ serialized properly. This behavior differs from the @ref dump()
+ function which serializes NaN or Infinity to `null`.
+
+ @note The optimized formats for containers are supported: Parameter
+ @a use_size adds size information to the beginning of a container and
+ removes the closing marker. Parameter @a use_type further checks
+ whether all elements of a container have the same type and adds the
+ type marker to the beginning of the container. The @a use_type
+ parameter must only be used together with @a use_size = true. Note
+ that @a use_size = true alone may result in larger representations -
+ the benefit of this parameter is that the receiving side is
+ immediately informed on the number of elements of the container.
+
+ @param[in] j JSON value to serialize
+ @param[in] use_size whether to add size annotations to container types
+ @param[in] use_type whether to add type annotations to container types
+ (must be combined with @a use_size = true)
+ @return UBJSON serialization as byte vector
+
+ @complexity Linear in the size of the JSON value @a j.
+
+ @liveexample{The example shows the serialization of a JSON value to a byte
+ vector in UBJSON format.,to_ubjson}
+
+ @sa http://ubjson.org
+ @sa @ref from_ubjson(detail::input_adapter, const bool strict) for the
+ analogous deserialization
+ @sa @ref to_cbor(const basic_json& for the related CBOR format
+ @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
+
+ @since version 3.1.0
+ */
+ static std::vector<uint8_t> to_ubjson(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false)
+ {
+ std::vector<uint8_t> result;
+ to_ubjson(j, result, use_size, use_type);
+ return result;
+ }
+
+ static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);
+ }
+
+ static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<char>(o).write_ubjson(j, use_size, use_type);
+ }
+
+ /*!
@brief create a JSON value from an input in CBOR format
Deserializes a given input @a i to a JSON value using the CBOR (Concise
@@ -13535,6 +16085,8 @@ class basic_json
@sa @ref to_cbor(const basic_json&) for the analogous serialization
@sa @ref from_msgpack(detail::input_adapter, const bool) for the
related MessagePack format
+ @sa @ref from_ubjson(detail::input_adapter, const bool) for the related
+ UBJSON format
@since version 2.0.9; parameter @a start_index since 2.1.1; changed to
consume input adapters, removed start_index parameter, and added
@@ -13622,6 +16174,8 @@ class basic_json
@sa @ref to_msgpack(const basic_json&) for the analogous serialization
@sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR
format
+ @sa @ref from_ubjson(detail::input_adapter, const bool) for the related
+ UBJSON format
@since version 2.0.9; parameter @a start_index since 2.1.1; changed to
consume input adapters, removed start_index parameter, and added
@@ -13643,6 +16197,72 @@ class basic_json
return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict);
}
+ /*!
+ @brief create a JSON value from an input in UBJSON format
+
+ Deserializes a given input @a i to a JSON value using the UBJSON (Universal
+ Binary JSON) serialization format.
+
+ The library maps UBJSON types to JSON value types as follows:
+
+ UBJSON type | JSON value type | marker
+ ----------- | --------------------------------------- | ------
+ no-op | *no value, next value is read* | `N`
+ null | `null` | `Z`
+ false | `false` | `F`
+ true | `true` | `T`
+ float32 | number_float | `d`
+ float64 | number_float | `D`
+ uint8 | number_unsigned | `U`
+ int8 | number_integer | `i`
+ int16 | number_integer | `I`
+ int32 | number_integer | `l`
+ int64 | number_integer | `L`
+ string | string | `S`
+ char | string | `C`
+ array | array (optimized values are supported) | `[`
+ object | object (optimized values are supported) | `{`
+
+ @note The mapping is **complete** in the sense that any UBJSON value can
+ be converted to a JSON value.
+
+ @param[in] i an input in UBJSON format convertible to an input adapter
+ @param[in] strict whether to expect the input to be consumed until EOF
+ (true by default)
+
+ @throw parse_error.110 if the given input ends prematurely or the end of
+ file was not reached when @a strict was set to true
+ @throw parse_error.112 if a parse error occurs
+ @throw parse_error.113 if a string could not be parsed successfully
+
+ @complexity Linear in the size of the input @a i.
+
+ @liveexample{The example shows the deserialization of a byte vector in
+ UBJSON format to a JSON value.,from_ubjson}
+
+ @sa http://ubjson.org
+ @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ analogous serialization
+ @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR
+ format
+ @sa @ref from_msgpack(detail::input_adapter, const bool) for the related
+ MessagePack format
+
+ @since version 3.1.0
+ */
+ static basic_json from_ubjson(detail::input_adapter i,
+ const bool strict = true)
+ {
+ return binary_reader(i).parse_ubjson(strict);
+ }
+
+ template<typename A1, typename A2,
+ detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
+ static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true)
+ {
+ return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_ubjson(strict);
+ }
+
/// @}
//////////////////////////
@@ -14066,7 +16686,7 @@ class basic_json
// wrapper to get a value for an operation
const auto get_value = [&val](const std::string & op,
const std::string & member,
- bool string_type) -> basic_json&
+ bool string_type) -> basic_json &
{
// find value
auto it = val.m_value.object->find(member);
@@ -14216,6 +16836,7 @@ class basic_json
diff for two JSON values.,diff}
@sa @ref patch -- apply a JSON patch
+ @sa @ref merge_patch -- apply a JSON Merge Patch
@sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
@@ -14347,418 +16968,86 @@ class basic_json
}
/// @}
-};
-/////////////
-// presets //
-/////////////
-
-/*!
-@brief default JSON class
+ ////////////////////////////////
+ // JSON Merge Patch functions //
+ ////////////////////////////////
-This type is the default specialization of the @ref basic_json class which
-uses the standard template types.
-
-@since version 1.0.0
-*/
-using json = basic_json<>;
-
-//////////////////
-// json_pointer //
-//////////////////
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-NLOHMANN_BASIC_JSON_TPL&
-json_pointer::get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const
-{
- using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
- auto result = &j;
-
- // in case no reference tokens exist, return a reference to the JSON value
- // j which will be overwritten by a primitive value
- for (const auto& reference_token : reference_tokens)
- {
- switch (result->m_type)
- {
- case detail::value_t::null:
- {
- if (reference_token == "0")
- {
- // start a new array if reference token is 0
- result = &result->operator[](0);
- }
- else
- {
- // start a new object otherwise
- result = &result->operator[](reference_token);
- }
- break;
- }
-
- case detail::value_t::object:
- {
- // create an entry in the object
- result = &result->operator[](reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- // create an entry in the array
- JSON_TRY
- {
- result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
- }
- break;
- }
+ /// @name JSON Merge Patch functions
+ /// @{
- /*
- The following code is only reached if there exists a reference
- token _and_ the current value is primitive. In this case, we have
- an error situation, because primitive values may only occur as
- single value; that is, with an empty list of reference tokens.
- */
- default:
- JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
- }
- }
+ /*!
+ @brief applies a JSON Merge Patch
+
+ The merge patch format is primarily intended for use with the HTTP PATCH
+ method as a means of describing a set of modifications to a target
+ resource's content. This function applies a merge patch to the current
+ JSON value.
+
+ The function implements the following algorithm from Section 2 of
+ [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):
+
+ ```
+ define MergePatch(Target, Patch):
+ if Patch is an Object:
+ if Target is not an Object:
+ Target = {} // Ignore the contents and set it to an empty Object
+ for each Name/Value pair in Patch:
+ if Value is null:
+ if Name exists in Target:
+ remove the Name/Value pair from Target
+ else:
+ Target[Name] = MergePatch(Target[Name], Value)
+ return Target
+ else:
+ return Patch
+ ```
+
+ Thereby, `Target` is the current object; that is, the patch is applied to
+ the current value.
+
+ @param[in] patch the patch to apply
+
+ @complexity Linear in the lengths of @a patch.
+
+ @liveexample{The following code shows how a JSON Merge Patch is applied to
+ a JSON document.,merge_patch}
- return *result;
-}
+ @sa @ref patch -- apply a JSON patch
+ @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-NLOHMANN_BASIC_JSON_TPL&
-json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const
-{
- using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
- for (const auto& reference_token : reference_tokens)
+ @since version 3.0.0
+ */
+ void merge_patch(const basic_json& patch)
{
- // convert null values to arrays or objects before continuing
- if (ptr->m_type == detail::value_t::null)
+ if (patch.is_object())
{
- // check if reference token is a number
- const bool nums =
- std::all_of(reference_token.begin(), reference_token.end(),
- [](const char x)
+ if (not is_object())
{
- return (x >= '0' and x <= '9');
- });
-
- // change value to array for numbers or "-" or to object otherwise
- *ptr = (nums or reference_token == "-")
- ? detail::value_t::array
- : detail::value_t::object;
- }
-
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
+ *this = object();
}
-
- case detail::value_t::array:
+ for (auto it = patch.begin(); it != patch.end(); ++it)
{
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + reference_token +
- "' must not begin with '0'"));
- }
-
- if (reference_token == "-")
+ if (it.value().is_null())
{
- // explicitly treat "-" as index beyond the end
- ptr = &ptr->operator[](ptr->m_value.array->size());
+ erase(it.key());
}
else
{
- // convert array index to number; unchecked access
- JSON_TRY
- {
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
- }
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
-
- return *ptr;
-}
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-NLOHMANN_BASIC_JSON_TPL&
-json_pointer::get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const
-{
- using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- if (JSON_UNLIKELY(reference_token == "-"))
- {
- // "-" always fails the range check
- JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + reference_token +
- "' must not begin with '0'"));
- }
-
- // note: at performs range check
- JSON_TRY
- {
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
-
- return *ptr;
-}
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-const NLOHMANN_BASIC_JSON_TPL&
-json_pointer::get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
-{
- using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- if (JSON_UNLIKELY(reference_token == "-"))
- {
- // "-" cannot be used for const access
- JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + reference_token +
- "' must not begin with '0'"));
- }
-
- // use unchecked array access
- JSON_TRY
- {
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
- }
- break;
- }
-
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
- }
- }
-
- return *ptr;
-}
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-const NLOHMANN_BASIC_JSON_TPL&
-json_pointer::get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
-{
- using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case detail::value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case detail::value_t::array:
- {
- if (JSON_UNLIKELY(reference_token == "-"))
- {
- // "-" always fails the range check
- JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
- {
- JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + reference_token +
- "' must not begin with '0'"));
- }
-
- // note: at performs range check
- JSON_TRY
- {
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
- }
- JSON_CATCH(std::invalid_argument&)
- {
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ operator[](it.key()).merge_patch(it.value());
}
- break;
}
-
- default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
- }
-
- return *ptr;
-}
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-void json_pointer::flatten(const std::string& reference_string,
- const NLOHMANN_BASIC_JSON_TPL& value,
- NLOHMANN_BASIC_JSON_TPL& result)
-{
- switch (value.m_type)
- {
- case detail::value_t::array:
- {
- if (value.m_value.array->empty())
- {
- // flatten empty array as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate array and use index as reference string
- for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
- {
- flatten(reference_string + "/" + std::to_string(i),
- value.m_value.array->operator[](i), result);
- }
- }
- break;
- }
-
- case detail::value_t::object:
- {
- if (value.m_value.object->empty())
- {
- // flatten empty object as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate object and use keys as reference string
- for (const auto& element : *value.m_value.object)
- {
- flatten(reference_string + "/" + escape(element.first), element.second, result);
- }
- }
- break;
- }
-
- default:
- {
- // add primitive value with its reference string
- result[reference_string] = value;
- break;
- }
- }
-}
-
-NLOHMANN_BASIC_JSON_TPL_DECLARATION
-NLOHMANN_BASIC_JSON_TPL
-json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value)
-{
- if (JSON_UNLIKELY(not value.is_object()))
- {
- JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
- }
-
- NLOHMANN_BASIC_JSON_TPL result;
-
- // iterate the JSON object values
- for (const auto& element : *value.m_value.object)
- {
- if (JSON_UNLIKELY(not element.second.is_primitive()))
+ else
{
- JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
+ *this = patch;
}
-
- // assign value to reference pointed to by JSON pointer; Note that if
- // the JSON pointer is "" (i.e., points to the whole value), function
- // get_and_create returns a reference to result itself. An assignment
- // will then create a primitive value.
- json_pointer(element.first).get_and_create(result) = element.second;
}
- return result;
-}
-
-inline bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept
-{
- return (lhs.reference_tokens == rhs.reference_tokens);
-}
-
-inline bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept
-{
- return not (lhs == rhs);
-}
+ /// @}
+};
} // namespace nlohmann
-
///////////////////////
// nonmember support //
///////////////////////
@@ -14853,6 +17142,9 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
return nlohmann::json::json_pointer(std::string(s, n));
}
+// #include <nlohmann/detail/macro_unscope.hpp>
+
+
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
@@ -14868,7 +17160,11 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
#undef JSON_LIKELY
#undef JSON_UNLIKELY
#undef JSON_DEPRECATED
+#undef JSON_HAS_CPP_14
+#undef JSON_HAS_CPP_17
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL
+#undef NLOHMANN_JSON_HAS_HELPER
+
#endif
diff --git a/include/nlohmann/json_fwd.hpp b/include/nlohmann/json_fwd.hpp
new file mode 100644
index 0000000..750adb6
--- /dev/null
+++ b/include/nlohmann/json_fwd.hpp
@@ -0,0 +1,64 @@
+#ifndef NLOHMANN_JSON_FWD_HPP
+#define NLOHMANN_JSON_FWD_HPP
+
+#include <cstdint> // int64_t, uint64_t
+#include <map> // map
+#include <memory> // allocator
+#include <string> // string
+#include <vector> // vector
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+namespace nlohmann
+{
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename = void, typename = void>
+struct adl_serializer;
+
+template<template<typename U, typename V, typename... Args> class ObjectType =
+ std::map,
+ template<typename U, typename... Args> class ArrayType = std::vector,
+ class StringType = std::string, class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename U> class AllocatorType = std::allocator,
+ template<typename T, typename SFINAE = void> class JSONSerializer =
+ adl_serializer>
+class basic_json;
+
+/*!
+@brief JSON Pointer
+
+A JSON pointer defines a string syntax for identifying a specific value
+within a JSON document. It can be used with functions `at` and
+`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+
+@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
+
+@since version 2.0.0
+*/
+template<typename BasicJsonType>
+class json_pointer;
+
+/*!
+@brief default JSON class
+
+This type is the default specialization of the @ref basic_json class which
+uses the standard template types.
+
+@since version 1.0.0
+*/
+using json = basic_json<>;
+}
+
+#endif