diff --git a/source/dataman/CMakeLists.txt b/source/dataman/CMakeLists.txt index f8ee0813ee7226d0632cc7cab9403b57ffeff2b1..5adafab771f64ee9ff140fea4a9bdaaa830f4f21 100644 --- a/source/dataman/CMakeLists.txt +++ b/source/dataman/CMakeLists.txt @@ -11,7 +11,10 @@ add_library(dataman CacheMan.cpp CacheMan.h ) target_include_directories(dataman PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(dataman PRIVATE adios2sys) +target_link_libraries(dataman + PRIVATE adios2sys + PUBLIC NLohmannJson +) list(APPEND dataman_targets dataman) # Add the dataman plugins as MODULE libraries instead of SHARED libraries. diff --git a/thirdparty/.clang-tidy b/thirdparty/.clang-tidy new file mode 100644 index 0000000000000000000000000000000000000000..7dfec2fb694c221b0cf08d05c260fed05d5d2f25 --- /dev/null +++ b/thirdparty/.clang-tidy @@ -0,0 +1,9 @@ +Checks: -*,readability-function-size + +CheckOptions: + - key: readability-function-size.LineThreshold + value: -1 + - key: readability-function-size.StatementThreshold + value: -1 + - key: readability-function-size.BranchThreshold + value: -1 diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 5db05938ccc248e90c8532b0dd1ab1d1d02a116e..006157536e884d8f2065b3dd26e638be3df48fc6 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(KWSys) +add_subdirectory(NLohmannJson) if(BUILD_TESTING) add_subdirectory(GTest) diff --git a/thirdparty/NLohmannJson/CMakeLists.txt b/thirdparty/NLohmannJson/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7aaef186fef9b708ed4c3ed97fbb11e38253a0a6 --- /dev/null +++ b/thirdparty/NLohmannJson/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(NLohmannJson INTERFACE) +target_include_directories(NLohmannJson + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/json/src +) diff --git a/thirdparty/NLohmannJson/Readme.txt b/thirdparty/NLohmannJson/Readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4264273bbc5566dfef4f92fba766ff248ff905c --- /dev/null +++ b/thirdparty/NLohmannJson/Readme.txt @@ -0,0 +1,4 @@ +This is a copy of the upstream JSON For Modern C++ library located at +https://github.com/nlohmann/json. Do not make changes directly to this repo +but instead to the upstream repository. Update this copy of json.hpp by running +the update.sh script. diff --git a/thirdparty/NLohmannJson/json/LICENSE.MIT b/thirdparty/NLohmannJson/json/LICENSE.MIT new file mode 100644 index 0000000000000000000000000000000000000000..00599afe6a85170b057ca0e5240ae45c9386b9fc --- /dev/null +++ b/thirdparty/NLohmannJson/json/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2017 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/source/dataman/json.hpp b/thirdparty/NLohmannJson/json/src/json.hpp similarity index 57% rename from source/dataman/json.hpp rename to thirdparty/NLohmannJson/json/src/json.hpp index c695ce2156e2595ea40ca2e5528c11ff9dcdf64a..ee04c77c76650155d066805828c8b4112ecea7fe 100644 --- a/source/dataman/json.hpp +++ b/thirdparty/NLohmannJson/json/src/json.hpp @@ -30,77 +30,74 @@ SOFTWARE. #define NLOHMANN_JSON_HPP #include <algorithm> // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform -#include <array> // array +#include <array> // array #include <cassert> // assert +#include <cctype> // isdigit #include <ciso646> // and, not, or -#include <clocale> // lconv, localeconv -#include <cmath> // isfinite, labs, ldexp, signbit +#include <cmath> // isfinite, labs, ldexp, signbit #include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <cstdint> // int64_t, uint64_t #include <cstdlib> // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull #include <cstring> // strlen -#include <forward_list> // forward_list -#include <functional> // function, hash, less +#include <forward_list> // forward_list +#include <functional> // function, hash, less #include <initializer_list> // initializer_list -#include <iostream> // istream, ostream -#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator -#include <limits> // numeric_limits -#include <locale> // locale -#include <map> // map -#include <memory> // addressof, allocator, allocator_traits, unique_ptr -#include <numeric> // accumulate -#include <sstream> // stringstream -#include <string> // getline, stoi, string, to_string +#include <iomanip> // setw +#include <iostream> // istream, ostream +#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator +#include <limits> // numeric_limits +#include <locale> // locale +#include <map> // map +#include <memory> // addressof, allocator, allocator_traits, unique_ptr +#include <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, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type -#include <utility> // declval, forward, make_pair, move, pair, swap -#include <vector> // vector +#include <utility> // declval, forward, make_pair, move, pair, swap +#include <vector> // vector // exclude unsupported compilers #if defined(__clang__) -#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < \ - 30400 -#error \ - "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" -#endif + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif #elif defined(__GNUC__) -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40805 -#error \ - "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" -#endif + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #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__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" #endif // disable documentation warnings on clang #if defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdocumentation" + #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)) + #define JSON_DEPRECATED __attribute__((deprecated)) #elif defined(_MSC_VER) -#define JSON_DEPRECATED __declspec(deprecated) + #define JSON_DEPRECATED __declspec(deprecated) #else -#define JSON_DEPRECATED + #define JSON_DEPRECATED #endif // allow to disable exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ - defined(_CPPUNWIND)) && \ - not defined(JSON_NOEXCEPTION) -#define JSON_THROW(exception) throw exception -#define JSON_TRY try -#define JSON_CATCH(exception) catch (exception) +#if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) #else -#define JSON_THROW(exception) std::abort() -#define JSON_TRY if (true) -#define JSON_CATCH(exception) if (false) + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) #endif /*! @@ -121,348 +118,6 @@ This namespace collects some functions that could not be defined inside the */ namespace detail { -//////////////// -// exceptions // -//////////////// - -/*! -@brief general exception of the @ref basic_json class - -Extension of std::exception objects with a member @a id for exception ids. - -@note To have nothrow-copy-constructible exceptions, we internally use - std::runtime_error which can cope with arbitrary-length error messages. - Intermediate strings are built with static functions and then passed to - the actual constructor. - -@since version 3.0.0 -*/ -class exception : public std::exception -{ -public: - /// returns the explanatory string - virtual const char *what() const noexcept override { return m.what(); } - - /// the id of the exception - const int id; - -protected: - exception(int id_, const char *what_arg) : id(id_), m(what_arg) {} - - static std::string name(const std::string &ename, int id) - { - return "[json.exception." + ename + "." + std::to_string(id) + "] "; - } - -private: - /// an exception object as storage for error messages - std::runtime_error m; -}; - -/*! -@brief exception indicating a parse error - -This excpetion is thrown by the library when a parse error occurs. Parse -errors can occur during the deserialization of JSON text as well as when -using JSON Patch. - -Member @a byte holds the byte index of the last read character in the input -file. - -@note For an input with n bytes, 1 is the index of the first character - and n+1 is the index of the terminating null byte or the end of - file. This also holds true when reading a byte vector (CBOR or - MessagePack). - -Exceptions have ids 1xx. - -name / id | example massage | description ------------------------------- | --------------- | ------------------------- -json.exception.parse_error.101 | parse error at 2: unexpected end of input; -expected string literal | This error indicates a syntax error while -deserializing a JSON text. The error message describes that an unexpected token -(character) was encountered, and the member @a byte indicates the error -position. -json.exception.parse_error.102 | parse error at 14: missing or wrong low -surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code -points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate -pairs"). This error indicates that the surrogate pair is incomplete or contains -an invalid code point. -json.exception.parse_error.103 | parse error: code points above 0x10FFFF are -invalid | Unicode supports code points up to 0x10FFFF. Code points above -0x10FFFF are invalid. -json.exception.parse_error.104 | parse error: JSON patch must be an array of -objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch -document to be a JSON document that represents an array of objects. -json.exception.parse_error.105 | parse error: operation must have string member -'op' | An operation of a JSON Patch document must contain exactly one "op" -member, whose value indicates the operation to perform. Its value must be one of -"add", "remove", "replace", "move", "copy", or "test"; other values are errors. -json.exception.parse_error.106 | parse error: array index '01' must not begin -with '0' | An array index in a JSON Pointer ([RFC -6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number wihtout a -leading `0`. -json.exception.parse_error.107 | parse error: JSON pointer must be empty or -begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing -a sequence of zero or more reference tokens, each prefixed by a `/` character. -json.exception.parse_error.108 | parse error: escape character '~' must be -followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid -escape sequences. -json.exception.parse_error.109 | parse error: array index 'one' is not a number -| A JSON Pointer array index must be a number. -json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from -vector | When parsing CBOR or MessagePack, the byte vector ends before the -complete value has been read. -json.exception.parse_error.111 | parse error: bad input stream | Parsing CBOR or -MessagePack from an input stream where the [`badbit` or -`failbit`](http://en.cppreference.com/w/cpp/io/ios_base/iostate) is set. -json.exception.parse_error.112 | parse error at 1: error reading CBOR; last -byte: 0xf8 | Not all types of CBOR or MessagePack are supported. This exception -occurs if an unsupported byte was read. -json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last -byte: 0x98 | While parsing a map key, a value that is not a string has been -read. - -@since version 3.0.0 -*/ -class parse_error : public exception -{ -public: - /*! - @brief create a parse error exception - @param[in] id the id of the exception - @param[in] byte_ the byte index where the error occured (or 0 if - the position cannot be determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - static parse_error create(int id, size_t byte_, const std::string &what_arg) - { - std::string w = exception::name("parse_error", id) + "parse error" + - (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + - ": " + what_arg; - return parse_error(id, byte_, w.c_str()); - } - - /*! - @brief byte index of the parse error - - The byte index of the last read character in the input file. - - @note For an input with n bytes, 1 is the index of the first character - and n+1 is the index of the terminating null byte or the end of - file. This also holds true when reading a byte vector (CBOR or - MessagePack). - */ - const size_t byte; - -private: - parse_error(int id_, size_t byte_, const char *what_arg) - : exception(id_, what_arg), byte(byte_) - { - } -}; - -/*! -@brief exception indicating errors with iterators - -Exceptions have ids 2xx. - -name / id | example massage | description ------------------------------------ | --------------- | -------------------------- -json.exception.invalid_iterator.201 | iterators are not compatible | The -iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are -not compatible, meaning they do not belong to the same container. Therefore, the -range (@a first, @a last) is invalid. -json.exception.invalid_iterator.202 | iterator does not fit current value | In -an erase or insert function, the passed iterator @a pos does not belong to the -JSON value for which the function was called. It hence does not define a valid -position for the deletion/insertion. -json.exception.invalid_iterator.203 | iterators do not fit current value | -Either iterator passed to function @ref erase(IteratorType first, IteratorType -last) does not belong to the JSON value from which values shall be erased. It -hence does not define a valid range to delete values from. -json.exception.invalid_iterator.204 | iterators out of range | When an iterator -range for a primitive type (number, boolean, or string) is passed to a -constructor or an erase function, this range has to be exactly (@ref begin(), -@ref end()), because this is the only way the single stored value is expressed. -All other ranges are invalid. -json.exception.invalid_iterator.205 | iterator out of range | When an iterator -for a primitive type (number, boolean, or string) is passed to an erase -function, the iterator has to be the @ref begin() iterator, because it is the -only way to address the stored value. All other iterators are invalid. -json.exception.invalid_iterator.206 | cannot construct with iterators from null -| The iterators passed to constructor @ref basic_json(InputIT first, InputIT -last) belong to a JSON null value and hence to not define a valid range. -json.exception.invalid_iterator.207 | cannot use key() for non-object iterators -| The key() member function can only be used on iterators belonging to a JSON -object, because other types do not have a concept of a key. -json.exception.invalid_iterator.208 | cannot use operator[] for object iterators -| The operator[] to specify a concrete offset cannot be used on iterators -belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.209 | cannot use offsets with object iterators | -The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a -JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.210 | iterators do not fit | The iterator range -passed to the insert function are not compatible, meaning they do not belong to -the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.211 | passed iterators may not belong to -container | The iterator range passed to the insert function must not be a -subrange of the container to insert to. -json.exception.invalid_iterator.212 | cannot compare iterators of different -containers | When two iterators are compared, they must belong to the same -container. -json.exception.invalid_iterator.213 | cannot compare order of object iterators | -The order of object iterators cannot be compated, because JSON objects are -unordered. -json.exception.invalid_iterator.214 | cannot get value | Cannot get value for -iterator: Either the iterator belongs to a null value or it is an iterator to a -primitive type (number, boolean, or string), but the iterator is different to -@ref begin(). - -@since version 3.0.0 -*/ -class invalid_iterator : public exception -{ -public: - static invalid_iterator create(int id, const std::string &what_arg) - { - std::string w = exception::name("invalid_iterator", id) + what_arg; - return invalid_iterator(id, w.c_str()); - } - -private: - invalid_iterator(int id_, const char *what_arg) : exception(id_, what_arg) - { - } -}; - -/*! -@brief exception indicating executing a member function with a wrong type - -Exceptions have ids 3xx. - -name / id | example massage | description ------------------------------ | --------------- | ------------------------- -json.exception.type_error.301 | cannot create object from initializer list | To -create an object from an initializer list, the initializer list must consist -only of a list of pairs whose first element is a string. When this constraint is -violated, an array is created instead. -json.exception.type_error.302 | type must be object, but is array | During -implicit or explicit value conversion, the JSON type must be compatible to the -target type. For instance, a JSON string can only be converted into string -types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual -type is object | To retrieve a reference to a value stored in a @ref basic_json -object with @ref get_ref, the type of the reference must match the value type. -For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. -json.exception.type_error.304 | cannot use at() with string | The @ref at() -member functions can only be executed for certain JSON types. -json.exception.type_error.305 | cannot use operator[] with string | The @ref -operator[] member functions can only be executed for certain JSON types. -json.exception.type_error.306 | cannot use value() with string | The @ref -value() member functions can only be executed for certain JSON types. -json.exception.type_error.307 | cannot use erase() with string | The @ref -erase() member functions can only be executed for certain JSON types. -json.exception.type_error.308 | cannot use push_back() with string | The @ref -push_back() and @ref operator+= member functions can only be executed for -certain JSON types. -json.exception.type_error.309 | cannot use insert() with | The @ref insert() -member functions can only be executed for certain JSON types. -json.exception.type_error.310 | cannot use swap() with number | The @ref swap() -member functions can only be executed for certain JSON types. -json.exception.type_error.311 | cannot use emplace_back() with string | The @ref -emplace_back() member function can only be executed for certain JSON types. -json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten -function converts an object whose keys are JSON Pointers back into an arbitrary -nested JSON value. The JSON Pointers must not overlap, because then the -resulting value would not be well defined. -json.exception.type_error.314 | only objects can be unflattened | The @ref -unflatten function only works for an object whose keys are JSON Pointers. -json.exception.type_error.315 | values in object must be primitive | The @ref -unflatten function only works for an object whose keys are JSON Pointers and -whose values are primitive. - -@since version 3.0.0 -*/ -class type_error : public exception -{ -public: - static type_error create(int id, const std::string &what_arg) - { - std::string w = exception::name("type_error", id) + what_arg; - return type_error(id, w.c_str()); - } - -private: - type_error(int id_, const char *what_arg) : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating access out of the defined range - -Exceptions have ids 4xx. - -name / id | example massage | description -------------------------------- | --------------- | ------------------------- -json.exception.out_of_range.401 | array index 3 is out of range | The provided -array index @a i is larger than @a size-1. -json.exception.out_of_range.402 | array index '-' (3) is out of range | The -special array index `-` in a JSON Pointer never describes a valid element of the -array, but the index past the end. That is, it can only be used to add elements -at this position, but not to read it. -json.exception.out_of_range.403 | key 'foo' not found | The provided key was not -found in the JSON object. -json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference -token in a JSON Pointer could not be resolved. -json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch -operations 'remove' and 'add' can not be applied to the root element of the JSON -value. -json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed -number could not be stored as without changing it to NaN or INF. - -@since version 3.0.0 -*/ -class out_of_range : public exception -{ -public: - static out_of_range create(int id, const std::string &what_arg) - { - std::string w = exception::name("out_of_range", id) + what_arg; - return out_of_range(id, w.c_str()); - } - -private: - out_of_range(int id_, const char *what_arg) : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating other errors - -Exceptions have ids 5xx. - -name / id | example massage | description ------------------------------- | --------------- | ------------------------- -json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", -"value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful -operation is also printed. - -@since version 3.0.0 -*/ -class other_error : public exception -{ -public: - static other_error create(int id, const std::string &what_arg) - { - std::string w = exception::name("other_error", id) + what_arg; - return other_error(id, w.c_str()); - } - -private: - other_error(int id_, const char *what_arg) : exception(id_, what_arg) {} -}; - /////////////////////////// // JSON type enumeration // /////////////////////////// @@ -516,15 +171,16 @@ Returns an ordering that is similar to Python: inline bool operator<(const value_t lhs, const value_t rhs) noexcept { static constexpr std::array<uint8_t, 8> order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2, // unsigned - 2, // float - }}; + 0, // null + 3, // object + 4, // array + 5, // string + 1, // boolean + 2, // integer + 2, // unsigned + 2, // float + } + }; // discarded values are not comparable if (lhs == value_t::discarded or rhs == value_t::discarded) @@ -536,17 +192,23 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept order[static_cast<std::size_t>(rhs)]; } + ///////////// // helpers // ///////////// // alias templates to reduce boilerplate -template <bool B, typename T = void> +template<bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type; -template <typename T> -using uncvref_t = - typename std::remove_cv<typename std::remove_reference<T>::type>::type; +template<typename T> +using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; + +// taken from http://stackoverflow.com/a/26936864/266378 +template<typename T> +using is_unscoped_enum = + std::integral_constant<bool, std::is_convertible<T, int>::value and + std::is_enum<T>::value>; /* Implementation of two C++17 constructs: conjunction, negation. This is needed @@ -561,48 +223,29 @@ Please note that those constructs must be used with caution, since symbols can become very long quickly (which can slow down compilation and cause MSVC internal compiler errors). Only use it when you have to (see example ahead). */ -template <class...> -struct conjunction : std::true_type -{ -}; -template <class B1> -struct conjunction<B1> : B1 -{ -}; -template <class B1, class... Bn> -struct conjunction<B1, Bn...> - : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type -{ -}; +template<class...> struct conjunction : std::true_type {}; +template<class B1> struct conjunction<B1> : B1 {}; +template<class B1, class... Bn> +struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {}; -template <class B> -struct negation : std::integral_constant<bool, !B::value> -{ -}; +template<class B> struct negation : std::integral_constant < bool, !B::value > {}; // dispatch utility (taken from ranges-v3) -template <unsigned N> -struct priority_tag : priority_tag<N - 1> -{ -}; -template <> -struct priority_tag<0> -{ -}; +template<unsigned N> struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + ////////////////// // constructors // ////////////////// -template <value_t> -struct external_constructor; +template<value_t> struct external_constructor; -template <> +template<> struct external_constructor<value_t::boolean> { - template <typename BasicJsonType> - static void construct(BasicJsonType &j, - typename BasicJsonType::boolean_t b) noexcept + template<typename BasicJsonType> + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept { j.m_type = value_t::boolean; j.m_value = b; @@ -610,12 +253,11 @@ struct external_constructor<value_t::boolean> } }; -template <> +template<> struct external_constructor<value_t::string> { - template <typename BasicJsonType> - static void construct(BasicJsonType &j, - const typename BasicJsonType::string_t &s) + template<typename BasicJsonType> + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) { j.m_type = value_t::string; j.m_value = s; @@ -623,26 +265,31 @@ struct external_constructor<value_t::string> } }; -template <> +template<> struct external_constructor<value_t::number_float> { - template <typename BasicJsonType> - static void construct(BasicJsonType &j, - typename BasicJsonType::number_float_t val) noexcept + template<typename BasicJsonType> + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { - j.m_type = value_t::number_float; - j.m_value = val; + // replace infinity and NAN by null + if (not std::isfinite(val)) + { + j = BasicJsonType{}; + } + else + { + j.m_type = value_t::number_float; + j.m_value = val; + } j.assert_invariant(); } }; -template <> +template<> struct external_constructor<value_t::number_unsigned> { - template <typename BasicJsonType> - static void - construct(BasicJsonType &j, - typename BasicJsonType::number_unsigned_t val) noexcept + template<typename BasicJsonType> + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept { j.m_type = value_t::number_unsigned; j.m_value = val; @@ -650,12 +297,11 @@ struct external_constructor<value_t::number_unsigned> } }; -template <> +template<> struct external_constructor<value_t::number_integer> { - template <typename BasicJsonType> - static void construct(BasicJsonType &j, - typename BasicJsonType::number_integer_t val) noexcept + template<typename BasicJsonType> + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept { j.m_type = value_t::number_integer; j.m_value = val; @@ -663,76 +309,58 @@ struct external_constructor<value_t::number_integer> } }; -template <> +template<> struct external_constructor<value_t::array> { - template <typename BasicJsonType> - static void construct(BasicJsonType &j, - const typename BasicJsonType::array_t &arr) + template<typename BasicJsonType> + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) { j.m_type = value_t::array; j.m_value = arr; j.assert_invariant(); } - template < - typename BasicJsonType, typename CompatibleArrayType, - enable_if_t<not std::is_same<CompatibleArrayType, - typename BasicJsonType::array_t>::value, - int> = 0> - static void construct(BasicJsonType &j, const CompatibleArrayType &arr) + template<typename BasicJsonType, typename CompatibleArrayType, + enable_if_t<not std::is_same<CompatibleArrayType, + typename BasicJsonType::array_t>::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) { using std::begin; using std::end; j.m_type = value_t::array; - j.m_value.array = j.template create<typename BasicJsonType::array_t>( - begin(arr), end(arr)); - j.assert_invariant(); - } - - template <typename BasicJsonType> - static void construct(BasicJsonType &j, const std::vector<bool> &arr) - { - j.m_type = value_t::array; - j.m_value = value_t::array; - j.m_value.array->reserve(arr.size()); - for (bool x : arr) - { - j.m_value.array->push_back(x); - } + j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); j.assert_invariant(); } }; -template <> +template<> struct external_constructor<value_t::object> { - template <typename BasicJsonType> - static void construct(BasicJsonType &j, - const typename BasicJsonType::object_t &obj) + template<typename BasicJsonType> + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) { j.m_type = value_t::object; j.m_value = obj; j.assert_invariant(); } - template < - typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<not std::is_same<CompatibleObjectType, - typename BasicJsonType::object_t>::value, - int> = 0> - static void construct(BasicJsonType &j, const CompatibleObjectType &obj) + template<typename BasicJsonType, typename CompatibleObjectType, + enable_if_t<not std::is_same<CompatibleObjectType, + typename BasicJsonType::object_t>::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; using std::end; j.m_type = value_t::object; - j.m_value.object = j.template create<typename BasicJsonType::object_t>( - begin(obj), end(obj)); + j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj)); j.assert_invariant(); } }; + //////////////////////// // has_/is_ functions // //////////////////////// @@ -747,18 +375,15 @@ contains a `mapped_type`, whereas `std::vector` fails the test. @sa http://stackoverflow.com/a/7728728/266378 @since version 1.0.0, overworked in version 2.0.6 */ -#define NLOHMANN_JSON_HAS_HELPER(type) \ - template <typename T> \ - struct has_##type \ - { \ - private: \ - template <typename U, typename = typename U::type> \ - static int detect(U &&); \ - static void detect(...); \ - \ - public: \ - static constexpr bool value = \ - std::is_integral<decltype(detect(std::declval<T>()))>::value; \ +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template<typename T> struct has_##type { \ + private: \ + template<typename U, typename = typename U::type> \ + static int detect(U &&); \ + static void detect(...); \ + public: \ + static constexpr bool value = \ + std::is_integral<decltype(detect(std::declval<T>()))>::value; \ } NLOHMANN_JSON_HAS_HELPER(mapped_type); @@ -768,66 +393,59 @@ NLOHMANN_JSON_HAS_HELPER(iterator); #undef NLOHMANN_JSON_HAS_HELPER -template <bool B, class RealType, class CompatibleObjectType> -struct is_compatible_object_type_impl : std::false_type -{ -}; -template <class RealType, class CompatibleObjectType> +template<bool B, class RealType, class CompatibleObjectType> +struct is_compatible_object_type_impl : std::false_type {}; + +template<class RealType, class CompatibleObjectType> struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType> { static constexpr auto value = - std::is_constructible< - typename RealType::key_type, - typename CompatibleObjectType::key_type>::value and - std::is_constructible< - typename RealType::mapped_type, - typename CompatibleObjectType::mapped_type>::value; + std::is_constructible<typename RealType::key_type, + typename CompatibleObjectType::key_type>::value and + std::is_constructible<typename RealType::mapped_type, + typename CompatibleObjectType::mapped_type>::value; }; -template <class BasicJsonType, class CompatibleObjectType> +template<class BasicJsonType, class CompatibleObjectType> struct is_compatible_object_type { - static auto constexpr value = is_compatible_object_type_impl< - conjunction<negation<std::is_same<void, CompatibleObjectType>>, - has_mapped_type<CompatibleObjectType>, - has_key_type<CompatibleObjectType>>::value, - typename BasicJsonType::object_t, CompatibleObjectType>::value; + static auto constexpr value = is_compatible_object_type_impl < + conjunction<negation<std::is_same<void, CompatibleObjectType>>, + has_mapped_type<CompatibleObjectType>, + has_key_type<CompatibleObjectType>>::value, + typename BasicJsonType::object_t, CompatibleObjectType >::value; }; -template <typename BasicJsonType, typename T> +template<typename BasicJsonType, typename T> struct is_basic_json_nested_type { - static auto constexpr value = - std::is_same<T, typename BasicJsonType::iterator>::value or - std::is_same<T, typename BasicJsonType::const_iterator>::value or - std::is_same<T, typename BasicJsonType::reverse_iterator>::value or - std::is_same<T, - typename BasicJsonType::const_reverse_iterator>::value or - std::is_same<T, typename BasicJsonType::json_pointer>::value; + static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or + std::is_same<T, typename BasicJsonType::const_iterator>::value or + std::is_same<T, typename BasicJsonType::reverse_iterator>::value or + std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value or + std::is_same<T, typename BasicJsonType::json_pointer>::value; }; -template <class BasicJsonType, class CompatibleArrayType> +template<class BasicJsonType, class CompatibleArrayType> struct is_compatible_array_type { - static auto constexpr value = conjunction< - negation<std::is_same<void, CompatibleArrayType>>, - negation<is_compatible_object_type<BasicJsonType, CompatibleArrayType>>, + static auto constexpr value = + conjunction<negation<std::is_same<void, CompatibleArrayType>>, + negation<is_compatible_object_type< + BasicJsonType, CompatibleArrayType>>, negation<std::is_constructible<typename BasicJsonType::string_t, - CompatibleArrayType>>, + CompatibleArrayType>>, negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>, has_value_type<CompatibleArrayType>, has_iterator<CompatibleArrayType>>::value; }; -template <bool, typename, typename> -struct is_compatible_integer_type_impl : std::false_type -{ -}; +template<bool, typename, typename> +struct is_compatible_integer_type_impl : std::false_type {}; -template <typename RealIntegerType, typename CompatibleNumberIntegerType> -struct is_compatible_integer_type_impl<true, RealIntegerType, - CompatibleNumberIntegerType> +template<typename RealIntegerType, typename CompatibleNumberIntegerType> +struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType> { // is there an assert somewhere on overflows? using RealLimits = std::numeric_limits<RealIntegerType>; @@ -835,364 +453,329 @@ struct is_compatible_integer_type_impl<true, RealIntegerType, static constexpr auto value = std::is_constructible<RealIntegerType, - CompatibleNumberIntegerType>::value and + CompatibleNumberIntegerType>::value and CompatibleLimits::is_integer and RealLimits::is_signed == CompatibleLimits::is_signed; }; -template <typename RealIntegerType, typename CompatibleNumberIntegerType> +template<typename RealIntegerType, typename CompatibleNumberIntegerType> struct is_compatible_integer_type { - static constexpr auto - value = is_compatible_integer_type_impl < - std::is_integral<CompatibleNumberIntegerType>::value and - not std::is_same<bool, CompatibleNumberIntegerType>::value, + static constexpr auto value = + is_compatible_integer_type_impl < + std::is_integral<CompatibleNumberIntegerType>::value and + not std::is_same<bool, CompatibleNumberIntegerType>::value, RealIntegerType, CompatibleNumberIntegerType > ::value; }; + // trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists -template <typename BasicJsonType, typename T> +template<typename BasicJsonType, typename T> struct has_from_json { -private: + private: // also check the return type of from_json - template <typename U, typename = enable_if_t<std::is_same< - void, decltype(uncvref_t<U>::from_json( - std::declval<BasicJsonType>(), - std::declval<T &>()))>::value>> - static int detect(U &&); + template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json( + std::declval<BasicJsonType>(), std::declval<T&>()))>::value>> + static int detect(U&&); static void detect(...); -public: + public: static constexpr bool value = std::is_integral<decltype( - detect(std::declval<typename BasicJsonType::template json_serializer< - T, void>>()))>::value; + detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value; }; // This trait checks if JSONSerializer<T>::from_json(json const&) exists // this overload is used for non-default-constructible user-defined-types -template <typename BasicJsonType, typename T> +template<typename BasicJsonType, typename T> struct has_non_default_from_json { -private: - template <typename U, typename = enable_if_t<std::is_same< - T, decltype(uncvref_t<U>::from_json( - std::declval<BasicJsonType>()))>::value>> - static int detect(U &&); + private: + template < + typename U, + typename = enable_if_t<std::is_same< + T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >> + static int detect(U&&); static void detect(...); -public: - static constexpr bool value = std::is_integral<decltype( - detect(std::declval<typename BasicJsonType::template json_serializer< - T, void>>()))>::value; + public: + static constexpr bool value = std::is_integral<decltype(detect( + std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value; }; // This trait checks if BasicJsonType::json_serializer<T>::to_json exists -template <typename BasicJsonType, typename T> +template<typename BasicJsonType, typename T> struct has_to_json { -private: - template <typename U, - typename = decltype(uncvref_t<U>::to_json( - std::declval<BasicJsonType &>(), std::declval<T>()))> - static int detect(U &&); + private: + template<typename U, typename = decltype(uncvref_t<U>::to_json( + std::declval<BasicJsonType&>(), std::declval<T>()))> + static int detect(U&&); static void detect(...); -public: - static constexpr bool value = std::is_integral<decltype( - detect(std::declval<typename BasicJsonType::template json_serializer< - T, void>>()))>::value; + public: + static constexpr bool value = std::is_integral<decltype(detect( + std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value; }; + ///////////// // to_json // ///////////// -template <typename BasicJsonType, typename T, - enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, - int> = 0> -void to_json(BasicJsonType &j, T b) noexcept +template<typename BasicJsonType, typename T, enable_if_t< + std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept { external_constructor<value_t::boolean>::construct(j, b); } -template <typename BasicJsonType, typename CompatibleString, - enable_if_t<std::is_constructible<typename BasicJsonType::string_t, - CompatibleString>::value, - int> = 0> -void to_json(BasicJsonType &j, const CompatibleString &s) +template<typename BasicJsonType, typename CompatibleString, + enable_if_t<std::is_constructible<typename BasicJsonType::string_t, + CompatibleString>::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) { external_constructor<value_t::string>::construct(j, s); } -template <typename BasicJsonType, typename FloatType, - enable_if_t<std::is_floating_point<FloatType>::value, int> = 0> -void to_json(BasicJsonType &j, FloatType val) noexcept -{ - external_constructor<value_t::number_float>::construct( - j, static_cast<typename BasicJsonType::number_float_t>(val)); -} - -template <typename BasicJsonType, typename CompatibleNumberUnsignedType, - enable_if_t<is_compatible_integer_type< - typename BasicJsonType::number_unsigned_t, - CompatibleNumberUnsignedType>::value, - int> = 0> -void to_json(BasicJsonType &j, CompatibleNumberUnsignedType val) noexcept +template<typename BasicJsonType, typename FloatType, + enable_if_t<std::is_floating_point<FloatType>::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept { - external_constructor<value_t::number_unsigned>::construct( - j, static_cast<typename BasicJsonType::number_unsigned_t>(val)); + external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val)); } -template <typename BasicJsonType, typename CompatibleNumberIntegerType, - enable_if_t<is_compatible_integer_type< - typename BasicJsonType::number_integer_t, - CompatibleNumberIntegerType>::value, - int> = 0> -void to_json(BasicJsonType &j, CompatibleNumberIntegerType val) noexcept +template < + typename BasicJsonType, typename CompatibleNumberUnsignedType, + enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, + CompatibleNumberUnsignedType>::value, int> = 0 > +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept { - external_constructor<value_t::number_integer>::construct( - j, static_cast<typename BasicJsonType::number_integer_t>(val)); + external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val)); } -template <typename BasicJsonType, typename EnumType, - enable_if_t<std::is_enum<EnumType>::value, int> = 0> -void to_json(BasicJsonType &j, EnumType e) noexcept +template < + typename BasicJsonType, typename CompatibleNumberIntegerType, + enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, + CompatibleNumberIntegerType>::value, int> = 0 > +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept { - using underlying_type = typename std::underlying_type<EnumType>::type; - external_constructor<value_t::number_integer>::construct( - j, static_cast<underlying_type>(e)); + external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val)); } -template <typename BasicJsonType> -void to_json(BasicJsonType &j, const std::vector<bool> &e) +template<typename BasicJsonType, typename UnscopedEnumType, + enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0> +void to_json(BasicJsonType& j, UnscopedEnumType e) noexcept { - external_constructor<value_t::array>::construct(j, e); + external_constructor<value_t::number_integer>::construct(j, e); } -template <typename BasicJsonType, typename CompatibleArrayType, - enable_if_t<is_compatible_array_type<BasicJsonType, - CompatibleArrayType>::value or - std::is_same<typename BasicJsonType::array_t, - CompatibleArrayType>::value, - int> = 0> -void to_json(BasicJsonType &j, const CompatibleArrayType &arr) +template < + typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < + is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or + std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor<value_t::array>::construct(j, arr); } -template <typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, - CompatibleObjectType>::value, - int> = 0> -void to_json(BasicJsonType &j, const CompatibleObjectType &arr) +template < + typename BasicJsonType, typename CompatibleObjectType, + enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, + int> = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& arr) { external_constructor<value_t::object>::construct(j, arr); } -template <typename BasicJsonType, typename T, std::size_t N, - enable_if_t<not std::is_constructible< - typename BasicJsonType::string_t, T (&)[N]>::value, - int> = 0> -void to_json(BasicJsonType &j, T (&arr)[N]) -{ - external_constructor<value_t::array>::construct(j, arr); -} /////////////// // from_json // /////////////// // overloads for basic_json template parameters -template < - typename BasicJsonType, typename ArithmeticType, - enable_if_t<std::is_arithmetic<ArithmeticType>::value and - not std::is_same<ArithmeticType, - typename BasicJsonType::boolean_t>::value, - int> = 0> -void get_arithmetic_value(const BasicJsonType &j, ArithmeticType &val) +template<typename BasicJsonType, typename ArithmeticType, + enable_if_t<std::is_arithmetic<ArithmeticType>::value and + not std::is_same<ArithmeticType, + typename BasicJsonType::boolean_t>::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast<value_t>(j)) { - case value_t::number_unsigned: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr< - const typename BasicJsonType::number_unsigned_t *>()); - break; - } - case value_t::number_integer: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr< - const typename BasicJsonType::number_integer_t *>()); - break; - } - case value_t::number_float: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr< - const typename BasicJsonType::number_float_t *>()); - break; - } - default: - { - JSON_THROW(type_error::create(302, "type must be number, but is " + - j.type_name())); - } + case value_t::number_unsigned: + { + val = static_cast<ArithmeticType>( + *j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>()); + break; + } + case value_t::number_integer: + { + val = static_cast<ArithmeticType>( + *j.template get_ptr<const typename BasicJsonType::number_integer_t*>()); + break; + } + case value_t::number_float: + { + val = static_cast<ArithmeticType>( + *j.template get_ptr<const typename BasicJsonType::number_float_t*>()); + break; + } + default: + { + JSON_THROW( + std::domain_error("type must be number, but is " + j.type_name())); + } } } -template <typename BasicJsonType> -void from_json(const BasicJsonType &j, typename BasicJsonType::boolean_t &b) +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { if (not j.is_boolean()) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + - j.type_name())); + JSON_THROW(std::domain_error("type must be boolean, but is " + j.type_name())); } - b = *j.template get_ptr<const typename BasicJsonType::boolean_t *>(); + b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); } -template <typename BasicJsonType> -void from_json(const BasicJsonType &j, typename BasicJsonType::string_t &s) +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { if (not j.is_string()) { - JSON_THROW(type_error::create(302, "type must be string, but is " + - j.type_name())); + JSON_THROW(std::domain_error("type must be string, but is " + j.type_name())); } - s = *j.template get_ptr<const typename BasicJsonType::string_t *>(); + s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); } -template <typename BasicJsonType> -void from_json(const BasicJsonType &j, - typename BasicJsonType::number_float_t &val) +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) { get_arithmetic_value(j, val); } -template <typename BasicJsonType> -void from_json(const BasicJsonType &j, - typename BasicJsonType::number_unsigned_t &val) +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) { get_arithmetic_value(j, val); } -template <typename BasicJsonType> -void from_json(const BasicJsonType &j, - typename BasicJsonType::number_integer_t &val) +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) { get_arithmetic_value(j, val); } -template <typename BasicJsonType, typename EnumType, - enable_if_t<std::is_enum<EnumType>::value, int> = 0> -void from_json(const BasicJsonType &j, EnumType &e) +template<typename BasicJsonType, typename UnscopedEnumType, + enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0> +void from_json(const BasicJsonType& j, UnscopedEnumType& e) { - typename std::underlying_type<EnumType>::type val; + typename std::underlying_type<UnscopedEnumType>::type val; get_arithmetic_value(j, val); - e = static_cast<EnumType>(val); + e = static_cast<UnscopedEnumType>(val); } -template <typename BasicJsonType> -void from_json(const BasicJsonType &j, typename BasicJsonType::array_t &arr) +template<typename BasicJsonType> +void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) { if (not j.is_array()) { - JSON_THROW(type_error::create(302, "type must be array, but is " + - j.type_name())); + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); } - arr = *j.template get_ptr<const typename BasicJsonType::array_t *>(); + arr = *j.template get_ptr<const typename BasicJsonType::array_t*>(); } // forward_list doesn't have an insert method -template <typename BasicJsonType, typename T, typename Allocator, - enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> -void from_json(const BasicJsonType &j, std::forward_list<T, Allocator> &l) +template<typename BasicJsonType, typename T, typename Allocator> +void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) { - if (not j.is_array()) + // do not perform the check when user wants to retrieve jsons + // (except when it's null.. ?) + if (j.is_null()) { - JSON_THROW(type_error::create(302, "type must be array, but is " + - j.type_name())); + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + if (not std::is_same<T, BasicJsonType>::value) + { + if (not j.is_array()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } } - for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) { l.push_front(it->template get<T>()); } } -template <typename BasicJsonType, typename CompatibleArrayType> -void from_json_array_impl(const BasicJsonType &j, CompatibleArrayType &arr, - priority_tag<0>) +template<typename BasicJsonType, typename CompatibleArrayType> +void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0>) { using std::begin; using std::end; - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), - [](const BasicJsonType &i) { - // get<BasicJsonType>() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); - }); + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) + { + // get<BasicJsonType>() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get<typename CompatibleArrayType::value_type>(); + }); } -template <typename BasicJsonType, typename CompatibleArrayType> -auto from_json_array_impl(const BasicJsonType &j, CompatibleArrayType &arr, - priority_tag<1>) - -> decltype( - arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), - void()) +template<typename BasicJsonType, typename CompatibleArrayType> +auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1>) +-> decltype( + arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), + void()) { using std::begin; using std::end; arr.reserve(j.size()); std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), - [](const BasicJsonType &i) { - // get<BasicJsonType>() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get<typename CompatibleArrayType::value_type>(); - }); + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i) + { + // get<BasicJsonType>() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get<typename CompatibleArrayType::value_type>(); + }); } -template < - typename BasicJsonType, typename CompatibleArrayType, - enable_if_t< - is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and - std::is_convertible<BasicJsonType, typename CompatibleArrayType:: - value_type>::value and - not std::is_same<typename BasicJsonType::array_t, - CompatibleArrayType>::value, - int> = 0> -void from_json(const BasicJsonType &j, CompatibleArrayType &arr) +template<typename BasicJsonType, typename CompatibleArrayType, + enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and + not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0> +void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { - if (not j.is_array()) + if (j.is_null()) { - JSON_THROW(type_error::create(302, "type must be array, but is " + - j.type_name())); + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); } - from_json_array_impl(j, arr, priority_tag<1>{}); + // when T == BasicJsonType, do not check if value_t is correct + if (not std::is_same<typename CompatibleArrayType::value_type, BasicJsonType>::value) + { + if (not j.is_array()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + } + from_json_array_impl(j, arr, priority_tag<1> {}); } -template <typename BasicJsonType, typename CompatibleObjectType, - enable_if_t<is_compatible_object_type<BasicJsonType, - CompatibleObjectType>::value, - int> = 0> -void from_json(const BasicJsonType &j, CompatibleObjectType &obj) +template<typename BasicJsonType, typename CompatibleObjectType, + enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0> +void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { if (not j.is_object()) { - JSON_THROW(type_error::create(302, "type must be object, but is " + - j.type_name())); + JSON_THROW(std::domain_error("type must be object, but is " + j.type_name())); } - auto inner_object = - j.template get_ptr<const typename BasicJsonType::object_t *>(); + auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>(); using std::begin; using std::end; // we could avoid the assignment, but this might require a for loop, which @@ -1205,134 +788,118 @@ void from_json(const BasicJsonType &j, CompatibleObjectType &obj) // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? -template < - typename BasicJsonType, typename ArithmeticType, - enable_if_t< - std::is_arithmetic<ArithmeticType>::value and - not std::is_same<ArithmeticType, typename BasicJsonType:: - number_unsigned_t>::value and - not std::is_same<ArithmeticType, typename BasicJsonType:: - number_integer_t>::value and - not std::is_same<ArithmeticType, - typename BasicJsonType::number_float_t>::value and - not std::is_same<ArithmeticType, - typename BasicJsonType::boolean_t>::value, - int> = 0> -void from_json(const BasicJsonType &j, ArithmeticType &val) +template<typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic<ArithmeticType>::value and + not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and + not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and + not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and + not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast<value_t>(j)) { - case value_t::number_unsigned: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr< - const typename BasicJsonType::number_unsigned_t *>()); - break; - } - case value_t::number_integer: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr< - const typename BasicJsonType::number_integer_t *>()); - break; - } - case value_t::number_float: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr< - const typename BasicJsonType::number_float_t *>()); - break; - } - case value_t::boolean: - { - val = static_cast<ArithmeticType>( - *j.template get_ptr<const typename BasicJsonType::boolean_t *>()); - break; - } - default: - { - JSON_THROW(type_error::create(302, "type must be number, but is " + - j.type_name())); - } + case value_t::number_unsigned: + { + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>()); + break; + } + case value_t::number_integer: + { + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>()); + break; + } + case value_t::number_float: + { + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>()); + break; + } + case value_t::boolean: + { + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>()); + break; + } + default: + { + JSON_THROW(std::domain_error("type must be number, but is " + j.type_name())); + } } } struct to_json_fn { -private: - template <typename BasicJsonType, typename T> - auto call(BasicJsonType &j, T &&val, priority_tag<1>) const - noexcept(noexcept(to_json(j, std::forward<T>(val)))) - -> decltype(to_json(j, std::forward<T>(val)), void()) + private: + template<typename BasicJsonType, typename T> + auto call(BasicJsonType& j, T&& val, priority_tag<1>) const noexcept(noexcept(to_json(j, std::forward<T>(val)))) + -> decltype(to_json(j, std::forward<T>(val)), void()) { return to_json(j, std::forward<T>(val)); } - template <typename BasicJsonType, typename T> - void call(BasicJsonType &, T &&, priority_tag<0>) const noexcept + template<typename BasicJsonType, typename T> + void call(BasicJsonType&, T&&, priority_tag<0>) const noexcept { static_assert(sizeof(BasicJsonType) == 0, "could not find to_json() method in T's namespace"); } -public: - template <typename BasicJsonType, typename T> - void operator()(BasicJsonType &j, T &&val) const - noexcept(noexcept(std::declval<to_json_fn>().call(j, - std::forward<T>(val), - priority_tag<1>{}))) + public: + template<typename BasicJsonType, typename T> + void operator()(BasicJsonType& j, T&& val) const + noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {}))) { - return call(j, std::forward<T>(val), priority_tag<1>{}); + return call(j, std::forward<T>(val), priority_tag<1> {}); } }; struct from_json_fn { -private: - template <typename BasicJsonType, typename T> - auto call(const BasicJsonType &j, T &val, priority_tag<1>) const - noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) + private: + template<typename BasicJsonType, typename T> + auto call(const BasicJsonType& j, T& val, priority_tag<1>) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) { return from_json(j, val); } - template <typename BasicJsonType, typename T> - void call(const BasicJsonType &, T &, priority_tag<0>) const noexcept + template<typename BasicJsonType, typename T> + void call(const BasicJsonType&, T&, priority_tag<0>) const noexcept { static_assert(sizeof(BasicJsonType) == 0, "could not find from_json() method in T's namespace"); } -public: - template <typename BasicJsonType, typename T> - void operator()(const BasicJsonType &j, T &val) const - noexcept(noexcept(std::declval<from_json_fn>().call(j, val, - priority_tag<1>{}))) + public: + template<typename BasicJsonType, typename T> + void operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {}))) { - return call(j, val, priority_tag<1>{}); + return call(j, val, priority_tag<1> {}); } }; // taken from ranges-v3 -template <typename T> +template<typename T> struct static_const { static constexpr T value{}; }; -template <typename T> +template<typename T> constexpr T static_const<T>::value; } // namespace detail + /// namespace to hold default `to_json` / `from_json` functions namespace { -constexpr const auto &to_json = detail::static_const<detail::to_json_fn>::value; -constexpr const auto &from_json = - detail::static_const<detail::from_json_fn>::value; +constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; +constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; } + /*! @brief default JSONSerializer template argument @@ -1340,7 +907,7 @@ This serializer ignores the template arguments and uses ADL ([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) for serialization. */ -template <typename = void, typename = void> +template<typename = void, typename = void> struct adl_serializer { /*! @@ -1352,8 +919,8 @@ struct adl_serializer @param[in] j JSON value to read from @param[in,out] val value to write to */ - template <typename BasicJsonType, typename ValueType> - static void from_json(BasicJsonType &&j, ValueType &val) noexcept( + template<typename BasicJsonType, typename ValueType> + static void from_json(BasicJsonType&& j, ValueType& val) noexcept( noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val))) { ::nlohmann::from_json(std::forward<BasicJsonType>(j), val); @@ -1368,14 +935,15 @@ struct adl_serializer @param[in,out] j JSON value to write to @param[in] val value to read from */ - template <typename BasicJsonType, typename ValueType> - static void to_json(BasicJsonType &j, ValueType &&val) noexcept( + template<typename BasicJsonType, typename ValueType> + static void to_json(BasicJsonType& j, ValueType&& val) noexcept( noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val)))) { ::nlohmann::to_json(j, std::forward<ValueType>(val)); } }; + /*! @brief a class to store JSON values @@ -1400,15 +968,12 @@ and `from_json()` (@ref adl_serializer by default) @requirement The class satisfies the following concept requirements: - Basic - - -[DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): + - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): JSON values can be default constructed. The result will be a JSON null value. - - -[MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): + - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): A JSON value can be constructed from an rvalue argument. - - -[CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): + - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): A JSON value can be copy-constructed from an lvalue expression. - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): A JSON value van be assigned from an rvalue argument. @@ -1417,20 +982,16 @@ and `from_json()` (@ref adl_serializer by default) - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): JSON values can be destructed. - Layout - - -[StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): + - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): JSON values have - [standard -layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): All non-static data members are private and standard layout types, the class has no virtual functions or (virtual) base classes. - Library-wide - - -[EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): + - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): JSON values can be compared with `==`, see @ref operator==(const_reference,const_reference). - - -[LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): + - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): JSON values can be compared with `<`, see @ref operator<(const_reference,const_reference). - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): @@ -1442,8 +1003,7 @@ layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - Container - [Container](http://en.cppreference.com/w/cpp/concept/Container): JSON values can be used like STL containers and provide iterator access. - - -[ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); + - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); JSON values can be used like STL containers and provide reverse iterator access. @@ -1465,61 +1025,35 @@ Format](http://rfc7159.net/rfc7159) @nosubgrouping */ -template <template <typename U, typename V, typename... Args> class ObjectType = - std::map, - template <typename U, typename... Args> class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template <typename U> class AllocatorType = std::allocator, - template <typename T, typename SFINAE = void> class JSONSerializer = - adl_serializer> +template < + template<typename U, typename V, typename... Args> class ObjectType = std::map, + template<typename U, typename... Args> class ArrayType = std::vector, + class StringType = std::string, + class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template<typename U> class AllocatorType = std::allocator, + template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer + > class basic_json { -private: - template <detail::value_t> - friend struct detail::external_constructor; + private: + template<detail::value_t> friend struct detail::external_constructor; /// workaround type for MSVC - using basic_json_t = - basic_json<ObjectType, ArrayType, StringType, BooleanType, - NumberIntegerType, NumberUnsignedType, NumberFloatType, - AllocatorType, JSONSerializer>; + using basic_json_t = basic_json<ObjectType, ArrayType, StringType, + BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, + AllocatorType, JSONSerializer>; -public: + public: using value_t = detail::value_t; // forward declarations - template <typename U> - class iter_impl; - template <typename Base> - class json_reverse_iterator; + template<typename U> class iter_impl; + template<typename Base> class json_reverse_iterator; class json_pointer; - template <typename T, typename SFINAE> + template<typename T, typename SFINAE> using json_serializer = JSONSerializer<T, SFINAE>; - //////////////// - // exceptions // - //////////////// - - /// @name exceptions - /// Classes to implement user-defined exceptions. - /// @{ - - /// @copydoc detail::exception - using exception = detail::exception; - /// @copydoc detail::parse_error - using parse_error = detail::parse_error; - /// @copydoc detail::invalid_iterator - using invalid_iterator = detail::invalid_iterator; - /// @copydoc detail::type_error - using type_error = detail::type_error; - /// @copydoc detail::out_of_range - using out_of_range = detail::out_of_range; - /// @copydoc detail::other_error - using other_error = detail::other_error; - - /// @} - ///////////////////// // container types // ///////////////////// @@ -1533,9 +1067,9 @@ public: using value_type = basic_json; /// the type of an element reference - using reference = value_type &; + using reference = value_type&; /// the type of an element const reference - using const_reference = const value_type &; + using const_reference = const value_type&; /// a type to represent differences between iterators using difference_type = std::ptrdiff_t; @@ -1548,26 +1082,27 @@ public: /// the type of an element pointer using pointer = typename std::allocator_traits<allocator_type>::pointer; /// the type of an element const pointer - using const_pointer = - typename std::allocator_traits<allocator_type>::const_pointer; + using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer; /// an iterator for a basic_json container using iterator = iter_impl<basic_json>; /// a const iterator for a basic_json container 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>; + using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>; /// a const reverse iterator for a basic_json container - using const_reverse_iterator = - json_reverse_iterator<typename basic_json::const_iterator>; + using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>; /// @} + /*! @brief returns the allocator associated with the container */ - static allocator_type get_allocator() { return allocator_type(); } + static allocator_type get_allocator() + { + return allocator_type(); + } /*! @brief returns version information on the library @@ -1578,18 +1113,12 @@ public: @return JSON object holding version information key | description ----------- | --------------- - `compiler` | Information on the used compiler. It is an object with the - following keys: `c++` (the used C++ standard), `family` (the compiler - family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, - `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). `copyright` | The copyright line for the library as string. `name` | The name of the library as string. - `platform` | The used platform as string. Possible values are `win32`, - `linux`, `apple`, `unix`, and `unknown`. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. `url` | The URL of the project as string. - `version` | The version of the library. It is an object with the following - keys: `major`, `minor`, and `patch` as defined by [Semantic - Versioning](http://semver.org), and `string` (the version string). + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). @liveexample{The following code shows an example output of the `meta()` function.,meta} @@ -1605,8 +1134,13 @@ public: result["copyright"] = "(C) 2013-2017 Niels Lohmann"; result["name"] = "JSON for Modern C++"; result["url"] = "https://github.com/nlohmann/json"; - result["version"] = { - {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1}}; + result["version"] = + { + {"string", "2.1.1"}, + {"major", 2}, + {"minor", 1}, + {"patch", 1} + }; #ifdef _WIN32 result["platform"] = "win32"; @@ -1621,16 +1155,11 @@ public: #endif #if defined(__clang__) - result["compiler"] = {{"family", "clang"}, - {"version", __clang_version__}}; + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; #elif defined(__ICC) || defined(__INTEL_COMPILER) result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; #elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = { - {"family", "gcc"}, - {"version", std::to_string(__GNUC__) + "." + - std::to_string(__GNUC_MINOR__) + "." + - std::to_string(__GNUC_PATCHLEVEL__)}}; + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; #elif defined(__HP_cc) || defined(__HP_aCC) result["compiler"] = "hp" #elif defined(__IBMCPP__) @@ -1653,6 +1182,7 @@ public: return result; } + /////////////////////////// // JSON value data types // /////////////////////////// @@ -1745,9 +1275,11 @@ public: 7159](http://rfc7159.net/rfc7159), because any order implements the specified "unordered" nature of JSON objects. */ - using object_t = - ObjectType<StringType, basic_json, std::less<StringType>, - AllocatorType<std::pair<const StringType, basic_json>>>; + using object_t = ObjectType<StringType, + basic_json, + std::less<StringType>, + AllocatorType<std::pair<const StringType, + basic_json>>>; /*! @brief a type for an array @@ -2087,15 +1619,18 @@ public: /// @} -private: + private: + /// helper for exception-safe object creation - template <typename T, typename... Args> - static T *create(Args &&... args) + template<typename T, typename... Args> + static T* create(Args&& ... args) { AllocatorType<T> alloc; - auto deleter = [&](T *object) { alloc.deallocate(object, 1); }; - std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), - deleter); + auto deleter = [&](T * object) + { + alloc.deallocate(object, 1); + }; + std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter); alloc.construct(object.get(), std::forward<Args>(args)...); assert(object != nullptr); return object.release(); @@ -2129,13 +1664,14 @@ private: @since version 1.0.0 */ - union json_value { + union json_value + { /// object (stored with pointer to save storage) - object_t *object; + object_t* object; /// array (stored with pointer to save storage) - array_t *array; + array_t* array; /// string (stored with pointer to save storage) - string_t *string; + string_t* string; /// boolean boolean_t boolean; /// number (integer) @@ -2160,75 +1696,81 @@ private: { switch (t) { - case value_t::object: - { - object = create<object_t>(); - break; - } + case value_t::object: + { + object = create<object_t>(); + break; + } - case value_t::array: - { - array = create<array_t>(); - break; - } + case value_t::array: + { + array = create<array_t>(); + break; + } - case value_t::string: - { - string = create<string_t>(""); - break; - } + case value_t::string: + { + string = create<string_t>(""); + break; + } - case value_t::boolean: - { - boolean = boolean_t(false); - break; - } + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } - case value_t::number_integer: - { - number_integer = number_integer_t(0); - break; - } + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } - case value_t::number_unsigned: - { - number_unsigned = number_unsigned_t(0); - break; - } + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } - case value_t::number_float: - { - number_float = number_float_t(0.0); - break; - } + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } - case value_t::null: - { - break; - } + case value_t::null: + { + break; + } - default: - { - if (t == value_t::null) + default: { - JSON_THROW( - other_error::create(500, - "961c151d2e87f2686a955a9be24d316f13" - "62bf21 2.1.1")); // LCOV_EXCL_LINE + if (t == value_t::null) + { + JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + } + break; } - break; - } } } /// constructor for strings - json_value(const string_t &value) { string = create<string_t>(value); } + json_value(const string_t& value) + { + string = create<string_t>(value); + } /// constructor for objects - json_value(const object_t &value) { object = create<object_t>(value); } + json_value(const object_t& value) + { + object = create<object_t>(value); + } /// constructor for arrays - json_value(const array_t &value) { array = create<array_t>(value); } + json_value(const array_t& value) + { + array = create<array_t>(value); + } }; /*! @@ -2247,7 +1789,7 @@ private: assert(m_type != value_t::string or m_value.string != nullptr); } -public: + public: ////////////////////////// // JSON parser callback // ////////////////////////// @@ -2258,8 +1800,7 @@ public: 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" + @image html callback_events.png "Example when certain parse events are triggered" @since version 1.0.0 */ @@ -2297,24 +1838,14 @@ public: parameter @a event | description | parameter @a depth | parameter @a parsed ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a - JSON object | depth of the parent of the JSON object | a JSON value with - type discarded - parse_event_t::key | the parser read a key of a value in an object | depth - of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a - JSON object | depth of the parent of the JSON object | the parsed JSON - object - parse_event_t::array_start | the parser read `[` and started to process a - JSON array | depth of the parent of the JSON array | a JSON value with type - discarded - 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" + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + 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: @@ -2341,8 +1872,10 @@ public: @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)>; + ////////////////// // constructors // @@ -2372,13 +1905,16 @@ public: @complexity Constant. + @throw std::bad_alloc if allocation for object, array, or string value + fails + @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} @since version 1.0.0 */ basic_json(const value_t value_type) - : m_type(value_type), m_value(value_type) + : m_type(value_type), m_value(value_type) { assert_invariant(); } @@ -2401,7 +1937,8 @@ public: @since version 1.0.0 */ - basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) { assert_invariant(); } @@ -2459,17 +1996,15 @@ public: @since version 2.1.0 */ - template < - typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>, - detail::enable_if_t<not std::is_base_of<std::istream, U>::value and - not std::is_same<U, basic_json_t>::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json<basic_json, U>::value, - int> = 0> - basic_json(CompatibleType &&val) noexcept( - noexcept(JSONSerializer<U>::to_json(std::declval<basic_json_t &>(), - std::forward<CompatibleType>(val)))) + template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>, + detail::enable_if_t<not std::is_base_of<std::istream, U>::value and + not std::is_same<U, basic_json_t>::value and + not detail::is_basic_json_nested_type< + basic_json_t, U>::value and + detail::has_to_json<basic_json, U>::value, + int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json( + std::declval<basic_json_t&>(), std::forward<CompatibleType>(val)))) { JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val)); assert_invariant(); @@ -2527,12 +2062,10 @@ public: value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect - @throw type_error.301 if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string. In this case, the constructor could not - create an object. If @a type_deduction would have be `true`, an array - would have been created. See @ref object(std::initializer_list<basic_json>) - for an example. + @throw std::domain_error if @a type_deduction is `false`, @a manual_type + is `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string; example: `"cannot create object from + initializer list"` @complexity Linear in the size of the initializer list @a init. @@ -2547,15 +2080,16 @@ public: @since version 1.0.0 */ basic_json(std::initializer_list<basic_json> init, - bool type_deduction = true, value_t manual_type = value_t::array) + bool type_deduction = true, + value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string - bool is_an_object = std::all_of( - init.begin(), init.end(), [](const basic_json &element) { - return element.is_array() and element.size() == 2 and - element[0].is_string(); - }); + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json & element) + { + 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) @@ -2569,8 +2103,7 @@ public: // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { - JSON_THROW(type_error::create( - 301, "cannot create object from initializer list")); + JSON_THROW(std::domain_error("cannot create object from initializer list")); } } @@ -2580,11 +2113,10 @@ public: m_type = value_t::object; m_value = value_t::object; - std::for_each(init.begin(), init.end(), - [this](const basic_json &element) { - m_value.object->emplace( - *(element[0].m_value.string), element[1]); - }); + std::for_each(init.begin(), init.end(), [this](const basic_json & element) + { + m_value.object->emplace(*(element[0].m_value.string), element[1]); + }); } else { @@ -2654,12 +2186,9 @@ public: @return JSON object value - @throw type_error.301 if @a init is not a list of pairs whose first - elements are strings. In this case, no object can be created. When such a - value is passed to @ref basic_json(std::initializer_list<basic_json>, bool, - value_t), - an array would have been created from the passed initializer list @a init. - See example below. + @throw std::domain_error if @a init is not a pair whose first elements are + strings; thrown by + @ref basic_json(std::initializer_list<basic_json>, bool, value_t) @complexity Linear in the size of @a init. @@ -2697,7 +2226,8 @@ public: @since version 1.0.0 */ - basic_json(size_type cnt, const basic_json &val) : m_type(value_t::array) + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) { m_value.array = create<array_t>(cnt, val); assert_invariant(); @@ -2710,10 +2240,10 @@ public: The semantics depends on the different types a JSON value can have: - In case of primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, invalid_iterator.204 is thrown. + copied. Otherwise, std::out_of_range is thrown. - In case of structured types (array, object), the constructor behaves as similar versions for `std::vector`. - - In case of a null type, invalid_iterator.206 is thrown. + - In case of a null type, std::domain_error is thrown. @tparam InputIT an input iterator type (@ref iterator or @ref const_iterator) @@ -2724,19 +2254,14 @@ public: @pre Iterators @a first and @a last must be initialized. **This precondition is enforced with an assertion.** - @pre Range `[first, last)` is valid. Usually, this precondition cannot be - checked efficiently. Only certain edge cases are detected; see the - description of the exceptions below. - - @throw invalid_iterator.201 if iterators @a first and @a last are not - compatible (i.e., do not belong to the same JSON value). In this case, - the range `[first, last)` is undefined. - @throw invalid_iterator.204 if iterators @a first and @a last belong to a - primitive type (number, boolean, or string), but @a first does not point - to the first element any more. In this case, the range `[first, last)` is - undefined. See example code below. - @throw invalid_iterator.206 if iterators @a first and @a last belong to a - null value. In this case, the range `[first, last)` is undefined. + @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, + boolean, or string) where an out of range error can be detected easily; + example: `"iterators out of range"` + @throw std::bad_alloc if allocation for object, array, or string fails + @throw std::domain_error if called with a null value; example: `"cannot + use construct with iterators from null"` @complexity Linear in distance between @a first and @a last. @@ -2745,13 +2270,9 @@ public: @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> + 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); @@ -2760,8 +2281,7 @@ public: // make sure iterator fits the current value if (first.m_object != last.m_object) { - JSON_THROW( - invalid_iterator::create(201, "iterators are not compatible")); + JSON_THROW(std::domain_error("iterators are not compatible")); } // copy type from first iterator @@ -2770,84 +2290,115 @@ public: // check if iterator range is complete for primitive values switch (m_type) { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or - not last.m_it.primitive_iterator.is_end()) + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: { - JSON_THROW( - invalid_iterator::create(204, "iterators out of range")); + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + JSON_THROW(std::out_of_range("iterators out of range")); + } + break; } - break; - } - default: - { - break; - } + default: + { + break; + } } switch (m_type) { - case value_t::number_integer: - { - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } - case value_t::number_unsigned: - { - m_value.number_unsigned = first.m_object->m_value.number_unsigned; - break; - } + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } - case value_t::number_float: - { - m_value.number_float = first.m_object->m_value.number_float; - break; - } + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } - case value_t::boolean: - { - m_value.boolean = first.m_object->m_value.boolean; - break; - } + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } - case value_t::string: - { - m_value = *first.m_object->m_value.string; - break; - } + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } - case value_t::object: - { - m_value.object = create<object_t>(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } + case value_t::object: + { + m_value.object = create<object_t>(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } - case value_t::array: - { - m_value.array = create<array_t>(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } + case value_t::array: + { + m_value.array = create<array_t>(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } - default: - { - JSON_THROW(invalid_iterator::create( - 206, "cannot construct with iterators from " + - first.m_object->type_name())); - } + default: + { + JSON_THROW(std::domain_error("cannot use construct with iterators from " + first.m_object->type_name())); + } } assert_invariant(); } + /*! + @brief construct a JSON value given an input stream + + @param[in,out] i stream 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) + + @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. + + @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, deprecated in version 2.0.3, to be removed in + version 3.0.0 + */ + JSON_DEPRECATED + explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) + { + *this = parser(i, cb).parse(); + assert_invariant(); + } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// @@ -2867,64 +2418,67 @@ public: - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. + @throw std::bad_alloc if allocation for object, array, or string fails. + @liveexample{The following code shows an example for the copy constructor.,basic_json__basic_json} @since version 1.0.0 */ - basic_json(const basic_json &other) : m_type(other.m_type) + 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: - { - m_value = *other.m_value.object; - break; - } + case value_t::object: + { + m_value = *other.m_value.object; + break; + } - case value_t::array: - { - m_value = *other.m_value.array; - break; - } + case value_t::array: + { + m_value = *other.m_value.array; + break; + } - case value_t::string: - { - m_value = *other.m_value.string; - break; - } + case value_t::string: + { + m_value = *other.m_value.string; + break; + } - case value_t::boolean: - { - m_value = other.m_value.boolean; - break; - } + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } - case value_t::number_integer: - { - m_value = other.m_value.number_integer; - break; - } + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } - case value_t::number_unsigned: - { - m_value = other.m_value.number_unsigned; - break; - } + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } - case value_t::number_float: - { - m_value = other.m_value.number_float; - break; - } + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } - default: - { - break; - } + default: + { + break; + } } assert_invariant(); @@ -2948,8 +2502,9 @@ public: @since version 1.0.0 */ - basic_json(basic_json &&other) noexcept : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) { // check that passed value is valid other.assert_invariant(); @@ -2984,11 +2539,12 @@ public: @since version 1.0.0 */ - reference &operator=(basic_json other) noexcept( - std::is_nothrow_move_constructible<value_t>::value - and std::is_nothrow_move_assignable<value_t>::value - and std::is_nothrow_move_constructible<json_value>::value - and std::is_nothrow_move_assignable<json_value>::value) + reference& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible<value_t>::value and + std::is_nothrow_move_assignable<value_t>::value and + std::is_nothrow_move_constructible<json_value>::value and + std::is_nothrow_move_assignable<json_value>::value + ) { // check that passed value is valid other.assert_invariant(); @@ -3022,41 +2578,41 @@ public: switch (m_type) { - case value_t::object: - { - AllocatorType<object_t> alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } + case value_t::object: + { + AllocatorType<object_t> alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + break; + } - case value_t::array: - { - AllocatorType<array_t> alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } + case value_t::array: + { + AllocatorType<array_t> alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + break; + } - case value_t::string: - { - AllocatorType<string_t> alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } + case value_t::string: + { + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + break; + } - default: - { - // all other types need no specific destructor - break; - } + default: + { + // all other types need no specific destructor + break; + } } } /// @} -public: + public: /////////////////////// // object inspection // /////////////////////// @@ -3091,15 +2647,14 @@ public: string_t dump(const int indent = -1) const { std::stringstream ss; - serializer s(ss); if (indent >= 0) { - s.dump(*this, true, static_cast<unsigned int>(indent)); + dump(ss, true, static_cast<unsigned int>(indent)); } else { - s.dump(*this, false, 0); + dump(ss, false, 0); } return ss.str(); @@ -3123,7 +2678,10 @@ public: @since version 1.0.0 */ - constexpr value_t type() const noexcept { return m_type; } + constexpr value_t type() const noexcept + { + return m_type; + } /*! @brief return whether type is primitive @@ -3199,7 +2757,10 @@ public: @since version 1.0.0 */ - constexpr bool is_null() const noexcept { return m_type == value_t::null; } + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } /*! @brief return whether value is a boolean @@ -3279,8 +2840,7 @@ public: */ constexpr bool is_number_integer() const noexcept { - return m_type == value_t::number_integer or - m_type == value_t::number_unsigned; + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; } /*! @@ -3450,114 +3010,109 @@ public: @since version 1.0.0 */ - constexpr operator value_t() const noexcept { return m_type; } + constexpr operator value_t() const noexcept + { + return m_type; + } /// @} -private: + private: ////////////////// // value access // ////////////////// /// get a boolean (explicit) - boolean_t get_impl(boolean_t * /*unused*/) const + boolean_t get_impl(boolean_t* /*unused*/) const { if (is_boolean()) { return m_value.boolean; } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + - type_name())); + JSON_THROW(std::domain_error("type must be boolean, but is " + type_name())); } /// get a pointer to the value (object) - object_t *get_impl_ptr(object_t * /*unused*/) noexcept + object_t* get_impl_ptr(object_t* /*unused*/) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) - constexpr const object_t *get_impl_ptr(const object_t * /*unused*/) const - noexcept + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) - array_t *get_impl_ptr(array_t * /*unused*/) noexcept + array_t* get_impl_ptr(array_t* /*unused*/) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) - constexpr const array_t *get_impl_ptr(const array_t * /*unused*/) const - noexcept + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) - string_t *get_impl_ptr(string_t * /*unused*/) noexcept + string_t* get_impl_ptr(string_t* /*unused*/) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) - constexpr const string_t *get_impl_ptr(const string_t * /*unused*/) const - noexcept + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) - boolean_t *get_impl_ptr(boolean_t * /*unused*/) noexcept + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) - constexpr const boolean_t *get_impl_ptr(const boolean_t * /*unused*/) const - noexcept + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) - number_integer_t *get_impl_ptr(number_integer_t * /*unused*/) noexcept + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) - constexpr const number_integer_t * - get_impl_ptr(const number_integer_t * /*unused*/) const noexcept + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) - number_unsigned_t *get_impl_ptr(number_unsigned_t * /*unused*/) noexcept + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t * - get_impl_ptr(const number_unsigned_t * /*unused*/) const noexcept + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (floating-point number) - number_float_t *get_impl_ptr(number_float_t * /*unused*/) noexcept + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) - constexpr const number_float_t * - get_impl_ptr(const number_float_t * /*unused*/) const noexcept + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } @@ -3570,11 +3125,11 @@ private: @tparam ThisType will be deduced as `basic_json` or `const basic_json` - @throw type_error.303 if ReferenceType does not match underlying value + @throw std::domain_error if ReferenceType does not match underlying value type of the current JSON */ - template <typename ReferenceType, typename ThisType> - static ReferenceType get_ref_impl(ThisType &obj) + template<typename ReferenceType, typename ThisType> + static ReferenceType get_ref_impl(ThisType& obj) { // helper type using PointerType = typename std::add_pointer<ReferenceType>::type; @@ -3587,12 +3142,11 @@ private: return *ptr; } - JSON_THROW(type_error::create( - 303, "incompatible ReferenceType for get_ref, actual type is " + - obj.type_name())); + JSON_THROW(std::domain_error("incompatible ReferenceType for get_ref, actual type is " + + obj.type_name())); } -public: + public: /// @name value access /// Direct access to the stored value of a JSON value. /// @{ @@ -3611,11 +3165,11 @@ public: @since version 2.1.0 */ - template <typename BasicJsonType, - detail::enable_if_t< - std::is_same<typename std::remove_const<BasicJsonType>::type, - basic_json_t>::value, - int> = 0> + template < + typename BasicJsonType, + detail::enable_if_t<std::is_same<typename std::remove_const<BasicJsonType>::type, + basic_json_t>::value, + int> = 0 > basic_json get() const { return *this; @@ -3625,10 +3179,8 @@ public: @brief get a value (explicit) Explicit type conversion between the JSON value and a compatible value - which is - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). The value is converted by calling the @ref json_serializer<ValueType> `from_json()` method. @@ -3662,27 +3214,24 @@ public: @since version 2.1.0 */ - template <typename ValueTypeCV, - typename ValueType = detail::uncvref_t<ValueTypeCV>, - detail::enable_if_t< - not std::is_same<basic_json_t, ValueType>::value and - detail::has_from_json<basic_json_t, ValueType>::value and - not detail::has_non_default_from_json<basic_json_t, - ValueType>::value, - int> = 0> - ValueType get() const - noexcept(noexcept(JSONSerializer<ValueType>::from_json( - std::declval<const basic_json_t &>(), std::declval<ValueType &>()))) + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t<ValueTypeCV>, + detail::enable_if_t < + not std::is_same<basic_json_t, ValueType>::value and + detail::has_from_json<basic_json_t, ValueType>::value and + not detail::has_non_default_from_json<basic_json_t, ValueType>::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>()))) { // we cannot static_assert on ValueTypeCV being non-const, because // there is support for get<const basic_json_t>(), which is why we // still need the uncvref static_assert(not std::is_reference<ValueTypeCV>::value, - "get() cannot be used with reference types, you might " - "want to use get_ref()"); - static_assert( - std::is_default_constructible<ValueType>::value, - "types must be DefaultConstructible when used with get()"); + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible<ValueType>::value, + "types must be DefaultConstructible when used with get()"); ValueType ret; JSONSerializer<ValueType>::from_json(*this, ret); @@ -3693,10 +3242,8 @@ public: @brief get a value (explicit); special case Explicit type conversion between the JSON value and a compatible value - which is **not** - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and **not** - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). The value is converted by calling the @ref json_serializer<ValueType> `from_json()` method. @@ -3726,16 +3273,13 @@ public: typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and - detail::has_non_default_from_json< - basic_json_t, ValueType>::value, - int> = 0> - ValueType get() const - noexcept(noexcept(JSONSerializer<ValueTypeCV>::from_json( - std::declval<const basic_json_t &>()))) + detail::has_non_default_from_json<basic_json_t, + ValueType>::value, int> = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>()))) { static_assert(not std::is_reference<ValueTypeCV>::value, - "get() cannot be used with reference types, you might " - "want to use get_ref()"); + "get() cannot be used with reference types, you might want to use get_ref()"); return JSONSerializer<ValueTypeCV>::from_json(*this); } @@ -3766,9 +3310,8 @@ public: @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 @@ -3779,9 +3322,8 @@ public: @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 @@ -3814,24 +3356,24 @@ public: @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; + 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"); + 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)); @@ -3841,27 +3383,25 @@ public: @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; + 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"); + 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)); @@ -3882,10 +3422,10 @@ public: @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws - type_error.303 otherwise + std::domain_error otherwise - @throw type_error.303 in case passed type @a ReferenceType is incompatible - with the stored JSON value; see example below + @throw std::domain_error in case passed type @a ReferenceType is + incompatible with the stored JSON value @complexity Constant. @@ -3893,9 +3433,8 @@ public: @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 @@ -3906,12 +3445,9 @@ public: @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 @@ -3932,9 +3468,8 @@ public: @return copy of the JSON value, converted to type @a ValueType - @throw type_error.302 in case passed type @a ValueType is incompatible - to the JSON value type (e.g., the JSON value is of type boolean, but a - string is requested); see example below + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON, thrown by @ref get() const @complexity Linear in the size of the JSON value. @@ -3947,25 +3482,13 @@ public: @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 -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 - and - not std::is_same< - ValueType, - std::initializer_list<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<< ambiguity under VS2015 + and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value #endif -#if defined(_MSC_VER) && defined(_HAS_CXX17) && \ - _HAS_CXX17 == 1 // fix for issue #464 - and - not std::is_same<ValueType, typename std::string_view>::value -#endif - , - int>::type = 0> + , int >::type = 0 > operator ValueType() const { // delegate the call to get<>() const @@ -3974,6 +3497,7 @@ public: /// @} + //////////////////// // element access // //////////////////// @@ -3992,40 +3516,36 @@ public: @return reference to the element at index @a idx - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @throw std::domain_error if the JSON value is not an array; example: + `"cannot use at() with string"` + @throw std::out_of_range if the index @a idx is out of range of the array; + that is, `idx >= size()`; example: `"array index 7 is out of range"` @complexity Constant. - @since version 1.0.0 - @liveexample{The example below shows how array elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__size_type} + written using `at()`.,at__size_type} + + @since version 1.0.0 */ reference at(size_type idx) { // at only works for arrays if (is_array()) { - JSON_TRY { return m_value.array->at(idx); } - JSON_CATCH(std::out_of_range &) + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + - std::to_string(idx) + - " is out of range")); + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW( - type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } @@ -4039,40 +3559,36 @@ public: @return const reference to the element at index @a idx - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @throw std::domain_error if the JSON value is not an array; example: + `"cannot use at() with string"` + @throw std::out_of_range if the index @a idx is out of range of the array; + that is, `idx >= size()`; example: `"array index 7 is out of range"` @complexity Constant. - @since version 1.0.0 - @liveexample{The example below shows how array elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__size_type_const} + `at()`.,at__size_type_const} + + @since version 1.0.0 */ const_reference at(size_type idx) const { // at only works for arrays if (is_array()) { - JSON_TRY { return m_value.array->at(idx); } - JSON_CATCH(std::out_of_range &) + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + - std::to_string(idx) + - " is out of range")); + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); } } else { - JSON_THROW( - type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } @@ -4086,43 +3602,40 @@ public: @return reference to the element at key @a key - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @throw std::domain_error if the JSON value is not an object; example: + `"cannot use at() with boolean"` + @throw std::out_of_range if the key @a key is is not stored in the object; + that is, `find(key) == end()`; example: `"key "the fast" not found"` @complexity Logarithmic in the size of the container. + @liveexample{The example below shows how object elements can be read and + written using `at()`.,at__object_t_key_type} + @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__object_t_key_type} */ - reference at(const typename object_t::key_type &key) + reference at(const typename object_t::key_type& key) { // at only works for objects if (is_object()) { - JSON_TRY { return m_value.object->at(key); } - JSON_CATCH(std::out_of_range &) + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW( - out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(std::out_of_range("key '" + key + "' not found")); } } else { - JSON_THROW( - type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } @@ -4136,43 +3649,40 @@ public: @return const reference to the element at key @a key - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @throw std::domain_error if the JSON value is not an object; example: + `"cannot use at() with boolean"` + @throw std::out_of_range if the key @a key is is not stored in the object; + that is, `find(key) == end()`; example: `"key "the fast" not found"` @complexity Logarithmic in the size of the container. + @liveexample{The example below shows how object elements can be read using + `at()`.,at__object_t_key_type_const} + @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @sa @ref value() for access by value with a default value @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__object_t_key_type_const} */ - const_reference at(const typename object_t::key_type &key) const + const_reference at(const typename object_t::key_type& key) const { // at only works for objects if (is_object()) { - JSON_TRY { return m_value.object->at(key); } - JSON_CATCH(std::out_of_range &) + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) { // create better exception explanation - JSON_THROW( - out_of_range::create(403, "key '" + key + "' not found")); + JSON_THROW(std::out_of_range("key '" + key + "' not found")); } } else { - JSON_THROW( - type_error::create(304, "cannot use at() with " + type_name())); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } @@ -4189,8 +3699,8 @@ public: @return reference to the element at index @a idx - @throw type_error.305 if the JSON value is not an array or null; in that - cases, using the [] operator with an index makes no sense. + @throw std::domain_error if JSON is not an array or null; example: + `"cannot use operator[] with string"` @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @@ -4225,8 +3735,7 @@ public: return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + - type_name())); + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -4238,8 +3747,8 @@ public: @return const reference to the element at index @a idx - @throw type_error.305 if the JSON value is not an array; in that cases, - using the [] operator with an index makes no sense. + @throw std::domain_error if JSON is not an array; example: `"cannot use + operator[] with null"` @complexity Constant. @@ -4256,8 +3765,7 @@ public: return m_value.array->operator[](idx); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + - type_name())); + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -4273,8 +3781,8 @@ public: @return reference to the element at key @a key - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @@ -4287,7 +3795,7 @@ public: @since version 1.0.0 */ - reference operator[](const typename object_t::key_type &key) + reference operator[](const typename object_t::key_type& key) { // implicitly convert null value to an empty object if (is_null()) @@ -4303,8 +3811,7 @@ public: return m_value.object->operator[](key); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + - type_name())); + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -4323,8 +3830,8 @@ public: @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` @complexity Logarithmic in the size of the container. @@ -4337,7 +3844,7 @@ public: @since version 1.0.0 */ - const_reference operator[](const typename object_t::key_type &key) const + const_reference operator[](const typename object_t::key_type& key) const { // const operator[] only works for objects if (is_object()) @@ -4346,8 +3853,7 @@ public: return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + - type_name())); + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -4363,8 +3869,8 @@ public: @return reference to the element at key @a key - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @@ -4377,8 +3883,8 @@ public: @since version 1.0.0 */ - template <typename T, std::size_t n> - reference operator[](T *(&key)[n]) + template<typename T, std::size_t n> + reference operator[](T * (&key)[n]) { return operator[](static_cast<const T>(key)); } @@ -4398,8 +3904,8 @@ public: @return const reference to the element at key @a key - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` @complexity Logarithmic in the size of the container. @@ -4412,8 +3918,8 @@ public: @since version 1.0.0 */ - template <typename T, std::size_t n> - const_reference operator[](T *(&key)[n]) const + template<typename T, std::size_t n> + const_reference operator[](T * (&key)[n]) const { return operator[](static_cast<const T>(key)); } @@ -4431,8 +3937,8 @@ public: @return reference to the element at key @a key - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @@ -4445,8 +3951,8 @@ public: @since version 1.1.0 */ - template <typename T> - reference operator[](T *key) + template<typename T> + reference operator[](T* key) { // implicitly convert null to object if (is_null()) @@ -4462,8 +3968,7 @@ public: return m_value.object->operator[](key); } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + - type_name())); + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -4482,8 +3987,8 @@ public: @pre The element with key @a key must exist. **This precondition is enforced with an assertion.** - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` @complexity Logarithmic in the size of the container. @@ -4496,8 +4001,8 @@ public: @since version 1.1.0 */ - template <typename T> - const_reference operator[](T *key) const + template<typename T> + const_reference operator[](T* key) const { // at only works for objects if (is_object()) @@ -4506,8 +4011,7 @@ public: return m_value.object->find(key)->second; } - JSON_THROW(type_error::create(305, "cannot use operator[] with " + - type_name())); + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -4520,7 +4024,7 @@ public: @code {.cpp} try { return at(key); - } catch(out_of_range) { + } catch(std::out_of_range) { return default_value; } @endcode @@ -4543,8 +4047,8 @@ public: @return copy of the element at key @a key or @a default_value if @a key is not found - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` @complexity Logarithmic in the size of the container. @@ -4558,12 +4062,9 @@ public: @since version 1.0.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 + 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 if (is_object()) @@ -4579,18 +4080,15 @@ public: } else { - JSON_THROW(type_error::create(306, "cannot use value() with " + - type_name())); + JSON_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 typename object_t::key_type&, ValueType) - const + @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 + string_t value(const typename object_t::key_type& key, const char* default_value) const { return value(key, string_t(default_value)); } @@ -4605,7 +4103,7 @@ public: @code {.cpp} try { return at(ptr); - } catch(out_of_range) { + } catch(std::out_of_range) { return default_value; } @endcode @@ -4624,8 +4122,8 @@ public: @return copy of the element at key @a key or @a default_value if @a key is not found - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` @complexity Logarithmic in the size of the container. @@ -4636,29 +4134,32 @@ public: @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 + 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 - JSON_TRY { return ptr.get_checked(this); } - JSON_CATCH(out_of_range &) { return default_value; } + JSON_TRY + { + return ptr.get_checked(this); + } + JSON_CATCH (std::out_of_range&) + { + return default_value; + } } - JSON_THROW( - type_error::create(306, "cannot use value() with " + type_name())); + JSON_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 + string_t value(const json_pointer& ptr, const char* default_value) const { return value(ptr, string_t(default_value)); } @@ -4680,7 +4181,7 @@ public: assertions**). @post The JSON value remains unchanged. - @throw invalid_iterator.214 when called on `null` value + @throw std::out_of_range when called on `null` value @liveexample{The following code shows an example for `front()`.,front} @@ -4688,12 +4189,18 @@ public: @since version 1.0.0 */ - reference front() { return *begin(); } + reference front() + { + return *begin(); + } /*! @copydoc basic_json::front() */ - const_reference front() const { return *cbegin(); } + const_reference front() const + { + return *cbegin(); + } /*! @brief access the last element @@ -4717,8 +4224,7 @@ public: assertions**). @post The JSON value remains unchanged. - @throw invalid_iterator.214 when called on a `null` value. See example - below. + @throw std::out_of_range when called on `null` value. @liveexample{The following code shows an example for `back()`.,back} @@ -4762,12 +4268,11 @@ public: @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.202 if called on an iterator which does not belong - to the current JSON value; example: `"iterator does not fit current - value"` - @throw invalid_iterator.205 if called on a primitive type with invalid + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` + @throw std::domain_error if called on an iterator which does not belong to + the current JSON value; example: `"iterator does not fit current value"` + @throw std::out_of_range if called on a primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of range"` @@ -4789,71 +4294,62 @@ public: @since version 1.0.0 */ - 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> + 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) { - JSON_THROW(invalid_iterator::create( - 202, "iterator does not fit current value")); + JSON_THROW(std::domain_error("iterator does not fit current value")); } IteratorType result = end(); switch (m_type) { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not pos.m_it.primitive_iterator.is_begin()) + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: { - JSON_THROW( - invalid_iterator::create(205, "iterator out of range")); + if (not pos.m_it.primitive_iterator.is_begin()) + { + JSON_THROW(std::out_of_range("iterator out of range")); + } + + if (is_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; } - if (is_string()) + case value_t::object: { - AllocatorType<string_t> alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; } - m_type = value_t::null; - assert_invariant(); - break; - } - - case value_t::object: - { - result.m_it.object_iterator = - m_value.object->erase(pos.m_it.object_iterator); - break; - } - - case value_t::array: - { - result.m_it.array_iterator = - m_value.array->erase(pos.m_it.array_iterator); - break; - } + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + - type_name())); - } + default: + { + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + } } return result; @@ -4879,11 +4375,11 @@ public: @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.203 if called on iterators which does not belong - to the current JSON value; example: `"iterators do not fit current value"` - @throw invalid_iterator.204 if called on a primitive type with invalid + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` + @throw std::domain_error if called on iterators which does not belong to + the current JSON value; example: `"iterators do not fit current value"` + @throw std::out_of_range if called on a primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out of range"` @@ -4905,72 +4401,64 @@ public: @since version 1.0.0 */ - 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> + 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) { - JSON_THROW(invalid_iterator::create( - 203, "iterators do not fit current value")); + JSON_THROW(std::domain_error("iterators do not fit current value")); } IteratorType result = end(); switch (m_type) { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or - not last.m_it.primitive_iterator.is_end()) + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: { - JSON_THROW( - invalid_iterator::create(204, "iterators out of range")); + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + JSON_THROW(std::out_of_range("iterators out of range")); + } + + if (is_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; } - if (is_string()) + case value_t::object: { - AllocatorType<string_t> alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; } - m_type = value_t::null; - assert_invariant(); - break; - } - - case value_t::object: - { - result.m_it.object_iterator = m_value.object->erase( - first.m_it.object_iterator, last.m_it.object_iterator); - break; - } - - case value_t::array: - { - result.m_it.array_iterator = m_value.array->erase( - first.m_it.array_iterator, last.m_it.array_iterator); - break; - } + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + - type_name())); - } + default: + { + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); + } } return result; @@ -4990,7 +4478,7 @@ public: @post References and iterators to the erased elements are invalidated. Other references and iterators are not affected. - @throw type_error.307 when called on a type other than JSON object; + @throw std::domain_error when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` @@ -5005,7 +4493,7 @@ public: @since version 1.0.0 */ - size_type erase(const typename object_t::key_type &key) + size_type erase(const typename object_t::key_type& key) { // this erase only works for objects if (is_object()) @@ -5013,8 +4501,7 @@ public: return m_value.object->erase(key); } - JSON_THROW( - type_error::create(307, "cannot use erase() with " + type_name())); + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); } /*! @@ -5024,9 +4511,9 @@ public: @param[in] idx index of the element to remove - @throw type_error.307 when called on a type other than JSON object; + @throw std::domain_error when called on a type other than JSON array; example: `"cannot use erase() with null"` - @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + @throw std::out_of_range when `idx >= size()`; example: `"array index 17 is out of range"` @complexity Linear in distance between @a idx and the end of the container. @@ -5048,23 +4535,20 @@ public: { if (idx >= size()) { - JSON_THROW(out_of_range::create(401, "array index " + - std::to_string(idx) + - " is out of range")); + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); } - m_value.array->erase(m_value.array->begin() + - static_cast<difference_type>(idx)); + m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx)); } else { - JSON_THROW(type_error::create(307, "cannot use erase() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); } } /// @} + //////////// // lookup // //////////// @@ -5151,6 +4635,7 @@ public: /// @} + /////////////// // iterators // /////////////// @@ -5192,7 +4677,10 @@ public: /*! @copydoc basic_json::cbegin() */ - const_iterator begin() const noexcept { return cbegin(); } + const_iterator begin() const noexcept + { + return cbegin(); + } /*! @brief returns a const iterator to the first element @@ -5260,7 +4748,10 @@ public: /*! @copydoc basic_json::cend() */ - const_iterator end() const noexcept { return cend(); } + const_iterator end() const noexcept + { + return cend(); + } /*! @brief returns a const iterator to one past the last element @@ -5317,12 +4808,18 @@ public: @since version 1.0.0 */ - reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } /*! @copydoc basic_json::crbegin() */ - const_reverse_iterator rbegin() const noexcept { return crbegin(); } + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } /*! @brief returns an iterator to the reverse-end @@ -5348,12 +4845,18 @@ public: @since version 1.0.0 */ - reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } /*! @copydoc basic_json::crend() */ - const_reverse_iterator rend() const noexcept { return crend(); } + const_reverse_iterator rend() const noexcept + { + return crend(); + } /*! @brief returns a const reverse iterator to the last element @@ -5413,12 +4916,11 @@ public: return const_reverse_iterator(cbegin()); } -private: + private: // forward declaration - template <typename IteratorType> - class iteration_proxy; + template<typename IteratorType> class iteration_proxy; -public: + public: /*! @brief wrapper to access iterator member functions in range-based for @@ -5438,14 +4940,14 @@ public: /*! @copydoc iterator_wrapper(reference) */ - static iteration_proxy<const_iterator> - iterator_wrapper(const_reference cont) + static iteration_proxy<const_iterator> iterator_wrapper(const_reference cont) { return iteration_proxy<const_iterator>(cont); } /// @} + ////////////// // capacity // ////////////// @@ -5494,29 +4996,29 @@ public: { switch (m_type) { - case value_t::null: - { - // null values are empty - return true; - } + case value_t::null: + { + // null values are empty + return true; + } - case value_t::array: - { - // delegate call to array_t::empty() - return m_value.array->empty(); - } + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } - case value_t::object: - { - // delegate call to object_t::empty() - return m_value.object->empty(); - } + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } - default: - { - // all other types are nonempty - return false; - } + default: + { + // all other types are nonempty + return false; + } } } @@ -5562,29 +5064,29 @@ public: { switch (m_type) { - case value_t::null: - { - // null values are empty - return 0; - } + case value_t::null: + { + // null values are empty + return 0; + } - case value_t::array: - { - // delegate call to array_t::size() - return m_value.array->size(); - } + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } - case value_t::object: - { - // delegate call to object_t::size() - return m_value.object->size(); - } + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } - default: - { - // all other types have size 1 - return 1; - } + default: + { + // all other types have size 1 + return 1; + } } } @@ -5628,28 +5130,29 @@ public: { switch (m_type) { - case value_t::array: - { - // delegate call to array_t::max_size() - return m_value.array->max_size(); - } + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } - case value_t::object: - { - // delegate call to object_t::max_size() - return m_value.object->max_size(); - } + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } - default: - { - // all other types have max_size() == size() - return size(); - } + default: + { + // all other types have max_size() == size() + return size(); + } } } /// @} + /////////////// // modifiers // /////////////// @@ -5683,52 +5186,52 @@ public: { switch (m_type) { - case value_t::number_integer: - { - m_value.number_integer = 0; - break; - } - - case value_t::number_unsigned: - { - m_value.number_unsigned = 0; - break; - } + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } - case value_t::number_float: - { - m_value.number_float = 0.0; - break; - } + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } - case value_t::boolean: - { - m_value.boolean = false; - break; - } + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } - case value_t::string: - { - m_value.string->clear(); - break; - } + case value_t::boolean: + { + m_value.boolean = false; + break; + } - case value_t::array: - { - m_value.array->clear(); - break; - } + case value_t::string: + { + m_value.string->clear(); + break; + } - case value_t::object: - { - m_value.object->clear(); - break; - } + case value_t::array: + { + m_value.array->clear(); + break; + } - default: - { - break; - } + case value_t::object: + { + m_value.object->clear(); + break; + } + + default: + { + break; + } } } @@ -5741,7 +5244,7 @@ public: @param[in] val the value to add to the JSON array - @throw type_error.308 when called on a type other than JSON array or + @throw std::domain_error when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. @@ -5752,13 +5255,12 @@ public: @since version 1.0.0 */ - void push_back(basic_json &&val) + void push_back(basic_json&& val) { // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5779,7 +5281,7 @@ public: @brief add an object to an array @copydoc push_back(basic_json&&) */ - reference operator+=(basic_json &&val) + reference operator+=(basic_json&& val) { push_back(std::move(val)); return *this; @@ -5789,13 +5291,12 @@ public: @brief add an object to an array @copydoc push_back(basic_json&&) */ - void push_back(const basic_json &val) + void push_back(const basic_json& val) { // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); } // transform null object into an array @@ -5814,7 +5315,7 @@ public: @brief add an object to an array @copydoc push_back(basic_json&&) */ - reference operator+=(const basic_json &val) + reference operator+=(const basic_json& val) { push_back(val); return *this; @@ -5829,7 +5330,7 @@ public: @param[in] val the value to add to the JSON object - @throw type_error.308 when called on a type other than JSON object or + @throw std::domain_error when called on a type other than JSON object or null; example: `"cannot use push_back() with number"` @complexity Logarithmic in the size of the container, O(log(`size()`)). @@ -5840,13 +5341,12 @@ public: @since version 1.0.0 */ - void push_back(const typename object_t::value_type &val) + void push_back(const typename object_t::value_type& val) { // push_back only works for null objects or objects if (not(is_null() or is_object())) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); } // transform null object into an object @@ -5865,7 +5365,7 @@ public: @brief add an object to an object @copydoc push_back(const typename object_t::value_type&) */ - reference operator+=(const typename object_t::value_type &val) + reference operator+=(const typename object_t::value_type& val) { push_back(val); return *this; @@ -5884,7 +5384,7 @@ public: @ref push_back(const typename object_t::value_type&). Otherwise, @a init is converted to a JSON value and added using @ref push_back(basic_json&&). - @param[in] init an initializer list + @param init an initializer list @complexity Linear in the size of the initializer list @a init. @@ -5929,7 +5429,7 @@ public: @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 type_error.311 when called on a type other than JSON array or + @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. @@ -5940,14 +5440,13 @@ public: @since version 2.0.8 */ - template <class... Args> - void emplace_back(Args &&... args) + template<class... Args> + void emplace_back(Args&& ... args) { // emplace_back only works for null objects or arrays if (not(is_null() or is_array())) { - JSON_THROW(type_error::create( - 311, "cannot use emplace_back() with " + type_name())); + JSON_THROW(std::domain_error("cannot use emplace_back() with " + type_name())); } // transform null object into an array @@ -5977,7 +5476,7 @@ public: already-existing element if no insertion happened, and a bool denoting whether the insertion took place. - @throw type_error.311 when called on a type other than JSON object or + @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()`)). @@ -5989,14 +5488,13 @@ public: @since version 2.0.8 */ - template <class... Args> - std::pair<iterator, bool> emplace(Args &&... args) + 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())) { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use emplace() with " + type_name())); } // transform null object into an object @@ -6027,10 +5525,10 @@ public: @param[in] val element to insert @return iterator pointing to the inserted @a val. - @throw type_error.309 if called on JSON values other than arrays; + @throw std::domain_error if called on JSON values other than arrays; example: `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` @complexity Constant plus linear in the distance between @a pos and end of the container. @@ -6039,7 +5537,7 @@ public: @since version 1.0.0 */ - iterator insert(const_iterator pos, const basic_json &val) + iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays if (is_array()) @@ -6047,26 +5545,23 @@ public: // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator::create( - 202, "iterator does not fit current value")); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // insert to array and return iterator iterator result(this); - result.m_it.array_iterator = - m_value.array->insert(pos.m_it.array_iterator, val); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } - JSON_THROW( - type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } /*! @brief inserts element @copydoc insert(const_iterator, const basic_json&) */ - iterator insert(const_iterator pos, basic_json &&val) + iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } @@ -6083,10 +5578,10 @@ public: @return iterator pointing to the first element inserted, or @a pos if `cnt==0` - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. @@ -6095,7 +5590,7 @@ public: @since version 1.0.0 */ - iterator insert(const_iterator pos, size_type cnt, const basic_json &val) + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) { // insert only works for arrays if (is_array()) @@ -6103,19 +5598,16 @@ public: // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator::create( - 202, "iterator does not fit current value")); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // insert to array and return iterator iterator result(this); - result.m_it.array_iterator = - m_value.array->insert(pos.m_it.array_iterator, cnt, val); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } - JSON_THROW( - type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } /*! @@ -6128,13 +5620,13 @@ public: @param[in] first begin of the range of elements to insert @param[in] last end of the range of elements to insert - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - @throw invalid_iterator.211 if @a first or @a last are iterators into + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + @throw std::domain_error if @a first and @a last do not belong to the same + JSON value; example: `"iterators do not fit"` + @throw std::domain_error if @a first or @a last are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` @@ -6148,40 +5640,37 @@ public: @since version 1.0.0 */ - iterator insert(const_iterator pos, const_iterator first, - const_iterator last) + iterator insert(const_iterator pos, const_iterator first, const_iterator last) { // insert only works for arrays if (not is_array()) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator::create( - 202, "iterator does not fit current value")); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + JSON_THROW(std::domain_error("iterators do not fit")); } if (first.m_object == this or last.m_object == this) { - JSON_THROW(invalid_iterator::create( - 211, "passed iterators may not belong to container")); + JSON_THROW(std::domain_error("passed iterators may not belong to container")); } // insert to array and return iterator iterator result(this); result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, first.m_it.array_iterator, - last.m_it.array_iterator); + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); return result; } @@ -6194,10 +5683,10 @@ public: the end() iterator @param[in] ilist initializer list to insert the values from - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty @@ -6214,21 +5703,18 @@ public: // insert only works for arrays if (not is_array()) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - JSON_THROW(invalid_iterator::create( - 202, "iterator does not fit current value")); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // insert to array and return iterator iterator result(this); - result.m_it.array_iterator = - m_value.array->insert(pos.m_it.array_iterator, ilist); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } @@ -6249,11 +5735,12 @@ public: @since version 1.0.0 */ - void swap(reference other) noexcept( - std::is_nothrow_move_constructible<value_t>::value - and std::is_nothrow_move_assignable<value_t>::value - and std::is_nothrow_move_constructible<json_value>::value - and std::is_nothrow_move_assignable<json_value>::value) + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible<value_t>::value and + std::is_nothrow_move_assignable<value_t>::value and + std::is_nothrow_move_constructible<json_value>::value and + std::is_nothrow_move_assignable<json_value>::value + ) { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); @@ -6270,8 +5757,8 @@ public: @param[in,out] other array to exchange the contents with - @throw type_error.310 when JSON value is not an array; example: `"cannot - use swap() with string"` + @throw std::domain_error when JSON value is not an array; example: + `"cannot use swap() with string"` @complexity Constant. @@ -6280,7 +5767,7 @@ public: @since version 1.0.0 */ - void swap(array_t &other) + void swap(array_t& other) { // swap only works for arrays if (is_array()) @@ -6289,8 +5776,7 @@ public: } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); } } @@ -6304,7 +5790,7 @@ public: @param[in,out] other object to exchange the contents with - @throw type_error.310 when JSON value is not an object; example: + @throw std::domain_error when JSON value is not an object; example: `"cannot use swap() with string"` @complexity Constant. @@ -6314,7 +5800,7 @@ public: @since version 1.0.0 */ - void swap(object_t &other) + void swap(object_t& other) { // swap only works for objects if (is_object()) @@ -6323,8 +5809,7 @@ public: } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); } } @@ -6338,7 +5823,7 @@ public: @param[in,out] other string to exchange the contents with - @throw type_error.310 when JSON value is not a string; example: `"cannot + @throw std::domain_error when JSON value is not a string; example: `"cannot use swap() with boolean"` @complexity Constant. @@ -6348,7 +5833,7 @@ public: @since version 1.0.0 */ - void swap(string_t &other) + void swap(string_t& other) { // swap only works for strings if (is_string()) @@ -6357,14 +5842,13 @@ public: } else { - JSON_THROW(type_error::create(310, "cannot use swap() with " + - type_name())); + JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); } } /// @} -public: + public: ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// @@ -6377,17 +5861,13 @@ public: Compares two JSON values for equality according to the following rules: - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same according to their respective - `operator==`. + their stored values are the same. - Integer and floating-point numbers are automatically converted before comparison. Floating-point numbers are compared indirectly: two floating-point numbers `f1` and `f2` are considered equal if neither - `f1 > f2` nor `f2 > f1` holds. Note than two NaN values are always - treated as unequal. + `f1 > f2` nor `f2 > f1` holds. - Two JSON null values are equal. - @note NaN values never compare equal to themselves or to other NaN values. - @param[in] lhs first JSON value to consider @param[in] rhs second JSON value to consider @return whether the values @a lhs and @a rhs are equal @@ -6408,80 +5888,67 @@ public: { switch (lhs_type) { - case value_t::array: - { - return *lhs.m_value.array == *rhs.m_value.array; - } - case value_t::object: - { - return *lhs.m_value.object == *rhs.m_value.object; - } - case value_t::null: - { - return true; - } - case value_t::string: - { - return *lhs.m_value.string == *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean == rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer == rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned == - rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float == rhs.m_value.number_float; - } - default: - { - return false; - } + case value_t::array: + { + return *lhs.m_value.array == *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object == *rhs.m_value.object; + } + case value_t::null: + { + return true; + } + case value_t::string: + { + return *lhs.m_value.string == *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean == rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer == rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float == rhs.m_value.number_float; + } + default: + { + return false; + } } } - else if (lhs_type == value_t::number_integer and - rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { - return static_cast<number_float_t>(lhs.m_value.number_integer) == - rhs.m_value.number_float; + return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and - rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { - return lhs.m_value.number_float == - static_cast<number_float_t>(rhs.m_value.number_integer); + return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer); } - else if (lhs_type == value_t::number_unsigned and - rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { - return static_cast<number_float_t>(lhs.m_value.number_unsigned) == - rhs.m_value.number_float; + return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and - rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { - return lhs.m_value.number_float == - static_cast<number_float_t>(rhs.m_value.number_unsigned); + return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_unsigned and - rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { - return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == - rhs.m_value.number_integer; + return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; } - else if (lhs_type == value_t::number_integer and - rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { - return lhs.m_value.number_integer == - static_cast<number_integer_t>(rhs.m_value.number_unsigned); + return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned); } return false; @@ -6491,9 +5958,8 @@ public: @brief comparison: equal @copydoc operator==(const_reference, const_reference) */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> + template<typename ScalarType, typename std::enable_if< + std::is_scalar<ScalarType>::value, int>::type = 0> friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept { return (lhs == basic_json(rhs)); @@ -6503,9 +5969,8 @@ public: @brief comparison: equal @copydoc operator==(const_reference, const_reference) */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> + template<typename ScalarType, typename std::enable_if< + std::is_scalar<ScalarType>::value, int>::type = 0> friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) == rhs); @@ -6529,16 +5994,15 @@ public: */ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { - return not(lhs == rhs); + return not (lhs == rhs); } /*! @brief comparison: not equal @copydoc operator!=(const_reference, const_reference) */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> + template<typename ScalarType, typename std::enable_if< + std::is_scalar<ScalarType>::value, int>::type = 0> friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept { return (lhs != basic_json(rhs)); @@ -6548,9 +6012,8 @@ public: @brief comparison: not equal @copydoc operator!=(const_reference, const_reference) */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> + template<typename ScalarType, typename std::enable_if< + std::is_scalar<ScalarType>::value, int>::type = 0> friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept { return (basic_json(lhs) != rhs); @@ -6589,80 +6052,67 @@ public: { switch (lhs_type) { - case value_t::array: - { - return *lhs.m_value.array < *rhs.m_value.array; - } - case value_t::object: - { - return *lhs.m_value.object < *rhs.m_value.object; - } - case value_t::null: - { - return false; - } - case value_t::string: - { - return *lhs.m_value.string < *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean < rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer < rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned < - rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float < rhs.m_value.number_float; - } - default: - { - return false; - } + case value_t::array: + { + return *lhs.m_value.array < *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object < *rhs.m_value.object; + } + case value_t::null: + { + return false; + } + case value_t::string: + { + return *lhs.m_value.string < *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean < rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer < rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float < rhs.m_value.number_float; + } + default: + { + return false; + } } } - else if (lhs_type == value_t::number_integer and - rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { - return static_cast<number_float_t>(lhs.m_value.number_integer) < - rhs.m_value.number_float; + return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and - rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { - return lhs.m_value.number_float < - static_cast<number_float_t>(rhs.m_value.number_integer); + return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer); } - else if (lhs_type == value_t::number_unsigned and - rhs_type == value_t::number_float) + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) { - return static_cast<number_float_t>(lhs.m_value.number_unsigned) < - rhs.m_value.number_float; + return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float; } - else if (lhs_type == value_t::number_float and - rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) { - return lhs.m_value.number_float < - static_cast<number_float_t>(rhs.m_value.number_unsigned); + return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_integer and - rhs_type == value_t::number_unsigned) + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) { - return lhs.m_value.number_integer < - static_cast<number_integer_t>(rhs.m_value.number_unsigned); + return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned); } - else if (lhs_type == value_t::number_unsigned and - rhs_type == value_t::number_integer) + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) { - return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < - rhs.m_value.number_integer; + return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } // We only reach this line if we cannot compare values. In that case, @@ -6671,30 +6121,6 @@ public: return operator<(lhs_type, rhs_type); } - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs < basic_json(rhs)); - } - - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) < rhs); - } - /*! @brief comparison: less than or equal @@ -6714,31 +6140,7 @@ public: */ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { - return not(rhs < lhs); - } - - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs <= basic_json(rhs)); - } - - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) <= rhs); + return not (rhs < lhs); } /*! @@ -6760,31 +6162,7 @@ public: */ friend bool operator>(const_reference lhs, const_reference rhs) noexcept { - return not(lhs <= rhs); - } - - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs > basic_json(rhs)); - } - - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) > rhs); + return not (lhs <= rhs); } /*! @@ -6806,35 +6184,12 @@ public: */ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept { - return not(lhs < rhs); - } - - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs >= basic_json(rhs)); - } - - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template <typename ScalarType, - typename std::enable_if<std::is_scalar<ScalarType>::value, - int>::type = 0> - friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) >= rhs); + return not (lhs < rhs); } /// @} + /////////////////// // serialization // /////////////////// @@ -6842,684 +6197,273 @@ public: /// @name serialization /// @{ -private: /*! - @brief wrapper around the serialization functions - */ - class serializer - { - private: - serializer(const serializer &) = delete; - serializer &operator=(const serializer &) = delete; + @brief serialize to stream - public: - /*! - @param[in] s output stream to serialize to - */ - serializer(std::ostream &s) - : o(s), loc(std::localeconv()), - thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), - decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]) - { - } + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. The + indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. - /*! - @brief internal implementation of the serialization function - - This function is called by the public member function dump and - organizes the serialization internally. The indentation level is - propagated as additional parameter. In case of arrays and objects, the - function is called recursively. - - - strings and object keys are escaped using `escape_string()` - - integer numbers are converted implicitly via `operator<<` - - floating-point numbers are converted to a string using `"%g"` format - - @param[in] val value to serialize - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used - internally) - */ - void dump(const basic_json &val, const bool pretty_print, - const unsigned int indent_step, - const unsigned int current_indent = 0) - { - switch (val.m_type) - { - case value_t::object: - { - if (val.m_value.object->empty()) - { - o.write("{}", 2); - return; - } + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize - if (pretty_print) - { - o.write("{\n", 2); + @return the stream @a o - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) - { - indent_string.resize(new_indent, ' '); - } + @complexity Linear. - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; - ++cnt, ++i) - { - o.write(indent_string.c_str(), - static_cast<std::streamsize>(new_indent)); - o.put('\"'); - dump_escaped(i->first); - o.write("\": ", 3); - dump(i->second, true, indent_step, new_indent); - o.write(",\n", 2); - } + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} - // last element - assert(i != val.m_value.object->cend()); - o.write(indent_string.c_str(), - static_cast<std::streamsize>(new_indent)); - o.put('\"'); - dump_escaped(i->first); - o.write("\": ", 3); - dump(i->second, true, indent_step, new_indent); + @since version 1.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); - o.put('\n'); - o.write(indent_string.c_str(), - static_cast<std::streamsize>(current_indent)); - o.put('}'); - } - else - { - o.put('{'); + // reset width to 0 for subsequent calls to this stream + o.width(0); - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; - ++cnt, ++i) - { - o.put('\"'); - dump_escaped(i->first); - o.write("\":", 2); - dump(i->second, false, indent_step, current_indent); - o.put(','); - } + // do the actual serialization + j.dump(o, pretty_print, static_cast<unsigned int>(indentation)); - // last element - assert(i != val.m_value.object->cend()); - o.put('\"'); - dump_escaped(i->first); - o.write("\":", 2); - dump(i->second, false, indent_step, current_indent); + return o; + } - o.put('}'); - } + /*! + @brief serialize to stream + @copydoc operator<<(std::ostream&, const basic_json&) + */ + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } - return; - } + /// @} - case value_t::array: - { - if (val.m_value.array->empty()) - { - o.write("[]", 2); - return; - } - if (pretty_print) - { - o.write("[\n", 2); + ///////////////////// + // deserialization // + ///////////////////// - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) - { - indent_string.resize(new_indent, ' '); - } + /// @name deserialization + /// @{ - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); - i != val.m_value.array->cend() - 1; ++i) - { - o.write(indent_string.c_str(), - static_cast<std::streamsize>(new_indent)); - dump(*i, true, indent_step, new_indent); - o.write(",\n", 2); - } + /*! + @brief deserialize from an array - // last element - assert(not val.m_value.array->empty()); - o.write(indent_string.c_str(), - static_cast<std::streamsize>(new_indent)); - dump(val.m_value.array->back(), true, indent_step, - new_indent); + This function reads from an array of 1-byte values. - o.put('\n'); - o.write(indent_string.c_str(), - static_cast<std::streamsize>(current_indent)); - o.put(']'); - } - else - { - o.put('['); + @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.** - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); - i != val.m_value.array->cend() - 1; ++i) - { - dump(*i, false, indent_step, current_indent); - o.put(','); - } + @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) - // last element - assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, indent_step, - current_indent); + @return result of the deserialization - o.put(']'); - } + @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. - return; - } + @note A UTF-8 byte order mark is silently ignored. - case value_t::string: - { - o.put('\"'); - dump_escaped(*val.m_value.string); - o.put('\"'); - return; - } + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} - case value_t::boolean: - { - if (val.m_value.boolean) - { - o.write("true", 4); - } - else - { - o.write("false", 5); - } - return; - } + @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); + } - case value_t::number_integer: - { - dump_integer(val.m_value.number_integer); - return; - } + /*! + @brief deserialize from string literal - case value_t::number_unsigned: - { - dump_integer(val.m_value.number_unsigned); - return; - } + @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) - case value_t::number_float: - { - dump_float(val.m_value.number_float); - return; - } + @return result of the deserialization - case value_t::discarded: - { - o.write("<discarded>", 11); - return; - } + @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. - case value_t::null: - { - o.write("null", 4); - return; - } - } - } + @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) - private: - /*! - @brief calculates the extra space to escape a JSON string + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} - @param[in] s the string to escape - @return the number of characters required to escape string @a s + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream - @complexity Linear in the length of string @a s. - */ - static std::size_t extra_space(const string_t &s) noexcept - { - return std::accumulate( - s.begin(), s.end(), size_t{}, - [](size_t res, typename string_t::value_type c) { - switch (c) - { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - { - // from c (1 byte) to \x (2 bytes) - return res + 1; - } - - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // from c (1 byte) to \uxxxx (6 bytes) - return res + 5; - } + @since version 1.0.0 (originally for @ref string_t) + */ + 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(reinterpret_cast<const char*>(s), cb).parse(); + } - default: - { - return res; - } - } - }); - } + /*! + @brief deserialize from stream - /*! - @brief dump escaped string + @param[in,out] i stream 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) - Escape a string by replacing certain special characters by a sequence - of an escape character (backslash) and another character and other - control characters by a sequence of "\u" followed by a four-digit hex - representation. The escaped string is written to output stream @a o. + @return result of the deserialization - @param[in] s the string to escape + @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. - @complexity Linear in the length of string @a s. - */ - void dump_escaped(const string_t &s) const - { - const auto space = extra_space(s); - if (space == 0) - { - o.write(s.c_str(), static_cast<std::streamsize>(s.size())); - return; - } + @note A UTF-8 byte order mark is silently ignored. - // create a result string of necessary size - string_t result(s.size() + space, '\\'); - std::size_t pos = 0; + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} - for (const auto &c : s) - { - switch (c) - { - // quotation mark (0x22) - case '"': - { - result[pos + 1] = '"'; - pos += 2; - break; - } + @sa @ref parse(const CharT, const parser_callback_t) for a version + that reads from a string - // reverse solidus (0x5c) - case '\\': - { - // nothing to change - pos += 2; - break; - } + @since version 1.0.0 + */ + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) + { + return parser(i, cb).parse(); + } - // backspace (0x08) - case '\b': - { - result[pos + 1] = 'b'; - pos += 2; - break; - } + /*! + @copydoc parse(std::istream&, const parser_callback_t) + */ + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) + { + return parser(i, cb).parse(); + } - // formfeed (0x0c) - case '\f': - { - result[pos + 1] = 'f'; - pos += 2; - break; - } + /*! + @brief deserialize from an iterator range with contiguous storage - // newline (0x0a) - case '\n': - { - result[pos + 1] = 'n'; - pos += 2; - break; - } + 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. - // carriage return (0x0d) - case '\r': - { - result[pos + 1] = 'r'; - pos += 2; - break; - } + @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.** - // horizontal tab (0x09) - case '\t': - { - result[pos + 1] = 't'; - pos += 2; - break; - } + @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. - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // convert a number 0..15 to its hex representation - // (0..f) - static const char hexify[16] = { - '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]}) - { - result[++pos] = m; - } - - ++pos; - break; - } + @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) - default: - { - // all other characters are added as-is - result[pos++] = c; - break; - } - } - } + @return result of the deserialization - assert(pos == s.size() + space); - o.write(result.c_str(), - static_cast<std::streamsize>(result.size())); - } + @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. - /*! - @brief dump an integer + @note A UTF-8 byte order mark is silently ignored. - Dump a given integer to output stream @a o. Works internally with - @a number_buffer. + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} - @param[in] x integer number (signed or unsigned) to dump - @tparam NumberType either @a number_integer_t or @a number_unsigned_t - */ - template <typename NumberType, - detail::enable_if_t< - std::is_same<NumberType, number_unsigned_t>::value or - std::is_same<NumberType, number_integer_t>::value, - int> = 0> - void dump_integer(NumberType x) + @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::pair<bool, int>(true, 0), + [&first](std::pair<bool, int> res, decltype(*first) val) { - // special case for "0" - if (x == 0) - { - o.put('0'); - return; - } + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); - const bool is_negative = x < 0; - size_t i = 0; + // 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"); - // spare 1 byte for '\0' - while (x != 0 and i < number_buffer.size() - 1) - { - const auto digit = std::labs(static_cast<long>(x % 10)); - number_buffer[i++] = static_cast<char>('0' + digit); - x /= 10; - } + // 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(); + } - // make sure the number has been processed completely - assert(x == 0); - - if (is_negative) - { - // make sure there is capacity for the '-' - assert(i < number_buffer.size() - 2); - number_buffer[i++] = '-'; - } - - std::reverse(number_buffer.begin(), number_buffer.begin() + i); - o.write(number_buffer.data(), static_cast<std::streamsize>(i)); - } - - /*! - @brief dump a floating-point number - - Dump a given floating-point number to output stream @a o. Works - internally with @a number_buffer. - - @param[in] x floating-point number to dump - */ - void dump_float(number_float_t x) - { - // NaN / inf - if (not std::isfinite(x) or std::isnan(x)) - { - o.write("null", 4); - return; - } - - // special case for 0.0 and -0.0 - if (x == 0) - { - if (std::signbit(x)) - { - o.write("-0.0", 4); - } - else - { - o.write("0.0", 3); - } - return; - } - - // get number of digits for a text -> float -> text round-trip - static constexpr auto d = - std::numeric_limits<number_float_t>::digits10; - - // the actual conversion - std::ptrdiff_t len = snprintf(number_buffer.data(), - number_buffer.size(), "%.*g", d, x); - - // negative value indicates an error - assert(len > 0); - // check if buffer was large enough - assert(static_cast<size_t>(len) < number_buffer.size()); - - // erase thousands separator - if (thousands_sep != '\0') - { - const auto end = - std::remove(number_buffer.begin(), - number_buffer.begin() + len, thousands_sep); - std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); - len = (end - number_buffer.begin()); - } - - // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') - { - for (auto &c : number_buffer) - { - if (c == decimal_point) - { - c = '.'; - break; - } - } - } - - o.write(number_buffer.data(), static_cast<std::streamsize>(len)); - - // determine if need to append ".0" - const bool value_is_int_like = std::none_of( - number_buffer.begin(), number_buffer.begin() + len + 1, - [](char c) { return c == '.' or c == 'e'; }); - - if (value_is_int_like) - { - o.write(".0", 2); - } - } - - private: - /// the output of the serializer - std::ostream &o; - - /// a (hopefully) large enough character buffer - std::array<char, 64> number_buffer{{}}; - - /// the locale - const std::lconv *loc = nullptr; - /// the locale's thousand separator character - const char thousands_sep = '\0'; - /// the locale's decimal point character - const char decimal_point = '\0'; - - /// the indentation string - string_t indent_string = string_t(512, ' '); - }; - -public: - /*! - @brief serialize to stream - - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. The - indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. - - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @complexity Linear. - - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} - - @since version 1.0.0 - */ - friend std::ostream &operator<<(std::ostream &o, const basic_json &j) - { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); - - // reset width to 0 for subsequent calls to this stream - o.width(0); - - // do the actual serialization - serializer s(o); - s.dump(j, pretty_print, static_cast<unsigned int>(indentation)); - return o; - } - - /*! - @brief serialize to stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::ostream& operator<<(std::ostream&, const basic_json&) - instead; that is, replace calls like `j >> o;` with `o << j;`. - */ - JSON_DEPRECATED - friend std::ostream &operator>>(const basic_json &j, std::ostream &o) - { - return o << j; + return parser(first, last, cb).parse(); } - /// @} - - ///////////////////// - // deserialization // - ///////////////////// - - /// @name deserialization - /// @{ - /*! - @brief deserialize from an array + @brief deserialize from a container with contiguous storage - This function reads from an array of 1-byte values. + 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.** - @param[in] array array to read from + @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 - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - @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. @@ -7527,2301 +6471,2080 @@ public: @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} + from a contiguous container.,parse__contiguouscontainer__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) + 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(array), std::end(array), cb); + return parse(std::begin(c), std::end(c), cb); } /*! - @brief deserialize from string literal + @brief deserialize from stream - @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) + Deserializes an input stream to a JSON value. - @return result of the deserialization + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @throw std::invalid_argument in case of parse errors @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. + LL(1) parser. @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} + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream + @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 (originally for @ref string_t) + @since version 1.0.0 */ - 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) + friend std::istream& operator<<(basic_json& j, std::istream& i) { - return parser(reinterpret_cast<const char *>(s), cb).parse(); + j = parser(i).parse(); + return i; } /*! @brief deserialize from stream + @copydoc operator<<(basic_json&, std::istream&) + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + j = parser(i).parse(); + return i; + } - @param[in,out] i stream 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) + /// @} - @return result of the deserialization + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - @throw parse_error.111 if input stream is in a bad state + /// @name binary serialization/deserialization support + /// @{ - @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. + private: + /*! + @note Some code in the switch cases has been copied, because otherwise + copilers would complain about implicit fallthrough and there is no + portable attribute to mute such warnings. + */ + 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); - @note A UTF-8 byte order mark is silently ignored. + switch (bytes) + { + case 8: + { + vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 070) & 0xff)); + vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 060) & 0xff)); + vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 050) & 0xff)); + vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 040) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); + vec.push_back(static_cast<uint8_t>(number & 0xff)); + break; + } - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} + case 4: + { + vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff)); + vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); + vec.push_back(static_cast<uint8_t>(number & 0xff)); + break; + } - @sa @ref parse(const CharT, const parser_callback_t) for a version - that reads from a string + case 2: + { + vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); + vec.push_back(static_cast<uint8_t>(number & 0xff)); + break; + } - @since version 1.0.0 - */ - static basic_json parse(std::istream &i, - const parser_callback_t cb = nullptr) - { - return parser(i, cb).parse(); + case 1: + { + vec.push_back(static_cast<uint8_t>(number & 0xff)); + break; + } + } } /*! - @copydoc parse(std::istream&, const parser_callback_t) - */ - static basic_json parse(std::istream &&i, - const parser_callback_t cb = nullptr) - { - return parser(i, cb).parse(); - } + @brief take sufficient bytes from a vector to fill an integer variable - /*! - @brief deserialize from an iterator range with contiguous storage + 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. - 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. + @param[in] vec byte vector to read from + @param[in] current_index the position in the vector after which to read - @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.** + @return the next sizeof(T) bytes from @a vec, in reverse order as T - @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 T the integral return type - @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) + @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the + vector @a vec to read - @return result of the deserialization + 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. - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + Precondition: - @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. + vec: | | | a | b | c | d | T: | | | | | + ^ ^ ^ ^ + current_index i ptr sizeof(T) - @note A UTF-8 byte order mark is silently ignored. + Postcondition: - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} + vec: | | | a | b | c | d | T: | d | c | b | a | + ^ ^ ^ + | i ptr + current_index - @since version 2.0.3 + @sa Code adapted from <http://stackoverflow.com/a/41031865/266378>. */ - 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) + template<typename T> + static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index) { - // 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::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) + if (current_index + sizeof(T) + 1 > vec.size()) { - return parser("").parse(); + JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector")); } - return parser(first, last, cb).parse(); + T result; + auto* 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 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 - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @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. + @brief create a MessagePack serialization of a given JSON value - @note A UTF-8 byte order mark is silently ignored. + This is a straightforward implementation of the MessagePack specification. - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to - @since version 2.0.3 + @sa https://github.com/msgpack/msgpack/blob/master/spec.md */ - 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) + static void to_msgpack_internal(const basic_json& j, std::vector<uint8_t>& v) { - // delegate the call to the iterator-range parse overload - return parse(std::begin(c), std::end(c), cb); - } + switch (j.type()) + { + case value_t::null: + { + // nil + v.push_back(0xc0); + break; + } - /*! - @brief deserialize from stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::istream& operator>>(std::istream&, basic_json&) - instead; that is, replace calls like `j << i;` with `i >> j;`. - */ - JSON_DEPRECATED - friend std::istream &operator<<(basic_json &j, std::istream &i) - { - j = parser(i).parse(); - return i; - } + case value_t::boolean: + { + // true and false + v.push_back(j.m_value.boolean ? 0xc3 : 0xc2); + break; + } - /*! - @brief deserialize from stream + 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 <= std::numeric_limits<uint8_t>::max()) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits<uint16_t>::max()) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits<uint32_t>::max()) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits<uint64_t>::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 >= std::numeric_limits<int8_t>::min() and j.m_value.number_integer <= std::numeric_limits<int8_t>::max()) + { + // int 8 + v.push_back(0xd0); + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits<int16_t>::min() and j.m_value.number_integer <= std::numeric_limits<int16_t>::max()) + { + // int 16 + v.push_back(0xd1); + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits<int32_t>::min() and j.m_value.number_integer <= std::numeric_limits<int32_t>::max()) + { + // int 32 + v.push_back(0xd2); + add_to_vector(v, 4, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits<int64_t>::min() and j.m_value.number_integer <= std::numeric_limits<int64_t>::max()) + { + // int 64 + v.push_back(0xd3); + add_to_vector(v, 8, j.m_value.number_integer); + } + } + break; + } - Deserializes an input stream to a JSON value. + 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 <= std::numeric_limits<uint8_t>::max()) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits<uint16_t>::max()) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits<uint32_t>::max()) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits<uint64_t>::max()) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to + case value_t::number_float: + { + // float 64 + v.push_back(0xcb); + const auto* 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; + } - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - @throw parse_error.111 if input stream is in a bad state + 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); + } - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } - @note A UTF-8 byte order mark is silently ignored. + 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); + } - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} + // append each element + for (const auto& el : *j.m_value.array) + { + to_msgpack_internal(el, v); + } + break; + } - @sa parse(std::istream&, const parser_callback_t) for a variant with a - parser callback function to filter values while parsing + 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); + } - @since version 1.0.0 - */ - friend std::istream &operator>>(std::istream &i, basic_json &j) - { - j = parser(i).parse(); - return i; + // 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 - ////////////////////////////////////////// - // binary serialization/deserialization // - ////////////////////////////////////////// + This is a straightforward implementation of the CBOR specification. - /// @name binary serialization/deserialization support - /// @{ + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to -private: - /*! - @note Some code in the switch cases has been copied, because otherwise - copilers would complain about implicit fallthrough and there is no - portable attribute to mute such warnings. + @sa https://tools.ietf.org/html/rfc7049 */ - template <typename T> - static void add_to_vector(std::vector<uint8_t> &vec, size_t bytes, - const T number) + static void to_cbor_internal(const basic_json& j, std::vector<uint8_t>& v) { - assert(bytes == 1 or bytes == 2 or bytes == 4 or bytes == 8); - - switch (bytes) + switch (j.type()) { - case 8: - { - vec.push_back(static_cast<uint8_t>( - (static_cast<uint64_t>(number) >> 070) & 0xff)); - vec.push_back(static_cast<uint8_t>( - (static_cast<uint64_t>(number) >> 060) & 0xff)); - vec.push_back(static_cast<uint8_t>( - (static_cast<uint64_t>(number) >> 050) & 0xff)); - vec.push_back(static_cast<uint8_t>( - (static_cast<uint64_t>(number) >> 040) & 0xff)); - vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff)); - vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff)); - vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); - vec.push_back(static_cast<uint8_t>(number & 0xff)); - break; - } + case value_t::null: + { + v.push_back(0xf6); + break; + } - case 4: - { - vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff)); - vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff)); - vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); - vec.push_back(static_cast<uint8_t>(number & 0xff)); - break; - } + case value_t::boolean: + { + v.push_back(j.m_value.boolean ? 0xf5 : 0xf4); + break; + } - case 2: - { - vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff)); - vec.push_back(static_cast<uint8_t>(number & 0xff)); - 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 <= std::numeric_limits<uint8_t>::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 <= std::numeric_limits<uint16_t>::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 <= std::numeric_limits<uint32_t>::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 <= std::numeric_limits<uint8_t>::max()) + { + // int 8 + v.push_back(0x38); + add_to_vector(v, 1, positive_number); + } + else if (positive_number <= std::numeric_limits<uint16_t>::max()) + { + // int 16 + v.push_back(0x39); + add_to_vector(v, 2, positive_number); + } + else if (positive_number <= std::numeric_limits<uint32_t>::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 1: - { - vec.push_back(static_cast<uint8_t>(number & 0xff)); - 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; + } - /*! - @brief take sufficient bytes from a vector to fill an integer variable + case value_t::number_float: + { + // Double-Precision Float + v.push_back(0xfb); + const auto* 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; + } - 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. + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + v.push_back(0x60 + static_cast<uint8_t>(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 - @param[in] vec byte vector to read from - @param[in] current_index the position in the vector after which to read + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } - @return the next sizeof(T) bytes from @a vec, in reverse order as T + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + v.push_back(0x80 + static_cast<uint8_t>(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 - @tparam T the integral return type + // append each element + for (const auto& el : *j.m_value.array) + { + to_cbor_internal(el, v); + } + break; + } - @throw parse_error.110 if there are less than sizeof(T)+1 bytes in the - vector @a vec to read + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + v.push_back(0xa0 + static_cast<uint8_t>(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 - 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. + // append each element + for (const auto& el : *j.m_value.object) + { + to_cbor_internal(el.first, v); + to_cbor_internal(el.second, v); + } + break; + } - Precondition: + default: + { + break; + } + } + } - vec: | | | a | b | c | d | T: | | | | | - ^ ^ ^ ^ - current_index i ptr sizeof(T) - Postcondition: + /* + @brief checks if given lengths do not exceed the size of a given vector - vec: | | | a | b | c | d | T: | d | c | b | a | - ^ ^ ^ - | i ptr - current_index + 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. - @sa Code adapted from <http://stackoverflow.com/a/41031865/266378>. + 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()` */ - template <typename T> - static T get_from_vector(const std::vector<uint8_t> &vec, - const size_t current_index) + static void check_length(const size_t size, const size_t len, const size_t offset) { - // check if we can read sizeof(T) bytes starting the next index - check_length(vec.size(), sizeof(T), current_index + 1); + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + JSON_THROW(std::out_of_range("len out of range")); + } - T result; - auto *ptr = reinterpret_cast<uint8_t *>(&result); - for (size_t i = 0; i < sizeof(T); ++i) + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits<size_t>::max() - offset))) { - *ptr++ = vec[current_index + sizeof(T) - i]; + JSON_THROW(std::out_of_range("len+offset out of range")); + } + + // last case: reading past the end of the vector + if (len + offset > size) + { + JSON_THROW(std::out_of_range("len+offset out of range")); } - return result; } /*! - @brief create a MessagePack serialization of a given JSON value + @brief create a JSON value from a given MessagePack vector - This is a straightforward implementation of the MessagePack specification. + @param[in] v MessagePack serialization + @param[in] idx byte index to start reading from @a v - @param[in] j JSON value to serialize - @param[in,out] v byte vector to write the serialization to + @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 void to_msgpack_internal(const basic_json &j, - std::vector<uint8_t> &v) + static basic_json from_msgpack_internal(const std::vector<uint8_t>& v, size_t& idx) { - switch (j.type()) - { - case value_t::null: + // 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) { - // nil - v.push_back(0xc0); - break; + if (v[current_idx] <= 0x7f) // positive fixint + { + return v[current_idx]; + } + 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); + } } - - case value_t::boolean: + else if (v[current_idx] >= 0xe0) // negative fixint { - // true and false - v.push_back(j.m_value.boolean ? 0xc3 : 0xc2); - break; + return static_cast<int8_t>(v[current_idx]); } - - case value_t::number_integer: + else { - if (j.m_value.number_integer >= 0) + switch (v[current_idx]) { - // 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) + case 0xc0: // nil { - // positive fixnum - add_to_vector(v, 1, j.m_value.number_unsigned); + return value_t::null; } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint8_t>::max)()) + + case 0xc2: // false { - // uint 8 - v.push_back(0xcc); - add_to_vector(v, 1, j.m_value.number_unsigned); + return false; } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint16_t>::max)()) + + case 0xc3: // true { - // uint 16 - v.push_back(0xcd); - add_to_vector(v, 2, j.m_value.number_unsigned); + return true; } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint32_t>::max)()) + + case 0xca: // float 32 { - // uint 32 - v.push_back(0xce); - add_to_vector(v, 4, j.m_value.number_unsigned); + // copy bytes in reverse order into the double variable + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + } + idx += sizeof(float); // skip content bytes + return res; } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint64_t>::max)()) + + case 0xcb: // float 64 { - // uint 64 - v.push_back(0xcf); - add_to_vector(v, 8, j.m_value.number_unsigned); + // 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.at(current_idx + 1 + byte); + } + idx += sizeof(double); // skip content bytes + return res; } - } - else - { - if (j.m_value.number_integer >= -32) + + case 0xcc: // uint 8 { - // negative fixnum - add_to_vector(v, 1, j.m_value.number_integer); + idx += 1; // skip content byte + return get_from_vector<uint8_t>(v, current_idx); } - else if (j.m_value.number_integer >= - (std::numeric_limits<int8_t>::min)() and - j.m_value.number_integer <= - (std::numeric_limits<int8_t>::max)()) + + case 0xcd: // uint 16 { - // int 8 - v.push_back(0xd0); - add_to_vector(v, 1, j.m_value.number_integer); + idx += 2; // skip 2 content bytes + return get_from_vector<uint16_t>(v, current_idx); } - else if (j.m_value.number_integer >= - (std::numeric_limits<int16_t>::min)() and - j.m_value.number_integer <= - (std::numeric_limits<int16_t>::max)()) + + case 0xce: // uint 32 { - // int 16 - v.push_back(0xd1); - add_to_vector(v, 2, j.m_value.number_integer); + idx += 4; // skip 4 content bytes + return get_from_vector<uint32_t>(v, current_idx); } - else if (j.m_value.number_integer >= - (std::numeric_limits<int32_t>::min)() and - j.m_value.number_integer <= - (std::numeric_limits<int32_t>::max)()) + + case 0xcf: // uint 64 { - // int 32 - v.push_back(0xd2); - add_to_vector(v, 4, j.m_value.number_integer); + idx += 8; // skip 8 content bytes + return get_from_vector<uint64_t>(v, current_idx); } - else if (j.m_value.number_integer >= - (std::numeric_limits<int64_t>::min)() and - j.m_value.number_integer <= - (std::numeric_limits<int64_t>::max)()) + + case 0xd0: // int 8 { - // int 64 - v.push_back(0xd3); - add_to_vector(v, 8, j.m_value.number_integer); + idx += 1; // skip content byte + return get_from_vector<int8_t>(v, current_idx); } - } - 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 <= - (std::numeric_limits<uint8_t>::max)()) - { - // uint 8 - v.push_back(0xcc); - add_to_vector(v, 1, j.m_value.number_unsigned); - } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint16_t>::max)()) - { - // uint 16 - v.push_back(0xcd); - add_to_vector(v, 2, j.m_value.number_unsigned); - } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint32_t>::max)()) - { - // uint 32 - v.push_back(0xce); - add_to_vector(v, 4, j.m_value.number_unsigned); - } - else if (j.m_value.number_unsigned <= - (std::numeric_limits<uint64_t>::max)()) - { - // uint 64 - v.push_back(0xcf); - add_to_vector(v, 8, j.m_value.number_unsigned); - } - break; - } + case 0xd1: // int 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector<int16_t>(v, current_idx); + } - case value_t::number_float: - { - // float 64 - v.push_back(0xcb); - const auto *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 0xd2: // int 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector<int32_t>(v, current_idx); + } - 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); - } + case 0xd3: // int 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector<int64_t>(v, current_idx); + } - // append string - std::copy(j.m_value.string->begin(), j.m_value.string->end(), - std::back_inserter(v)); - break; - } + 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 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); - } + 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); + } - // append each element - for (const auto &el : *j.m_value.array) - { - to_msgpack_internal(el, v); - } - break; - } + 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 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); - } + 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; + } - // append each element - for (const auto &el : *j.m_value.object) - { - to_msgpack_internal(el.first, v); - to_msgpack_internal(el.second, v); - } - break; - } + 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; + } - default: - { - break; - } + 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: + { + JSON_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 CBOR serialization of a given JSON value + @brief create a JSON value from a given CBOR vector - This is a straightforward implementation of the CBOR specification. + @param[in] v CBOR serialization + @param[in] idx byte index to start reading from @a v - @param[in] j JSON value to serialize - @param[in,out] v byte vector to write the serialization to + @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 void to_cbor_internal(const basic_json &j, std::vector<uint8_t> &v) + static basic_json from_cbor_internal(const std::vector<uint8_t>& v, size_t& idx) { - switch (j.type()) - { - case value_t::null: - { - v.push_back(0xf6); - break; - } + // store and increment index + const size_t current_idx = idx++; - case value_t::boolean: - { - v.push_back(j.m_value.boolean ? 0xf5 : 0xf4); - break; - } + 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 value_t::number_integer: - { - if (j.m_value.number_integer >= 0) + case 0x18: // Unsigned integer (one-byte uint8_t follows) { - // 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 <= - (std::numeric_limits<uint8_t>::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 <= - (std::numeric_limits<uint16_t>::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 <= - (std::numeric_limits<uint32_t>::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); - } + 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); } - else + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) { - // 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 <= - (std::numeric_limits<uint8_t>::max)()) - { - // int 8 - v.push_back(0x38); - add_to_vector(v, 1, positive_number); - } - else if (positive_number <= - (std::numeric_limits<uint16_t>::max)()) - { - // int 16 - v.push_back(0x39); - add_to_vector(v, 2, positive_number); - } - else if (positive_number <= - (std::numeric_limits<uint32_t>::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); - } + idx += 8; // skip 8 content bytes + return get_from_vector<uint64_t>(v, current_idx); } - break; - } - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned <= 0x17) + // 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) { - v.push_back(static_cast<uint8_t>(j.m_value.number_unsigned)); + idx += 2; // skip 2 content bytes + return static_cast<number_integer_t>(-1) - get_from_vector<uint16_t>(v, current_idx); } - else if (j.m_value.number_unsigned <= 0xff) + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) { - v.push_back(0x18); - // one-byte uint8_t - add_to_vector(v, 1, j.m_value.number_unsigned); + idx += 4; // skip 4 content bytes + return static_cast<number_integer_t>(-1) - get_from_vector<uint32_t>(v, current_idx); } - else if (j.m_value.number_unsigned <= 0xffff) + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) { - v.push_back(0x19); - // two-byte uint16_t - add_to_vector(v, 2, j.m_value.number_unsigned); + 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); } - else if (j.m_value.number_unsigned <= 0xffffffff) + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - v.push_back(0x1a); - // four-byte uint32_t - add_to_vector(v, 4, j.m_value.number_unsigned); + 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); } - else if (j.m_value.number_unsigned <= 0xffffffffffffffff) + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - v.push_back(0x1b); - // eight-byte uint64_t - add_to_vector(v, 8, j.m_value.number_unsigned); + 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); } - break; - } - case value_t::number_float: - { - // Double-Precision Float - v.push_back(0xfb); - const auto *helper = - reinterpret_cast<const uint8_t *>(&(j.m_value.number_float)); - for (size_t i = 0; i < 8; ++i) + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) { - v.push_back(helper[7 - i]); + 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); } - break; - } - case value_t::string: - { - const auto N = j.m_value.string->size(); - if (N <= 0x17) + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) { - v.push_back( - static_cast<uint8_t>(0x60 + N)); // 1 byte for string + size + 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); } - else if (N <= 0xff) + + case 0x7f: // UTF-8 string (indefinite length) { - v.push_back(0x78); // one-byte uint8_t for N - add_to_vector(v, 1, N); + 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; } - else if (N <= 0xffff) + + // 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: { - v.push_back(0x79); // two-byte uint16_t for N - add_to_vector(v, 2, N); + 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; } - else if (N <= 0xffffffff) + + case 0x98: // array (one-byte uint8_t for n follows) { - v.push_back(0x7a); // four-byte uint32_t for N - add_to_vector(v, 4, N); + 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; } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) + + case 0x99: // array (two-byte uint16_t for n follow) { - v.push_back(0x7b); // eight-byte uint64_t for N - add_to_vector(v, 8, N); + 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; } - // LCOV_EXCL_STOP - // append string - std::copy(j.m_value.string->begin(), j.m_value.string->end(), - std::back_inserter(v)); - break; - } + 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 value_t::array: - { - const auto N = j.m_value.array->size(); - if (N <= 0x17) + case 0x9b: // array (eight-byte uint64_t for n follow) { - v.push_back( - static_cast<uint8_t>(0x80 + N)); // 1 byte for array + size + 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; } - else if (N <= 0xff) + + case 0x9f: // array (indefinite length) { - v.push_back(0x98); // one-byte uint8_t for N - add_to_vector(v, 1, N); + 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; } - else if (N <= 0xffff) + + // 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: { - v.push_back(0x99); // two-byte uint16_t for N - add_to_vector(v, 2, N); + 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; } - else if (N <= 0xffffffff) + + case 0xb8: // map (one-byte uint8_t for n follows) { - v.push_back(0x9a); // four-byte uint32_t for N - add_to_vector(v, 4, N); + 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; } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) + + case 0xb9: // map (two-byte uint16_t for n follow) { - v.push_back(0x9b); // eight-byte uint64_t for N - add_to_vector(v, 8, N); + 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; } - // LCOV_EXCL_STOP - // append each element - for (const auto &el : *j.m_value.array) + case 0xba: // map (four-byte uint32_t for n follow) { - to_cbor_internal(el, v); + 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; } - break; - } - case value_t::object: - { - const auto N = j.m_value.object->size(); - if (N <= 0x17) + 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) { - v.push_back( - static_cast<uint8_t>(0xa0 + N)); // 1 byte for object + size + 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; } - else if (N <= 0xff) + + case 0xf4: // false { - v.push_back(0xb8); - add_to_vector(v, 1, N); // one-byte uint8_t for N + return false; } - else if (N <= 0xffff) + + case 0xf5: // true { - v.push_back(0xb9); - add_to_vector(v, 2, N); // two-byte uint16_t for N + return true; } - else if (N <= 0xffffffff) + + case 0xf6: // null { - v.push_back(0xba); - add_to_vector(v, 4, N); // four-byte uint32_t for N + return value_t::null; } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) + + case 0xf9: // Half-Precision Float (two-byte IEEE 754) { - v.push_back(0xbb); - add_to_vector(v, 8, N); // eight-byte uint64_t for N + 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.at(current_idx + 1) << 8) + v.at(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 + ? std::numeric_limits<double>::infinity() + : std::numeric_limits<double>::quiet_NaN(); + } + return (half & 0x8000) != 0 ? -val : val; } - // LCOV_EXCL_STOP - // append each element - for (const auto &el : *j.m_value.object) + case 0xfa: // Single-Precision Float (four-byte IEEE 754) { - to_cbor_internal(el.first, v); - to_cbor_internal(el.second, v); + // copy bytes in reverse order into the float variable + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast<uint8_t*>(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + } + idx += sizeof(float); // skip content bytes + return res; } - break; - } - default: - { - break; - } + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + // 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.at(current_idx + 1 + byte); + } + idx += sizeof(double); // skip content bytes + return res; + } + + default: // anything else (0xFF is handled inside the other types) + { + JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast<int>(v[current_idx])))); + } } } - /* - @brief checks if given lengths do not exceed the size of a given vector + public: + /*! + @brief create a MessagePack serialization of a given JSON value - 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. + 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. - This function checks whether reading the bytes is safe; that is, offset is - a valid index in the vector, offset+len + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector - @param[in] size size of the byte vector - @param[in] len number of bytes to read - @param[in] offset offset where to start reading + @complexity Linear in the size of the JSON value @a j. - vec: x x x x x X X X X X - ^ ^ ^ - 0 offset len + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} - @throws out_of_range if `len > v.size()` + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + + @since version 2.0.9 */ - static void check_length(const size_t size, const size_t len, - const size_t offset) + static std::vector<uint8_t> to_msgpack(const basic_json& j) { - // simple case: requested length is greater than the vector's length - if (len > size or offset > size) - { - JSON_THROW(parse_error::create( - 110, offset + 1, - "cannot read " + std::to_string(len) + " bytes from vector")); - } + std::vector<uint8_t> result; + to_msgpack_internal(j, result); + return result; + } - // second case: adding offset would result in overflow - if ((size > ((std::numeric_limits<size_t>::max)() - offset))) - { - JSON_THROW(parse_error::create( - 110, offset + 1, - "cannot read " + std::to_string(len) + " bytes from vector")); - } + /*! + @brief create a JSON value from a byte vector in MessagePack format - // last case: reading past the end of the vector - if (len + offset > size) - { - JSON_THROW(parse_error::create( - 110, offset + 1, - "cannot read " + std::to_string(len) + " bytes from vector")); - } + 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 + @param[in] start_index the index to start reading from @a v (0 by default) + @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>&, const size_t) for the + related CBOR format + + @since version 2.0.9, parameter @a start_index since 2.1.1 + */ + static basic_json from_msgpack(const std::vector<uint8_t>& v, + const size_t start_index = 0) + { + size_t i = start_index; + return from_msgpack_internal(v, i); } - /*! - @brief check if the next byte belongs to a string + /*! + @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 - While parsing a map, the keys must be strings. This function checks if the - current byte is one of the start bytes for a string in MessagePack: + @complexity Linear in the size of the JSON value @a j. - - 0xa0 - 0xbf: fixstr - - 0xd9: str 8 - - 0xda: str 16 - - 0xdb: str 32 + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} - @param[in] v MessagePack serialization - @param[in] idx byte index in @a v to check for a string + @sa http://cbor.io + @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format - @throw parse_error.113 if `v[idx]` does not belong to a string + @since version 2.0.9 */ - static void msgpack_expect_string(const std::vector<uint8_t> &v, size_t idx) + static std::vector<uint8_t> to_cbor(const basic_json& j) { - check_length(v.size(), 1, idx); - - const auto byte = v[idx]; - if ((byte >= 0xa0 and byte <= 0xbf) or (byte >= 0xd9 and byte <= 0xdb)) - { - return; - } - - std::stringstream ss; - ss << std::hex << static_cast<int>(v[idx]); - JSON_THROW(parse_error::create( - 113, idx + 1, - "expected a MessagePack string; last byte: 0x" + ss.str())); + std::vector<uint8_t> result; + to_cbor_internal(j, result); + return result; } /*! - @brief check if the next byte belongs to a string + @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 + @param[in] start_index the index to start reading from @a v (0 by default) + @return deserialized JSON value - While parsing a map, the keys must be strings. This function checks if the - current byte is one of the start bytes for a string in CBOR: + @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 - - 0x60 - 0x77: fixed length - - 0x78 - 0x7b: variable length - - 0x7f: indefinity length + @complexity Linear in the size of the byte vector @a v. - @param[in] v CBOR serialization - @param[in] idx byte index in @a v to check for a string + @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>&, const size_t) for the + related MessagePack format - @throw parse_error.113 if `v[idx]` does not belong to a string + @since version 2.0.9, parameter @a start_index since 2.1.1 */ - static void cbor_expect_string(const std::vector<uint8_t> &v, size_t idx) + static basic_json from_cbor(const std::vector<uint8_t>& v, + const size_t start_index = 0) { - check_length(v.size(), 1, idx); + size_t i = start_index; + return from_cbor_internal(v, i); + } - const auto byte = v[idx]; - if ((byte >= 0x60 and byte <= 0x7b) or byte == 0x7f) - { - return; - } + /// @} - std::stringstream ss; - ss << std::hex << static_cast<int>(v[idx]); - JSON_THROW(parse_error::create( - 113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str())); - } + /////////////////////////// + // convenience functions // + /////////////////////////// /*! - @brief create a JSON value from a given MessagePack vector + @brief return the type as string - @param[in] v MessagePack serialization - @param[in] idx byte index to start reading from @a v + 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 deserialized JSON value + @return basically a string representation of a the @a m_type member - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from MessagePack were - used in the given vector @a v or if the input is not valid MessagePack - @throw parse_error.113 if a string was expected as map key, but not found + @complexity Constant. - @sa https://github.com/msgpack/msgpack/blob/master/spec.md + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @since version 1.0.0, public since 2.1.0 */ - static basic_json from_msgpack_internal(const std::vector<uint8_t> &v, - size_t &idx) + std::string type_name() const { - // store and increment index - const size_t current_idx = idx++; - - // make sure reading 1 byte is safe - check_length(v.size(), 1, current_idx); - - if (v[current_idx] <= 0xbf) { - if (v[current_idx] <= 0x7f) // positive fixint - { - return v[current_idx]; - } - 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) - { - msgpack_expect_string(v, idx); - 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 + switch (m_type) { - 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); + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; } } - 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; - } + private: + /*! + @brief calculates the extra space to escape a JSON string - case 0xc3: // true - { - return true; - } + @param[in] s the string to escape + @return the number of characters required to escape string @a s - case 0xca: // float 32 + @complexity Linear in the length of string @a s. + */ + static std::size_t extra_space(const string_t& s) noexcept + { + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) + { + switch (c) { - // copy bytes in reverse order into the double variable - float res; - check_length(v.size(), sizeof(float), current_idx + 1); - for (size_t byte = 0; byte < sizeof(float); ++byte) + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': { - reinterpret_cast<uint8_t *>( - &res)[sizeof(float) - byte - 1] = - v[current_idx + 1 + byte]; + // from c (1 byte) to \x (2 bytes) + return res + 1; } - idx += sizeof(float); // skip content bytes - return res; - } - case 0xcb: // float 64 - { - // copy bytes in reverse order into the double variable - double res; - check_length(v.size(), sizeof(double), current_idx + 1); - for (size_t byte = 0; byte < sizeof(double); ++byte) + default: { - 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); - } + if (c >= 0x00 and c <= 0x1f) + { + // from c (1 byte) to \uxxxx (6 bytes) + return res + 5; + } - case 0xcd: // uint 16 - { - idx += 2; // skip 2 content bytes - return get_from_vector<uint16_t>(v, current_idx); + return res; + } } + }); + } - case 0xce: // uint 32 - { - idx += 4; // skip 4 content bytes - return get_from_vector<uint32_t>(v, current_idx); - } + /*! + @brief escape a string - case 0xcf: // uint 64 - { - idx += 8; // skip 8 content bytes - return get_from_vector<uint64_t>(v, current_idx); - } + Escape a string by replacing certain special characters by a sequence of + an escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. - case 0xd0: // int 8 - { - idx += 1; // skip content byte - return get_from_vector<int8_t>(v, current_idx); - } + @param[in] s the string to escape + @return the escaped string - case 0xd1: // int 16 - { - idx += 2; // skip 2 content bytes - return get_from_vector<int16_t>(v, current_idx); - } + @complexity Linear in the length of string @a s. + */ + static string_t escape_string(const string_t& s) + { + const auto space = extra_space(s); + if (space == 0) + { + return s; + } - case 0xd2: // int 32 - { - idx += 4; // skip 4 content bytes - return get_from_vector<int32_t>(v, current_idx); - } + // create a result string of necessary size + string_t result(s.size() + space, '\\'); + std::size_t pos = 0; - case 0xd3: // int 64 + for (const auto& c : s) + { + switch (c) { - idx += 8; // skip 8 content bytes - return get_from_vector<int64_t>(v, current_idx); - } + // quotation mark (0x22) + case '"': + { + result[pos + 1] = '"'; + pos += 2; + break; + } - 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); - } + // reverse solidus (0x5c) + case '\\': + { + // nothing to change + pos += 2; + break; + } - 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); - } + // backspace (0x08) + case '\b': + { + result[pos + 1] = 'b'; + pos += 2; + break; + } - 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); - } + // formfeed (0x0c) + case '\f': + { + result[pos + 1] = 'f'; + pos += 2; + break; + } - 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) + // newline (0x0a) + case '\n': { - result.push_back(from_msgpack_internal(v, idx)); + result[pos + 1] = 'n'; + pos += 2; + break; } - 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) + // carriage return (0x0d) + case '\r': { - result.push_back(from_msgpack_internal(v, idx)); + result[pos + 1] = 'r'; + pos += 2; + break; } - 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) + // horizontal tab (0x09) + case '\t': { - msgpack_expect_string(v, idx); - std::string key = from_msgpack_internal(v, idx); - result[key] = from_msgpack_internal(v, idx); + result[pos + 1] = 't'; + pos += 2; + break; } - 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) + default: { - msgpack_expect_string(v, idx); - std::string key = from_msgpack_internal(v, idx); - result[key] = from_msgpack_internal(v, idx); - } - return result; - } + if (c >= 0x00 and c <= 0x1f) + { + // convert a number 0..15 to its hex representation + // (0..f) + static const char hexify[16] = + { + '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] + }) + { + result[++pos] = m; + } - default: - { - std::stringstream ss; - ss << std::hex << static_cast<int>(v[current_idx]); - JSON_THROW(parse_error::create( - 112, current_idx + 1, - "error reading MessagePack; last byte: 0x" + ss.str())); - } + ++pos; + } + else + { + // all other characters are added as-is + result[pos++] = c; + } + break; + } } } - } - - /*! - @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 + return result; + } - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from CBOR were - used in the given vector @a v or if the input is not valid CBOR - @throw parse_error.113 if a string was expected as map key, but not found - @sa https://tools.ietf.org/html/rfc7049 + /*! + @brief locale-independent serialization for built-in arithmetic types */ - static basic_json from_cbor_internal(const std::vector<uint8_t> &v, - size_t &idx) + struct numtostr { - // store and increment index - const size_t current_idx = idx++; - - // make sure reading 1 byte is safe - check_length(v.size(), 1, current_idx); - - switch (v[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) + public: + template<typename NumberType> + numtostr(NumberType value) { - std::string result; - while (static_cast<void>(check_length(v.size(), 1, idx)), - v[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; + x_write(value, std::is_integral<NumberType>()); } - case 0x98: // array (one-byte uint8_t for n follows) + const char* c_str() const { - 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; + return m_buf.data(); } - 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; - } + private: + /// a (hopefully) large enough character buffer + std::array < char, 64 > m_buf{{}}; - case 0x9a: // array (four-byte uint32_t for n follow) + template<typename NumberType> + void x_write(NumberType x, /*is_integral=*/std::true_type) { - 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) + // special case for "0" + if (x == 0) { - result.push_back(from_cbor_internal(v, idx)); + m_buf[0] = '0'; + return; } - 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; - } + const bool is_negative = x < 0; + size_t i = 0; - case 0x9f: // array (indefinite length) - { - basic_json result = value_t::array; - while (static_cast<void>(check_length(v.size(), 1, idx)), - v[idx] != 0xff) + // spare 1 byte for '\0' + while (x != 0 and i < m_buf.size() - 1) { - 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) - { - cbor_expect_string(v, idx); - std::string key = from_cbor_internal(v, idx); - result[key] = from_cbor_internal(v, idx); + const auto digit = std::labs(static_cast<long>(x % 10)); + m_buf[i++] = static_cast<char>('0' + digit); + x /= 10; } - 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) - { - cbor_expect_string(v, idx); - std::string key = from_cbor_internal(v, idx); - result[key] = from_cbor_internal(v, idx); - } - return result; - } + // make sure the number has been processed completely + assert(x == 0); - 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) + if (is_negative) { - cbor_expect_string(v, idx); - std::string key = from_cbor_internal(v, idx); - result[key] = from_cbor_internal(v, idx); + // make sure there is capacity for the '-' + assert(i < m_buf.size() - 2); + m_buf[i++] = '-'; } - 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) - { - cbor_expect_string(v, idx); - std::string key = from_cbor_internal(v, idx); - result[key] = from_cbor_internal(v, idx); - } - return result; + std::reverse(m_buf.begin(), m_buf.begin() + i); } - case 0xbb: // map (eight-byte uint64_t for n follow) + template<typename NumberType> + void x_write(NumberType x, /*is_integral=*/std::false_type) { - 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) + // special case for 0.0 and -0.0 + if (x == 0) { - cbor_expect_string(v, idx); - std::string key = from_cbor_internal(v, idx); - result[key] = from_cbor_internal(v, idx); + size_t i = 0; + if (std::signbit(x)) + { + m_buf[i++] = '-'; + } + m_buf[i++] = '0'; + m_buf[i++] = '.'; + m_buf[i] = '0'; + return; } - return result; - } - case 0xbf: // map (indefinite length) - { - basic_json result = value_t::object; - while (static_cast<void>(check_length(v.size(), 1, idx)), - v[idx] != 0xff) - { - cbor_expect_string(v, idx); - std::string key = from_cbor_internal(v, idx); - result[key] = from_cbor_internal(v, idx); - } - // skip break byte (0xFF) - idx += 1; - return result; - } + // get number of digits for a text -> float -> text round-trip + static constexpr auto d = std::numeric_limits<NumberType>::digits10; - case 0xf4: // false - { - return false; - } + // the actual conversion + const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); - case 0xf5: // true - { - return true; - } + // negative value indicates an error + assert(written_bytes > 0); + // check if buffer was large enough + assert(static_cast<size_t>(written_bytes) < m_buf.size()); - case 0xf6: // null - { - return value_t::null; - } + // read information from locale + const auto loc = localeconv(); + assert(loc != nullptr); + const char thousands_sep = !loc->thousands_sep ? '\0' + : loc->thousands_sep[0]; - case 0xf9: // Half-Precision Float (two-byte IEEE 754) - { - idx += 2; // skip two content bytes + const char decimal_point = !loc->decimal_point ? '\0' + : loc->decimal_point[0]; - // 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. - check_length(v.size(), 2, current_idx + 1); - 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 + // erase thousands separator + if (thousands_sep != '\0') { - val = mant == 0 ? std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::quiet_NaN(); + const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); + std::fill(end, m_buf.end(), '\0'); } - return (half & 0x8000) != 0 ? -val : val; - } - case 0xfa: // Single-Precision Float (four-byte IEEE 754) - { - // copy bytes in reverse order into the float variable - float res; - check_length(v.size(), sizeof(float), current_idx + 1); - for (size_t byte = 0; byte < sizeof(float); ++byte) + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') { - reinterpret_cast<uint8_t *>(&res)[sizeof(float) - byte - 1] = - v[current_idx + 1 + byte]; + for (auto& c : m_buf) + { + if (c == decimal_point) + { + c = '.'; + break; + } + } } - idx += sizeof(float); // skip content bytes - return res; - } - case 0xfb: // Double-Precision Float (eight-byte IEEE 754) - { - // copy bytes in reverse order into the double variable - double res; - check_length(v.size(), sizeof(double), current_idx + 1); - for (size_t byte = 0; byte < sizeof(double); ++byte) + // determine if need to append ".0" + size_t i = 0; + bool value_is_int_like = true; + for (i = 0; i < m_buf.size(); ++i) { - 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) - { - std::stringstream ss; - ss << std::hex << static_cast<int>(v[current_idx]); - JSON_THROW(parse_error::create(112, current_idx + 1, - "error reading CBOR; last byte: 0x" + - ss.str())); - } - } - } - -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. - - The library uses the following mapping from JSON values types to - MessagePack types according to the MessagePack specification: - - JSON value type | value/range | MessagePack type | - first byte - --------------- | --------------------------------- | ---------------- | - ---------- - null | `null` | nil | - 0xc0 - boolean | `true` | true | - 0xc3 - boolean | `false` | false | - 0xc2 - number_integer | -9223372036854775808..-2147483649 | int64 | - 0xd3 - number_integer | -2147483648..-32769 | int32 | - 0xd2 - number_integer | -32768..-129 | int16 |Â 0xd1 - number_integer | -128..-33 | int8 | - 0xd0 - number_integer | -32..-1 | negative fixint | - 0xe0..0xff - number_integer | 0..127 | positive fixint | - 0x00..0x7f - number_integer | 128..255 | uint 8 |Â 0xcc - number_integer | 256..65535 | uint 16 | - 0xcd - number_integer | 65536..4294967295 | uint 32 | - 0xce - number_integer | 4294967296..18446744073709551615 | uint 64 | - 0xcf - number_unsigned | 0..127 | positive fixint | - 0x00..0x7f - number_unsigned | 128..255 | uint 8 |Â 0xcc - number_unsigned | 256..65535 | uint 16 | - 0xcd - number_unsigned | 65536..4294967295 | uint 32 | - 0xce - number_unsigned | 4294967296..18446744073709551615 | uint 64 | - 0xcf - number_float | *any value* | float 64 | - 0xcb - string | *length*: 0..31 | fixstr | - 0xa0..0xbf - string | *length*: 32..255 | str 8 | - 0xd9 - string | *length*: 256..65535 | str 16 | - 0xda - string | *length*: 65536..4294967295 | str 32 | - 0xdb - array | *size*: 0..15 | fixarray | - 0x90..0x9f - array | *size*: 16..65535 | array 16 | - 0xdc - array | *size*: 65536..4294967295 | array 32 | - 0xdd - object | *size*: 0..15 | fix map | - 0x80..0x8f - object | *size*: 16..65535 | map 16 | - 0xde - object | *size*: 65536..4294967295 | map 32 | - 0xdf - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a MessagePack value. - - @note The following values can **not** be converted to a MessagePack value: - - strings with more than 4294967295 bytes - - arrays with more than 4294967295 elements - - objects with more than 4294967295 elements - - @note The following MessagePack types are not used in the conversion: - - bin 8 - bin 32 (0xc4..0xc6) - - ext 8 - ext 32 (0xc7..0xc9) - - float 32 (0xca) - - fixext 1 - fixext 16 (0xd4..0xd8) - - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. - - @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>&, const size_t) for the - analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format - - @since version 2.0.9 - */ - 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. - - The library maps MessagePack types to JSON value types as follows: - - MessagePack type | JSON value type | first byte - ---------------- | --------------- | ---------- - positive fixint | number_unsigned | 0x00..0x7f - fixmap | object | 0x80..0x8f - fixarray | array | 0x90..0x9f - fixstr | string | 0xa0..0xbf - nil | `null` | 0xc0 - false | `false` | 0xc2 - true | `true` | 0xc3 - float 32 | number_float | 0xca - float 64 | number_float | 0xcb - uint 8 | number_unsigned | 0xcc - uint 16 | number_unsigned | 0xcd - uint 32 | number_unsigned | 0xce - uint 64 | number_unsigned | 0xcf - int 8 | number_integer | 0xd0 - int 16 | number_integer | 0xd1 - int 32 | number_integer | 0xd2 - int 64 | number_integer | 0xd3 - str 8 | string | 0xd9 - str 16 | string | 0xda - str 32 | string | 0xdb - array 16 | array | 0xdc - array 32 | array | 0xdd - map 16 | object | 0xde - map 32 | object | 0xdf - negative fixint | number_integer | 0xe0-0xff - - @warning The mapping is **incomplete** in the sense that not all - MessagePack types can be converted to a JSON value. The following - MessagePack types are not supported and will yield parse errors: - - bin 8 - bin 32 (0xc4..0xc6) - - ext 8 - ext 32 (0xc7..0xc9) - - fixext 1 - fixext 16 (0xd4..0xd8) - - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. - - @param[in] v a byte vector in MessagePack format - @param[in] start_index the index to start reading from @a v (0 by default) - @return deserialized JSON value - - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from MessagePack were - used in the given vector @a v or if the input is not valid MessagePack - @throw parse_error.113 if a string was expected as map key, but not found - - @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>&, const size_t) for the - related CBOR format + // break when end of number is reached + if (m_buf[i] == '\0') + { + break; + } - @since version 2.0.9, parameter @a start_index since 2.1.1 - */ - static basic_json from_msgpack(const std::vector<uint8_t> &v, - const size_t start_index = 0) - { - size_t i = start_index; - return from_msgpack_internal(v, i); - } + // check if we find non-int character + value_is_int_like = value_is_int_like and m_buf[i] != '.' and + m_buf[i] != 'e' and m_buf[i] != 'E'; + } - /*! - @brief create a MessagePack serialization of a given JSON value + if (value_is_int_like) + { + // there must be 2 bytes left for ".0" + assert((i + 2) < m_buf.size()); + // we write to the end of the number + assert(m_buf[i] == '\0'); + assert(m_buf[i - 1] != '\0'); - 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. + // add ".0" + m_buf[i] = '.'; + m_buf[i + 1] = '0'; - The library uses the following mapping from JSON values types to - CBOR types according to the CBOR specification (RFC 7049): - - JSON value type | value/range | CBOR type - | first byte - --------------- | ------------------------------------------ | - ---------------------------------- | --------------- - null | `null` | Null | 0xf6 - boolean | `true` | True | 0xf5 - boolean | `false` | False | 0xf4 - number_integer | -9223372036854775808..-2147483649 | Negative - integer (8 bytes follow) | 0x3b - number_integer | -2147483648..-32769 | Negative - integer (4 bytes follow) | 0x3a - number_integer | -32768..-129 | Negative - integer (2 bytes follow) | 0x39 - number_integer | -128..-25 | Negative - integer (1 byte follow) | 0x38 - number_integer | -24..-1 | Negative - integer | 0x20..0x37 - number_integer | 0..23 | Integer - | 0x00..0x17 - number_integer | 24..255 | Unsigned - integer (1 byte follow) | 0x18 - number_integer | 256..65535 | Unsigned - integer (2 bytes follow) | 0x19 - number_integer | 65536..4294967295 | Unsigned - integer (4 bytes follow) | 0x1a - number_integer | 4294967296..18446744073709551615 | Unsigned - integer (8 bytes follow) | 0x1b - number_unsigned | 0..23 | Integer - | 0x00..0x17 - number_unsigned | 24..255 | Unsigned - integer (1 byte follow) | 0x18 - number_unsigned | 256..65535 | Unsigned - integer (2 bytes follow) | 0x19 - number_unsigned | 65536..4294967295 | Unsigned - integer (4 bytes follow) | 0x1a - number_unsigned | 4294967296..18446744073709551615 | Unsigned - integer (8 bytes follow) | 0x1b - number_float | *any value* | - Double-Precision Float | 0xfb - string | *length*: 0..23 | UTF-8 string - | 0x60..0x77 - string | *length*: 23..255 | UTF-8 string - (1 byte follow) | 0x78 - string | *length*: 256..65535 | UTF-8 string - (2 bytes follow) | 0x79 - string | *length*: 65536..4294967295 | UTF-8 string - (4 bytes follow) | 0x7a - string | *length*: 4294967296..18446744073709551615 | UTF-8 string - (8 bytes follow) | 0x7b - array | *size*: 0..23 | array - | 0x80..0x97 - array | *size*: 23..255 | array (1 byte - follow) | 0x98 - array | *size*: 256..65535 | array (2 - bytes follow) | 0x99 - array | *size*: 65536..4294967295 | array (4 - bytes follow) | 0x9a - array | *size*: 4294967296..18446744073709551615 | array (8 - bytes follow) | 0x9b - object | *size*: 0..23 | map - | 0xa0..0xb7 - object | *size*: 23..255 | map (1 byte - follow) | 0xb8 - object | *size*: 256..65535 | map (2 bytes - follow) | 0xb9 - object | *size*: 65536..4294967295 | map (4 bytes - follow) | 0xba - object | *size*: 4294967296..18446744073709551615 | map (8 bytes - follow) | 0xbb - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a CBOR value. - - @note The following CBOR types are not used in the conversion: - - byte strings (0x40..0x5f) - - UTF-8 strings terminated by "break" (0x7f) - - arrays terminated by "break" (0x9f) - - maps terminated by "break" (0xbf) - - date/time (0xc0..0xc1) - - bignum (0xc2..0xc3) - - decimal fraction (0xc4) - - bigfloat (0xc5) - - tagged items (0xc6..0xd4, 0xd8..0xdb) - - expected conversions (0xd5..0xd7) - - simple values (0xe0..0xf3, 0xf8) - - undefined (0xf7) - - half and single-precision floats (0xf9-0xfa) - - break (0xff) + // the resulting string is properly terminated + assert(m_buf[i + 2] == '\0'); + } + } + }; - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector - @complexity Linear in the size of the JSON value @a j. + /*! + @brief internal implementation of the serialization function - @liveexample{The example shows the serialization of a JSON value to a byte - vector in CBOR format.,to_cbor} + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. Note that - @sa http://cbor.io - @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the - analogous deserialization - @sa @ref to_msgpack(const basic_json& for the related MessagePack format + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format - @since version 2.0.9 + @param[out] o stream to write to + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) */ - static std::vector<uint8_t> to_cbor(const basic_json &j) + void dump(std::ostream& o, + const bool pretty_print, + const unsigned int indent_step, + const unsigned int current_indent = 0) const { - std::vector<uint8_t> result; - to_cbor_internal(j, result); - return result; - } + // variable to hold indentation for recursive calls + unsigned int new_indent = current_indent; - /*! - @brief create a JSON value from a byte vector in CBOR format + switch (m_type) + { + case value_t::object: + { + if (m_value.object->empty()) + { + o << "{}"; + return; + } - Deserializes a given byte vector @a v to a JSON value using the CBOR - (Concise Binary Object Representation) serialization format. + o << "{"; - The library maps CBOR types to JSON value types as follows: - - CBOR type | JSON value type | first byte - ---------------------- | --------------- | ---------- - Integer | number_unsigned | 0x00..0x17 - Unsigned integer | number_unsigned | 0x18 - Unsigned integer | number_unsigned | 0x19 - Unsigned integer | number_unsigned | 0x1a - Unsigned integer | number_unsigned | 0x1b - Negative integer | number_integer | 0x20..0x37 - Negative integer | number_integer | 0x38 - Negative integer | number_integer | 0x39 - Negative integer | number_integer | 0x3a - Negative integer | number_integer | 0x3b - Negative integer | number_integer | 0x40..0x57 - UTF-8 string | string | 0x60..0x77 - UTF-8 string | string | 0x78 - UTF-8 string | string | 0x79 - UTF-8 string | string | 0x7a - UTF-8 string | string | 0x7b - UTF-8 string | string | 0x7f - array | array | 0x80..0x97 - array | array | 0x98 - array | array | 0x99 - array | array | 0x9a - array | array | 0x9b - array | array | 0x9f - map | object | 0xa0..0xb7 - map | object | 0xb8 - map | object | 0xb9 - map | object | 0xba - map | object | 0xbb - map | object | 0xbf - False | `false` | 0xf4 - True |Â `true` | 0xf5 - Nill | `null` | 0xf6 - Half-Precision Float | number_float | 0xf9 - Single-Precision Float | number_float | 0xfa - Double-Precision Float | number_float | 0xfb - - @warning The mapping is **incomplete** in the sense that not all CBOR - types can be converted to a JSON value. The following CBOR types - are not supported and will yield parse errors (parse_error.112): - - byte strings (0x40..0x5f) - - date/time (0xc0..0xc1) - - bignum (0xc2..0xc3) - - decimal fraction (0xc4) - - bigfloat (0xc5) - - tagged items (0xc6..0xd4, 0xd8..0xdb) - - expected conversions (0xd5..0xd7) - - simple values (0xe0..0xf3, 0xf8) - - undefined (0xf7) - - @warning CBOR allows map keys of any type, whereas JSON only allows - strings as keys in object values. Therefore, CBOR maps with keys - other than UTF-8 strings are rejected (parse_error.113). - - @note Any CBOR output created @ref to_cbor can be successfully parsed by - @ref from_cbor. + // increase indentation + if (pretty_print) + { + new_indent += indent_step; + o << "\n"; + } - @param[in] v a byte vector in CBOR format - @param[in] start_index the index to start reading from @a v (0 by default) - @return deserialized JSON value + for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) + { + if (i != m_value.object->cbegin()) + { + o << (pretty_print ? ",\n" : ","); + } + o << string_t(new_indent, ' ') << "\"" + << escape_string(i->first) << "\":" + << (pretty_print ? " " : ""); + i->second.dump(o, pretty_print, indent_step, new_indent); + } - @throw parse_error.110 if the given vector ends prematurely - @throw parse_error.112 if unsupported features from CBOR were - used in the given vector @a v or if the input is not valid CBOR - @throw parse_error.113 if a string was expected as map key, but not found + // decrease indentation + if (pretty_print) + { + new_indent -= indent_step; + o << "\n"; + } - @complexity Linear in the size of the byte vector @a v. + o << string_t(new_indent, ' ') + "}"; + return; + } - @liveexample{The example shows the deserialization of a byte vector in CBOR - format to a JSON value.,from_cbor} + case value_t::array: + { + if (m_value.array->empty()) + { + o << "[]"; + return; + } - @sa http://cbor.io - @sa @ref to_cbor(const basic_json&) for the analogous serialization - @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the - related MessagePack format + o << "["; - @since version 2.0.9, parameter @a start_index since 2.1.1 - */ - static basic_json from_cbor(const std::vector<uint8_t> &v, - const size_t start_index = 0) - { - size_t i = start_index; - return from_cbor_internal(v, i); - } + // increase indentation + if (pretty_print) + { + new_indent += indent_step; + o << "\n"; + } - /// @} + for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) + { + if (i != m_value.array->cbegin()) + { + o << (pretty_print ? ",\n" : ","); + } + o << string_t(new_indent, ' '); + i->dump(o, pretty_print, indent_step, new_indent); + } - /////////////////////////// - // convenience functions // - /////////////////////////// + // decrease indentation + if (pretty_print) + { + new_indent -= indent_step; + o << "\n"; + } - /*! - @brief return the type as string + o << string_t(new_indent, ' ') << "]"; + return; + } - 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. + case value_t::string: + { + o << string_t("\"") << escape_string(*m_value.string) << "\""; + return; + } - @return basically a string representation of a the @a m_type member + case value_t::boolean: + { + o << (m_value.boolean ? "true" : "false"); + return; + } - @complexity Constant. + case value_t::number_integer: + { + o << numtostr(m_value.number_integer).c_str(); + return; + } - @liveexample{The following code exemplifies `type_name()` for all JSON - types.,type_name} + case value_t::number_unsigned: + { + o << numtostr(m_value.number_unsigned).c_str(); + return; + } - @since version 1.0.0, public since 2.1.0 - */ - std::string type_name() const - { - { - switch (m_type) + case value_t::number_float: { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; + o << numtostr(m_value.number_float).c_str(); + return; + } + case value_t::discarded: - return "discarded"; - default: - return "number"; + { + o << "<discarded>"; + return; + } + + case value_t::null: + { + o << "null"; + return; } } } -private: + private: ////////////////////// // member variables // ////////////////////// @@ -9832,7 +8555,8 @@ private: /// the value of the current element json_value m_value = {}; -private: + + private: /////////////// // iterators // /////////////// @@ -9848,13 +8572,23 @@ private: */ class primitive_iterator_t { - public: - difference_type get_value() const noexcept { return m_it; } + public: + + difference_type get_value() const noexcept + { + return m_it; + } /// set iterator to a defined beginning - void set_begin() noexcept { m_it = begin_value; } + void set_begin() noexcept + { + m_it = begin_value; + } /// set iterator to a defined past the end - void set_end() noexcept { m_it = end_value; } + void set_end() noexcept + { + m_it = end_value; + } /// return whether the iterator can be dereferenced constexpr bool is_begin() const noexcept @@ -9863,40 +8597,37 @@ private: } /// return whether the iterator is at end - constexpr bool is_end() const noexcept { return (m_it == end_value); } + constexpr bool is_end() const noexcept + { + return (m_it == end_value); + } - friend constexpr bool operator==(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it == rhs.m_it; } - friend constexpr bool operator!=(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return !(lhs == rhs); } - friend constexpr bool operator<(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it < rhs.m_it; } - friend constexpr bool operator<=(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it <= rhs.m_it; } - friend constexpr bool operator>(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it > rhs.m_it; } - friend constexpr bool operator>=(primitive_iterator_t lhs, - primitive_iterator_t rhs) noexcept + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it >= rhs.m_it; } @@ -9908,19 +8639,17 @@ private: return result; } - friend constexpr difference_type - operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { return lhs.m_it - rhs.m_it; } - friend std::ostream &operator<<(std::ostream &os, - primitive_iterator_t it) + friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) { return os << it.m_it; } - primitive_iterator_t &operator++() + primitive_iterator_t& operator++() { ++m_it; return *this; @@ -9933,7 +8662,7 @@ private: return result; } - primitive_iterator_t &operator--() + primitive_iterator_t& operator--() { --m_it; return *this; @@ -9946,25 +8675,24 @@ private: return result; } - primitive_iterator_t &operator+=(difference_type n) + primitive_iterator_t& operator+=(difference_type n) { m_it += n; return *this; } - primitive_iterator_t &operator-=(difference_type n) + primitive_iterator_t& operator-=(difference_type n) { m_it -= n; return *this; } - private: + private: static constexpr difference_type begin_value = 0; static constexpr difference_type end_value = begin_value + 1; /// iterator as signed integer type - difference_type m_it = - std::numeric_limits<std::ptrdiff_t>::denorm_min(); + difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min(); }; /*! @@ -9984,38 +8712,38 @@ private: primitive_iterator_t primitive_iterator; /// create an uninitialized internal_iterator - internal_iterator() noexcept : object_iterator(), - array_iterator(), - primitive_iterator() - { - } + internal_iterator() noexcept + : object_iterator(), array_iterator(), primitive_iterator() + {} }; /// proxy class for the iterator_wrapper functions - template <typename IteratorType> + template<typename IteratorType> class iteration_proxy { - private: + private: /// helper class for iteration class iteration_proxy_internal { - private: + private: /// the iterator IteratorType anchor; /// an index for arrays (used to create key names) size_t array_index = 0; - public: + public: explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) - { - } + {} /// dereference operator (needed for range-based for) - iteration_proxy_internal &operator*() { return *this; } + iteration_proxy_internal& operator*() + { + return *this; + } /// increment operator (needed for range-based for) - iteration_proxy_internal &operator++() + iteration_proxy_internal& operator++() { ++anchor; ++array_index; @@ -10024,7 +8752,7 @@ private: } /// inequality operator (needed for range-based for) - bool operator!=(const iteration_proxy_internal &o) const + bool operator!= (const iteration_proxy_internal& o) const { return anchor != o.anchor; } @@ -10036,23 +8764,23 @@ private: switch (anchor.m_object->type()) { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } + // use integer array index as key + case value_t::array: + { + return std::to_string(array_index); + } - // use key from the object - case value_t::object: - { - return anchor.key(); - } + // use key from the object + case value_t::object: + { + return anchor.key(); + } - // use an empty key for all primitive types - default: - { - return ""; - } + // use an empty key for all primitive types + default: + { + return ""; + } } } @@ -10066,12 +8794,11 @@ private: /// the container to iterate typename IteratorType::reference container; - public: + public: /// construct iteration proxy from a container explicit iteration_proxy(typename IteratorType::reference cont) - : container(cont) - { - } + : container(cont) + {} /// return iterator begin (needed for range-based for) iteration_proxy_internal begin() noexcept @@ -10086,7 +8813,7 @@ private: } }; -public: + public: /*! @brief a template for a random access iterator for the @ref basic_json class @@ -10100,39 +8827,36 @@ public: on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + - [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, simplified in version 2.0.9 */ - template <typename U> + 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, + 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: + 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 std::conditional<std::is_const<U>::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; + 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 std::conditional<std::is_const<U>::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; + 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; @@ -10145,29 +8869,30 @@ public: @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ - explicit iter_impl(pointer object) noexcept : m_object(object) + explicit iter_impl(pointer object) noexcept + : m_object(object) { assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } + case basic_json::value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } + case basic_json::value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } } } @@ -10197,29 +8922,28 @@ public: @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) - { - } + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} /*! @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 std::is_nothrow_move_assignable< - internal_iterator>::value) + 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 + std::is_nothrow_move_assignable<internal_iterator>::value + ) { std::swap(m_object, other.m_object); std::swap(m_it, other.m_it); return *this; } - private: + private: /*! @brief set the iterator to the first value @pre The iterator is initialized; i.e. `m_object != nullptr`. @@ -10230,30 +8954,30 @@ public: switch (m_object->m_type) { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } - case basic_json::value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } + case basic_json::value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } - default: - { - m_it.primitive_iterator.set_begin(); - break; - } + default: + { + m_it.primitive_iterator.set_begin(); + break; + } } } @@ -10267,27 +8991,27 @@ public: switch (m_object->m_type) { - case basic_json::value_t::object: - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } - default: - { - m_it.primitive_iterator.set_end(); - break; - } + default: + { + m_it.primitive_iterator.set_end(); + break; + } } } - public: + public: /*! @brief return a reference to the value pointed to by the iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. @@ -10298,32 +9022,32 @@ public: switch (m_object->m_type) { - case basic_json::value_t::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_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } - case basic_json::value_t::null: - { - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } - default: - { - if (m_it.primitive_iterator.is_begin()) + case basic_json::value_t::null: { - return *m_object; + JSON_THROW(std::out_of_range("cannot get value")); } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return *m_object; + } + + JSON_THROW(std::out_of_range("cannot get value")); + } } } @@ -10335,29 +9059,29 @@ public: { assert(m_object != nullptr); - switch (m_object->m_type) - { - case basic_json::value_t::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_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } - - default: + switch (m_object->m_type) { - if (m_it.primitive_iterator.is_begin()) + case basic_json::value_t::object: { - return m_object; + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return m_object; + } + + JSON_THROW(std::out_of_range("cannot get value")); + } } } @@ -10376,29 +9100,29 @@ public: @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl &operator++() + iter_impl& operator++() { assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, 1); - break; - } + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, 1); - break; - } + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } - default: - { - ++m_it.primitive_iterator; - break; - } + default: + { + ++m_it.primitive_iterator; + break; + } } return *this; @@ -10419,29 +9143,29 @@ public: @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl &operator--() + iter_impl& operator--() { assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, -1); - break; - } + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } - default: - { - --m_it.primitive_iterator; - break; - } + default: + { + --m_it.primitive_iterator; + break; + } } return *this; @@ -10451,34 +9175,32 @@ public: @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const iter_impl &other) const + bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(invalid_iterator::create( - 212, "cannot compare iterators of different containers")); + JSON_THROW(std::domain_error("cannot compare iterators of different containers")); } assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - return (m_it.object_iterator == other.m_it.object_iterator); - } + case basic_json::value_t::object: + { + return (m_it.object_iterator == other.m_it.object_iterator); + } - case basic_json::value_t::array: - { - return (m_it.array_iterator == other.m_it.array_iterator); - } + case basic_json::value_t::array: + { + return (m_it.array_iterator == other.m_it.array_iterator); + } - default: - { - return (m_it.primitive_iterator == - other.m_it.primitive_iterator); - } + default: + { + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } } } @@ -10486,7 +9208,7 @@ public: @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const iter_impl &other) const + bool operator!=(const iter_impl& other) const { return not operator==(other); } @@ -10495,35 +9217,32 @@ public: @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<(const iter_impl &other) const + bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - JSON_THROW(invalid_iterator::create( - 212, "cannot compare iterators of different containers")); + JSON_THROW(std::domain_error("cannot compare iterators of different containers")); } assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create( - 213, "cannot compare order of object iterators")); - } + case basic_json::value_t::object: + { + JSON_THROW(std::domain_error("cannot compare order of object iterators")); + } - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } + case basic_json::value_t::array: + { + return (m_it.array_iterator < other.m_it.array_iterator); + } - default: - { - return (m_it.primitive_iterator < - other.m_it.primitive_iterator); - } + default: + { + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } } } @@ -10531,16 +9250,16 @@ public: @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<=(const iter_impl &other) const + bool operator<=(const iter_impl& other) const { - return not other.operator<(*this); + return not other.operator < (*this); } /*! @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>(const iter_impl &other) const + bool operator>(const iter_impl& other) const { return not operator<=(other); } @@ -10549,7 +9268,7 @@ public: @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>=(const iter_impl &other) const + bool operator>=(const iter_impl& other) const { return not operator<(other); } @@ -10558,29 +9277,28 @@ public: @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl &operator+=(difference_type i) + iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create( - 209, "cannot use offsets with object iterators")); - } + case basic_json::value_t::object: + { + JSON_THROW(std::domain_error("cannot use offsets with object iterators")); + } - case basic_json::value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } - default: - { - m_it.primitive_iterator += i; - break; - } + default: + { + m_it.primitive_iterator += i; + break; + } } return *this; @@ -10590,7 +9308,10 @@ public: @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - iter_impl &operator-=(difference_type i) { return operator+=(-i); } + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } /*! @brief add to iterator @@ -10618,27 +9339,26 @@ public: @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - difference_type operator-(const iter_impl &other) const + difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); switch (m_object->m_type) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create( - 209, "cannot use offsets with object iterators")); - } + case basic_json::value_t::object: + { + JSON_THROW(std::domain_error("cannot use offsets with object iterators")); + } - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } + case basic_json::value_t::array: + { + return m_it.array_iterator - other.m_it.array_iterator; + } - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } + default: + { + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } } } @@ -10652,31 +9372,30 @@ public: switch (m_object->m_type) { - case basic_json::value_t::object: - { - JSON_THROW(invalid_iterator::create( - 208, "cannot use operator[] for object iterators")); - } - - case basic_json::value_t::array: - { - return *std::next(m_it.array_iterator, n); - } + case basic_json::value_t::object: + { + JSON_THROW(std::domain_error("cannot use operator[] for object iterators")); + } - case basic_json::value_t::null: - { - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + case basic_json::value_t::array: + { + return *std::next(m_it.array_iterator, n); + } - default: - { - if (m_it.primitive_iterator.get_value() == -n) + case basic_json::value_t::null: { - return *m_object; + JSON_THROW(std::out_of_range("cannot get value")); } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); - } + default: + { + if (m_it.primitive_iterator.get_value() == -n) + { + return *m_object; + } + + JSON_THROW(std::out_of_range("cannot get value")); + } } } @@ -10693,17 +9412,19 @@ public: return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create( - 207, "cannot use key() for non-object iterators")); + JSON_THROW(std::domain_error("cannot use key() for non-object iterators")); } /*! @brief return the value of an iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - reference value() const { return operator*(); } + reference value() const + { + return operator*(); + } - private: + private: /// associated JSON instance pointer m_object = nullptr; /// the actual iterator of the associated instance @@ -10718,8 +9439,7 @@ public: create @ref const_reverse_iterator). @requirement The class satisfies the following concept requirements: - - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + - [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): @@ -10728,27 +9448,24 @@ public: @since version 1.0.0 */ - template <typename Base> + template<typename Base> class json_reverse_iterator : public std::reverse_iterator<Base> { - public: + public: /// shortcut to the reverse iterator adaptor using base_iterator = std::reverse_iterator<Base>; /// the reference type for the pointed-to element using reference = typename Base::reference; /// create reverse iterator from iterator - json_reverse_iterator( - const typename base_iterator::iterator_type &it) noexcept + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept : base_iterator(it) - { - } + {} /// create reverse iterator from base class - json_reverse_iterator(const base_iterator &it) noexcept + json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) - { - } + {} /// post-increment (it++) json_reverse_iterator operator++(int) @@ -10757,7 +9474,7 @@ public: } /// pre-increment (++it) - json_reverse_iterator &operator++() + json_reverse_iterator& operator++() { base_iterator::operator++(); return *this; @@ -10770,14 +9487,14 @@ public: } /// pre-decrement (--it) - json_reverse_iterator &operator--() + json_reverse_iterator& operator--() { base_iterator::operator--(); return *this; } /// add to iterator - json_reverse_iterator &operator+=(difference_type i) + json_reverse_iterator& operator+=(difference_type i) { base_iterator::operator+=(i); return *this; @@ -10800,7 +9517,7 @@ public: } /// return difference - difference_type operator-(const json_reverse_iterator &other) const + difference_type operator-(const json_reverse_iterator& other) const { return this->base() - other.base(); } @@ -10822,11 +9539,12 @@ public: reference value() const { auto it = --this->base(); - return it.operator*(); + return it.operator * (); } }; -private: + + private: ////////////////////// // lexer and parser // ////////////////////// @@ -10840,25 +9558,22 @@ private: */ class lexer { - public: + public: /// token types for the parser enum class token_type { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_unsigned, ///< an unsigned integer -- use get_number() for - /// actual value - value_integer, ///< a signed integer -- use get_number() for actual - /// value - value_float, ///< an floating point number -- use get_number() for - /// actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number() for actual value + value_integer, ///< a signed integer -- use get_number() for actual value + value_float, ///< an floating point number -- use get_number() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` name_separator, ///< the name separator `:` value_separator, ///< the value separator `,` parse_error, ///< indicating a parse error @@ -10869,7 +9584,7 @@ private: using lexer_char_t = unsigned char; /// a lexer from a buffer with given length - lexer(const lexer_char_t *buff, const size_t len) noexcept + lexer(const lexer_char_t* buff, const size_t len) noexcept : m_content(buff) { assert(m_content != nullptr); @@ -10877,24 +9592,21 @@ private: m_limit = m_content + len; } - /*! - @brief a lexer from an input stream - @throw parse_error.111 if input stream is in a bad state - */ - explicit lexer(std::istream &s) : m_stream(&s), m_line_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() { // immediately abort if stream is erroneous if (s.fail()) { - JSON_THROW(parse_error::create(111, 0, "bad input stream")); + JSON_THROW(std::invalid_argument("stream error")); } // fill buffer fill_line_buffer(); // skip UTF-8 byte-order mark - if (m_line_buffer.size() >= 3 and - m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") { m_line_buffer[0] = ' '; m_line_buffer[1] = ' '; @@ -10904,8 +9616,8 @@ private: // switch off unwanted functions (due to pointer members) lexer() = delete; - lexer(const lexer &) = delete; - lexer operator=(const lexer &) = delete; + lexer(const lexer&) = delete; + lexer operator=(const lexer&) = delete; /*! @brief create a string from one or two Unicode code points @@ -10921,17 +9633,17 @@ private: @return string representation of the code point; the length of the result string is between 1 and 4 characters. - @throw parse_error.102 if the low surrogate is invalid; example: - `""missing or wrong low surrogate""` - @throw parse_error.103 if code point is > 0x10ffff; example: `"code + @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> */ - string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) const + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) { // calculate the code point from the given code points std::size_t codepoint = codepoint1; @@ -10954,8 +9666,7 @@ private: } else { - JSON_THROW(parse_error::create( - 102, get_position(), "missing or wrong low surrogate")); + JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); } } @@ -10964,44 +9675,32 @@ private: if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) - result.append( - 1, static_cast<typename string_t::value_type>(codepoint)); + result.append(1, static_cast<typename string_t::value_type>(codepoint)); } else if (codepoint <= 0x7ff) { // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>( - 0xC0 | (codepoint >> 6))); - result.append(1, static_cast<typename string_t::value_type>( - 0x80 | (codepoint & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); } else if (codepoint <= 0xffff) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>( - 0xE0 | (codepoint >> 12))); - result.append(1, static_cast<typename string_t::value_type>( - 0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>( - 0x80 | (codepoint & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); } else if (codepoint <= 0x10ffff) { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast<typename string_t::value_type>( - 0xF0 | (codepoint >> 18))); - result.append(1, static_cast<typename string_t::value_type>( - 0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>( - 0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast<typename string_t::value_type>( - 0x80 | (codepoint & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F))); } else { - JSON_THROW(parse_error::create( - 103, get_position(), - "code points above 0x10FFFF are invalid")); + JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); } return result; @@ -11012,41 +9711,41 @@ private: { switch (t) { - case token_type::uninitialized: - return "<uninitialized>"; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return "<parse error>"; - case token_type::end_of_input: - return "end of input"; - default: - { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE - } + case token_type::uninitialized: + return "<uninitialized>"; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return "<parse error>"; + case token_type::end_of_input: + return "end of input"; + default: + { + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE + } } } @@ -11066,9 +9765,10 @@ private: 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 is the continue statement for - whitespace. To loop forever, the input must be an infinite sequence - whitespace. This contradicts the assumption of finite input, q.e.d. + 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() { @@ -11081,38 +9781,48 @@ private: 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, 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, + 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 + fill_line_buffer(5); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -11225,24 +9935,24 @@ private: } } } - basic_json_parser_2: +basic_json_parser_2: ++m_cursor; { last_token_type = token_type::end_of_input; break; } - basic_json_parser_4: +basic_json_parser_4: ++m_cursor; - basic_json_parser_5: - { - last_token_type = token_type::parse_error; - break; - } - basic_json_parser_6: +basic_json_parser_5: + { + last_token_type = token_type::parse_error; + break; + } +basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -11250,10 +9960,9 @@ private: goto basic_json_parser_6; } { - position += static_cast<size_t>((m_cursor - m_start)); continue; } - basic_json_parser_9: +basic_json_parser_9: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych <= 0x1F) @@ -11273,13 +9982,13 @@ private: goto basic_json_parser_31; } goto basic_json_parser_5; - basic_json_parser_10: +basic_json_parser_10: ++m_cursor; { last_token_type = token_type::value_separator; break; } - basic_json_parser_12: +basic_json_parser_12: yych = *++m_cursor; if (yych <= '/') { @@ -11294,7 +10003,7 @@ private: goto basic_json_parser_45; } goto basic_json_parser_5; - basic_json_parser_13: +basic_json_parser_13: yyaccept = 1; yych = *(m_marker = ++m_cursor); if (yych <= '9') @@ -11325,17 +10034,17 @@ private: } } } - basic_json_parser_14: - { - last_token_type = token_type::value_unsigned; - break; - } - basic_json_parser_15: +basic_json_parser_14: + { + last_token_type = token_type::value_unsigned; + break; + } +basic_json_parser_15: yyaccept = 1; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - fill_line_buffer(3); // LCOV_EXCL_LINE + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -11362,25 +10071,25 @@ private: } goto basic_json_parser_14; } - basic_json_parser_17: +basic_json_parser_17: ++m_cursor; { last_token_type = token_type::name_separator; break; } - basic_json_parser_19: +basic_json_parser_19: ++m_cursor; { last_token_type = token_type::begin_array; break; } - basic_json_parser_21: +basic_json_parser_21: ++m_cursor; { last_token_type = token_type::end_array; break; } - basic_json_parser_23: +basic_json_parser_23: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 'a') @@ -11388,7 +10097,7 @@ private: goto basic_json_parser_52; } goto basic_json_parser_5; - basic_json_parser_24: +basic_json_parser_24: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 'u') @@ -11396,7 +10105,7 @@ private: goto basic_json_parser_53; } goto basic_json_parser_5; - basic_json_parser_25: +basic_json_parser_25: yyaccept = 0; yych = *(m_marker = ++m_cursor); if (yych == 'r') @@ -11404,26 +10113,26 @@ private: goto basic_json_parser_54; } goto basic_json_parser_5; - basic_json_parser_26: +basic_json_parser_26: ++m_cursor; { last_token_type = token_type::begin_object; break; } - basic_json_parser_28: +basic_json_parser_28: ++m_cursor; { last_token_type = token_type::end_object; break; } - basic_json_parser_30: +basic_json_parser_30: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; - basic_json_parser_31: +basic_json_parser_31: if (yybm[0 + yych] & 128) { goto basic_json_parser_30; @@ -11481,7 +10190,7 @@ private: } } } - basic_json_parser_32: +basic_json_parser_32: m_cursor = m_marker; if (yyaccept <= 1) { @@ -11505,17 +10214,17 @@ private: goto basic_json_parser_58; } } - basic_json_parser_33: +basic_json_parser_33: ++m_cursor; { last_token_type = token_type::value_string; break; } - basic_json_parser_35: +basic_json_parser_35: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'e') @@ -11590,11 +10299,11 @@ private: } } } - basic_json_parser_36: +basic_json_parser_36: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x7F) @@ -11606,11 +10315,11 @@ private: goto basic_json_parser_30; } goto basic_json_parser_32; - basic_json_parser_37: +basic_json_parser_37: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x9F) @@ -11622,11 +10331,11 @@ private: goto basic_json_parser_36; } goto basic_json_parser_32; - basic_json_parser_38: +basic_json_parser_38: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x7F) @@ -11638,11 +10347,11 @@ private: goto basic_json_parser_36; } goto basic_json_parser_32; - basic_json_parser_39: +basic_json_parser_39: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x7F) @@ -11654,11 +10363,11 @@ private: goto basic_json_parser_36; } goto basic_json_parser_32; - basic_json_parser_40: +basic_json_parser_40: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x8F) @@ -11670,11 +10379,11 @@ private: goto basic_json_parser_38; } goto basic_json_parser_32; - basic_json_parser_41: +basic_json_parser_41: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x7F) @@ -11686,11 +10395,11 @@ private: goto basic_json_parser_38; } goto basic_json_parser_32; - basic_json_parser_42: +basic_json_parser_42: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 0x7F) @@ -11702,7 +10411,7 @@ private: goto basic_json_parser_38; } goto basic_json_parser_32; - basic_json_parser_43: +basic_json_parser_43: yyaccept = 2; yych = *(m_marker = ++m_cursor); if (yych <= '9') @@ -11733,17 +10442,17 @@ private: } } } - basic_json_parser_44: - { - last_token_type = token_type::value_integer; - break; - } - basic_json_parser_45: +basic_json_parser_44: + { + last_token_type = token_type::value_integer; + break; + } +basic_json_parser_45: yyaccept = 2; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - fill_line_buffer(3); // LCOV_EXCL_LINE + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '9') @@ -11777,7 +10486,7 @@ private: goto basic_json_parser_44; } } - basic_json_parser_47: +basic_json_parser_47: yych = *++m_cursor; if (yych <= '/') { @@ -11788,11 +10497,11 @@ private: goto basic_json_parser_56; } goto basic_json_parser_32; - basic_json_parser_48: +basic_json_parser_48: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '/') @@ -11803,12 +10512,12 @@ private: { goto basic_json_parser_48; } - basic_json_parser_50: - { - last_token_type = token_type::parse_error; - break; - } - basic_json_parser_51: +basic_json_parser_50: + { + last_token_type = token_type::parse_error; + break; + } +basic_json_parser_51: yych = *++m_cursor; if (yych <= ',') { @@ -11834,32 +10543,32 @@ private: } goto basic_json_parser_32; } - basic_json_parser_52: +basic_json_parser_52: yych = *++m_cursor; if (yych == 'l') { goto basic_json_parser_62; } goto basic_json_parser_32; - basic_json_parser_53: +basic_json_parser_53: yych = *++m_cursor; if (yych == 'l') { goto basic_json_parser_63; } goto basic_json_parser_32; - basic_json_parser_54: +basic_json_parser_54: yych = *++m_cursor; if (yych == 'u') { goto basic_json_parser_64; } goto basic_json_parser_32; - basic_json_parser_55: +basic_json_parser_55: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') @@ -11890,12 +10599,12 @@ private: } goto basic_json_parser_32; } - basic_json_parser_56: +basic_json_parser_56: yyaccept = 3; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - fill_line_buffer(3); // LCOV_EXCL_LINE + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'D') @@ -11920,12 +10629,12 @@ private: goto basic_json_parser_51; } } - basic_json_parser_58: - { - last_token_type = token_type::value_float; - break; - } - basic_json_parser_59: +basic_json_parser_58: + { + last_token_type = token_type::value_float; + break; + } +basic_json_parser_59: yych = *++m_cursor; if (yych <= '/') { @@ -11935,11 +10644,11 @@ private: { goto basic_json_parser_32; } - basic_json_parser_60: +basic_json_parser_60: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '/') @@ -11951,32 +10660,32 @@ private: goto basic_json_parser_60; } goto basic_json_parser_58; - basic_json_parser_62: +basic_json_parser_62: yych = *++m_cursor; if (yych == 's') { goto basic_json_parser_66; } goto basic_json_parser_32; - basic_json_parser_63: +basic_json_parser_63: yych = *++m_cursor; if (yych == 'l') { goto basic_json_parser_67; } goto basic_json_parser_32; - basic_json_parser_64: +basic_json_parser_64: yych = *++m_cursor; if (yych == 'e') { goto basic_json_parser_69; } goto basic_json_parser_32; - basic_json_parser_65: +basic_json_parser_65: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') @@ -12007,30 +10716,30 @@ private: } goto basic_json_parser_32; } - basic_json_parser_66: +basic_json_parser_66: yych = *++m_cursor; if (yych == 'e') { goto basic_json_parser_72; } goto basic_json_parser_32; - basic_json_parser_67: +basic_json_parser_67: ++m_cursor; { last_token_type = token_type::literal_null; break; } - basic_json_parser_69: +basic_json_parser_69: ++m_cursor; { last_token_type = token_type::literal_true; break; } - basic_json_parser_71: +basic_json_parser_71: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') @@ -12061,17 +10770,17 @@ private: } goto basic_json_parser_32; } - basic_json_parser_72: +basic_json_parser_72: ++m_cursor; { last_token_type = token_type::literal_false; break; } - basic_json_parser_74: +basic_json_parser_74: ++m_cursor; if (m_limit <= m_cursor) { - fill_line_buffer(1); // LCOV_EXCL_LINE + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') @@ -12103,9 +10812,9 @@ private: goto basic_json_parser_32; } } + } - position += static_cast<size_t>((m_cursor - m_start)); return last_token_type; } @@ -12140,26 +10849,23 @@ private: 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())); + 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()); + 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); + assert(m_marker == nullptr or m_marker <= m_limit); // number of processed characters (p) - const auto num_processed_chars = - static_cast<size_t>(m_start - m_content); + const auto 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; + 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; @@ -12185,13 +10891,6 @@ private: m_line_buffer.erase(0, num_processed_chars); // read next line from input stream m_line_buffer_tmp.clear(); - - // check if stream is still good - if (m_stream->fail()) - { - JSON_THROW(parse_error::create(111, 0, "bad input stream")); - } - std::getline(*m_stream, m_line_buffer_tmp, '\n'); // add line with newline symbol to the line buffer @@ -12200,22 +10899,20 @@ private: } // set pointers - m_content = - reinterpret_cast<const lexer_char_t *>(m_line_buffer.data()); + m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.data()); assert(m_content != nullptr); - m_start = m_content; + m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_line_buffer.size(); + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token string_t get_token_string() const { assert(m_start != nullptr); - return string_t( - reinterpret_cast<typename string_t::const_pointer>(m_start), - static_cast<size_t>(m_cursor - m_start)); + return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), + static_cast<size_t>(m_cursor - m_start)); } /*! @@ -12273,8 +10970,7 @@ private: @return string value of current token without opening and closing quotes - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @throw std::out_of_range if to_unicode fails */ string_t get_string() const { @@ -12284,18 +10980,16 @@ private: 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) + for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++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 + // 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)); + result.push_back(static_cast<typename string_t::value_type>(*k)); } i = e - 1; // -1 because of ++i } @@ -12307,100 +11001,85 @@ private: switch (*i) { - // the default escapes - case 't': - { - result += "\t"; - break; - } - case 'b': - { - result += "\b"; - break; - } - case 'f': - { - result += "\f"; - break; - } - case 'n': - { - result += "\n"; - break; - } - case 'r': - { - result += "\r"; - break; - } - case '\\': - { - result += "\\"; - break; - } - case '/': - { - result += "/"; - break; - } - case '"': - { - result += "\""; - break; - } - - // unicode - case 'u': - { - // get code xxxx from uxxxx - auto codepoint = std::strtoul( - std::string( - reinterpret_cast< - typename string_t::const_pointer>(i + 1), - 4) - .c_str(), - nullptr, 16); - - // check if codepoint is a high surrogate - if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + // the default escapes + case 't': { - // make sure there is a subsequent unicode - if ((i + 6 >= m_limit) or *(i + 5) != '\\' or - *(i + 6) != 'u') - { - JSON_THROW(parse_error::create( - 102, get_position(), - "missing low surrogate")); - } - - // get code yyyy from uxxxx\uyyyy - auto codepoint2 = std::strtoul( - std::string( - reinterpret_cast< - typename string_t::const_pointer>(i + - 7), - 4) - .c_str(), - nullptr, 16); - result += to_unicode(codepoint, codepoint2); - // skip the next 10 characters (xxxx\uyyyy) - i += 10; + result += "\t"; + break; } - else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + case 'b': { - // we found a lone low surrogate - JSON_THROW(parse_error::create( - 102, get_position(), "missing high surrogate")); + result += "\b"; + break; } - else + case 'f': { - // add unicode character(s) - result += to_unicode(codepoint); - // skip the next four characters (xxxx) - i += 4; + result += "\f"; + break; + } + case 'n': + { + result += "\n"; + break; + } + case 'r': + { + result += "\r"; + break; + } + case '\\': + { + result += "\\"; + break; + } + case '/': + { + result += "/"; + break; + } + case '"': + { + result += "\""; + break; + } + + // unicode + case 'u': + { + // get code xxxx from uxxxx + auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1), + 4).c_str(), nullptr, 16); + + // check if codepoint is a high surrogate + if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + { + // make sure there is a subsequent unicode + if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') + { + JSON_THROW(std::invalid_argument("missing low surrogate")); + } + + // get code yyyy from uxxxx\uyyyy + auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer> + (i + 7), 4).c_str(), nullptr, 16); + result += to_unicode(codepoint, codepoint2); + // skip the next 10 characters (xxxx\uyyyy) + i += 10; + } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + JSON_THROW(std::invalid_argument("missing high surrogate")); + } + else + { + // add unicode character(s) + result += to_unicode(codepoint); + // skip the next four characters (xxxx) + i += 4; + } + break; } - break; - } } } } @@ -12408,6 +11087,7 @@ private: return result; } + /*! @brief parse string into a built-in arithmetic type as if the current locale is POSIX. @@ -12419,11 +11099,10 @@ private: */ struct strtonum { - public: - strtonum(const char *start, const char *end) - : m_start(start), m_end(end) - { - } + public: + strtonum(const char* start, const char* end) + : m_start(start), m_end(end) + {} /*! @return true iff parsed successfully as number of type T @@ -12431,38 +11110,37 @@ private: @param[in,out] val shall contain parsed value, or undefined value if could not parse */ - template <typename T, typename = typename std::enable_if< - std::is_arithmetic<T>::value>::type> - bool to(T &val) const + template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type> + bool to(T& val) const { return parse(val, std::is_integral<T>()); } - private: - const char *const m_start = nullptr; - const char *const m_end = nullptr; + private: + const char* const m_start = nullptr; + const char* const m_end = nullptr; // floating-point conversion // overloaded wrappers for strtod/strtof/strtold // that will be called from parse<floating_point_t> - static void strtof(float &f, const char *str, char **endptr) + static void strtof(float& f, const char* str, char** endptr) { f = std::strtof(str, endptr); } - static void strtof(double &f, const char *str, char **endptr) + static void strtof(double& f, const char* str, char** endptr) { f = std::strtod(str, endptr); } - static void strtof(long double &f, const char *str, char **endptr) + static void strtof(long double& f, const char* str, char** endptr) { f = std::strtold(str, endptr); } - template <typename T> - bool parse(T &value, /*is_integral=*/std::false_type) const + template<typename T> + bool parse(T& value, /*is_integral=*/std::false_type) const { // replace decimal separator with locale-specific version, // when necessary; data will point to either the original @@ -12479,16 +11157,13 @@ private: // instead of C++'s numpunct facet of the current std::locale const auto loc = localeconv(); assert(loc != nullptr); - const char decimal_point_char = (loc->decimal_point == nullptr) - ? '.' - : loc->decimal_point[0]; + const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0]; - const char *data = m_start; + const char* data = m_start; if (decimal_point_char != '.') { - const size_t ds_pos = static_cast<size_t>( - std::find(m_start, m_end, '.') - m_start); + const size_t ds_pos = static_cast<size_t>(std::find(m_start, m_end, '.') - m_start); if (ds_pos != len) { @@ -12511,7 +11186,7 @@ private: } } - char *endptr = nullptr; + char* endptr = nullptr; value = 0; // this calls appropriate overload depending on T strtof(value, data, &endptr); @@ -12531,39 +11206,34 @@ private: // integral conversion - signed long long parse_integral(char **endptr, - /*is_signed*/ std::true_type) const + signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const { return std::strtoll(m_start, endptr, 10); } - unsigned long long - parse_integral(char **endptr, /*is_signed*/ std::false_type) const + unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const { return std::strtoull(m_start, endptr, 10); } - template <typename T> - bool parse(T &value, /*is_integral=*/std::true_type) const + template<typename T> + bool parse(T& value, /*is_integral=*/std::true_type) const { - char *endptr = nullptr; + char* endptr = nullptr; errno = 0; // these are thread-local const auto x = parse_integral(&endptr, std::is_signed<T>()); // called right overload? - static_assert( - std::is_signed<T>() == std::is_signed<decltype(x)>(), ""); + static_assert(std::is_signed<T>() == std::is_signed<decltype(x)>(), ""); value = static_cast<T>(x); - return (x == static_cast<decltype(x)>( - value)) // x fits into destination T - and (x < 0) == (value < 0) // preserved sign - // and ((x != 0) or is_integral()) // strto[u]ll - // did nto fail - and (errno == 0) // strto[u]ll did not overflow - and (m_start < m_end) // token was not empty - and (endptr == m_end); // parsed entire token exactly + return (x == static_cast<decltype(x)>(value)) // x fits into destination T + and (x < 0) == (value < 0) // preserved sign + //and ((x != 0) or is_integral()) // strto[u]ll did nto fail + and (errno == 0) // strto[u]ll did not overflow + and (m_start < m_end) // token was not empty + and (endptr == m_end); // parsed entire token exactly } }; @@ -12586,7 +11256,7 @@ private: @param[out] result @ref basic_json object to receive the number. @param[in] token the type of the number token */ - bool get_number(basic_json &result, const token_type token) const + bool get_number(basic_json& result, const token_type token) const { assert(m_start != nullptr); assert(m_start < m_cursor); @@ -12594,41 +11264,41 @@ private: (token == token_type::value_integer) or (token == token_type::value_float)); - strtonum num_converter(reinterpret_cast<const char *>(m_start), - reinterpret_cast<const char *>(m_cursor)); + strtonum num_converter(reinterpret_cast<const char*>(m_start), + reinterpret_cast<const char*>(m_cursor)); switch (token) { - case lexer::token_type::value_unsigned: - { - number_unsigned_t val; - if (num_converter.to(val)) + case lexer::token_type::value_unsigned: { - // parsing successful - result.m_type = value_t::number_unsigned; - result.m_value = val; - return true; + number_unsigned_t val; + if (num_converter.to(val)) + { + // parsing successful + result.m_type = value_t::number_unsigned; + result.m_value = val; + return true; + } + break; } - break; - } - case lexer::token_type::value_integer: - { - number_integer_t val; - if (num_converter.to(val)) + case lexer::token_type::value_integer: { - // parsing successful - result.m_type = value_t::number_integer; - result.m_value = val; - return true; + number_integer_t val; + if (num_converter.to(val)) + { + // parsing successful + result.m_type = value_t::number_integer; + result.m_value = val; + return true; + } + break; } - break; - } - default: - { - break; - } + default: + { + break; + } } // parse float (either explicitly or because a previous conversion @@ -12640,12 +11310,11 @@ private: result.m_type = value_t::number_float; result.m_value = val; - // throw in case of infinity or NAN + // replace infinity and NAN by null if (not std::isfinite(result.m_value.number_float)) { - JSON_THROW(out_of_range::create( - 406, "number overflow parsing '" + get_token_string() + - "'")); + result.m_type = value_t::null; + result.m_value = basic_json::json_value(); } return true; @@ -12655,29 +11324,25 @@ private: return false; } - constexpr size_t get_position() const { return position; } - - private: + private: /// optional input stream - std::istream *m_stream = nullptr; + std::istream* m_stream = nullptr; /// line buffer buffer for m_stream - string_t m_line_buffer{}; + string_t m_line_buffer {}; /// used for filling m_line_buffer - string_t m_line_buffer_tmp{}; + string_t m_line_buffer_tmp {}; /// the buffer pointer - const lexer_char_t *m_content = nullptr; + const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol - const lexer_char_t *m_start = nullptr; + const lexer_char_t* m_start = nullptr; /// pointer for backtracking information - const lexer_char_t *m_marker = nullptr; + const lexer_char_t* m_marker = nullptr; /// pointer to the current symbol - const lexer_char_t *m_cursor = nullptr; + const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer - const lexer_char_t *m_limit = nullptr; + const lexer_char_t* m_limit = nullptr; /// the last token type token_type last_token_type = token_type::end_of_input; - /// current position in the input (read bytes) - size_t position = 0; }; /*! @@ -12687,46 +11352,30 @@ private: */ class parser { - public: + public: /// 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)) - { - } + 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)) + {} - /*! - @brief a parser reading from an input stream - @throw parse_error.111 if input stream is in a bad state - */ - parser(std::istream &is, const parser_callback_t cb = nullptr) - : callback(cb), m_lexer(is) - { - } + /// a parser reading from an input stream + 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))) - { - } - - /*! - @brief public parser interface - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ + 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 @@ -12742,210 +11391,197 @@ private: return result.is_discarded() ? basic_json() : std::move(result); } - private: - /*! - @brief the actual parser - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ + private: + /// the actual parser basic_json parse_internal(bool keep) { auto result = basic_json(value_t::discarded); switch (last_token) { - case lexer::token_type::begin_object: - { - 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 = value_t::object; - } - - // read next token - get_token(); - - // closing } -> we are done - if (last_token == lexer::token_type::end_object) + case lexer::token_type::begin_object: { - get_token(); - if (keep and callback and - not callback(--depth, parse_event_t::object_end, - result)) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { - result = basic_json(value_t::discarded); + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; } - return result; - } - // no comma is expected here - unexpect(lexer::token_type::value_separator); + // read next token + get_token(); - // otherwise: parse key-value pairs - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) + // closing } -> we are done + if (last_token == lexer::token_type::end_object) { get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + return result; } - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); + // no comma is expected here + unexpect(lexer::token_type::value_separator); - bool keep_tag = false; - if (keep) + // otherwise: parse key-value pairs + do { - if (callback) + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); + get_token(); } - else + + // store key + expect(lexer::token_type::value_string); + const auto key = m_lexer.get_string(); + + bool keep_tag = false; + if (keep) + { + if (callback) + { + basic_json k(key); + keep_tag = callback(depth, parse_event_t::key, k); + } + else + { + keep_tag = true; + } + } + + // parse separator (:) + get_token(); + expect(lexer::token_type::name_separator); + + // parse and add value + get_token(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) { - keep_tag = true; + result[key] = std::move(value); } } + while (last_token == lexer::token_type::value_separator); - // parse separator (:) + // closing } + expect(lexer::token_type::end_object); get_token(); - expect(lexer::token_type::name_separator); - - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) { - result[key] = std::move(value); + result = basic_json(value_t::discarded); } - } while (last_token == lexer::token_type::value_separator); - // closing } - expect(lexer::token_type::end_object); - get_token(); - if (keep and callback and - not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); + return result; } - return result; - } - - case lexer::token_type::begin_array: - { - if (keep and - (not callback or - ((keep = callback(depth++, parse_event_t::array_start, - result)) != 0))) + case lexer::token_type::begin_array: { - // explicitly set result to object to cope with [] - result.m_type = value_t::array; - result.m_value = value_t::array; - } - - // read next token - get_token(); + 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 = value_t::array; + } - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { + // read next token get_token(); - if (callback and - not callback(--depth, parse_event_t::array_end, result)) + + // closing ] -> we are done + if (last_token == lexer::token_type::end_array) { - result = basic_json(value_t::discarded); + get_token(); + if (callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } + return result; } - return result; - } - // no comma is expected here - unexpect(lexer::token_type::value_separator); + // no comma is expected here + unexpect(lexer::token_type::value_separator); - // otherwise: parse values - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) + // otherwise: parse values + do { - get_token(); + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(std::move(value)); + } } + while (last_token == lexer::token_type::value_separator); - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) + // closing ] + expect(lexer::token_type::end_array); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) { - result.push_back(std::move(value)); + result = basic_json(value_t::discarded); } - } while (last_token == lexer::token_type::value_separator); - // closing ] - expect(lexer::token_type::end_array); - get_token(); - if (keep and callback and - not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); + return result; } - return result; - } - - case lexer::token_type::literal_null: - { - get_token(); - result.m_type = value_t::null; - break; - } + case lexer::token_type::literal_null: + { + get_token(); + result.m_type = value_t::null; + break; + } - case lexer::token_type::value_string: - { - const auto s = m_lexer.get_string(); - get_token(); - result = basic_json(s); - break; - } + case lexer::token_type::value_string: + { + const auto s = m_lexer.get_string(); + get_token(); + result = basic_json(s); + break; + } - case lexer::token_type::literal_true: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = true; - break; - } + case lexer::token_type::literal_true: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = true; + break; + } - case lexer::token_type::literal_false: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = false; - break; - } + case lexer::token_type::literal_false: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = false; + break; + } - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: - { - m_lexer.get_number(result, last_token); - get_token(); - break; - } + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + { + m_lexer.get_number(result, last_token); + get_token(); + break; + } - default: - { - // the last token was unexpected - unexpect(last_token); - } + default: + { + // the last token was unexpected + unexpect(last_token); + } } - if (keep and callback and - not callback(depth, parse_event_t::value, result)) + if (keep and callback and not callback(depth, parse_event_t::value, result)) { result = basic_json(value_t::discarded); } @@ -12959,52 +11595,43 @@ private: return last_token; } - /*! - @throw parse_error.101 if expected token did not occur - */ void expect(typename lexer::token_type t) const { if (t != last_token) { std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error - ? ("'" + m_lexer.get_token_string() + "'") - : lexer::token_type_name(last_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); - JSON_THROW(parse_error::create(101, m_lexer.get_position(), - error_msg)); + JSON_THROW(std::invalid_argument(error_msg)); } } - /*! - @throw parse_error.101 if unexpected token occurred - */ void unexpect(typename lexer::token_type t) const { if (t == last_token) { std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error - ? ("'" + m_lexer.get_token_string() + "'") - : lexer::token_type_name(last_token)); - JSON_THROW(parse_error::create(101, m_lexer.get_position(), - error_msg)); + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); + JSON_THROW(std::invalid_argument(error_msg)); } } - private: + private: /// current level of recursion int depth = 0; /// callback function const parser_callback_t callback = nullptr; /// the type of the last read token - typename lexer::token_type last_token = - lexer::token_type::uninitialized; + typename lexer::token_type last_token = lexer::token_type::uninitialized; /// the lexer lexer m_lexer; }; -public: + public: /*! @brief JSON Pointer @@ -13021,7 +11648,7 @@ public: /// allow basic_json to access private members friend class basic_json; - public: + public: /*! @brief create JSON pointer @@ -13032,22 +11659,21 @@ public: empty string is assumed which references the whole JSON value - @throw parse_error.107 if the given JSON pointer @a s is nonempty and - does not begin with a slash (`/`); see example below - - @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s - is not followed by `0` (representing `~`) or `1` (representing `/`); - see example below + @throw std::domain_error if reference token is nonempty and does not + begin with a slash (`/`); example: `"JSON pointer must be empty or + begin with /"` + @throw std::domain_error if a tilde (`~`) is not followed by `0` + (representing `~`) or `1` (representing `/`); example: `"escape error: + ~ must be followed with 0 or 1"` @liveexample{The example shows the construction several valid JSON pointers as well as the exceptional behavior.,json_pointer} @since version 2.0.0 */ - explicit json_pointer(const std::string &s = "") - : reference_tokens(split(s)) - { - } + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} /*! @brief return a string representation of the JSON pointer @@ -13066,27 +11692,27 @@ public: */ std::string to_string() const noexcept { - return std::accumulate( - reference_tokens.begin(), reference_tokens.end(), std::string{}, - [](const std::string &a, const std::string &b) { - return a + "/" + escape(b); - }); + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); } /// @copydoc to_string() - operator std::string() const { return to_string(); } + operator std::string() const + { + return to_string(); + } - private: - /*! - @brief remove and return last reference pointer - @throw out_of_range.405 if JSON pointer has no parent - */ + private: + /// remove and return last reference pointer std::string pop_back() { if (is_root()) { - JSON_THROW( - out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(std::domain_error("JSON pointer has no parent")); } auto last = reference_tokens.back(); @@ -13095,14 +11721,16 @@ public: } /// return whether pointer points to the root document - bool is_root() const { return reference_tokens.empty(); } + bool is_root() const + { + return reference_tokens.empty(); + } json_pointer top() const { if (is_root()) { - JSON_THROW( - out_of_range::create(405, "JSON pointer has no parent")); + JSON_THROW(std::domain_error("JSON pointer has no parent")); } json_pointer result = *this; @@ -13114,9 +11742,6 @@ public: @brief create and return a reference to the pointed to value @complexity Linear in the number of reference tokens. - - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened */ reference get_and_create(reference j) const { @@ -13124,61 +11749,50 @@ public: // in case no reference tokens exist, return a reference to the // JSON value j which will be overwritten by a primitive value - for (const auto &reference_token : reference_tokens) + for (const auto& reference_token : reference_tokens) { switch (result->m_type) { - case value_t::null: - { - if (reference_token == "0") + case value_t::null: { - // start a new array if reference token is 0 - result = &result->operator[](0); + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; } - else + + case value_t::object: { - // start a new object otherwise + // create an entry in the object result = &result->operator[](reference_token); + break; } - break; - } - - case value_t::object: - { - // create an entry in the object - result = &result->operator[](reference_token); - break; - } - case value_t::array: - { - // create an entry in the array - JSON_TRY + case value_t::array: { - result = &result->operator[]( - static_cast<size_type>(std::stoi(reference_token))); + // create an entry in the array + result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; } - JSON_CATCH(std::invalid_argument &) + + /* + The following code is only reached if there exists a + reference token _and_ the current value is primitive. In + this case, we have an error situation, because primitive + values may only occur as single value; that is, with an + empty list of reference tokens. + */ + default: { - JSON_THROW(parse_error::create( - 109, 0, "array index '" + reference_token + - "' is not a number")); + JSON_THROW(std::domain_error("invalid value to unflatten")); } - break; - } - - /* - The following code is only reached if there exists a - reference token _and_ the current value is primitive. In - this case, we have an error situation, because primitive - values may only occur as single value; that is, with an - empty list of reference tokens. - */ - default: - { - JSON_THROW( - type_error::create(313, "invalid value to unflatten")); - } } } @@ -13200,21 +11814,24 @@ public: @complexity Linear in the length of the JSON pointer. - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number */ reference get_unchecked(pointer ptr) const { - for (const auto &reference_token : reference_tokens) + 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 (x >= '0' and x <= '9'); }); + 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 @@ -13230,120 +11847,82 @@ public: switch (ptr->m_type) { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and - reference_token[0] == '0') + case value_t::object: { - JSON_THROW(parse_error::create( - 106, 0, "array index '" + reference_token + - "' must not begin with '0'")); + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; } - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else + case value_t::array: { - // convert array index to number; unchecked access - JSON_TRY + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + if (reference_token == "-") { - ptr = &ptr->operator[](static_cast<size_type>( - std::stoi(reference_token))); + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); } - JSON_CATCH(std::invalid_argument &) + else { - JSON_THROW(parse_error::create( - 109, 0, "array index '" + reference_token + - "' is not a number")); + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); } + break; } - break; - } - default: - { - JSON_THROW(out_of_range::create( - 404, "unresolved reference token '" + reference_token + - "'")); - } + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } } } return *ptr; } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ reference get_checked(pointer ptr) const { - for (const auto &reference_token : reference_tokens) + for (const auto& reference_token : reference_tokens) { switch (ptr->m_type) { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") + case value_t::object: { - // "-" always fails the range check - JSON_THROW(out_of_range::create( - 402, - "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + // note: at performs range check + ptr = &ptr->at(reference_token); + break; } - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and - reference_token[0] == '0') + case value_t::array: { - JSON_THROW(parse_error::create( - 106, 0, "array index '" + reference_token + - "' must not begin with '0'")); - } + if (reference_token == "-") + { + // "-" always fails the range check + JSON_THROW(std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at( - static_cast<size_type>(std::stoi(reference_token))); + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); + break; } - JSON_CATCH(std::invalid_argument &) + + default: { - JSON_THROW(parse_error::create( - 109, 0, "array index '" + reference_token + - "' is not a number")); + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); } - break; - } - - default: - { - JSON_THROW(out_of_range::create( - 404, "unresolved reference token '" + reference_token + - "'")); - } } } @@ -13357,151 +11936,97 @@ public: @return const reference to the JSON value pointed to by the JSON pointer - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved */ const_reference get_unchecked(const_pointer ptr) const { - for (const auto &reference_token : reference_tokens) + for (const auto& reference_token : reference_tokens) { switch (ptr->m_type) { - case value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") + case value_t::object: { - // "-" cannot be used for const access - JSON_THROW(out_of_range::create( - 402, - "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; } - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and - reference_token[0] == '0') + case value_t::array: { - JSON_THROW(parse_error::create( - 106, 0, "array index '" + reference_token + - "' must not begin with '0'")); - } + if (reference_token == "-") + { + // "-" cannot be used for const access + JSON_THROW(std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } - // use unchecked array access - JSON_TRY - { - ptr = &ptr->operator[]( - static_cast<size_type>(std::stoi(reference_token))); - } - JSON_CATCH(std::invalid_argument &) - { - JSON_THROW(parse_error::create( - 109, 0, "array index '" + reference_token + - "' is not a number")); - } - break; - } + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } - default: - { - JSON_THROW(out_of_range::create( - 404, "unresolved reference token '" + reference_token + - "'")); - } + // use unchecked array access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } } } return *ptr; } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ const_reference get_checked(const_pointer ptr) const { - for (const auto &reference_token : reference_tokens) + for (const auto& reference_token : reference_tokens) { switch (ptr->m_type) { - case value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case value_t::array: - { - if (reference_token == "-") + case value_t::object: { - // "-" always fails the range check - JSON_THROW(out_of_range::create( - 402, - "array index '-' (" + - std::to_string(ptr->m_value.array->size()) + - ") is out of range")); + // note: at performs range check + ptr = &ptr->at(reference_token); + break; } - // error condition (cf. RFC 6901, Sect. 4) - if (reference_token.size() > 1 and - reference_token[0] == '0') + case value_t::array: { - JSON_THROW(parse_error::create( - 106, 0, "array index '" + reference_token + - "' must not begin with '0'")); - } + if (reference_token == "-") + { + // "-" always fails the range check + JSON_THROW(std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } - // note: at performs range check - JSON_TRY - { - ptr = &ptr->at( - static_cast<size_type>(std::stoi(reference_token))); + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token))); + break; } - JSON_CATCH(std::invalid_argument &) + + default: { - JSON_THROW(parse_error::create( - 109, 0, "array index '" + reference_token + - "' is not a number")); + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); } - break; - } - - default: - { - JSON_THROW(out_of_range::create( - 404, "unresolved reference token '" + reference_token + - "'")); - } } } return *ptr; } - /*! - @brief split the string input to reference tokens - - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. - - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector<std::string> - split(const std::string &reference_string) + /// split the string input to reference tokens + static std::vector<std::string> split(const std::string& reference_string) { std::vector<std::string> result; @@ -13514,10 +12039,7 @@ public: // check if nonempty reference string begins with slash if (reference_string[0] != '/') { - JSON_THROW(parse_error::create( - 107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); + JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); } // extract the reference tokens: @@ -13526,36 +12048,33 @@ public: for ( // search for the first slash after the first character size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token + // set the beginning of the first reference token start = 1; // we can stop if start == string::npos+1 = 0 start != 0; // set the beginning of the next reference token // (will eventually be 0 if slash == std::string::npos) start = slash + 1, - // find next slash + // find next slash slash = reference_string.find_first_of('/', start)) { // use the text between the beginning of the reference token // (start) and the last slash (slash). - auto reference_token = - reference_string.substr(start, slash - start); + auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) { assert(reference_token[pos] == '~'); // ~ must be followed by 0 or 1 if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) { - JSON_THROW(parse_error::create( - 108, 0, "escape character '~' must be followed " - "with '0' or '1'")); + JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); } } @@ -13567,6 +12086,7 @@ public: return result; } + private: /*! @brief replace all occurrences of a substring by another string @@ -13575,22 +12095,22 @@ public: @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** + @pre The search string @a f must not be empty. @since version 2.0.0 */ - static void replace_substring(std::string &s, const std::string &f, - const std::string &t) + static void replace_substring(std::string& s, + const std::string& f, + const std::string& t) { assert(not f.empty()); - for (size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ) - ; + for ( + size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ); } /// escape tilde and slash @@ -13603,7 +12123,7 @@ public: } /// unescape tilde and slash - static void unescape(std::string &s) + static void unescape(std::string& s) { // first transform any occurrence of the sequence '~1' to '/' replace_substring(s, "~1", "/"); @@ -13618,55 +12138,56 @@ public: @note Empty objects or arrays are flattened to `null`. */ - static void flatten(const std::string &reference_string, - const basic_json &value, basic_json &result) + static void flatten(const std::string& reference_string, + const basic_json& value, + basic_json& result) { switch (value.m_type) { - case value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else + case value_t::array: { - // iterate array and use index as reference string - for (size_t i = 0; i < value.m_value.array->size(); ++i) + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else { - flatten(reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), result); + // iterate array and use index as reference string + for (size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } } + break; } - break; - } - case value_t::object: - { - if (value.m_value.object->empty()) - { - // flatten empty object as null - result[reference_string] = nullptr; - } - else + case value_t::object: { - // iterate object and use keys as reference string - for (const auto &element : *value.m_value.object) + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else { - flatten(reference_string + "/" + escape(element.first), - element.second, result); + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } } + break; } - break; - } - default: - { - // add primitive value with its reference string - result[reference_string] = value; - break; - } + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } } } @@ -13674,29 +12195,22 @@ public: @param[in] value flattened JSON @return unflattened JSON - - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened */ - static basic_json unflatten(const basic_json &value) + static basic_json unflatten(const basic_json& value) { if (not value.is_object()) { - JSON_THROW( - type_error::create(314, "only objects can be unflattened")); + JSON_THROW(std::domain_error("only objects can be unflattened")); } basic_json result; // iterate the JSON object values - for (const auto &element : *value.m_value.object) + for (const auto& element : *value.m_value.object) { if (not element.second.is_primitive()) { - JSON_THROW(type_error::create( - 315, "values in object must be primitive")); + JSON_THROW(std::domain_error("values in object must be primitive")); } // assign value to reference pointed to by JSON pointer; Note @@ -13704,27 +12218,27 @@ public: // value), function get_and_create returns a reference to // result itself. An assignment will then create a primitive // value. - json_pointer(element.first).get_and_create(result) = - element.second; + json_pointer(element.first).get_and_create(result) = element.second; } return result; } - friend bool operator==(json_pointer const &lhs, - json_pointer const &rhs) noexcept + private: + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept { return lhs.reference_tokens == rhs.reference_tokens; } - friend bool operator!=(json_pointer const &lhs, - json_pointer const &rhs) noexcept + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept { return !(lhs == rhs); } /// the reference tokens - std::vector<std::string> reference_tokens{}; + std::vector<std::string> reference_tokens {}; }; ////////////////////////// @@ -13759,15 +12273,15 @@ public: @complexity Constant. - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number @liveexample{The behavior is shown in the example.,operatorjson_pointer} @since version 2.0.0 */ - reference operator[](const json_pointer &ptr) + reference operator[](const json_pointer& ptr) { return ptr.get_unchecked(this); } @@ -13786,17 +12300,15 @@ public: @complexity Constant. - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number - @liveexample{The behavior is shown in the - example.,operatorjson_pointer_const} + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} @since version 2.0.0 */ - const_reference operator[](const json_pointer &ptr) const + const_reference operator[](const json_pointer& ptr) const { return ptr.get_unchecked(this); } @@ -13811,32 +12323,20 @@ public: @return reference to the element pointed to by @a ptr - @throw parse_error.106 if an array index in the passed JSON pointer @a ptr - begins with '0'. See example below. - - @throw parse_error.109 if an array index in the passed JSON pointer @a ptr - is not a number. See example below. - - @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr - is out of range. See example below. - - @throw out_of_range.402 if the array index '-' is used in the passed JSON - pointer @a ptr. As `at` provides checked access (and no elements are - implicitly inserted), the index '-' is always invalid. See example below. - - @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. - See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - @complexity Constant. - @since version 2.0.0 + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number @liveexample{The behavior is shown in the example.,at_json_pointer} + + @since version 2.0.0 */ - reference at(const json_pointer &ptr) { return ptr.get_checked(this); } + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } /*! @brief access specified element via JSON Pointer @@ -13848,32 +12348,17 @@ public: @return reference to the element pointed to by @a ptr - @throw parse_error.106 if an array index in the passed JSON pointer @a ptr - begins with '0'. See example below. - - @throw parse_error.109 if an array index in the passed JSON pointer @a ptr - is not a number. See example below. - - @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr - is out of range. See example below. - - @throw out_of_range.402 if the array index '-' is used in the passed JSON - pointer @a ptr. As `at` provides checked access (and no elements are - implicitly inserted), the index '-' is always invalid. See example below. - - @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. - See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - @complexity Constant. - @since version 2.0.0 + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number @liveexample{The behavior is shown in the example.,at_json_pointer_const} + + @since version 2.0.0 */ - const_reference at(const json_pointer &ptr) const + const_reference at(const json_pointer& ptr) const { return ptr.get_checked(this); } @@ -13927,9 +12412,6 @@ public: @complexity Linear in the size the JSON value. - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitve - @liveexample{The following code shows how a flattened JSON object is unflattened into the original nested JSON object.,unflatten} @@ -13937,7 +12419,10 @@ public: @since version 2.0.0 */ - basic_json unflatten() const { return json_pointer::unflatten(*this); } + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } /// @} @@ -13964,23 +12449,12 @@ public: any case, the original value is not changed: the patch is applied to a copy of the value. - @throw parse_error.104 if the JSON patch does not consist of an array of - objects - - @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory + @throw std::out_of_range if a JSON pointer inside the patch could not + be resolved successfully in the current JSON value; example: `"key baz + not found"` + @throw invalid_argument if the JSON patch is malformed (e.g., mandatory attributes are missing); example: `"operation add must have member path"` - @throw out_of_range.401 if an array index is out of range. - - @throw out_of_range.403 if a JSON pointer inside the patch could not be - resolved successfully in the current JSON value; example: `"key baz not - found"` - - @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", - "move") - - @throw other_error.501 if "test" operation was unsuccessful - @complexity Linear in the size of the JSON value and the length of the JSON patch. As usually only a fraction of the JSON value is affected by the patch, the complexity can usually be neglected. @@ -13995,24 +12469,16 @@ public: @since version 2.0.0 */ - basic_json patch(const basic_json &json_patch) const + basic_json patch(const basic_json& json_patch) const { // make a working copy to apply the patch to basic_json result = *this; // the valid JSON Patch operations - enum class patch_operations - { - add, - remove, - replace, - move, - copy, - test, - invalid - }; + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; - const auto get_op = [](const std::string &op) { + const auto get_op = [](const std::string op) + { if (op == "add") { return patch_operations::add; @@ -14042,8 +12508,8 @@ public: }; // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer &ptr, - basic_json val) { + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { // adding to the root of the target document means replacing it if (ptr.is_root()) { @@ -14060,60 +12526,57 @@ public: // get reference to parent of JSON pointer ptr const auto last_path = ptr.pop_back(); - basic_json &parent = result[ptr]; + basic_json& parent = result[ptr]; switch (parent.m_type) { - case value_t::null: - case value_t::object: - { - // use operator[] to add value - parent[last_path] = val; - break; - } - - case value_t::array: - { - if (last_path == "-") + case value_t::null: + case value_t::object: { - // special case: append to back - parent.push_back(val); + // use operator[] to add value + parent[last_path] = val; + break; } - else + + case value_t::array: { - const auto idx = std::stoi(last_path); - if (static_cast<size_type>(idx) > parent.size()) + if (last_path == "-") { - // avoid undefined behavior - JSON_THROW(out_of_range::create( - 401, "array index " + std::to_string(idx) + - " is out of range")); + // special case: append to back + parent.push_back(val); } else { - // default case: insert add offset - parent.insert(parent.begin() + - static_cast<difference_type>(idx), - val); + const auto idx = std::stoi(last_path); + if (static_cast<size_type>(idx) > parent.size()) + { + // avoid undefined behavior + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + } + else + { + // default case: insert add offset + parent.insert(parent.begin() + static_cast<difference_type>(idx), val); + } } + break; } - break; - } - default: - { - // if there exists a parent it cannot be primitive - assert(false); // LCOV_EXCL_LINE - } + default: + { + // if there exists a parent it cannot be primitive + assert(false); // LCOV_EXCL_LINE + } } } }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer &ptr) { + const auto operation_remove = [&result](json_pointer & ptr) + { // get reference to parent of JSON pointer ptr const auto last_path = ptr.pop_back(); - basic_json &parent = result.at(ptr); + basic_json& parent = result.at(ptr); // remove child if (parent.is_object()) @@ -14126,8 +12589,7 @@ public: } else { - JSON_THROW(out_of_range::create(403, "key '" + last_path + - "' not found")); + JSON_THROW(std::out_of_range("key '" + last_path + "' not found")); } } else if (parent.is_array()) @@ -14137,52 +12599,47 @@ public: } }; - // type check: top level value must be an array + // type check if (not json_patch.is_array()) { - JSON_THROW(parse_error::create( - 104, 0, "JSON patch must be an array of objects")); + // a JSON patch must be an array of objects + JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); } // iterate and apply the operations - for (const auto &val : json_patch) + for (const auto& val : json_patch) { // wrapper to get a value for an operation - const auto get_value = [&val](const std::string &op, - const std::string &member, - bool string_type) -> basic_json & { + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json& + { // find value auto it = val.m_value.object->find(member); // context-sensitive error message - const auto error_msg = - (op == "op") ? "operation" : "operation '" + op + "'"; + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; // check if desired value is present if (it == val.m_value.object->end()) { - JSON_THROW(parse_error::create( - 105, 0, - error_msg + " must have member '" + member + "'")); + JSON_THROW(std::invalid_argument(error_msg + " must have member '" + member + "'")); } // check if result is of type string if (string_type and not it->second.is_string()) { - JSON_THROW(parse_error::create( - 105, 0, error_msg + " must have string member '" + - member + "'")); + JSON_THROW(std::invalid_argument(error_msg + " must have string member '" + member + "'")); } // no error: return value return it->second; }; - // type check: every element of the array must be an object + // type check if (not val.is_object()) { - JSON_THROW(parse_error::create( - 104, 0, "JSON patch must be an array of objects")); + JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); } // collect mandatory members @@ -14192,85 +12649,81 @@ public: switch (get_op(op)) { - case patch_operations::add: - { - operation_add(ptr, get_value("add", "value", false)); - break; - } - - case patch_operations::remove: - { - operation_remove(ptr); - break; - } - - case patch_operations::replace: - { - // the "path" location must exist - use at() - result.at(ptr) = get_value("replace", "value", false); - break; - } - - case patch_operations::move: - { - const std::string from_path = get_value("move", "from", true); - json_pointer from_ptr(from_path); - - // the "from" location must exist - use at() - basic_json v = result.at(from_ptr); - - // The move operation is functionally identical to a - // "remove" operation on the "from" location, followed - // immediately by an "add" operation at the target - // location with the value that was just removed. - operation_remove(from_ptr); - operation_add(ptr, v); - break; - } - - case patch_operations::copy: - { - const std::string from_path = get_value("copy", "from", true); - ; - const json_pointer from_ptr(from_path); + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } - // the "from" location must exist - use at() - result[ptr] = result.at(from_ptr); - break; - } + case patch_operations::remove: + { + operation_remove(ptr); + break; + } - case patch_operations::test: - { - bool success = false; - JSON_TRY + case patch_operations::replace: { - // check if "value" matches the one at "path" // the "path" location must exist - use at() - success = - (result.at(ptr) == get_value("test", "value", false)); + result.at(ptr) = get_value("replace", "value", false); + break; } - JSON_CATCH(out_of_range &) + + case patch_operations::move: { - // ignore out of range errors: success remains false + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; } - // throw an exception if test fails - if (not success) + case patch_operations::copy: { - JSON_THROW(other_error::create(501, "unsuccessful: " + - val.dump())); + const std::string from_path = get_value("copy", "from", true);; + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + result[ptr] = result.at(from_ptr); + break; } - break; - } + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_CATCH (std::out_of_range&) + { + // ignore out of range errors: success remains false + } - case patch_operations::invalid: - { - // op must be "add", "remove", "replace", "move", "copy", or - // "test" - JSON_THROW(parse_error::create( - 105, 0, "operation value '" + op + "' is invalid")); - } + // throw an exception if test fails + if (not success) + { + JSON_THROW(std::domain_error("unsuccessful: " + val.dump())); + } + + break; + } + + case patch_operations::invalid: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(std::invalid_argument("operation value '" + op + "' is invalid")); + } } } @@ -14309,8 +12762,9 @@ public: @since version 2.0.0 */ - static basic_json diff(const basic_json &source, const basic_json &target, - const std::string &path = "") + static basic_json diff(const basic_json& source, + const basic_json& target, + const std::string& path = "") { // the patch basic_json result(value_t::array); @@ -14325,102 +12779,115 @@ public: { // different types: replace value result.push_back( - {{"op", "replace"}, {"path", path}, {"value", target}}); + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); } else { switch (source.type()) { - case value_t::array: - { - // first pass: traverse common elements - size_t i = 0; - while (i < source.size() and i < target.size()) + case value_t::array: { - // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], - path + "/" + std::to_string(i)); - result.insert(result.end(), temp_diff.begin(), - temp_diff.end()); - ++i; - } + // first pass: traverse common elements + size_t i = 0; + while (i < source.size() and i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } - // i now reached the end of at least one array - // in a second pass, traverse the remaining elements + // i now reached the end of at least one array + // 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()) - { - // add operations in reverse order to avoid invalid - // indices - result.insert( - result.begin() + end_index, - object({{"op", "remove"}, - {"path", path + "/" + std::to_string(i)}})); - ++i; - } + // remove my remaining elements + const auto end_index = static_cast<difference_type>(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } - // add other remaining elements - while (i < target.size()) - { - result.push_back({{"op", "add"}, - {"path", path + "/" + std::to_string(i)}, - {"value", target[i]}}); - ++i; - } + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + std::to_string(i)}, + {"value", target[i]} + }); + ++i; + } - break; - } + break; + } - case value_t::object: - { - // first pass: traverse this object's elements - for (auto it = source.begin(); it != source.end(); ++it) + case value_t::object: { - // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); - - if (target.find(it.key()) != target.end()) + // first pass: traverse this object's elements + for (auto it = source.begin(); it != source.end(); ++it) { - // recursive call to compare object values at key it - auto temp_diff = diff(it.value(), target[it.key()], - path + "/" + key); - result.insert(result.end(), temp_diff.begin(), - temp_diff.end()); + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, + {"path", path + "/" + key} + })); + } } - else + + // second pass: traverse other object's elements + for (auto it = target.begin(); it != target.end(); ++it) { - // found a key that is not in o -> remove it - result.push_back(object( - {{"op", "remove"}, {"path", path + "/" + key}})); + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + key}, + {"value", it.value()} + }); + } } + + break; } - // second pass: traverse other object's elements - for (auto it = target.begin(); it != target.end(); ++it) + default: { - if (source.find(it.key()) == source.end()) + // both primitive type: replace value + result.push_back( { - // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); - result.push_back({{"op", "add"}, - {"path", path + "/" + key}, - {"value", it.value()}}); - } + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + break; } - - break; - } - - default: - { - // both primitive type: replace value - result.push_back( - {{"op", "replace"}, {"path", path}, {"value", target}}); - break; - } } } @@ -14445,6 +12912,7 @@ uses the standard template types. using json = basic_json<>; } // namespace nlohmann + /////////////////////// // nonmember support // /////////////////////// @@ -14457,16 +12925,18 @@ namespace std @since version 1.0.0 */ -template <> -inline void swap(nlohmann::json &j1, nlohmann::json &j2) noexcept( - is_nothrow_move_constructible<nlohmann::json>::value - and is_nothrow_move_assignable<nlohmann::json>::value) +template<> +inline void swap(nlohmann::json& j1, + nlohmann::json& j2) noexcept( + is_nothrow_move_constructible<nlohmann::json>::value and + is_nothrow_move_assignable<nlohmann::json>::value + ) { j1.swap(j2); } /// hash value for JSON objects -template <> +template<> struct hash<nlohmann::json> { /*! @@ -14474,29 +12944,13 @@ struct hash<nlohmann::json> @since version 1.0.0 */ - std::size_t operator()(const nlohmann::json &j) const + std::size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation - const auto &h = hash<nlohmann::json::string_t>(); + const auto& h = hash<nlohmann::json::string_t>(); return h(j.dump()); } }; - -/// specialization for std::less<value_t> -template <> -struct less<::nlohmann::detail::value_t> -{ - /*! - @brief compare two value_t enum values - @since version 3.0.0 - */ - bool operator()(nlohmann::detail::value_t lhs, - nlohmann::detail::value_t rhs) const noexcept - { - return nlohmann::detail::operator<(lhs, rhs); - } -}; - } // namespace std /*! @@ -14512,7 +12966,7 @@ if no parse error occurred. @since version 1.0.0 */ -inline nlohmann::json operator"" _json(const char *s, std::size_t n) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) { return nlohmann::json::parse(s, s + n); } @@ -14521,8 +12975,7 @@ inline nlohmann::json operator"" _json(const char *s, std::size_t 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 +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 @@ -14531,24 +12984,20 @@ object if no parse error occurred. @since version 2.0.0 */ -inline nlohmann::json::json_pointer operator"" _json_pointer(const char *s, - std::size_t n) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { return nlohmann::json::json_pointer(std::string(s, n)); } // restore GCC/clang diagnostic settings #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic pop -#endif -#if defined(__clang__) -#pragma GCC diagnostic pop + #pragma GCC diagnostic pop #endif // clean up #undef JSON_CATCH +#undef JSON_DEPRECATED #undef JSON_THROW #undef JSON_TRY -#undef JSON_DEPRECATED #endif diff --git a/thirdparty/NLohmannJson/update.sh b/thirdparty/NLohmannJson/update.sh new file mode 100755 index 0000000000000000000000000000000000000000..0fa3ffca3aface5d12c33e371d06a0375e3dbd67 --- /dev/null +++ b/thirdparty/NLohmannJson/update.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e +set -x +shopt -s dotglob + +readonly name="NLohmannJson" +readonly ownership="JSON For Modern C++ Upstream <robot@adios2>" +readonly subtree="thirdparty/NLohmannJson/json" +readonly repo="https://github.com/nlohmann/json.git" +readonly tag="v2.1.1" +readonly shortlog="true" +readonly paths=" +LICENSE.MIT +src/json.hpp +" + +extract_source () { + git_archive +} + +. "${BASH_SOURCE%/*}/../update-common.sh"