summaryrefslogtreecommitdiff
path: root/ext/json/json.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json/json.hpp')
-rw-r--r--ext/json/json.hpp5088
1 files changed, 3590 insertions, 1498 deletions
diff --git a/ext/json/json.hpp b/ext/json/json.hpp
index 9d6687dd..9d48e7a6 100644
--- a/ext/json/json.hpp
+++ b/ext/json/json.hpp
@@ -1,11 +1,11 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.0.0
+| | |__ | | | | | | version 2.0.10
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
+Copyright (c) 2013-2017 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,29 +29,45 @@ SOFTWARE.
#ifndef NLOHMANN_JSON_HPP
#define NLOHMANN_JSON_HPP
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cerrno>
-#include <ciso646>
-#include <cmath>
-#include <cstddef>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-#include <initializer_list>
-#include <iomanip>
-#include <iostream>
-#include <iterator>
-#include <limits>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <type_traits>
-#include <utility>
-#include <vector>
+#include <algorithm> // all_of, for_each, transform
+#include <array> // array
+#include <cassert> // assert
+#include <cctype> // isdigit
+#include <ciso646> // and, not, or
+#include <cmath> // isfinite, ldexp, signbit
+#include <cstddef> // nullptr_t, ptrdiff_t, size_t
+#include <cstdint> // int64_t, uint64_t
+#include <cstdlib> // strtod, strtof, strtold, strtoul
+#include <cstring> // strlen
+#include <functional> // function, hash, less
+#include <initializer_list> // initializer_list
+#include <iomanip> // setw
+#include <iostream> // istream, ostream
+#include <iterator> // advance, begin, 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 <numeric> // accumulate
+#include <sstream> // stringstream
+#include <stdexcept> // domain_error, invalid_argument, out_of_range
+#include <string> // getline, stoi, string, to_string
+#include <type_traits> // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference
+#include <utility> // declval, forward, make_pair, move, pair, swap
+#include <vector> // vector
+
+// exclude unsupported compilers
+#if defined(__clang__)
+ #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
+ #if CLANG_VERSION < 30400
+ #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+#elif defined(__GNUC__)
+ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+ #if GCC_VERSION < 40900
+ #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+#endif
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
@@ -59,6 +75,21 @@ SOFTWARE.
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
+// disable documentation warnings on clang
+#if defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdocumentation"
+#endif
+
+// allow for portable deprecation warnings
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+ #define JSON_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+ #define JSON_DEPRECATED __declspec(deprecated)
+#else
+ #define JSON_DEPRECATED
+#endif
+
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@@ -76,29 +107,25 @@ namespace
{
/*!
@brief Helper to determine whether there's a key_type for T.
+
+Thus 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
*/
template<typename T>
struct has_mapped_type
{
private:
- template<typename C> static char test(typename C::mapped_type*);
- template<typename C> static char (&test(...))[2];
- public:
- static constexpr bool value = sizeof(test<T>(0)) == 1;
-};
+ template <typename U, typename = typename U::mapped_type>
+ static int detect(U&&);
-/*!
-@brief helper class to create locales with decimal point
-@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
-*/
-class DecimalSeparator : public std::numpunct<char>
-{
- protected:
- char do_decimal_point() const
- {
- return '.';
- }
+ static void detect(...);
+ public:
+ static constexpr bool value =
+ std::is_integral<decltype(detect(std::declval<T>()))>::value;
};
}
@@ -163,6 +190,13 @@ default)
JSON values can be used like STL containers and provide reverse iterator
access.
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
@internal
@note ObjectType trick from http://stackoverflow.com/a/9860911
@endinternal
@@ -188,17 +222,13 @@ class basic_json
{
private:
/// workaround type for MSVC
- using basic_json_t = basic_json<ObjectType,
- ArrayType,
- StringType,
- BooleanType,
- NumberIntegerType,
- NumberUnsignedType,
- NumberFloatType,
+ using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
+ BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType>;
public:
// forward declarations
+ template<typename U> class iter_impl;
template<typename Base> class json_reverse_iterator;
class json_pointer;
@@ -207,6 +237,8 @@ class basic_json
/////////////////////
/// @name container types
+ /// The canonic container types to use @ref basic_json like any other STL
+ /// container.
/// @{
/// the type of elements in a basic_json container
@@ -231,9 +263,9 @@ class basic_json
using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
/// an iterator for a basic_json container
- class iterator;
+ using iterator = iter_impl<basic_json>;
/// a const iterator for a basic_json container
- class const_iterator;
+ using const_iterator = iter_impl<const basic_json>;
/// a reverse iterator for a basic_json container
using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
/// a const reverse iterator for a basic_json container
@@ -256,6 +288,8 @@ class basic_json
///////////////////////////
/// @name JSON value data types
+ /// The data types to store a JSON value. These types are derived from
+ /// the template arguments passed to class @ref basic_json.
/// @{
/*!
@@ -358,7 +392,7 @@ class basic_json
@tparam ArrayType container type to store arrays (e.g., `std::vector` or
`std::list`)
- @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
+ @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
#### Default type
@@ -595,15 +629,14 @@ class basic_json
> that implementations will agree exactly on their numeric values.
As this range is a subrange (when considered in conjunction with the
- number_integer_t type) of the exactly supported range [0, UINT64_MAX], this
- class's integer type is interoperable.
+ number_integer_t type) of the exactly supported range [0, UINT64_MAX],
+ this class's integer type is interoperable.
#### Storage
Integer number values are stored directly inside a @ref basic_json type.
@sa @ref number_float_t -- type for number values (floating-point)
-
@sa @ref number_integer_t -- type for number values (integer)
@since version 2.0.0
@@ -691,7 +724,19 @@ class basic_json
This enumeration collects the different JSON types. It is internally used
to distinguish the stored values, and the functions @ref is_null(), @ref
is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref
- is_number(), and @ref is_discarded() rely on it.
+ is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and
+ @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and
+ @ref is_structured() rely on it.
+
+ @note There are three enumeration entries (number_integer,
+ number_unsigned, and number_float), because the library distinguishes
+ these three types for numbers: @ref number_unsigned_t is used for unsigned
+ integers, @ref number_integer_t is used for signed integers, and @ref
+ number_float_t is used for floating-point numbers or to approximate
+ integers which do not fit in the limits of their respective type.
+
+ @sa @ref basic_json(const value_t value_type) -- create a JSON value with
+ the default value for a given type
@since version 1.0.0
*/
@@ -702,7 +747,7 @@ class basic_json
array, ///< array (ordered collection of values)
string, ///< string value
boolean, ///< boolean value
- number_integer, ///< number value (integer)
+ number_integer, ///< number value (signed integer)
number_unsigned, ///< number value (unsigned integer)
number_float, ///< number value (floating-point)
discarded ///< discarded by the the parser callback function
@@ -711,73 +756,6 @@ class basic_json
private:
- /*!
- @brief a type to hold JSON type information
-
- This bitfield type holds information about JSON types. It is internally
- used to hold the basic JSON type enumeration, as well as additional
- information in the case of values that have been parsed from a string
- including whether of not it was created directly or parsed, and in the
- case of floating point numbers the number of significant figures in the
- original representaiton and if it was in exponential form, if a '+' was
- included in the exponent and the capitilization of the exponent marker.
- The sole purpose of this information is to permit accurate round trips.
-
- @since version 2.0.0
- */
- union type_data_t
- {
- struct
- {
- /// the type of the value (@ref value_t)
- uint16_t type : 4;
- /// whether the number was parsed from a string
- uint16_t parsed : 1;
- /// whether parsed number contained an exponent ('e'/'E')
- uint16_t has_exp : 1;
- /// whether parsed number contained a plus in the exponent
- uint16_t exp_plus : 1;
- /// whether parsed number's exponent was capitalized ('E')
- uint16_t exp_cap : 1;
- /// the number of figures for a parsed number
- uint16_t precision : 8;
- } bits;
- uint16_t data;
-
- /// return the type as value_t
- operator value_t() const
- {
- return static_cast<value_t>(bits.type);
- }
-
- /// test type for equality (ignore other fields)
- bool operator==(const value_t& rhs) const
- {
- return static_cast<value_t>(bits.type) == rhs;
- }
-
- /// assignment
- type_data_t& operator=(value_t rhs)
- {
- bits.type = static_cast<uint16_t>(rhs) & 15; // avoid overflow
- return *this;
- }
-
- /// construct from value_t
- type_data_t(value_t t) noexcept
- {
- *reinterpret_cast<uint16_t*>(this) = 0;
- bits.type = static_cast<uint16_t>(t) & 15; // avoid overflow
- }
-
- /// default constructor
- type_data_t() noexcept
- {
- data = 0;
- bits.type = reinterpret_cast<uint16_t>(value_t::null);
- }
- };
-
/// helper for exception-safe object creation
template<typename T, typename... Args>
static T* create(Args&& ... args)
@@ -789,6 +767,7 @@ class basic_json
};
std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
alloc.construct(object.get(), std::forward<Args>(args)...);
+ assert(object.get() != nullptr);
return object.release();
}
@@ -799,7 +778,24 @@ class basic_json
/*!
@brief a JSON value
- The actual storage for a JSON value of the @ref basic_json class.
+ The actual storage for a JSON value of the @ref basic_json class. This
+ union combines the different storage types for the JSON value types
+ defined in @ref value_t.
+
+ JSON type | value_t type | used type
+ --------- | --------------- | ------------------------
+ object | object | pointer to @ref object_t
+ array | array | pointer to @ref array_t
+ string | string | pointer to @ref string_t
+ boolean | boolean | @ref boolean_t
+ number | number_integer | @ref number_integer_t
+ number | number_unsigned | @ref number_unsigned_t
+ number | number_float | @ref number_float_t
+ null | null | *no value is stored*
+
+ @note Variable-length types (objects, arrays, and strings) are stored as
+ pointers. The size of the union should not exceed 64 bits if the default
+ value types are used.
@since version 1.0.0
*/
@@ -877,8 +873,17 @@ class basic_json
break;
}
+ case value_t::null:
+ {
+ break;
+ }
+
default:
{
+ if (t == value_t::null)
+ {
+ throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE
+ }
break;
}
}
@@ -903,6 +908,21 @@ class basic_json
}
};
+ /*!
+ @brief checks the class invariants
+
+ This function asserts the class invariants. It needs to be called at the
+ end of every constructor to make sure that created objects respect the
+ invariant. Furthermore, it has to be called each time the type of a JSON
+ value is changed, because the invariant expresses a relationship between
+ @a m_type and @a m_value.
+ */
+ void assert_invariant() const
+ {
+ assert(m_type != value_t::object or m_value.object != nullptr);
+ assert(m_type != value_t::array or m_value.array != nullptr);
+ assert(m_type != value_t::string or m_value.string != nullptr);
+ }
public:
//////////////////////////
@@ -915,6 +935,8 @@ class basic_json
This enumeration lists the parser events that can trigger calling a
callback function of type @ref parser_callback_t during parsing.
+ @image html callback_events.png "Example when certain parse events are triggered"
+
@since version 1.0.0
*/
enum class parse_event_t : uint8_t
@@ -937,12 +959,13 @@ class basic_json
@brief per-element parser callback type
With a parser callback function, the result of parsing a JSON text can be
- influenced. When passed to @ref parse(std::istream&, parser_callback_t) or
- @ref parse(const string_t&, parser_callback_t), it is called on certain
- events (passed as @ref parse_event_t via parameter @a event) with a set
- recursion depth @a depth and context JSON value @a parsed. The return
- value of the callback function is a boolean indicating whether the element
- that emitted the callback shall be kept or not.
+ influenced. When passed to @ref parse(std::istream&, const
+ parser_callback_t) or @ref parse(const CharT, const parser_callback_t),
+ it is called on certain events (passed as @ref parse_event_t via parameter
+ @a event) with a set recursion depth @a depth and context JSON value
+ @a parsed. The return value of the callback function is a boolean
+ indicating whether the element that emitted the callback shall be kept or
+ not.
We distinguish six scenarios (determined by the event type) in which the
callback function can be called. The following table describes the values
@@ -957,6 +980,8 @@ class basic_json
parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array
parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value
+ @image html callback_events.png "Example when certain parse events are triggered"
+
Discarding a value (i.e., returning `false`) has different effects
depending on the context in which function was called:
@@ -978,11 +1003,13 @@ class basic_json
skipped completely or replaced by an empty discarded object.
@sa @ref parse(std::istream&, parser_callback_t) or
- @ref parse(const string_t&, parser_callback_t) for examples
+ @ref parse(const CharT, const parser_callback_t) for examples
@since version 1.0.0
*/
- using parser_callback_t = std::function<bool(int depth, parse_event_t event, basic_json& parsed)>;
+ using parser_callback_t = std::function<bool(int depth,
+ parse_event_t event,
+ basic_json& parsed)>;
//////////////////
@@ -990,6 +1017,8 @@ class basic_json
//////////////////
/// @name constructors and destructors
+ /// Constructors of class @ref basic_json, copy/move constructor, copy
+ /// assignment, static functions creating objects, and the destructor.
/// @{
/*!
@@ -1033,40 +1062,15 @@ class basic_json
*/
basic_json(const value_t value_type)
: m_type(value_type), m_value(value_type)
- {}
-
- /*!
- @brief create a null object (implicitly)
-
- Create a `null` JSON value. This is the implicit version of the `null`
- value constructor as it takes no parameters.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this constructor never throws
- exceptions.
-
- @requirement This function helps `basic_json` satisfying the
- [Container](http://en.cppreference.com/w/cpp/concept/Container)
- requirements:
- - The complexity is constant.
- - As postcondition, it holds: `basic_json().empty() == true`.
-
- @liveexample{The following code shows the constructor for a `null` JSON
- value.,basic_json}
-
- @sa @ref basic_json(std::nullptr_t) -- create a `null` value
-
- @since version 1.0.0
- */
- basic_json() = default;
+ {
+ assert_invariant();
+ }
/*!
- @brief create a null object (explicitly)
+ @brief create a null object
- Create a `null` JSON value. This is the explicitly version of the `null`
- value constructor as it takes a null pointer as parameter. It allows to
- create `null` values by explicitly assigning a `nullptr` to a JSON value.
+ Create a `null` JSON value. It either takes a null pointer as parameter
+ (explicitly creating `null`) or no parameter (implicitly creating `null`).
The passed null pointer itself is not read -- it is only used to choose
the right constructor.
@@ -1075,17 +1079,16 @@ class basic_json
@exceptionsafety No-throw guarantee: this constructor never throws
exceptions.
- @liveexample{The following code shows the constructor with null pointer
- parameter.,basic_json__nullptr_t}
-
- @sa @ref basic_json() -- default constructor (implicitly creating a `null`
- value)
+ @liveexample{The following code shows the constructor with and without a
+ null pointer parameter.,basic_json__nullptr_t}
@since version 1.0.0
*/
- basic_json(std::nullptr_t) noexcept
+ basic_json(std::nullptr_t = nullptr) noexcept
: basic_json(value_t::null)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an object (explicit)
@@ -1108,7 +1111,9 @@ class basic_json
*/
basic_json(const object_t& val)
: m_type(value_t::object), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an object (implicit)
@@ -1136,17 +1141,16 @@ class basic_json
@since version 1.0.0
*/
- template <class CompatibleObjectType, typename
- std::enable_if<
- std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
- std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
- = 0>
+ template<class CompatibleObjectType, typename std::enable_if<
+ std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
+ std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0>
basic_json(const CompatibleObjectType& val)
: m_type(value_t::object)
{
using std::begin;
using std::end;
m_value.object = create<object_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
@@ -1170,7 +1174,9 @@ class basic_json
*/
basic_json(const array_t& val)
: m_type(value_t::array), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an array (implicit)
@@ -1198,22 +1204,21 @@ class basic_json
@since version 1.0.0
*/
- template <class CompatibleArrayType, typename
- std::enable_if<
- not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
- not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
- not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
- std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
- = 0>
+ template<class CompatibleArrayType, typename std::enable_if<
+ not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
+ not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
+ not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
+ not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
+ not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
+ not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
+ std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0>
basic_json(const CompatibleArrayType& val)
: m_type(value_t::array)
{
using std::begin;
using std::end;
m_value.array = create<array_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
@@ -1239,7 +1244,9 @@ class basic_json
*/
basic_json(const string_t& val)
: m_type(value_t::string), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a string (explicit)
@@ -1263,7 +1270,9 @@ class basic_json
*/
basic_json(const typename string_t::value_type* val)
: basic_json(string_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a string (implicit)
@@ -1288,13 +1297,13 @@ class basic_json
@since version 1.0.0
*/
- template <class CompatibleStringType, typename
- std::enable_if<
- std::is_constructible<string_t, CompatibleStringType>::value, int>::type
- = 0>
+ template<class CompatibleStringType, typename std::enable_if<
+ std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
basic_json(const CompatibleStringType& val)
: basic_json(string_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a boolean (explicit)
@@ -1312,7 +1321,9 @@ class basic_json
*/
basic_json(boolean_t val) noexcept
: m_type(value_t::boolean), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an integer number (explicit)
@@ -1337,15 +1348,14 @@ class basic_json
@since version 1.0.0
*/
- template<typename T,
- typename std::enable_if<
- not (std::is_same<T, int>::value)
- and std::is_same<T, number_integer_t>::value
- , int>::type
- = 0>
+ template<typename T, typename std::enable_if<
+ not (std::is_same<T, int>::value) and
+ std::is_same<T, number_integer_t>::value, int>::type = 0>
basic_json(const number_integer_t val) noexcept
: m_type(value_t::number_integer), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an integer number from an enum type (explicit)
@@ -1375,7 +1385,9 @@ class basic_json
basic_json(const int val) noexcept
: m_type(value_t::number_integer),
m_value(static_cast<number_integer_t>(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an integer number (implicit)
@@ -1402,25 +1414,25 @@ class basic_json
@since version 1.0.0
*/
- template<typename CompatibleNumberIntegerType, typename
- std::enable_if<
+ template<typename CompatibleNumberIntegerType, typename std::enable_if<
std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
- CompatibleNumberIntegerType>::type
- = 0>
+ CompatibleNumberIntegerType>::type = 0>
basic_json(const CompatibleNumberIntegerType val) noexcept
: m_type(value_t::number_integer),
m_value(static_cast<number_integer_t>(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an unsigned integer number (explicit)
Create an unsigned integer number JSON value with a given content.
- @tparam T helper type to compare number_unsigned_t and unsigned int
- (not visible in) the interface.
+ @tparam T helper type to compare number_unsigned_t and unsigned int (not
+ visible in) the interface.
@param[in] val an integer to create a JSON number from
@@ -1431,15 +1443,14 @@ class basic_json
@since version 2.0.0
*/
- template<typename T,
- typename std::enable_if<
- not (std::is_same<T, int>::value)
- and std::is_same<T, number_unsigned_t>::value
- , int>::type
- = 0>
+ template<typename T, typename std::enable_if<
+ not (std::is_same<T, int>::value) and
+ std::is_same<T, number_unsigned_t>::value, int>::type = 0>
basic_json(const number_unsigned_t val) noexcept
: m_type(value_t::number_unsigned), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an unsigned number (implicit)
@@ -1461,17 +1472,17 @@ class basic_json
@since version 2.0.0
*/
- template <typename CompatibleNumberUnsignedType, typename
- std::enable_if <
- std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
- std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
- not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
- CompatibleNumberUnsignedType>::type
- = 0>
+ template<typename CompatibleNumberUnsignedType, typename std::enable_if <
+ std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
+ std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
+ not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
+ CompatibleNumberUnsignedType>::type = 0>
basic_json(const CompatibleNumberUnsignedType val) noexcept
: m_type(value_t::number_unsigned),
m_value(static_cast<number_unsigned_t>(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a floating-point number (explicit)
@@ -1484,8 +1495,8 @@ class basic_json
disallows NaN values:
> Numeric values that cannot be represented in the grammar below (such as
> Infinity and NaN) are not permitted.
- In case the parameter @a val is not a number, a JSON null value is
- created instead.
+ In case the parameter @a val is not a number, a JSON null value is created
+ instead.
@complexity Constant.
@@ -1506,6 +1517,8 @@ class basic_json
m_type = value_t::null;
m_value = json_value();
}
+
+ assert_invariant();
}
/*!
@@ -1539,14 +1552,14 @@ class basic_json
@since version 1.0.0
*/
- template<typename CompatibleNumberFloatType, typename = typename
- std::enable_if<
+ template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
- std::is_floating_point<CompatibleNumberFloatType>::value>::type
- >
+ std::is_floating_point<CompatibleNumberFloatType>::value>::type>
basic_json(const CompatibleNumberFloatType val) noexcept
: basic_json(number_float_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a container (array or object) from an initializer list
@@ -1558,21 +1571,21 @@ class basic_json
1. If the list is empty, an empty JSON object value `{}` is created.
2. If the list consists of pairs whose first element is a string, a JSON
- object value is created where the first elements of the pairs are treated
- as keys and the second elements are as values.
+ object value is created where the first elements of the pairs are
+ treated as keys and the second elements are as values.
3. In all other cases, an array is created.
The rules aim to create the best fit between a C++ initializer list and
JSON values. The rationale is as follows:
1. The empty initializer list is written as `{}` which is exactly an empty
- JSON object.
+ JSON object.
2. C++ has now way of describing mapped types other than to list a list of
- pairs. As JSON requires that keys must be of type string, rule 2 is the
- weakest constraint one can pose on initializer lists to interpret them as
- an object.
+ pairs. As JSON requires that keys must be of type string, rule 2 is the
+ weakest constraint one can pose on initializer lists to interpret them
+ as an object.
3. In all other cases, the initializer list could not be interpreted as
- JSON object type, so interpreting it as JSON array type is safe.
+ JSON object type, so interpreting it as JSON array type is safe.
With the rules described above, the following JSON values cannot be
expressed by an initializer list:
@@ -1621,22 +1634,13 @@ class basic_json
bool type_deduction = true,
value_t manual_type = value_t::array)
{
- // the initializer list could describe an object
- bool is_an_object = true;
-
// check if each element is an array with two elements whose first
// element is a string
- for (const auto& element : init)
+ bool is_an_object = std::all_of(init.begin(), init.end(),
+ [](const basic_json & element)
{
- if (not element.is_array() or element.size() != 2
- or not element[0].is_string())
- {
- // we found an element that makes it impossible to use the
- // initializer list as object
- is_an_object = false;
- break;
- }
- }
+ return element.is_array() and element.size() == 2 and element[0].is_string();
+ });
// adjust type if type deduction is not wanted
if (not type_deduction)
@@ -1660,12 +1664,10 @@ class basic_json
m_type = value_t::object;
m_value = value_t::object;
- assert(m_value.object != nullptr);
-
- for (auto& element : init)
+ std::for_each(init.begin(), init.end(), [this](const basic_json & element)
{
m_value.object->emplace(*(element[0].m_value.string), element[1]);
- }
+ });
}
else
{
@@ -1673,6 +1675,8 @@ class basic_json
m_type = value_t::array;
m_value.array = create<array_t>(init);
}
+
+ assert_invariant();
}
/*!
@@ -1777,6 +1781,7 @@ class basic_json
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
+ assert_invariant();
}
/*!
@@ -1797,6 +1802,9 @@ class basic_json
@param[in] first begin of the range to copy from (included)
@param[in] last end of the range to copy from (excluded)
+ @pre Iterators @a first and @a last must be initialized. **This
+ precondition is enforced with an assertion.**
+
@throw std::domain_error if iterators are not compatible; that is, do not
belong to the same JSON value; example: `"iterators are not compatible"`
@throw std::out_of_range if iterators are for a primitive type (number,
@@ -1813,20 +1821,23 @@ class basic_json
@since version 1.0.0
*/
- template <class InputIT, typename
- std::enable_if<
- std::is_same<InputIT, typename basic_json_t::iterator>::value or
- std::is_same<InputIT, typename basic_json_t::const_iterator>::value
- , int>::type
- = 0>
- basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
+ template<class InputIT, typename std::enable_if<
+ std::is_same<InputIT, typename basic_json_t::iterator>::value or
+ std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
+ basic_json(InputIT first, InputIT last)
{
+ assert(first.m_object != nullptr);
+ assert(last.m_object != nullptr);
+
// make sure iterator fits the current value
if (first.m_object != last.m_object)
{
throw std::domain_error("iterators are not compatible");
}
+ // copy type from first iterator
+ m_type = first.m_object->m_type;
+
// check if iterator range is complete for primitive values
switch (m_type)
{
@@ -1853,35 +1864,30 @@ class basic_json
{
case value_t::number_integer:
{
- assert(first.m_object != nullptr);
m_value.number_integer = first.m_object->m_value.number_integer;
break;
}
case value_t::number_unsigned:
{
- assert(first.m_object != nullptr);
m_value.number_unsigned = first.m_object->m_value.number_unsigned;
break;
}
case value_t::number_float:
{
- assert(first.m_object != nullptr);
m_value.number_float = first.m_object->m_value.number_float;
break;
}
case value_t::boolean:
{
- assert(first.m_object != nullptr);
m_value.boolean = first.m_object->m_value.boolean;
break;
}
case value_t::string:
{
- assert(first.m_object != nullptr);
m_value = *first.m_object->m_value.string;
break;
}
@@ -1900,10 +1906,11 @@ class basic_json
default:
{
- assert(first.m_object != nullptr);
throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
}
}
+
+ assert_invariant();
}
/*!
@@ -1920,15 +1927,25 @@ class basic_json
@note A UTF-8 byte order mark is silently ignored.
+ @deprecated This constructor is deprecated and will be removed in version
+ 3.0.0 to unify the interface of the library. Deserialization will be
+ done by stream operators or by calling one of the `parse` functions,
+ e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls
+ like `json j(i);` for an input stream @a i need to be replaced by
+ `json j = json::parse(i);`. See the example below.
+
@liveexample{The example below demonstrates constructing a JSON value from
a `std::stringstream` with and without callback
function.,basic_json__istream}
- @since version 2.0.0
+ @since version 2.0.0, deprecated in version 2.0.3, to be removed in
+ version 3.0.0
*/
- explicit basic_json(std::istream& i, parser_callback_t cb = nullptr)
+ JSON_DEPRECATED
+ explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
{
*this = parser(i, cb).parse();
+ assert_invariant();
}
///////////////////////////////////////
@@ -1960,25 +1977,25 @@ class basic_json
basic_json(const basic_json& other)
: m_type(other.m_type)
{
+ // check of passed value is valid
+ other.assert_invariant();
+
switch (m_type)
{
case value_t::object:
{
- assert(other.m_value.object != nullptr);
m_value = *other.m_value.object;
break;
}
case value_t::array:
{
- assert(other.m_value.array != nullptr);
m_value = *other.m_value.array;
break;
}
case value_t::string:
{
- assert(other.m_value.string != nullptr);
m_value = *other.m_value.string;
break;
}
@@ -2012,6 +2029,8 @@ class basic_json
break;
}
}
+
+ assert_invariant();
}
/*!
@@ -2036,9 +2055,14 @@ class basic_json
: m_type(std::move(other.m_type)),
m_value(std::move(other.m_value))
{
+ // check that passed value is valid
+ other.assert_invariant();
+
// invalidate payload
other.m_type = value_t::null;
other.m_value = {};
+
+ assert_invariant();
}
/*!
@@ -2071,9 +2095,14 @@ class basic_json
std::is_nothrow_move_assignable<json_value>::value
)
{
+ // check that passed value is valid
+ other.assert_invariant();
+
using std::swap;
swap(m_type, other.m_type);
swap(m_value, other.m_value);
+
+ assert_invariant();
return *this;
}
@@ -2094,6 +2123,8 @@ class basic_json
*/
~basic_json()
{
+ assert_invariant();
+
switch (m_type)
{
case value_t::object:
@@ -2136,19 +2167,20 @@ class basic_json
///////////////////////
/// @name object inspection
+ /// Functions to inspect the type of a JSON value.
/// @{
/*!
@brief serialization
Serialization function for JSON values. The function tries to mimic
- Python's @p json.dumps() function, and currently supports its @p indent
+ Python's `json.dumps()` function, and currently supports its @a indent
parameter.
- @param[in] indent if indent is nonnegative, then array elements and object
+ @param[in] indent If indent is nonnegative, then array elements and object
members will be pretty-printed with that indent level. An indent level of
- 0 will only insert newlines. -1 (the default) selects the most compact
- representation
+ `0` will only insert newlines. `-1` (the default) selects the most compact
+ representation.
@return string containing the serialization of the JSON value
@@ -2164,6 +2196,14 @@ class basic_json
string_t dump(const int indent = -1) const
{
std::stringstream ss;
+ // fix locale problems
+ ss.imbue(std::locale::classic());
+
+ // 6, 15 or 16 digits of precision allows round-trip IEEE 754
+ // string->float->string, string->double->string or string->long
+ // double->string; to be safe, we read this value from
+ // std::numeric_limits<number_float_t>::digits10
+ ss.precision(std::numeric_limits<double>::digits10);
if (indent >= 0)
{
@@ -2540,16 +2580,13 @@ class basic_json
//////////////////
/// get an object (explicit)
- template <class T, typename
- std::enable_if<
- std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
- std::is_convertible<basic_json_t, typename T::mapped_type>::value
- , int>::type = 0>
+ template<class T, typename std::enable_if<
+ std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
+ std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_object())
{
- assert(m_value.object != nullptr);
return T(m_value.object->begin(), m_value.object->end());
}
else
@@ -2563,7 +2600,6 @@ class basic_json
{
if (is_object())
{
- assert(m_value.object != nullptr);
return *(m_value.object);
}
else
@@ -2573,20 +2609,17 @@ class basic_json
}
/// get an array (explicit)
- template <class T, typename
- std::enable_if<
- std::is_convertible<basic_json_t, typename T::value_type>::value and
- not std::is_same<basic_json_t, typename T::value_type>::value and
- not std::is_arithmetic<T>::value and
- not std::is_convertible<std::string, T>::value and
- not has_mapped_type<T>::value
- , int>::type = 0>
+ template<class T, typename std::enable_if<
+ std::is_convertible<basic_json_t, typename T::value_type>::value and
+ not std::is_same<basic_json_t, typename T::value_type>::value and
+ not std::is_arithmetic<T>::value and
+ not std::is_convertible<std::string, T>::value and
+ not has_mapped_type<T>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_array())
{
T to_vector;
- assert(m_value.array != nullptr);
std::transform(m_value.array->begin(), m_value.array->end(),
std::inserter(to_vector, to_vector.end()), [](basic_json i)
{
@@ -2601,17 +2634,14 @@ class basic_json
}
/// get an array (explicit)
- template <class T, typename
- std::enable_if<
- std::is_convertible<basic_json_t, T>::value and
- not std::is_same<basic_json_t, T>::value
- , int>::type = 0>
+ template<class T, typename std::enable_if<
+ std::is_convertible<basic_json_t, T>::value and
+ not std::is_same<basic_json_t, T>::value, int>::type = 0>
std::vector<T> get_impl(std::vector<T>*) const
{
if (is_array())
{
std::vector<T> to_vector;
- assert(m_value.array != nullptr);
to_vector.reserve(m_value.array->size());
std::transform(m_value.array->begin(), m_value.array->end(),
std::inserter(to_vector, to_vector.end()), [](basic_json i)
@@ -2627,16 +2657,13 @@ class basic_json
}
/// get an array (explicit)
- template <class T, typename
- std::enable_if<
- std::is_same<basic_json, typename T::value_type>::value and
- not has_mapped_type<T>::value
- , int>::type = 0>
+ template<class T, typename std::enable_if<
+ std::is_same<basic_json, typename T::value_type>::value and
+ not has_mapped_type<T>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_array())
{
- assert(m_value.array != nullptr);
return T(m_value.array->begin(), m_value.array->end());
}
else
@@ -2650,7 +2677,6 @@ class basic_json
{
if (is_array())
{
- assert(m_value.array != nullptr);
return *(m_value.array);
}
else
@@ -2660,15 +2686,12 @@ class basic_json
}
/// get a string (explicit)
- template <typename T, typename
- std::enable_if<
- std::is_convertible<string_t, T>::value
- , int>::type = 0>
+ template<typename T, typename std::enable_if<
+ std::is_convertible<string_t, T>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_string())
{
- assert(m_value.string != nullptr);
return *m_value.string;
}
else
@@ -2678,10 +2701,8 @@ class basic_json
}
/// get a number (explicit)
- template<typename T, typename
- std::enable_if<
- std::is_arithmetic<T>::value
- , int>::type = 0>
+ template<typename T, typename std::enable_if<
+ std::is_arithmetic<T>::value, int>::type = 0>
T get_impl(T*) const
{
switch (m_type)
@@ -2814,8 +2835,10 @@ class basic_json
template<typename ReferenceType, typename ThisType>
static ReferenceType get_ref_impl(ThisType& obj)
{
- // delegate the call to get_ptr<>()
+ // helper type
using PointerType = typename std::add_pointer<ReferenceType>::type;
+
+ // delegate the call to get_ptr<>()
auto ptr = obj.template get_ptr<PointerType>();
if (ptr != nullptr)
@@ -2832,6 +2855,7 @@ class basic_json
public:
/// @name value access
+ /// Direct access to the stored value of a JSON value.
/// @{
/*!
@@ -2867,10 +2891,8 @@ class basic_json
@since version 1.0.0
*/
- template<typename ValueType, typename
- std::enable_if<
- not std::is_pointer<ValueType>::value
- , int>::type = 0>
+ template<typename ValueType, typename std::enable_if<
+ not std::is_pointer<ValueType>::value, int>::type = 0>
ValueType get() const
{
return get_impl(static_cast<ValueType*>(nullptr));
@@ -2882,7 +2904,8 @@ class basic_json
Explicit pointer access to the internally stored JSON value. No copies are
made.
- @warning The pointer becomes invalid if the underlying JSON object changes.
+ @warning The pointer becomes invalid if the underlying JSON object
+ changes.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@@ -2902,10 +2925,8 @@ class basic_json
@since version 1.0.0
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get() noexcept
{
// delegate the call to get_ptr
@@ -2916,10 +2937,8 @@ class basic_json
@brief get a pointer value (explicit)
@copydoc get()
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
constexpr const PointerType get() const noexcept
{
// delegate the call to get_ptr
@@ -2937,7 +2956,8 @@ class basic_json
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
- @ref number_unsigned_t, or @ref number_float_t.
+ @ref number_unsigned_t, or @ref number_float_t. Enforced by a static
+ assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@@ -2951,12 +2971,25 @@ class basic_json
@since version 1.0.0
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept
{
+ // get the type of the PointerType (remove pointer and const)
+ using pointee_t = typename std::remove_const<typename
+ std::remove_pointer<typename
+ std::remove_const<PointerType>::type>::type>::type;
+ // make sure the type matches the allowed types
+ static_assert(
+ std::is_same<object_t, pointee_t>::value
+ or std::is_same<array_t, pointee_t>::value
+ or std::is_same<string_t, pointee_t>::value
+ or std::is_same<boolean_t, pointee_t>::value
+ or std::is_same<number_integer_t, pointee_t>::value
+ or std::is_same<number_unsigned_t, pointee_t>::value
+ or std::is_same<number_float_t, pointee_t>::value
+ , "incompatible pointer type");
+
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
@@ -2965,13 +2998,26 @@ class basic_json
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- and std::is_const<typename std::remove_pointer<PointerType>::type>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value and
+ std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept
{
+ // get the type of the PointerType (remove pointer and const)
+ using pointee_t = typename std::remove_const<typename
+ std::remove_pointer<typename
+ std::remove_const<PointerType>::type>::type>::type;
+ // make sure the type matches the allowed types
+ static_assert(
+ std::is_same<object_t, pointee_t>::value
+ or std::is_same<array_t, pointee_t>::value
+ or std::is_same<string_t, pointee_t>::value
+ or std::is_same<boolean_t, pointee_t>::value
+ or std::is_same<number_integer_t, pointee_t>::value
+ or std::is_same<number_unsigned_t, pointee_t>::value
+ or std::is_same<number_float_t, pointee_t>::value
+ , "incompatible pointer type");
+
// delegate the call to get_impl_ptr<>() const
return get_impl_ptr(static_cast<const PointerType>(nullptr));
}
@@ -2987,7 +3033,7 @@ class basic_json
@tparam ReferenceType reference type; must be a reference to @ref array_t,
@ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or
- @ref number_float_t.
+ @ref number_float_t. Enforced by static assertion.
@return reference to the internally stored JSON value if the requested
reference type @a ReferenceType fits to the JSON value; throws
@@ -3002,10 +3048,8 @@ class basic_json
@since version 1.1.0
*/
- template<typename ReferenceType, typename
- std::enable_if<
- std::is_reference<ReferenceType>::value
- , int>::type = 0>
+ template<typename ReferenceType, typename std::enable_if<
+ std::is_reference<ReferenceType>::value, int>::type = 0>
ReferenceType get_ref()
{
// delegate call to get_ref_impl
@@ -3016,11 +3060,9 @@ class basic_json
@brief get a reference value (implicit)
@copydoc get_ref()
*/
- template<typename ReferenceType, typename
- std::enable_if<
- std::is_reference<ReferenceType>::value
- and std::is_const<typename std::remove_reference<ReferenceType>::type>::value
- , int>::type = 0>
+ template<typename ReferenceType, typename std::enable_if<
+ std::is_reference<ReferenceType>::value and
+ std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
ReferenceType get_ref() const
{
// delegate call to get_ref_impl
@@ -3055,10 +3097,9 @@ class basic_json
@since version 1.0.0
*/
- template < typename ValueType, typename
- std::enable_if <
- not std::is_pointer<ValueType>::value
- and not std::is_same<ValueType, typename string_t::value_type>::value
+ template < typename ValueType, typename std::enable_if <
+ not std::is_pointer<ValueType>::value and
+ not std::is_same<ValueType, typename string_t::value_type>::value
#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif
@@ -3077,6 +3118,7 @@ class basic_json
////////////////////
/// @name element access
+ /// Access to the JSON value.
/// @{
/*!
@@ -3108,7 +3150,6 @@ class basic_json
{
try
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
@@ -3152,7 +3193,6 @@ class basic_json
{
try
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
@@ -3200,7 +3240,6 @@ class basic_json
{
try
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
@@ -3248,7 +3287,6 @@ class basic_json
{
try
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
@@ -3295,16 +3333,18 @@ class basic_json
{
m_type = value_t::array;
m_value.array = create<array_t>();
+ assert_invariant();
}
// operator[] only works for arrays
if (is_array())
{
- // fill up array with null values until given idx is reached
- assert(m_value.array != nullptr);
- for (size_t i = m_value.array->size(); i <= idx; ++i)
+ // fill up array with null values if given idx is outside range
+ if (idx >= m_value.array->size())
{
- m_value.array->push_back(basic_json());
+ m_value.array->insert(m_value.array->end(),
+ idx - m_value.array->size() + 1,
+ basic_json());
}
return m_value.array->operator[](idx);
@@ -3339,7 +3379,6 @@ class basic_json
// const operator[] only works for arrays
if (is_array())
{
- assert(m_value.array != nullptr);
return m_value.array->operator[](idx);
}
else
@@ -3382,12 +3421,12 @@ class basic_json
{
m_type = value_t::object;
m_value.object = create<object_t>();
+ assert_invariant();
}
// operator[] only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
@@ -3409,6 +3448,9 @@ class basic_json
@return const reference to the element at key @a key
+ @pre The element with key @a key must exist. **This precondition is
+ enforced with an assertion.**
+
@throw std::domain_error if JSON is not an object; example: `"cannot use
operator[] with null"`
@@ -3428,7 +3470,6 @@ class basic_json
// const operator[] only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
@@ -3541,12 +3582,12 @@ class basic_json
{
m_type = value_t::object;
m_value = value_t::object;
+ assert_invariant();
}
// at only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
@@ -3568,6 +3609,9 @@ class basic_json
@return const reference to the element at key @a key
+ @pre The element with key @a key must exist. **This precondition is
+ enforced with an assertion.**
+
@throw std::domain_error if JSON is not an object; example: `"cannot use
operator[] with null"`
@@ -3588,7 +3632,6 @@ class basic_json
// at only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
@@ -3601,8 +3644,8 @@ class basic_json
/*!
@brief access specified object element with default value
- Returns either a copy of an object's element at the specified key @a key or
- a given default value if no element with key @a key exists.
+ Returns either a copy of an object's element at the specified key @a key
+ or a given default value if no element with key @a key exists.
The function is basically equivalent to executing
@code {.cpp}
@@ -3646,10 +3689,8 @@ class basic_json
@since version 1.0.0
*/
- template <class ValueType, typename
- std::enable_if<
- std::is_convertible<basic_json_t, ValueType>::value
- , int>::type = 0>
+ template<class ValueType, typename std::enable_if<
+ std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
ValueType value(const typename object_t::key_type& key, ValueType default_value) const
{
// at only works for objects
@@ -3674,7 +3715,7 @@ class basic_json
/*!
@brief overload for a default value of type const char*
- @copydoc basic_json::value()
+ @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const
*/
string_t value(const typename object_t::key_type& key, const char* default_value) const
{
@@ -3682,19 +3723,93 @@ class basic_json
}
/*!
+ @brief access specified object element via JSON Pointer with default value
+
+ Returns either a copy of an object's element at the specified key @a key
+ or a given default value if no element with key @a key exists.
+
+ The function is basically equivalent to executing
+ @code {.cpp}
+ try {
+ return at(ptr);
+ } catch(std::out_of_range) {
+ return default_value;
+ }
+ @endcode
+
+ @note Unlike @ref at(const json_pointer&), this function does not throw
+ if the given key @a key was not found.
+
+ @param[in] ptr a JSON pointer to the element to access
+ @param[in] default_value the value to return if @a ptr found no value
+
+ @tparam ValueType type compatible to JSON values, for instance `int` for
+ JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
+ JSON arrays. Note the type of the expected value at @a key and the default
+ value @a default_value must be compatible.
+
+ @return copy of the element at key @a key or @a default_value if @a key
+ is not found
+
+ @throw std::domain_error if JSON is not an object; example: `"cannot use
+ value() with null"`
+
+ @complexity Logarithmic in the size of the container.
+
+ @liveexample{The example below shows how object elements can be queried
+ with a default value.,basic_json__value_ptr}
+
+ @sa @ref operator[](const json_pointer&) for unchecked access by reference
+
+ @since version 2.0.2
+ */
+ template<class ValueType, typename std::enable_if<
+ std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
+ ValueType value(const json_pointer& ptr, ValueType default_value) const
+ {
+ // at only works for objects
+ if (is_object())
+ {
+ // if pointer resolves a value, return it or use default value
+ try
+ {
+ return ptr.get_checked(this);
+ }
+ catch (std::out_of_range&)
+ {
+ return default_value;
+ }
+ }
+ else
+ {
+ throw std::domain_error("cannot use value() with " + type_name());
+ }
+ }
+
+ /*!
+ @brief overload for a default value of type const char*
+ @copydoc basic_json::value(const json_pointer&, ValueType) const
+ */
+ string_t value(const json_pointer& ptr, const char* default_value) const
+ {
+ return value(ptr, string_t(default_value));
+ }
+
+ /*!
@brief access the first element
Returns a reference to the first element in the container. For a JSON
container `c`, the expression `c.front()` is equivalent to `*c.begin()`.
@return In case of a structured type (array or object), a reference to the
- first element is returned. In cast of number, string, or boolean values, a
+ first element is returned. In case of number, string, or boolean values, a
reference to the value is returned.
@complexity Constant.
@pre The JSON value must not be `null` (would throw `std::out_of_range`)
- or an empty array or object (undefined behavior, guarded by assertions).
+ or an empty array or object (undefined behavior, **guarded by
+ assertions**).
@post The JSON value remains unchanged.
@throw std::out_of_range when called on `null` value
@@ -3730,13 +3845,14 @@ class basic_json
@endcode
@return In case of a structured type (array or object), a reference to the
- last element is returned. In cast of number, string, or boolean values, a
+ last element is returned. In case of number, string, or boolean values, a
reference to the value is returned.
@complexity Constant.
@pre The JSON value must not be `null` (would throw `std::out_of_range`)
- or an empty array or object (undefined behavior, guarded by assertions).
+ or an empty array or object (undefined behavior, **guarded by
+ assertions**).
@post The JSON value remains unchanged.
@throw std::out_of_range when called on `null` value.
@@ -3778,7 +3894,7 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a
pos refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@@ -3800,7 +3916,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType}
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -3809,13 +3925,11 @@ class basic_json
@since version 1.0.0
*/
- template <class InteratorType, typename
- std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
- , int>::type
- = 0>
- InteratorType erase(InteratorType pos)
+ template<class IteratorType, typename std::enable_if<
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
+ = 0>
+ IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
if (this != pos.m_object)
@@ -3823,7 +3937,7 @@ class basic_json
throw std::domain_error("iterator does not fit current value");
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -3840,24 +3954,25 @@ class basic_json
if (is_string())
{
- delete m_value.string;
+ AllocatorType<string_t> alloc;
+ alloc.destroy(m_value.string);
+ alloc.deallocate(m_value.string, 1);
m_value.string = nullptr;
}
m_type = value_t::null;
+ assert_invariant();
break;
}
case value_t::object:
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
break;
}
case value_t::array:
{
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
break;
}
@@ -3886,7 +4001,7 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a
second refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@@ -3909,7 +4024,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType}
- @sa @ref erase(InteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@sa @ref erase(const size_type) -- removes the element from an array at
@@ -3917,13 +4032,11 @@ class basic_json
@since version 1.0.0
*/
- template <class InteratorType, typename
- std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
- , int>::type
- = 0>
- InteratorType erase(InteratorType first, InteratorType last)
+ template<class IteratorType, typename std::enable_if<
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
+ = 0>
+ IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
if (this != first.m_object or this != last.m_object)
@@ -3931,7 +4044,7 @@ class basic_json
throw std::domain_error("iterators do not fit current value");
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -3948,17 +4061,19 @@ class basic_json
if (is_string())
{
- delete m_value.string;
+ AllocatorType<string_t> alloc;
+ alloc.destroy(m_value.string);
+ alloc.deallocate(m_value.string, 1);
m_value.string = nullptr;
}
m_type = value_t::null;
+ assert_invariant();
break;
}
case value_t::object:
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
last.m_it.object_iterator);
break;
@@ -3966,7 +4081,6 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
last.m_it.array_iterator);
break;
@@ -4002,8 +4116,8 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__key_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const size_type) -- removes the element from an array at
the given index
@@ -4015,7 +4129,6 @@ class basic_json
// this erase only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
return m_value.object->erase(key);
}
else
@@ -4040,8 +4153,8 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__size_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -4058,7 +4171,6 @@ class basic_json
throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
}
- assert(m_value.array != nullptr);
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
@@ -4084,10 +4196,14 @@ class basic_json
element is not found or the JSON value is not an object, end() is
returned.
+ @note This method always returns @ref end() when executed on a JSON type
+ that is not an object.
+
@param[in] key key value of the element to search for
@return Iterator to an element with key equivalent to @a key. If no such
- element is found, past-the-end (see end()) iterator is returned.
+ element is found or the JSON value is not an object, past-the-end (see
+ @ref end()) iterator is returned.
@complexity Logarithmic in the size of the JSON object.
@@ -4101,7 +4217,6 @@ class basic_json
if (is_object())
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
@@ -4118,7 +4233,6 @@ class basic_json
if (is_object())
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
@@ -4132,6 +4246,9 @@ class basic_json
default `std::map` type, the return value will always be `0` (@a key was
not found) or `1` (@a key was found).
+ @note This method always returns `0` when executed on a JSON type that is
+ not an object.
+
@param[in] key key value of the element to count
@return Number of elements with key @a key. If the JSON value is not an
@@ -4146,7 +4263,6 @@ class basic_json
size_type count(typename object_t::key_type key) const
{
// return 0 for all nonobject types
- assert(not is_object() or m_value.object != nullptr);
return is_object() ? m_value.object->count(key) : 0;
}
@@ -4488,6 +4604,10 @@ class basic_json
object | result of function `object_t::empty()`
array | result of function `array_t::empty()`
+ @note This function does not return whether a string stored as JSON value
+ is empty - it returns whether the JSON container itself is empty which is
+ false in the case of a string.
+
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their `empty()` functions have constant
complexity.
@@ -4517,13 +4637,13 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
+ // delegate call to array_t::empty()
return m_value.array->empty();
}
case value_t::object:
{
- assert(m_value.object != nullptr);
+ // delegate call to object_t::empty()
return m_value.object->empty();
}
@@ -4551,6 +4671,10 @@ class basic_json
object | result of function object_t::size()
array | result of function array_t::size()
+ @note This function does not return the length of a string stored as JSON
+ value - it returns the number of elements in the JSON value which is 1 in
+ the case of a string.
+
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their size() functions have constant
complexity.
@@ -4581,13 +4705,13 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
+ // delegate call to array_t::size()
return m_value.array->size();
}
case value_t::object:
{
- assert(m_value.object != nullptr);
+ // delegate call to object_t::size()
return m_value.object->size();
}
@@ -4641,13 +4765,13 @@ class basic_json
{
case value_t::array:
{
- assert(m_value.array != nullptr);
+ // delegate call to array_t::max_size()
return m_value.array->max_size();
}
case value_t::object:
{
- assert(m_value.object != nullptr);
+ // delegate call to object_t::max_size()
return m_value.object->max_size();
}
@@ -4684,9 +4808,6 @@ class basic_json
object | `{}`
array | `[]`
- @note Floating-point numbers are set to `0.0` which will be serialized to
- `0`. The vale type remains @ref number_float_t.
-
@complexity Linear in the size of the JSON value.
@liveexample{The example below shows the effect of `clear()` to different
@@ -4724,21 +4845,18 @@ class basic_json
case value_t::string:
{
- assert(m_value.string != nullptr);
m_value.string->clear();
break;
}
case value_t::array:
{
- assert(m_value.array != nullptr);
m_value.array->clear();
break;
}
case value_t::object:
{
- assert(m_value.object != nullptr);
m_value.object->clear();
break;
}
@@ -4783,10 +4901,10 @@ class basic_json
{
m_type = value_t::array;
m_value = value_t::array;
+ assert_invariant();
}
// add element to array (move semantics)
- assert(m_value.array != nullptr);
m_value.array->push_back(std::move(val));
// invalidate object
val.m_type = value_t::null;
@@ -4819,10 +4937,10 @@ class basic_json
{
m_type = value_t::array;
m_value = value_t::array;
+ assert_invariant();
}
// add element to array
- assert(m_value.array != nullptr);
m_value.array->push_back(val);
}
@@ -4869,10 +4987,10 @@ class basic_json
{
m_type = value_t::object;
m_value = value_t::object;
+ assert_invariant();
}
// add element to array
- assert(m_value.object != nullptr);
m_value.object->insert(val);
}
@@ -4935,6 +5053,102 @@ class basic_json
}
/*!
+ @brief add an object to an array
+
+ Creates a JSON value from the passed parameters @a args to the end of the
+ JSON value. If the function is called on a JSON null value, an empty array
+ is created before appending the value created from @a args.
+
+ @param[in] args arguments to forward to a constructor of @ref basic_json
+ @tparam Args compatible types to create a @ref basic_json object
+
+ @throw std::domain_error when called on a type other than JSON array or
+ null; example: `"cannot use emplace_back() with number"`
+
+ @complexity Amortized constant.
+
+ @liveexample{The example shows how `push_back()` can be used to add
+ elements to a JSON array. Note how the `null` value was silently converted
+ to a JSON array.,emplace_back}
+
+ @since version 2.0.8
+ */
+ template<class... Args>
+ void emplace_back(Args&& ... args)
+ {
+ // emplace_back only works for null objects or arrays
+ if (not(is_null() or is_array()))
+ {
+ throw std::domain_error("cannot use emplace_back() with " + type_name());
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ m_value.array->emplace_back(std::forward<Args>(args)...);
+ }
+
+ /*!
+ @brief add an object to an object if key does not exist
+
+ Inserts a new element into a JSON object constructed in-place with the given
+ @a args if there is no element with the key in the container. If the
+ function is called on a JSON null value, an empty object is created before
+ appending the value created from @a args.
+
+ @param[in] args arguments to forward to a constructor of @ref basic_json
+ @tparam Args compatible types to create a @ref basic_json object
+
+ @return a pair consisting of an iterator to the inserted element, or the
+ already-existing element if no insertion happened, and a bool
+ denoting whether the insertion took place.
+
+ @throw std::domain_error when called on a type other than JSON object or
+ null; example: `"cannot use emplace() with number"`
+
+ @complexity Logarithmic in the size of the container, O(log(`size()`)).
+
+ @liveexample{The example shows how `emplace()` can be used to add elements
+ to a JSON object. Note how the `null` value was silently converted to a
+ JSON object. Further note how no value is added if there was already one
+ value stored with the same key.,emplace}
+
+ @since version 2.0.8
+ */
+ template<class... Args>
+ std::pair<iterator, bool> emplace(Args&& ... args)
+ {
+ // emplace only works for null objects or arrays
+ if (not(is_null() or is_object()))
+ {
+ throw std::domain_error("cannot use emplace() with " + type_name());
+ }
+
+ // transform null object into an object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value = value_t::object;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ auto res = m_value.object->emplace(std::forward<Args>(args)...);
+ // create result iterator and set iterator to the result of emplace
+ auto it = begin();
+ it.m_it.object_iterator = res.first;
+
+ // return pair of iterator and boolean
+ return {it, res.second};
+ }
+
+ /*!
@brief inserts element
Inserts element @a val before iterator @a pos.
@@ -4969,7 +5183,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
return result;
}
@@ -5025,7 +5238,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
return result;
}
@@ -5092,7 +5304,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(
pos.m_it.array_iterator,
first.m_it.array_iterator,
@@ -5140,7 +5351,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist);
return result;
}
@@ -5171,6 +5381,7 @@ class basic_json
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
+ assert_invariant();
}
/*!
@@ -5198,7 +5409,6 @@ class basic_json
// swap only works for arrays
if (is_array())
{
- assert(m_value.array != nullptr);
std::swap(*(m_value.array), other);
}
else
@@ -5232,7 +5442,6 @@ class basic_json
// swap only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
std::swap(*(m_value.object), other);
}
else
@@ -5266,7 +5475,6 @@ class basic_json
// swap only works for strings
if (is_string())
{
- assert(m_value.string != nullptr);
std::swap(*(m_value.string), other);
}
else
@@ -5353,14 +5561,10 @@ class basic_json
{
case value_t::array:
{
- assert(lhs.m_value.array != nullptr);
- assert(rhs.m_value.array != nullptr);
return *lhs.m_value.array == *rhs.m_value.array;
}
case value_t::object:
{
- assert(lhs.m_value.object != nullptr);
- assert(rhs.m_value.object != nullptr);
return *lhs.m_value.object == *rhs.m_value.object;
}
case value_t::null:
@@ -5369,8 +5573,6 @@ class basic_json
}
case value_t::string:
{
- assert(lhs.m_value.string != nullptr);
- assert(rhs.m_value.string != nullptr);
return *lhs.m_value.string == *rhs.m_value.string;
}
case value_t::boolean:
@@ -5543,14 +5745,10 @@ class basic_json
{
case value_t::array:
{
- assert(lhs.m_value.array != nullptr);
- assert(rhs.m_value.array != nullptr);
return *lhs.m_value.array < *rhs.m_value.array;
}
case value_t::object:
{
- assert(lhs.m_value.object != nullptr);
- assert(rhs.m_value.object != nullptr);
return *lhs.m_value.object < *rhs.m_value.object;
}
case value_t::null:
@@ -5559,8 +5757,6 @@ class basic_json
}
case value_t::string:
{
- assert(lhs.m_value.string != nullptr);
- assert(rhs.m_value.string != nullptr);
return *lhs.m_value.string < *rhs.m_value.string;
}
case value_t::boolean:
@@ -5702,6 +5898,10 @@ class basic_json
`std::setw(4)` on @a o sets the indentation level to `4` and the
serialization result is the same as calling `dump(4)`.
+ @note During serializaion, the locale and the precision of the output
+ stream @a o are changed. The original values are restored when the
+ function returns.
+
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@@ -5723,8 +5923,22 @@ class basic_json
// reset width to 0 for subsequent calls to this stream
o.width(0);
+ // fix locale problems
+ const auto old_locale = o.imbue(std::locale::classic());
+ // set precision
+
+ // 6, 15 or 16 digits of precision allows round-trip IEEE 754
+ // string->float->string, string->double->string or string->long
+ // double->string; to be safe, we read this value from
+ // std::numeric_limits<number_float_t>::digits10
+ const auto old_precision = o.precision(std::numeric_limits<double>::digits10);
+
// do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
+
+ // reset locale and precision
+ o.imbue(old_locale);
+ o.precision(old_precision);
return o;
}
@@ -5748,9 +5962,45 @@ class basic_json
/// @{
/*!
- @brief deserialize from string
+ @brief deserialize from an array
+
+ This function reads from an array of 1-byte values.
- @param[in] s string to read a serialized JSON value from
+ @pre Each element of the container has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+
+ @param[in] array array to read from
+ @param[in] cb a parser callback function of type @ref parser_callback_t
+ which is used to control the deserialization by filtering unwanted values
+ (optional)
+
+ @return result of the deserialization
+
+ @complexity Linear in the length of the input. The parser is a predictive
+ LL(1) parser. The complexity can be higher if the parser callback function
+ @a cb has a super-linear complexity.
+
+ @note A UTF-8 byte order mark is silently ignored.
+
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from an array.,parse__array__parser_callback_t}
+
+ @since version 2.0.3
+ */
+ template<class T, std::size_t N>
+ static basic_json parse(T (&array)[N],
+ const parser_callback_t cb = nullptr)
+ {
+ // delegate the call to the iterator-range parse overload
+ return parse(std::begin(array), std::end(array), cb);
+ }
+
+ /*!
+ @brief deserialize from string literal
+
+ @tparam CharT character/literal type with size of 1 byte
+ @param[in] s string literal to read a serialized JSON value from
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@@ -5762,18 +6012,25 @@ class basic_json
@a cb has a super-linear complexity.
@note A UTF-8 byte order mark is silently ignored.
+ @note String containers like `std::string` or @ref string_t can be parsed
+ with @ref parse(const ContiguousContainer&, const parser_callback_t)
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__string__parser_callback_t}
- @sa @ref parse(std::istream&, parser_callback_t) for a version that reads
- from an input stream
+ @sa @ref parse(std::istream&, const parser_callback_t) for a version that
+ reads from an input stream
- @since version 1.0.0
+ @since version 1.0.0 (originally for @ref string_t)
*/
- static basic_json parse(const string_t& s, parser_callback_t cb = nullptr)
+ template<typename CharT, typename std::enable_if<
+ std::is_pointer<CharT>::value and
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value and
+ sizeof(typename std::remove_pointer<CharT>::type) == 1, int>::type = 0>
+ static basic_json parse(const CharT s,
+ const parser_callback_t cb = nullptr)
{
- return parser(s, cb).parse();
+ return parser(reinterpret_cast<const char*>(s), cb).parse();
}
/*!
@@ -5795,25 +6052,151 @@ class basic_json
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__istream__parser_callback_t}
- @sa @ref parse(const string_t&, parser_callback_t) for a version that
- reads from a string
+ @sa @ref parse(const CharT, const parser_callback_t) for a version
+ that reads from a string
@since version 1.0.0
*/
- static basic_json parse(std::istream& i, parser_callback_t cb = nullptr)
+ static basic_json parse(std::istream& i,
+ const parser_callback_t cb = nullptr)
{
return parser(i, cb).parse();
}
/*!
- @copydoc parse(std::istream&, parser_callback_t)
+ @copydoc parse(std::istream&, const parser_callback_t)
*/
- static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr)
+ static basic_json parse(std::istream&& i,
+ const parser_callback_t cb = nullptr)
{
return parser(i, cb).parse();
}
/*!
+ @brief deserialize from an iterator range with contiguous storage
+
+ This function reads from an iterator range of a container with contiguous
+ storage of 1-byte values. Compatible container types include
+ `std::vector`, `std::string`, `std::array`, `std::valarray`, and
+ `std::initializer_list`. Furthermore, C-style arrays can be used with
+ `std::begin()`/`std::end()`. User-defined containers can be used as long
+ as they implement random-access iterators and a contiguous storage.
+
+ @pre The iterator range is contiguous. Violating this precondition yields
+ undefined behavior. **This precondition is enforced with an assertion.**
+ @pre Each element in the range has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+
+ @warning There is no way to enforce all preconditions at compile-time. If
+ the function is called with noncompliant iterators and with
+ assertions switched off, the behavior is undefined and will most
+ likely yield segmentation violation.
+
+ @tparam IteratorType iterator of container with contiguous storage
+ @param[in] first begin of the range to parse (included)
+ @param[in] last end of the range to parse (excluded)
+ @param[in] cb a parser callback function of type @ref parser_callback_t
+ which is used to control the deserialization by filtering unwanted values
+ (optional)
+
+ @return result of the deserialization
+
+ @complexity Linear in the length of the input. The parser is a predictive
+ LL(1) parser. The complexity can be higher if the parser callback function
+ @a cb has a super-linear complexity.
+
+ @note A UTF-8 byte order mark is silently ignored.
+
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from an iterator range.,parse__iteratortype__parser_callback_t}
+
+ @since version 2.0.3
+ */
+ template<class IteratorType, typename std::enable_if<
+ std::is_base_of<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
+ static basic_json parse(IteratorType first, IteratorType last,
+ const parser_callback_t cb = nullptr)
+ {
+ // assertion to check that the iterator range is indeed contiguous,
+ // see http://stackoverflow.com/a/35008842/266378 for more discussion
+ assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0),
+ [&first](std::pair<bool, int> res, decltype(*first) val)
+ {
+ res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
+ return res;
+ }).first);
+
+ // assertion to check that each element is 1 byte long
+ static_assert(sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
+ "each element in the iterator range must have the size of 1 byte");
+
+ // if iterator range is empty, create a parser with an empty string
+ // to generate "unexpected EOF" error message
+ if (std::distance(first, last) <= 0)
+ {
+ return parser("").parse();
+ }
+
+ return parser(first, last, cb).parse();
+ }
+
+ /*!
+ @brief deserialize from a container with contiguous storage
+
+ This function reads from a container with contiguous storage of 1-byte
+ values. Compatible container types include `std::vector`, `std::string`,
+ `std::array`, and `std::initializer_list`. User-defined containers can be
+ used as long as they implement random-access iterators and a contiguous
+ storage.
+
+ @pre The container storage is contiguous. Violating this precondition
+ yields undefined behavior. **This precondition is enforced with an
+ assertion.**
+ @pre Each element of the container has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+
+ @warning There is no way to enforce all preconditions at compile-time. If
+ the function is called with a noncompliant container and with
+ assertions switched off, the behavior is undefined and will most
+ likely yield segmentation violation.
+
+ @tparam ContiguousContainer container type with contiguous storage
+ @param[in] c container to read from
+ @param[in] cb a parser callback function of type @ref parser_callback_t
+ which is used to control the deserialization by filtering unwanted values
+ (optional)
+
+ @return result of the deserialization
+
+ @complexity Linear in the length of the input. The parser is a predictive
+ LL(1) parser. The complexity can be higher if the parser callback function
+ @a cb has a super-linear complexity.
+
+ @note A UTF-8 byte order mark is silently ignored.
+
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
+
+ @since version 2.0.3
+ */
+ template<class ContiguousContainer, typename std::enable_if<
+ not std::is_pointer<ContiguousContainer>::value and
+ std::is_base_of<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value
+ , int>::type = 0>
+ static basic_json parse(const ContiguousContainer& c,
+ const parser_callback_t cb = nullptr)
+ {
+ // delegate the call to the iterator-range parse overload
+ return parse(std::begin(c), std::end(c), cb);
+ }
+
+ /*!
@brief deserialize from stream
Deserializes an input stream to a JSON value.
@@ -5831,8 +6214,8 @@ class basic_json
@liveexample{The example below shows how a JSON value is constructed by
reading a serialization from a stream.,operator_deserialize}
- @sa parse(std::istream&, parser_callback_t) for a variant with a parser
- callback function to filter values while parsing
+ @sa parse(std::istream&, const parser_callback_t) for a variant with a
+ parser callback function to filter values while parsing
@since version 1.0.0
*/
@@ -5854,14 +6237,1515 @@ class basic_json
/// @}
+ //////////////////////////////////////////
+ // binary serialization/deserialization //
+ //////////////////////////////////////////
+
+ /// @name binary serialization/deserialization support
+ /// @{
+
+ private:
+ template<typename T>
+ static void add_to_vector(std::vector<uint8_t>& vec, size_t bytes, const T number)
+ {
+ assert(bytes == 1 or bytes == 2 or bytes == 4 or bytes == 8);
+
+ switch (bytes)
+ {
+ case 8:
+ {
+ vec.push_back(static_cast<uint8_t>((number >> 070) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 060) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 050) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 040) & 0xff));
+ // intentional fall-through
+ }
+
+ case 4:
+ {
+ vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff));
+ // intentional fall-through
+ }
+
+ case 2:
+ {
+ vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
+ // intentional fall-through
+ }
+
+ case 1:
+ {
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
+ }
+ }
+ }
+
+ /*!
+ @brief take sufficient bytes from a vector to fill an integer variable
+
+ In the context of binary serialization formats, we need to read several
+ bytes from a byte vector and combine them to multi-byte integral data
+ types.
+
+ @param[in] vec byte vector to read from
+ @param[in] current_index the position in the vector after which to read
+
+ @return the next sizeof(T) bytes from @a vec, in reverse order as T
+
+ @tparam T the integral return type
+
+ @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the
+ vector @a vec to read
+
+ In the for loop, the bytes from the vector are copied in reverse order into
+ the return value. In the figures below, let sizeof(T)=4 and `i` be the loop
+ variable.
+
+ Precondition:
+
+ vec: | | | a | b | c | d | T: | | | | |
+ ^ ^ ^ ^
+ current_index i ptr sizeof(T)
+
+ Postcondition:
+
+ vec: | | | a | b | c | d | T: | d | c | b | a |
+ ^ ^ ^
+ | i ptr
+ current_index
+
+ @sa Code adapted from <http://stackoverflow.com/a/41031865/266378>.
+ */
+ template<typename T>
+ static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index)
+ {
+ if (current_index + sizeof(T) + 1 > vec.size())
+ {
+ throw std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector");
+ }
+
+ T result;
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(&result);
+ for (size_t i = 0; i < sizeof(T); ++i)
+ {
+ *ptr++ = vec[current_index + sizeof(T) - i];
+ }
+ return result;
+ }
+
+ /*!
+ @brief create a MessagePack serialization of a given JSON value
+
+ This is a straightforward implementation of the MessagePack specification.
+
+ @param[in] j JSON value to serialize
+ @param[in,out] v byte vector to write the serialization to
+
+ @sa https://github.com/msgpack/msgpack/blob/master/spec.md
+ */
+ static void to_msgpack_internal(const basic_json& j, std::vector<uint8_t>& v)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ // nil
+ v.push_back(0xc0);
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ // true and false
+ v.push_back(j.m_value.boolean ? 0xc3 : 0xc2);
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // MessagePack does not differentiate between positive
+ // signed integers and unsigned integers. Therefore, we used
+ // the code from the value_t::number_unsigned case here.
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ add_to_vector(v, 1, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT8_MAX)
+ {
+ // uint 8
+ v.push_back(0xcc);
+ add_to_vector(v, 1, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT16_MAX)
+ {
+ // uint 16
+ v.push_back(0xcd);
+ add_to_vector(v, 2, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT32_MAX)
+ {
+ // uint 32
+ v.push_back(0xce);
+ add_to_vector(v, 4, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT64_MAX)
+ {
+ // uint 64
+ v.push_back(0xcf);
+ add_to_vector(v, 8, j.m_value.number_unsigned);
+ }
+ }
+ else
+ {
+ if (j.m_value.number_integer >= -32)
+ {
+ // negative fixnum
+ add_to_vector(v, 1, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX)
+ {
+ // int 8
+ v.push_back(0xd0);
+ add_to_vector(v, 1, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX)
+ {
+ // int 16
+ v.push_back(0xd1);
+ add_to_vector(v, 2, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX)
+ {
+ // int 32
+ v.push_back(0xd2);
+ add_to_vector(v, 4, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX)
+ {
+ // int 64
+ v.push_back(0xd3);
+ add_to_vector(v, 8, j.m_value.number_integer);
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ add_to_vector(v, 1, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT8_MAX)
+ {
+ // uint 8
+ v.push_back(0xcc);
+ add_to_vector(v, 1, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT16_MAX)
+ {
+ // uint 16
+ v.push_back(0xcd);
+ add_to_vector(v, 2, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT32_MAX)
+ {
+ // uint 32
+ v.push_back(0xce);
+ add_to_vector(v, 4, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= UINT64_MAX)
+ {
+ // uint 64
+ v.push_back(0xcf);
+ add_to_vector(v, 8, j.m_value.number_unsigned);
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ // float 64
+ v.push_back(0xcb);
+ const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
+ for (size_t i = 0; i < 8; ++i)
+ {
+ v.push_back(helper[7 - i]);
+ }
+ break;
+ }
+
+ case value_t::string:
+ {
+ const auto N = j.m_value.string->size();
+ if (N <= 31)
+ {
+ // fixstr
+ v.push_back(static_cast<uint8_t>(0xa0 | N));
+ }
+ else if (N <= 255)
+ {
+ // str 8
+ v.push_back(0xd9);
+ add_to_vector(v, 1, N);
+ }
+ else if (N <= 65535)
+ {
+ // str 16
+ v.push_back(0xda);
+ add_to_vector(v, 2, N);
+ }
+ else if (N <= 4294967295)
+ {
+ // str 32
+ v.push_back(0xdb);
+ add_to_vector(v, 4, N);
+ }
+
+ // append string
+ std::copy(j.m_value.string->begin(), j.m_value.string->end(),
+ std::back_inserter(v));
+ break;
+ }
+
+ case value_t::array:
+ {
+ const auto N = j.m_value.array->size();
+ if (N <= 15)
+ {
+ // fixarray
+ v.push_back(static_cast<uint8_t>(0x90 | N));
+ }
+ else if (N <= 0xffff)
+ {
+ // array 16
+ v.push_back(0xdc);
+ add_to_vector(v, 2, N);
+ }
+ else if (N <= 0xffffffff)
+ {
+ // array 32
+ v.push_back(0xdd);
+ add_to_vector(v, 4, N);
+ }
+
+ // append each element
+ for (const auto& el : *j.m_value.array)
+ {
+ to_msgpack_internal(el, v);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ const auto N = j.m_value.object->size();
+ if (N <= 15)
+ {
+ // fixmap
+ v.push_back(static_cast<uint8_t>(0x80 | (N & 0xf)));
+ }
+ else if (N <= 65535)
+ {
+ // map 16
+ v.push_back(0xde);
+ add_to_vector(v, 2, N);
+ }
+ else if (N <= 4294967295)
+ {
+ // map 32
+ v.push_back(0xdf);
+ add_to_vector(v, 4, N);
+ }
+
+ // append each element
+ for (const auto& el : *j.m_value.object)
+ {
+ to_msgpack_internal(el.first, v);
+ to_msgpack_internal(el.second, v);
+ }
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ /*!
+ @brief create a CBOR serialization of a given JSON value
+
+ This is a straightforward implementation of the CBOR specification.
+
+ @param[in] j JSON value to serialize
+ @param[in,out] v byte vector to write the serialization to
+
+ @sa https://tools.ietf.org/html/rfc7049
+ */
+ static void to_cbor_internal(const basic_json& j, std::vector<uint8_t>& v)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ v.push_back(0xf6);
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ v.push_back(j.m_value.boolean ? 0xf5 : 0xf4);
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // CBOR does not differentiate between positive signed
+ // integers and unsigned integers. Therefore, we used the
+ // code from the value_t::number_unsigned case here.
+ if (j.m_value.number_integer <= 0x17)
+ {
+ add_to_vector(v, 1, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer <= UINT8_MAX)
+ {
+ v.push_back(0x18);
+ // one-byte uint8_t
+ add_to_vector(v, 1, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer <= UINT16_MAX)
+ {
+ v.push_back(0x19);
+ // two-byte uint16_t
+ add_to_vector(v, 2, j.m_value.number_integer);
+ }
+ else if (j.m_value.number_integer <= UINT32_MAX)
+ {
+ v.push_back(0x1a);
+ // four-byte uint32_t
+ add_to_vector(v, 4, j.m_value.number_integer);
+ }
+ else
+ {
+ v.push_back(0x1b);
+ // eight-byte uint64_t
+ add_to_vector(v, 8, j.m_value.number_integer);
+ }
+ }
+ else
+ {
+ // The conversions below encode the sign in the first byte,
+ // and the value is converted to a positive number.
+ const auto positive_number = -1 - j.m_value.number_integer;
+ if (j.m_value.number_integer >= -24)
+ {
+ v.push_back(static_cast<uint8_t>(0x20 + positive_number));
+ }
+ else if (positive_number <= UINT8_MAX)
+ {
+ // int 8
+ v.push_back(0x38);
+ add_to_vector(v, 1, positive_number);
+ }
+ else if (positive_number <= UINT16_MAX)
+ {
+ // int 16
+ v.push_back(0x39);
+ add_to_vector(v, 2, positive_number);
+ }
+ else if (positive_number <= UINT32_MAX)
+ {
+ // int 32
+ v.push_back(0x3a);
+ add_to_vector(v, 4, positive_number);
+ }
+ else
+ {
+ // int 64
+ v.push_back(0x3b);
+ add_to_vector(v, 8, positive_number);
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= 0x17)
+ {
+ v.push_back(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= 0xff)
+ {
+ v.push_back(0x18);
+ // one-byte uint8_t
+ add_to_vector(v, 1, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= 0xffff)
+ {
+ v.push_back(0x19);
+ // two-byte uint16_t
+ add_to_vector(v, 2, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= 0xffffffff)
+ {
+ v.push_back(0x1a);
+ // four-byte uint32_t
+ add_to_vector(v, 4, j.m_value.number_unsigned);
+ }
+ else if (j.m_value.number_unsigned <= 0xffffffffffffffff)
+ {
+ v.push_back(0x1b);
+ // eight-byte uint64_t
+ add_to_vector(v, 8, j.m_value.number_unsigned);
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ // Double-Precision Float
+ v.push_back(0xfb);
+ const uint8_t* helper = reinterpret_cast<const uint8_t*>(&(j.m_value.number_float));
+ for (size_t i = 0; i < 8; ++i)
+ {
+ v.push_back(helper[7 - i]);
+ }
+ break;
+ }
+
+ case value_t::string:
+ {
+ const auto N = j.m_value.string->size();
+ if (N <= 0x17)
+ {
+ v.push_back(0x60 + N); // 1 byte for string + size
+ }
+ else if (N <= 0xff)
+ {
+ v.push_back(0x78); // one-byte uint8_t for N
+ add_to_vector(v, 1, N);
+ }
+ else if (N <= 0xffff)
+ {
+ v.push_back(0x79); // two-byte uint16_t for N
+ add_to_vector(v, 2, N);
+ }
+ else if (N <= 0xffffffff)
+ {
+ v.push_back(0x7a); // four-byte uint32_t for N
+ add_to_vector(v, 4, N);
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xffffffffffffffff)
+ {
+ v.push_back(0x7b); // eight-byte uint64_t for N
+ add_to_vector(v, 8, N);
+ }
+ // LCOV_EXCL_STOP
+
+ // append string
+ std::copy(j.m_value.string->begin(), j.m_value.string->end(),
+ std::back_inserter(v));
+ break;
+ }
+
+ case value_t::array:
+ {
+ const auto N = j.m_value.array->size();
+ if (N <= 0x17)
+ {
+ v.push_back(0x80 + N); // 1 byte for array + size
+ }
+ else if (N <= 0xff)
+ {
+ v.push_back(0x98); // one-byte uint8_t for N
+ add_to_vector(v, 1, N);
+ }
+ else if (N <= 0xffff)
+ {
+ v.push_back(0x99); // two-byte uint16_t for N
+ add_to_vector(v, 2, N);
+ }
+ else if (N <= 0xffffffff)
+ {
+ v.push_back(0x9a); // four-byte uint32_t for N
+ add_to_vector(v, 4, N);
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xffffffffffffffff)
+ {
+ v.push_back(0x9b); // eight-byte uint64_t for N
+ add_to_vector(v, 8, N);
+ }
+ // LCOV_EXCL_STOP
+
+ // append each element
+ for (const auto& el : *j.m_value.array)
+ {
+ to_cbor_internal(el, v);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ const auto N = j.m_value.object->size();
+ if (N <= 0x17)
+ {
+ v.push_back(0xa0 + N); // 1 byte for object + size
+ }
+ else if (N <= 0xff)
+ {
+ v.push_back(0xb8);
+ add_to_vector(v, 1, N); // one-byte uint8_t for N
+ }
+ else if (N <= 0xffff)
+ {
+ v.push_back(0xb9);
+ add_to_vector(v, 2, N); // two-byte uint16_t for N
+ }
+ else if (N <= 0xffffffff)
+ {
+ v.push_back(0xba);
+ add_to_vector(v, 4, N); // four-byte uint32_t for N
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xffffffffffffffff)
+ {
+ v.push_back(0xbb);
+ add_to_vector(v, 8, N); // eight-byte uint64_t for N
+ }
+ // LCOV_EXCL_STOP
+
+ // append each element
+ for (const auto& el : *j.m_value.object)
+ {
+ to_cbor_internal(el.first, v);
+ to_cbor_internal(el.second, v);
+ }
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+
+ /*
+ @brief checks if given lengths do not exceed the size of a given vector
+
+ To secure the access to the byte vector during CBOR/MessagePack
+ deserialization, bytes are copied from the vector into buffers. This
+ function checks if the number of bytes to copy (@a len) does not exceed the
+ size @s size of the vector. Additionally, an @a offset is given from where
+ to start reading the bytes.
+
+ This function checks whether reading the bytes is safe; that is, offset is a
+ valid index in the vector, offset+len
+
+ @param[in] size size of the byte vector
+ @param[in] len number of bytes to read
+ @param[in] offset offset where to start reading
+
+ vec: x x x x x X X X X X
+ ^ ^ ^
+ 0 offset len
+
+ @throws out_of_range if `len > v.size()`
+ */
+ static void check_length(const size_t size, const size_t len, const size_t offset)
+ {
+ // simple case: requested length is greater than the vector's length
+ if (len > size or offset > size)
+ {
+ throw std::out_of_range("len out of range");
+ }
+
+ // second case: adding offset would result in overflow
+ if ((size > (std::numeric_limits<size_t>::max() - offset)))
+ {
+ throw std::out_of_range("len+offset out of range");
+ }
+
+ // last case: reading past the end of the vector
+ if (len + offset > size)
+ {
+ throw std::out_of_range("len+offset out of range");
+ }
+ }
+
+ /*!
+ @brief create a JSON value from a given MessagePack vector
+
+ @param[in] v MessagePack serialization
+ @param[in] idx byte index to start reading from @a v
+
+ @return deserialized JSON value
+
+ @throw std::invalid_argument if unsupported features from MessagePack were
+ used in the given vector @a v or if the input is not valid MessagePack
+ @throw std::out_of_range if the given vector ends prematurely
+
+ @sa https://github.com/msgpack/msgpack/blob/master/spec.md
+ */
+ static basic_json from_msgpack_internal(const std::vector<uint8_t>& v, size_t& idx)
+ {
+ // make sure reading 1 byte is safe
+ check_length(v.size(), 1, idx);
+
+ // store and increment index
+ const size_t current_idx = idx++;
+
+ if (v[current_idx] <= 0xbf)
+ {
+ if (v[current_idx] <= 0x7f) // positive fixint
+ {
+ return v[current_idx];
+ }
+ else if (v[current_idx] <= 0x8f) // fixmap
+ {
+ basic_json result = value_t::object;
+ const size_t len = v[current_idx] & 0x0f;
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_msgpack_internal(v, idx);
+ result[key] = from_msgpack_internal(v, idx);
+ }
+ return result;
+ }
+ else if (v[current_idx] <= 0x9f) // fixarray
+ {
+ basic_json result = value_t::array;
+ const size_t len = v[current_idx] & 0x0f;
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_msgpack_internal(v, idx));
+ }
+ return result;
+ }
+ else // fixstr
+ {
+ const size_t len = v[current_idx] & 0x1f;
+ const size_t offset = current_idx + 1;
+ idx += len; // skip content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+ }
+ else if (v[current_idx] >= 0xe0) // negative fixint
+ {
+ return static_cast<int8_t>(v[current_idx]);
+ }
+ else
+ {
+ switch (v[current_idx])
+ {
+ case 0xc0: // nil
+ {
+ return value_t::null;
+ }
+
+ case 0xc2: // false
+ {
+ return false;
+ }
+
+ case 0xc3: // true
+ {
+ return true;
+ }
+
+ case 0xca: // float 32
+ {
+ // copy bytes in reverse order into the double variable
+ check_length(v.size(), sizeof(float), 1);
+ float res;
+ for (size_t byte = 0; byte < sizeof(float); ++byte)
+ {
+ reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
+ }
+ idx += sizeof(float); // skip content bytes
+ return res;
+ }
+
+ case 0xcb: // float 64
+ {
+ // copy bytes in reverse order into the double variable
+ check_length(v.size(), sizeof(double), 1);
+ double res;
+ for (size_t byte = 0; byte < sizeof(double); ++byte)
+ {
+ reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
+ }
+ idx += sizeof(double); // skip content bytes
+ return res;
+ }
+
+ case 0xcc: // uint 8
+ {
+ idx += 1; // skip content byte
+ return get_from_vector<uint8_t>(v, current_idx);
+ }
+
+ case 0xcd: // uint 16
+ {
+ idx += 2; // skip 2 content bytes
+ return get_from_vector<uint16_t>(v, current_idx);
+ }
+
+ case 0xce: // uint 32
+ {
+ idx += 4; // skip 4 content bytes
+ return get_from_vector<uint32_t>(v, current_idx);
+ }
+
+ case 0xcf: // uint 64
+ {
+ idx += 8; // skip 8 content bytes
+ return get_from_vector<uint64_t>(v, current_idx);
+ }
+
+ case 0xd0: // int 8
+ {
+ idx += 1; // skip content byte
+ return get_from_vector<int8_t>(v, current_idx);
+ }
+
+ case 0xd1: // int 16
+ {
+ idx += 2; // skip 2 content bytes
+ return get_from_vector<int16_t>(v, current_idx);
+ }
+
+ case 0xd2: // int 32
+ {
+ idx += 4; // skip 4 content bytes
+ return get_from_vector<int32_t>(v, current_idx);
+ }
+
+ case 0xd3: // int 64
+ {
+ idx += 8; // skip 8 content bytes
+ return get_from_vector<int64_t>(v, current_idx);
+ }
+
+ case 0xd9: // str 8
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
+ const size_t offset = current_idx + 2;
+ idx += len + 1; // skip size byte + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0xda: // str 16
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
+ const size_t offset = current_idx + 3;
+ idx += len + 2; // skip 2 size bytes + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0xdb: // str 32
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
+ const size_t offset = current_idx + 5;
+ idx += len + 4; // skip 4 size bytes + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0xdc: // array 16
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
+ idx += 2; // skip 2 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_msgpack_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0xdd: // array 32
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
+ idx += 4; // skip 4 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_msgpack_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0xde: // map 16
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
+ idx += 2; // skip 2 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_msgpack_internal(v, idx);
+ result[key] = from_msgpack_internal(v, idx);
+ }
+ return result;
+ }
+
+ case 0xdf: // map 32
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
+ idx += 4; // skip 4 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_msgpack_internal(v, idx);
+ result[key] = from_msgpack_internal(v, idx);
+ }
+ return result;
+ }
+
+ default:
+ {
+ throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
+ }
+ }
+ }
+ }
+
+ /*!
+ @brief create a JSON value from a given CBOR vector
+
+ @param[in] v CBOR serialization
+ @param[in] idx byte index to start reading from @a v
+
+ @return deserialized JSON value
+
+ @throw std::invalid_argument if unsupported features from CBOR were used in
+ the given vector @a v or if the input is not valid CBOR
+ @throw std::out_of_range if the given vector ends prematurely
+
+ @sa https://tools.ietf.org/html/rfc7049
+ */
+ static basic_json from_cbor_internal(const std::vector<uint8_t>& v, size_t& idx)
+ {
+ // store and increment index
+ const size_t current_idx = idx++;
+
+ switch (v.at(current_idx))
+ {
+ // Integer 0x00..0x17 (0..23)
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ {
+ return v[current_idx];
+ }
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ {
+ idx += 1; // skip content byte
+ return get_from_vector<uint8_t>(v, current_idx);
+ }
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ {
+ idx += 2; // skip 2 content bytes
+ return get_from_vector<uint16_t>(v, current_idx);
+ }
+
+ case 0x1a: // Unsigned integer (four-byte uint32_t follows)
+ {
+ idx += 4; // skip 4 content bytes
+ return get_from_vector<uint32_t>(v, current_idx);
+ }
+
+ case 0x1b: // Unsigned integer (eight-byte uint64_t follows)
+ {
+ idx += 8; // skip 8 content bytes
+ return get_from_vector<uint64_t>(v, current_idx);
+ }
+
+ // Negative integer -1-0x00..-1-0x17 (-1..-24)
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ case 0x2f:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ {
+ return static_cast<int8_t>(0x20 - 1 - v[current_idx]);
+ }
+
+ case 0x38: // Negative integer (one-byte uint8_t follows)
+ {
+ idx += 1; // skip content byte
+ // must be uint8_t !
+ return static_cast<number_integer_t>(-1) - get_from_vector<uint8_t>(v, current_idx);
+ }
+
+ case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ idx += 2; // skip 2 content bytes
+ return static_cast<number_integer_t>(-1) - get_from_vector<uint16_t>(v, current_idx);
+ }
+
+ case 0x3a: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ idx += 4; // skip 4 content bytes
+ return static_cast<number_integer_t>(-1) - get_from_vector<uint32_t>(v, current_idx);
+ }
+
+ case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ idx += 8; // skip 8 content bytes
+ return static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(get_from_vector<uint64_t>(v, current_idx));
+ }
+
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6a:
+ case 0x6b:
+ case 0x6c:
+ case 0x6d:
+ case 0x6e:
+ case 0x6f:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ const auto len = static_cast<size_t>(v[current_idx] - 0x60);
+ const size_t offset = current_idx + 1;
+ idx += len; // skip content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
+ const size_t offset = current_idx + 2;
+ idx += len + 1; // skip size byte + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
+ const size_t offset = current_idx + 3;
+ idx += len + 2; // skip 2 size bytes + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0x7a: // UTF-8 string (four-byte uint32_t for n follow)
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
+ const size_t offset = current_idx + 5;
+ idx += len + 4; // skip 4 size bytes + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow)
+ {
+ const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
+ const size_t offset = current_idx + 9;
+ idx += len + 8; // skip 8 size bytes + content bytes
+ check_length(v.size(), len, offset);
+ return std::string(reinterpret_cast<const char*>(v.data()) + offset, len);
+ }
+
+ case 0x7f: // UTF-8 string (indefinite length)
+ {
+ std::string result;
+ while (v.at(idx) != 0xff)
+ {
+ string_t s = from_cbor_internal(v, idx);
+ result += s;
+ }
+ // skip break byte (0xFF)
+ idx += 1;
+ return result;
+ }
+
+ // array (0x00..0x17 data items follow)
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8a:
+ case 0x8b:
+ case 0x8c:
+ case 0x8d:
+ case 0x8e:
+ case 0x8f:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(v[current_idx] - 0x80);
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_cbor_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0x98: // array (one-byte uint8_t for n follows)
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
+ idx += 1; // skip 1 size byte
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_cbor_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0x99: // array (two-byte uint16_t for n follow)
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
+ idx += 2; // skip 4 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_cbor_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0x9a: // array (four-byte uint32_t for n follow)
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
+ idx += 4; // skip 4 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_cbor_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0x9b: // array (eight-byte uint64_t for n follow)
+ {
+ basic_json result = value_t::array;
+ const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
+ idx += 8; // skip 8 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ result.push_back(from_cbor_internal(v, idx));
+ }
+ return result;
+ }
+
+ case 0x9f: // array (indefinite length)
+ {
+ basic_json result = value_t::array;
+ while (v.at(idx) != 0xff)
+ {
+ result.push_back(from_cbor_internal(v, idx));
+ }
+ // skip break byte (0xFF)
+ idx += 1;
+ return result;
+ }
+
+ // map (0x00..0x17 pairs of data items follow)
+ case 0xa0:
+ case 0xa1:
+ case 0xa2:
+ case 0xa3:
+ case 0xa4:
+ case 0xa5:
+ case 0xa6:
+ case 0xa7:
+ case 0xa8:
+ case 0xa9:
+ case 0xaa:
+ case 0xab:
+ case 0xac:
+ case 0xad:
+ case 0xae:
+ case 0xaf:
+ case 0xb0:
+ case 0xb1:
+ case 0xb2:
+ case 0xb3:
+ case 0xb4:
+ case 0xb5:
+ case 0xb6:
+ case 0xb7:
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(v[current_idx] - 0xa0);
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_cbor_internal(v, idx);
+ result[key] = from_cbor_internal(v, idx);
+ }
+ return result;
+ }
+
+ case 0xb8: // map (one-byte uint8_t for n follows)
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(get_from_vector<uint8_t>(v, current_idx));
+ idx += 1; // skip 1 size byte
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_cbor_internal(v, idx);
+ result[key] = from_cbor_internal(v, idx);
+ }
+ return result;
+ }
+
+ case 0xb9: // map (two-byte uint16_t for n follow)
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(get_from_vector<uint16_t>(v, current_idx));
+ idx += 2; // skip 2 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_cbor_internal(v, idx);
+ result[key] = from_cbor_internal(v, idx);
+ }
+ return result;
+ }
+
+ case 0xba: // map (four-byte uint32_t for n follow)
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(get_from_vector<uint32_t>(v, current_idx));
+ idx += 4; // skip 4 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_cbor_internal(v, idx);
+ result[key] = from_cbor_internal(v, idx);
+ }
+ return result;
+ }
+
+ case 0xbb: // map (eight-byte uint64_t for n follow)
+ {
+ basic_json result = value_t::object;
+ const auto len = static_cast<size_t>(get_from_vector<uint64_t>(v, current_idx));
+ idx += 8; // skip 8 size bytes
+ for (size_t i = 0; i < len; ++i)
+ {
+ std::string key = from_cbor_internal(v, idx);
+ result[key] = from_cbor_internal(v, idx);
+ }
+ return result;
+ }
+
+ case 0xbf: // map (indefinite length)
+ {
+ basic_json result = value_t::object;
+ while (v.at(idx) != 0xff)
+ {
+ std::string key = from_cbor_internal(v, idx);
+ result[key] = from_cbor_internal(v, idx);
+ }
+ // skip break byte (0xFF)
+ idx += 1;
+ return result;
+ }
+
+ case 0xf4: // false
+ {
+ return false;
+ }
+
+ case 0xf5: // true
+ {
+ return true;
+ }
+
+ case 0xf6: // null
+ {
+ return value_t::null;
+ }
+
+ case 0xf9: // Half-Precision Float (two-byte IEEE 754)
+ {
+ check_length(v.size(), 2, 1);
+ idx += 2; // skip two content bytes
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added to
+ // IEEE 754 in 2008, today's programming platforms often still
+ // only have limited support for them. It is very easy to
+ // include at least decoding support for them even without such
+ // support. An example of a small decoder for half-precision
+ // floating-point numbers in the C language is shown in Fig. 3.
+ const int half = (v[current_idx + 1] << 8) + v[current_idx + 2];
+ const int exp = (half >> 10) & 0x1f;
+ const int mant = half & 0x3ff;
+ double val;
+ if (exp == 0)
+ {
+ val = std::ldexp(mant, -24);
+ }
+ else if (exp != 31)
+ {
+ val = std::ldexp(mant + 1024, exp - 25);
+ }
+ else
+ {
+ val = mant == 0 ? INFINITY : NAN;
+ }
+ return half & 0x8000 ? -val : val;
+ }
+
+ case 0xfa: // Single-Precision Float (four-byte IEEE 754)
+ {
+ // copy bytes in reverse order into the float variable
+ check_length(v.size(), sizeof(float), 1);
+ float res;
+ for (size_t byte = 0; byte < sizeof(float); ++byte)
+ {
+ reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte];
+ }
+ idx += sizeof(float); // skip content bytes
+ return res;
+ }
+
+ case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ check_length(v.size(), sizeof(double), 1);
+ // copy bytes in reverse order into the double variable
+ double res;
+ for (size_t byte = 0; byte < sizeof(double); ++byte)
+ {
+ reinterpret_cast<uint8_t*>(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte];
+ }
+ idx += sizeof(double); // skip content bytes
+ return res;
+ }
+
+ default: // anything else (0xFF is handled inside the other types)
+ {
+ throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])));
+ }
+ }
+ }
+
+ public:
+ /*!
+ @brief create a MessagePack serialization of a given JSON value
+
+ Serializes a given JSON value @a j to a byte vector using the MessagePack
+ serialization format. MessagePack is a binary serialization format which
+ aims to be more compact than JSON itself, yet more efficient to parse.
+
+ @param[in] j JSON value to serialize
+ @return MessagePack 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 MessagePack format.,to_msgpack}
+
+ @sa http://msgpack.org
+ @sa @ref from_msgpack(const std::vector<uint8_t>&) for the analogous
+ deserialization
+ @sa @ref to_cbor(const basic_json& for the related CBOR format
+ */
+ static std::vector<uint8_t> to_msgpack(const basic_json& j)
+ {
+ std::vector<uint8_t> result;
+ to_msgpack_internal(j, result);
+ return result;
+ }
+
+ /*!
+ @brief create a JSON value from a byte vector in MessagePack format
+
+ Deserializes a given byte vector @a v to a JSON value using the MessagePack
+ serialization format.
+
+ @param[in] v a byte vector in MessagePack format
+ @return deserialized JSON value
+
+ @throw std::invalid_argument if unsupported features from MessagePack were
+ used in the given vector @a v or if the input is not valid MessagePack
+ @throw std::out_of_range if the given vector ends prematurely
+
+ @complexity Linear in the size of the byte vector @a v.
+
+ @liveexample{The example shows the deserialization of a byte vector in
+ MessagePack format to a JSON value.,from_msgpack}
+
+ @sa http://msgpack.org
+ @sa @ref to_msgpack(const basic_json&) for the analogous serialization
+ @sa @ref from_cbor(const std::vector<uint8_t>&) for the related CBOR format
+ */
+ static basic_json from_msgpack(const std::vector<uint8_t>& v)
+ {
+ size_t i = 0;
+ return from_msgpack_internal(v, i);
+ }
+
+ /*!
+ @brief create a MessagePack serialization of a given JSON value
+
+ Serializes a given JSON value @a j to a byte vector using the CBOR (Concise
+ Binary Object Representation) serialization format. CBOR is a binary
+ serialization format which aims to be more compact than JSON itself, yet
+ more efficient to parse.
+
+ @param[in] j JSON value to serialize
+ @return MessagePack 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 CBOR format.,to_cbor}
+
+ @sa http://cbor.io
+ @sa @ref from_cbor(const std::vector<uint8_t>&) for the analogous
+ deserialization
+ @sa @ref to_msgpack(const basic_json& for the related MessagePack format
+ */
+ static std::vector<uint8_t> to_cbor(const basic_json& j)
+ {
+ std::vector<uint8_t> result;
+ to_cbor_internal(j, result);
+ return result;
+ }
+
+ /*!
+ @brief create a JSON value from a byte vector in CBOR format
+
+ Deserializes a given byte vector @a v to a JSON value using the CBOR
+ (Concise Binary Object Representation) serialization format.
+
+ @param[in] v a byte vector in CBOR format
+ @return deserialized JSON value
+
+ @throw std::invalid_argument if unsupported features from CBOR were used in
+ the given vector @a v or if the input is not valid MessagePack
+ @throw std::out_of_range if the given vector ends prematurely
+
+ @complexity Linear in the size of the byte vector @a v.
+
+ @liveexample{The example shows the deserialization of a byte vector in CBOR
+ format to a JSON value.,from_cbor}
+
+ @sa http://cbor.io
+ @sa @ref to_cbor(const basic_json&) for the analogous serialization
+ @sa @ref from_msgpack(const std::vector<uint8_t>&) for the related
+ MessagePack format
+ */
+ static basic_json from_cbor(const std::vector<uint8_t>& v)
+ {
+ size_t i = 0;
+ return from_cbor_internal(v, i);
+ }
+
+ /// @}
private:
///////////////////////////
// convenience functions //
///////////////////////////
- /// return the type as string
- string_t type_name() const noexcept
+ /*!
+ @brief return the type as string
+
+ Returns the type name as string to be used in error messages - usually to
+ indicate that a function was called on a wrong JSON type.
+
+ @return basically a string representation of a the @a m_type member
+
+ @complexity Constant.
+
+ @since version 1.0.0
+ */
+ std::string type_name() const
{
switch (m_type)
{
@@ -5892,9 +7776,8 @@ class basic_json
*/
static std::size_t extra_space(const string_t& s) noexcept
{
- std::size_t result = 0;
-
- for (const auto& c : s)
+ return std::accumulate(s.begin(), s.end(), size_t{},
+ [](size_t res, typename string_t::value_type c)
{
switch (c)
{
@@ -5907,8 +7790,7 @@ class basic_json
case '\t':
{
// from c (1 byte) to \x (2 bytes)
- result += 1;
- break;
+ return res + 1;
}
default:
@@ -5916,14 +7798,15 @@ class basic_json
if (c >= 0x00 and c <= 0x1f)
{
// from c (1 byte) to \uxxxx (6 bytes)
- result += 5;
+ return res + 5;
+ }
+ else
+ {
+ return res;
}
- break;
}
}
- }
-
- return result;
+ });
}
/*!
@@ -6017,16 +7900,15 @@ class basic_json
{
// convert a number 0..15 to its hex representation
// (0..f)
- const auto hexify = [](const int v) -> char
+ static const char hexify[16] =
{
- return (v < 10)
- ? ('0' + static_cast<char>(v))
- : ('a' + static_cast<char>((v - 10) & 0x1f));
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
// print character c as \uxxxx
for (const char m :
- { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f)
+ { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
})
{
result[++pos] = m;
@@ -6076,8 +7958,6 @@ class basic_json
{
case value_t::object:
{
- assert(m_value.object != nullptr);
-
if (m_value.object->empty())
{
o << "{}";
@@ -6118,8 +7998,6 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
-
if (m_value.array->empty())
{
o << "[]";
@@ -6158,7 +8036,6 @@ class basic_json
case value_t::string:
{
- assert(m_value.string != nullptr);
o << string_t("\"") << escape_string(*m_value.string) << "\"";
return;
}
@@ -6183,79 +8060,14 @@ class basic_json
case value_t::number_float:
{
- // check if number was parsed from a string
- if (m_type.bits.parsed)
+ if (m_value.number_float == 0)
{
- // check if parsed number had an exponent given
- if (m_type.bits.has_exp)
- {
- // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
- char buf[263];
- int len;
-
- // handle capitalization of the exponent
- if (m_type.bits.exp_cap)
- {
- len = snprintf(buf, sizeof(buf), "%.*E",
- m_type.bits.precision, m_value.number_float) + 1;
- }
- else
- {
- len = snprintf(buf, sizeof(buf), "%.*e",
- m_type.bits.precision, m_value.number_float) + 1;
- }
-
- // remove '+' sign from the exponent if necessary
- if (not m_type.bits.exp_plus)
- {
- if (len > static_cast<int>(sizeof(buf)))
- {
- len = sizeof(buf);
- }
- for (int i = 0; i < len; i++)
- {
- if (buf[i] == '+')
- {
- for (; i + 1 < len; i++)
- {
- buf[i] = buf[i + 1];
- }
- }
- }
- }
-
- o << buf;
- }
- else
- {
- // no exponent - output as a decimal
- std::stringstream ss;
- ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
- ss << std::setprecision(m_type.bits.precision)
- << std::fixed << m_value.number_float;
- o << ss.str();
- }
+ // special case for zero to get "0.0"/"-0.0"
+ o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
}
else
{
- if (m_value.number_float == 0)
- {
- // special case for zero to get "0.0"/"-0.0"
- o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
- }
- else
- {
- // Otherwise 6, 15 or 16 digits of precision allows
- // round-trip IEEE 754 string->float->string,
- // string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- std::stringstream ss;
- ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
- ss << std::setprecision(std::numeric_limits<double>::digits10)
- << m_value.number_float;
- o << ss.str();
- }
+ o << m_value.number_float;
}
return;
}
@@ -6280,7 +8092,7 @@ class basic_json
//////////////////////
/// the type of the current element
- type_data_t m_type = value_t::null;
+ value_t m_type = value_t::null;
/// the value of the current element
json_value m_value = {};
@@ -6467,40 +8279,61 @@ class basic_json
public:
/*!
- @brief a const random access iterator for the @ref basic_json class
+ @brief a template for a random access iterator for the @ref basic_json class
+
+ This class implements a both iterators (iterator and const_iterator) for the
+ @ref basic_json class.
- This class implements a const iterator for the @ref basic_json class. From
- this class, the @ref iterator class is derived.
+ @note An iterator is called *initialized* when a pointer to a JSON value
+ has been set (e.g., by a constructor or a copy assignment). If the
+ iterator is default-constructed, it is *uninitialized* and most
+ methods are undefined. **The library uses assertions to detect calls
+ on uninitialized iterators.**
@requirement The class satisfies the following concept requirements:
- [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
The iterator that can be moved to point (forward and backward) to any
element in constant time.
- @since version 1.0.0
+ @since version 1.0.0, simplified in version 2.0.9
*/
- class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
+ template<typename U>
+ class iter_impl : public std::iterator<std::random_access_iterator_tag, U>
{
/// allow basic_json to access private members
friend class basic_json;
+ // make sure U is basic_json or const basic_json
+ static_assert(std::is_same<U, basic_json>::value
+ or std::is_same<U, const basic_json>::value,
+ "iter_impl only accepts (const) basic_json");
+
public:
/// the type of the values when the iterator is dereferenced
using value_type = typename basic_json::value_type;
/// a type to represent differences between iterators
using difference_type = typename basic_json::difference_type;
/// defines a pointer to the type iterated over (value_type)
- using pointer = typename basic_json::const_pointer;
+ using pointer = typename std::conditional<std::is_const<U>::value,
+ typename basic_json::const_pointer,
+ typename basic_json::pointer>::type;
/// defines a reference to the type iterated over (value_type)
- using reference = typename basic_json::const_reference;
+ using reference = typename std::conditional<std::is_const<U>::value,
+ typename basic_json::const_reference,
+ typename basic_json::reference>::type;
/// the category of the iterator
using iterator_category = std::bidirectional_iterator_tag;
/// default constructor
- const_iterator() = default;
+ iter_impl() = default;
- /// constructor for a given JSON instance
- explicit const_iterator(pointer object) noexcept
+ /*!
+ @brief constructor for a given JSON instance
+ @param[in] object pointer to a JSON object for this iterator
+ @pre object != nullptr
+ @post The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ explicit iter_impl(pointer object) noexcept
: m_object(object)
{
assert(m_object != nullptr);
@@ -6527,41 +8360,42 @@ class basic_json
}
}
- /// copy constructor given a nonconst iterator
- explicit const_iterator(const iterator& other) noexcept
- : m_object(other.m_object)
+ /*
+ Use operator `const_iterator` instead of `const_iterator(const iterator&
+ other) noexcept` to avoid two class definitions for @ref iterator and
+ @ref const_iterator.
+
+ This function is only called if this class is an @ref iterator. If this
+ class is a @ref const_iterator this function is not called.
+ */
+ operator const_iterator() const
{
- assert(m_object != nullptr);
+ const_iterator ret;
- switch (m_object->m_type)
+ if (m_object)
{
- case basic_json::value_t::object:
- {
- m_it.object_iterator = other.m_it.object_iterator;
- break;
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator = other.m_it.array_iterator;
- break;
- }
-
- default:
- {
- m_it.primitive_iterator = other.m_it.primitive_iterator;
- break;
- }
+ ret.m_object = m_object;
+ ret.m_it = m_it;
}
+
+ return ret;
}
- /// copy constructor
- const_iterator(const const_iterator& other) noexcept
+ /*!
+ @brief copy constructor
+ @param[in] other iterator to copy from
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl(const iter_impl& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}
- /// copy assignment
- const_iterator& operator=(const_iterator other) noexcept(
+ /*!
+ @brief copy assignment
+ @param[in,out] other iterator to copy from
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(iter_impl other) noexcept(
std::is_nothrow_move_constructible<pointer>::value and
std::is_nothrow_move_assignable<pointer>::value and
std::is_nothrow_move_constructible<internal_iterator>::value and
@@ -6574,7 +8408,10 @@ class basic_json
}
private:
- /// set the iterator to the first value
+ /*!
+ @brief set the iterator to the first value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
void set_begin() noexcept
{
assert(m_object != nullptr);
@@ -6583,14 +8420,12 @@ class basic_json
{
case basic_json::value_t::object:
{
- assert(m_object->m_value.object != nullptr);
m_it.object_iterator = m_object->m_value.object->begin();
break;
}
case basic_json::value_t::array:
{
- assert(m_object->m_value.array != nullptr);
m_it.array_iterator = m_object->m_value.array->begin();
break;
}
@@ -6610,7 +8445,10 @@ class basic_json
}
}
- /// set the iterator past the last value
+ /*!
+ @brief set the iterator past the last value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
void set_end() noexcept
{
assert(m_object != nullptr);
@@ -6619,14 +8457,12 @@ class basic_json
{
case basic_json::value_t::object:
{
- assert(m_object->m_value.object != nullptr);
m_it.object_iterator = m_object->m_value.object->end();
break;
}
case basic_json::value_t::array:
{
- assert(m_object->m_value.array != nullptr);
m_it.array_iterator = m_object->m_value.array->end();
break;
}
@@ -6640,7 +8476,10 @@ class basic_json
}
public:
- /// return a reference to the value pointed to by the iterator
+ /*!
+ @brief return a reference to the value pointed to by the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
reference operator*() const
{
assert(m_object != nullptr);
@@ -6649,14 +8488,12 @@ class basic_json
{
case basic_json::value_t::object:
{
- assert(m_object->m_value.object);
assert(m_it.object_iterator != m_object->m_value.object->end());
return m_it.object_iterator->second;
}
case basic_json::value_t::array:
{
- assert(m_object->m_value.array);
assert(m_it.array_iterator != m_object->m_value.array->end());
return *m_it.array_iterator;
}
@@ -6680,7 +8517,10 @@ class basic_json
}
}
- /// dereference the iterator
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
pointer operator->() const
{
assert(m_object != nullptr);
@@ -6689,14 +8529,12 @@ class basic_json
{
case basic_json::value_t::object:
{
- assert(m_object->m_value.object);
assert(m_it.object_iterator != m_object->m_value.object->end());
return &(m_it.object_iterator->second);
}
case basic_json::value_t::array:
{
- assert(m_object->m_value.array);
assert(m_it.array_iterator != m_object->m_value.array->end());
return &*m_it.array_iterator;
}
@@ -6715,16 +8553,22 @@ class basic_json
}
}
- /// post-increment (it++)
- const_iterator operator++(int)
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator++(int)
{
auto result = *this;
++(*this);
return result;
}
- /// pre-increment (++it)
- const_iterator& operator++()
+ /*!
+ @brief pre-increment (++it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator++()
{
assert(m_object != nullptr);
@@ -6732,13 +8576,13 @@ class basic_json
{
case basic_json::value_t::object:
{
- ++m_it.object_iterator;
+ std::advance(m_it.object_iterator, 1);
break;
}
case basic_json::value_t::array:
{
- ++m_it.array_iterator;
+ std::advance(m_it.array_iterator, 1);
break;
}
@@ -6752,16 +8596,22 @@ class basic_json
return *this;
}
- /// post-decrement (it--)
- const_iterator operator--(int)
+ /*!
+ @brief post-decrement (it--)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator--(int)
{
auto result = *this;
--(*this);
return result;
}
- /// pre-decrement (--it)
- const_iterator& operator--()
+ /*!
+ @brief pre-decrement (--it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator--()
{
assert(m_object != nullptr);
@@ -6769,13 +8619,13 @@ class basic_json
{
case basic_json::value_t::object:
{
- --m_it.object_iterator;
+ std::advance(m_it.object_iterator, -1);
break;
}
case basic_json::value_t::array:
{
- --m_it.array_iterator;
+ std::advance(m_it.array_iterator, -1);
break;
}
@@ -6789,8 +8639,11 @@ class basic_json
return *this;
}
- /// comparison: equal
- bool operator==(const const_iterator& other) const
+ /*!
+ @brief comparison: equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator==(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
@@ -6819,14 +8672,20 @@ class basic_json
}
}
- /// comparison: not equal
- bool operator!=(const const_iterator& other) const
+ /*!
+ @brief comparison: not equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator!=(const iter_impl& other) const
{
return not operator==(other);
}
- /// comparison: smaller
- bool operator<(const const_iterator& other) const
+ /*!
+ @brief comparison: smaller
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (m_object != other.m_object)
@@ -6855,26 +8714,38 @@ class basic_json
}
}
- /// comparison: less than or equal
- bool operator<=(const const_iterator& other) const
+ /*!
+ @brief comparison: less than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<=(const iter_impl& other) const
{
return not other.operator < (*this);
}
- /// comparison: greater than
- bool operator>(const const_iterator& other) const
+ /*!
+ @brief comparison: greater than
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>(const iter_impl& other) const
{
return not operator<=(other);
}
- /// comparison: greater than or equal
- bool operator>=(const const_iterator& other) const
+ /*!
+ @brief comparison: greater than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>=(const iter_impl& other) const
{
return not operator<(other);
}
- /// add to iterator
- const_iterator& operator+=(difference_type i)
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator+=(difference_type i)
{
assert(m_object != nullptr);
@@ -6887,7 +8758,7 @@ class basic_json
case basic_json::value_t::array:
{
- m_it.array_iterator += i;
+ std::advance(m_it.array_iterator, i);
break;
}
@@ -6901,30 +8772,42 @@ class basic_json
return *this;
}
- /// subtract from iterator
- const_iterator& operator-=(difference_type i)
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator-=(difference_type i)
{
return operator+=(-i);
}
- /// add to iterator
- const_iterator operator+(difference_type i)
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator+(difference_type i)
{
auto result = *this;
result += i;
return result;
}
- /// subtract from iterator
- const_iterator operator-(difference_type i)
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator-(difference_type i)
{
auto result = *this;
result -= i;
return result;
}
- /// return difference
- difference_type operator-(const const_iterator& other) const
+ /*!
+ @brief return difference
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ difference_type operator-(const iter_impl& other) const
{
assert(m_object != nullptr);
@@ -6947,7 +8830,10 @@ class basic_json
}
}
- /// access to successor
+ /*!
+ @brief access to successor
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
reference operator[](difference_type n) const
{
assert(m_object != nullptr);
@@ -6961,7 +8847,7 @@ class basic_json
case basic_json::value_t::array:
{
- return *(m_it.array_iterator + n);
+ return *std::next(m_it.array_iterator, n);
}
case basic_json::value_t::null:
@@ -6983,7 +8869,10 @@ class basic_json
}
}
- /// return the key of an object iterator
+ /*!
+ @brief return the key of an object iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
typename object_t::key_type key() const
{
assert(m_object != nullptr);
@@ -6998,7 +8887,10 @@ class basic_json
}
}
- /// return the value of an iterator
+ /*!
+ @brief return the value of an iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
reference value() const
{
return operator*();
@@ -7012,141 +8904,6 @@ class basic_json
};
/*!
- @brief a mutable random access iterator for the @ref basic_json class
-
- @requirement The class satisfies the following concept requirements:
- - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
- The iterator that can be moved to point (forward and backward) to any
- element in constant time.
- - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
- It is possible to write to the pointed-to element.
-
- @since version 1.0.0
- */
- class iterator : public const_iterator
- {
- public:
- using base_iterator = const_iterator;
- using pointer = typename basic_json::pointer;
- using reference = typename basic_json::reference;
-
- /// default constructor
- iterator() = default;
-
- /// constructor for a given JSON instance
- explicit iterator(pointer object) noexcept
- : base_iterator(object)
- {}
-
- /// copy constructor
- iterator(const iterator& other) noexcept
- : base_iterator(other)
- {}
-
- /// copy assignment
- iterator& operator=(iterator other) noexcept(
- std::is_nothrow_move_constructible<pointer>::value and
- std::is_nothrow_move_assignable<pointer>::value and
- std::is_nothrow_move_constructible<internal_iterator>::value and
- std::is_nothrow_move_assignable<internal_iterator>::value
- )
- {
- base_iterator::operator=(other);
- return *this;
- }
-
- /// return a reference to the value pointed to by the iterator
- reference operator*() const
- {
- return const_cast<reference>(base_iterator::operator*());
- }
-
- /// dereference the iterator
- pointer operator->() const
- {
- return const_cast<pointer>(base_iterator::operator->());
- }
-
- /// post-increment (it++)
- iterator operator++(int)
- {
- iterator result = *this;
- base_iterator::operator++();
- return result;
- }
-
- /// pre-increment (++it)
- iterator& operator++()
- {
- base_iterator::operator++();
- return *this;
- }
-
- /// post-decrement (it--)
- iterator operator--(int)
- {
- iterator result = *this;
- base_iterator::operator--();
- return result;
- }
-
- /// pre-decrement (--it)
- iterator& operator--()
- {
- base_iterator::operator--();
- return *this;
- }
-
- /// add to iterator
- iterator& operator+=(difference_type i)
- {
- base_iterator::operator+=(i);
- return *this;
- }
-
- /// subtract from iterator
- iterator& operator-=(difference_type i)
- {
- base_iterator::operator-=(i);
- return *this;
- }
-
- /// add to iterator
- iterator operator+(difference_type i)
- {
- auto result = *this;
- result += i;
- return result;
- }
-
- /// subtract from iterator
- iterator operator-(difference_type i)
- {
- auto result = *this;
- result -= i;
- return result;
- }
-
- /// return difference
- difference_type operator-(const iterator& other) const
- {
- return base_iterator::operator-(other);
- }
-
- /// access to successor
- reference operator[](difference_type n) const
- {
- return const_cast<reference>(base_iterator::operator[](n));
- }
-
- /// return the value of an iterator
- reference value() const
- {
- return const_cast<reference>(base_iterator::value());
- }
- };
-
- /*!
@brief a template for a reverse iterator class
@tparam Base the base iterator type to reverse. Valid types are @ref
@@ -7296,54 +9053,69 @@ class basic_json
/// the char type to use in the lexer
using lexer_char_t = unsigned char;
- /// constructor with a given buffer
- explicit lexer(const string_t& s) noexcept
- : m_stream(nullptr), m_buffer(s)
+ /// a lexer from a buffer with given length
+ lexer(const lexer_char_t* buff, const size_t len) noexcept
+ : m_content(buff)
{
- m_content = reinterpret_cast<const lexer_char_t*>(s.c_str());
assert(m_content != nullptr);
m_start = m_cursor = m_content;
- m_limit = m_content + s.size();
+ m_limit = m_content + len;
}
- /// constructor with a given stream
- explicit lexer(std::istream* s) noexcept
- : m_stream(s), m_buffer()
+ /// a lexer from an input stream
+ explicit lexer(std::istream& s)
+ : m_stream(&s), m_line_buffer()
{
- assert(m_stream != nullptr);
- getline(*m_stream, m_buffer);
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
- assert(m_content != nullptr);
- m_start = m_cursor = m_content;
- m_limit = m_content + m_buffer.size();
- }
+ // immediately abort if stream is erroneous
+ if (s.fail())
+ {
+ throw std::invalid_argument("stream error");
+ }
- /// default constructor
- lexer() = default;
+ // fill buffer
+ fill_line_buffer();
- // switch off unwanted functions
+ // skip UTF-8 byte-order mark
+ if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF")
+ {
+ m_line_buffer[0] = ' ';
+ m_line_buffer[1] = ' ';
+ m_line_buffer[2] = ' ';
+ }
+ }
+
+ // switch off unwanted functions (due to pointer members)
+ lexer() = delete;
lexer(const lexer&) = delete;
lexer operator=(const lexer&) = delete;
/*!
- @brief create a string from a Unicode code point
+ @brief create a string from one or two Unicode code points
+
+ There are two cases: (1) @a codepoint1 is in the Basic Multilingual
+ Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2)
+ @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to
+ represent a code point above U+FFFF.
@param[in] codepoint1 the code point (can be high surrogate)
@param[in] codepoint2 the code point (can be low surrogate or 0)
- @return string representation of the code point
+ @return string representation of the code point; the length of the
+ result string is between 1 and 4 characters.
@throw std::out_of_range if code point is > 0x10ffff; example: `"code
points above 0x10FFFF are invalid"`
@throw std::invalid_argument if the low surrogate is invalid; example:
`""missing or wrong low surrogate""`
+ @complexity Constant.
+
@see <http://en.wikipedia.org/wiki/UTF-8#Sample_code>
*/
static string_t to_unicode(const std::size_t codepoint1,
const std::size_t codepoint2 = 0)
{
- // calculate the codepoint from the given code points
+ // calculate the code point from the given code points
std::size_t codepoint = codepoint1;
// check if codepoint1 is a high surrogate
@@ -7405,7 +9177,7 @@ class basic_json
}
/// return name of values of type token_type (only used for errors)
- static std::string token_type_name(token_type t)
+ static std::string token_type_name(const token_type t)
{
switch (t)
{
@@ -7454,799 +9226,1035 @@ class basic_json
function consists of a large block of code with `goto` jumps.
@return the class of the next token read from the buffer
+
+ @complexity Linear in the length of the input.\n
+
+ Proposition: The loop below will always terminate for finite input.\n
+
+ Proof (by contradiction): Assume a finite input. To loop forever, the
+ loop must never hit code with a `break` statement. The only code
+ snippets without a `break` statement are the continue statements for
+ whitespace and byte-order-marks. To loop forever, the input must be an
+ infinite sequence of whitespace or byte-order-marks. This contradicts
+ the assumption of finite input, q.e.d.
*/
- token_type scan() noexcept
+ token_type scan()
{
- // pointer for backtracking information
- m_marker = nullptr;
+ while (true)
+ {
+ // pointer for backtracking information
+ m_marker = nullptr;
- // remember the begin of the token
- m_start = m_cursor;
- assert(m_start != nullptr);
+ // remember the begin of the token
+ m_start = m_cursor;
+ assert(m_start != nullptr);
- {
- lexer_char_t yych;
- unsigned int yyaccept = 0;
- static const unsigned char yybm[] =
- {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 32, 32, 0, 0, 32, 0, 0,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 160, 128, 0, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 0, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- };
- if ((m_limit - m_cursor) < 5)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 32)
- {
- goto basic_json_parser_6;
- }
- if (yych <= '\\')
- {
- if (yych <= '-')
- {
- if (yych <= '"')
- {
- if (yych <= 0x00)
- {
- goto basic_json_parser_2;
- }
- if (yych <= '!')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_9;
- }
- else
- {
- if (yych <= '+')
- {
- goto basic_json_parser_4;
- }
- if (yych <= ',')
- {
- goto basic_json_parser_10;
- }
- goto basic_json_parser_12;
- }
+ {
+ lexer_char_t yych;
+ unsigned int yyaccept = 0;
+ static const unsigned char yybm[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 32, 32, 0, 0, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 160, 128, 0, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 0, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ if ((m_limit - m_cursor) < 5)
+ {
+ fill_line_buffer(5); // LCOV_EXCL_LINE
}
- else
+ yych = *m_cursor;
+ if (yybm[0 + yych] & 32)
{
- if (yych <= '9')
+ goto basic_json_parser_6;
+ }
+ if (yych <= '[')
+ {
+ if (yych <= '-')
{
- if (yych <= '/')
+ if (yych <= '"')
{
- goto basic_json_parser_4;
+ if (yych <= 0x00)
+ {
+ goto basic_json_parser_2;
+ }
+ if (yych <= '!')
+ {
+ goto basic_json_parser_4;
+ }
+ goto basic_json_parser_9;
}
- if (yych <= '0')
+ else
{
- goto basic_json_parser_13;
+ if (yych <= '+')
+ {
+ goto basic_json_parser_4;
+ }
+ if (yych <= ',')
+ {
+ goto basic_json_parser_10;
+ }
+ goto basic_json_parser_12;
}
- goto basic_json_parser_15;
}
else
{
- if (yych <= ':')
+ if (yych <= '9')
{
- goto basic_json_parser_17;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_4;
+ }
+ if (yych <= '0')
+ {
+ goto basic_json_parser_13;
+ }
+ goto basic_json_parser_15;
}
- if (yych == '[')
+ else
{
+ if (yych <= ':')
+ {
+ goto basic_json_parser_17;
+ }
+ if (yych <= 'Z')
+ {
+ goto basic_json_parser_4;
+ }
goto basic_json_parser_19;
}
- goto basic_json_parser_4;
}
}
- }
- else
- {
- if (yych <= 't')
+ else
{
- if (yych <= 'f')
+ if (yych <= 'n')
{
- if (yych <= ']')
- {
- goto basic_json_parser_21;
- }
if (yych <= 'e')
{
+ if (yych == ']')
+ {
+ goto basic_json_parser_21;
+ }
goto basic_json_parser_4;
}
- goto basic_json_parser_23;
- }
- else
- {
- if (yych == 'n')
+ else
{
+ if (yych <= 'f')
+ {
+ goto basic_json_parser_23;
+ }
+ if (yych <= 'm')
+ {
+ goto basic_json_parser_4;
+ }
goto basic_json_parser_24;
}
- if (yych <= 's')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_25;
- }
- }
- else
- {
- if (yych <= '|')
- {
- if (yych == '{')
- {
- goto basic_json_parser_26;
- }
- goto basic_json_parser_4;
}
else
{
- if (yych <= '}')
+ if (yych <= 'z')
{
- goto basic_json_parser_28;
+ if (yych == 't')
+ {
+ goto basic_json_parser_25;
+ }
+ goto basic_json_parser_4;
}
- if (yych == 0xEF)
+ else
{
- goto basic_json_parser_30;
+ if (yych <= '{')
+ {
+ goto basic_json_parser_26;
+ }
+ if (yych == '}')
+ {
+ goto basic_json_parser_28;
+ }
+ goto basic_json_parser_4;
}
- goto basic_json_parser_4;
}
}
- }
basic_json_parser_2:
- ++m_cursor;
- {
- return token_type::end_of_input;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::end_of_input;
+ break;
+ }
basic_json_parser_4:
- ++m_cursor;
+ ++m_cursor;
basic_json_parser_5:
- {
- return token_type::parse_error;
- }
+ {
+ last_token_type = token_type::parse_error;
+ break;
+ }
basic_json_parser_6:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 32)
- {
- goto basic_json_parser_6;
- }
- {
- return scan();
- }
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yybm[0 + yych] & 32)
+ {
+ goto basic_json_parser_6;
+ }
+ {
+ continue;
+ }
basic_json_parser_9:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych <= 0x0F)
- {
+ yyaccept = 0;
+ yych = *(m_marker = ++m_cursor);
+ if (yych <= 0x1F)
+ {
+ goto basic_json_parser_5;
+ }
+ if (yych <= 0x7F)
+ {
+ goto basic_json_parser_31;
+ }
+ if (yych <= 0xC1)
+ {
+ goto basic_json_parser_5;
+ }
+ if (yych <= 0xF4)
+ {
+ goto basic_json_parser_31;
+ }
goto basic_json_parser_5;
- }
- goto basic_json_parser_32;
basic_json_parser_10:
- ++m_cursor;
- {
- return token_type::value_separator;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::value_separator;
+ break;
+ }
basic_json_parser_12:
- yych = *++m_cursor;
- if (yych <= '/')
- {
+ yych = *++m_cursor;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_5;
+ }
+ if (yych <= '0')
+ {
+ goto basic_json_parser_13;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_15;
+ }
goto basic_json_parser_5;
- }
- if (yych <= '0')
- {
- goto basic_json_parser_13;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_15;
- }
- goto basic_json_parser_5;
basic_json_parser_13:
- yyaccept = 1;
- yych = *(m_marker = ++m_cursor);
- if (yych <= 'D')
- {
- if (yych == '.')
+ yyaccept = 1;
+ yych = *(m_marker = ++m_cursor);
+ if (yych <= 'D')
{
- goto basic_json_parser_37;
+ if (yych == '.')
+ {
+ goto basic_json_parser_43;
+ }
}
- }
- else
- {
- if (yych <= 'E')
+ else
{
- goto basic_json_parser_38;
+ if (yych <= 'E')
+ {
+ goto basic_json_parser_44;
+ }
+ if (yych == 'e')
+ {
+ goto basic_json_parser_44;
+ }
}
- if (yych == 'e')
+basic_json_parser_14:
{
- goto basic_json_parser_38;
+ last_token_type = token_type::value_number;
+ break;
}
- }
-basic_json_parser_14:
- {
- return token_type::value_number;
- }
basic_json_parser_15:
- yyaccept = 1;
- m_marker = ++m_cursor;
- if ((m_limit - m_cursor) < 3)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 64)
- {
- goto basic_json_parser_15;
- }
- if (yych <= 'D')
- {
- if (yych == '.')
+ yyaccept = 1;
+ m_marker = ++m_cursor;
+ if ((m_limit - m_cursor) < 3)
{
- goto basic_json_parser_37;
+ fill_line_buffer(3); // LCOV_EXCL_LINE
}
- goto basic_json_parser_14;
- }
- else
- {
- if (yych <= 'E')
+ yych = *m_cursor;
+ if (yybm[0 + yych] & 64)
{
- goto basic_json_parser_38;
+ goto basic_json_parser_15;
}
- if (yych == 'e')
+ if (yych <= 'D')
{
- goto basic_json_parser_38;
+ if (yych == '.')
+ {
+ goto basic_json_parser_43;
+ }
+ goto basic_json_parser_14;
+ }
+ else
+ {
+ if (yych <= 'E')
+ {
+ goto basic_json_parser_44;
+ }
+ if (yych == 'e')
+ {
+ goto basic_json_parser_44;
+ }
+ goto basic_json_parser_14;
}
- goto basic_json_parser_14;
- }
basic_json_parser_17:
- ++m_cursor;
- {
- return token_type::name_separator;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::name_separator;
+ break;
+ }
basic_json_parser_19:
- ++m_cursor;
- {
- return token_type::begin_array;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::begin_array;
+ break;
+ }
basic_json_parser_21:
- ++m_cursor;
- {
- return token_type::end_array;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::end_array;
+ break;
+ }
basic_json_parser_23:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'a')
- {
- goto basic_json_parser_39;
- }
- goto basic_json_parser_5;
+ yyaccept = 0;
+ yych = *(m_marker = ++m_cursor);
+ if (yych == 'a')
+ {
+ goto basic_json_parser_45;
+ }
+ goto basic_json_parser_5;
basic_json_parser_24:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'u')
- {
- goto basic_json_parser_40;
- }
- goto basic_json_parser_5;
+ yyaccept = 0;
+ yych = *(m_marker = ++m_cursor);
+ if (yych == 'u')
+ {
+ goto basic_json_parser_46;
+ }
+ goto basic_json_parser_5;
basic_json_parser_25:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'r')
- {
- goto basic_json_parser_41;
- }
- goto basic_json_parser_5;
+ yyaccept = 0;
+ yych = *(m_marker = ++m_cursor);
+ if (yych == 'r')
+ {
+ goto basic_json_parser_47;
+ }
+ goto basic_json_parser_5;
basic_json_parser_26:
- ++m_cursor;
- {
- return token_type::begin_object;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::begin_object;
+ break;
+ }
basic_json_parser_28:
- ++m_cursor;
- {
- return token_type::end_object;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::end_object;
+ break;
+ }
basic_json_parser_30:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 0xBB)
- {
- goto basic_json_parser_42;
- }
- goto basic_json_parser_5;
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
basic_json_parser_31:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
-basic_json_parser_32:
- if (yybm[0 + yych] & 128)
- {
- goto basic_json_parser_31;
- }
- if (yych <= 0x0F)
- {
- goto basic_json_parser_33;
- }
- if (yych <= '"')
- {
- goto basic_json_parser_34;
- }
- goto basic_json_parser_36;
-basic_json_parser_33:
- m_cursor = m_marker;
- if (yyaccept == 0)
- {
- goto basic_json_parser_5;
- }
- else
- {
- goto basic_json_parser_14;
- }
-basic_json_parser_34:
- ++m_cursor;
- {
- return token_type::value_string;
- }
-basic_json_parser_36:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= 'e')
- {
- if (yych <= '/')
+ if (yybm[0 + yych] & 128)
+ {
+ goto basic_json_parser_30;
+ }
+ if (yych <= 0xE0)
{
- if (yych == '"')
+ if (yych <= '\\')
{
- goto basic_json_parser_31;
+ if (yych <= 0x1F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '"')
+ {
+ goto basic_json_parser_33;
+ }
+ goto basic_json_parser_35;
}
- if (yych <= '.')
+ else
{
- goto basic_json_parser_33;
+ if (yych <= 0xC1)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0xDF)
+ {
+ goto basic_json_parser_36;
+ }
+ goto basic_json_parser_37;
}
- goto basic_json_parser_31;
}
else
{
- if (yych <= '\\')
+ if (yych <= 0xEF)
{
- if (yych <= '[')
+ if (yych == 0xED)
{
- goto basic_json_parser_33;
+ goto basic_json_parser_39;
}
- goto basic_json_parser_31;
+ goto basic_json_parser_38;
}
else
{
- if (yych == 'b')
+ if (yych <= 0xF0)
+ {
+ goto basic_json_parser_40;
+ }
+ if (yych <= 0xF3)
{
- goto basic_json_parser_31;
+ goto basic_json_parser_41;
+ }
+ if (yych <= 0xF4)
+ {
+ goto basic_json_parser_42;
}
- goto basic_json_parser_33;
}
}
- }
- else
- {
- if (yych <= 'q')
+basic_json_parser_32:
+ m_cursor = m_marker;
+ if (yyaccept == 0)
{
- if (yych <= 'f')
+ goto basic_json_parser_5;
+ }
+ else
+ {
+ goto basic_json_parser_14;
+ }
+basic_json_parser_33:
+ ++m_cursor;
+ {
+ last_token_type = token_type::value_string;
+ break;
+ }
+basic_json_parser_35:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 'e')
+ {
+ if (yych <= '/')
{
- goto basic_json_parser_31;
+ if (yych == '"')
+ {
+ goto basic_json_parser_30;
+ }
+ if (yych <= '.')
+ {
+ goto basic_json_parser_32;
+ }
+ goto basic_json_parser_30;
}
- if (yych == 'n')
+ else
{
- goto basic_json_parser_31;
+ if (yych <= '\\')
+ {
+ if (yych <= '[')
+ {
+ goto basic_json_parser_32;
+ }
+ goto basic_json_parser_30;
+ }
+ else
+ {
+ if (yych == 'b')
+ {
+ goto basic_json_parser_30;
+ }
+ goto basic_json_parser_32;
+ }
}
- goto basic_json_parser_33;
}
else
{
- if (yych <= 's')
+ if (yych <= 'q')
{
- if (yych <= 'r')
+ if (yych <= 'f')
{
- goto basic_json_parser_31;
+ goto basic_json_parser_30;
}
- goto basic_json_parser_33;
+ if (yych == 'n')
+ {
+ goto basic_json_parser_30;
+ }
+ goto basic_json_parser_32;
}
else
{
- if (yych <= 't')
+ if (yych <= 's')
{
- goto basic_json_parser_31;
+ if (yych <= 'r')
+ {
+ goto basic_json_parser_30;
+ }
+ goto basic_json_parser_32;
}
- if (yych <= 'u')
+ else
{
- goto basic_json_parser_43;
+ if (yych <= 't')
+ {
+ goto basic_json_parser_30;
+ }
+ if (yych <= 'u')
+ {
+ goto basic_json_parser_48;
+ }
+ goto basic_json_parser_32;
}
- goto basic_json_parser_33;
}
}
- }
+basic_json_parser_36:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 0x7F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0xBF)
+ {
+ goto basic_json_parser_30;
+ }
+ goto basic_json_parser_32;
basic_json_parser_37:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_44;
- }
- goto basic_json_parser_33;
-basic_json_parser_38:
- yych = *++m_cursor;
- if (yych <= ',')
- {
- if (yych == '+')
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_46;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= '-')
+ yych = *m_cursor;
+ if (yych <= 0x9F)
{
- goto basic_json_parser_46;
+ goto basic_json_parser_32;
}
- if (yych <= '/')
+ if (yych <= 0xBF)
{
- goto basic_json_parser_33;
+ goto basic_json_parser_36;
}
- if (yych <= '9')
+ goto basic_json_parser_32;
+basic_json_parser_38:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_47;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- goto basic_json_parser_33;
- }
+ yych = *m_cursor;
+ if (yych <= 0x7F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0xBF)
+ {
+ goto basic_json_parser_36;
+ }
+ goto basic_json_parser_32;
basic_json_parser_39:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_49;
- }
- goto basic_json_parser_33;
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 0x7F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0x9F)
+ {
+ goto basic_json_parser_36;
+ }
+ goto basic_json_parser_32;
basic_json_parser_40:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_50;
- }
- goto basic_json_parser_33;
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 0x8F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0xBF)
+ {
+ goto basic_json_parser_38;
+ }
+ goto basic_json_parser_32;
basic_json_parser_41:
- yych = *++m_cursor;
- if (yych == 'u')
- {
- goto basic_json_parser_51;
- }
- goto basic_json_parser_33;
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 0x7F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0xBF)
+ {
+ goto basic_json_parser_38;
+ }
+ goto basic_json_parser_32;
basic_json_parser_42:
- yych = *++m_cursor;
- if (yych == 0xBF)
- {
- goto basic_json_parser_52;
- }
- goto basic_json_parser_33;
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 0x7F)
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 0x8F)
+ {
+ goto basic_json_parser_38;
+ }
+ goto basic_json_parser_32;
basic_json_parser_43:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
+ yych = *++m_cursor;
if (yych <= '/')
{
- goto basic_json_parser_33;
+ goto basic_json_parser_32;
}
if (yych <= '9')
{
- goto basic_json_parser_54;
+ goto basic_json_parser_49;
}
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
+ goto basic_json_parser_32;
+basic_json_parser_44:
+ yych = *++m_cursor;
+ if (yych <= ',')
{
- goto basic_json_parser_54;
+ if (yych == '+')
+ {
+ goto basic_json_parser_51;
+ }
+ goto basic_json_parser_32;
}
- if (yych <= '`')
+ else
{
- goto basic_json_parser_33;
+ if (yych <= '-')
+ {
+ goto basic_json_parser_51;
+ }
+ if (yych <= '/')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_52;
+ }
+ goto basic_json_parser_32;
}
- if (yych <= 'f')
+basic_json_parser_45:
+ yych = *++m_cursor;
+ if (yych == 'l')
{
goto basic_json_parser_54;
}
- goto basic_json_parser_33;
- }
-basic_json_parser_44:
- yyaccept = 1;
- m_marker = ++m_cursor;
- if ((m_limit - m_cursor) < 3)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= 'D')
- {
- if (yych <= '/')
+ goto basic_json_parser_32;
+basic_json_parser_46:
+ yych = *++m_cursor;
+ if (yych == 'l')
{
- goto basic_json_parser_14;
+ goto basic_json_parser_55;
}
- if (yych <= '9')
+ goto basic_json_parser_32;
+basic_json_parser_47:
+ yych = *++m_cursor;
+ if (yych == 'u')
{
- goto basic_json_parser_44;
+ goto basic_json_parser_56;
}
- goto basic_json_parser_14;
- }
- else
- {
- if (yych <= 'E')
+ goto basic_json_parser_32;
+basic_json_parser_48:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_38;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- if (yych == 'e')
+ yych = *m_cursor;
+ if (yych <= '@')
{
- goto basic_json_parser_38;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_57;
+ }
+ goto basic_json_parser_32;
+ }
+ else
+ {
+ if (yych <= 'F')
+ {
+ goto basic_json_parser_57;
+ }
+ if (yych <= '`')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 'f')
+ {
+ goto basic_json_parser_57;
+ }
+ goto basic_json_parser_32;
}
- goto basic_json_parser_14;
- }
-basic_json_parser_46:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych >= ':')
- {
- goto basic_json_parser_33;
- }
-basic_json_parser_47:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_14;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_47;
- }
- goto basic_json_parser_14;
basic_json_parser_49:
- yych = *++m_cursor;
- if (yych == 's')
- {
- goto basic_json_parser_55;
- }
- goto basic_json_parser_33;
-basic_json_parser_50:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_56;
- }
- goto basic_json_parser_33;
+ yyaccept = 1;
+ m_marker = ++m_cursor;
+ if ((m_limit - m_cursor) < 3)
+ {
+ fill_line_buffer(3); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= 'D')
+ {
+ if (yych <= '/')
+ {
+ goto basic_json_parser_14;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_49;
+ }
+ goto basic_json_parser_14;
+ }
+ else
+ {
+ if (yych <= 'E')
+ {
+ goto basic_json_parser_44;
+ }
+ if (yych == 'e')
+ {
+ goto basic_json_parser_44;
+ }
+ goto basic_json_parser_14;
+ }
basic_json_parser_51:
- yych = *++m_cursor;
- if (yych == 'e')
- {
- goto basic_json_parser_58;
- }
- goto basic_json_parser_33;
-basic_json_parser_52:
- ++m_cursor;
- {
- return scan();
- }
-basic_json_parser_54:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
+ yych = *++m_cursor;
if (yych <= '/')
{
- goto basic_json_parser_33;
+ goto basic_json_parser_32;
}
- if (yych <= '9')
+ if (yych >= ':')
{
- goto basic_json_parser_60;
+ goto basic_json_parser_32;
}
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
+basic_json_parser_52:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_60;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- if (yych <= '`')
+ yych = *m_cursor;
+ if (yych <= '/')
{
- goto basic_json_parser_33;
+ goto basic_json_parser_14;
}
- if (yych <= 'f')
+ if (yych <= '9')
{
- goto basic_json_parser_60;
+ goto basic_json_parser_52;
}
- goto basic_json_parser_33;
- }
+ goto basic_json_parser_14;
+basic_json_parser_54:
+ yych = *++m_cursor;
+ if (yych == 's')
+ {
+ goto basic_json_parser_58;
+ }
+ goto basic_json_parser_32;
basic_json_parser_55:
- yych = *++m_cursor;
- if (yych == 'e')
- {
- goto basic_json_parser_61;
- }
- goto basic_json_parser_33;
+ yych = *++m_cursor;
+ if (yych == 'l')
+ {
+ goto basic_json_parser_59;
+ }
+ goto basic_json_parser_32;
basic_json_parser_56:
- ++m_cursor;
- {
- return token_type::literal_null;
- }
-basic_json_parser_58:
- ++m_cursor;
- {
- return token_type::literal_true;
- }
-basic_json_parser_60:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
+ yych = *++m_cursor;
+ if (yych == 'e')
{
- goto basic_json_parser_33;
+ goto basic_json_parser_61;
}
- if (yych <= '9')
+ goto basic_json_parser_32;
+basic_json_parser_57:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_63;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
+ yych = *m_cursor;
+ if (yych <= '@')
{
- goto basic_json_parser_63;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_63;
+ }
+ goto basic_json_parser_32;
}
- if (yych <= '`')
+ else
{
- goto basic_json_parser_33;
+ if (yych <= 'F')
+ {
+ goto basic_json_parser_63;
+ }
+ if (yych <= '`')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 'f')
+ {
+ goto basic_json_parser_63;
+ }
+ goto basic_json_parser_32;
}
- if (yych <= 'f')
+basic_json_parser_58:
+ yych = *++m_cursor;
+ if (yych == 'e')
{
- goto basic_json_parser_63;
+ goto basic_json_parser_64;
+ }
+ goto basic_json_parser_32;
+basic_json_parser_59:
+ ++m_cursor;
+ {
+ last_token_type = token_type::literal_null;
+ break;
}
- goto basic_json_parser_33;
- }
basic_json_parser_61:
- ++m_cursor;
- {
- return token_type::literal_false;
- }
+ ++m_cursor;
+ {
+ last_token_type = token_type::literal_true;
+ break;
+ }
basic_json_parser_63:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_33;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- if (yych <= '9')
+ yych = *m_cursor;
+ if (yych <= '@')
{
- goto basic_json_parser_31;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_66;
+ }
+ goto basic_json_parser_32;
}
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
+ else
{
- goto basic_json_parser_31;
+ if (yych <= 'F')
+ {
+ goto basic_json_parser_66;
+ }
+ if (yych <= '`')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 'f')
+ {
+ goto basic_json_parser_66;
+ }
+ goto basic_json_parser_32;
+ }
+basic_json_parser_64:
+ ++m_cursor;
+ {
+ last_token_type = token_type::literal_false;
+ break;
}
- if (yych <= '`')
+basic_json_parser_66:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
{
- goto basic_json_parser_33;
+ fill_line_buffer(1); // LCOV_EXCL_LINE
}
- if (yych <= 'f')
+ yych = *m_cursor;
+ if (yych <= '@')
{
- goto basic_json_parser_31;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_30;
+ }
+ goto basic_json_parser_32;
+ }
+ else
+ {
+ if (yych <= 'F')
+ {
+ goto basic_json_parser_30;
+ }
+ if (yych <= '`')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= 'f')
+ {
+ goto basic_json_parser_30;
+ }
+ goto basic_json_parser_32;
}
- goto basic_json_parser_33;
}
+
}
+ return last_token_type;
}
- /// append data from the stream to the internal buffer
- void yyfill() noexcept
- {
- if (m_stream == nullptr or not * m_stream)
+ /*!
+ @brief append data from the stream to the line buffer
+
+ This function is called by the scan() function when the end of the
+ buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
+ incremented without leaving the limits of the line buffer. Note re2c
+ decides when to call this function.
+
+ If the lexer reads from contiguous storage, there is no trailing null
+ byte. Therefore, this function must make sure to add these padding
+ null bytes.
+
+ If the lexer reads from an input stream, this function reads the next
+ line of the input.
+
+ @pre
+ p p p p p p u u u u u x . . . . . .
+ ^ ^ ^ ^
+ m_content m_start | m_limit
+ m_cursor
+
+ @post
+ u u u u u x x x x x x x . . . . . .
+ ^ ^ ^
+ | m_cursor m_limit
+ m_start
+ m_content
+ */
+ void fill_line_buffer(size_t n = 0)
+ {
+ // if line buffer is used, m_content points to its data
+ assert(m_line_buffer.empty()
+ or m_content == reinterpret_cast<const lexer_char_t*>(m_line_buffer.data()));
+
+ // if line buffer is used, m_limit is set past the end of its data
+ assert(m_line_buffer.empty()
+ or m_limit == m_content + m_line_buffer.size());
+
+ // pointer relationships
+ assert(m_content <= m_start);
+ assert(m_start <= m_cursor);
+ assert(m_cursor <= m_limit);
+ assert(m_marker == nullptr or m_marker <= m_limit);
+
+ // number of processed characters (p)
+ const size_t num_processed_chars = static_cast<size_t>(m_start - m_content);
+ // offset for m_marker wrt. to m_start
+ const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start;
+ // number of unprocessed characters (u)
+ const auto offset_cursor = m_cursor - m_start;
+
+ // no stream is used or end of file is reached
+ if (m_stream == nullptr or m_stream->eof())
{
- return;
- }
+ // m_start may or may not be pointing into m_line_buffer at
+ // this point. We trust the standand library to do the right
+ // thing. See http://stackoverflow.com/q/28142011/266378
+ m_line_buffer.assign(m_start, m_limit);
- const auto offset_start = m_start - m_content;
- const auto offset_marker = m_marker - m_start;
- const auto offset_cursor = m_cursor - m_start;
+ // append n characters to make sure that there is sufficient
+ // space between m_cursor and m_limit
+ m_line_buffer.append(1, '\x00');
+ if (n > 0)
+ {
+ m_line_buffer.append(n - 1, '\x01');
+ }
+ }
+ else
+ {
+ // delete processed characters from line buffer
+ m_line_buffer.erase(0, num_processed_chars);
+ // read next line from input stream
+ m_line_buffer_tmp.clear();
+ std::getline(*m_stream, m_line_buffer_tmp, '\n');
- m_buffer.erase(0, static_cast<size_t>(offset_start));
- std::string line;
- assert(m_stream != nullptr);
- std::getline(*m_stream, line);
- m_buffer += "\n" + line; // add line with newline symbol
+ // add line with newline symbol to the line buffer
+ m_line_buffer += m_line_buffer_tmp;
+ m_line_buffer.push_back('\n');
+ }
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
+ // set pointers
+ m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.data());
assert(m_content != nullptr);
m_start = m_content;
m_marker = m_start + offset_marker;
m_cursor = m_start + offset_cursor;
- m_limit = m_start + m_buffer.size() - 1;
+ m_limit = m_start + m_line_buffer.size();
}
/// return string representation of last read token
- string_t get_token() const
+ string_t get_token_string() const
{
assert(m_start != nullptr);
return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start),
@@ -8271,21 +10279,69 @@ basic_json_parser_63:
of the construction of the values.
2. Unescaped characters are copied as is.
+ @pre `m_cursor - m_start >= 2`, meaning the length of the last token
+ is at least 2 bytes which is trivially true for any string (which
+ consists of at least two quotes).
+
+ " c1 c2 c3 ... "
+ ^ ^
+ m_start m_cursor
+
+ @complexity Linear in the length of the string.\n
+
+ Lemma: The loop body will always terminate.\n
+
+ Proof (by contradiction): Assume the loop body does not terminate. As
+ the loop body does not contain another loop, one of the called
+ functions must never return. The called functions are `std::strtoul`
+ and to_unicode. Neither function can loop forever, so the loop body
+ will never loop forever which contradicts the assumption that the loop
+ body does not terminate, q.e.d.\n
+
+ Lemma: The loop condition for the for loop is eventually false.\n
+
+ Proof (by contradiction): Assume the loop does not terminate. Due to
+ the above lemma, this can only be due to a tautological loop
+ condition; that is, the loop condition i < m_cursor - 1 must always be
+ true. Let x be the change of i for any loop iteration. Then
+ m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This
+ can be rephrased to m_cursor - m_start - 2 > x. With the
+ precondition, we x <= 0, meaning that the loop condition holds
+ indefinitly if i is always decreased. However, observe that the value
+ of i is strictly increasing with each iteration, as it is incremented
+ by 1 in the iteration expression and never decremented inside the loop
+ body. Hence, the loop condition will eventually be false which
+ contradicts the assumption that the loop condition is a tautology,
+ q.e.d.
+
@return string value of current token without opening and closing
quotes
@throw std::out_of_range if to_unicode fails
*/
string_t get_string() const
{
+ assert(m_cursor - m_start >= 2);
+
string_t result;
result.reserve(static_cast<size_t>(m_cursor - m_start - 2));
// iterate the result between the quotes
for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i)
{
- // process escaped characters
- if (*i == '\\')
+ // find next escape character
+ auto e = std::find(i, m_cursor - 1, '\\');
+ if (e != i)
+ {
+ // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705
+ for (auto k = i; k < e; k++)
+ {
+ result.push_back(static_cast<typename string_t::value_type>(*k));
+ }
+ i = e - 1; // -1 because of ++i
+ }
+ else
{
+ // processing escaped character
// read next character
++i;
@@ -8356,6 +10412,11 @@ basic_json_parser_63:
// skip the next 10 characters (xxxx\uyyyy)
i += 10;
}
+ else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF)
+ {
+ // we found a lone low surrogate
+ throw std::invalid_argument("missing high surrogate");
+ }
else
{
// add unicode character(s)
@@ -8367,12 +10428,6 @@ basic_json_parser_63:
}
}
}
- else
- {
- // all other characters are just copied to the end of the
- // string
- result.append(1, static_cast<typename string_t::value_type>(*i));
- }
}
return result;
@@ -8386,17 +10441,10 @@ basic_json_parser_63:
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
- @param[in] type the @ref number_float_t in use
-
@param[in,out] endptr recieves a pointer to the first character after
the number
@return the floating point number
-
- @bug This function uses `std::strtof`, `std::strtod`, or `std::strtold`
- which use the current C locale to determine which character is used as
- decimal point character. This may yield to parse errors if the locale
- does not used `.`.
*/
long double str_to_float_t(long double* /* type */, char** endptr) const
{
@@ -8411,8 +10459,6 @@ basic_json_parser_63:
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
- @param[in] type the @ref number_float_t in use
-
@param[in,out] endptr recieves a pointer to the first character after
the number
@@ -8431,8 +10477,6 @@ basic_json_parser_63:
supplied via the first parameter. Set this to @a
static_cast<number_float_t*>(nullptr).
- @param[in] type the @ref number_float_t in use
-
@param[in,out] endptr recieves a pointer to the first character after
the number
@@ -8457,18 +10501,12 @@ basic_json_parser_63:
number_integer_t or @ref number_unsigned_t then it sets the result
parameter accordingly.
- The 'floating point representation' includes the number of significant
- figures after the radix point, whether the number is in exponential or
- decimal form, the capitalization of the exponent marker, and if the
- optional '+' is present in the exponent. This information is necessary
- to perform accurate round trips of floating point numbers.
-
If the number is a floating point number the number is then parsed
using @a std:strtod (or @a std:strtof or @a std::strtold).
@param[out] result @ref basic_json object to receive the number, or
- NAN if the conversion read past the current token. The latter case
- needs to be treated by the caller function.
+ NAN if the conversion read past the current token. The latter case
+ needs to be treated by the caller function.
*/
void get_number(basic_json& result) const
{
@@ -8476,15 +10514,6 @@ basic_json_parser_63:
const lexer::lexer_char_t* curptr = m_start;
- // remember this number was parsed (for later serialization)
- result.m_type.bits.parsed = true;
-
- // 'found_radix_point' will be set to 0xFF upon finding a radix
- // point and later used to mask in/out the precision depending
- // whether a radix is found i.e. 'precision &= found_radix_point'
- uint8_t found_radix_point = 0;
- uint8_t precision = 0;
-
// accumulate the integer conversion result (unsigned for now)
number_unsigned_t value = 0;
@@ -8517,50 +10546,34 @@ basic_json_parser_63:
{
// don't count '.' but change to float
type = value_t::number_float;
-
- // reset precision count
- precision = 0;
- found_radix_point = 0xFF;
continue;
}
// assume exponent (if not then will fail parse): change to
// float, stop counting and record exponent details
type = value_t::number_float;
- result.m_type.bits.has_exp = true;
-
- // exponent capitalization
- result.m_type.bits.exp_cap = (*curptr == 'E');
-
- // exponent '+' sign
- result.m_type.bits.exp_plus = (*(++curptr) == '+');
break;
}
// skip if definitely not an integer
if (type != value_t::number_float)
{
- // multiply last value by ten and add the new digit
- auto temp = value * 10 + *curptr - 0x30;
+ auto digit = static_cast<number_unsigned_t>(*curptr - '0');
- // test for overflow
- if (temp < value || temp > max)
+ // overflow if value * 10 + digit > max, move terms around
+ // to avoid overflow in intermediate values
+ if (value > (max - digit) / 10)
{
// overflow
type = value_t::number_float;
}
else
{
- // no overflow - save it
- value = temp;
+ // no overflow
+ value = value * 10 + digit;
}
}
- ++precision;
}
- // If no radix point was found then precision would now be set to
- // the number of digits, which is wrong - clear it.
- result.m_type.bits.precision = precision & found_radix_point;
-
// save the value (if not a float)
if (type == value_t::number_unsigned)
{
@@ -8568,12 +10581,34 @@ basic_json_parser_63:
}
else if (type == value_t::number_integer)
{
- result.m_value.number_integer = -static_cast<number_integer_t>(value);
+ // invariant: if we parsed a '-', the absolute value is between
+ // 0 (we allow -0) and max == -INT64_MIN
+ assert(value >= 0);
+ assert(value <= max);
+
+ if (value == max)
+ {
+ // we cannot simply negate value (== max == -INT64_MIN),
+ // see https://github.com/nlohmann/json/issues/389
+ result.m_value.number_integer = static_cast<number_integer_t>(INT64_MIN);
+ }
+ else
+ {
+ // all other values can be negated safely
+ result.m_value.number_integer = -static_cast<number_integer_t>(value);
+ }
}
else
{
// parse with strtod
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
+
+ // replace infinity and NAN by null
+ if (not std::isfinite(result.m_value.number_float))
+ {
+ type = value_t::null;
+ result.m_value = basic_json::json_value();
+ }
}
// save the type
@@ -8583,8 +10618,10 @@ basic_json_parser_63:
private:
/// optional input stream
std::istream* m_stream = nullptr;
- /// the buffer
- string_t m_buffer;
+ /// line buffer buffer for m_stream
+ string_t m_line_buffer {};
+ /// used for filling m_line_buffer
+ string_t m_line_buffer_tmp {};
/// the buffer pointer
const lexer_char_t* m_content = nullptr;
/// pointer to the beginning of the current symbol
@@ -8595,6 +10632,8 @@ basic_json_parser_63:
const lexer_char_t* m_cursor = nullptr;
/// pointer to the end of the buffer
const lexer_char_t* m_limit = nullptr;
+ /// the last token type
+ token_type last_token_type = token_type::end_of_input;
};
/*!
@@ -8605,32 +10644,42 @@ basic_json_parser_63:
class parser
{
public:
- /// constructor for strings
- parser(const string_t& s, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
- {
- // read first token
- get_token();
- }
+ /// a parser reading from a string literal
+ parser(const char* buff, const parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), std::strlen(buff))
+ {}
/// a parser reading from an input stream
- parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
- {
- // read first token
- get_token();
- }
+ parser(std::istream& is, const parser_callback_t cb = nullptr)
+ : callback(cb), m_lexer(is)
+ {}
+
+ /// a parser reading from an iterator range with contiguous storage
+ template<class IteratorType, typename std::enable_if<
+ std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
+ , int>::type
+ = 0>
+ parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
+ static_cast<size_t>(std::distance(first, last)))
+ {}
/// public parser interface
basic_json parse()
{
+ // read first token
+ get_token();
+
basic_json result = parse_internal(true);
+ result.assert_invariant();
expect(lexer::token_type::end_of_input);
// return parser result and replace it with null in case the
// top-level value was discarded by the callback function
- return result.is_discarded() ? basic_json() : result;
+ return result.is_discarded() ? basic_json() : std::move(result);
}
private:
@@ -8643,11 +10692,12 @@ basic_json_parser_63:
{
case lexer::token_type::begin_object:
{
- if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result))))
+ if (keep and (not callback
+ or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0)))
{
// explicitly set result to object to cope with {}
result.m_type = value_t::object;
- result.m_value = json_value(value_t::object);
+ result.m_value = value_t::object;
}
// read next token
@@ -8721,11 +10771,12 @@ basic_json_parser_63:
case lexer::token_type::begin_array:
{
- if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result))))
+ if (keep and (not callback
+ or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0)))
{
// explicitly set result to object to cope with []
result.m_type = value_t::array;
- result.m_value = json_value(value_t::array);
+ result.m_value = value_t::array;
}
// read next token
@@ -8827,7 +10878,7 @@ basic_json_parser_63:
}
/// get next token from lexer
- typename lexer::token_type get_token() noexcept
+ typename lexer::token_type get_token()
{
last_token = m_lexer.scan();
return last_token;
@@ -8838,7 +10889,8 @@ basic_json_parser_63:
if (t != last_token)
{
std::string error_msg = "parse error - unexpected ";
- error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") :
+ error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() +
+ "'") :
lexer::token_type_name(last_token));
error_msg += "; expected " + lexer::token_type_name(t);
throw std::invalid_argument(error_msg);
@@ -8850,7 +10902,8 @@ basic_json_parser_63:
if (t == last_token)
{
std::string error_msg = "parse error - unexpected ";
- error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") :
+ error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() +
+ "'") :
lexer::token_type_name(last_token));
throw std::invalid_argument(error_msg);
}
@@ -8860,7 +10913,7 @@ basic_json_parser_63:
/// current level of recursion
int depth = 0;
/// callback function
- parser_callback_t callback;
+ const parser_callback_t callback = nullptr;
/// the type of the last read token
typename lexer::token_type last_token = lexer::token_type::uninitialized;
/// the lexer
@@ -8928,14 +10981,12 @@ basic_json_parser_63:
*/
std::string to_string() const noexcept
{
- std::string result;
-
- for (const auto& reference_token : reference_tokens)
+ return std::accumulate(reference_tokens.begin(),
+ reference_tokens.end(), std::string{},
+ [](const std::string & a, const std::string & b)
{
- result += "/" + escape(reference_token);
- }
-
- return result;
+ return a + "/" + escape(b);
+ });
}
/// @copydoc to_string()
@@ -8978,6 +11029,8 @@ basic_json_parser_63:
/*!
@brief create and return a reference to the pointed to value
+
+ @complexity Linear in the number of reference tokens.
*/
reference get_and_create(reference j) const
{
@@ -9038,6 +11091,12 @@ basic_json_parser_63:
/*!
@brief return a reference to the pointed to value
+ @note This version does not throw if a value is not present, but tries
+ to create nested values instead. For instance, calling this function
+ with pointer `"/this/that"` on a null value is equivalent to calling
+ `operator[]("this").operator[]("that")` on that value, effectively
+ changing the null value to an object.
+
@param[in] ptr a JSON value
@return reference to the JSON value pointed to by the JSON pointer
@@ -9052,6 +11111,29 @@ basic_json_parser_63:
{
for (const auto& reference_token : reference_tokens)
{
+ // convert null values to arrays or objects before continuing
+ if (ptr->m_type == 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 std::isdigit(x);
+ });
+
+ // change value to array for numbers or "-" or to object
+ // otherwise
+ if (nums or reference_token == "-")
+ {
+ *ptr = value_t::array;
+ }
+ else
+ {
+ *ptr = value_t::object;
+ }
+ }
+
switch (ptr->m_type)
{
case value_t::object:
@@ -9233,7 +11315,7 @@ basic_json_parser_63:
}
/// split the string input to reference tokens
- static std::vector<std::string> split(std::string reference_string)
+ static std::vector<std::string> split(const std::string& reference_string)
{
std::vector<std::string> result;
@@ -9297,12 +11379,10 @@ basic_json_parser_63:
/*!
@brief replace all occurrences of a substring by another string
- @param[in,out] s the string to manipulate
+ @param[in,out] s the string to manipulate; changed so that all
+ occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
- @param[out] t the string to replace @a f
-
- @return The string @a s where all occurrences of @a f are replaced
- with @a t.
+ @param[in] t the string to replace @a f
@pre The search string @a f must not be empty.
@@ -9718,7 +11798,7 @@ basic_json_parser_63:
json_pointer top_pointer = ptr.top();
if (top_pointer != ptr)
{
- basic_json& x = result.at(top_pointer);
+ result.at(top_pointer);
}
// get reference to parent of JSON pointer ptr
@@ -9961,7 +12041,7 @@ basic_json_parser_63:
*/
static basic_json diff(const basic_json& source,
const basic_json& target,
- std::string path = "")
+ const std::string& path = "")
{
// the patch
basic_json result(value_t::array);
@@ -10002,9 +12082,12 @@ basic_json_parser_63:
// in a second pass, traverse the remaining elements
// remove my remaining elements
+ const auto end_index = static_cast<difference_type>(result.size());
while (i < source.size())
{
- result.push_back(object(
+ // add operations in reverse order to avoid invalid
+ // indices
+ result.insert(result.begin() + end_index, object(
{
{"op", "remove"},
{"path", path + "/" + std::to_string(i)}
@@ -10120,7 +12203,7 @@ namespace std
@since version 1.0.0
*/
-template <>
+template<>
inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept(
is_nothrow_move_constructible<nlohmann::json>::value and
@@ -10131,7 +12214,7 @@ inline void swap(nlohmann::json& j1,
}
/// hash value for JSON objects
-template <>
+template<>
struct hash<nlohmann::json>
{
/*!
@@ -10152,27 +12235,36 @@ struct hash<nlohmann::json>
@brief user-defined string literal for JSON values
This operator implements a user-defined string literal for JSON objects. It
-can be used by adding \p "_json" to a string literal and returns a JSON object
+can be used by adding `"_json"` to a string literal and returns a JSON object
if no parse error occurred.
@param[in] s a string representation of a JSON object
+@param[in] n the length of string @a s
@return a JSON object
@since version 1.0.0
*/
-inline nlohmann::json operator "" _json(const char* s, std::size_t)
+inline nlohmann::json operator "" _json(const char* s, std::size_t n)
{
- return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
+ return nlohmann::json::parse(s, s + n);
}
/*!
@brief user-defined string literal for JSON pointer
+This operator implements a user-defined string literal for JSON Pointers. It
+can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer
+object if no parse error occurred.
+
+@param[in] s a string representation of a JSON Pointer
+@param[in] n the length of string @a s
+@return a JSON pointer object
+
@since version 2.0.0
*/
-inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
{
- return nlohmann::json::json_pointer(s);
+ return nlohmann::json::json_pointer(std::string(s, n));
}
// restore GCC/clang diagnostic settings