diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-08-10 17:27:46 -0700 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-08-10 17:27:46 -0700 |
commit | dd21c8a577f8bfba9caa58c7567893b2ec10aef9 (patch) | |
tree | 1a2e4dfc9d355dec75a9621a757c6dc618734a83 /ext/json | |
parent | 7d906df8057fa869ab6248b3fb215a94eaf0bffa (diff) | |
download | infinitytier-dd21c8a577f8bfba9caa58c7567893b2ec10aef9.tar.gz infinitytier-dd21c8a577f8bfba9caa58c7567893b2ec10aef9.zip |
Rev nlohmann/json
Diffstat (limited to 'ext/json')
-rw-r--r-- | ext/json/README.md | 21 | ||||
-rw-r--r-- | ext/json/json.hpp | 2286 |
2 files changed, 1281 insertions, 1026 deletions
diff --git a/ext/json/README.md b/ext/json/README.md index cb05d744..c0bb61b1 100644 --- a/ext/json/README.md +++ b/ext/json/README.md @@ -1,10 +1,10 @@ - +[](https://github.com/nlohmann/json/releases) [](https://travis-ci.org/nlohmann/json) [](https://ci.appveyor.com/project/nlohmann/json) [](https://coveralls.io/r/nlohmann/json) [](https://scan.coverity.com/projects/nlohmann-json) -[](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG) +[](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN) [](http://nlohmann.github.io/json) [](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [](https://github.com/nlohmann/json/releases) @@ -24,7 +24,7 @@ Other aspects were not so important to us: - **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs. -- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) with a decent regular expression processor should be even faster (but would consist of more files which makes the integration harder). +- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) may be even faster (but would consist of more files which makes the integration harder). See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information. @@ -291,7 +291,7 @@ json j_umset(c_umset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] ``` -Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys are can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container. +Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container. ```cpp std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} }; @@ -323,7 +323,7 @@ json j_original = R"({ })"_json; // access members with a JSON pointer (RFC 6901) -j_original["/baz/2"_json_pointer]; +j_original["/baz/1"_json_pointer]; // "two" // a JSON patch (RFC 6902) @@ -344,8 +344,8 @@ json j_result = j_original.patch(j_patch); json::diff(j_result, j_original); // [ // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, -// { "op":"remove","path":"/hello" }, -// { "op":"add","path":"/foo","value":"bar" } +// { "op": "remove","path": "/hello" }, +// { "op": "add", "path": "/foo", "value": "bar" } // ] ``` @@ -390,7 +390,7 @@ Though it's 2016 already, the support for C++11 is still a bit sparse. Currently - GCC 4.9 - 6.0 (and possibly later) - Clang 3.4 - 3.9 (and possibly later) -- Microsoft Visual C++ 2015 / 14.0 (and possibly later) +- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) I would be happy to learn about other compilers/versions. @@ -483,6 +483,9 @@ I deeply appreciate the help of the following people. - [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake. - [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files. - [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal). +- [Mário Feroldi](https://github.com/thelostt) fixed a small typo. +- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release. +- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings. Thanks a lot for helping out! @@ -502,7 +505,7 @@ $ make $ ./json_unit "*" =============================================================================== -All tests passed (5568721 assertions in 32 test cases) +All tests passed (8905012 assertions in 32 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/ext/json/json.hpp b/ext/json/json.hpp index 9d6687dd..878fb899 100644 --- a/ext/json/json.hpp +++ b/ext/json/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.0 +| | |__ | | | | | | version 2.0.2 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. @@ -32,11 +32,10 @@ SOFTWARE. #include <algorithm> #include <array> #include <cassert> -#include <cerrno> #include <ciso646> #include <cmath> #include <cstddef> -#include <cstdio> +#include <cstdint> #include <cstdlib> #include <functional> #include <initializer_list> @@ -44,8 +43,10 @@ SOFTWARE. #include <iostream> #include <iterator> #include <limits> +#include <locale> #include <map> #include <memory> +#include <numeric> #include <sstream> #include <stdexcept> #include <string> @@ -53,6 +54,19 @@ SOFTWARE. #include <utility> #include <vector> +// exclude unsupported compilers +#if defined(__clang__) + #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) + #if CLANG_VERSION < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif +#elif defined(__GNUC__) + #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + #if GCC_VERSION < 40900 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif +#endif + // disable float-equal warnings on GCC/clang #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push @@ -76,7 +90,13 @@ namespace { /*! @brief Helper to determine whether there's a key_type for T. + +Thus helper is used to tell associative containers apart from other containers +such as sequence containers. For instance, `std::map` passes the test as it +contains a `mapped_type`, whereas `std::vector` fails the test. + @sa http://stackoverflow.com/a/7728728/266378 +@since version 1.0.0 */ template<typename T> struct has_mapped_type @@ -90,11 +110,18 @@ struct has_mapped_type /*! @brief helper class to create locales with decimal point + +This struct is used a default locale during the JSON serialization. JSON +requires the decimal point to be `.`, so this function overloads the +`do_decimal_point()` function to return `.`. This function is called by +float-to-string conversions to retrieve the decimal separator between integer +and fractional parts. + @sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 +@since version 2.0.0 */ -class DecimalSeparator : public std::numpunct<char> +struct DecimalSeparator : std::numpunct<char> { - protected: char do_decimal_point() const { return '.'; @@ -163,6 +190,13 @@ default) JSON values can be used like STL containers and provide reverse iterator access. +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + @internal @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal @@ -188,13 +222,8 @@ class basic_json { private: /// workaround type for MSVC - using basic_json_t = basic_json<ObjectType, - ArrayType, - StringType, - BooleanType, - NumberIntegerType, - NumberUnsignedType, - NumberFloatType, + using basic_json_t = basic_json<ObjectType, ArrayType, StringType, + BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType>; public: @@ -207,6 +236,8 @@ class basic_json ///////////////////// /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. /// @{ /// the type of elements in a basic_json container @@ -256,6 +287,8 @@ class basic_json /////////////////////////// /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. /// @{ /*! @@ -358,7 +391,7 @@ class basic_json @tparam ArrayType container type to store arrays (e.g., `std::vector` or `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) #### Default type @@ -595,15 +628,14 @@ class basic_json > that implementations will agree exactly on their numeric values. As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], this - class's integer type is interoperable. + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. #### Storage Integer number values are stored directly inside a @ref basic_json type. @sa @ref number_float_t -- type for number values (floating-point) - @sa @ref number_integer_t -- type for number values (integer) @since version 2.0.0 @@ -691,7 +723,19 @@ class basic_json This enumeration collects the different JSON types. It is internally used to distinguish the stored values, and the functions @ref is_null(), @ref is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref - is_number(), and @ref is_discarded() rely on it. + is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and + @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and + @ref is_structured() rely on it. + + @note There are three enumeration entries (number_integer, + number_unsigned, and number_float), because the library distinguishes + these three types for numbers: @ref number_unsigned_t is used for unsigned + integers, @ref number_integer_t is used for signed integers, and @ref + number_float_t is used for floating-point numbers or to approximate + integers which do not fit in the limits of their respective type. + + @sa @ref basic_json(const value_t value_type) -- create a JSON value with + the default value for a given type @since version 1.0.0 */ @@ -702,7 +746,7 @@ class basic_json array, ///< array (ordered collection of values) string, ///< string value boolean, ///< boolean value - number_integer, ///< number value (integer) + number_integer, ///< number value (signed integer) number_unsigned, ///< number value (unsigned integer) number_float, ///< number value (floating-point) discarded ///< discarded by the the parser callback function @@ -711,73 +755,6 @@ class basic_json private: - /*! - @brief a type to hold JSON type information - - This bitfield type holds information about JSON types. It is internally - used to hold the basic JSON type enumeration, as well as additional - information in the case of values that have been parsed from a string - including whether of not it was created directly or parsed, and in the - case of floating point numbers the number of significant figures in the - original representaiton and if it was in exponential form, if a '+' was - included in the exponent and the capitilization of the exponent marker. - The sole purpose of this information is to permit accurate round trips. - - @since version 2.0.0 - */ - union type_data_t - { - struct - { - /// the type of the value (@ref value_t) - uint16_t type : 4; - /// whether the number was parsed from a string - uint16_t parsed : 1; - /// whether parsed number contained an exponent ('e'/'E') - uint16_t has_exp : 1; - /// whether parsed number contained a plus in the exponent - uint16_t exp_plus : 1; - /// whether parsed number's exponent was capitalized ('E') - uint16_t exp_cap : 1; - /// the number of figures for a parsed number - uint16_t precision : 8; - } bits; - uint16_t data; - - /// return the type as value_t - operator value_t() const - { - return static_cast<value_t>(bits.type); - } - - /// test type for equality (ignore other fields) - bool operator==(const value_t& rhs) const - { - return static_cast<value_t>(bits.type) == rhs; - } - - /// assignment - type_data_t& operator=(value_t rhs) - { - bits.type = static_cast<uint16_t>(rhs) & 15; // avoid overflow - return *this; - } - - /// construct from value_t - type_data_t(value_t t) noexcept - { - *reinterpret_cast<uint16_t*>(this) = 0; - bits.type = static_cast<uint16_t>(t) & 15; // avoid overflow - } - - /// default constructor - type_data_t() noexcept - { - data = 0; - bits.type = reinterpret_cast<uint16_t>(value_t::null); - } - }; - /// helper for exception-safe object creation template<typename T, typename... Args> static T* create(Args&& ... args) @@ -789,6 +766,7 @@ class basic_json }; std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter); alloc.construct(object.get(), std::forward<Args>(args)...); + assert(object.get() != nullptr); return object.release(); } @@ -799,7 +777,24 @@ class basic_json /*! @brief a JSON value - The actual storage for a JSON value of the @ref basic_json class. + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. @since version 1.0.0 */ @@ -903,6 +898,21 @@ class basic_json } }; + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } public: ////////////////////////// @@ -915,6 +925,8 @@ class basic_json This enumeration lists the parser events that can trigger calling a callback function of type @ref parser_callback_t during parsing. + @image html callback_events.png "Example when certain parse events are triggered" + @since version 1.0.0 */ enum class parse_event_t : uint8_t @@ -937,12 +949,13 @@ class basic_json @brief per-element parser callback type With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t), it is called on certain - events (passed as @ref parse_event_t via parameter @a event) with a set - recursion depth @a depth and context JSON value @a parsed. The return - value of the callback function is a boolean indicating whether the element - that emitted the callback shall be kept or not. + influenced. When passed to @ref parse(std::istream&, const + parser_callback_t) or @ref parse(const string_t&, const parser_callback_t), + it is called on certain events (passed as @ref parse_event_t via parameter + @a event) with a set recursion depth @a depth and context JSON value + @a parsed. The return value of the callback function is a boolean + indicating whether the element that emitted the callback shall be kept or + not. We distinguish six scenarios (determined by the event type) in which the callback function can be called. The following table describes the values @@ -957,6 +970,8 @@ class basic_json parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + @image html callback_events.png "Example when certain parse events are triggered" + Discarding a value (i.e., returning `false`) has different effects depending on the context in which function was called: @@ -982,7 +997,9 @@ class basic_json @since version 1.0.0 */ - using parser_callback_t = std::function<bool(int depth, parse_event_t event, basic_json& parsed)>; + using parser_callback_t = std::function<bool(int depth, + parse_event_t event, + basic_json& parsed)>; ////////////////// @@ -990,6 +1007,8 @@ class basic_json ////////////////// /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. /// @{ /*! @@ -1033,7 +1052,9 @@ class basic_json */ basic_json(const value_t value_type) : m_type(value_type), m_value(value_type) - {} + { + assert_invariant(); + } /*! @brief create a null object (implicitly) @@ -1041,6 +1062,9 @@ class basic_json Create a `null` JSON value. This is the implicit version of the `null` value constructor as it takes no parameters. + @note The class invariant is satisfied, because it poses no requirements + for null values. + @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws @@ -1085,7 +1109,9 @@ class basic_json */ basic_json(std::nullptr_t) noexcept : basic_json(value_t::null) - {} + { + assert_invariant(); + } /*! @brief create an object (explicit) @@ -1108,7 +1134,9 @@ class basic_json */ basic_json(const object_t& val) : m_type(value_t::object), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an object (implicit) @@ -1147,6 +1175,7 @@ class basic_json using std::begin; using std::end; m_value.object = create<object_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1170,7 +1199,9 @@ class basic_json */ basic_json(const array_t& val) : m_type(value_t::array), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an array (implicit) @@ -1214,6 +1245,7 @@ class basic_json using std::begin; using std::end; m_value.array = create<array_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1239,7 +1271,9 @@ class basic_json */ basic_json(const string_t& val) : m_type(value_t::string), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create a string (explicit) @@ -1263,7 +1297,9 @@ class basic_json */ basic_json(const typename string_t::value_type* val) : basic_json(string_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a string (implicit) @@ -1294,7 +1330,9 @@ class basic_json = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a boolean (explicit) @@ -1312,7 +1350,9 @@ class basic_json */ basic_json(boolean_t val) noexcept : m_type(value_t::boolean), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an integer number (explicit) @@ -1345,7 +1385,9 @@ class basic_json = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an integer number from an enum type (explicit) @@ -1375,7 +1417,9 @@ class basic_json basic_json(const int val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create an integer number (implicit) @@ -1412,15 +1456,17 @@ class basic_json basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create an unsigned integer number (explicit) Create an unsigned integer number JSON value with a given content. - @tparam T helper type to compare number_unsigned_t and unsigned int - (not visible in) the interface. + @tparam T helper type to compare number_unsigned_t and unsigned int (not + visible in) the interface. @param[in] val an integer to create a JSON number from @@ -1439,7 +1485,9 @@ class basic_json = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an unsigned number (implicit) @@ -1471,7 +1519,9 @@ class basic_json basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast<number_unsigned_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create a floating-point number (explicit) @@ -1484,8 +1534,8 @@ class basic_json disallows NaN values: > Numeric values that cannot be represented in the grammar below (such as > Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. + In case the parameter @a val is not a number, a JSON null value is created + instead. @complexity Constant. @@ -1506,6 +1556,8 @@ class basic_json m_type = value_t::null; m_value = json_value(); } + + assert_invariant(); } /*! @@ -1546,7 +1598,9 @@ class basic_json > basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a container (array or object) from an initializer list @@ -1558,21 +1612,21 @@ class basic_json 1. If the list is empty, an empty JSON object value `{}` is created. 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are treated - as keys and the second elements are as values. + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. 3. In all other cases, an array is created. The rules aim to create the best fit between a C++ initializer list and JSON values. The rationale is as follows: 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. + JSON object. 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them as - an object. + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. + JSON object type, so interpreting it as JSON array type is safe. With the rules described above, the following JSON values cannot be expressed by an initializer list: @@ -1621,22 +1675,13 @@ class basic_json bool type_deduction = true, value_t manual_type = value_t::array) { - // the initializer list could describe an object - bool is_an_object = true; - // check if each element is an array with two elements whose first // element is a string - for (const auto& element : init) + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json & element) { - if (not element.is_array() or element.size() != 2 - or not element[0].is_string()) - { - // we found an element that makes it impossible to use the - // initializer list as object - is_an_object = false; - break; - } - } + return element.is_array() and element.size() == 2 and element[0].is_string(); + }); // adjust type if type deduction is not wanted if (not type_deduction) @@ -1660,12 +1705,10 @@ class basic_json m_type = value_t::object; m_value = value_t::object; - assert(m_value.object != nullptr); - - for (auto& element : init) + std::for_each(init.begin(), init.end(), [this](const basic_json & element) { m_value.object->emplace(*(element[0].m_value.string), element[1]); - } + }); } else { @@ -1673,6 +1716,8 @@ class basic_json m_type = value_t::array; m_value.array = create<array_t>(init); } + + assert_invariant(); } /*! @@ -1777,6 +1822,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create<array_t>(cnt, val); + assert_invariant(); } /*! @@ -1797,6 +1843,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) + @pre Iterators @a first and @a last must be initialized. + @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @throw std::out_of_range if iterators are for a primitive type (number, @@ -1819,14 +1867,20 @@ class basic_json std::is_same<InputIT, typename basic_json_t::const_iterator>::value , int>::type = 0> - basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) + basic_json(InputIT first, InputIT last) { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + // make sure iterator fits the current value if (first.m_object != last.m_object) { throw std::domain_error("iterators are not compatible"); } + // copy type from first iterator + m_type = first.m_object->m_type; + // check if iterator range is complete for primitive values switch (m_type) { @@ -1853,35 +1907,30 @@ class basic_json { case value_t::number_integer: { - assert(first.m_object != nullptr); m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { - assert(first.m_object != nullptr); m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { - assert(first.m_object != nullptr); m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { - assert(first.m_object != nullptr); m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { - assert(first.m_object != nullptr); m_value = *first.m_object->m_value.string; break; } @@ -1900,10 +1949,11 @@ class basic_json default: { - assert(first.m_object != nullptr); throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); } } + + assert_invariant(); } /*! @@ -1926,9 +1976,10 @@ class basic_json @since version 2.0.0 */ - explicit basic_json(std::istream& i, parser_callback_t cb = nullptr) + explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); + assert_invariant(); } /////////////////////////////////////// @@ -1960,25 +2011,25 @@ class basic_json basic_json(const basic_json& other) : m_type(other.m_type) { + // check of passed value is valid + other.assert_invariant(); + switch (m_type) { case value_t::object: { - assert(other.m_value.object != nullptr); m_value = *other.m_value.object; break; } case value_t::array: { - assert(other.m_value.array != nullptr); m_value = *other.m_value.array; break; } case value_t::string: { - assert(other.m_value.string != nullptr); m_value = *other.m_value.string; break; } @@ -2012,6 +2063,8 @@ class basic_json break; } } + + assert_invariant(); } /*! @@ -2036,9 +2089,14 @@ class basic_json : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { + // check that passed value is valid + other.assert_invariant(); + // invalidate payload other.m_type = value_t::null; other.m_value = {}; + + assert_invariant(); } /*! @@ -2071,9 +2129,14 @@ class basic_json std::is_nothrow_move_assignable<json_value>::value ) { + // check that passed value is valid + other.assert_invariant(); + using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); + + assert_invariant(); return *this; } @@ -2094,6 +2157,8 @@ class basic_json */ ~basic_json() { + assert_invariant(); + switch (m_type) { case value_t::object: @@ -2136,19 +2201,20 @@ class basic_json /////////////////////// /// @name object inspection + /// Functions to inspect the type of a JSON value. /// @{ /*! @brief serialization Serialization function for JSON values. The function tries to mimic - Python's @p json.dumps() function, and currently supports its @p indent + Python's `json.dumps()` function, and currently supports its @a indent parameter. - @param[in] indent if indent is nonnegative, then array elements and object + @param[in] indent If indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of - 0 will only insert newlines. -1 (the default) selects the most compact - representation + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. @return string containing the serialization of the JSON value @@ -2164,6 +2230,14 @@ class basic_json string_t dump(const int indent = -1) const { std::stringstream ss; + // fix locale problems + ss.imbue(std::locale(std::locale(), new DecimalSeparator)); + + // 6, 15 or 16 digits of precision allows round-trip IEEE 754 + // string->float->string, string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits<number_float_t>::digits10 + ss.precision(std::numeric_limits<double>::digits10); if (indent >= 0) { @@ -2549,7 +2623,6 @@ class basic_json { if (is_object()) { - assert(m_value.object != nullptr); return T(m_value.object->begin(), m_value.object->end()); } else @@ -2563,7 +2636,6 @@ class basic_json { if (is_object()) { - assert(m_value.object != nullptr); return *(m_value.object); } else @@ -2586,7 +2658,6 @@ class basic_json if (is_array()) { T to_vector; - assert(m_value.array != nullptr); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) { @@ -2611,7 +2682,6 @@ class basic_json if (is_array()) { std::vector<T> to_vector; - assert(m_value.array != nullptr); to_vector.reserve(m_value.array->size()); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) @@ -2636,7 +2706,6 @@ class basic_json { if (is_array()) { - assert(m_value.array != nullptr); return T(m_value.array->begin(), m_value.array->end()); } else @@ -2650,7 +2719,6 @@ class basic_json { if (is_array()) { - assert(m_value.array != nullptr); return *(m_value.array); } else @@ -2668,7 +2736,6 @@ class basic_json { if (is_string()) { - assert(m_value.string != nullptr); return *m_value.string; } else @@ -2814,8 +2881,10 @@ class basic_json template<typename ReferenceType, typename ThisType> static ReferenceType get_ref_impl(ThisType& obj) { - // delegate the call to get_ptr<>() + // helper type using PointerType = typename std::add_pointer<ReferenceType>::type; + + // delegate the call to get_ptr<>() auto ptr = obj.template get_ptr<PointerType>(); if (ptr != nullptr) @@ -2832,6 +2901,7 @@ class basic_json public: /// @name value access + /// Direct access to the stored value of a JSON value. /// @{ /*! @@ -2882,7 +2952,8 @@ class basic_json Explicit pointer access to the internally stored JSON value. No copies are made. - @warning The pointer becomes invalid if the underlying JSON object changes. + @warning The pointer becomes invalid if the underlying JSON object + changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, @@ -2937,7 +3008,8 @@ class basic_json @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2957,6 +3029,21 @@ class basic_json , int>::type = 0> PointerType get_ptr() noexcept { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const<typename + std::remove_pointer<typename + std::remove_const<PointerType>::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same<object_t, pointee_t>::value + or std::is_same<array_t, pointee_t>::value + or std::is_same<string_t, pointee_t>::value + or std::is_same<boolean_t, pointee_t>::value + or std::is_same<number_integer_t, pointee_t>::value + or std::is_same<number_unsigned_t, pointee_t>::value + or std::is_same<number_float_t, pointee_t>::value + , "incompatible pointer type"); + // delegate the call to get_impl_ptr<>() return get_impl_ptr(static_cast<PointerType>(nullptr)); } @@ -2972,6 +3059,21 @@ class basic_json , int>::type = 0> constexpr const PointerType get_ptr() const noexcept { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const<typename + std::remove_pointer<typename + std::remove_const<PointerType>::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same<object_t, pointee_t>::value + or std::is_same<array_t, pointee_t>::value + or std::is_same<string_t, pointee_t>::value + or std::is_same<boolean_t, pointee_t>::value + or std::is_same<number_integer_t, pointee_t>::value + or std::is_same<number_unsigned_t, pointee_t>::value + or std::is_same<number_float_t, pointee_t>::value + , "incompatible pointer type"); + // delegate the call to get_impl_ptr<>() const return get_impl_ptr(static_cast<const PointerType>(nullptr)); } @@ -2987,7 +3089,7 @@ class basic_json @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. + @ref number_float_t. Enforced by static assertion. @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws @@ -3077,6 +3179,7 @@ class basic_json //////////////////// /// @name element access + /// Access to the JSON value. /// @{ /*! @@ -3108,7 +3211,6 @@ class basic_json { try { - assert(m_value.array != nullptr); return m_value.array->at(idx); } catch (std::out_of_range&) @@ -3152,7 +3254,6 @@ class basic_json { try { - assert(m_value.array != nullptr); return m_value.array->at(idx); } catch (std::out_of_range&) @@ -3200,7 +3301,6 @@ class basic_json { try { - assert(m_value.object != nullptr); return m_value.object->at(key); } catch (std::out_of_range&) @@ -3248,7 +3348,6 @@ class basic_json { try { - assert(m_value.object != nullptr); return m_value.object->at(key); } catch (std::out_of_range&) @@ -3295,16 +3394,18 @@ class basic_json { m_type = value_t::array; m_value.array = create<array_t>(); + assert_invariant(); } // operator[] only works for arrays if (is_array()) { - // fill up array with null values until given idx is reached - assert(m_value.array != nullptr); - for (size_t i = m_value.array->size(); i <= idx; ++i) + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) { - m_value.array->push_back(basic_json()); + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); } return m_value.array->operator[](idx); @@ -3339,7 +3440,6 @@ class basic_json // const operator[] only works for arrays if (is_array()) { - assert(m_value.array != nullptr); return m_value.array->operator[](idx); } else @@ -3382,12 +3482,12 @@ class basic_json { m_type = value_t::object; m_value.object = create<object_t>(); + assert_invariant(); } // operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } else @@ -3428,7 +3528,6 @@ class basic_json // const operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -3541,12 +3640,12 @@ class basic_json { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } else @@ -3588,7 +3687,6 @@ class basic_json // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -3601,8 +3699,8 @@ class basic_json /*! @brief access specified object element with default value - Returns either a copy of an object's element at the specified key @a key or - a given default value if no element with key @a key exists. + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} @@ -3674,7 +3772,7 @@ class basic_json /*! @brief overload for a default value of type const char* - @copydoc basic_json::value() + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const */ string_t value(const typename object_t::key_type& key, const char* default_value) const { @@ -3682,6 +3780,81 @@ class basic_json } /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(std::out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template <class ValueType, typename + std::enable_if< + std::is_convertible<basic_json_t, ValueType>::value + , int>::type = 0> + ValueType value(const json_pointer& ptr, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if pointer resolves a value, return it or use default value + try + { + return ptr.get_checked(this); + } + catch (std::out_of_range&) + { + return default_value; + } + } + else + { + throw std::domain_error("cannot use value() with " + type_name()); + } + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! @brief access the first element Returns a reference to the first element in the container. For a JSON @@ -3840,24 +4013,25 @@ class basic_json if (is_string()) { - delete m_value.string; + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } @@ -3948,17 +4122,19 @@ class basic_json if (is_string()) { - delete m_value.string; + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; @@ -3966,7 +4142,6 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; @@ -4015,7 +4190,6 @@ class basic_json // this erase only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->erase(key); } else @@ -4058,7 +4232,6 @@ class basic_json throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); } - assert(m_value.array != nullptr); m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx)); } else @@ -4101,7 +4274,6 @@ class basic_json if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -4118,7 +4290,6 @@ class basic_json if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -4146,7 +4317,6 @@ class basic_json size_type count(typename object_t::key_type key) const { // return 0 for all nonobject types - assert(not is_object() or m_value.object != nullptr); return is_object() ? m_value.object->count(key) : 0; } @@ -4488,6 +4658,10 @@ class basic_json object | result of function `object_t::empty()` array | result of function `array_t::empty()` + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their `empty()` functions have constant complexity. @@ -4517,13 +4691,13 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); + // delegate call to array_t::empty() return m_value.array->empty(); } case value_t::object: { - assert(m_value.object != nullptr); + // delegate call to object_t::empty() return m_value.object->empty(); } @@ -4551,6 +4725,10 @@ class basic_json object | result of function object_t::size() array | result of function array_t::size() + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + @complexity Constant, as long as @ref array_t and @ref object_t satisfy the Container concept; that is, their size() functions have constant complexity. @@ -4581,13 +4759,13 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); + // delegate call to array_t::size() return m_value.array->size(); } case value_t::object: { - assert(m_value.object != nullptr); + // delegate call to object_t::size() return m_value.object->size(); } @@ -4641,13 +4819,13 @@ class basic_json { case value_t::array: { - assert(m_value.array != nullptr); + // delegate call to array_t::max_size() return m_value.array->max_size(); } case value_t::object: { - assert(m_value.object != nullptr); + // delegate call to object_t::max_size() return m_value.object->max_size(); } @@ -4724,21 +4902,18 @@ class basic_json case value_t::string: { - assert(m_value.string != nullptr); m_value.string->clear(); break; } case value_t::array: { - assert(m_value.array != nullptr); m_value.array->clear(); break; } case value_t::object: { - assert(m_value.object != nullptr); m_value.object->clear(); break; } @@ -4783,10 +4958,10 @@ class basic_json { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array (move semantics) - assert(m_value.array != nullptr); m_value.array->push_back(std::move(val)); // invalidate object val.m_type = value_t::null; @@ -4819,10 +4994,10 @@ class basic_json { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array - assert(m_value.array != nullptr); m_value.array->push_back(val); } @@ -4869,10 +5044,10 @@ class basic_json { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // add element to array - assert(m_value.object != nullptr); m_value.object->insert(val); } @@ -4969,7 +5144,6 @@ class basic_json // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } @@ -5025,7 +5199,6 @@ class basic_json // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } @@ -5092,7 +5265,6 @@ class basic_json // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert( pos.m_it.array_iterator, first.m_it.array_iterator, @@ -5140,7 +5312,6 @@ class basic_json // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } @@ -5171,6 +5342,7 @@ class basic_json { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + assert_invariant(); } /*! @@ -5198,7 +5370,6 @@ class basic_json // swap only works for arrays if (is_array()) { - assert(m_value.array != nullptr); std::swap(*(m_value.array), other); } else @@ -5232,7 +5403,6 @@ class basic_json // swap only works for objects if (is_object()) { - assert(m_value.object != nullptr); std::swap(*(m_value.object), other); } else @@ -5266,7 +5436,6 @@ class basic_json // swap only works for strings if (is_string()) { - assert(m_value.string != nullptr); std::swap(*(m_value.string), other); } else @@ -5353,14 +5522,10 @@ class basic_json { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array == *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object == *rhs.m_value.object; } case value_t::null: @@ -5369,8 +5534,6 @@ class basic_json } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string == *rhs.m_value.string; } case value_t::boolean: @@ -5543,14 +5706,10 @@ class basic_json { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array < *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object < *rhs.m_value.object; } case value_t::null: @@ -5559,8 +5718,6 @@ class basic_json } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string < *rhs.m_value.string; } case value_t::boolean: @@ -5702,6 +5859,10 @@ class basic_json `std::setw(4)` on @a o sets the indentation level to `4` and the serialization result is the same as calling `dump(4)`. + @note During serializaion, the locale and the precision of the output + stream @a o are changed. The original values are restored when the + function returns. + @param[in,out] o stream to serialize to @param[in] j JSON value to serialize @@ -5723,8 +5884,22 @@ class basic_json // reset width to 0 for subsequent calls to this stream o.width(0); + // fix locale problems + const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator)); + // set precision + + // 6, 15 or 16 digits of precision allows round-trip IEEE 754 + // string->float->string, string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits<number_float_t>::digits10 + const auto old_precision = o.precision(std::numeric_limits<double>::digits10); + // do the actual serialization j.dump(o, pretty_print, static_cast<unsigned int>(indentation)); + + // reset locale and precision + o.imbue(old_locale); + o.precision(old_precision); return o; } @@ -5766,12 +5941,13 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} - @sa @ref parse(std::istream&, parser_callback_t) for a version that reads - from an input stream + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream @since version 1.0.0 */ - static basic_json parse(const string_t& s, parser_callback_t cb = nullptr) + static basic_json parse(const string_t& s, + const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } @@ -5795,20 +5971,22 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const string_t&, parser_callback_t) for a version that - reads from a string + @sa @ref parse(const string_t&, const parser_callback_t) for a version + that reads from a string @since version 1.0.0 */ - static basic_json parse(std::istream& i, parser_callback_t cb = nullptr) + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) { return parser(i, cb).parse(); } /*! - @copydoc parse(std::istream&, parser_callback_t) + @copydoc parse(std::istream&, const parser_callback_t) */ - static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr) + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) { return parser(i, cb).parse(); } @@ -5831,8 +6009,8 @@ class basic_json @liveexample{The example below shows how a JSON value is constructed by reading a serialization from a stream.,operator_deserialize} - @sa parse(std::istream&, parser_callback_t) for a variant with a parser - callback function to filter values while parsing + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing @since version 1.0.0 */ @@ -5860,8 +6038,19 @@ class basic_json // convenience functions // /////////////////////////// - /// return the type as string - string_t type_name() const noexcept + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return basically a string representation of a the @ref m_type member + + @complexity Constant. + + @since version 1.0.0 + */ + std::string type_name() const { switch (m_type) { @@ -5892,9 +6081,8 @@ class basic_json */ static std::size_t extra_space(const string_t& s) noexcept { - std::size_t result = 0; - - for (const auto& c : s) + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) { switch (c) { @@ -5907,8 +6095,7 @@ class basic_json case '\t': { // from c (1 byte) to \x (2 bytes) - result += 1; - break; + return res + 1; } default: @@ -5916,14 +6103,15 @@ class basic_json if (c >= 0x00 and c <= 0x1f) { // from c (1 byte) to \uxxxx (6 bytes) - result += 5; + return res + 5; + } + else + { + return res; } - break; } } - } - - return result; + }); } /*! @@ -6017,16 +6205,15 @@ class basic_json { // convert a number 0..15 to its hex representation // (0..f) - const auto hexify = [](const int v) -> char + static const char hexify[16] = { - return (v < 10) - ? ('0' + static_cast<char>(v)) - : ('a' + static_cast<char>((v - 10) & 0x1f)); + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; // print character c as \uxxxx for (const char m : - { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f) + { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] }) { result[++pos] = m; @@ -6076,8 +6263,6 @@ class basic_json { case value_t::object: { - assert(m_value.object != nullptr); - if (m_value.object->empty()) { o << "{}"; @@ -6118,8 +6303,6 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); - if (m_value.array->empty()) { o << "[]"; @@ -6158,7 +6341,6 @@ class basic_json case value_t::string: { - assert(m_value.string != nullptr); o << string_t("\"") << escape_string(*m_value.string) << "\""; return; } @@ -6183,79 +6365,14 @@ class basic_json case value_t::number_float: { - // check if number was parsed from a string - if (m_type.bits.parsed) + if (m_value.number_float == 0) { - // check if parsed number had an exponent given - if (m_type.bits.has_exp) - { - // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1) - char buf[263]; - int len; - - // handle capitalization of the exponent - if (m_type.bits.exp_cap) - { - len = snprintf(buf, sizeof(buf), "%.*E", - m_type.bits.precision, m_value.number_float) + 1; - } - else - { - len = snprintf(buf, sizeof(buf), "%.*e", - m_type.bits.precision, m_value.number_float) + 1; - } - - // remove '+' sign from the exponent if necessary - if (not m_type.bits.exp_plus) - { - if (len > static_cast<int>(sizeof(buf))) - { - len = sizeof(buf); - } - for (int i = 0; i < len; i++) - { - if (buf[i] == '+') - { - for (; i + 1 < len; i++) - { - buf[i] = buf[i + 1]; - } - } - } - } - - o << buf; - } - else - { - // no exponent - output as a decimal - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems - ss << std::setprecision(m_type.bits.precision) - << std::fixed << m_value.number_float; - o << ss.str(); - } + // special case for zero to get "0.0"/"-0.0" + o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); } else { - if (m_value.number_float == 0) - { - // special case for zero to get "0.0"/"-0.0" - o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); - } - else - { - // Otherwise 6, 15 or 16 digits of precision allows - // round-trip IEEE 754 string->float->string, - // string->double->string or string->long - // double->string; to be safe, we read this value from - // std::numeric_limits<number_float_t>::digits10 - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems - ss << std::setprecision(std::numeric_limits<double>::digits10) - << m_value.number_float; - o << ss.str(); - } + o << m_value.number_float; } return; } @@ -6280,7 +6397,7 @@ class basic_json ////////////////////// /// the type of the current element - type_data_t m_type = value_t::null; + value_t m_type = value_t::null; /// the value of the current element json_value m_value = {}; @@ -6472,6 +6589,12 @@ class basic_json This class implements a const iterator for the @ref basic_json class. From this class, the @ref iterator class is derived. + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. The library uses assertions to detect calls + on uninitialized iterators. + @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any @@ -6499,7 +6622,12 @@ class basic_json /// default constructor const_iterator() = default; - /// constructor for a given JSON instance + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ explicit const_iterator(pointer object) noexcept : m_object(object) { @@ -6527,40 +6655,53 @@ class basic_json } } - /// copy constructor given a nonconst iterator + /*! + @brief copy constructor given a non-const iterator + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ explicit const_iterator(const iterator& other) noexcept : m_object(other.m_object) { - assert(m_object != nullptr); - - switch (m_object->m_type) + if (m_object != nullptr) { - case basic_json::value_t::object: + switch (m_object->m_type) { - m_it.object_iterator = other.m_it.object_iterator; - break; - } + case basic_json::value_t::object: + { + m_it.object_iterator = other.m_it.object_iterator; + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } + case basic_json::value_t::array: + { + m_it.array_iterator = other.m_it.array_iterator; + break; + } - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; + default: + { + m_it.primitive_iterator = other.m_it.primitive_iterator; + break; + } } } } - /// copy constructor + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ const_iterator(const const_iterator& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} - /// copy assignment + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ const_iterator& operator=(const_iterator other) noexcept( std::is_nothrow_move_constructible<pointer>::value and std::is_nothrow_move_assignable<pointer>::value and @@ -6574,7 +6715,10 @@ class basic_json } private: - /// set the iterator to the first value + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ void set_begin() noexcept { assert(m_object != nullptr); @@ -6583,14 +6727,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->begin(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->begin(); break; } @@ -6610,7 +6752,10 @@ class basic_json } } - /// set the iterator past the last value + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ void set_end() noexcept { assert(m_object != nullptr); @@ -6619,14 +6764,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->end(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->end(); break; } @@ -6640,7 +6783,10 @@ class basic_json } public: - /// return a reference to the value pointed to by the iterator + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator*() const { assert(m_object != nullptr); @@ -6649,14 +6795,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -6680,7 +6824,10 @@ class basic_json } } - /// dereference the iterator + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ pointer operator->() const { assert(m_object != nullptr); @@ -6689,14 +6836,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -6715,7 +6860,10 @@ class basic_json } } - /// post-increment (it++) + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator++(int) { auto result = *this; @@ -6723,7 +6871,10 @@ class basic_json return result; } - /// pre-increment (++it) + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator++() { assert(m_object != nullptr); @@ -6732,13 +6883,13 @@ class basic_json { case basic_json::value_t::object: { - ++m_it.object_iterator; + std::advance(m_it.object_iterator, 1); break; } case basic_json::value_t::array: { - ++m_it.array_iterator; + std::advance(m_it.array_iterator, 1); break; } @@ -6752,7 +6903,10 @@ class basic_json return *this; } - /// post-decrement (it--) + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator--(int) { auto result = *this; @@ -6760,7 +6914,10 @@ class basic_json return result; } - /// pre-decrement (--it) + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator--() { assert(m_object != nullptr); @@ -6769,13 +6926,13 @@ class basic_json { case basic_json::value_t::object: { - --m_it.object_iterator; + std::advance(m_it.object_iterator, -1); break; } case basic_json::value_t::array: { - --m_it.array_iterator; + std::advance(m_it.array_iterator, -1); break; } @@ -6789,7 +6946,10 @@ class basic_json return *this; } - /// comparison: equal + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator==(const const_iterator& other) const { // if objects are not the same, the comparison is undefined @@ -6819,13 +6979,19 @@ class basic_json } } - /// comparison: not equal + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator!=(const const_iterator& other) const { return not operator==(other); } - /// comparison: smaller + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator<(const const_iterator& other) const { // if objects are not the same, the comparison is undefined @@ -6855,25 +7021,37 @@ class basic_json } } - /// comparison: less than or equal + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator<=(const const_iterator& other) const { return not other.operator < (*this); } - /// comparison: greater than + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator>(const const_iterator& other) const { return not operator<=(other); } - /// comparison: greater than or equal + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator>=(const const_iterator& other) const { return not operator<(other); } - /// add to iterator + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator+=(difference_type i) { assert(m_object != nullptr); @@ -6887,7 +7065,7 @@ class basic_json case basic_json::value_t::array: { - m_it.array_iterator += i; + std::advance(m_it.array_iterator, i); break; } @@ -6901,13 +7079,19 @@ class basic_json return *this; } - /// subtract from iterator + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator-=(difference_type i) { return operator+=(-i); } - /// add to iterator + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator+(difference_type i) { auto result = *this; @@ -6915,7 +7099,10 @@ class basic_json return result; } - /// subtract from iterator + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator-(difference_type i) { auto result = *this; @@ -6923,7 +7110,10 @@ class basic_json return result; } - /// return difference + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ difference_type operator-(const const_iterator& other) const { assert(m_object != nullptr); @@ -6947,7 +7137,10 @@ class basic_json } } - /// access to successor + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator[](difference_type n) const { assert(m_object != nullptr); @@ -6961,7 +7154,7 @@ class basic_json case basic_json::value_t::array: { - return *(m_it.array_iterator + n); + return *std::next(m_it.array_iterator, n); } case basic_json::value_t::null: @@ -6983,7 +7176,10 @@ class basic_json } } - /// return the key of an object iterator + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ typename object_t::key_type key() const { assert(m_object != nullptr); @@ -6998,7 +7194,10 @@ class basic_json } } - /// return the value of an iterator + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference value() const { return operator*(); @@ -7300,7 +7499,7 @@ class basic_json explicit lexer(const string_t& s) noexcept : m_stream(nullptr), m_buffer(s) { - m_content = reinterpret_cast<const lexer_char_t*>(s.c_str()); + m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; m_limit = m_content + s.size(); @@ -7311,7 +7510,7 @@ class basic_json : m_stream(s), m_buffer() { assert(m_stream != nullptr); - getline(*m_stream, m_buffer); + std::getline(*m_stream, m_buffer); m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; @@ -7326,24 +7525,32 @@ class basic_json lexer operator=(const lexer&) = delete; /*! - @brief create a string from a Unicode code point + @brief create a string from one or two Unicode code points + + There are two cases: (1) @a codepoint1 is in the Basic Multilingual + Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) + @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to + represent a code point above U+FFFF. @param[in] codepoint1 the code point (can be high surrogate) @param[in] codepoint2 the code point (can be low surrogate or 0) - @return string representation of the code point + @return string representation of the code point; the length of the + result string is between 1 and 4 characters. @throw std::out_of_range if code point is > 0x10ffff; example: `"code points above 0x10FFFF are invalid"` @throw std::invalid_argument if the low surrogate is invalid; example: `""missing or wrong low surrogate""` + @complexity Constant. + @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code> */ static string_t to_unicode(const std::size_t codepoint1, const std::size_t codepoint2 = 0) { - // calculate the codepoint from the given code points + // calculate the code point from the given code points std::size_t codepoint = codepoint1; // check if codepoint1 is a high surrogate @@ -7405,7 +7612,7 @@ class basic_json } /// return name of values of type token_type (only used for errors) - static std::string token_type_name(token_type t) + static std::string token_type_name(const token_type t) { switch (t) { @@ -7454,403 +7661,416 @@ class basic_json function consists of a large block of code with `goto` jumps. @return the class of the next token read from the buffer + + @complexity Linear in the length of the input.\n + + Proposition: The loop below will always terminate for finite input.\n + + Proof (by contradiction): Assume a finite input. To loop forever, the + loop must never hit code with a `break` statement. The only code + snippets without a `break` statement are the continue statements for + whitespace and byte-order-marks. To loop forever, the input must be an + infinite sequence of whitespace or byte-order-marks. This contradicts + the assumption of finite input, q.e.d. */ token_type scan() noexcept { - // pointer for backtracking information - m_marker = nullptr; + while (true) + { + // pointer for backtracking information + m_marker = nullptr; - // remember the begin of the token - m_start = m_cursor; - assert(m_start != nullptr); + // remember the begin of the token + m_start = m_cursor; + assert(m_start != nullptr); - { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 160, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - }; - if ((m_limit - m_cursor) < 5) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - if (yych <= '\\') - { - if (yych <= '-') - { - if (yych <= '"') - { - if (yych <= 0x00) - { - goto basic_json_parser_2; - } - if (yych <= '!') - { - goto basic_json_parser_4; - } - goto basic_json_parser_9; - } - else - { - if (yych <= '+') - { - goto basic_json_parser_4; - } - if (yych <= ',') - { - goto basic_json_parser_10; - } - goto basic_json_parser_12; - } + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 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; } - else + yych = *m_cursor; + if (yybm[0 + yych] & 32) { - if (yych <= '9') - { - if (yych <= '/') - { - goto basic_json_parser_4; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - goto basic_json_parser_15; - } - else - { - if (yych <= ':') - { - goto basic_json_parser_17; - } - if (yych == '[') - { - goto basic_json_parser_19; - } - goto basic_json_parser_4; - } + goto basic_json_parser_6; } - } - else - { - if (yych <= 't') + if (yych <= '\\') { - if (yych <= 'f') + if (yych <= '-') { - if (yych <= ']') + if (yych <= '"') { - goto basic_json_parser_21; + if (yych <= 0x00) + { + goto basic_json_parser_2; + } + if (yych <= '!') + { + goto basic_json_parser_4; + } + goto basic_json_parser_9; } - if (yych <= 'e') + else { - goto basic_json_parser_4; + if (yych <= '+') + { + goto basic_json_parser_4; + } + if (yych <= ',') + { + goto basic_json_parser_10; + } + goto basic_json_parser_12; } - goto basic_json_parser_23; } else { - if (yych == 'n') + if (yych <= '9') { - goto basic_json_parser_24; + if (yych <= '/') + { + goto basic_json_parser_4; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + goto basic_json_parser_15; } - if (yych <= 's') + else { + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych == '[') + { + goto basic_json_parser_19; + } goto basic_json_parser_4; } - goto basic_json_parser_25; } } else { - if (yych <= '|') + if (yych <= 't') { - if (yych == '{') + if (yych <= 'f') + { + if (yych <= ']') + { + goto basic_json_parser_21; + } + if (yych <= 'e') + { + goto basic_json_parser_4; + } + goto basic_json_parser_23; + } + else { - goto basic_json_parser_26; + if (yych == 'n') + { + goto basic_json_parser_24; + } + if (yych <= 's') + { + goto basic_json_parser_4; + } + goto basic_json_parser_25; } - goto basic_json_parser_4; } else { - if (yych <= '}') + if (yych <= '|') { - goto basic_json_parser_28; + if (yych == '{') + { + goto basic_json_parser_26; + } + goto basic_json_parser_4; } - if (yych == 0xEF) + else { - goto basic_json_parser_30; + if (yych <= '}') + { + goto basic_json_parser_28; + } + if (yych == 0xEF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_4; } - goto basic_json_parser_4; } } - } basic_json_parser_2: - ++m_cursor; - { - return token_type::end_of_input; - } + ++m_cursor; + { + last_token_type = token_type::end_of_input; + break; + } basic_json_parser_4: - ++m_cursor; + ++m_cursor; basic_json_parser_5: - { - return token_type::parse_error; - } + { + last_token_type = token_type::parse_error; + break; + } basic_json_parser_6: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - { - return scan(); - } + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + { + continue; + } basic_json_parser_9: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x0F) - { - goto basic_json_parser_5; - } - goto basic_json_parser_32; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) + { + goto basic_json_parser_5; + } + goto basic_json_parser_32; basic_json_parser_10: - ++m_cursor; - { - return token_type::value_separator; - } + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } basic_json_parser_12: - yych = *++m_cursor; - if (yych <= '/') - { + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_5; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + if (yych <= '9') + { + goto basic_json_parser_15; + } goto basic_json_parser_5; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - if (yych <= '9') - { - goto basic_json_parser_15; - } - goto basic_json_parser_5; basic_json_parser_13: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') { - goto basic_json_parser_37; + if (yych == '.') + { + goto basic_json_parser_37; + } } - } - else - { - if (yych <= 'E') + else { - goto basic_json_parser_38; + if (yych <= 'E') + { + goto basic_json_parser_38; + } + if (yych == 'e') + { + goto basic_json_parser_38; + } } - if (yych == 'e') +basic_json_parser_14: { - goto basic_json_parser_38; + last_token_type = token_type::value_number; + break; } - } -basic_json_parser_14: - { - return token_type::value_number; - } basic_json_parser_15: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yybm[0 + yych] & 64) - { - goto basic_json_parser_15; - } - if (yych <= 'D') - { - if (yych == '.') + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) { - goto basic_json_parser_37; + yyfill(); // LCOV_EXCL_LINE; } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') + yych = *m_cursor; + if (yybm[0 + yych] & 64) { - goto basic_json_parser_38; + goto basic_json_parser_15; } - if (yych == 'e') + if (yych <= 'D') { - goto basic_json_parser_38; + if (yych == '.') + { + goto basic_json_parser_37; + } + 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; } - goto basic_json_parser_14; - } basic_json_parser_17: - ++m_cursor; - { - return token_type::name_separator; - } + ++m_cursor; + { + last_token_type = token_type::name_separator; + break; + } basic_json_parser_19: - ++m_cursor; - { - return token_type::begin_array; - } + ++m_cursor; + { + last_token_type = token_type::begin_array; + break; + } basic_json_parser_21: - ++m_cursor; - { - return token_type::end_array; - } + ++m_cursor; + { + last_token_type = token_type::end_array; + break; + } basic_json_parser_23: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_39; - } - goto basic_json_parser_5; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_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; + 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; + 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; - } + ++m_cursor; + { + last_token_type = token_type::begin_object; + break; + } basic_json_parser_28: - ++m_cursor; - { - return token_type::end_object; - } + ++m_cursor; + { + last_token_type = token_type::end_object; + break; + } basic_json_parser_30: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_42; - } - goto basic_json_parser_5; + 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; + ++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; + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_31; + } + if (yych <= 0x1F) + { + goto basic_json_parser_33; + } + if (yych <= '"') + { + goto basic_json_parser_34; + } + goto basic_json_parser_36; basic_json_parser_33: - m_cursor = m_marker; - if (yyaccept == 0) - { - goto basic_json_parser_5; - } - else - { - goto basic_json_parser_14; - } + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_5; + } + else + { + goto basic_json_parser_14; + } basic_json_parser_34: - ++m_cursor; - { - return token_type::value_string; - } + ++m_cursor; + { + last_token_type = token_type::value_string; + break; + } basic_json_parser_36: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'e') - { - if (yych <= '/') + ++m_cursor; + if (m_limit <= m_cursor) { - if (yych == '"') - { - goto basic_json_parser_31; - } - if (yych <= '.') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; + yyfill(); // LCOV_EXCL_LINE; } - else + yych = *m_cursor; + if (yych <= 'e') { - if (yych <= '\\') + if (yych <= '/') { - if (yych <= '[') + if (yych == '"') + { + goto basic_json_parser_31; + } + if (yych <= '.') { goto basic_json_parser_33; } @@ -7858,33 +8078,33 @@ basic_json_parser_36: } else { - if (yych == 'b') + if (yych <= '\\') { + if (yych <= '[') + { + goto basic_json_parser_33; + } goto basic_json_parser_31; } - goto basic_json_parser_33; - } - } - } - else - { - if (yych <= 'q') - { - if (yych <= 'f') - { - goto basic_json_parser_31; - } - if (yych == 'n') - { - goto basic_json_parser_31; + else + { + if (yych == 'b') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; + } } - goto basic_json_parser_33; } else { - if (yych <= 's') + if (yych <= 'q') { - if (yych <= 'r') + if (yych <= 'f') + { + goto basic_json_parser_31; + } + if (yych == 'n') { goto basic_json_parser_31; } @@ -7892,331 +8112,347 @@ basic_json_parser_36: } else { - if (yych <= 't') + if (yych <= 's') { - goto basic_json_parser_31; + if (yych <= 'r') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; } - if (yych <= 'u') + else { - goto basic_json_parser_43; + if (yych <= 't') + { + goto basic_json_parser_31; + } + if (yych <= 'u') + { + goto basic_json_parser_43; + } + goto basic_json_parser_33; } - goto basic_json_parser_33; } } - } basic_json_parser_37: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_44; - } - goto basic_json_parser_33; -basic_json_parser_38: - yych = *++m_cursor; - if (yych <= ',') - { - if (yych == '+') + yych = *++m_cursor; + if (yych <= '/') { - goto basic_json_parser_46; + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_44; } goto basic_json_parser_33; - } - else - { - if (yych <= '-') +basic_json_parser_38: + yych = *++m_cursor; + if (yych <= ',') { - goto basic_json_parser_46; + if (yych == '+') + { + goto basic_json_parser_46; + } + goto basic_json_parser_33; } - if (yych <= '/') + 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; } - if (yych <= '9') +basic_json_parser_39: + yych = *++m_cursor; + if (yych == 'l') { - goto basic_json_parser_47; + goto basic_json_parser_49; } 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; + 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; + 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; + 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) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') + ++m_cursor; + if (m_limit <= m_cursor) + { + yyfill(); // LCOV_EXCL_LINE; + } + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_54; + } goto basic_json_parser_33; } - if (yych <= '9') + else { - goto basic_json_parser_54; + if (yych <= 'F') + { + goto basic_json_parser_54; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_54; + } + goto basic_json_parser_33; } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') +basic_json_parser_44: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) { - goto basic_json_parser_54; + yyfill(); // LCOV_EXCL_LINE; } - if (yych <= '`') + 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 <= 'f') + if (yych >= ':') { - goto basic_json_parser_54; + goto basic_json_parser_33; } - goto basic_json_parser_33; - } -basic_json_parser_44: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'D') - { +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_44; + goto basic_json_parser_47; } goto basic_json_parser_14; - } - else - { - if (yych <= 'E') +basic_json_parser_49: + yych = *++m_cursor; + if (yych == 's') { - goto basic_json_parser_38; + goto basic_json_parser_55; } - if (yych == 'e') + goto basic_json_parser_33; +basic_json_parser_50: + yych = *++m_cursor; + if (yych == 'l') { - goto basic_json_parser_38; + goto basic_json_parser_56; } - 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; + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_58; + } + goto basic_json_parser_33; basic_json_parser_52: - ++m_cursor; - { - return scan(); - } -basic_json_parser_54: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') + ++m_cursor; { - goto basic_json_parser_33; + continue; } - if (yych <= '9') +basic_json_parser_54: + ++m_cursor; + if (m_limit <= m_cursor) { - goto basic_json_parser_60; + yyfill(); // LCOV_EXCL_LINE; } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') + yych = *m_cursor; + if (yych <= '@') { - goto basic_json_parser_60; + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_60; + } + goto basic_json_parser_33; } - if (yych <= '`') + else { + if (yych <= 'F') + { + goto basic_json_parser_60; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_60; + } goto basic_json_parser_33; } - if (yych <= 'f') +basic_json_parser_55: + yych = *++m_cursor; + if (yych == 'e') { - goto basic_json_parser_60; + goto basic_json_parser_61; } goto basic_json_parser_33; - } -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) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') + ++m_cursor; { - goto basic_json_parser_33; + last_token_type = token_type::literal_null; + break; } - if (yych <= '9') +basic_json_parser_58: + ++m_cursor; { - goto basic_json_parser_63; + last_token_type = token_type::literal_true; + break; } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') +basic_json_parser_60: + ++m_cursor; + if (m_limit <= m_cursor) { - goto basic_json_parser_63; + yyfill(); // LCOV_EXCL_LINE; } - if (yych <= '`') + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_63; + } goto basic_json_parser_33; } - if (yych <= 'f') - { - goto basic_json_parser_63; - } - goto basic_json_parser_33; - } -basic_json_parser_61: - ++m_cursor; - { - return token_type::literal_false; - } -basic_json_parser_63: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') + else { + if (yych <= 'F') + { + goto basic_json_parser_63; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_63; + } goto basic_json_parser_33; } - if (yych <= '9') +basic_json_parser_61: + ++m_cursor; { - goto basic_json_parser_31; + last_token_type = token_type::literal_false; + break; } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') +basic_json_parser_63: + ++m_cursor; + if (m_limit <= m_cursor) { - goto basic_json_parser_31; + yyfill(); // LCOV_EXCL_LINE; } - if (yych <= '`') + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') + { + goto basic_json_parser_33; + } + if (yych <= '9') + { + goto basic_json_parser_31; + } goto basic_json_parser_33; } - if (yych <= 'f') + else { - goto basic_json_parser_31; + if (yych <= 'F') + { + goto basic_json_parser_31; + } + if (yych <= '`') + { + goto basic_json_parser_33; + } + if (yych <= 'f') + { + goto basic_json_parser_31; + } + goto basic_json_parser_33; } - goto basic_json_parser_33; } + } + return last_token_type; } /// append data from the stream to the internal buffer @@ -8246,7 +8482,7 @@ basic_json_parser_63: } /// return string representation of last read token - string_t get_token() const + string_t get_token_string() const { assert(m_start != nullptr); return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), @@ -8271,12 +8507,49 @@ basic_json_parser_63: of the construction of the values. 2. Unescaped characters are copied as is. + @pre `m_cursor - m_start >= 2`, meaning the length of the last token + is at least 2 bytes which is trivially true for any string (which + consists of at least two quotes). + + " c1 c2 c3 ... " + ^ ^ + m_start m_cursor + + @complexity Linear in the length of the string.\n + + Lemma: The loop body will always terminate.\n + + Proof (by contradiction): Assume the loop body does not terminate. As + the loop body does not contain another loop, one of the called + functions must never return. The called functions are `std::strtoul` + and to_unicode. Neither function can loop forever, so the loop body + will never loop forever which contradicts the assumption that the loop + body does not terminate, q.e.d.\n + + Lemma: The loop condition for the for loop is eventually false.\n + + Proof (by contradiction): Assume the loop does not terminate. Due to + the above lemma, this can only be due to a tautological loop + condition; that is, the loop condition i < m_cursor - 1 must always be + true. Let x be the change of i for any loop iteration. Then + m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This + can be rephrased to m_cursor - m_start - 2 > x. With the + precondition, we x <= 0, meaning that the loop condition holds + indefinitly if i is always decreased. However, observe that the value + of i is strictly increasing with each iteration, as it is incremented + by 1 in the iteration expression and never decremented inside the loop + body. Hence, the loop condition will eventually be false which + contradicts the assumption that the loop condition is a tautology, + q.e.d. + @return string value of current token without opening and closing quotes @throw std::out_of_range if to_unicode fails */ string_t get_string() const { + assert(m_cursor - m_start >= 2); + string_t result; result.reserve(static_cast<size_t>(m_cursor - m_start - 2)); @@ -8392,11 +8665,6 @@ basic_json_parser_63: the number @return the floating point number - - @bug This function uses `std::strtof`, `std::strtod`, or `std::strtold` - which use the current C locale to determine which character is used as - decimal point character. This may yield to parse errors if the locale - does not used `.`. */ long double str_to_float_t(long double* /* type */, char** endptr) const { @@ -8457,18 +8725,12 @@ basic_json_parser_63: number_integer_t or @ref number_unsigned_t then it sets the result parameter accordingly. - The 'floating point representation' includes the number of significant - figures after the radix point, whether the number is in exponential or - decimal form, the capitalization of the exponent marker, and if the - optional '+' is present in the exponent. This information is necessary - to perform accurate round trips of floating point numbers. - If the number is a floating point number the number is then parsed using @a std:strtod (or @a std:strtof or @a std::strtold). @param[out] result @ref basic_json object to receive the number, or - NAN if the conversion read past the current token. The latter case - needs to be treated by the caller function. + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. */ void get_number(basic_json& result) const { @@ -8476,15 +8738,6 @@ basic_json_parser_63: const lexer::lexer_char_t* curptr = m_start; - // remember this number was parsed (for later serialization) - result.m_type.bits.parsed = true; - - // 'found_radix_point' will be set to 0xFF upon finding a radix - // point and later used to mask in/out the precision depending - // whether a radix is found i.e. 'precision &= found_radix_point' - uint8_t found_radix_point = 0; - uint8_t precision = 0; - // accumulate the integer conversion result (unsigned for now) number_unsigned_t value = 0; @@ -8517,22 +8770,11 @@ basic_json_parser_63: { // don't count '.' but change to float type = value_t::number_float; - - // reset precision count - precision = 0; - found_radix_point = 0xFF; continue; } // assume exponent (if not then will fail parse): change to // float, stop counting and record exponent details type = value_t::number_float; - result.m_type.bits.has_exp = true; - - // exponent capitalization - result.m_type.bits.exp_cap = (*curptr == 'E'); - - // exponent '+' sign - result.m_type.bits.exp_plus = (*(++curptr) == '+'); break; } @@ -8540,7 +8782,7 @@ basic_json_parser_63: if (type != value_t::number_float) { // multiply last value by ten and add the new digit - auto temp = value * 10 + *curptr - 0x30; + auto temp = value * 10 + *curptr - '0'; // test for overflow if (temp < value || temp > max) @@ -8554,13 +8796,8 @@ basic_json_parser_63: 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) { @@ -8595,6 +8832,8 @@ basic_json_parser_63: const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer const lexer_char_t* m_limit = nullptr; + /// the last token type + token_type last_token_type = token_type::end_of_input; }; /*! @@ -8606,7 +8845,7 @@ basic_json_parser_63: { public: /// constructor for strings - parser(const string_t& s, parser_callback_t cb = nullptr) noexcept + parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept : callback(cb), m_lexer(s) { // read first token @@ -8614,7 +8853,7 @@ basic_json_parser_63: } /// a parser reading from an input stream - parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept + parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept : callback(cb), m_lexer(&_is) { // read first token @@ -8625,12 +8864,13 @@ basic_json_parser_63: basic_json parse() { basic_json result = parse_internal(true); + result.assert_invariant(); expect(lexer::token_type::end_of_input); // return parser result and replace it with null in case the // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : result; + return result.is_discarded() ? basic_json() : std::move(result); } private: @@ -8647,7 +8887,7 @@ basic_json_parser_63: { // explicitly set result to object to cope with {} result.m_type = value_t::object; - result.m_value = json_value(value_t::object); + result.m_value = value_t::object; } // read next token @@ -8725,7 +8965,7 @@ basic_json_parser_63: { // explicitly set result to object to cope with [] result.m_type = value_t::array; - result.m_value = json_value(value_t::array); + result.m_value = value_t::array; } // read next token @@ -8838,7 +9078,8 @@ basic_json_parser_63: if (t != last_token) { std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); throw std::invalid_argument(error_msg); @@ -8850,7 +9091,8 @@ basic_json_parser_63: if (t == last_token) { std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : lexer::token_type_name(last_token)); throw std::invalid_argument(error_msg); } @@ -8860,7 +9102,7 @@ basic_json_parser_63: /// current level of recursion int depth = 0; /// callback function - parser_callback_t callback; + const parser_callback_t callback = nullptr; /// the type of the last read token typename lexer::token_type last_token = lexer::token_type::uninitialized; /// the lexer @@ -8928,14 +9170,12 @@ basic_json_parser_63: */ std::string to_string() const noexcept { - std::string result; - - for (const auto& reference_token : reference_tokens) + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) { - result += "/" + escape(reference_token); - } - - return result; + return a + "/" + escape(b); + }); } /// @copydoc to_string() @@ -8978,6 +9218,8 @@ basic_json_parser_63: /*! @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. */ reference get_and_create(reference j) const { @@ -9299,7 +9541,7 @@ basic_json_parser_63: @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 + @param[in] t the string to replace @a f @return The string @a s where all occurrences of @a f are replaced with @a t. @@ -10002,9 +10244,12 @@ basic_json_parser_63: // in a second pass, traverse the remaining elements // remove my remaining elements + const auto end_index = static_cast<difference_type>(result.size()); while (i < source.size()) { - result.push_back(object( + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( { {"op", "remove"}, {"path", path + "/" + std::to_string(i)} @@ -10152,7 +10397,7 @@ struct hash<nlohmann::json> @brief user-defined string literal for JSON values This operator implements a user-defined string literal for JSON objects. It -can be used by adding \p "_json" to a string literal and returns a JSON object +can be used by adding `"_json"` to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object @@ -10168,6 +10413,13 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t) /*! @brief user-defined string literal for JSON pointer +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@return a JSON pointer object + @since version 2.0.0 */ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) |