Merge branch 'feature/iterator_range_parsing' into develop
This commit is contained in:
		
						commit
						776880bc49
					
				
					 18 changed files with 1075 additions and 498 deletions
				
			
		
							
								
								
									
										10
									
								
								.github/CONTRIBUTING.md
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/CONTRIBUTING.md
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -41,16 +41,10 @@ There are currently two files which need to be edited:
 | 
			
		|||
 | 
			
		||||
2. [`test/src/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
 | 
			
		||||
 | 
			
		||||
   If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled with
 | 
			
		||||
   If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled and executed with
 | 
			
		||||
 | 
			
		||||
   ```sh
 | 
			
		||||
   make
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
   and can be executed with
 | 
			
		||||
 | 
			
		||||
   ```sh
 | 
			
		||||
   ./json_unit
 | 
			
		||||
   make check
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
   The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -492,6 +492,8 @@ I deeply appreciate the help of the following people.
 | 
			
		|||
- [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
 | 
			
		||||
- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
 | 
			
		||||
- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
 | 
			
		||||
- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
 | 
			
		||||
- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290).
 | 
			
		||||
 | 
			
		||||
Thanks a lot for helping out!
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -510,7 +512,7 @@ To compile and run the tests, you need to execute
 | 
			
		|||
$ make check
 | 
			
		||||
 | 
			
		||||
===============================================================================
 | 
			
		||||
All tests passed (8905099 assertions in 32 test cases)
 | 
			
		||||
All tests passed (8905154 assertions in 35 test cases)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										28
									
								
								doc/examples/parse__array__parser_callback_t.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								doc/examples/parse__array__parser_callback_t.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
#include <json.hpp>
 | 
			
		||||
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    // a JSON text
 | 
			
		||||
    char text[] = R"(
 | 
			
		||||
    {
 | 
			
		||||
        "Image": {
 | 
			
		||||
            "Width":  800,
 | 
			
		||||
            "Height": 600,
 | 
			
		||||
            "Title":  "View from 15th Floor",
 | 
			
		||||
            "Thumbnail": {
 | 
			
		||||
                "Url":    "http://www.example.com/image/481989943",
 | 
			
		||||
                "Height": 125,
 | 
			
		||||
                "Width":  100
 | 
			
		||||
            },
 | 
			
		||||
            "Animated" : false,
 | 
			
		||||
            "IDs": [116, 943, 234, 38793]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    )";
 | 
			
		||||
 | 
			
		||||
    // parse and serialize JSON
 | 
			
		||||
    json j_complete = json::parse(text);
 | 
			
		||||
    std::cout << std::setw(4) << j_complete << "\n\n";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								doc/examples/parse__array__parser_callback_t.link
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								doc/examples/parse__array__parser_callback_t.link
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
<a target="_blank" href="http://melpon.org/wandbox/permlink/CwZnqGqte14SYJ5s"><b>online</b></a>
 | 
			
		||||
							
								
								
									
										20
									
								
								doc/examples/parse__array__parser_callback_t.output
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								doc/examples/parse__array__parser_callback_t.output
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
{
 | 
			
		||||
    "Image": {
 | 
			
		||||
        "Animated": false,
 | 
			
		||||
        "Height": 600,
 | 
			
		||||
        "IDs": [
 | 
			
		||||
            116,
 | 
			
		||||
            943,
 | 
			
		||||
            234,
 | 
			
		||||
            38793
 | 
			
		||||
        ],
 | 
			
		||||
        "Thumbnail": {
 | 
			
		||||
            "Height": 125,
 | 
			
		||||
            "Url": "http://www.example.com/image/481989943",
 | 
			
		||||
            "Width": 100
 | 
			
		||||
        },
 | 
			
		||||
        "Title": "View from 15th Floor",
 | 
			
		||||
        "Width": 800
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
#include <json.hpp>
 | 
			
		||||
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    // a JSON text given as std::vector
 | 
			
		||||
    std::vector<uint8_t> text = {'[', '1', ',', '2', ',', '3', ']', '\0'};
 | 
			
		||||
 | 
			
		||||
    // parse and serialize JSON
 | 
			
		||||
    json j_complete = json::parse(text);
 | 
			
		||||
    std::cout << std::setw(4) << j_complete << "\n\n";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
<a target="_blank" href="http://melpon.org/wandbox/permlink/F8VaVFyys87qQRt5"><b>online</b></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
[
 | 
			
		||||
    1,
 | 
			
		||||
    2,
 | 
			
		||||
    3
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								doc/examples/parse__iteratortype__parser_callback_t.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								doc/examples/parse__iteratortype__parser_callback_t.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
#include <json.hpp>
 | 
			
		||||
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    // a JSON text given as std::vector
 | 
			
		||||
    std::vector<uint8_t> text = {'[', '1', ',', '2', ',', '3', ']', '\0'};
 | 
			
		||||
 | 
			
		||||
    // parse and serialize JSON
 | 
			
		||||
    json j_complete = json::parse(text.begin(), text.end());
 | 
			
		||||
    std::cout << std::setw(4) << j_complete << "\n\n";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								doc/examples/parse__iteratortype__parser_callback_t.link
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								doc/examples/parse__iteratortype__parser_callback_t.link
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
<a target="_blank" href="http://melpon.org/wandbox/permlink/ojh4Eeol4G9RgeRV"><b>online</b></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
[
 | 
			
		||||
    1,
 | 
			
		||||
    2,
 | 
			
		||||
    3
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ using json = nlohmann::json;
 | 
			
		|||
int main()
 | 
			
		||||
{
 | 
			
		||||
    // a JSON text
 | 
			
		||||
    std::string text = R"(
 | 
			
		||||
    auto text = R"(
 | 
			
		||||
    {
 | 
			
		||||
        "Image": {
 | 
			
		||||
            "Width":  800,
 | 
			
		||||
| 
						 | 
				
			
			@ -44,4 +44,4 @@ int main()
 | 
			
		|||
    // parse (with callback) and serialize JSON
 | 
			
		||||
    json j_filtered = json::parse(text, cb);
 | 
			
		||||
    std::cout << std::setw(4) << j_filtered << '\n';
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
<a target="_blank" href="http://melpon.org/wandbox/permlink/SrKpkE9ivmvd2OUy"><b>online</b></a>
 | 
			
		||||
<a target="_blank" href="http://melpon.org/wandbox/permlink/n888UNQlMFduURhE"><b>online</b></a>
 | 
			
		||||
							
								
								
									
										605
									
								
								src/json.hpp
									
										
									
									
									
								
							
							
						
						
									
										605
									
								
								src/json.hpp
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -37,6 +37,7 @@ SOFTWARE.
 | 
			
		|||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <initializer_list>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
| 
						 | 
				
			
			@ -959,7 +960,7 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    With a parser callback function, the result of parsing a JSON text can be
 | 
			
		||||
    influenced. When passed to @ref parse(std::istream&, const
 | 
			
		||||
    parser_callback_t) or @ref parse(const string_t&, const parser_callback_t),
 | 
			
		||||
    parser_callback_t) or @ref parse(const char*, const parser_callback_t),
 | 
			
		||||
    it is called on certain events (passed as @ref parse_event_t via parameter
 | 
			
		||||
    @a event) with a set recursion depth @a depth and context JSON value
 | 
			
		||||
    @a parsed. The return value of the callback function is a boolean
 | 
			
		||||
| 
						 | 
				
			
			@ -1002,7 +1003,7 @@ class basic_json
 | 
			
		|||
    skipped completely or replaced by an empty discarded object.
 | 
			
		||||
 | 
			
		||||
    @sa @ref parse(std::istream&, parser_callback_t) or
 | 
			
		||||
    @ref parse(const string_t&, parser_callback_t) for examples
 | 
			
		||||
    @ref parse(const char*, parser_callback_t) for examples
 | 
			
		||||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
| 
						 | 
				
			
			@ -1140,11 +1141,9 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class CompatibleObjectType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
 | 
			
		||||
                  std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    template<class CompatibleObjectType, typename std::enable_if<
 | 
			
		||||
                 std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
 | 
			
		||||
                 std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0>
 | 
			
		||||
    basic_json(const CompatibleObjectType& val)
 | 
			
		||||
        : m_type(value_t::object)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1205,16 +1204,14 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class CompatibleArrayType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
 | 
			
		||||
                  not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
 | 
			
		||||
                  not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
 | 
			
		||||
                  not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
 | 
			
		||||
                  not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
 | 
			
		||||
                  not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
 | 
			
		||||
                  std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    template<class CompatibleArrayType, typename std::enable_if<
 | 
			
		||||
                 not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
 | 
			
		||||
                 not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
 | 
			
		||||
                 not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
 | 
			
		||||
                 not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
 | 
			
		||||
                 not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
 | 
			
		||||
                 not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
 | 
			
		||||
                 std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0>
 | 
			
		||||
    basic_json(const CompatibleArrayType& val)
 | 
			
		||||
        : m_type(value_t::array)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1300,10 +1297,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class CompatibleStringType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_constructible<string_t, CompatibleStringType>::value, int>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    template<class CompatibleStringType, typename std::enable_if<
 | 
			
		||||
                 std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
 | 
			
		||||
    basic_json(const CompatibleStringType& val)
 | 
			
		||||
        : basic_json(string_t(val))
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1353,12 +1348,9 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename T,
 | 
			
		||||
             typename std::enable_if<
 | 
			
		||||
                 not (std::is_same<T, int>::value)
 | 
			
		||||
                 and std::is_same<T, number_integer_t>::value
 | 
			
		||||
                 , int>::type
 | 
			
		||||
             = 0>
 | 
			
		||||
    template<typename T, typename std::enable_if<
 | 
			
		||||
                 not (std::is_same<T, int>::value) and
 | 
			
		||||
                 std::is_same<T, number_integer_t>::value, int>::type = 0>
 | 
			
		||||
    basic_json(const number_integer_t val) noexcept
 | 
			
		||||
        : m_type(value_t::number_integer), m_value(val)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1422,13 +1414,11 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename CompatibleNumberIntegerType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
    template<typename CompatibleNumberIntegerType, typename std::enable_if<
 | 
			
		||||
                 std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
 | 
			
		||||
                 std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
 | 
			
		||||
                 std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
 | 
			
		||||
                 CompatibleNumberIntegerType>::type
 | 
			
		||||
             = 0>
 | 
			
		||||
                 CompatibleNumberIntegerType>::type = 0>
 | 
			
		||||
    basic_json(const CompatibleNumberIntegerType val) noexcept
 | 
			
		||||
        : m_type(value_t::number_integer),
 | 
			
		||||
          m_value(static_cast<number_integer_t>(val))
 | 
			
		||||
| 
						 | 
				
			
			@ -1453,12 +1443,9 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 2.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename T,
 | 
			
		||||
             typename std::enable_if<
 | 
			
		||||
                 not (std::is_same<T, int>::value)
 | 
			
		||||
                 and std::is_same<T, number_unsigned_t>::value
 | 
			
		||||
                 , int>::type
 | 
			
		||||
             = 0>
 | 
			
		||||
    template<typename T, typename std::enable_if<
 | 
			
		||||
                 not (std::is_same<T, int>::value) and
 | 
			
		||||
                 std::is_same<T, number_unsigned_t>::value, int>::type = 0>
 | 
			
		||||
    basic_json(const number_unsigned_t val) noexcept
 | 
			
		||||
        : m_type(value_t::number_unsigned), m_value(val)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1485,13 +1472,11 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 2.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <typename CompatibleNumberUnsignedType, typename
 | 
			
		||||
              std::enable_if <
 | 
			
		||||
                  std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
 | 
			
		||||
                  std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
 | 
			
		||||
                  not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
 | 
			
		||||
                  CompatibleNumberUnsignedType>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    template<typename CompatibleNumberUnsignedType, typename std::enable_if <
 | 
			
		||||
                 std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
 | 
			
		||||
                 std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
 | 
			
		||||
                 not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
 | 
			
		||||
                 CompatibleNumberUnsignedType>::type = 0>
 | 
			
		||||
    basic_json(const CompatibleNumberUnsignedType val) noexcept
 | 
			
		||||
        : m_type(value_t::number_unsigned),
 | 
			
		||||
          m_value(static_cast<number_unsigned_t>(val))
 | 
			
		||||
| 
						 | 
				
			
			@ -1567,11 +1552,9 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename CompatibleNumberFloatType, typename = typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
    template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
 | 
			
		||||
                 std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
 | 
			
		||||
                 std::is_floating_point<CompatibleNumberFloatType>::value>::type
 | 
			
		||||
             >
 | 
			
		||||
                 std::is_floating_point<CompatibleNumberFloatType>::value>::type>
 | 
			
		||||
    basic_json(const CompatibleNumberFloatType val) noexcept
 | 
			
		||||
        : basic_json(number_float_t(val))
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1838,12 +1821,9 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class InputIT, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_same<InputIT, typename basic_json_t::iterator>::value or
 | 
			
		||||
                  std::is_same<InputIT, typename basic_json_t::const_iterator>::value
 | 
			
		||||
                  , int>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    template<class InputIT, typename std::enable_if<
 | 
			
		||||
                 std::is_same<InputIT, typename basic_json_t::iterator>::value or
 | 
			
		||||
                 std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
 | 
			
		||||
    basic_json(InputIT first, InputIT last)
 | 
			
		||||
    {
 | 
			
		||||
        assert(first.m_object != nullptr);
 | 
			
		||||
| 
						 | 
				
			
			@ -2601,11 +2581,9 @@ class basic_json
 | 
			
		|||
    //////////////////
 | 
			
		||||
 | 
			
		||||
    /// get an object (explicit)
 | 
			
		||||
    template <class T, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
 | 
			
		||||
                  std::is_convertible<basic_json_t, typename T::mapped_type>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<class T, typename std::enable_if<
 | 
			
		||||
                 std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
 | 
			
		||||
                 std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0>
 | 
			
		||||
    T get_impl(T*) const
 | 
			
		||||
    {
 | 
			
		||||
        if (is_object())
 | 
			
		||||
| 
						 | 
				
			
			@ -2632,14 +2610,12 @@ class basic_json
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// get an array (explicit)
 | 
			
		||||
    template <class T, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_convertible<basic_json_t, typename T::value_type>::value and
 | 
			
		||||
                  not std::is_same<basic_json_t, typename T::value_type>::value and
 | 
			
		||||
                  not std::is_arithmetic<T>::value and
 | 
			
		||||
                  not std::is_convertible<std::string, T>::value and
 | 
			
		||||
                  not has_mapped_type<T>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<class T, typename std::enable_if<
 | 
			
		||||
                 std::is_convertible<basic_json_t, typename T::value_type>::value and
 | 
			
		||||
                 not std::is_same<basic_json_t, typename T::value_type>::value and
 | 
			
		||||
                 not std::is_arithmetic<T>::value and
 | 
			
		||||
                 not std::is_convertible<std::string, T>::value and
 | 
			
		||||
                 not has_mapped_type<T>::value, int>::type = 0>
 | 
			
		||||
    T get_impl(T*) const
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array())
 | 
			
		||||
| 
						 | 
				
			
			@ -2659,11 +2635,9 @@ class basic_json
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// get an array (explicit)
 | 
			
		||||
    template <class T, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_convertible<basic_json_t, T>::value and
 | 
			
		||||
                  not std::is_same<basic_json_t, T>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<class T, typename std::enable_if<
 | 
			
		||||
                 std::is_convertible<basic_json_t, T>::value and
 | 
			
		||||
                 not std::is_same<basic_json_t, T>::value, int>::type = 0>
 | 
			
		||||
    std::vector<T> get_impl(std::vector<T>*) const
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array())
 | 
			
		||||
| 
						 | 
				
			
			@ -2684,11 +2658,9 @@ class basic_json
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// get an array (explicit)
 | 
			
		||||
    template <class T, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_same<basic_json, typename T::value_type>::value and
 | 
			
		||||
                  not has_mapped_type<T>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<class T, typename std::enable_if<
 | 
			
		||||
                 std::is_same<basic_json, typename T::value_type>::value and
 | 
			
		||||
                 not has_mapped_type<T>::value, int>::type = 0>
 | 
			
		||||
    T get_impl(T*) const
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array())
 | 
			
		||||
| 
						 | 
				
			
			@ -2715,10 +2687,8 @@ class basic_json
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// get a string (explicit)
 | 
			
		||||
    template <typename T, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_convertible<string_t, T>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<typename T, typename std::enable_if<
 | 
			
		||||
                 std::is_convertible<string_t, T>::value, int>::type = 0>
 | 
			
		||||
    T get_impl(T*) const
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string())
 | 
			
		||||
| 
						 | 
				
			
			@ -2732,10 +2702,8 @@ class basic_json
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// get a number (explicit)
 | 
			
		||||
    template<typename T, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_arithmetic<T>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename T, typename std::enable_if<
 | 
			
		||||
                 std::is_arithmetic<T>::value, int>::type = 0>
 | 
			
		||||
    T get_impl(T*) const
 | 
			
		||||
    {
 | 
			
		||||
        switch (m_type)
 | 
			
		||||
| 
						 | 
				
			
			@ -2924,10 +2892,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename ValueType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 not std::is_pointer<ValueType>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename ValueType, typename std::enable_if<
 | 
			
		||||
                 not std::is_pointer<ValueType>::value, int>::type = 0>
 | 
			
		||||
    ValueType get() const
 | 
			
		||||
    {
 | 
			
		||||
        return get_impl(static_cast<ValueType*>(nullptr));
 | 
			
		||||
| 
						 | 
				
			
			@ -2960,10 +2926,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename PointerType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename PointerType, typename std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value, int>::type = 0>
 | 
			
		||||
    PointerType get() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        // delegate the call to get_ptr
 | 
			
		||||
| 
						 | 
				
			
			@ -2974,10 +2938,8 @@ class basic_json
 | 
			
		|||
    @brief get a pointer value (explicit)
 | 
			
		||||
    @copydoc get()
 | 
			
		||||
    */
 | 
			
		||||
    template<typename PointerType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename PointerType, typename std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value, int>::type = 0>
 | 
			
		||||
    constexpr const PointerType get() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        // delegate the call to get_ptr
 | 
			
		||||
| 
						 | 
				
			
			@ -3010,10 +2972,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename PointerType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename PointerType, typename std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value, int>::type = 0>
 | 
			
		||||
    PointerType get_ptr() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        // get the type of the PointerType (remove pointer and const)
 | 
			
		||||
| 
						 | 
				
			
			@ -3039,11 +2999,9 @@ class basic_json
 | 
			
		|||
    @brief get a pointer value (implicit)
 | 
			
		||||
    @copydoc get_ptr()
 | 
			
		||||
    */
 | 
			
		||||
    template<typename PointerType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value
 | 
			
		||||
                 and std::is_const<typename std::remove_pointer<PointerType>::type>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename PointerType, typename std::enable_if<
 | 
			
		||||
                 std::is_pointer<PointerType>::value and
 | 
			
		||||
                 std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
 | 
			
		||||
    constexpr const PointerType get_ptr() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        // get the type of the PointerType (remove pointer and const)
 | 
			
		||||
| 
						 | 
				
			
			@ -3091,10 +3049,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.1.0
 | 
			
		||||
    */
 | 
			
		||||
    template<typename ReferenceType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_reference<ReferenceType>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename ReferenceType, typename std::enable_if<
 | 
			
		||||
                 std::is_reference<ReferenceType>::value, int>::type = 0>
 | 
			
		||||
    ReferenceType get_ref()
 | 
			
		||||
    {
 | 
			
		||||
        // delegate call to get_ref_impl
 | 
			
		||||
| 
						 | 
				
			
			@ -3105,11 +3061,9 @@ class basic_json
 | 
			
		|||
    @brief get a reference value (implicit)
 | 
			
		||||
    @copydoc get_ref()
 | 
			
		||||
    */
 | 
			
		||||
    template<typename ReferenceType, typename
 | 
			
		||||
             std::enable_if<
 | 
			
		||||
                 std::is_reference<ReferenceType>::value
 | 
			
		||||
                 and std::is_const<typename std::remove_reference<ReferenceType>::type>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    template<typename ReferenceType, typename std::enable_if<
 | 
			
		||||
                 std::is_reference<ReferenceType>::value and
 | 
			
		||||
                 std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
 | 
			
		||||
    ReferenceType get_ref() const
 | 
			
		||||
    {
 | 
			
		||||
        // delegate call to get_ref_impl
 | 
			
		||||
| 
						 | 
				
			
			@ -3144,10 +3098,9 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template < typename ValueType, typename
 | 
			
		||||
               std::enable_if <
 | 
			
		||||
                   not std::is_pointer<ValueType>::value
 | 
			
		||||
                   and not std::is_same<ValueType, typename string_t::value_type>::value
 | 
			
		||||
    template < typename ValueType, typename std::enable_if <
 | 
			
		||||
                   not std::is_pointer<ValueType>::value and
 | 
			
		||||
                   not std::is_same<ValueType, typename string_t::value_type>::value
 | 
			
		||||
#ifndef _MSC_VER  // Fix for issue #167 operator<< abiguity under VS2015
 | 
			
		||||
                   and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -3737,10 +3690,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class ValueType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_convertible<basic_json_t, ValueType>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<class ValueType, typename std::enable_if<
 | 
			
		||||
                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
 | 
			
		||||
    ValueType value(const typename object_t::key_type& key, ValueType default_value) const
 | 
			
		||||
    {
 | 
			
		||||
        // at only works for objects
 | 
			
		||||
| 
						 | 
				
			
			@ -3813,10 +3764,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 2.0.2
 | 
			
		||||
    */
 | 
			
		||||
    template <class ValueType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_convertible<basic_json_t, ValueType>::value
 | 
			
		||||
                  , int>::type = 0>
 | 
			
		||||
    template<class ValueType, typename std::enable_if<
 | 
			
		||||
                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
 | 
			
		||||
    ValueType value(const json_pointer& ptr, ValueType default_value) const
 | 
			
		||||
    {
 | 
			
		||||
        // at only works for objects
 | 
			
		||||
| 
						 | 
				
			
			@ -3946,7 +3895,7 @@ class basic_json
 | 
			
		|||
    @return Iterator following the last removed element. If the iterator @a
 | 
			
		||||
    pos refers to the last element, the `end()` iterator is returned.
 | 
			
		||||
 | 
			
		||||
    @tparam InteratorType an @ref iterator or @ref const_iterator
 | 
			
		||||
    @tparam IteratorType an @ref iterator or @ref const_iterator
 | 
			
		||||
 | 
			
		||||
    @post Invalidates iterators and references at or after the point of the
 | 
			
		||||
    erase, including the `end()` iterator.
 | 
			
		||||
| 
						 | 
				
			
			@ -3968,7 +3917,7 @@ class basic_json
 | 
			
		|||
    @liveexample{The example shows the result of `erase()` for different JSON
 | 
			
		||||
    types.,erase__IteratorType}
 | 
			
		||||
 | 
			
		||||
    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
 | 
			
		||||
    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
 | 
			
		||||
    the given range
 | 
			
		||||
    @sa @ref erase(const typename object_t::key_type&) -- removes the element
 | 
			
		||||
    from an object at the given key
 | 
			
		||||
| 
						 | 
				
			
			@ -3977,13 +3926,11 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class InteratorType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_same<InteratorType, typename basic_json_t::iterator>::value or
 | 
			
		||||
                  std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
 | 
			
		||||
                  , int>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    InteratorType erase(InteratorType pos)
 | 
			
		||||
    template<class IteratorType, typename std::enable_if<
 | 
			
		||||
                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or
 | 
			
		||||
                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
 | 
			
		||||
             = 0>
 | 
			
		||||
    IteratorType erase(IteratorType pos)
 | 
			
		||||
    {
 | 
			
		||||
        // make sure iterator fits the current value
 | 
			
		||||
        if (this != pos.m_object)
 | 
			
		||||
| 
						 | 
				
			
			@ -3991,7 +3938,7 @@ class basic_json
 | 
			
		|||
            throw std::domain_error("iterator does not fit current value");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InteratorType result = end();
 | 
			
		||||
        IteratorType result = end();
 | 
			
		||||
 | 
			
		||||
        switch (m_type)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -4055,7 +4002,7 @@ class basic_json
 | 
			
		|||
    @return Iterator following the last removed element. If the iterator @a
 | 
			
		||||
    second refers to the last element, the `end()` iterator is returned.
 | 
			
		||||
 | 
			
		||||
    @tparam InteratorType an @ref iterator or @ref const_iterator
 | 
			
		||||
    @tparam IteratorType an @ref iterator or @ref const_iterator
 | 
			
		||||
 | 
			
		||||
    @post Invalidates iterators and references at or after the point of the
 | 
			
		||||
    erase, including the `end()` iterator.
 | 
			
		||||
| 
						 | 
				
			
			@ -4078,7 +4025,7 @@ class basic_json
 | 
			
		|||
    @liveexample{The example shows the result of `erase()` for different JSON
 | 
			
		||||
    types.,erase__IteratorType_IteratorType}
 | 
			
		||||
 | 
			
		||||
    @sa @ref erase(InteratorType) -- removes the element at a given position
 | 
			
		||||
    @sa @ref erase(IteratorType) -- removes the element at a given position
 | 
			
		||||
    @sa @ref erase(const typename object_t::key_type&) -- removes the element
 | 
			
		||||
    from an object at the given key
 | 
			
		||||
    @sa @ref erase(const size_type) -- removes the element from an array at
 | 
			
		||||
| 
						 | 
				
			
			@ -4086,13 +4033,11 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    */
 | 
			
		||||
    template <class InteratorType, typename
 | 
			
		||||
              std::enable_if<
 | 
			
		||||
                  std::is_same<InteratorType, typename basic_json_t::iterator>::value or
 | 
			
		||||
                  std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
 | 
			
		||||
                  , int>::type
 | 
			
		||||
              = 0>
 | 
			
		||||
    InteratorType erase(InteratorType first, InteratorType last)
 | 
			
		||||
    template<class IteratorType, typename std::enable_if<
 | 
			
		||||
                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or
 | 
			
		||||
                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
 | 
			
		||||
             = 0>
 | 
			
		||||
    IteratorType erase(IteratorType first, IteratorType last)
 | 
			
		||||
    {
 | 
			
		||||
        // make sure iterator fits the current value
 | 
			
		||||
        if (this != first.m_object or this != last.m_object)
 | 
			
		||||
| 
						 | 
				
			
			@ -4100,7 +4045,7 @@ class basic_json
 | 
			
		|||
            throw std::domain_error("iterators do not fit current value");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InteratorType result = end();
 | 
			
		||||
        IteratorType result = end();
 | 
			
		||||
 | 
			
		||||
        switch (m_type)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -4172,8 +4117,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @liveexample{The example shows the effect of `erase()`.,erase__key_type}
 | 
			
		||||
 | 
			
		||||
    @sa @ref erase(InteratorType) -- removes the element at a given position
 | 
			
		||||
    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
 | 
			
		||||
    @sa @ref erase(IteratorType) -- removes the element at a given position
 | 
			
		||||
    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
 | 
			
		||||
    the given range
 | 
			
		||||
    @sa @ref erase(const size_type) -- removes the element from an array at
 | 
			
		||||
    the given index
 | 
			
		||||
| 
						 | 
				
			
			@ -4209,8 +4154,8 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @liveexample{The example shows the effect of `erase()`.,erase__size_type}
 | 
			
		||||
 | 
			
		||||
    @sa @ref erase(InteratorType) -- removes the element at a given position
 | 
			
		||||
    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
 | 
			
		||||
    @sa @ref erase(IteratorType) -- removes the element at a given position
 | 
			
		||||
    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
 | 
			
		||||
    the given range
 | 
			
		||||
    @sa @ref erase(const typename object_t::key_type&) -- removes the element
 | 
			
		||||
    from an object at the given key
 | 
			
		||||
| 
						 | 
				
			
			@ -5918,10 +5863,16 @@ class basic_json
 | 
			
		|||
    /// @{
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
    @brief deserialize from string
 | 
			
		||||
    @brief deserialize from an array
 | 
			
		||||
 | 
			
		||||
    @param[in] s  string to read a serialized JSON value from
 | 
			
		||||
    @param[in] cb a parser callback function of type @ref parser_callback_t
 | 
			
		||||
    This function reads from an array of 1-byte values.
 | 
			
		||||
 | 
			
		||||
    @pre Each element of the container has a size of 1 byte. Violating this
 | 
			
		||||
    precondition yields undefined behavior. **This precondition is enforced
 | 
			
		||||
    with a static assertion.**
 | 
			
		||||
 | 
			
		||||
    @param[in] array  array to read from
 | 
			
		||||
    @param[in] cb  a parser callback function of type @ref parser_callback_t
 | 
			
		||||
    which is used to control the deserialization by filtering unwanted values
 | 
			
		||||
    (optional)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5933,18 +5884,54 @@ class basic_json
 | 
			
		|||
 | 
			
		||||
    @note A UTF-8 byte order mark is silently ignored.
 | 
			
		||||
 | 
			
		||||
    @liveexample{The example below demonstrates the `parse()` function reading
 | 
			
		||||
    from an array.,parse__array__parser_callback_t}
 | 
			
		||||
 | 
			
		||||
    @since version 2.0.3
 | 
			
		||||
    */
 | 
			
		||||
    template<class T, std::size_t N>
 | 
			
		||||
    static basic_json parse(T (&array)[N],
 | 
			
		||||
                            const parser_callback_t cb = nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        // delegate the call to the iterator-range parse overload
 | 
			
		||||
        return parse(std::begin(array), std::end(array), cb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
    @brief deserialize from string literal
 | 
			
		||||
 | 
			
		||||
    @tparam CharT character/literal type with size of 1 byte
 | 
			
		||||
    @param[in] s  string literal to read a serialized JSON value from
 | 
			
		||||
    @param[in] cb a parser callback function of type @ref parser_callback_t
 | 
			
		||||
    which is used to control the deserialization by filtering unwanted values
 | 
			
		||||
    (optional)
 | 
			
		||||
 | 
			
		||||
    @return result of the deserialization
 | 
			
		||||
 | 
			
		||||
    @complexity Linear in the length of the input. The parser is a predictive
 | 
			
		||||
    LL(1) parser. The complexity can be higher if the parser callback function
 | 
			
		||||
    @a cb has a super-linear complexity.
 | 
			
		||||
 | 
			
		||||
    @note A UTF-8 byte order mark is silently ignored.
 | 
			
		||||
    @note String containers like `std::string` or @ref string_t can be parsed
 | 
			
		||||
          with @ref parse(const ContiguousContainer&, const parser_callback_t)
 | 
			
		||||
 | 
			
		||||
    @liveexample{The example below demonstrates the `parse()` function with
 | 
			
		||||
    and without callback function.,parse__string__parser_callback_t}
 | 
			
		||||
 | 
			
		||||
    @sa @ref parse(std::istream&, const parser_callback_t) for a version that
 | 
			
		||||
    reads from an input stream
 | 
			
		||||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
    @since version 1.0.0 (originally for @ref string_t)
 | 
			
		||||
    */
 | 
			
		||||
    static basic_json parse(const string_t& s,
 | 
			
		||||
    template<typename CharPT, typename std::enable_if<
 | 
			
		||||
                 std::is_pointer<CharPT>::value and
 | 
			
		||||
                 std::is_integral<typename std::remove_pointer<CharPT>::type>::value and
 | 
			
		||||
                 sizeof(typename std::remove_pointer<CharPT>::type) == 1, int>::type = 0>
 | 
			
		||||
    static basic_json parse(const CharPT s,
 | 
			
		||||
                            const parser_callback_t cb = nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        return parser(s, cb).parse();
 | 
			
		||||
        return parser(reinterpret_cast<const char*>(s), cb).parse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
| 
						 | 
				
			
			@ -5966,7 +5953,7 @@ class basic_json
 | 
			
		|||
    @liveexample{The example below demonstrates the `parse()` function with
 | 
			
		||||
    and without callback function.,parse__istream__parser_callback_t}
 | 
			
		||||
 | 
			
		||||
    @sa @ref parse(const string_t&, const parser_callback_t) for a version
 | 
			
		||||
    @sa @ref parse(const char*, const parser_callback_t) for a version
 | 
			
		||||
    that reads from a string
 | 
			
		||||
 | 
			
		||||
    @since version 1.0.0
 | 
			
		||||
| 
						 | 
				
			
			@ -5986,6 +5973,130 @@ class basic_json
 | 
			
		|||
        return parser(i, cb).parse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
    @brief deserialize from an iterator range with contiguous storage
 | 
			
		||||
 | 
			
		||||
    This function reads from an iterator range of a container with contiguous
 | 
			
		||||
    storage of 1-byte values. Compatible container types include
 | 
			
		||||
    `std::vector`, `std::string`, `std::array`, `std::valarray`, and
 | 
			
		||||
    `std::initializer_list`. Furthermore, C-style arrays can be used with
 | 
			
		||||
    `std::begin()`/`std::end()`. User-defined containers can be used as long
 | 
			
		||||
    as they implement random-access iterators and a contiguous storage.
 | 
			
		||||
 | 
			
		||||
    @pre The iterator range is contiguous. Violating this precondition yields
 | 
			
		||||
    undefined behavior. **This precondition is enforced with an assertion.**
 | 
			
		||||
    @pre Each element in the range has a size of 1 byte. Violating this
 | 
			
		||||
    precondition yields undefined behavior. **This precondition is enforced
 | 
			
		||||
    with a static assertion.**
 | 
			
		||||
 | 
			
		||||
    @warning There is no way to enforce all preconditions at compile-time. If
 | 
			
		||||
             the function is called with noncompliant iterators and with
 | 
			
		||||
             assertions switched off, the behavior is undefined and will most
 | 
			
		||||
             likely yield segmentation violation.
 | 
			
		||||
 | 
			
		||||
    @tparam IteratorType iterator of container with contiguous storage
 | 
			
		||||
    @param[in] first  begin of the range to parse (included)
 | 
			
		||||
    @param[in] last  end of the range to parse (excluded)
 | 
			
		||||
    @param[in] cb  a parser callback function of type @ref parser_callback_t
 | 
			
		||||
    which is used to control the deserialization by filtering unwanted values
 | 
			
		||||
    (optional)
 | 
			
		||||
 | 
			
		||||
    @return result of the deserialization
 | 
			
		||||
 | 
			
		||||
    @complexity Linear in the length of the input. The parser is a predictive
 | 
			
		||||
    LL(1) parser. The complexity can be higher if the parser callback function
 | 
			
		||||
    @a cb has a super-linear complexity.
 | 
			
		||||
 | 
			
		||||
    @note A UTF-8 byte order mark is silently ignored.
 | 
			
		||||
 | 
			
		||||
    @liveexample{The example below demonstrates the `parse()` function reading
 | 
			
		||||
    from an iterator range.,parse__iteratortype__parser_callback_t}
 | 
			
		||||
 | 
			
		||||
    @since version 2.0.3
 | 
			
		||||
    */
 | 
			
		||||
    template<class IteratorType, typename std::enable_if<
 | 
			
		||||
                 std::is_base_of<
 | 
			
		||||
                     std::random_access_iterator_tag,
 | 
			
		||||
                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
 | 
			
		||||
    static basic_json parse(IteratorType first, IteratorType last,
 | 
			
		||||
                            const parser_callback_t cb = nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        // assertion to check that the iterator range is indeed contiguous,
 | 
			
		||||
        // see http://stackoverflow.com/a/35008842/266378 for more discussion
 | 
			
		||||
        assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0),
 | 
			
		||||
                               [&first](std::pair<bool, int> res, decltype(*first) val)
 | 
			
		||||
        {
 | 
			
		||||
            res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
 | 
			
		||||
            return res;
 | 
			
		||||
        }).first);
 | 
			
		||||
 | 
			
		||||
        // assertion to check that each element is 1 byte long
 | 
			
		||||
        static_assert(sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
 | 
			
		||||
                      "each element in the iterator range must have the size of 1 byte");
 | 
			
		||||
 | 
			
		||||
        // if iterator range is empty, create a parser with an empty string
 | 
			
		||||
        // to generate "unexpected EOF" error message
 | 
			
		||||
        if (std::distance(first, last) <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            return parser("").parse();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return parser(first, last, cb).parse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
    @brief deserialize from a container with contiguous storage
 | 
			
		||||
 | 
			
		||||
    This function reads from a container with contiguous storage of 1-byte
 | 
			
		||||
    values. Compatible container types include `std::vector`, `std::string`,
 | 
			
		||||
    `std::array`, and `std::initializer_list`. User-defined containers can be
 | 
			
		||||
    used as long as they implement random-access iterators and a contiguous
 | 
			
		||||
    storage.
 | 
			
		||||
 | 
			
		||||
    @pre The container storage is contiguous. Violating this precondition
 | 
			
		||||
    yields undefined behavior. **This precondition is enforced with an
 | 
			
		||||
    assertion.**
 | 
			
		||||
    @pre Each element of the container has a size of 1 byte. Violating this
 | 
			
		||||
    precondition yields undefined behavior. **This precondition is enforced
 | 
			
		||||
    with a static assertion.**
 | 
			
		||||
 | 
			
		||||
    @warning There is no way to enforce all preconditions at compile-time. If
 | 
			
		||||
             the function is called with a noncompliant container and with
 | 
			
		||||
             assertions switched off, the behavior is undefined and will most
 | 
			
		||||
             likely yield segmentation violation.
 | 
			
		||||
 | 
			
		||||
    @tparam ContiguousContainer container type with contiguous storage
 | 
			
		||||
    @param[in] c  container to read from
 | 
			
		||||
    @param[in] cb  a parser callback function of type @ref parser_callback_t
 | 
			
		||||
    which is used to control the deserialization by filtering unwanted values
 | 
			
		||||
    (optional)
 | 
			
		||||
 | 
			
		||||
    @return result of the deserialization
 | 
			
		||||
 | 
			
		||||
    @complexity Linear in the length of the input. The parser is a predictive
 | 
			
		||||
    LL(1) parser. The complexity can be higher if the parser callback function
 | 
			
		||||
    @a cb has a super-linear complexity.
 | 
			
		||||
 | 
			
		||||
    @note A UTF-8 byte order mark is silently ignored.
 | 
			
		||||
 | 
			
		||||
    @liveexample{The example below demonstrates the `parse()` function reading
 | 
			
		||||
    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
 | 
			
		||||
 | 
			
		||||
    @since version 2.0.3
 | 
			
		||||
    */
 | 
			
		||||
    template<class ContiguousContainer, typename std::enable_if<
 | 
			
		||||
                 not std::is_pointer<ContiguousContainer>::value and
 | 
			
		||||
                 std::is_base_of<
 | 
			
		||||
                     std::random_access_iterator_tag,
 | 
			
		||||
                     typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value
 | 
			
		||||
                 , int>::type = 0>
 | 
			
		||||
    static basic_json parse(const ContiguousContainer& c,
 | 
			
		||||
                            const parser_callback_t cb = nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        // delegate the call to the iterator-range parse overload
 | 
			
		||||
        return parse(std::begin(c), std::end(c), cb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
    @brief deserialize from stream
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -6039,7 +6150,7 @@ class basic_json
 | 
			
		|||
    Returns the type name as string to be used in error messages - usually to
 | 
			
		||||
    indicate that a function was called on a wrong JSON type.
 | 
			
		||||
 | 
			
		||||
    @return basically a string representation of a the @ref m_type member
 | 
			
		||||
    @return basically a string representation of a the @a m_type member
 | 
			
		||||
 | 
			
		||||
    @complexity Constant.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7490,32 +7601,25 @@ class basic_json
 | 
			
		|||
        /// the char type to use in the lexer
 | 
			
		||||
        using lexer_char_t = unsigned char;
 | 
			
		||||
 | 
			
		||||
        /// constructor with a given buffer
 | 
			
		||||
        explicit lexer(const string_t& s) noexcept
 | 
			
		||||
            : m_stream(nullptr), m_buffer(s)
 | 
			
		||||
        /// a lexer from a buffer with given length
 | 
			
		||||
        lexer(const lexer_char_t* buff, const size_t len) noexcept
 | 
			
		||||
            : m_content(buff)
 | 
			
		||||
        {
 | 
			
		||||
            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
 | 
			
		||||
            assert(m_content != nullptr);
 | 
			
		||||
            m_start = m_cursor = m_content;
 | 
			
		||||
            m_limit = m_content + s.size();
 | 
			
		||||
            m_limit = m_content + len;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// constructor with a given stream
 | 
			
		||||
        explicit lexer(std::istream* s) noexcept
 | 
			
		||||
            : m_stream(s), m_buffer()
 | 
			
		||||
        /// a lexer from an input stream
 | 
			
		||||
        explicit lexer(std::istream& s)
 | 
			
		||||
            : m_stream(&s), m_line_buffer()
 | 
			
		||||
        {
 | 
			
		||||
            assert(m_stream != nullptr);
 | 
			
		||||
            std::getline(*m_stream, m_buffer);
 | 
			
		||||
            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
 | 
			
		||||
            assert(m_content != nullptr);
 | 
			
		||||
            m_start = m_cursor = m_content;
 | 
			
		||||
            m_limit = m_content + m_buffer.size();
 | 
			
		||||
            // fill buffer
 | 
			
		||||
            fill_line_buffer();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// default constructor
 | 
			
		||||
        lexer() = default;
 | 
			
		||||
 | 
			
		||||
        // switch off unwanted functions
 | 
			
		||||
        // switch off unwanted functions (due to pointer members)
 | 
			
		||||
        lexer() = delete;
 | 
			
		||||
        lexer(const lexer&) = delete;
 | 
			
		||||
        lexer operator=(const lexer&) = delete;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7668,7 +7772,7 @@ class basic_json
 | 
			
		|||
        infinite sequence of whitespace or byte-order-marks. This contradicts
 | 
			
		||||
        the assumption of finite input, q.e.d.
 | 
			
		||||
        */
 | 
			
		||||
        token_type scan() noexcept
 | 
			
		||||
        token_type scan()
 | 
			
		||||
        {
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -7684,7 +7788,7 @@ class basic_json
 | 
			
		|||
                    re2c:define:YYCURSOR  = m_cursor;
 | 
			
		||||
                    re2c:define:YYLIMIT   = m_limit;
 | 
			
		||||
                    re2c:define:YYMARKER  = m_marker;
 | 
			
		||||
                    re2c:define:YYFILL    = "yyfill()";
 | 
			
		||||
                    re2c:define:YYFILL    = "fill_line_buffer()";
 | 
			
		||||
                    re2c:yyfill:parameter = 0;
 | 
			
		||||
                    re2c:indent:string    = "    ";
 | 
			
		||||
                    re2c:indent:top       = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -7747,30 +7851,76 @@ class basic_json
 | 
			
		|||
            return last_token_type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// append data from the stream to the internal buffer
 | 
			
		||||
        void yyfill() noexcept
 | 
			
		||||
        {
 | 
			
		||||
            if (m_stream == nullptr or not * m_stream)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        /*!
 | 
			
		||||
        @brief append data from the stream to the line buffer
 | 
			
		||||
 | 
			
		||||
        This function is called by the scan() function when the end of the
 | 
			
		||||
        buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
 | 
			
		||||
        incremented without leaving the limits of the line buffer. Note re2c
 | 
			
		||||
        decides when to call this function.
 | 
			
		||||
 | 
			
		||||
        If the lexer reads from contiguous storage, there is no trailing null
 | 
			
		||||
        byte. Therefore, this function must make sure to add these padding
 | 
			
		||||
        null bytes.
 | 
			
		||||
 | 
			
		||||
        If the lexer reads from an input stream, this function reads the next
 | 
			
		||||
        line of the input.
 | 
			
		||||
 | 
			
		||||
        @pre
 | 
			
		||||
            p p p p p p u u u u u x . . . . . .
 | 
			
		||||
            ^           ^       ^   ^
 | 
			
		||||
            m_content   m_start |   m_limit
 | 
			
		||||
                                m_cursor
 | 
			
		||||
 | 
			
		||||
        @post
 | 
			
		||||
            u u u u u x x x x x x x . . . . . .
 | 
			
		||||
            ^       ^               ^
 | 
			
		||||
            |       m_cursor        m_limit
 | 
			
		||||
            m_start
 | 
			
		||||
            m_content
 | 
			
		||||
        */
 | 
			
		||||
        void fill_line_buffer()
 | 
			
		||||
        {
 | 
			
		||||
            // number of processed characters (p)
 | 
			
		||||
            const auto offset_start = m_start - m_content;
 | 
			
		||||
            const auto offset_marker = m_marker - m_start;
 | 
			
		||||
            // offset for m_marker wrt. to m_start
 | 
			
		||||
            const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start;
 | 
			
		||||
            // number of unprocessed characters (u)
 | 
			
		||||
            const auto offset_cursor = m_cursor - m_start;
 | 
			
		||||
 | 
			
		||||
            m_buffer.erase(0, static_cast<size_t>(offset_start));
 | 
			
		||||
            std::string line;
 | 
			
		||||
            assert(m_stream != nullptr);
 | 
			
		||||
            std::getline(*m_stream, line);
 | 
			
		||||
            m_buffer += "\n" + line; // add line with newline symbol
 | 
			
		||||
            // no stream is used or end of file is reached
 | 
			
		||||
            if (m_stream == nullptr or not * m_stream)
 | 
			
		||||
            {
 | 
			
		||||
                // copy unprocessed characters to line buffer
 | 
			
		||||
                m_line_buffer.clear();
 | 
			
		||||
                for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor)
 | 
			
		||||
                {
 | 
			
		||||
                    m_line_buffer.append(1, static_cast<const char>(*m_cursor));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
 | 
			
		||||
                // append 5 characters (size of longest keyword "false") to
 | 
			
		||||
                // make sure that there is sufficient space between m_cursor
 | 
			
		||||
                // and m_limit
 | 
			
		||||
                m_line_buffer.append(5, '\0');
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // delete processed characters from line buffer
 | 
			
		||||
                m_line_buffer.erase(0, static_cast<size_t>(offset_start));
 | 
			
		||||
                // read next line from input stream
 | 
			
		||||
                std::string line;
 | 
			
		||||
                std::getline(*m_stream, line);
 | 
			
		||||
                // add line with newline symbol to the line buffer
 | 
			
		||||
                m_line_buffer += "\n" + line;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // set pointers
 | 
			
		||||
            m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str());
 | 
			
		||||
            assert(m_content != nullptr);
 | 
			
		||||
            m_start  = m_content;
 | 
			
		||||
            m_marker = m_start + offset_marker;
 | 
			
		||||
            m_cursor = m_start + offset_cursor;
 | 
			
		||||
            m_limit  = m_start + m_buffer.size() - 1;
 | 
			
		||||
            m_limit  = m_start + m_line_buffer.size();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// return string representation of last read token
 | 
			
		||||
| 
						 | 
				
			
			@ -8112,8 +8262,8 @@ class basic_json
 | 
			
		|||
      private:
 | 
			
		||||
        /// optional input stream
 | 
			
		||||
        std::istream* m_stream = nullptr;
 | 
			
		||||
        /// the buffer
 | 
			
		||||
        string_t m_buffer;
 | 
			
		||||
        /// line buffer buffer for m_stream
 | 
			
		||||
        string_t m_line_buffer {};
 | 
			
		||||
        /// the buffer pointer
 | 
			
		||||
        const lexer_char_t* m_content = nullptr;
 | 
			
		||||
        /// pointer to the beginning of the current symbol
 | 
			
		||||
| 
						 | 
				
			
			@ -8136,25 +8286,34 @@ class basic_json
 | 
			
		|||
    class parser
 | 
			
		||||
    {
 | 
			
		||||
      public:
 | 
			
		||||
        /// constructor for strings
 | 
			
		||||
        parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
 | 
			
		||||
            : callback(cb), m_lexer(s)
 | 
			
		||||
        {
 | 
			
		||||
            // read first token
 | 
			
		||||
            get_token();
 | 
			
		||||
        }
 | 
			
		||||
        /// a parser reading from a string literal
 | 
			
		||||
        parser(const char* buff, const parser_callback_t cb = nullptr)
 | 
			
		||||
            : callback(cb),
 | 
			
		||||
              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), strlen(buff))
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        /// a parser reading from an input stream
 | 
			
		||||
        parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
 | 
			
		||||
            : callback(cb), m_lexer(&_is)
 | 
			
		||||
        {
 | 
			
		||||
            // read first token
 | 
			
		||||
            get_token();
 | 
			
		||||
        }
 | 
			
		||||
        parser(std::istream& is, const parser_callback_t cb = nullptr)
 | 
			
		||||
            : callback(cb), m_lexer(is)
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        /// a parser reading from an iterator range with contiguous storage
 | 
			
		||||
        template<class IteratorType, typename std::enable_if<
 | 
			
		||||
                     std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
 | 
			
		||||
                     , int>::type
 | 
			
		||||
                 = 0>
 | 
			
		||||
        parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
 | 
			
		||||
            : callback(cb),
 | 
			
		||||
              m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
 | 
			
		||||
                      static_cast<size_t>(std::distance(first, last)))
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        /// public parser interface
 | 
			
		||||
        basic_json parse()
 | 
			
		||||
        {
 | 
			
		||||
            // read first token
 | 
			
		||||
            get_token();
 | 
			
		||||
 | 
			
		||||
            basic_json result = parse_internal(true);
 | 
			
		||||
            result.assert_invariant();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -8361,7 +8520,7 @@ class basic_json
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        /// get next token from lexer
 | 
			
		||||
        typename lexer::token_type get_token() noexcept
 | 
			
		||||
        typename lexer::token_type get_token()
 | 
			
		||||
        {
 | 
			
		||||
            last_token = m_lexer.scan();
 | 
			
		||||
            return last_token;
 | 
			
		||||
| 
						 | 
				
			
			@ -9659,7 +9818,7 @@ namespace std
 | 
			
		|||
 | 
			
		||||
@since version 1.0.0
 | 
			
		||||
*/
 | 
			
		||||
template <>
 | 
			
		||||
template<>
 | 
			
		||||
inline void swap(nlohmann::json& j1,
 | 
			
		||||
                 nlohmann::json& j2) noexcept(
 | 
			
		||||
                     is_nothrow_move_constructible<nlohmann::json>::value and
 | 
			
		||||
| 
						 | 
				
			
			@ -9670,7 +9829,7 @@ inline void swap(nlohmann::json& j1,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/// hash value for JSON objects
 | 
			
		||||
template <>
 | 
			
		||||
template<>
 | 
			
		||||
struct hash<nlohmann::json>
 | 
			
		||||
{
 | 
			
		||||
    /*!
 | 
			
		||||
| 
						 | 
				
			
			@ -9701,7 +9860,7 @@ if no parse error occurred.
 | 
			
		|||
*/
 | 
			
		||||
inline nlohmann::json operator "" _json(const char* s, std::size_t)
 | 
			
		||||
{
 | 
			
		||||
    return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
 | 
			
		||||
    return nlohmann::json::parse(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,43 +38,67 @@ TEST_CASE("lexer class")
 | 
			
		|||
    {
 | 
			
		||||
        SECTION("structural characters")
 | 
			
		||||
        {
 | 
			
		||||
            CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array);
 | 
			
		||||
            CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array);
 | 
			
		||||
            CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object);
 | 
			
		||||
            CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object);
 | 
			
		||||
            CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator);
 | 
			
		||||
            CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::begin_array);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::end_array);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::begin_object);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::end_object);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_separator);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::name_separator);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("literal names")
 | 
			
		||||
        {
 | 
			
		||||
            CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null);
 | 
			
		||||
            CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true);
 | 
			
		||||
            CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
 | 
			
		||||
                              4).scan() == json::lexer::token_type::literal_null);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
 | 
			
		||||
                              4).scan() == json::lexer::token_type::literal_true);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
 | 
			
		||||
                              5).scan() == json::lexer::token_type::literal_false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("numbers")
 | 
			
		||||
        {
 | 
			
		||||
            CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::value_number);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("whitespace")
 | 
			
		||||
        {
 | 
			
		||||
            // result is end_of_input, because not token is following
 | 
			
		||||
            CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
 | 
			
		||||
                              1).scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
            CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
 | 
			
		||||
                              7).scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +124,11 @@ TEST_CASE("lexer class")
 | 
			
		|||
    {
 | 
			
		||||
        for (int c = 1; c < 128; ++c)
 | 
			
		||||
        {
 | 
			
		||||
            auto s = std::string(1, c);
 | 
			
		||||
            // create string from the ASCII code
 | 
			
		||||
            const auto s = std::string(1, c);
 | 
			
		||||
            // store scan() result
 | 
			
		||||
            const auto res = json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(s.c_str()),
 | 
			
		||||
                                         1).scan();
 | 
			
		||||
 | 
			
		||||
            switch (c)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +150,7 @@ TEST_CASE("lexer class")
 | 
			
		|||
                case ('8'):
 | 
			
		||||
                case ('9'):
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error);
 | 
			
		||||
                    CHECK(res != json::lexer::token_type::parse_error);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -132,14 +160,14 @@ TEST_CASE("lexer class")
 | 
			
		|||
                case ('\n'):
 | 
			
		||||
                case ('\r'):
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input);
 | 
			
		||||
                    CHECK(res == json::lexer::token_type::end_of_input);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // anything else is not expected
 | 
			
		||||
                default:
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error);
 | 
			
		||||
                    CHECK(res == json::lexer::token_type::parse_error);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,8 @@ SOFTWARE.
 | 
			
		|||
#include "json.hpp"
 | 
			
		||||
using nlohmann::json;
 | 
			
		||||
 | 
			
		||||
#include <valarray>
 | 
			
		||||
 | 
			
		||||
TEST_CASE("parser class")
 | 
			
		||||
{
 | 
			
		||||
    SECTION("parse")
 | 
			
		||||
| 
						 | 
				
			
			@ -487,7 +489,7 @@ TEST_CASE("parser class")
 | 
			
		|||
                case ('r'):
 | 
			
		||||
                case ('t'):
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s.c_str()).parse());
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -500,8 +502,8 @@ TEST_CASE("parser class")
 | 
			
		|||
                // any other combination of backslash and character is invalid
 | 
			
		||||
                default:
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s.c_str()).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s.c_str()).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -559,22 +561,22 @@ TEST_CASE("parser class")
 | 
			
		|||
 | 
			
		||||
                if (valid(c))
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s1).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s2).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s3).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s4).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s1.c_str()).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s2.c_str()).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s3.c_str()).parse());
 | 
			
		||||
                    CHECK_NOTHROW(json::parser(s4.c_str()).parse());
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s1.c_str()).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), std::invalid_argument);
 | 
			
		||||
                    CHECK_THROWS_AS(json::parser(s4.c_str()).parse(), std::invalid_argument);
 | 
			
		||||
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s1.c_str()).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                    CHECK_THROWS_WITH(json::parser(s4.c_str()).parse(), "parse error - unexpected '\"'");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -755,11 +757,47 @@ TEST_CASE("parser class")
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("copy constructor")
 | 
			
		||||
    SECTION("constructing from contiguous containers")
 | 
			
		||||
    {
 | 
			
		||||
        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}));
 | 
			
		||||
        SECTION("from std::vector")
 | 
			
		||||
        {
 | 
			
		||||
            std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("from std::array")
 | 
			
		||||
        {
 | 
			
		||||
            std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
 | 
			
		||||
            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("from array")
 | 
			
		||||
        {
 | 
			
		||||
            uint8_t v[] = {'t', 'r', 'u', 'e'};
 | 
			
		||||
            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("from char literal")
 | 
			
		||||
        {
 | 
			
		||||
            CHECK(json::parser("true").parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("from std::string")
 | 
			
		||||
        {
 | 
			
		||||
            std::string v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("from std::initializer_list")
 | 
			
		||||
        {
 | 
			
		||||
            std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("from std::valarray")
 | 
			
		||||
        {
 | 
			
		||||
            std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
            CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,8 @@ SOFTWARE.
 | 
			
		|||
#include "json.hpp"
 | 
			
		||||
using nlohmann::json;
 | 
			
		||||
 | 
			
		||||
#include <valarray>
 | 
			
		||||
 | 
			
		||||
TEST_CASE("deserialization")
 | 
			
		||||
{
 | 
			
		||||
    SECTION("successful deserialization")
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +45,14 @@ TEST_CASE("deserialization")
 | 
			
		|||
            CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("string")
 | 
			
		||||
        SECTION("string literal")
 | 
			
		||||
        {
 | 
			
		||||
            auto s = "[\"foo\",1,2,3,false,{\"one\":1}]";
 | 
			
		||||
            json j = json::parse(s);
 | 
			
		||||
            CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("string_t")
 | 
			
		||||
        {
 | 
			
		||||
            json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}]";
 | 
			
		||||
            json j = json::parse(s);
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +83,7 @@ TEST_CASE("deserialization")
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("successful deserialization")
 | 
			
		||||
    SECTION("unsuccessful deserialization")
 | 
			
		||||
    {
 | 
			
		||||
        SECTION("stream")
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -116,4 +125,103 @@ TEST_CASE("deserialization")
 | 
			
		|||
                              "parse error - unexpected end of input; expected ']'");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("contiguous containers")
 | 
			
		||||
    {
 | 
			
		||||
        SECTION("directly")
 | 
			
		||||
        {
 | 
			
		||||
            SECTION("from std::vector")
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(v) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::array")
 | 
			
		||||
            {
 | 
			
		||||
                std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
 | 
			
		||||
                CHECK(json::parse(v) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from array")
 | 
			
		||||
            {
 | 
			
		||||
                uint8_t v[] = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(v) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from chars")
 | 
			
		||||
            {
 | 
			
		||||
                uint8_t *v = new uint8_t[5];
 | 
			
		||||
                v[0] = 't';
 | 
			
		||||
                v[1] = 'r';
 | 
			
		||||
                v[2] = 'u';
 | 
			
		||||
                v[3] = 'e';
 | 
			
		||||
                v[4] = '\0';
 | 
			
		||||
                CHECK(json::parse(v) == json(true));
 | 
			
		||||
                delete[] v;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::string")
 | 
			
		||||
            {
 | 
			
		||||
                std::string v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(v) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::initializer_list")
 | 
			
		||||
            {
 | 
			
		||||
                std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(v) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("empty container")
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<uint8_t> v;
 | 
			
		||||
                CHECK_THROWS_AS(json::parse(v), std::invalid_argument);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SECTION("via iterator range")
 | 
			
		||||
        {
 | 
			
		||||
            SECTION("from std::vector")
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::array")
 | 
			
		||||
            {
 | 
			
		||||
                std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
 | 
			
		||||
                CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from array")
 | 
			
		||||
            {
 | 
			
		||||
                uint8_t v[] = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::string")
 | 
			
		||||
            {
 | 
			
		||||
                std::string v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::initializer_list")
 | 
			
		||||
            {
 | 
			
		||||
                std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("from std::valarray")
 | 
			
		||||
            {
 | 
			
		||||
                std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'};
 | 
			
		||||
                CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SECTION("with empty range")
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<uint8_t> v;
 | 
			
		||||
                CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue