diff options
-rw-r--r-- | ext/json/README.md | 51 | ||||
-rw-r--r-- | ext/json/json.hpp | 1309 |
2 files changed, 873 insertions, 487 deletions
diff --git a/ext/json/README.md b/ext/json/README.md index c0bb61b1..4bcbe97f 100644 --- a/ext/json/README.md +++ b/ext/json/README.md @@ -1,24 +1,24 @@ [](https://github.com/nlohmann/json/releases) [](https://travis-ci.org/nlohmann/json) -[](https://ci.appveyor.com/project/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/p5o4znPnGHJpDVqN) +[](http://melpon.org/wandbox/permlink/fsf5FqYe6GoX68W6) [](http://nlohmann.github.io/json) [](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [](https://github.com/nlohmann/json/releases) [](http://github.com/nlohmann/json/issues) +[](https://bestpractices.coreinfrastructure.org/projects/289) ## Design goals There are myriads of [JSON](http://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals: -- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you know, what I mean. +- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean. - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. -- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. +- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). Other aspects were not so important to us: @@ -283,8 +283,8 @@ json j_uset(c_uset); // only one entry for "one" is used // maybe ["two", "three", "four", "one"] std::multiset<std::string> c_mset {"one", "two", "one", "four"}; -json j_mset(c_mset); // only one entry for "one" is used -// maybe ["one", "two", "four"] +json j_mset(c_mset); // both entries for "one" are used +// maybe ["one", "two", "one", "four"] std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"}; json j_umset(c_umset); // both entries for "one" are used @@ -416,7 +416,13 @@ The following compilers are currently used in continuous integration at [Travis] | GCC 4.9.3 | Ubuntu 14.04.4 LTS | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 | | GCC 5.3.0 | Ubuntu 14.04.4 LTS | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 | | GCC 6.1.1 | Ubuntu 14.04.4 LTS | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 | +| Clang 3.6.0 | Ubuntu 14.04.4 LTS | clang version 3.6.0 (tags/RELEASE_360/final) | +| Clang 3.6.1 | Ubuntu 14.04.4 LTS | clang version 3.6.1 (tags/RELEASE_361/final) | +| Clang 3.6.2 | Ubuntu 14.04.4 LTS | clang version 3.6.2 (tags/RELEASE_362/final) | +| Clang 3.7.0 | Ubuntu 14.04.4 LTS | clang version 3.7.0 (tags/RELEASE_370/final) | +| Clang 3.7.1 | Ubuntu 14.04.4 LTS | clang version 3.7.1 (tags/RELEASE_371/final) | | Clang 3.8.0 | Ubuntu 14.04.4 LTS | clang version 3.8.0 (tags/RELEASE_380/final) | +| Clang 3.8.1 | Ubuntu 14.04.4 LTS | clang version 3.8.1 (tags/RELEASE_381/final) | | Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) | | Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) | | Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) | @@ -424,7 +430,7 @@ The following compilers are currently used in continuous integration at [Travis] | Clang Xcode 7.1 | Darwin Kernel Version 14.5.0 (OSX 10.10.5) | Apple LLVM version 7.0.0 (clang-700.1.76) | | Clang Xcode 7.2 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.0.2 (clang-700.1.81) | | Clang Xcode 7.3 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.3.0 (clang-703.0.29) | -| Clang Xcode 8.0 | Darwin Kernel Version 15.5.0 (OSX 10.11.5) | Apple LLVM version 8.0.0 (clang-800.0.24.1) | +| Clang Xcode 8.0 | Darwin Kernel Version 15.6.0 (OSX 10.11.6) | Apple LLVM version 8.0.0 (clang-800.0.38) | | Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 | @@ -486,14 +492,26 @@ I deeply appreciate the help of the following people. - [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. +- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case. +- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). +- [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation. +- [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`. +- [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion. +- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable. +- [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file. Thanks a lot for helping out! ## Notes -- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726). - As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. +- The library supports **Unicode input** as follows: + - Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1). + - Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse errors. + - [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. + - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. ## Execute unit tests @@ -501,11 +519,20 @@ Thanks a lot for helping out! To compile and run the tests, you need to execute ```sh -$ make -$ ./json_unit "*" +$ make check =============================================================================== -All tests passed (8905012 assertions in 32 test cases) +All tests passed (8905491 assertions in 36 test cases) +``` + +Alternatively, you can use [CMake](https://cmake.org) and run + +```sh +$ mkdir build +$ cd build +$ cmake .. +$ make +$ ctest ``` 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 878fb899..a302bb02 100644 --- a/ext/json/json.hpp +++ b/ext/json/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.7 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. @@ -29,30 +29,32 @@ SOFTWARE. #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP -#include <algorithm> -#include <array> -#include <cassert> -#include <ciso646> -#include <cmath> -#include <cstddef> -#include <cstdint> -#include <cstdlib> -#include <functional> -#include <initializer_list> -#include <iomanip> -#include <iostream> -#include <iterator> -#include <limits> -#include <locale> -#include <map> -#include <memory> -#include <numeric> -#include <sstream> -#include <stdexcept> -#include <string> -#include <type_traits> -#include <utility> -#include <vector> +#include <algorithm> // all_of, for_each, transform +#include <array> // array +#include <cassert> // assert +#include <cctype> // isdigit +#include <ciso646> // and, not, or +#include <cmath> // isfinite, signbit +#include <cstddef> // nullptr_t, ptrdiff_t, size_t +#include <cstdint> // int64_t, uint64_t +#include <cstdlib> // strtod, strtof, strtold, strtoul +#include <cstring> // strlen +#include <functional> // function, hash, less +#include <initializer_list> // initializer_list +#include <iomanip> // setw +#include <iostream> // istream, ostream +#include <iterator> // advance, begin, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator +#include <limits> // numeric_limits +#include <locale> // locale, numpunct +#include <map> // map +#include <memory> // addressof, allocator, allocator_traits, unique_ptr +#include <numeric> // accumulate +#include <sstream> // stringstream +#include <stdexcept> // domain_error, invalid_argument, out_of_range +#include <string> // getline, stoi, string, to_string +#include <type_traits> // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference +#include <utility> // declval, forward, make_pair, move, pair, swap +#include <vector> // vector // exclude unsupported compilers #if defined(__clang__) @@ -73,6 +75,15 @@ SOFTWARE. #pragma GCC diagnostic ignored "-Wfloat-equal" #endif +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -96,16 +107,19 @@ 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 +@since version 1.0.0, overworked in version 2.0.6 */ template<typename T> struct has_mapped_type { private: - template<typename C> static char test(typename C::mapped_type*); - template<typename C> static char (&test(...))[2]; + template <typename U, typename = typename U::mapped_type> + static int detect(U&&); + + static void detect(...); public: - static constexpr bool value = sizeof(test<T>(0)) == 1; + static constexpr bool value = + std::is_integral<decltype(detect(std::declval<T>()))>::value; }; /*! @@ -950,7 +964,7 @@ class basic_json With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const string_t&, const parser_callback_t), + parser_callback_t) or @ref parse(const char*, 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 @@ -993,7 +1007,7 @@ class basic_json skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples + @ref parse(const char*, parser_callback_t) for examples @since version 1.0.0 */ @@ -1057,40 +1071,10 @@ class basic_json } /*! - @brief create a null object (implicitly) - - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @note The class invariant is satisfied, because it poses no requirements - for null values. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() = default; - - /*! - @brief create a null object (explicitly) + @brief create a null object - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a `nullptr` to a JSON value. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @@ -1099,15 +1083,12 @@ class basic_json @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - basic_json(std::nullptr_t) noexcept + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); @@ -1164,11 +1145,9 @@ class basic_json @since version 1.0.0 */ - template <class CompatibleObjectType, typename - std::enable_if< - std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and - std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type - = 0> + template<class CompatibleObjectType, typename std::enable_if< + std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and + std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1229,16 +1208,14 @@ class basic_json @since version 1.0.0 */ - template <class CompatibleArrayType, typename - std::enable_if< - not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and - not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and - not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and - not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and - not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and - not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and - std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type - = 0> + template<class CompatibleArrayType, typename std::enable_if< + not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and + not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and + not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and + not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and + not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and + not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and + std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1324,10 +1301,8 @@ class basic_json @since version 1.0.0 */ - template <class CompatibleStringType, typename - std::enable_if< - std::is_constructible<string_t, CompatibleStringType>::value, int>::type - = 0> + template<class CompatibleStringType, typename std::enable_if< + std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1377,12 +1352,9 @@ class basic_json @since version 1.0.0 */ - template<typename T, - typename std::enable_if< - not (std::is_same<T, int>::value) - and std::is_same<T, number_integer_t>::value - , int>::type - = 0> + template<typename T, typename std::enable_if< + not (std::is_same<T, int>::value) and + std::is_same<T, number_integer_t>::value, int>::type = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) { @@ -1446,13 +1418,11 @@ class basic_json @since version 1.0.0 */ - template<typename CompatibleNumberIntegerType, typename - std::enable_if< + template<typename CompatibleNumberIntegerType, typename std::enable_if< std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and std::numeric_limits<CompatibleNumberIntegerType>::is_integer and std::numeric_limits<CompatibleNumberIntegerType>::is_signed, - CompatibleNumberIntegerType>::type - = 0> + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) @@ -1477,12 +1447,9 @@ class basic_json @since version 2.0.0 */ - template<typename T, - typename std::enable_if< - not (std::is_same<T, int>::value) - and std::is_same<T, number_unsigned_t>::value - , int>::type - = 0> + template<typename T, typename std::enable_if< + not (std::is_same<T, int>::value) and + std::is_same<T, number_unsigned_t>::value, int>::type = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) { @@ -1509,13 +1476,11 @@ class basic_json @since version 2.0.0 */ - template <typename CompatibleNumberUnsignedType, typename - std::enable_if < - std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and - std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and - not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template<typename CompatibleNumberUnsignedType, typename std::enable_if < + std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and + std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and + not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, + CompatibleNumberUnsignedType>::type = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast<number_unsigned_t>(val)) @@ -1591,11 +1556,9 @@ class basic_json @since version 1.0.0 */ - template<typename CompatibleNumberFloatType, typename = typename - std::enable_if< + template<typename CompatibleNumberFloatType, typename = typename std::enable_if< std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and - std::is_floating_point<CompatibleNumberFloatType>::value>::type - > + std::is_floating_point<CompatibleNumberFloatType>::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1843,7 +1806,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. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -1861,12 +1825,9 @@ class basic_json @since version 1.0.0 */ - template <class InputIT, typename - std::enable_if< - std::is_same<InputIT, typename basic_json_t::iterator>::value or - std::is_same<InputIT, typename basic_json_t::const_iterator>::value - , int>::type - = 0> + template<class InputIT, typename std::enable_if< + std::is_same<InputIT, typename basic_json_t::iterator>::value or + std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -1970,12 +1931,21 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + @liveexample{The example below demonstrates constructing a JSON value from a `std::stringstream` with and without callback function.,basic_json__istream} - @since version 2.0.0 + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 */ + JSON_DEPRECATED explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); @@ -2231,7 +2201,8 @@ class basic_json { std::stringstream ss; // fix locale problems - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); + const static std::locale loc(std::locale(), new DecimalSeparator); + ss.imbue(loc); // 6, 15 or 16 digits of precision allows round-trip IEEE 754 // string->float->string, string->double->string or string->long @@ -2614,11 +2585,9 @@ class basic_json ////////////////// /// get an object (explicit) - template <class T, typename - std::enable_if< - std::is_convertible<typename object_t::key_type, typename T::key_type>::value and - std::is_convertible<basic_json_t, typename T::mapped_type>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_convertible<typename object_t::key_type, typename T::key_type>::value and + std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2645,14 +2614,12 @@ class basic_json } /// get an array (explicit) - template <class T, typename - std::enable_if< - std::is_convertible<basic_json_t, typename T::value_type>::value and - not std::is_same<basic_json_t, typename T::value_type>::value and - not std::is_arithmetic<T>::value and - not std::is_convertible<std::string, T>::value and - not has_mapped_type<T>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_convertible<basic_json_t, typename T::value_type>::value and + not std::is_same<basic_json_t, typename T::value_type>::value and + not std::is_arithmetic<T>::value and + not std::is_convertible<std::string, T>::value and + not has_mapped_type<T>::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2672,11 +2639,9 @@ class basic_json } /// get an array (explicit) - template <class T, typename - std::enable_if< - std::is_convertible<basic_json_t, T>::value and - not std::is_same<basic_json_t, T>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_convertible<basic_json_t, T>::value and + not std::is_same<basic_json_t, T>::value, int>::type = 0> std::vector<T> get_impl(std::vector<T>*) const { if (is_array()) @@ -2697,11 +2662,9 @@ class basic_json } /// get an array (explicit) - template <class T, typename - std::enable_if< - std::is_same<basic_json, typename T::value_type>::value and - not has_mapped_type<T>::value - , int>::type = 0> + template<class T, typename std::enable_if< + std::is_same<basic_json, typename T::value_type>::value and + not has_mapped_type<T>::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2728,10 +2691,8 @@ class basic_json } /// get a string (explicit) - template <typename T, typename - std::enable_if< - std::is_convertible<string_t, T>::value - , int>::type = 0> + template<typename T, typename std::enable_if< + std::is_convertible<string_t, T>::value, int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2745,10 +2706,8 @@ class basic_json } /// get a number (explicit) - template<typename T, typename - std::enable_if< - std::is_arithmetic<T>::value - , int>::type = 0> + template<typename T, typename std::enable_if< + std::is_arithmetic<T>::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2937,10 +2896,8 @@ class basic_json @since version 1.0.0 */ - template<typename ValueType, typename - std::enable_if< - not std::is_pointer<ValueType>::value - , int>::type = 0> + template<typename ValueType, typename std::enable_if< + not std::is_pointer<ValueType>::value, int>::type = 0> ValueType get() const { return get_impl(static_cast<ValueType*>(nullptr)); @@ -2973,10 +2930,8 @@ class basic_json @since version 1.0.0 */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2987,10 +2942,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3023,10 +2976,8 @@ class basic_json @since version 1.0.0 */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value, int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3052,11 +3003,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template<typename PointerType, typename - std::enable_if< - std::is_pointer<PointerType>::value - and std::is_const<typename std::remove_pointer<PointerType>::type>::value - , int>::type = 0> + template<typename PointerType, typename std::enable_if< + std::is_pointer<PointerType>::value and + std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3104,10 +3053,8 @@ class basic_json @since version 1.1.0 */ - template<typename ReferenceType, typename - std::enable_if< - std::is_reference<ReferenceType>::value - , int>::type = 0> + template<typename ReferenceType, typename std::enable_if< + std::is_reference<ReferenceType>::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3118,11 +3065,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template<typename ReferenceType, typename - std::enable_if< - std::is_reference<ReferenceType>::value - and std::is_const<typename std::remove_reference<ReferenceType>::type>::value - , int>::type = 0> + template<typename ReferenceType, typename std::enable_if< + std::is_reference<ReferenceType>::value and + std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3157,10 +3102,9 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer<ValueType>::value - and not std::is_same<ValueType, typename string_t::value_type>::value + template < typename ValueType, typename std::enable_if < + not std::is_pointer<ValueType>::value and + not std::is_same<ValueType, typename string_t::value_type>::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value #endif @@ -3509,6 +3453,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3614,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3744,10 +3694,8 @@ class basic_json @since version 1.0.0 */ - template <class ValueType, typename - std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value - , int>::type = 0> + template<class ValueType, typename std::enable_if< + std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3820,10 +3768,8 @@ class basic_json @since version 2.0.2 */ - template <class ValueType, typename - std::enable_if< - std::is_convertible<basic_json_t, ValueType>::value - , int>::type = 0> + template<class ValueType, typename std::enable_if< + std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3867,7 +3813,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3909,7 +3856,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -3951,7 +3899,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -3973,7 +3921,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -3982,13 +3930,11 @@ class basic_json @since version 1.0.0 */ - template <class InteratorType, typename - std::enable_if< - std::is_same<InteratorType, typename basic_json_t::iterator>::value or - std::is_same<InteratorType, typename basic_json_t::const_iterator>::value - , int>::type - = 0> - InteratorType erase(InteratorType pos) + template<class IteratorType, typename std::enable_if< + std::is_same<IteratorType, typename basic_json_t::iterator>::value or + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) @@ -3996,7 +3942,7 @@ class basic_json throw std::domain_error("iterator does not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4060,7 +4006,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -4083,7 +4029,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} - @sa @ref erase(InteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at @@ -4091,13 +4037,11 @@ class basic_json @since version 1.0.0 */ - template <class InteratorType, typename - std::enable_if< - std::is_same<InteratorType, typename basic_json_t::iterator>::value or - std::is_same<InteratorType, typename basic_json_t::const_iterator>::value - , int>::type - = 0> - InteratorType erase(InteratorType first, InteratorType last) + template<class IteratorType, typename std::enable_if< + std::is_same<IteratorType, typename basic_json_t::iterator>::value or + std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) @@ -4105,7 +4049,7 @@ class basic_json throw std::domain_error("iterators do not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4177,8 +4121,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__key_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @@ -4214,8 +4158,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -5923,9 +5867,45 @@ class basic_json /// @{ /*! - @brief deserialize from string + @brief deserialize from an array + + This function reads from an array of 1-byte values. - @param[in] s string to read a serialized JSON value from + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template<class T, std::size_t N> + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -5937,6 +5917,8 @@ class basic_json @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @@ -5944,12 +5926,16 @@ class basic_json @sa @ref parse(std::istream&, const parser_callback_t) for a version that reads from an input stream - @since version 1.0.0 + @since version 1.0.0 (originally for @ref string_t) */ - static basic_json parse(const string_t& s, + template<typename CharPT, typename std::enable_if< + std::is_pointer<CharPT>::value and + std::is_integral<typename std::remove_pointer<CharPT>::type>::value and + sizeof(typename std::remove_pointer<CharPT>::type) == 1, int>::type = 0> + static basic_json parse(const CharPT s, const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); + return parser(reinterpret_cast<const char*>(s), cb).parse(); } /*! @@ -5971,7 +5957,7 @@ 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&, const parser_callback_t) for a version + @sa @ref parse(const char*, const parser_callback_t) for a version that reads from a string @since version 1.0.0 @@ -5992,6 +5978,130 @@ class basic_json } /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template<class IteratorType, typename std::enable_if< + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0), + [&first](std::pair<bool, int> res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert(sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } + + return parser(first, last, cb).parse(); + } + + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template<class ContiguousContainer, typename std::enable_if< + not std::is_pointer<ContiguousContainer>::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + /*! @brief deserialize from stream Deserializes an input stream to a JSON value. @@ -6044,7 +6154,7 @@ class basic_json 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 + @return basically a string representation of a the @a m_type member @complexity Constant. @@ -6592,8 +6702,8 @@ class basic_json @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. + 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): @@ -7495,32 +7605,33 @@ class basic_json /// the char type to use in the lexer using lexer_char_t = unsigned char; - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) { - m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + len; } - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() { - assert(m_stream != nullptr); - 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; - m_limit = m_content + m_buffer.size(); - } + // fill buffer + fill_line_buffer(); - /// default constructor - lexer() = default; + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") + { + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } + } - // switch off unwanted functions + // switch off unwanted functions (due to pointer members) + lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -7673,7 +7784,7 @@ class basic_json infinite sequence of whitespace or byte-order-marks. This contradicts the assumption of finite input, q.e.d. */ - token_type scan() noexcept + token_type scan() { while (true) { @@ -7706,33 +7817,33 @@ class basic_json 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(5); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) { goto basic_json_parser_6; } - if (yych <= '\\') + if (yych <= '[') { if (yych <= '-') { @@ -7781,62 +7892,58 @@ class basic_json { goto basic_json_parser_17; } - if (yych == '[') + if (yych <= 'Z') { - goto basic_json_parser_19; + goto basic_json_parser_4; } - goto basic_json_parser_4; + goto basic_json_parser_19; } } } else { - if (yych <= 't') + if (yych <= 'n') { - if (yych <= 'f') + if (yych <= 'e') { - if (yych <= ']') + if (yych == ']') { goto basic_json_parser_21; } - if (yych <= 'e') - { - goto basic_json_parser_4; - } - goto basic_json_parser_23; + goto basic_json_parser_4; } else { - if (yych == 'n') + if (yych <= 'f') { - goto basic_json_parser_24; + goto basic_json_parser_23; } - if (yych <= 's') + if (yych <= 'm') { goto basic_json_parser_4; } - goto basic_json_parser_25; + goto basic_json_parser_24; } } else { - if (yych <= '|') + if (yych <= 'z') { - if (yych == '{') + if (yych == 't') { - goto basic_json_parser_26; + goto basic_json_parser_25; } goto basic_json_parser_4; } else { - if (yych <= '}') + if (yych <= '{') { - goto basic_json_parser_28; + goto basic_json_parser_26; } - if (yych == 0xEF) + if (yych == '}') { - goto basic_json_parser_30; + goto basic_json_parser_28; } goto basic_json_parser_4; } @@ -7859,7 +7966,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7876,7 +7983,19 @@ basic_json_parser_9: { goto basic_json_parser_5; } - goto basic_json_parser_32; + if (yych <= 0x7F) + { + goto basic_json_parser_31; + } + if (yych <= 0xC1) + { + goto basic_json_parser_5; + } + if (yych <= 0xF4) + { + goto basic_json_parser_31; + } + goto basic_json_parser_5; basic_json_parser_10: ++m_cursor; { @@ -7905,18 +8024,18 @@ basic_json_parser_13: { if (yych == '.') { - goto basic_json_parser_37; + goto basic_json_parser_43; } } else { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } } basic_json_parser_14: @@ -7929,7 +8048,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -7940,7 +8059,7 @@ basic_json_parser_15: { if (yych == '.') { - goto basic_json_parser_37; + goto basic_json_parser_43; } goto basic_json_parser_14; } @@ -7948,11 +8067,11 @@ basic_json_parser_15: { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } goto basic_json_parser_14; } @@ -7979,7 +8098,7 @@ basic_json_parser_23: yych = *(m_marker = ++m_cursor); if (yych == 'a') { - goto basic_json_parser_39; + goto basic_json_parser_45; } goto basic_json_parser_5; basic_json_parser_24: @@ -7987,7 +8106,7 @@ basic_json_parser_24: yych = *(m_marker = ++m_cursor); if (yych == 'u') { - goto basic_json_parser_40; + goto basic_json_parser_46; } goto basic_json_parser_5; basic_json_parser_25: @@ -7995,7 +8114,7 @@ basic_json_parser_25: yych = *(m_marker = ++m_cursor); if (yych == 'r') { - goto basic_json_parser_41; + goto basic_json_parser_47; } goto basic_json_parser_5; basic_json_parser_26: @@ -8011,35 +8130,71 @@ basic_json_parser_28: 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; -basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; -basic_json_parser_32: +basic_json_parser_31: if (yybm[0 + yych] & 128) { - goto basic_json_parser_31; + goto basic_json_parser_30; } - if (yych <= 0x1F) + if (yych <= 0xE0) { - goto basic_json_parser_33; + if (yych <= '\\') + { + if (yych <= 0x1F) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_33; + } + goto basic_json_parser_35; + } + else + { + if (yych <= 0xC1) + { + goto basic_json_parser_32; + } + if (yych <= 0xDF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_37; + } } - if (yych <= '"') + else { - goto basic_json_parser_34; + if (yych <= 0xEF) + { + if (yych == 0xED) + { + goto basic_json_parser_39; + } + goto basic_json_parser_38; + } + else + { + if (yych <= 0xF0) + { + goto basic_json_parser_40; + } + if (yych <= 0xF3) + { + goto basic_json_parser_41; + } + if (yych <= 0xF4) + { + goto basic_json_parser_42; + } + } } - goto basic_json_parser_36; -basic_json_parser_33: +basic_json_parser_32: m_cursor = m_marker; if (yyaccept == 0) { @@ -8049,17 +8204,17 @@ basic_json_parser_33: { goto basic_json_parser_14; } -basic_json_parser_34: +basic_json_parser_33: ++m_cursor; { last_token_type = token_type::value_string; break; } -basic_json_parser_36: +basic_json_parser_35: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'e') @@ -8068,13 +8223,13 @@ basic_json_parser_36: { if (yych == '"') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= '.') { - goto basic_json_parser_33; + goto basic_json_parser_32; } - goto basic_json_parser_31; + goto basic_json_parser_30; } else { @@ -8082,17 +8237,17 @@ basic_json_parser_36: { if (yych <= '[') { - goto basic_json_parser_33; + goto basic_json_parser_32; } - goto basic_json_parser_31; + goto basic_json_parser_30; } else { if (yych == 'b') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } } @@ -8102,13 +8257,13 @@ basic_json_parser_36: { if (yych <= 'f') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych == 'n') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { @@ -8116,130 +8271,235 @@ basic_json_parser_36: { if (yych <= 'r') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 't') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= 'u') { - goto basic_json_parser_43; + goto basic_json_parser_48; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } } +basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_43: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_44; + goto basic_json_parser_49; } - goto basic_json_parser_33; -basic_json_parser_38: + goto basic_json_parser_32; +basic_json_parser_44: yych = *++m_cursor; if (yych <= ',') { if (yych == '+') { - goto basic_json_parser_46; + goto basic_json_parser_51; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= '-') { - goto basic_json_parser_46; + goto basic_json_parser_51; } if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_52; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_39: +basic_json_parser_45: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_49; + goto basic_json_parser_54; } - goto basic_json_parser_33; -basic_json_parser_40: + goto basic_json_parser_32; +basic_json_parser_46: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_50; + goto basic_json_parser_55; } - goto basic_json_parser_33; -basic_json_parser_41: + goto basic_json_parser_32; +basic_json_parser_47: 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_56; } - goto basic_json_parser_33; -basic_json_parser_43: + goto basic_json_parser_32; +basic_json_parser_48: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_54; + goto basic_json_parser_57; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_54; + goto basic_json_parser_57; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_54; + goto basic_json_parser_57; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_44: +basic_json_parser_49: yyaccept = 1; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'D') @@ -8250,7 +8510,7 @@ basic_json_parser_44: } if (yych <= '9') { - goto basic_json_parser_44; + goto basic_json_parser_49; } goto basic_json_parser_14; } @@ -8258,29 +8518,29 @@ basic_json_parser_44: { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } goto basic_json_parser_14; } -basic_json_parser_46: +basic_json_parser_51: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych >= ':') { - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_47: +basic_json_parser_52: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '/') @@ -8289,164 +8549,159 @@ basic_json_parser_47: } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_52; } goto basic_json_parser_14; -basic_json_parser_49: +basic_json_parser_54: yych = *++m_cursor; if (yych == 's') { - goto basic_json_parser_55; + goto basic_json_parser_58; } - goto basic_json_parser_33; -basic_json_parser_50: + goto basic_json_parser_32; +basic_json_parser_55: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_56; + goto basic_json_parser_59; } - goto basic_json_parser_33; -basic_json_parser_51: + goto basic_json_parser_32; +basic_json_parser_56: yych = *++m_cursor; if (yych == 'e') { - goto basic_json_parser_58; - } - goto basic_json_parser_33; -basic_json_parser_52: - ++m_cursor; - { - continue; + goto basic_json_parser_61; } -basic_json_parser_54: + goto basic_json_parser_32; +basic_json_parser_57: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_60; + goto basic_json_parser_63; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_60; + goto basic_json_parser_63; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_60; + goto basic_json_parser_63; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_55: +basic_json_parser_58: yych = *++m_cursor; if (yych == 'e') { - goto basic_json_parser_61; + goto basic_json_parser_64; } - goto basic_json_parser_33; -basic_json_parser_56: + goto basic_json_parser_32; +basic_json_parser_59: ++m_cursor; { last_token_type = token_type::literal_null; break; } -basic_json_parser_58: +basic_json_parser_61: ++m_cursor; { last_token_type = token_type::literal_true; break; } -basic_json_parser_60: +basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_63; + goto basic_json_parser_66; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_63; + goto basic_json_parser_66; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_63; + goto basic_json_parser_66; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_61: +basic_json_parser_64: ++m_cursor; { last_token_type = token_type::literal_false; break; } -basic_json_parser_63: +basic_json_parser_66: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } @@ -8455,30 +8710,80 @@ basic_json_parser_63: return last_token_type; } - /// append data from the stream to the internal buffer - void yyfill() noexcept + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer(size_t n = 0) { - if (m_stream == nullptr or not * m_stream) - { - return; - } - + // number of processed characters (p) const auto offset_start = m_start - m_content; - const auto offset_marker = m_marker - m_start; + // offset for m_marker wrt. to m_start + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; + // number of unprocessed characters (u) const auto offset_cursor = m_cursor - m_start; - m_buffer.erase(0, static_cast<size_t>(offset_start)); - std::string line; - assert(m_stream != nullptr); - std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) + { + // skip this part if we are already using the line buffer + if (m_start != reinterpret_cast<const lexer_char_t*>(m_line_buffer.data())) + { + // copy unprocessed characters to line buffer + m_line_buffer.clear(); + for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor) + { + m_line_buffer.append(1, static_cast<const char>(*m_cursor)); + } + } + + // append n characters to make sure that there is sufficient + // space between m_cursor and m_limit + m_line_buffer.append(1, '\x00'); + m_line_buffer.append(n - 1, '\x01'); + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast<size_t>(offset_start)); + // read next line from input stream + std::string line; + std::getline(*m_stream, line, '\n'); + // add line with newline symbol to the line buffer + m_line_buffer += line + "\n"; + } - m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str()); + // set pointers + m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str()); assert(m_content != nullptr); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token @@ -8629,6 +8934,11 @@ basic_json_parser_63: // skip the next 10 characters (xxxx\uyyyy) i += 10; } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + throw std::invalid_argument("missing high surrogate"); + } else { // add unicode character(s) @@ -8811,6 +9121,13 @@ basic_json_parser_63: { // parse with strtod result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL); + + // replace infinity and NAN by null + if (not std::isfinite(result.m_value.number_float)) + { + type = value_t::null; + result.m_value = basic_json::json_value(); + } } // save the type @@ -8820,8 +9137,8 @@ basic_json_parser_63: private: /// optional input stream std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol @@ -8844,25 +9161,34 @@ basic_json_parser_63: class parser { public: - /// constructor for strings - parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(s) - { - // read first token - get_token(); - } + /// a parser reading from a string literal + parser(const char* buff, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), std::strlen(buff)) + {} /// a parser reading from an input stream - parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(&_is) - { - // read first token - get_token(); - } + parser(std::istream& is, const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(is) + {} + + /// a parser reading from an iterator range with contiguous storage + template<class IteratorType, typename std::enable_if< + std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)), + static_cast<size_t>(std::distance(first, last))) + {} /// public parser interface basic_json parse() { + // read first token + get_token(); + basic_json result = parse_internal(true); result.assert_invariant(); @@ -8883,7 +9209,8 @@ basic_json_parser_63: { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8961,7 +9288,8 @@ basic_json_parser_63: case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; @@ -9067,7 +9395,7 @@ basic_json_parser_63: } /// get next token from lexer - typename lexer::token_type get_token() noexcept + typename lexer::token_type get_token() { last_token = m_lexer.scan(); return last_token; @@ -9280,6 +9608,12 @@ basic_json_parser_63: /*! @brief return a reference to the pointed to value + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + @param[in] ptr a JSON value @return reference to the JSON value pointed to by the JSON pointer @@ -9294,6 +9628,29 @@ basic_json_parser_63: { for (const auto& reference_token : reference_tokens) { + // convert null values to arrays or objects before continuing + if (ptr->m_type == value_t::null) + { + // check if reference token is a number + const bool nums = std::all_of(reference_token.begin(), + reference_token.end(), + [](const char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object + // otherwise + if (nums or reference_token == "-") + { + *ptr = value_t::array; + } + else + { + *ptr = value_t::object; + } + } + switch (ptr->m_type) { case value_t::object: @@ -9475,7 +9832,7 @@ basic_json_parser_63: } /// split the string input to reference tokens - static std::vector<std::string> split(std::string reference_string) + static std::vector<std::string> split(const std::string& reference_string) { std::vector<std::string> result; @@ -9960,7 +10317,7 @@ basic_json_parser_63: json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { - basic_json& x = result.at(top_pointer); + result.at(top_pointer); } // get reference to parent of JSON pointer ptr @@ -10203,7 +10560,7 @@ basic_json_parser_63: */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); @@ -10365,7 +10722,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible<nlohmann::json>::value and @@ -10376,7 +10733,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash<nlohmann::json> { /*! @@ -10401,30 +10758,32 @@ can be used by adding `"_json"` to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object +@param[in] n the length of string @a s @return a JSON object @since version 1.0.0 */ -inline nlohmann::json operator "" _json(const char* s, std::size_t) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) { - return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s)); + return nlohmann::json::parse(s, s + n); } /*! @brief user-defined string literal for JSON pointer This operator implements a user-defined string literal for JSON Pointers. It -can be used by adding `"_json"` to a string literal and returns a JSON pointer +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer object if no parse error occurred. @param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s @return a JSON pointer object @since version 2.0.0 */ -inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { - return nlohmann::json::json_pointer(s); + return nlohmann::json::json_pointer(std::string(s, n)); } // restore GCC/clang diagnostic settings |