diff --git a/gui/src/controller/Controller.java b/gui/src/controller/Controller.java index 1dbc42edd955cb66c4965aece87f9bb5e0fa95a2..94eb1609f349bffe5ec39344409a8b1d3ca6ed1a 100644 --- a/gui/src/controller/Controller.java +++ b/gui/src/controller/Controller.java @@ -83,6 +83,10 @@ public class Controller implements Initializable e.printStackTrace(); System.exit(1); } + // SENDING STATE TO THE GAME + // TODO: 5/10/16 Creating a state and sending it to the game + String state = "{}"; + gameInput.println(state); //Choice: who will start? ChoiceDialog<Player> playerChoice = new ChoiceDialog<>(Player.Red, Player.Red, Player.Blue); playerChoice.setTitle("Penguin game!"); diff --git a/src/game/penguin.cpp b/src/game/penguin.cpp index d0b69431b64bc10ffe844433500c85f8e40769c0..cffd46023f9b785088107c10beb862beca10eb94 100644 --- a/src/game/penguin.cpp +++ b/src/game/penguin.cpp @@ -5,8 +5,48 @@ using namespace std; namespace game { + /** + * Creates the game: load the state from standard input. + * Penguins in state can be composed of just their position: the moves will be updated automatically. + **/ penguin::penguin() { + cout << "Enter penguin game state as JSON on one line" << endl; + string line; + getline(cin, line); + json json_state = json::parse(line); + //Charging every element of the state if it exists + if(json_state.count("bitboards")) + { + if(json_state["bitboards"].count("onefish")) {state.one_fish = json_state["bitboards"]["onefish"];} + if(json_state["bitboards"].count("twofish")) {state.two_fish = json_state["bitboards"]["twofish"];} + if(json_state["bitboards"].count("threefish")) {state.three_fish = json_state["bitboards"]["threefish"];} + } + if(json_state.count("current_player")) + state.current_player_red = json_state["current_player"] == "Red" ? true : false; + if(json_state.count("penguins")) + { + if(json_state["penguins"].count("red")) + { + state.p1_red = json_state["penguins"]["red"][0]; + state.p2_red = json_state["penguins"]["red"][1]; + state.p3_red = json_state["penguins"]["red"][2]; + state.p4_red = json_state["penguins"]["red"][3]; + } + if(json_state["penguins"].count("blue")) + { + state.p1_blue = json_state["penguins"]["blue"][0]; + state.p2_blue = json_state["penguins"]["blue"][1]; + state.p3_blue = json_state["penguins"]["blue"][2]; + state.p4_blue = json_state["penguins"]["blue"][3]; + } + } + if(json_state.count("score")) + { + if(json_state["score"].count("red")) { state.score_red = json_state["score"]["red"]; } + if(json_state["score"].count("blue")) { state.score_blue = json_state["score"]["blue"]; } + } + //Update moves on all penguins uint64_t obstacles = (~(state.one_fish | state.two_fish | state.three_fish)); obstacles |= ((uint64_t) 1) << (state.p1_red & 63); @@ -28,6 +68,17 @@ namespace game state.nb_moves_blue += update_moves(&state.p2_blue, obstacles); state.nb_moves_blue += update_moves(&state.p3_blue, obstacles); state.nb_moves_blue += update_moves(&state.p4_blue, obstacles); + + if (state.nb_moves_red == 0) + { + state.canPlay_red = false; + state.nb_moves_red = 1; //We create an artificial move so that the mcts works + } + if (state.nb_moves_blue == 0) + { + state.canPlay_blue = false; + state.nb_moves_blue = 1; //We create an artificial move so that the mcts works + } } shared_ptr<game<penguin_state>> penguin::do_copy() const diff --git a/src/game/penguin.hpp b/src/game/penguin.hpp index c322c0a6f80a759f2daf450cc3eac8005c900313..e1364fc8ffec7d84809fb8fba33859fbeea41855 100644 --- a/src/game/penguin.hpp +++ b/src/game/penguin.hpp @@ -13,19 +13,19 @@ namespace game { struct penguin_state { - uint64_t one_fish = 533050011236269409; //Position of one-fish tiles (bitboard) - uint64_t two_fish = 40552008684274318; //Position of two-fish tiles (bitboard) - uint64_t three_fish = 579319484686303248; //Position of three-fish tiles (bitboard) + uint64_t one_fish = 1152921504606846975; //Position of one-fish tiles (bitboard) + uint64_t two_fish = 0; //Position of two-fish tiles (bitboard) + uint64_t three_fish = 0; //Position of three-fish tiles (bitboard) //Penguins - uint32_t p1_red = 24; - uint32_t p2_red = 28; - uint32_t p3_red = 48; - uint32_t p4_red = 50; - uint32_t p1_blue = 10; - uint32_t p2_blue = 13; - uint32_t p3_blue = 35; - uint32_t p4_blue = 32; + uint32_t p1_red = 0; + uint32_t p2_red = 1; + uint32_t p3_red = 6; + uint32_t p4_red = 7; + uint32_t p1_blue = 59; + uint32_t p2_blue = 58; + uint32_t p3_blue = 53; + uint32_t p4_blue = 54; int score_red = 0; int score_blue = 0; diff --git a/src/json/json.hpp b/src/json/json.hpp index db73336723ede83decfe13bac70f8165a0f52959..37080ebb7792484f3ca34e22c6fa09a98d0f39e7 100644 --- a/src/json/json.hpp +++ b/src/json/json.hpp @@ -1,38 +1,29 @@ -/*! -@mainpage - -These pages contain the API documentation of JSON for Modern C++, a C++11 -header-only JSON class. - -Class @ref nlohmann::basic_json is a good entry point for the documentation. - -@copyright The code is licensed under the [MIT - License](http://opensource.org/licenses/MIT): - <br> - Copyright © 2013-2016 Niels Lohmann. - <br> - 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: - <br> - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - <br> - 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. - -@author [Niels Lohmann](http://nlohmann.me) -@see https://github.com/nlohmann/json to download the source code - -@version 1.1.0 +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 2.0.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License <http://opensource.org/licenses/MIT>. +Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +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. */ #ifndef NLOHMANN_JSON_HPP @@ -41,6 +32,7 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation. #include <algorithm> #include <array> #include <cassert> +#include <cerrno> #include <ciso646> #include <cmath> #include <cstddef> @@ -61,25 +53,12 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation. #include <utility> #include <vector> -// enable ssize_t on MinGW -#ifdef __GNUC__ - #ifdef __MINGW32__ - #include <sys/types.h> - #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" #endif -// enable ssize_t for MSVC -#ifdef _MSC_VER - #include <basetsd.h> - using ssize_t = SSIZE_T; -#endif - /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -109,24 +88,39 @@ struct has_mapped_type static constexpr bool value = sizeof(test<T>(0)) == 1; }; +/*! +@brief helper class to create locales with decimal point +@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 +*/ +class DecimalSeparator : public std::numpunct<char> +{ + protected: + char do_decimal_point() const + { + return '.'; + } +}; + } /*! @brief a class to store JSON values -@tparam ObjectType type for JSON objects (@c std::map by default; will be used +@tparam ObjectType type for JSON objects (`std::map` by default; will be used in @ref object_t) -@tparam ArrayType type for JSON arrays (@c std::vector by default; will be used +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used in @ref array_t) -@tparam StringType type for JSON strings and object keys (@c std::string by +@tparam StringType type for JSON strings and object keys (`std::string` by default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (@c `bool` by default; will be used +@tparam BooleanType type for JSON booleans (`bool` by default; will be used in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by default; will be used in @ref number_integer_t) -@tparam NumberFloatType type for JSON floating-point numbers (@c `double` by +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (@c `std::allocator` by +@tparam AllocatorType type of the allocator to use (`std::allocator` by default) @requirement The class satisfies the following concept requirements: @@ -173,7 +167,8 @@ default) @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal -@see RFC 7159 <http://rfc7159.net/rfc7159> +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) @since version 1.0.0 @@ -184,7 +179,8 @@ template < template<typename U, typename... Args> class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = int64_t, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, template<typename U> class AllocatorType = std::allocator > @@ -197,10 +193,14 @@ class basic_json StringType, BooleanType, NumberIntegerType, + NumberUnsignedType, NumberFloatType, AllocatorType>; public: + // forward declarations + template<typename Base> class json_reverse_iterator; + class json_pointer; ///////////////////// // container types // @@ -230,9 +230,6 @@ class basic_json /// the type of an element const pointer using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer; - // forward declaration - template<typename Base> class json_reverse_iterator; - /// an iterator for a basic_json container class iterator; /// a const iterator for a basic_json container @@ -481,9 +478,10 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + C++ allows more precise storage if it is known whether the number is a + signed integer, an unsigned integer or a floating-point number. Therefore, + three different types, @ref number_integer_t, @ref number_unsigned_t and + @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. @@ -516,7 +514,7 @@ class basic_json that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will be - automatically be stored as @ref number_float_t. + automatically be stored as @ref number_unsigned_t or @ref number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are @@ -532,10 +530,84 @@ class basic_json @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_integer_t = NumberIntegerType; + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most programming + > languages. A number is represented in base 10 using decimal digits. It + > contains an integer component that may be prefixed with an optional minus + > sign, which may be followed by a fraction part and/or an exponent part. + > Leading zeros are not allowed. (...) Numeric values that cannot be + > represented in the grammar below (such as Infinity and NaN) are not + > permitted. + + This description includes both integer and floating-point numbers. However, + C++ allows more precise storage if it is known whether the number is a + signed integer, an unsigned integer or a floating-point number. Therefore, + three different types, @ref number_integer_t, @ref number_unsigned_t and + @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the template + parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the default + value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], this + class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + /*! @brief a type for a number (floating-point) @@ -549,9 +621,10 @@ class basic_json > permitted. This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + C++ allows more precise storage if it is known whether the number is a + signed integer, an unsigned integer or a floating-point number. Therefore, + three different types, @ref number_integer_t, @ref number_unsigned_t and + @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. @@ -597,6 +670,8 @@ class basic_json @sa @ref number_integer_t -- type for number values (integer) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_float_t = NumberFloatType; @@ -620,18 +695,87 @@ class basic_json */ enum class value_t : uint8_t { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function }; private: + + /*! + @brief a type to hold JSON type information + + This bitfield type holds information about JSON types. It is internally + used to hold the basic JSON type enumeration, as well as additional + information in the case of values that have been parsed from a string + including whether of not it was created directly or parsed, and in the + case of floating point numbers the number of significant figures in the + original representaiton and if it was in exponential form, if a '+' was + included in the exponent and the capitilization of the exponent marker. + The sole purpose of this information is to permit accurate round trips. + + @since version 2.0.0 + */ + union type_data_t + { + struct + { + /// the type of the value (@ref value_t) + uint16_t type : 4; + /// whether the number was parsed from a string + uint16_t parsed : 1; + /// whether parsed number contained an exponent ('e'/'E') + uint16_t has_exp : 1; + /// whether parsed number contained a plus in the exponent + uint16_t exp_plus : 1; + /// whether parsed number's exponent was capitalized ('E') + uint16_t exp_cap : 1; + /// the number of figures for a parsed number + uint16_t precision : 8; + } bits; + uint16_t data; + + /// return the type as value_t + operator value_t() const + { + return static_cast<value_t>(bits.type); + } + + /// test type for equality (ignore other fields) + bool operator==(const value_t& rhs) const + { + return static_cast<value_t>(bits.type) == rhs; + } + + /// assignment + type_data_t& operator=(value_t rhs) + { + bits.type = static_cast<uint16_t>(rhs); + return *this; + } + + /// construct from value_t + type_data_t(value_t t) noexcept + { + *reinterpret_cast<uint16_t*>(this) = 0; + bits.type = static_cast<uint16_t>(t); + } + + /// default constructor + type_data_t() noexcept + { + data = 0; + bits.type = reinterpret_cast<uint16_t>(value_t::null); + } + }; + /// helper for exception-safe object creation template<typename T, typename... Args> static T* create(Args&& ... args) @@ -669,15 +813,19 @@ class basic_json boolean_t boolean; /// number (integer) number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; /// number (floating-point) number_float_t number_float; /// default constructor (for null values) - json_value() noexcept = default; + json_value() = default; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type @@ -715,6 +863,12 @@ class basic_json 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); @@ -870,6 +1024,8 @@ class basic_json (floating-point) value @sa @ref basic_json(const number_integer_t) -- create a number (integer) value + @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned) + value @since version 1.0.0 */ @@ -885,7 +1041,12 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - As postcondition, it holds: `basic_json().empty() == true`. @@ -896,19 +1057,22 @@ class basic_json @since version 1.0.0 */ - basic_json() noexcept = default; + basic_json() = default; /*! @brief create a null object (explicitly) Create a `null` JSON value. This is the explicitly version of the `null` value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a @c nullptr to a JSON value. + create `null` values by explicitly assigning a `nullptr` to a JSON value. The passed null pointer itself is not read -- it is only used to choose the right constructor. @complexity Constant. + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + @liveexample{The following code shows the constructor with null pointer parameter.,basic_json__nullptr_t} @@ -948,11 +1112,14 @@ class basic_json @brief create an object (implicit) Create an object JSON value with a given content. This constructor allows - any type that can be used to construct values of type @ref object_t. - Examples include the types `std::map` and `std::unordered_map`. + any type @a CompatibleObjectType that can be used to construct values of + type @ref object_t. - @tparam CompatibleObjectType an object type whose `key_type` and - `value_type` is compatible to @ref object_t + @tparam CompatibleObjectType An object type whose `key_type` and + `value_type` is compatible to @ref object_t. Examples include `std::map`, + `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with + a `key_type` of `std::string`, and a `value_type` from which a @ref + basic_json value can be constructed. @param[in] val a value for the object @@ -1007,11 +1174,14 @@ class basic_json @brief create an array (implicit) Create an array JSON value with a given content. This constructor allows - any type that can be used to construct values of type @ref array_t. - Examples include the types `std::vector`, `std::list`, and `std::set`. + any type @a CompatibleArrayType that can be used to construct values of + type @ref array_t. - @tparam CompatibleArrayType an object type whose `value_type` is compatible - to @ref array_t + @tparam CompatibleArrayType An object type whose `value_type` is compatible + to @ref array_t. Examples include `std::vector`, `std::deque`, `std::list`, + `std::forward_list`, `std::array`, `std::set`, `std::unordered_set`, + `std::multiset`, and `unordered_multiset` with a `value_type` from which a + @ref basic_json value can be constructed. @param[in] val a value for the array @@ -1101,7 +1271,7 @@ class basic_json @param[in] val a value for the string @tparam CompatibleStringType an string type which is compatible to @ref - string_t + string_t, for instance `std::string`. @complexity Linear in the size of the passed @a val. @@ -1138,7 +1308,7 @@ class basic_json @since version 1.0.0 */ - basic_json(boolean_t val) + basic_json(boolean_t val) noexcept : m_type(value_t::boolean), m_value(val) {} @@ -1147,18 +1317,16 @@ class basic_json Create an integer number JSON value with a given content. - @tparam T helper type to compare number_integer_t and int (not visible in) - the interface. + @tparam T A helper type to remove this function via SFINAE in case @ref + number_integer_t is the same as `int`. In this case, this constructor would + have the same signature as @ref basic_json(const int value). Note the + helper type @a T is not visible in this constructor's interface. @param[in] val an integer to create a JSON number from - @note This constructor would have the same signature as @ref - basic_json(const int value), so we need to switch this one off in case - number_integer_t is the same as int. This is done via the helper type @a T. - @complexity Constant. - @liveexample{The example below shows the construction of a JSON integer + @liveexample{The example below shows the construction of an integer number value.,basic_json__number_integer_t} @sa @ref basic_json(const int) -- create a number value (integer) @@ -1171,8 +1339,9 @@ class basic_json typename std::enable_if< not (std::is_same<T, int>::value) and std::is_same<T, number_integer_t>::value - , int>::type = 0> - basic_json(const number_integer_t val) + , int>::type + = 0> + basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) {} @@ -1191,7 +1360,7 @@ class basic_json @complexity Constant. - @liveexample{The example below shows the construction of a JSON integer + @liveexample{The example below shows the construction of an integer number value from an anonymous enum.,basic_json__const_int} @sa @ref basic_json(const number_integer_t) -- create a number value @@ -1201,7 +1370,7 @@ class basic_json @since version 1.0.0 */ - basic_json(const int val) + basic_json(const int val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) {} @@ -1210,19 +1379,19 @@ class basic_json @brief create an integer number (implicit) Create an integer number JSON value with a given content. This constructor - allows any type that can be used to construct values of type @ref - number_integer_t. Examples may include the types `int`, `int32_t`, or - `short`. + allows any type @a CompatibleNumberIntegerType that can be used to + construct values of type @ref number_integer_t. - @tparam CompatibleNumberIntegerType an integer type which is compatible to - @ref number_integer_t. + @tparam CompatibleNumberIntegerType An integer type which is compatible to + @ref number_integer_t. Examples include the types `int`, `int32_t`, `long`, + and `short`. @param[in] val an integer to create a JSON number from @complexity Constant. - @liveexample{The example below shows the construction of several JSON - integer number values from compatible + @liveexample{The example below shows the construction of several integer + number values from compatible types.,basic_json__CompatibleIntegerNumberType} @sa @ref basic_json(const number_integer_t) -- create a number value @@ -1234,13 +1403,74 @@ class basic_json template<typename CompatibleNumberIntegerType, typename std::enable_if< std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and - std::numeric_limits<CompatibleNumberIntegerType>::is_integer, CompatibleNumberIntegerType>::type + std::numeric_limits<CompatibleNumberIntegerType>::is_integer and + std::numeric_limits<CompatibleNumberIntegerType>::is_signed, + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) {} + /*! + @brief create an unsigned integer number (explicit) + + Create an unsigned integer number JSON value with a given content. + + @tparam T helper type to compare number_unsigned_t and unsigned int + (not visible in) the interface. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number + value (unsigned integer) from a compatible number type + + @since version 2.0.0 + */ + template<typename T, + typename std::enable_if< + not (std::is_same<T, int>::value) + and std::is_same<T, number_unsigned_t>::value + , int>::type + = 0> + basic_json(const number_unsigned_t val) noexcept + : m_type(value_t::number_unsigned), m_value(val) + {} + + /*! + @brief create an unsigned number (implicit) + + Create an unsigned number JSON value with a given content. This constructor + allows any type @a CompatibleNumberUnsignedType that can be used to + construct values of type @ref number_unsigned_t. + + @tparam CompatibleNumberUnsignedType An integer type which is compatible to + @ref number_unsigned_t. Examples may include the types `unsigned int`, + `uint32_t`, or `unsigned short`. + + @param[in] val an unsigned integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const number_unsigned_t) -- create a number value + (unsigned) + + @since version 2.0.0 + */ + template < typename CompatibleNumberUnsignedType, typename + std::enable_if < + std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and + std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and + !std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, + CompatibleNumberUnsignedType >::type + = 0 > + basic_json(const CompatibleNumberUnsignedType val) noexcept + : m_type(value_t::number_unsigned), + m_value(static_cast<number_unsigned_t>(val)) + {} + /*! @brief create a floating-point number (explicit) @@ -1248,7 +1478,7 @@ class basic_json @param[in] val a floating-point value to create a JSON number from - @note RFC 7159 <http://www.rfc-editor.org/rfc/rfc7159.txt>, section 6 + @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 disallows NaN values: > Numeric values that cannot be represented in the grammar below (such > as Infinity and NaN) are not permitted. @@ -1265,7 +1495,7 @@ class basic_json @since version 1.0.0 */ - basic_json(const number_float_t val) + basic_json(const number_float_t val) noexcept : m_type(value_t::number_float), m_value(val) { // replace infinity and NAN by null @@ -1280,15 +1510,15 @@ class basic_json @brief create an floating-point number (implicit) Create an floating-point number JSON value with a given content. This - constructor allows any type that can be used to construct values of type - @ref number_float_t. Examples may include the types `float`. + constructor allows any type @a CompatibleNumberFloatType that can be used + to construct values of type @ref number_float_t. - @tparam CompatibleNumberFloatType a floating-point type which is compatible - to @ref number_float_t. + @tparam CompatibleNumberFloatType A floating-point type which is compatible + to @ref number_float_t. Examples may include the types `float` or `double`. @param[in] val a floating-point to create a JSON number from - @note RFC 7159 <http://www.rfc-editor.org/rfc/rfc7159.txt>, section 6 + @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 disallows NaN values: > Numeric values that cannot be represented in the grammar below (such > as Infinity and NaN) are not permitted. @@ -1297,7 +1527,7 @@ class basic_json @complexity Constant. - @liveexample{The example below shows the construction of several JSON + @liveexample{The example below shows the construction of several floating-point number values from compatible types.,basic_json__CompatibleNumberFloatType} @@ -1375,7 +1605,7 @@ class basic_json @complexity Linear in the size of the initializer list @a init. @liveexample{The example below shows how JSON values are created from - initializer lists,basic_json__list_init_t} + initializer lists.,basic_json__list_init_t} @sa @ref array(std::initializer_list<basic_json>) -- create a JSON array value from an initializer list @@ -1431,14 +1661,14 @@ class basic_json for (auto& element : init) { - m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); + m_value.object->emplace(*(element[0].m_value.string), element[1]); } } else { // the initializer list describes an array -> create array m_type = value_t::array; - m_value.array = create<array_t>(std::move(init)); + m_value.array = create<array_t>(init); } } @@ -1466,7 +1696,7 @@ class basic_json @complexity Linear in the size of @a init. - @liveexample{The following code shows an example for the @ref array + @liveexample{The following code shows an example for the `array` function.,array} @sa @ref basic_json(std::initializer_list<basic_json>, bool, value_t) -- @@ -1506,7 +1736,7 @@ class basic_json @complexity Linear in the size of @a init. - @liveexample{The following code shows an example for the @ref object + @liveexample{The following code shows an example for the `object` function.,object} @sa @ref basic_json(std::initializer_list<basic_json>, bool, value_t) -- @@ -1600,6 +1830,7 @@ class basic_json 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()) @@ -1624,6 +1855,13 @@ class basic_json break; } + case value_t::number_unsigned: + { + assert(first.m_object != nullptr); + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + case value_t::number_float: { assert(first.m_object != nullptr); @@ -1665,6 +1903,31 @@ class basic_json } } + /*! + @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. + + @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 + */ + explicit basic_json(std::istream& i, parser_callback_t cb = nullptr) + { + *this = parser(i, cb).parse(); + } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// @@ -1678,7 +1941,9 @@ class basic_json @complexity Linear in the size of @a other. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. @@ -1727,6 +1992,12 @@ class basic_json 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; @@ -1778,7 +2049,9 @@ class basic_json @complexity Linear. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is linear. @liveexample{The code below shows and example for the copy assignment. It @@ -1808,7 +2081,9 @@ class basic_json @complexity Linear. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is linear. - All stored elements are destroyed and all memory is freed. @@ -1909,12 +2184,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref type() for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON types.,type} @since version 1.0.0 */ - value_t type() const noexcept + constexpr value_t type() const noexcept { return m_type; } @@ -1930,12 +2208,21 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_primitive for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON types.,is_primitive} + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + @since version 1.0.0 */ - bool is_primitive() const noexcept + constexpr bool is_primitive() const noexcept { return is_null() or is_string() or is_boolean() or is_number(); } @@ -1950,12 +2237,19 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_structured for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON types.,is_structured} + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + @since version 1.0.0 */ - bool is_structured() const noexcept + constexpr bool is_structured() const noexcept { return is_array() or is_object(); } @@ -1969,12 +2263,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_null for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON types.,is_null} @since version 1.0.0 */ - bool is_null() const noexcept + constexpr bool is_null() const noexcept { return m_type == value_t::null; } @@ -1988,12 +2285,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_boolean for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON types.,is_boolean} @since version 1.0.0 */ - bool is_boolean() const noexcept + constexpr bool is_boolean() const noexcept { return m_type == value_t::boolean; } @@ -2004,20 +2304,26 @@ class basic_json This function returns true iff the JSON value is a number. This includes both integer and floating-point values. - @return `true` if type is number (regardless whether integer or - floating-type), `false` otherwise. + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. @complexity Constant. - @liveexample{The following code exemplifies @ref is_number for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON types.,is_number} - @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ - bool is_number() const noexcept + constexpr bool is_number() const noexcept { return is_number_integer() or is_number_float(); } @@ -2025,45 +2331,84 @@ class basic_json /*! @brief return whether value is an integer number - This function returns true iff the JSON value is an integer number. This - excludes floating-point values. + This function returns true iff the JSON value is an integer or unsigned + integer number. This excludes floating-point values. - @return `true` if type is an integer number, `false` otherwise. + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. @complexity Constant. - @liveexample{The following code exemplifies @ref is_number_integer for all + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all JSON types.,is_number_integer} @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ - bool is_number_integer() const noexcept + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept { - return m_type == value_t::number_integer; + return m_type == value_t::number_unsigned; } /*! @brief return whether value is a floating-point number This function returns true iff the JSON value is a floating-point number. - This excludes integer values. + This excludes integer and unsigned integer values. @return `true` if type is a floating-point number, `false` otherwise. @complexity Constant. - @liveexample{The following code exemplifies @ref is_number_float for all + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all JSON types.,is_number_float} @sa @ref is_number() -- check if value is number @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @since version 1.0.0 */ - bool is_number_float() const noexcept + constexpr bool is_number_float() const noexcept { return m_type == value_t::number_float; } @@ -2077,12 +2422,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_object for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON types.,is_object} @since version 1.0.0 */ - bool is_object() const noexcept + constexpr bool is_object() const noexcept { return m_type == value_t::object; } @@ -2096,12 +2444,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_array for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON types.,is_array} @since version 1.0.0 */ - bool is_array() const noexcept + constexpr bool is_array() const noexcept { return m_type == value_t::array; } @@ -2115,12 +2466,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_string for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON types.,is_string} @since version 1.0.0 */ - bool is_string() const noexcept + constexpr bool is_string() const noexcept { return m_type == value_t::string; } @@ -2139,12 +2493,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_discarded for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON types.,is_discarded} @since version 1.0.0 */ - bool is_discarded() const noexcept + constexpr bool is_discarded() const noexcept { return m_type == value_t::discarded; } @@ -2159,12 +2516,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies the value_t operator for all - JSON types.,operator__value_t} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} @since version 1.0.0 */ - operator value_t() const noexcept + constexpr operator value_t() const noexcept { return m_type; } @@ -2328,6 +2688,11 @@ class basic_json return static_cast<T>(m_value.number_integer); } + case value_t::number_unsigned: + { + return static_cast<T>(m_value.number_unsigned); + } + case value_t::number_float: { return static_cast<T>(m_value.number_float); @@ -2341,16 +2706,11 @@ class basic_json } /// get a boolean (explicit) - boolean_t get_impl(boolean_t*) const + constexpr boolean_t get_impl(boolean_t*) const { - if (is_boolean()) - { - return m_value.boolean; - } - else - { - throw std::domain_error("type must be boolean, but is " + type_name()); - } + return is_boolean() + ? m_value.boolean + : throw std::domain_error("type must be boolean, but is " + type_name()); } /// get a pointer to the value (object) @@ -2360,7 +2720,7 @@ class basic_json } /// get a pointer to the value (object) - const object_t* get_impl_ptr(const object_t*) const noexcept + constexpr const object_t* get_impl_ptr(const object_t*) const noexcept { return is_object() ? m_value.object : nullptr; } @@ -2372,7 +2732,7 @@ class basic_json } /// get a pointer to the value (array) - const array_t* get_impl_ptr(const array_t*) const noexcept + constexpr const array_t* get_impl_ptr(const array_t*) const noexcept { return is_array() ? m_value.array : nullptr; } @@ -2384,7 +2744,7 @@ class basic_json } /// get a pointer to the value (string) - const string_t* get_impl_ptr(const string_t*) const noexcept + constexpr const string_t* get_impl_ptr(const string_t*) const noexcept { return is_string() ? m_value.string : nullptr; } @@ -2396,7 +2756,7 @@ class basic_json } /// get a pointer to the value (boolean) - const boolean_t* get_impl_ptr(const boolean_t*) const noexcept + constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } @@ -2408,11 +2768,23 @@ class basic_json } /// get a pointer to the value (integer number) - const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept + constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) 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*) 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*) 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*) noexcept { @@ -2420,7 +2792,7 @@ class basic_json } /// get a pointer to the value (floating-point number) - const number_float_t* get_impl_ptr(const number_float_t*) const noexcept + constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } @@ -2510,8 +2882,8 @@ class basic_json @warning The pointer becomes invalid if the underlying JSON object changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref - number_float_t. + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2545,7 +2917,7 @@ class basic_json std::enable_if< std::is_pointer<PointerType>::value , int>::type = 0> - const PointerType get() const noexcept + constexpr const PointerType get() const noexcept { // delegate the call to get_ptr return get_ptr<PointerType>(); @@ -2561,8 +2933,8 @@ class basic_json state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref - number_float_t. + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2595,7 +2967,7 @@ class basic_json std::is_pointer<PointerType>::value and std::is_const<typename std::remove_pointer<PointerType>::type>::value , int>::type = 0> - const PointerType get_ptr() const noexcept + constexpr const PointerType get_ptr() const noexcept { // delegate the call to get_impl_ptr<>() const return get_impl_ptr(static_cast<const PointerType>(nullptr)); @@ -2660,9 +3032,9 @@ class basic_json @tparam ValueType non-pointer type compatible to the JSON value, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. + `std::vector` types for JSON arrays. The character type of @ref string_t as + well as an initializer list of this type is excluded to avoid ambiguities + as these types implicitly convert to `std::string`. @return copy of the JSON value, converted to type @a ValueType @@ -2722,7 +3094,7 @@ class basic_json @complexity Constant. @liveexample{The example below shows how array elements can be read and - written using at.,at__size_type} + written using `at()`.,at__size_type} @since version 1.0.0 */ @@ -2766,7 +3138,7 @@ class basic_json @complexity Constant. @liveexample{The example below shows how array elements can be read using - at.,at__size_type_const} + `at()`.,at__size_type_const} @since version 1.0.0 */ @@ -2810,7 +3182,7 @@ class basic_json @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} + written using `at()`.,at__object_t_key_type} @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @@ -2858,7 +3230,7 @@ class basic_json @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} + `at()`.,at__object_t_key_type_const} @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @@ -2901,30 +3273,31 @@ class basic_json @return reference to the element at index @a idx - @throw std::domain_error if JSON is not an array or null; example: `"cannot - use operator[] with null"` + @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()`. @liveexample{The example below shows how array elements can be read and - written using [] operator. Note the addition of `null` + written using `[]` operator. Note the addition of `null` values.,operatorarray__size_type} @since version 1.0.0 */ reference operator[](size_type idx) { - // implicitly convert null to object + // implicitly convert null value to an empty array if (is_null()) { m_type = value_t::array; m_value.array = create<array_t>(); } - // [] only works for arrays + // operator[] only works for arrays if (is_array()) { + // fill up array with null values until given idx is reached assert(m_value.array != nullptr); for (size_t i = m_value.array->size(); i <= idx; ++i) { @@ -2954,13 +3327,13 @@ class basic_json @complexity Constant. @liveexample{The example below shows how array elements can be read using - the [] operator.,operatorarray__size_type_const} + the `[]` operator.,operatorarray__size_type_const} @since version 1.0.0 */ const_reference operator[](size_type idx) const { - // at only works for arrays + // const operator[] only works for arrays if (is_array()) { assert(m_value.array != nullptr); @@ -2986,12 +3359,12 @@ class basic_json @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} + written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3001,14 +3374,14 @@ class basic_json */ reference operator[](const typename object_t::key_type& key) { - // implicitly convert null to object + // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create<object_t>(); } - // [] only works for objects + // operator[] only works for objects if (is_object()) { assert(m_value.object != nullptr); @@ -3039,7 +3412,7 @@ class basic_json @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} + the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3049,7 +3422,7 @@ class basic_json */ const_reference operator[](const typename object_t::key_type& key) const { - // [] only works for objects + // const operator[] only works for objects if (is_object()) { assert(m_value.object != nullptr); @@ -3076,12 +3449,12 @@ class basic_json @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} + written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3116,7 +3489,7 @@ class basic_json @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} + the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3144,12 +3517,12 @@ class basic_json @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} + written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3198,7 +3571,7 @@ class basic_json @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} + the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3317,11 +3690,15 @@ class basic_json @complexity Constant. - @note Calling `front` on an empty container is undefined. + @pre The JSON value must not be `null` (would throw `std::out_of_range`) or + an empty array or object (undefined behavior, guarded by assertions). + @post The JSON value remains unchanged. + + @throw std::out_of_range 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} - @liveexample{The following code shows an example for @ref front.,front} + @sa @ref back() -- access the last element @since version 1.0.0 */ @@ -3342,8 +3719,12 @@ class basic_json @brief access the last element Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to `{ auto tmp = - c.end(); --tmp; return *tmp; }`. + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode @return In case of a structured type (array or object), a reference to the last element is returned. In cast of number, string, or boolean values, a @@ -3351,11 +3732,15 @@ class basic_json @complexity Constant. - @note Calling `back` on an empty container is undefined. + @pre The JSON value must not be `null` (would throw `std::out_of_range`) or + an empty array or object (undefined behavior, guarded by assertions). + @post The JSON value remains unchanged. - @throw std::out_of_range when called on null value. + @throw std::out_of_range when called on `null` value. - @liveexample{The following code shows an example for @ref back.,back} + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element @since version 1.0.0 */ @@ -3379,28 +3764,29 @@ class basic_json /*! @brief remove element given an iterator - Removes the element specified by iterator @a pos. Invalidates iterators and - references at or after the point of the erase, including the end() - iterator. The iterator @a pos must be valid and dereferenceable. Thus the - end() iterator (which is valid, but is not dereferenceable) cannot be used - as a value for @a pos. + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. - If called on a primitive type other than null, the resulting JSON value + If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] pos iterator to the element to remove @return Iterator following the last removed element. If the iterator @a pos - refers to the last element, the end() iterator is returned. + refers to the last element, the `end()` iterator is returned. @tparam InteratorType an @ref iterator or @ref const_iterator + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + @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 end()); example: `"iterator out - of range"` + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` @complexity The complexity depends on the type: - objects: amortized constant @@ -3408,7 +3794,7 @@ class basic_json - strings: linear in the length of the string - other types: constant - @liveexample{The example shows the result of erase for different JSON + @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the @@ -3441,6 +3827,7 @@ class basic_json 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()) @@ -3484,21 +3871,23 @@ class basic_json /*! @brief remove elements given an iterator range - Removes the element specified by the range `[first; last)`. Invalidates - iterators and references at or after the point of the erase, including the - end() iterator. The iterator @a first does not need to be dereferenceable - if `first == last`: erasing an empty range is a no-op. + Removes the element specified by the range `[first; last)`. The iterator @a + first does not need to be dereferenceable if `first == last`: erasing an + empty range is a no-op. - If called on a primitive type other than null, the resulting JSON value + If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] first iterator to the beginning of the range to remove @param[in] last iterator past the end of the range to remove @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the end() iterator is returned. + second refers to the last element, the `end()` iterator is returned. @tparam InteratorType an @ref iterator or @ref const_iterator + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + @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 @@ -3514,7 +3903,7 @@ class basic_json - strings: linear in the length of the string - other types: constant - @liveexample{The example shows the result of erase for different JSON + @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} @sa @ref erase(InteratorType) -- removes the element at a given position @@ -3546,6 +3935,7 @@ class basic_json 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()) @@ -3595,16 +3985,19 @@ class basic_json @param[in] key value of the elements to remove - @return Number of elements removed. If ObjectType is the default `std::map` - type, the return value will always be `0` (@a key was not found) or `1` (@a - key was found). + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not found) + or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. @throw std::domain_error when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` - @liveexample{The example shows the effect of erase.,erase__key_type} + @liveexample{The example shows the effect of `erase()`.,erase__key_type} @sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the @@ -3637,12 +4030,12 @@ class basic_json @throw std::domain_error when called on a type other than JSON array; example: `"cannot use erase() with null"` - @throw std::out_of_range when `idx >= size()`; example: `"index out of - range"` + @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. - @liveexample{The example shows the effect of erase.,erase__size_type} + @liveexample{The example shows the effect of `erase()`.,erase__size_type} @sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the @@ -3659,7 +4052,7 @@ class basic_json { if (idx >= size()) { - throw std::out_of_range("index out of range"); + throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); } assert(m_value.array != nullptr); @@ -3671,6 +4064,16 @@ class basic_json } } + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + /*! @brief find an element in a JSON object @@ -3684,7 +4087,7 @@ class basic_json @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how find is used.,find__key_type} + @liveexample{The example shows how `find()` is used.,find__key_type} @since version 1.0.0 */ @@ -3732,7 +4135,7 @@ class basic_json @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how count is used.,count} + @liveexample{The example shows how `count()` is used.,count} @since version 1.0.0 */ @@ -3764,14 +4167,20 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - @liveexample{The following code shows an example for @ref begin.,begin} + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - iterator begin() + iterator begin() noexcept { iterator result(this); result.set_begin(); @@ -3781,7 +4190,7 @@ class basic_json /*! @copydoc basic_json::cbegin() */ - const_iterator begin() const + const_iterator begin() const noexcept { return cbegin(); } @@ -3797,15 +4206,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `const_cast<const basic_json&>(*this).begin()`. - @liveexample{The following code shows an example for @ref cbegin.,cbegin} + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - const_iterator cbegin() const + const_iterator cbegin() const noexcept { const_iterator result(this); result.set_begin(); @@ -3823,14 +4238,20 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - @liveexample{The following code shows an example for @ref end.,end} + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - iterator end() + iterator end() noexcept { iterator result(this); result.set_end(); @@ -3840,7 +4261,7 @@ class basic_json /*! @copydoc basic_json::cend() */ - const_iterator end() const + const_iterator end() const noexcept { return cend(); } @@ -3856,15 +4277,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `const_cast<const basic_json&>(*this).end()`. - @liveexample{The following code shows an example for @ref cend.,cend} + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - const_iterator cend() const + const_iterator cend() const noexcept { const_iterator result(this); result.set_end(); @@ -3880,15 +4307,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(end())`. - @liveexample{The following code shows an example for @ref rbegin.,rbegin} + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ - reverse_iterator rbegin() + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } @@ -3896,7 +4329,7 @@ class basic_json /*! @copydoc basic_json::crbegin() */ - const_reverse_iterator rbegin() const + const_reverse_iterator rbegin() const noexcept { return crbegin(); } @@ -3911,15 +4344,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(begin())`. - @liveexample{The following code shows an example for @ref rend.,rend} + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - reverse_iterator rend() + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } @@ -3927,7 +4366,7 @@ class basic_json /*! @copydoc basic_json::crend() */ - const_reverse_iterator rend() const + const_reverse_iterator rend() const noexcept { return crend(); } @@ -3942,15 +4381,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`. - @liveexample{The following code shows an example for @ref crbegin.,crbegin} + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ - const_reverse_iterator crbegin() const + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } @@ -3965,15 +4410,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `const_cast<const basic_json&>(*this).rend()`. - @liveexample{The following code shows an example for @ref crend.,crend} + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - const_reverse_iterator crend() const + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } @@ -4026,24 +4477,28 @@ class basic_json defined as follows: Value type | return value ----------- | ------------- - null | @c true - boolean | @c false - string | @c false - number | @c false - object | result of function object_t::empty() - array | result of function array_t::empty() + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their empty() functions have constant + Container concept; that is, their `empty()` functions have constant complexity. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `begin() == end()`. - @liveexample{The following code uses @ref empty to check if a @ref json + @liveexample{The following code uses `empty()` to check if a JSON object contains any elements.,empty} + @sa @ref size() -- returns the number of elements + @since version 1.0.0 */ bool empty() const noexcept @@ -4085,23 +4540,28 @@ class basic_json defined as follows: Value type | return value ----------- | ------------- - null | @c 0 - boolean | @c 1 - string | @c 1 - number | @c 1 + null | `0` + boolean | `1` + string | `1` + number | `1` object | result of function object_t::size() array | result of function array_t::size() @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their size() functions have constant complexity. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `std::distance(begin(), end())`. - @liveexample{The following code calls @ref size on the different value + @liveexample{The following code calls `size()` on the different value types.,size} + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + @since version 1.0.0 */ size_type size() const noexcept @@ -4145,25 +4605,29 @@ class basic_json defined as follows: Value type | return value ----------- | ------------- - null | @c 0 (same as size()) - boolean | @c 1 (same as size()) - string | @c 1 (same as size()) - number | @c 1 (same as size()) - object | result of function object_t::max_size() - array | result of function array_t::max_size() + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their max_size() functions have constant + Container concept; that is, their `max_size()` functions have constant complexity. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of returning `b.size()` where `b` is the largest possible JSON value. - @liveexample{The following code calls @ref max_size on the different value + @liveexample{The following code calls `max_size()` on the different value types. Note the output is implementation specific.,max_size} + @sa @ref size() -- returns the number of elements + @since version 1.0.0 */ size_type max_size() const noexcept @@ -4220,7 +4684,7 @@ class basic_json @complexity Linear in the size of the JSON value. - @liveexample{The example below shows the effect of @ref clear to different + @liveexample{The example below shows the effect of `clear()` to different JSON types.,clear} @since version 1.0.0 @@ -4235,6 +4699,12 @@ class basic_json break; } + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + case value_t::number_float: { m_value.number_float = 0.0; @@ -4282,16 +4752,16 @@ class basic_json function is called on a JSON null value, an empty array is created before appending @a val. - @param val the value to add to the JSON array + @param[in] val the value to add to the JSON array @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. - @liveexample{The example shows how `push_back` and `+=` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,push_back} + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} @since version 1.0.0 */ @@ -4375,9 +4845,9 @@ class basic_json @complexity Logarithmic in the size of the container, O(log(`size()`)). - @liveexample{The example shows how `push_back` and `+=` can be used to add - elements to a JSON object. Note how the `null` value was silently converted - to a JSON object.,push_back__object_t__value} + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} @since version 1.0.0 */ @@ -4408,32 +4878,80 @@ class basic_json reference operator+=(const typename object_t::value_type& val) { push_back(val); - return operator[](val.first); + return *this; } /*! - @brief inserts element + @brief add an object to an object - Inserts element @a val before iterator @a pos. + This function allows to use `push_back` with an initializer list. In case - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, - @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"` + @a init is converted into an object element and added using + @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&&). - @complexity Constant plus linear in the distance between pos and end of the - container. + @param init an initializer list - @liveexample{The example shows how insert is used.,insert} + @complexity Linear in the size of the initializer list @a init. - @since version 1.0.0 + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list<basic_json>`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} */ - iterator insert(const_iterator pos, const basic_json& val) + void push_back(std::initializer_list<basic_json> init) + { + if (is_object() and init.size() == 2 and init.begin()->is_string()) + { + const string_t key = *init.begin(); + push_back(typename object_t::value_type(key, *(init.begin() + 1))); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(std::initializer_list<basic_json>) + */ + reference operator+=(std::initializer_list<basic_json> init) + { + push_back(init); + return *this; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @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 Constant plus linear in the distance between pos and end of the + container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) { // insert only works for arrays if (is_array()) @@ -4485,7 +5003,7 @@ class basic_json @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. - @liveexample{The example shows how insert is used.,insert__count} + @liveexample{The example shows how `insert()` is used.,insert__count} @since version 1.0.0 */ @@ -4538,7 +5056,7 @@ class basic_json @complexity Linear in `std::distance(first, last)` plus linear in the distance between @a pos and end of the container. - @liveexample{The example shows how insert is used.,insert__range} + @liveexample{The example shows how `insert()` is used.,insert__range} @since version 1.0.0 */ @@ -4596,7 +5114,7 @@ class basic_json @complexity Linear in `ilist.size()` plus linear in the distance between @a pos and end of the container. - @liveexample{The example shows how insert is used.,insert__ilist} + @liveexample{The example shows how `insert()` is used.,insert__ilist} @since version 1.0.0 */ @@ -4633,8 +5151,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON arrays can be - swapped.,swap__reference} + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} @since version 1.0.0 */ @@ -4664,8 +5182,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON values can be - swapped.,swap__array_t} + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} @since version 1.0.0 */ @@ -4698,8 +5216,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON values can be - swapped.,swap__object_t} + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} @since version 1.0.0 */ @@ -4732,8 +5250,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON values can be - swapped.,swap__string_t} + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} @since version 1.0.0 */ @@ -4771,16 +5289,17 @@ class basic_json @since version 1.0.0 */ - friend bool operator<(const value_t lhs, const value_t rhs) + friend bool operator<(const value_t lhs, const value_t rhs) noexcept { - static constexpr std::array<uint8_t, 7> order = {{ + static constexpr std::array<uint8_t, 8> order = {{ 0, // null 3, // object 4, // array 5, // string 1, // boolean 2, // integer - 2 // float + 2, // unsigned + 2, // float } }; @@ -4856,6 +5375,10 @@ class basic_json { 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; @@ -4874,6 +5397,23 @@ class basic_json { 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) + { + 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) + { + 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) + { + 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) + { + return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned); + } + return false; } @@ -5025,6 +5565,10 @@ class basic_json { 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; @@ -5037,13 +5581,27 @@ class basic_json } 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) { - 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) + { + 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) + { + 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) + { + 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) + { + 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, @@ -5199,7 +5757,7 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. - @liveexample{The example below demonstrates the parse function with and + @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @sa @ref parse(std::istream&, parser_callback_t) for a version that reads @@ -5228,7 +5786,7 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. - @liveexample{The example below demonstrates the parse function with and + @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} @sa @ref parse(const string_t&, parser_callback_t) for a version that reads @@ -5297,7 +5855,7 @@ class basic_json /////////////////////////// /// return the type as string - string_t type_name() const + string_t type_name() const noexcept { switch (m_type) { @@ -5375,7 +5933,7 @@ class basic_json @complexity Linear in the length of string @a s. */ - static string_t escape_string(const string_t& s) noexcept + static string_t escape_string(const string_t& s) { const auto space = extra_space(s); if (space == 0) @@ -5451,7 +6009,8 @@ class basic_json { if (c >= 0x00 and c <= 0x1f) { - // convert a number 0..15 to its hex representation (0..f) + // convert a number 0..15 to its hex representation + // (0..f) auto hexify = [](const char v) -> char { return (v < 10) ? ('0' + v) : ('a' + v - 10); @@ -5488,9 +6047,9 @@ class basic_json additional parameter. In case of arrays and objects, the function is called recursively. Note that - - 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 + - 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[out] o stream to write to @param[in] pretty_print whether the output shall be pretty-printed @@ -5608,25 +6167,88 @@ class basic_json return; } + case value_t::number_unsigned: + { + o << m_value.number_unsigned; + return; + } + case value_t::number_float: { - // If the number is an integer then output as a fixed with with - // precision 1 to output "0.0", "1.0" etc as expected for some - // round trip tests otherwise 15 digits of precision allows - // round-trip IEEE 754 string->double->string; to be safe, we - // read this value from - // std::numeric_limits<number_float_t>::digits10 - if (std::fmod(m_value.number_float, 1) == 0) + // check if number was parsed from a string + if (m_type.bits.parsed) { - o << std::fixed << std::setprecision(1); + // check if parsed number had an exponent given + if (m_type.bits.has_exp) + { + // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1) + char buf[263]; + int len; + + // handle capitalization of the exponent + if (m_type.bits.exp_cap) + { + len = snprintf(buf, sizeof(buf), "%.*E", + m_type.bits.precision, m_value.number_float) + 1; + } + else + { + len = snprintf(buf, sizeof(buf), "%.*e", + m_type.bits.precision, m_value.number_float) + 1; + } + + // remove '+' sign from the exponent if necessary + if (not m_type.bits.exp_plus) + { + if (len > static_cast<int>(sizeof(buf))) + { + len = sizeof(buf); + } + for (int i = 0; i < len; i++) + { + if (buf[i] == '+') + { + for (; i + 1 < len; i++) + { + buf[i] = buf[i + 1]; + } + } + } + } + + o << buf; + } + else + { + // no exponent - output as a decimal + std::stringstream ss; + ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems + ss << std::setprecision(m_type.bits.precision) + << std::fixed << m_value.number_float; + o << ss.str(); + } } else { - // std::defaultfloat not supported in gcc version < 5 - o.unsetf(std::ios_base::floatfield); - o << std::setprecision(std::numeric_limits<double>::digits10); + if (m_value.number_float == 0) + { + // special case for zero to get "0.0"/"-0.0" + o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); + } + else + { + // Otherwise 6, 15 or 16 digits of precision allows + // round-trip IEEE 754 string->float->string, + // string->double->string or string->long double->string; + // to be safe, we read this value from + // std::numeric_limits<number_float_t>::digits10 + std::stringstream ss; + ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems + ss << std::setprecision(std::numeric_limits<double>::digits10) + << m_value.number_float; + o << ss.str(); + } } - o << m_value.number_float; return; } @@ -5650,7 +6272,7 @@ class basic_json ////////////////////// /// the type of the current element - value_t m_type = value_t::null; + type_data_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; @@ -5674,37 +6296,37 @@ class basic_json { public: /// set iterator to a defined beginning - void set_begin() + void set_begin() noexcept { m_it = begin_value; } /// set iterator to a defined past the end - void set_end() + void set_end() noexcept { m_it = end_value; } /// return whether the iterator can be dereferenced - bool is_begin() const + constexpr bool is_begin() const noexcept { return (m_it == begin_value); } /// return whether the iterator is at end - bool is_end() const + constexpr bool is_end() const noexcept { return (m_it == end_value); } /// return reference to the value to change and compare - operator difference_type& () + operator difference_type& () noexcept { return m_it; } /// return value to compare - operator difference_type () const + constexpr operator difference_type () const noexcept { return m_it; } @@ -5734,7 +6356,7 @@ class basic_json primitive_iterator_t primitive_iterator; /// create an uninitialized internal_iterator - internal_iterator() + internal_iterator() noexcept : object_iterator(), array_iterator(), primitive_iterator() {} }; @@ -5754,7 +6376,7 @@ class basic_json size_t array_index = 0; public: - iteration_proxy_internal(IteratorType it) + explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} @@ -5818,18 +6440,18 @@ class basic_json public: /// construct iteration proxy from a container - iteration_proxy(typename IteratorType::reference cont) + explicit iteration_proxy(typename IteratorType::reference cont) : container(cont) {} /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() + iteration_proxy_internal begin() noexcept { return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) - iteration_proxy_internal end() + iteration_proxy_internal end() noexcept { return iteration_proxy_internal(container.end()); } @@ -5870,7 +6492,8 @@ class basic_json const_iterator() = default; /// constructor for a given JSON instance - const_iterator(pointer object) : m_object(object) + explicit const_iterator(pointer object) noexcept + : m_object(object) { assert(m_object != nullptr); @@ -5897,7 +6520,8 @@ class basic_json } /// copy constructor given a nonconst iterator - const_iterator(const iterator& other) : m_object(other.m_object) + explicit const_iterator(const iterator& other) noexcept + : m_object(other.m_object) { assert(m_object != nullptr); @@ -5943,7 +6567,7 @@ class basic_json private: /// set the iterator to the first value - void set_begin() + void set_begin() noexcept { assert(m_object != nullptr); @@ -5979,7 +6603,7 @@ class basic_json } /// set the iterator past the last value - void set_end() + void set_end() noexcept { assert(m_object != nullptr); @@ -6402,7 +7026,7 @@ class basic_json iterator() = default; /// constructor for a given JSON instance - iterator(pointer object) noexcept + explicit iterator(pointer object) noexcept : base_iterator(object) {} @@ -6424,13 +7048,13 @@ class basic_json } /// return a reference to the value pointed to by the iterator - reference operator*() + reference operator*() const { return const_cast<reference>(base_iterator::operator*()); } /// dereference the iterator - pointer operator->() + pointer operator->() const { return const_cast<pointer>(base_iterator::operator->()); } @@ -6495,6 +7119,7 @@ class basic_json return result; } + /// return difference difference_type operator-(const iterator& other) const { return base_iterator::operator-(other); @@ -6540,12 +7165,12 @@ class basic_json using reference = typename Base::reference; /// create reverse iterator from iterator - json_reverse_iterator(const typename base_iterator::iterator_type& it) + 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) + json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} @@ -6635,7 +7260,7 @@ class basic_json @brief lexical analysis This class organizes the lexical analysis during JSON deserialization. The - core of it is a scanner generated by re2c <http://re2c.org> that processes + core of it is a scanner generated by [re2c](http://re2c.org) that processes a buffer and recognizes tokens according to RFC 7159. */ class lexer @@ -6644,20 +7269,20 @@ class basic_json /// 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_number, ///< a 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 - end_of_input ///< indicating the end of the input buffer + 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_number, ///< a 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 + end_of_input ///< indicating the end of the input buffer }; /// the char type to use in the lexer @@ -6710,8 +7335,6 @@ class basic_json static string_t to_unicode(const std::size_t codepoint1, const std::size_t codepoint2 = 0) { - string_t result; - // calculate the codepoint from the given code points std::size_t codepoint = codepoint1; @@ -6737,6 +7360,8 @@ class basic_json } } + string_t result; + if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) @@ -6815,10 +7440,10 @@ class basic_json /*! This function implements a scanner for JSON. It is specified using regular expressions that try to follow RFC 7159 as close as possible. - These regular expressions are then translated into a deterministic - finite automaton (DFA) by the tool re2c <http://re2c.org>. As a result, - the translated code for this function consists of a large block of code - with goto jumps. + These regular expressions are then translated into a minimized + deterministic finite automaton (DFA) by the tool + [re2c](http://re2c.org). As a result, the translated code for this + function consists of a large block of code with `goto` jumps. @return the class of the next token read from the buffer */ @@ -6839,354 +7464,360 @@ class basic_json { 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 0, 0, 32, 0, 0, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 96, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 0, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, }; if ((m_limit - m_cursor) < 5) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; - if (yych <= ':') + if (yybm[0 + yych] & 32) { - if (yych <= ' ') + goto basic_json_parser_6; + } + if (yych <= '\\') + { + if (yych <= '-') { - if (yych <= '\n') + if (yych <= '"') { if (yych <= 0x00) { - goto basic_json_parser_28; - } - if (yych <= 0x08) - { - goto basic_json_parser_30; + goto basic_json_parser_2; } - if (yych >= '\n') + if (yych <= '!') { goto basic_json_parser_4; } + goto basic_json_parser_9; } else { - if (yych == '\r') + if (yych <= '+') { - goto basic_json_parser_2; + goto basic_json_parser_4; } - if (yych <= 0x1F) + if (yych <= ',') { - goto basic_json_parser_30; + goto basic_json_parser_10; } + goto basic_json_parser_12; } } else { - if (yych <= ',') + if (yych <= '9') { - if (yych == '"') + if (yych <= '/') { - goto basic_json_parser_27; + goto basic_json_parser_4; } - if (yych <= '+') + if (yych <= '0') { - goto basic_json_parser_30; + goto basic_json_parser_13; } - goto basic_json_parser_16; + goto basic_json_parser_15; } else { - if (yych <= '/') + if (yych <= ':') { - if (yych <= '-') - { - goto basic_json_parser_23; - } - goto basic_json_parser_30; + goto basic_json_parser_17; } - else + if (yych == '[') { - if (yych <= '0') - { - goto basic_json_parser_24; - } - if (yych <= '9') - { - goto basic_json_parser_26; - } - goto basic_json_parser_18; + goto basic_json_parser_19; } + goto basic_json_parser_4; } } } else { - if (yych <= 'n') + if (yych <= 't') { - if (yych <= ']') + if (yych <= 'f') { - if (yych == '[') + if (yych <= ']') { - goto basic_json_parser_8; + goto basic_json_parser_21; } - if (yych <= '\\') + if (yych <= 'e') { - goto basic_json_parser_30; + goto basic_json_parser_4; } - goto basic_json_parser_10; + goto basic_json_parser_23; } else { - if (yych == 'f') + if (yych == 'n') { - goto basic_json_parser_22; + goto basic_json_parser_24; } - if (yych <= 'm') + if (yych <= 's') { - goto basic_json_parser_30; + goto basic_json_parser_4; } - goto basic_json_parser_20; + goto basic_json_parser_25; } } else { - if (yych <= '{') + if (yych <= '|') { - if (yych == 't') - { - goto basic_json_parser_21; - } - if (yych <= 'z') + if (yych == '{') { - goto basic_json_parser_30; + goto basic_json_parser_26; } - goto basic_json_parser_12; + goto basic_json_parser_4; } else { if (yych <= '}') { - if (yych <= '|') - { - goto basic_json_parser_30; - } - goto basic_json_parser_14; + goto basic_json_parser_28; } - else + if (yych == 0xEF) { - if (yych == 0xEF) - { - goto basic_json_parser_6; - } goto basic_json_parser_30; } + goto basic_json_parser_4; } } } basic_json_parser_2: ++m_cursor; - yych = *m_cursor; - goto basic_json_parser_5; -basic_json_parser_3: { - return scan(); + return token_type::end_of_input; } basic_json_parser_4: ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; basic_json_parser_5: - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_4; - } - goto basic_json_parser_3; -basic_json_parser_6: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_64; - } -basic_json_parser_7: { return token_type::parse_error; } -basic_json_parser_8: - ++m_cursor; - { - return token_type::begin_array; - } -basic_json_parser_10: - ++m_cursor; - { - return token_type::end_array; - } -basic_json_parser_12: - ++m_cursor; - { - return token_type::begin_object; - } -basic_json_parser_14: - ++m_cursor; - { - return token_type::end_object; - } -basic_json_parser_16: +basic_json_parser_6: ++m_cursor; + if (m_limit <= m_cursor) { - return token_type::value_separator; + yyfill(); // LCOV_EXCL_LINE; } -basic_json_parser_18: - ++m_cursor; + yych = *m_cursor; + if (yybm[0 + yych] & 32) { - return token_type::name_separator; + goto basic_json_parser_6; } -basic_json_parser_20: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') { - goto basic_json_parser_60; + return scan(); } - goto basic_json_parser_7; -basic_json_parser_21: +basic_json_parser_9: yyaccept = 0; yych = *(m_marker = ++m_cursor); - if (yych == 'r') + if (yych <= 0x0F) { - goto basic_json_parser_56; + goto basic_json_parser_5; } - goto basic_json_parser_7; -basic_json_parser_22: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') + goto basic_json_parser_32; +basic_json_parser_10: + ++m_cursor; { - goto basic_json_parser_51; + return token_type::value_separator; } - goto basic_json_parser_7; -basic_json_parser_23: +basic_json_parser_12: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_7; + goto basic_json_parser_5; } if (yych <= '0') { - goto basic_json_parser_50; + goto basic_json_parser_13; } if (yych <= '9') { - goto basic_json_parser_41; + goto basic_json_parser_15; } - goto basic_json_parser_7; -basic_json_parser_24: + goto basic_json_parser_5; +basic_json_parser_13: yyaccept = 1; yych = *(m_marker = ++m_cursor); if (yych <= 'D') { if (yych == '.') { - goto basic_json_parser_43; + goto basic_json_parser_37; } } else { if (yych <= 'E') { - goto basic_json_parser_44; + goto basic_json_parser_38; } if (yych == 'e') { - goto basic_json_parser_44; + goto basic_json_parser_38; } } -basic_json_parser_25: +basic_json_parser_14: { return token_type::value_number; } -basic_json_parser_26: +basic_json_parser_15: yyaccept = 1; - yych = *(m_marker = ++m_cursor); - goto basic_json_parser_42; -basic_json_parser_27: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x0F) - { - goto basic_json_parser_7; - } - goto basic_json_parser_32; -basic_json_parser_28: - ++m_cursor; - { - return token_type::end_of_input; - } -basic_json_parser_30: - yych = *++m_cursor; - goto basic_json_parser_7; -basic_json_parser_31: - ++m_cursor; - if (m_limit <= m_cursor) + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) { yyfill(); // LCOV_EXCL_LINE; } yych = *m_cursor; -basic_json_parser_32: if (yybm[0 + yych] & 64) { - goto basic_json_parser_31; + goto basic_json_parser_15; } - if (yych <= 0x0F) + if (yych <= 'D') { - goto basic_json_parser_33; + if (yych == '.') + { + goto basic_json_parser_37; + } + goto basic_json_parser_14; } - if (yych <= '"') + else { - goto basic_json_parser_35; + if (yych <= 'E') + { + goto basic_json_parser_38; + } + if (yych == 'e') + { + goto basic_json_parser_38; + } + goto basic_json_parser_14; } - goto basic_json_parser_34; -basic_json_parser_33: - m_cursor = m_marker; - if (yyaccept == 0) +basic_json_parser_17: + ++m_cursor; { - goto basic_json_parser_7; + return token_type::name_separator; + } +basic_json_parser_19: + ++m_cursor; + { + return token_type::begin_array; + } +basic_json_parser_21: + ++m_cursor; + { + return token_type::end_array; + } +basic_json_parser_23: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_39; + } + goto basic_json_parser_5; +basic_json_parser_24: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_40; + } + goto basic_json_parser_5; +basic_json_parser_25: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_41; + } + goto basic_json_parser_5; +basic_json_parser_26: + ++m_cursor; + { + return token_type::begin_object; + } +basic_json_parser_28: + ++m_cursor; + { + return token_type::end_object; + } +basic_json_parser_30: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 0xBB) + { + goto basic_json_parser_42; + } + goto basic_json_parser_5; +basic_json_parser_31: + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; +basic_json_parser_32: + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_31; + } + if (yych <= 0x0F) + { + goto basic_json_parser_33; + } + if (yych <= '"') + { + goto basic_json_parser_34; + } + goto basic_json_parser_36; +basic_json_parser_33: + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_5; } else { - goto basic_json_parser_25; + goto basic_json_parser_14; } basic_json_parser_34: + ++m_cursor; + { + return token_type::value_string; + } +basic_json_parser_36: ++m_cursor; if (m_limit <= m_cursor) { @@ -7259,18 +7890,78 @@ basic_json_parser_34: } if (yych <= 'u') { - goto basic_json_parser_37; + goto basic_json_parser_43; } goto basic_json_parser_33; } } } -basic_json_parser_35: - ++m_cursor; +basic_json_parser_37: + yych = *++m_cursor; + if (yych <= '/') { - return token_type::value_string; + goto basic_json_parser_33; } -basic_json_parser_37: + if (yych <= '9') + { + goto basic_json_parser_44; + } + goto basic_json_parser_33; +basic_json_parser_38: + yych = *++m_cursor; + if (yych <= ',') + { + if (yych == '+') + { + goto basic_json_parser_46; + } + goto basic_json_parser_33; + } + else + { + if (yych <= '-') + { + goto basic_json_parser_46; + } + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_47; + } + goto basic_json_parser_33; + } +basic_json_parser_39: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_49; + } + goto basic_json_parser_33; +basic_json_parser_40: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_50; + } + goto basic_json_parser_33; +basic_json_parser_41: + yych = *++m_cursor; + if (yych == 'u') + { + goto basic_json_parser_51; + } + goto basic_json_parser_33; +basic_json_parser_42: + yych = *++m_cursor; + if (yych == 0xBF) + { + goto basic_json_parser_52; + } + goto basic_json_parser_33; +basic_json_parser_43: ++m_cursor; if (m_limit <= m_cursor) { @@ -7283,27 +7974,113 @@ basic_json_parser_37: { goto basic_json_parser_33; } - if (yych >= ':') + if (yych <= '9') { - goto basic_json_parser_33; + goto basic_json_parser_54; } + goto basic_json_parser_33; } else { if (yych <= 'F') { - goto basic_json_parser_38; + goto basic_json_parser_54; } if (yych <= '`') { goto basic_json_parser_33; } - if (yych >= 'g') + if (yych <= 'f') { - goto basic_json_parser_33; + goto basic_json_parser_54; } + goto basic_json_parser_33; } -basic_json_parser_38: +basic_json_parser_44: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_38; + } + if (yych == 'e') + { + goto basic_json_parser_38; + } + goto basic_json_parser_14; + } +basic_json_parser_46: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych >= ':') + { + goto basic_json_parser_33; + } +basic_json_parser_47: + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_47; + } + goto basic_json_parser_14; +basic_json_parser_49: + yych = *++m_cursor; + if (yych == 's') + { + goto basic_json_parser_55; + } + goto basic_json_parser_33; +basic_json_parser_50: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_56; + } + goto basic_json_parser_33; +basic_json_parser_51: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_58; + } + goto basic_json_parser_33; +basic_json_parser_52: + ++m_cursor; + { + return scan(); + } +basic_json_parser_54: ++m_cursor; if (m_limit <= m_cursor) { @@ -7316,27 +8093,46 @@ basic_json_parser_38: { goto basic_json_parser_33; } - if (yych >= ':') + if (yych <= '9') { - goto basic_json_parser_33; + goto basic_json_parser_60; } + goto basic_json_parser_33; } else { if (yych <= 'F') { - goto basic_json_parser_39; + goto basic_json_parser_60; } if (yych <= '`') { goto basic_json_parser_33; } - if (yych >= 'g') + if (yych <= 'f') { - goto basic_json_parser_33; + goto basic_json_parser_60; } + goto basic_json_parser_33; } -basic_json_parser_39: +basic_json_parser_55: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_61; + } + goto basic_json_parser_33; +basic_json_parser_56: + ++m_cursor; + { + return token_type::literal_null; + } +basic_json_parser_58: + ++m_cursor; + { + return token_type::literal_true; + } +basic_json_parser_60: ++m_cursor; if (m_limit <= m_cursor) { @@ -7349,27 +8145,34 @@ basic_json_parser_39: { goto basic_json_parser_33; } - if (yych >= ':') + if (yych <= '9') { - goto basic_json_parser_33; + goto basic_json_parser_63; } + goto basic_json_parser_33; } else { if (yych <= 'F') { - goto basic_json_parser_40; + goto basic_json_parser_63; } if (yych <= '`') { goto basic_json_parser_33; } - if (yych >= 'g') + if (yych <= 'f') { - goto basic_json_parser_33; + goto basic_json_parser_63; } + goto basic_json_parser_33; } -basic_json_parser_40: +basic_json_parser_61: + ++m_cursor; + { + return token_type::literal_false; + } +basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { @@ -7404,218 +8207,8 @@ basic_json_parser_40: } goto basic_json_parser_33; } -basic_json_parser_41: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_42: - if (yybm[0 + yych] & 128) - { - goto basic_json_parser_41; - } - if (yych <= 'D') - { - if (yych != '.') - { - goto basic_json_parser_25; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_48; - } - goto basic_json_parser_33; -basic_json_parser_44: - yych = *++m_cursor; - if (yych <= ',') - { - if (yych != '+') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= '-') - { - goto basic_json_parser_45; - } - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_46; - } - goto basic_json_parser_33; - } -basic_json_parser_45: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } -basic_json_parser_46: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_25; - } - if (yych <= '9') - { - goto basic_json_parser_46; - } - goto basic_json_parser_25; -basic_json_parser_48: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'D') - { - if (yych <= '/') - { - goto basic_json_parser_25; - } - if (yych <= '9') - { - goto basic_json_parser_48; - } - goto basic_json_parser_25; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_50: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_43; - } - goto basic_json_parser_25; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_51: - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 's') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_false; - } -basic_json_parser_56: - yych = *++m_cursor; - if (yych != 'u') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_true; - } -basic_json_parser_60: - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_null; - } -basic_json_parser_64: - yych = *++m_cursor; - if (yych != 0xBF) - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return scan(); - } } - } /// append data from the stream to the internal buffer @@ -7626,9 +8219,9 @@ basic_json_parser_64: return; } - const ssize_t offset_start = m_start - m_content; - const ssize_t offset_marker = m_marker - m_start; - const ssize_t offset_cursor = m_cursor - m_start; + const auto offset_start = m_start - m_content; + const auto offset_marker = m_marker - m_start; + const auto offset_cursor = m_cursor - m_start; m_buffer.erase(0, static_cast<size_t>(offset_start)); std::string line; @@ -7645,7 +8238,7 @@ basic_json_parser_64: } /// return string representation of last read token - string_t get_token() const noexcept + string_t get_token() const { assert(m_start != nullptr); return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), @@ -7664,9 +8257,9 @@ basic_json_parser_64: 1. Escaped characters. In this case, a new character is constructed according to the nature of the escape. Some escapes create new - characters (e.g., @c "\\n" is replaced by @c "\n"), some are copied - as is (e.g., @c "\\\\"). Furthermore, Unicode escapes of the shape - @c "\\uxxxx" need special care. In this case, to_unicode takes care + characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied as + is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape + `"\\uxxxx"` need special care. In this case, to_unicode takes care of the construction of the values. 2. Unescaped characters are copied as is. @@ -7780,9 +8373,9 @@ basic_json_parser_64: @brief parse floating point number This function (and its overloads) serves to select the most approprate - standard floating point number parsing function (i.e., `std::strtof`, - `std::strtod`, or `std::strtold`) based on the type supplied via the - first parameter. Set this to @a static_cast<number_float_t>(nullptr). + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to + @a static_cast<number_float_t*>(nullptr). @param[in] type the @ref number_float_t in use @@ -7801,14 +8394,42 @@ basic_json_parser_64: return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); } - /// @copydoc str_to_float_t - double str_to_float_t(double*, char** endptr) const + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to + @a static_cast<number_float_t*>(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + double str_to_float_t(double* /* type */, char** endptr) const { return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); } - /// @copydoc str_to_float_t - float str_to_float_t(float*, char** endptr) const + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to + @a static_cast<number_float_t*>(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + float str_to_float_t(float* /* type */, char** endptr) const { return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr); } @@ -7816,30 +8437,138 @@ basic_json_parser_64: /*! @brief return number value for number tokens - This function translates the last token into a floating point number. - The pointer m_start points to the beginning of the parsed number. We - pass this pointer to std::strtod which sets endptr to the first - character past the converted number. If this pointer is not the same as - m_cursor, then either more or less characters have been used during the - comparison. This can happen for inputs like "01" which will be treated - like number 0 followed by number 1. - - @return the result of the number conversion or NAN if the conversion - read past the current token. The latter case needs to be treated by the - caller function. - - @throw std::range_error if passed value is out of range + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), + which is passed back to the caller via the result parameter. + + This function parses the integer component up to the radix point or + exponent while collecting information about the 'floating point + representation', which it stores in the result parameter. If there is + no radix point or exponent, and the number can fit into a + @ref number_integer_t or @ref number_unsigned_t then it sets the + result parameter accordingly. + + The 'floating point representation' includes the number of significant + figures after the radix point, whether the number is in exponential + or decimal form, the capitalization of the exponent marker, and if the + optional '+' is present in the exponent. This information is necessary + to perform accurate round trips of floating point numbers. + + If the number is a floating point number the number is then parsed + using @a std:strtod (or @a std:strtof or @a std::strtold). + + @param[out] result @ref basic_json object to receive the number, or + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. */ - number_float_t get_number() const + void get_number(basic_json& result) const { - // conversion - typename string_t::value_type* endptr; assert(m_start != nullptr); - number_float_t float_val = str_to_float_t(static_cast<number_float_t*>(nullptr), &endptr); - // return float_val if the whole number was translated and NAN - // otherwise - return (reinterpret_cast<lexer_char_t*>(endptr) == m_cursor) ? float_val : NAN; + const lexer::lexer_char_t* curptr = m_start; + + // remember this number was parsed (for later serialization) + result.m_type.bits.parsed = true; + + // 'found_radix_point' will be set to 0xFF upon finding a radix + // point and later used to mask in/out the precision depending + // whether a radix is found i.e. 'precision &= found_radix_point' + uint8_t found_radix_point = 0; + uint8_t precision = 0; + + // accumulate the integer conversion result (unsigned for now) + number_unsigned_t value = 0; + + // maximum absolute value of the relevant integer type + number_unsigned_t max; + + // temporarily store the type to avoid unecessary bitfield access + value_t type; + + // look for sign + if (*curptr == '-') + { + type = value_t::number_integer; + max = static_cast<uint64_t>(std::numeric_limits<number_integer_t>::max()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast<uint64_t>(std::numeric_limits<number_unsigned_t>::max()); + } + + // count the significant figures + for (; curptr < m_cursor; curptr++) + { + // quickly skip tests if a digit + if (*curptr < '0' || *curptr > '9') + { + if (*curptr == '.') + { + // don't count '.' but change to float + type = value_t::number_float; + + // reset precision count + precision = 0; + found_radix_point = 0xFF; + continue; + } + // assume exponent (if not then will fail parse): change to + // float, stop counting and record exponent details + type = value_t::number_float; + result.m_type.bits.has_exp = true; + + // exponent capitalization + result.m_type.bits.exp_cap = (*curptr == 'E'); + + // exponent '+' sign + result.m_type.bits.exp_plus = (*(++curptr) == '+'); + break; + } + + // skip if definitely not an integer + if (type != value_t::number_float) + { + // multiply last value by ten and add the new digit + auto temp = value * 10 + *curptr - 0x30; + + // test for overflow + if (temp < value || temp > max) + { + // overflow + type = value_t::number_float; + } + else + { + // no overflow - save it + value = temp; + } + } + ++precision; + } + + // If no radix point was found then precision would now be set to + // the number of digits, which is wrong - clear it. + result.m_type.bits.precision = precision & found_radix_point; + + // save the value (if not a float) + if (type == value_t::number_unsigned) + { + result.m_value.number_unsigned = value; + } + else if (type == value_t::number_integer) + { + result.m_value.number_integer = -static_cast<number_integer_t>(value); + } + else + { + // parse with strtod + result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL); + } + + // save the type + result.m_type = type; } private: @@ -7868,7 +8597,7 @@ basic_json_parser_64: { public: /// constructor for strings - parser(const string_t& s, parser_callback_t cb = nullptr) + parser(const string_t& s, parser_callback_t cb = nullptr) noexcept : callback(cb), m_lexer(s) { // read first token @@ -7876,7 +8605,7 @@ basic_json_parser_64: } /// a parser reading from an input stream - parser(std::istream& _is, parser_callback_t cb = nullptr) + parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept : callback(cb), m_lexer(&_is) { // read first token @@ -8069,32 +8798,8 @@ basic_json_parser_64: case lexer::token_type::value_number: { - result.m_value = m_lexer.get_number(); - - // NAN is returned if token could not be translated - // completely - if (std::isnan(result.m_value.number_float)) - { - throw std::invalid_argument(std::string("parse error - ") + - m_lexer.get_token() + " is not a number"); - } - + m_lexer.get_number(result); get_token(); - - // check if conversion loses precision (special case -0.0 always loses precision) - const auto int_val = static_cast<number_integer_t>(result.m_value.number_float); - if (result.m_value.number_float == static_cast<number_float_t>(int_val) and - result.m_value.number_integer != json_value(-0.0f).number_integer) - { - // we would not lose precision -> return int - result.m_type = value_t::number_integer; - result.m_value = int_val; - } - else - { - // we would lose precision -> return float - result.m_type = value_t::number_float; - } break; } @@ -8113,7 +8818,7 @@ basic_json_parser_64: } /// get next token from lexer - typename lexer::token_type get_token() + typename lexer::token_type get_token() noexcept { last_token = m_lexer.scan(); return last_token; @@ -8152,15 +8857,1238 @@ basic_json_parser_64: /// the lexer lexer m_lexer; }; -}; + public: + /*! + @brief JSON Pointer -///////////// -// presets // -///////////// + A JSON pointer defines a string syntax for identifying a specific value + within a JSON document. It can be used with functions `at` and + `operator[]`. Furthermore, JSON pointers are the base for JSON patches. -/*! -@brief default JSON class + @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + class json_pointer + { + /// allow basic_json to access private members + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the + empty string is assumed which references the whole JSON + value + + @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)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + std::string result; + + for (const auto& reference_token : reference_tokens) + { + result += "/" + escape(reference_token); + } + + return result; + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) + { + throw std::domain_error("JSON pointer has no parent"); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } + + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (is_root()) + { + throw std::domain_error("JSON pointer has no parent"); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + */ + reference get_and_create(reference j) const + { + pointer result = &j; + + // in case no reference tokens exist, return a reference to the + // JSON value j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->m_type) + { + case value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case 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 + result = &result->operator[](static_cast<size_type>(std::stoi(reference_token))); + 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: + { + throw std::domain_error("invalid value to unflatten"); + } + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @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) + { + 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') + { + throw std::domain_error("array index must not begin with '0'"); + } + + if (reference_token == "-") + { + // explicityly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); + } + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + reference get_checked(pointer ptr) const + { + 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 == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + 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; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + */ + const_reference get_unchecked(const_pointer ptr) const + { + 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 == "-") + { + // "-" cannot be used for const access + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // use unchecked array access + ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token))); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + const_reference get_checked(const_pointer ptr) const + { + 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 == "-") + { + // "-" always fails the range check + throw std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + 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; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + /// split the string input to reference tokens + static std::vector<std::string> split(std::string reference_string) + { + std::vector<std::string> result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + throw std::domain_error("JSON pointer must be empty or begin with '/'"); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + 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 + 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 + 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); + + // 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)) + { + 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')) + { + throw std::domain_error("escape error: '~' must be followed with '0' or '1'"); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate + @param[in] f the substring to replace with @a t + @param[out] t the string to replace @a f + + @return The string @a s where all occurrences of @a f are replaced + with @a t. + + @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) + { + 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 + ); + } + + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + 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 + { + // 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; + } + + case value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + */ + static basic_json unflatten(const basic_json& value) + { + if (not value.is_object()) + { + 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) + { + if (not element.second.is_primitive()) + { + throw std::domain_error("values in object must be primitive"); + } + + // assign value to reference pointed to by JSON pointer; + // Note that if the JSON pointer is "" (i.e., points to the + // whole value), function get_and_create returns a reference + // to result itself. An assignment will then create a + // primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + private: + /// the reference tokens + std::vector<std::string> reference_tokens {}; + }; + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to + @ref operator[](const typename object_t::key_type&), `null` values + are created in arrays and objects if necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @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) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @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} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @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); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer + @a ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @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 + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the + @ref unflatten() function. + + @return an object that maps JSON pointers to primitve values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this funcion, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @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"` + + @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. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_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}; + + const auto get_op = [](const std::string op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + 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()) + { + result = val; + } + else + { + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + basic_json& x = result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + 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 == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = std::stoi(last_path); + if (static_cast<size_type>(idx) > parent.size()) + { + // avoid undefined behavior + 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; + } + + 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) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (it != parent.end()) + { + parent.erase(it); + } + else + { + throw std::out_of_range("key '" + last_path + "' not found"); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(static_cast<size_type>(std::stoi(last_path))); + } + }; + + // type check + if (not patch.is_array()) + { + // a JSON patch must be an array of objects + throw std::invalid_argument("JSON patch must be an array of objects"); + } + + // iterate and apply th eoperations + for (const auto& val : 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& + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (it == val.m_value.object->end()) + { + 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()) + { + throw std::invalid_argument(error_msg + " must have string member '" + member + "'"); + } + + // no error: return value + return it->second; + }; + + // type check + if (not val.is_object()) + { + throw std::invalid_argument("JSON patch must be an array of objects"); + } + + // collect mandatory members + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + 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); + + // the "from" location must exist - use at() + result[ptr] = result.at(from_ptr); + break; + } + + case patch_operations::test: + { + bool success = false; + 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)); + } + catch (std::out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (not success) + { + throw std::domain_error("unsuccessful: " + val.dump()); + } + + break; + } + + case patch_operations::invalid: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + throw std::invalid_argument("operation value '" + op + "' is invalid"); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to copare from + @param[in] target JSON value to copare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + static basic_json diff(const basic_json& source, + const basic_json& target, + std::string path = "") noexcept + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"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()) + { + // 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 + + // remove my remaining elements + while (i < source.size()) + { + result.push_back(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; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.begin(); it != source.end(); ++it) + { + // 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} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.begin(); it != target.end(); ++it) + { + 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; + } + + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + break; + } + } + } + + return result; + } + + /// @} +}; + + +///////////// +// presets // +///////////// + +/*! +@brief default JSON class This type is the default specialization of the @ref basic_json class which uses the standard template types. @@ -8171,9 +10099,9 @@ using json = basic_json<>; } -///////////////////////// -// nonmember functions // -///////////////////////// +/////////////////////// +// nonmember support // +/////////////////////// // specialization of std::swap, and std::hash namespace std @@ -8214,9 +10142,9 @@ struct hash<nlohmann::json> /*! @brief user-defined string literal for JSON values -This operator implements a user-defined string literal for JSON objects. It can -be used by adding \p "_json" to a string literal and returns a JSON object if -no parse error occurred. +This operator implements a user-defined string literal for JSON objects. It +can be used by adding \p "_json" to a string literal and returns a JSON object +if no parse error occurred. @param[in] s a string representation of a JSON object @return a JSON object @@ -8228,6 +10156,16 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t) return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s)); } +/*! +@brief user-defined string literal for JSON pointer + +@since version 2.0.0 +*/ +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) +{ + return nlohmann::json::json_pointer(s); +} + // restore GCC/clang diagnostic settings #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop diff --git a/src/mcts/allocator.hpp b/src/mcts/allocator.hpp index f765282cc07ed8953538eb53062ed1f68657692a..73e64d86542929d420909ad188d81c2ef0c48101 100644 --- a/src/mcts/allocator.hpp +++ b/src/mcts/allocator.hpp @@ -15,7 +15,7 @@ namespace mcts void copy(node* n1, node* n2, unsigned int prunning = 0); public: - allocator(unsigned int size = 10000000U); + allocator(unsigned int size = 40000000U); ~allocator(); node* allocate(unsigned int size); void clear();