Merge branch 'develop' into coverity_scan
diff --git a/.gitignore b/.gitignore
index d5bd2f7..fd41a2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@
android
doc/xml
+
+benchmarks/files/numbers/*.json
diff --git a/.travis.yml b/.travis.yml
index b459f5d..ffe05ec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -168,3 +168,9 @@
- if [ `which valgrind` ]; then
valgrind --error-exitcode=1 --leak-check=full ./json_unit ;
fi
+ - if [ `which brew` ]; then
+ brew update ;
+ brew tap nlohmann/json ;
+ brew install nlohmann_json --HEAD ;
+ brew test nlohmann_json ;
+ fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0488b3..72802b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.0)
# define the project
-project(nlohmann_json VERSION 2.0.0 LANGUAGES CXX)
+project(nlohmann_json VERSION 2.0.2 LANGUAGES CXX)
enable_testing()
diff --git a/ChangeLog.md b/ChangeLog.md
index e3a7cc0..8062f3b 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,9 +1,11 @@
# Change Log
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
-## [Unreleased](https://github.com/nlohmann/json/tree/HEAD)
+## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-30)
+[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...v2.0.2)
-[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...HEAD)
+- value\(\) does not work with \_json\_pointer types [\#283](https://github.com/nlohmann/json/issues/283)
+- Easy serialization of classes [\#280](https://github.com/nlohmann/json/issues/280)
- Build error for std::int64 [\#282](https://github.com/nlohmann/json/issues/282)
diff --git a/Makefile b/Makefile
index 56e46d1..15d00f6 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,7 @@
# clean up
clean:
rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM
+ rm -fr benchmarks/files/numbers/*.json
$(MAKE) clean -Cdoc
@@ -85,6 +86,7 @@
# benchmarks
json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/cxxopts.hpp src/json.hpp
+ cd benchmarks/files/numbers ; python generate.py
$(CXX) -std=c++11 $(CXXFLAGS) -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@
./json_benchmarks
@@ -93,7 +95,9 @@
# changelog
##########################################################################
+NEXT_VERSION ?= "unreleased"
+
ChangeLog.md:
- github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s
+ github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION)
gsed -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md
gsed -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md
diff --git a/README.md b/README.md
index 24f6d7a..c0bb61b 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
@@ -24,7 +24,7 @@
- **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.
@@ -505,7 +505,7 @@
$ ./json_unit "*"
===============================================================================
-All tests passed (5568718 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/benchmarks/benchmarks.cpp b/benchmarks/benchmarks.cpp
index 1f5eb5e..ec6b462 100644
--- a/benchmarks/benchmarks.cpp
+++ b/benchmarks/benchmarks.cpp
@@ -44,6 +44,36 @@
}
})
+BENCHMARK("parse numbers/floats.json", [](benchpress::context* ctx)
+{
+ for (size_t i = 0; i < ctx->num_iterations(); ++i)
+ {
+ std::ifstream input_file("benchmarks/files/numbers/floats.json");
+ nlohmann::json j;
+ j << input_file;
+ }
+})
+
+BENCHMARK("parse numbers/signed_ints.json", [](benchpress::context* ctx)
+{
+ for (size_t i = 0; i < ctx->num_iterations(); ++i)
+ {
+ std::ifstream input_file("benchmarks/files/numbers/signed_ints.json");
+ nlohmann::json j;
+ j << input_file;
+ }
+})
+
+BENCHMARK("parse numbers/unsigned_ints.json", [](benchpress::context* ctx)
+{
+ for (size_t i = 0; i < ctx->num_iterations(); ++i)
+ {
+ std::ifstream input_file("benchmarks/files/numbers/unsigned_ints.json");
+ nlohmann::json j;
+ j << input_file;
+ }
+})
+
BENCHMARK("dump jeopardy.json", [](benchpress::context* ctx)
{
std::ifstream input_file("benchmarks/files/jeopardy/jeopardy.json");
diff --git a/benchmarks/files/numbers/generate.py b/benchmarks/files/numbers/generate.py
new file mode 100755
index 0000000..714ee3a
--- /dev/null
+++ b/benchmarks/files/numbers/generate.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+import json
+import random
+import sys
+
+random.seed(0)
+
+# floats
+result_floats = []
+for x in range(0, 1000000):
+ result_floats.append(random.uniform(-100000000.0, 100000000.0))
+json.dump(result_floats, open("floats.json", "w"), indent=2)
+
+# unsigned integers
+result_uints = []
+for x in range(0, 1000000):
+ result_uints.append(random.randint(0, 18446744073709551615))
+json.dump(result_uints, open("unsigned_ints.json", "w"), indent=2)
+
+# signed integers
+result_sints = []
+for x in range(0, 1000000):
+ result_sints.append(random.randint(-9223372036854775808, 9223372036854775807))
+json.dump(result_sints, open("signed_ints.json", "w"), indent=2)
diff --git a/doc/Doxyfile b/doc/Doxyfile
index e74a8a8..e0055d6 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "JSON for Modern C++"
-PROJECT_NUMBER = 2.0.0
+PROJECT_NUMBER = 2.0.2
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY = .
diff --git a/doc/examples/README.link b/doc/examples/README.link
index ed559e1..0e87a79 100644
--- a/doc/examples/README.link
+++ b/doc/examples/README.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/k5KRwVJ42VggxxzX"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__value_ptr.cpp b/doc/examples/basic_json__value_ptr.cpp
new file mode 100644
index 0000000..f45fb8b
--- /dev/null
+++ b/doc/examples/basic_json__value_ptr.cpp
@@ -0,0 +1,29 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+ // create a JSON object with different entry types
+ json j =
+ {
+ {"integer", 1},
+ {"floating", 42.23},
+ {"string", "hello world"},
+ {"boolean", true},
+ {"object", {{"key1", 1}, {"key2", 2}}},
+ {"array", {1, 2, 3}}
+ };
+
+ // access existing values
+ int v_integer = j.value("/integer"_json_pointer, 0);
+ double v_floating = j.value("/floating"_json_pointer, 47.11);
+
+ // access nonexisting values and rely on default value
+ std::string v_string = j.value("/nonexisting"_json_pointer, "oops");
+ bool v_boolean = j.value("/nonexisting"_json_pointer, false);
+
+ // output values
+ std::cout << std::boolalpha << v_integer << " " << v_floating
+ << " " << v_string << " " << v_boolean << "\n";
+}
diff --git a/doc/examples/basic_json__value_ptr.link b/doc/examples/basic_json__value_ptr.link
new file mode 100644
index 0000000..2f8fc83
--- /dev/null
+++ b/doc/examples/basic_json__value_ptr.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/K4L4D6nibuGXbjfd"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__value_ptr.output b/doc/examples/basic_json__value_ptr.output
new file mode 100644
index 0000000..dfc40e5
--- /dev/null
+++ b/doc/examples/basic_json__value_ptr.output
@@ -0,0 +1 @@
+1 42.23 oops false
diff --git a/doc/images/callback_events.png b/doc/images/callback_events.png
new file mode 100644
index 0000000..09aa2b3
--- /dev/null
+++ b/doc/images/callback_events.png
Binary files differ
diff --git a/doc/index.md b/doc/index.md
index 5373d4f..cf32a7a 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -268,4 +268,4 @@
@author [Niels Lohmann](http://nlohmann.me)
@see https://github.com/nlohmann/json to download the source code
-@version 2.0.0
+@version 2.0.2
diff --git a/doc/json.gif b/doc/json.gif
index 7edafe0..46f005d 100644
--- a/doc/json.gif
+++ b/doc/json.gif
Binary files differ
diff --git a/src/json.hpp b/src/json.hpp
index 2915532..878fb89 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.0.1
+| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -32,7 +32,6 @@
#include <algorithm>
#include <array>
#include <cassert>
-#include <cerrno>
#include <ciso646>
#include <cmath>
#include <cstddef>
@@ -44,6 +43,7 @@
#include <iostream>
#include <iterator>
#include <limits>
+#include <locale>
#include <map>
#include <memory>
#include <numeric>
@@ -190,6 +190,13 @@
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
@@ -384,7 +391,7 @@
@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
@@ -621,15 +628,14 @@
> 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
@@ -717,7 +723,19 @@
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
*/
@@ -728,7 +746,7 @@
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
@@ -748,6 +766,7 @@
};
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();
}
@@ -758,7 +777,24 @@
/*!
@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
*/
@@ -862,6 +898,21 @@
}
};
+ /*!
+ @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:
//////////////////////////
@@ -874,6 +925,8 @@
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
@@ -896,12 +949,13 @@
@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
@@ -916,6 +970,8 @@
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:
@@ -941,7 +997,9 @@
@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)>;
//////////////////
@@ -994,7 +1052,9 @@
*/
basic_json(const value_t value_type)
: m_type(value_type), m_value(value_type)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a null object (implicitly)
@@ -1002,6 +1062,9 @@
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
@@ -1046,7 +1109,9 @@
*/
basic_json(std::nullptr_t) noexcept
: basic_json(value_t::null)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an object (explicit)
@@ -1069,7 +1134,9 @@
*/
basic_json(const object_t& val)
: m_type(value_t::object), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an object (implicit)
@@ -1108,6 +1175,7 @@
using std::begin;
using std::end;
m_value.object = create<object_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
@@ -1131,7 +1199,9 @@
*/
basic_json(const array_t& val)
: m_type(value_t::array), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an array (implicit)
@@ -1175,6 +1245,7 @@
using std::begin;
using std::end;
m_value.array = create<array_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
@@ -1200,7 +1271,9 @@
*/
basic_json(const string_t& val)
: m_type(value_t::string), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a string (explicit)
@@ -1224,7 +1297,9 @@
*/
basic_json(const typename string_t::value_type* val)
: basic_json(string_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a string (implicit)
@@ -1255,7 +1330,9 @@
= 0>
basic_json(const CompatibleStringType& val)
: basic_json(string_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a boolean (explicit)
@@ -1273,7 +1350,9 @@
*/
basic_json(boolean_t val) noexcept
: m_type(value_t::boolean), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an integer number (explicit)
@@ -1306,7 +1385,9 @@
= 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)
@@ -1336,7 +1417,9 @@
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)
@@ -1373,15 +1456,17 @@
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
@@ -1400,7 +1485,9 @@
= 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)
@@ -1432,7 +1519,9 @@
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)
@@ -1445,8 +1534,8 @@
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.
@@ -1467,6 +1556,8 @@
m_type = value_t::null;
m_value = json_value();
}
+
+ assert_invariant();
}
/*!
@@ -1507,7 +1598,9 @@
>
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
@@ -1519,21 +1612,21 @@
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:
@@ -1612,8 +1705,6 @@
m_type = value_t::object;
m_value = value_t::object;
- assert(m_value.object != nullptr);
-
std::for_each(init.begin(), init.end(), [this](const basic_json & element)
{
m_value.object->emplace(*(element[0].m_value.string), element[1]);
@@ -1625,6 +1716,8 @@
m_type = value_t::array;
m_value.array = create<array_t>(init);
}
+
+ assert_invariant();
}
/*!
@@ -1729,6 +1822,7 @@
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
+ assert_invariant();
}
/*!
@@ -1749,6 +1843,8 @@
@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,
@@ -1771,14 +1867,20 @@
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)
{
@@ -1805,35 +1907,30 @@
{
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;
}
@@ -1852,10 +1949,11 @@
default:
{
- assert(first.m_object != nullptr);
throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
}
}
+
+ assert_invariant();
}
/*!
@@ -1878,9 +1976,10 @@
@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();
}
///////////////////////////////////////
@@ -1912,25 +2011,25 @@
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;
}
@@ -1964,6 +2063,8 @@
break;
}
}
+
+ assert_invariant();
}
/*!
@@ -1988,9 +2089,14 @@
: 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();
}
/*!
@@ -2023,9 +2129,14 @@
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;
}
@@ -2046,6 +2157,8 @@
*/
~basic_json()
{
+ assert_invariant();
+
switch (m_type)
{
case value_t::object:
@@ -2510,7 +2623,6 @@
{
if (is_object())
{
- assert(m_value.object != nullptr);
return T(m_value.object->begin(), m_value.object->end());
}
else
@@ -2524,7 +2636,6 @@
{
if (is_object())
{
- assert(m_value.object != nullptr);
return *(m_value.object);
}
else
@@ -2547,7 +2658,6 @@
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)
{
@@ -2572,7 +2682,6 @@
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)
@@ -2597,7 +2706,6 @@
{
if (is_array())
{
- assert(m_value.array != nullptr);
return T(m_value.array->begin(), m_value.array->end());
}
else
@@ -2611,7 +2719,6 @@
{
if (is_array())
{
- assert(m_value.array != nullptr);
return *(m_value.array);
}
else
@@ -2629,7 +2736,6 @@
{
if (is_string())
{
- assert(m_value.string != nullptr);
return *m_value.string;
}
else
@@ -2775,8 +2881,10 @@
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)
@@ -3103,7 +3211,6 @@
{
try
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
@@ -3147,7 +3254,6 @@
{
try
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
@@ -3195,7 +3301,6 @@
{
try
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
@@ -3243,7 +3348,6 @@
{
try
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
@@ -3290,13 +3394,13 @@
{
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 if given idx is outside range
- assert(m_value.array != nullptr);
if (idx >= m_value.array->size())
{
m_value.array->insert(m_value.array->end(),
@@ -3336,7 +3440,6 @@
// const operator[] only works for arrays
if (is_array())
{
- assert(m_value.array != nullptr);
return m_value.array->operator[](idx);
}
else
@@ -3379,12 +3482,12 @@
{
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
@@ -3425,7 +3528,6 @@
// 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;
}
@@ -3538,12 +3640,12 @@
{
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
@@ -3585,7 +3687,6 @@
// 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;
}
@@ -3598,8 +3699,8 @@
/*!
@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}
@@ -3671,7 +3772,7 @@
/*!
@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
{
@@ -3679,6 +3780,81 @@
}
/*!
+ @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
@@ -3837,24 +4013,25 @@
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;
}
@@ -3945,17 +4122,19 @@
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;
@@ -3963,7 +4142,6 @@
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;
@@ -4012,7 +4190,6 @@
// this erase only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
return m_value.object->erase(key);
}
else
@@ -4055,7 +4232,6 @@
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
@@ -4098,7 +4274,6 @@
if (is_object())
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
@@ -4115,7 +4290,6 @@
if (is_object())
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
@@ -4143,7 +4317,6 @@
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;
}
@@ -4485,6 +4658,10 @@
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.
@@ -4514,13 +4691,13 @@
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();
}
@@ -4548,6 +4725,10 @@
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.
@@ -4578,13 +4759,13 @@
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();
}
@@ -4638,13 +4819,13 @@
{
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();
}
@@ -4721,21 +4902,18 @@
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;
}
@@ -4780,10 +4958,10 @@
{
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;
@@ -4816,10 +4994,10 @@
{
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);
}
@@ -4866,10 +5044,10 @@
{
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);
}
@@ -4966,7 +5144,6 @@
// 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;
}
@@ -5022,7 +5199,6 @@
// 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;
}
@@ -5089,7 +5265,6 @@
// 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,
@@ -5137,7 +5312,6 @@
// 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;
}
@@ -5168,6 +5342,7 @@
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
+ assert_invariant();
}
/*!
@@ -5195,7 +5370,6 @@
// swap only works for arrays
if (is_array())
{
- assert(m_value.array != nullptr);
std::swap(*(m_value.array), other);
}
else
@@ -5229,7 +5403,6 @@
// swap only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
std::swap(*(m_value.object), other);
}
else
@@ -5263,7 +5436,6 @@
// swap only works for strings
if (is_string())
{
- assert(m_value.string != nullptr);
std::swap(*(m_value.string), other);
}
else
@@ -5350,14 +5522,10 @@
{
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:
@@ -5366,8 +5534,6 @@
}
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:
@@ -5540,14 +5706,10 @@
{
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:
@@ -5556,8 +5718,6 @@
}
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:
@@ -5732,14 +5892,14 @@
// 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_preicison = o.precision(std::numeric_limits<double>::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_preicison);
+ o.precision(old_precision);
return o;
}
@@ -5781,12 +5941,13 @@
@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();
}
@@ -5810,20 +5971,22 @@
@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();
}
@@ -5846,8 +6009,8 @@
@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
*/
@@ -5875,7 +6038,18 @@
// convenience functions //
///////////////////////////
- /// return the type as string
+ /*!
+ @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)
@@ -6089,8 +6263,6 @@
{
case value_t::object:
{
- assert(m_value.object != nullptr);
-
if (m_value.object->empty())
{
o << "{}";
@@ -6131,8 +6303,6 @@
case value_t::array:
{
- assert(m_value.array != nullptr);
-
if (m_value.array->empty())
{
o << "[]";
@@ -6171,7 +6341,6 @@
case value_t::string:
{
- assert(m_value.string != nullptr);
o << string_t("\"") << escape_string(*m_value.string) << "\"";
return;
}
@@ -6420,6 +6589,12 @@
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
@@ -6447,7 +6622,12 @@
/// 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)
{
@@ -6475,40 +6655,53 @@
}
}
- /// 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
@@ -6522,7 +6715,10 @@
}
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);
@@ -6531,14 +6727,12 @@
{
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;
}
@@ -6558,7 +6752,10 @@
}
}
- /// 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);
@@ -6567,14 +6764,12 @@
{
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;
}
@@ -6588,7 +6783,10 @@
}
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);
@@ -6597,14 +6795,12 @@
{
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;
}
@@ -6628,7 +6824,10 @@
}
}
- /// dereference the iterator
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
pointer operator->() const
{
assert(m_object != nullptr);
@@ -6637,14 +6836,12 @@
{
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;
}
@@ -6663,7 +6860,10 @@
}
}
- /// post-increment (it++)
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
const_iterator operator++(int)
{
auto result = *this;
@@ -6671,7 +6871,10 @@
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);
@@ -6700,7 +6903,10 @@
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;
@@ -6708,7 +6914,10 @@
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);
@@ -6737,7 +6946,10 @@
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
@@ -6767,13 +6979,19 @@
}
}
- /// 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
@@ -6803,25 +7021,37 @@
}
}
- /// 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);
@@ -6849,13 +7079,19 @@
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;
@@ -6863,7 +7099,10 @@
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;
@@ -6871,7 +7110,10 @@
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);
@@ -6895,7 +7137,10 @@
}
}
- /// 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);
@@ -6931,7 +7176,10 @@
}
}
- /// 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);
@@ -6946,7 +7194,10 @@
}
}
- /// 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*();
@@ -7248,7 +7499,7 @@
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();
@@ -7259,7 +7510,7 @@
: 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;
@@ -7274,24 +7525,32 @@
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
@@ -7353,7 +7612,7 @@
}
/// 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)
{
@@ -7402,6 +7661,17 @@
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
{
@@ -7422,8 +7692,8 @@
{
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,
+ 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,
@@ -7602,7 +7872,7 @@
basic_json_parser_9:
yyaccept = 0;
yych = *(m_marker = ++m_cursor);
- if (yych <= 0x0F)
+ if (yych <= 0x1F)
{
goto basic_json_parser_5;
}
@@ -7760,7 +8030,7 @@
{
goto basic_json_parser_31;
}
- if (yych <= 0x0F)
+ if (yych <= 0x1F)
{
goto basic_json_parser_33;
}
@@ -8237,12 +8507,49 @@
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));
@@ -8358,11 +8665,6 @@
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
{
@@ -8543,7 +8845,7 @@
{
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
@@ -8551,7 +8853,7 @@
}
/// 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
@@ -8562,6 +8864,7 @@
basic_json parse()
{
basic_json result = parse_internal(true);
+ result.assert_invariant();
expect(lexer::token_type::end_of_input);
@@ -8584,7 +8887,7 @@
{
// 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
@@ -8662,7 +8965,7 @@
{
// 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
@@ -8799,7 +9102,7 @@
/// 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
@@ -8915,6 +9218,8 @@
/*!
@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
{
@@ -9236,7 +9541,7 @@
@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.
@@ -10092,7 +10397,7 @@
@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
@@ -10108,6 +10413,13 @@
/*!
@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)
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 8eeedef..32482ea 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.0.1
+| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -32,7 +32,6 @@
#include <algorithm>
#include <array>
#include <cassert>
-#include <cerrno>
#include <ciso646>
#include <cmath>
#include <cstddef>
@@ -44,6 +43,7 @@
#include <iostream>
#include <iterator>
#include <limits>
+#include <locale>
#include <map>
#include <memory>
#include <numeric>
@@ -190,6 +190,13 @@
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
@@ -384,7 +391,7 @@
@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
@@ -621,15 +628,14 @@
> 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
@@ -717,7 +723,19 @@
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
*/
@@ -728,7 +746,7 @@
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
@@ -748,6 +766,7 @@
};
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();
}
@@ -758,7 +777,24 @@
/*!
@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
*/
@@ -862,6 +898,21 @@
}
};
+ /*!
+ @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:
//////////////////////////
@@ -874,6 +925,8 @@
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
@@ -896,12 +949,13 @@
@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
@@ -916,6 +970,8 @@
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:
@@ -941,7 +997,9 @@
@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)>;
//////////////////
@@ -994,7 +1052,9 @@
*/
basic_json(const value_t value_type)
: m_type(value_type), m_value(value_type)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a null object (implicitly)
@@ -1002,6 +1062,9 @@
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
@@ -1046,7 +1109,9 @@
*/
basic_json(std::nullptr_t) noexcept
: basic_json(value_t::null)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an object (explicit)
@@ -1069,7 +1134,9 @@
*/
basic_json(const object_t& val)
: m_type(value_t::object), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an object (implicit)
@@ -1108,6 +1175,7 @@
using std::begin;
using std::end;
m_value.object = create<object_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
@@ -1131,7 +1199,9 @@
*/
basic_json(const array_t& val)
: m_type(value_t::array), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an array (implicit)
@@ -1175,6 +1245,7 @@
using std::begin;
using std::end;
m_value.array = create<array_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
@@ -1200,7 +1271,9 @@
*/
basic_json(const string_t& val)
: m_type(value_t::string), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a string (explicit)
@@ -1224,7 +1297,9 @@
*/
basic_json(const typename string_t::value_type* val)
: basic_json(string_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a string (implicit)
@@ -1255,7 +1330,9 @@
= 0>
basic_json(const CompatibleStringType& val)
: basic_json(string_t(val))
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create a boolean (explicit)
@@ -1273,7 +1350,9 @@
*/
basic_json(boolean_t val) noexcept
: m_type(value_t::boolean), m_value(val)
- {}
+ {
+ assert_invariant();
+ }
/*!
@brief create an integer number (explicit)
@@ -1306,7 +1385,9 @@
= 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)
@@ -1336,7 +1417,9 @@
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)
@@ -1373,15 +1456,17 @@
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
@@ -1400,7 +1485,9 @@
= 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)
@@ -1432,7 +1519,9 @@
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)
@@ -1445,8 +1534,8 @@
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.
@@ -1467,6 +1556,8 @@
m_type = value_t::null;
m_value = json_value();
}
+
+ assert_invariant();
}
/*!
@@ -1507,7 +1598,9 @@
>
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
@@ -1519,21 +1612,21 @@
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:
@@ -1612,8 +1705,6 @@
m_type = value_t::object;
m_value = value_t::object;
- assert(m_value.object != nullptr);
-
std::for_each(init.begin(), init.end(), [this](const basic_json & element)
{
m_value.object->emplace(*(element[0].m_value.string), element[1]);
@@ -1625,6 +1716,8 @@
m_type = value_t::array;
m_value.array = create<array_t>(init);
}
+
+ assert_invariant();
}
/*!
@@ -1729,6 +1822,7 @@
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
+ assert_invariant();
}
/*!
@@ -1749,6 +1843,8 @@
@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,
@@ -1771,14 +1867,20 @@
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)
{
@@ -1805,35 +1907,30 @@
{
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;
}
@@ -1852,10 +1949,11 @@
default:
{
- assert(first.m_object != nullptr);
throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
}
}
+
+ assert_invariant();
}
/*!
@@ -1878,9 +1976,10 @@
@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();
}
///////////////////////////////////////
@@ -1912,25 +2011,25 @@
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;
}
@@ -1964,6 +2063,8 @@
break;
}
}
+
+ assert_invariant();
}
/*!
@@ -1988,9 +2089,14 @@
: 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();
}
/*!
@@ -2023,9 +2129,14 @@
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;
}
@@ -2046,6 +2157,8 @@
*/
~basic_json()
{
+ assert_invariant();
+
switch (m_type)
{
case value_t::object:
@@ -2510,7 +2623,6 @@
{
if (is_object())
{
- assert(m_value.object != nullptr);
return T(m_value.object->begin(), m_value.object->end());
}
else
@@ -2524,7 +2636,6 @@
{
if (is_object())
{
- assert(m_value.object != nullptr);
return *(m_value.object);
}
else
@@ -2547,7 +2658,6 @@
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)
{
@@ -2572,7 +2682,6 @@
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)
@@ -2597,7 +2706,6 @@
{
if (is_array())
{
- assert(m_value.array != nullptr);
return T(m_value.array->begin(), m_value.array->end());
}
else
@@ -2611,7 +2719,6 @@
{
if (is_array())
{
- assert(m_value.array != nullptr);
return *(m_value.array);
}
else
@@ -2629,7 +2736,6 @@
{
if (is_string())
{
- assert(m_value.string != nullptr);
return *m_value.string;
}
else
@@ -2775,8 +2881,10 @@
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)
@@ -3103,7 +3211,6 @@
{
try
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
@@ -3147,7 +3254,6 @@
{
try
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
@@ -3195,7 +3301,6 @@
{
try
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
@@ -3243,7 +3348,6 @@
{
try
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
@@ -3290,13 +3394,13 @@
{
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 if given idx is outside range
- assert(m_value.array != nullptr);
if (idx >= m_value.array->size())
{
m_value.array->insert(m_value.array->end(),
@@ -3336,7 +3440,6 @@
// const operator[] only works for arrays
if (is_array())
{
- assert(m_value.array != nullptr);
return m_value.array->operator[](idx);
}
else
@@ -3379,12 +3482,12 @@
{
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
@@ -3425,7 +3528,6 @@
// 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;
}
@@ -3538,12 +3640,12 @@
{
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
@@ -3585,7 +3687,6 @@
// 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;
}
@@ -3598,8 +3699,8 @@
/*!
@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}
@@ -3671,7 +3772,7 @@
/*!
@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
{
@@ -3679,6 +3780,81 @@
}
/*!
+ @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
@@ -3837,24 +4013,25 @@
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;
}
@@ -3945,17 +4122,19 @@
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;
@@ -3963,7 +4142,6 @@
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;
@@ -4012,7 +4190,6 @@
// this erase only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
return m_value.object->erase(key);
}
else
@@ -4055,7 +4232,6 @@
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
@@ -4098,7 +4274,6 @@
if (is_object())
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
@@ -4115,7 +4290,6 @@
if (is_object())
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
@@ -4143,7 +4317,6 @@
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;
}
@@ -4485,6 +4658,10 @@
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.
@@ -4514,13 +4691,13 @@
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();
}
@@ -4548,6 +4725,10 @@
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.
@@ -4578,13 +4759,13 @@
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();
}
@@ -4638,13 +4819,13 @@
{
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();
}
@@ -4721,21 +4902,18 @@
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;
}
@@ -4780,10 +4958,10 @@
{
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;
@@ -4816,10 +4994,10 @@
{
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);
}
@@ -4866,10 +5044,10 @@
{
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);
}
@@ -4966,7 +5144,6 @@
// 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;
}
@@ -5022,7 +5199,6 @@
// 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;
}
@@ -5089,7 +5265,6 @@
// 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,
@@ -5137,7 +5312,6 @@
// 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;
}
@@ -5168,6 +5342,7 @@
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
+ assert_invariant();
}
/*!
@@ -5195,7 +5370,6 @@
// swap only works for arrays
if (is_array())
{
- assert(m_value.array != nullptr);
std::swap(*(m_value.array), other);
}
else
@@ -5229,7 +5403,6 @@
// swap only works for objects
if (is_object())
{
- assert(m_value.object != nullptr);
std::swap(*(m_value.object), other);
}
else
@@ -5263,7 +5436,6 @@
// swap only works for strings
if (is_string())
{
- assert(m_value.string != nullptr);
std::swap(*(m_value.string), other);
}
else
@@ -5350,14 +5522,10 @@
{
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:
@@ -5366,8 +5534,6 @@
}
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:
@@ -5540,14 +5706,10 @@
{
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:
@@ -5556,8 +5718,6 @@
}
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:
@@ -5732,14 +5892,14 @@
// 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_preicison = o.precision(std::numeric_limits<double>::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_preicison);
+ o.precision(old_precision);
return o;
}
@@ -5781,12 +5941,13 @@
@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();
}
@@ -5810,20 +5971,22 @@
@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();
}
@@ -5846,8 +6009,8 @@
@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
*/
@@ -5875,7 +6038,18 @@
// convenience functions //
///////////////////////////
- /// return the type as string
+ /*!
+ @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)
@@ -6089,8 +6263,6 @@
{
case value_t::object:
{
- assert(m_value.object != nullptr);
-
if (m_value.object->empty())
{
o << "{}";
@@ -6131,8 +6303,6 @@
case value_t::array:
{
- assert(m_value.array != nullptr);
-
if (m_value.array->empty())
{
o << "[]";
@@ -6171,7 +6341,6 @@
case value_t::string:
{
- assert(m_value.string != nullptr);
o << string_t("\"") << escape_string(*m_value.string) << "\"";
return;
}
@@ -6420,6 +6589,12 @@
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
@@ -6447,7 +6622,12 @@
/// 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)
{
@@ -6475,40 +6655,53 @@
}
}
- /// 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
@@ -6522,7 +6715,10 @@
}
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);
@@ -6531,14 +6727,12 @@
{
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;
}
@@ -6558,7 +6752,10 @@
}
}
- /// 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);
@@ -6567,14 +6764,12 @@
{
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;
}
@@ -6588,7 +6783,10 @@
}
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);
@@ -6597,14 +6795,12 @@
{
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;
}
@@ -6628,7 +6824,10 @@
}
}
- /// dereference the iterator
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
pointer operator->() const
{
assert(m_object != nullptr);
@@ -6637,14 +6836,12 @@
{
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;
}
@@ -6663,7 +6860,10 @@
}
}
- /// post-increment (it++)
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
const_iterator operator++(int)
{
auto result = *this;
@@ -6671,7 +6871,10 @@
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);
@@ -6700,7 +6903,10 @@
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;
@@ -6708,7 +6914,10 @@
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);
@@ -6737,7 +6946,10 @@
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
@@ -6767,13 +6979,19 @@
}
}
- /// 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
@@ -6803,25 +7021,37 @@
}
}
- /// 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);
@@ -6849,13 +7079,19 @@
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;
@@ -6863,7 +7099,10 @@
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;
@@ -6871,7 +7110,10 @@
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);
@@ -6895,7 +7137,10 @@
}
}
- /// 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);
@@ -6931,7 +7176,10 @@
}
}
- /// 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);
@@ -6946,7 +7194,10 @@
}
}
- /// 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*();
@@ -7248,7 +7499,7 @@
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();
@@ -7259,7 +7510,7 @@
: 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;
@@ -7274,24 +7525,32 @@
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
@@ -7353,7 +7612,7 @@
}
/// 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)
{
@@ -7402,6 +7661,17 @@
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
{
@@ -7447,32 +7717,32 @@
"false" { last_token_type = token_type::literal_false; break; }
// number
- decimal_point = [.];
+ decimal_point = ".";
digit = [0-9];
digit_1_9 = [1-9];
- e = [eE];
- minus = [-];
- plus = [+];
- zero = [0];
- exp = e (minus|plus)? digit+;
+ e = "e" | "E";
+ minus = "-";
+ plus = "+";
+ zero = "0";
+ exp = e (minus | plus)? digit+;
frac = decimal_point digit+;
- int = (zero|digit_1_9 digit*);
+ int = (zero | digit_1_9 digit*);
number = minus? int frac? exp?;
number { last_token_type = token_type::value_number; break; }
// string
- quotation_mark = ["];
- escape = [\\];
- unescaped = [^"\\\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F];
- single_escaped = ["\\/bfnrt];
- unicode_escaped = [u][0-9a-fA-F]{4};
+ quotation_mark = "\"";
+ escape = "\\";
+ unescaped = [^"\\\x00-\x1f];
+ single_escaped = "\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t";
+ unicode_escaped = "u" [0-9a-fA-F]{4};
escaped = escape (single_escaped | unicode_escaped);
char = unescaped | escaped;
string = quotation_mark char* quotation_mark;
string { last_token_type = token_type::value_string; break; }
// end of file
- '\000' { last_token_type = token_type::end_of_input; break; }
+ "\000" { last_token_type = token_type::end_of_input; break; }
// anything else is an error
. { last_token_type = token_type::parse_error; break; }
@@ -7534,12 +7804,49 @@
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));
@@ -7655,11 +7962,6 @@
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
{
@@ -7840,7 +8142,7 @@
{
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
@@ -7848,7 +8150,7 @@
}
/// 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
@@ -7859,6 +8161,7 @@
basic_json parse()
{
basic_json result = parse_internal(true);
+ result.assert_invariant();
expect(lexer::token_type::end_of_input);
@@ -7881,7 +8184,7 @@
{
// 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
@@ -7959,7 +8262,7 @@
{
// 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
@@ -8096,7 +8399,7 @@
/// 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
@@ -8212,6 +8515,8 @@
/*!
@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
{
@@ -8533,7 +8838,7 @@
@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.
@@ -9389,7 +9694,7 @@
@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
@@ -9405,6 +9710,13 @@
/*!
@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)
diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp
index d8b7ef7..de8ad42 100644
--- a/test/src/fuzz.cpp
+++ b/test/src/fuzz.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (fuzz test support)
-| | |__ | | | | | | version 2.0.0
+| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Run "make fuzz_testing" and follow the instructions.
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index a7ca739..79a4bb0 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.0.1
+| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -3768,123 +3768,254 @@
SECTION("access specified element with default value")
{
- SECTION("access existing value")
+ SECTION("given a key")
{
- CHECK(j.value("integer", 2) == 1);
- CHECK(j.value("integer", 1.0) == Approx(1));
- CHECK(j.value("unsigned", 2) == 1u);
- CHECK(j.value("unsigned", 1.0) == Approx(1u));
- CHECK(j.value("null", json(1)) == json());
- CHECK(j.value("boolean", false) == true);
- CHECK(j.value("string", "bar") == "hello world");
- CHECK(j.value("string", std::string("bar")) == "hello world");
- CHECK(j.value("floating", 12.34) == Approx(42.23));
- CHECK(j.value("floating", 12) == 42);
- CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object()));
- CHECK(j.value("array", json({10, 100})) == json({1, 2, 3}));
+ SECTION("access existing value")
+ {
+ CHECK(j.value("integer", 2) == 1);
+ CHECK(j.value("integer", 1.0) == Approx(1));
+ CHECK(j.value("unsigned", 2) == 1u);
+ CHECK(j.value("unsigned", 1.0) == Approx(1u));
+ CHECK(j.value("null", json(1)) == json());
+ CHECK(j.value("boolean", false) == true);
+ CHECK(j.value("string", "bar") == "hello world");
+ CHECK(j.value("string", std::string("bar")) == "hello world");
+ CHECK(j.value("floating", 12.34) == Approx(42.23));
+ CHECK(j.value("floating", 12) == 42);
+ CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object()));
+ CHECK(j.value("array", json({10, 100})) == json({1, 2, 3}));
- CHECK(j_const.value("integer", 2) == 1);
- CHECK(j_const.value("integer", 1.0) == Approx(1));
- CHECK(j_const.value("unsigned", 2) == 1u);
- CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
- CHECK(j_const.value("boolean", false) == true);
- CHECK(j_const.value("string", "bar") == "hello world");
- CHECK(j_const.value("string", std::string("bar")) == "hello world");
- CHECK(j_const.value("floating", 12.34) == Approx(42.23));
- CHECK(j_const.value("floating", 12) == 42);
- CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object()));
- CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3}));
+ CHECK(j_const.value("integer", 2) == 1);
+ CHECK(j_const.value("integer", 1.0) == Approx(1));
+ CHECK(j_const.value("unsigned", 2) == 1u);
+ CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
+ CHECK(j_const.value("boolean", false) == true);
+ CHECK(j_const.value("string", "bar") == "hello world");
+ CHECK(j_const.value("string", std::string("bar")) == "hello world");
+ CHECK(j_const.value("floating", 12.34) == Approx(42.23));
+ CHECK(j_const.value("floating", 12) == 42);
+ CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object()));
+ CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3}));
+ }
+
+ SECTION("access non-existing value")
+ {
+ CHECK(j.value("_", 2) == 2);
+ CHECK(j.value("_", 2u) == 2u);
+ CHECK(j.value("_", false) == false);
+ CHECK(j.value("_", "bar") == "bar");
+ CHECK(j.value("_", 12.34) == Approx(12.34));
+ CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+ CHECK(j.value("_", json({10, 100})) == json({10, 100}));
+
+ CHECK(j_const.value("_", 2) == 2);
+ CHECK(j_const.value("_", 2u) == 2u);
+ CHECK(j_const.value("_", false) == false);
+ CHECK(j_const.value("_", "bar") == "bar");
+ CHECK(j_const.value("_", 12.34) == Approx(12.34));
+ CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+ CHECK(j_const.value("_", json({10, 100})) == json({10, 100}));
+ }
+
+ SECTION("access on non-object type")
+ {
+ SECTION("null")
+ {
+ json j_nonobject(json::value_t::null);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
+ }
+
+ SECTION("boolean")
+ {
+ json j_nonobject(json::value_t::boolean);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
+ }
+
+ SECTION("string")
+ {
+ json j_nonobject(json::value_t::string);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string");
+ }
+
+ SECTION("array")
+ {
+ json j_nonobject(json::value_t::array);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array");
+ }
+
+ SECTION("number (integer)")
+ {
+ json j_nonobject(json::value_t::number_integer);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+ }
+
+ SECTION("number (unsigned)")
+ {
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+ }
+
+ SECTION("number (floating-point)")
+ {
+ json j_nonobject(json::value_t::number_float);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+ }
+ }
}
- SECTION("access non-existing value")
+ SECTION("given a JSON pointer")
{
- CHECK(j.value("_", 2) == 2);
- CHECK(j.value("_", 2u) == 2u);
- CHECK(j.value("_", false) == false);
- CHECK(j.value("_", "bar") == "bar");
- CHECK(j.value("_", 12.34) == Approx(12.34));
- CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
- CHECK(j.value("_", json({10, 100})) == json({10, 100}));
-
- CHECK(j_const.value("_", 2) == 2);
- CHECK(j_const.value("_", 2u) == 2u);
- CHECK(j_const.value("_", false) == false);
- CHECK(j_const.value("_", "bar") == "bar");
- CHECK(j_const.value("_", 12.34) == Approx(12.34));
- CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
- CHECK(j_const.value("_", json({10, 100})) == json({10, 100}));
- }
-
- SECTION("access on non-object type")
- {
- SECTION("null")
+ SECTION("access existing value")
{
- json j_nonobject(json::value_t::null);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
+ CHECK(j.value("/integer"_json_pointer, 2) == 1);
+ CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1));
+ CHECK(j.value("/unsigned"_json_pointer, 2) == 1u);
+ CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
+ CHECK(j.value("/null"_json_pointer, json(1)) == json());
+ CHECK(j.value("/boolean"_json_pointer, false) == true);
+ CHECK(j.value("/string"_json_pointer, "bar") == "hello world");
+ CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world");
+ CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23));
+ CHECK(j.value("/floating"_json_pointer, 12) == 42);
+ CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
+ CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
+
+ CHECK(j_const.value("/integer"_json_pointer, 2) == 1);
+ CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1));
+ CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u);
+ CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
+ CHECK(j_const.value("/boolean"_json_pointer, false) == true);
+ CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world");
+ CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world");
+ CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23));
+ CHECK(j_const.value("/floating"_json_pointer, 12) == 42);
+ CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
+ CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
}
- SECTION("boolean")
+ SECTION("access non-existing value")
{
- json j_nonobject(json::value_t::boolean);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
+ CHECK(j.value("/not/existing"_json_pointer, 2) == 2);
+ CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u);
+ CHECK(j.value("/not/existing"_json_pointer, false) == false);
+ CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar");
+ CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
+ CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+ CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
+
+ CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2);
+ CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u);
+ CHECK(j_const.value("/not/existing"_json_pointer, false) == false);
+ CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar");
+ CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
+ CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+ CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
}
- SECTION("string")
+ SECTION("access on non-object type")
{
- json j_nonobject(json::value_t::string);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string");
- }
+ SECTION("null")
+ {
+ json j_nonobject(json::value_t::null);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null");
+ }
- SECTION("array")
- {
- json j_nonobject(json::value_t::array);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array");
- }
+ SECTION("boolean")
+ {
+ json j_nonobject(json::value_t::boolean);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+ "cannot use value() with boolean");
+ }
- SECTION("number (integer)")
- {
- json j_nonobject(json::value_t::number_integer);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
- }
+ SECTION("string")
+ {
+ json j_nonobject(json::value_t::string);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+ "cannot use value() with string");
+ }
- SECTION("number (unsigned)")
- {
- json j_nonobject(json::value_t::number_unsigned);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
- }
+ SECTION("array")
+ {
+ json j_nonobject(json::value_t::array);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array");
+ }
- SECTION("number (floating-point)")
- {
- json j_nonobject(json::value_t::number_float);
- const json j_nonobject_const(j_nonobject);
- CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+ SECTION("number (integer)")
+ {
+ json j_nonobject(json::value_t::number_integer);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+ "cannot use value() with number");
+ }
+
+ SECTION("number (unsigned)")
+ {
+ json j_nonobject(json::value_t::number_unsigned);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+ "cannot use value() with number");
+ }
+
+ SECTION("number (floating-point)")
+ {
+ json j_nonobject(json::value_t::number_float);
+ const json j_nonobject_const(j_nonobject);
+ CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+ "cannot use value() with number");
+ }
}
}
}
@@ -9716,6 +9847,39 @@
CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'");
// improve code coverage
CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument);
+ // unescaped control characters
+ CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument);
}
SECTION("escaped")
@@ -10326,6 +10490,14 @@
CHECK(j_empty_array == json());
}
}
+
+ SECTION("copy constructor")
+ {
+ json::string_t* s = new json::string_t("[1,2,3,4]");
+ json::parser p(*s);
+ delete s;
+ CHECK(p.parse() == json({1, 2, 3, 4}));
+ }
}
TEST_CASE("README", "[hide]")
@@ -12113,19 +12285,23 @@
TEST_CASE("Unicode", "[hide]")
{
- SECTION("full enumeration of Unicode codepoints")
+ SECTION("full enumeration of Unicode code points")
{
- // create a string from a codepoint
- auto codepoint_to_unicode = [](std::size_t cp)
+ // create an escaped string from a code point
+ const auto codepoint_to_unicode = [](std::size_t cp)
{
- char* buffer = new char[10];
- sprintf(buffer, "\\u%04lx", cp);
- std::string result(buffer);
- delete[] buffer;
- return result;
+ // copd points are represented as a six-character sequence: a
+ // reverse solidus, followed by the lowercase letter u, followed
+ // by four hexadecimal digits that encode the character's code
+ // point
+ std::stringstream ss;
+ ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp;
+ return ss.str();
};
- // generate all codepoints
+ // generate all UTF-8 code points; in total, 1112064 code points are
+ // generated: 0x1FFFFF code points - 2048 invalid values between
+ // 0xD800 and 0xDFFF.
for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
{
// The Unicode standard permanently reserves these code point
@@ -12135,34 +12311,57 @@
// no UTF forms, including UTF-16, can encode these code points.
if (cp >= 0xD800u and cp <= 0xDFFFu)
{
+ // if we would not skip these code points, we would get a
+ // "missing low surrogate" exception
continue;
}
- std::string res;
+ // string to store the code point as in \uxxxx format
+ std::string escaped_string;
+ // string to store the code point as unescaped character sequence
+ std::string unescaped_string;
if (cp < 0x10000u)
{
- // codepoint can be represented with 16 bit
- res += codepoint_to_unicode(cp);
+ // code points in the Basic Multilingual Plane can be
+ // represented with one \\uxxxx sequence
+ escaped_string = codepoint_to_unicode(cp);
+
+ // All Unicode characters may be placed within the quotation
+ // marks, except for the characters that must be escaped:
+ // quotation mark, reverse solidus, and the control characters
+ // (U+0000 through U+001F); we ignore these code points as
+ // they are checked with codepoint_to_unicode.
+ if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
+ {
+ unescaped_string = json::lexer::to_unicode(cp);
+ }
}
else
{
- // codepoint can be represented with a pair
- res += codepoint_to_unicode(0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu));
- res += codepoint_to_unicode(0xdc00u + ((cp - 0x10000u) & 0x3ffu));
+ // To escape an extended character that is not in the Basic
+ // Multilingual Plane, the character is represented as a
+ // 12-character sequence, encoding the UTF-16 surrogate pair
+ const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
+ const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
+ escaped_string = codepoint_to_unicode(codepoint1);
+ escaped_string += codepoint_to_unicode(codepoint2);
+ unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2);
}
- try
- {
- json j1, j2;
- CHECK_NOTHROW(j1 = json::parse("\"" + res + "\""));
- CHECK_NOTHROW(j2 = json::parse(j1.dump()));
- CHECK(j1 == j2);
- }
- catch (std::invalid_argument)
- {
- // we ignore parsing errors
- }
+ // all other code points are valid and must not yield parse errors
+ CAPTURE(cp);
+ CAPTURE(escaped_string);
+ CAPTURE(unescaped_string);
+
+ json j1, j2, j3, j4;
+ CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\""));
+ CHECK_NOTHROW(j2 = json::parse(j1.dump()));
+ CHECK(j1 == j2);
+
+ CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\""));
+ CHECK_NOTHROW(j4 = json::parse(j3.dump()));
+ CHECK(j3 == j4);
}
}
@@ -12175,6 +12374,8 @@
CHECK_NOTHROW(j << f);
// the array has 1112064 + 1 elemnts (a terminating "null" value)
+ // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
+ // 0xD800 and 0xDFFF.
CHECK(j.size() == 1112065);
SECTION("check JSON Pointers")
@@ -14159,6 +14360,19 @@
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
+
+ SECTION("issue #283 - value() does not work with _json_pointer types")
+ {
+ json j =
+ {
+ {"object", {{"key1", 1}, {"key2", 2}}},
+ };
+
+ int at_integer = j.at("/object/key2"_json_pointer);
+ int val_integer = j.value("/object/key2"_json_pointer, 0);
+
+ CHECK(at_integer == val_integer);
+ }
}
// special test case to check if memory is leaked if constructor throws