Merge branch 'feature/iterator_range_parsing' into develop

This commit is contained in:
Niels 2016-08-31 20:23:24 +02:00
commit 776880bc49
18 changed files with 1075 additions and 498 deletions

View file

@ -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. 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 ```sh
make make check
```
and can be executed with
```sh
./json_unit
``` ```
The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request. The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request.

View file

@ -492,6 +492,8 @@ I deeply appreciate the help of the following people.
- [Mário Feroldi](https://github.com/thelostt) fixed a small typo. - [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. - [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. - [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! Thanks a lot for helping out!
@ -510,7 +512,7 @@ To compile and run the tests, you need to execute
$ make check $ 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). For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).

View 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";
}

View file

@ -0,0 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/CwZnqGqte14SYJ5s"><b>online</b></a>

View 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
}
}

View 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);
std::cout << std::setw(4) << j_complete << "\n\n";
}

View file

@ -0,0 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/F8VaVFyys87qQRt5"><b>online</b></a>

View file

@ -0,0 +1,6 @@
[
1,
2,
3
]

View 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";
}

View file

@ -0,0 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/ojh4Eeol4G9RgeRV"><b>online</b></a>

View file

@ -0,0 +1,6 @@
[
1,
2,
3
]

View file

@ -5,7 +5,7 @@ using json = nlohmann::json;
int main() int main()
{ {
// a JSON text // a JSON text
std::string text = R"( auto text = R"(
{ {
"Image": { "Image": {
"Width": 800, "Width": 800,
@ -44,4 +44,4 @@ int main()
// parse (with callback) and serialize JSON // parse (with callback) and serialize JSON
json j_filtered = json::parse(text, cb); json j_filtered = json::parse(text, cb);
std::cout << std::setw(4) << j_filtered << '\n'; std::cout << std::setw(4) << j_filtered << '\n';
} }

View file

@ -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>

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,7 @@ SOFTWARE.
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <iomanip> #include <iomanip>
@ -959,7 +960,7 @@ class basic_json
With a parser callback function, the result of parsing a JSON text can be With a parser callback function, the result of parsing a JSON text can be
influenced. When passed to @ref parse(std::istream&, const 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 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 event) with a set recursion depth @a depth and context JSON value
@a parsed. The return value of the callback function is a boolean @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. skipped completely or replaced by an empty discarded object.
@sa @ref parse(std::istream&, parser_callback_t) or @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 @since version 1.0.0
*/ */
@ -1140,11 +1141,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template <class CompatibleObjectType, typename template<class CompatibleObjectType, typename std::enable_if<
std::enable_if< std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
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>
std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
= 0>
basic_json(const CompatibleObjectType& val) basic_json(const CompatibleObjectType& val)
: m_type(value_t::object) : m_type(value_t::object)
{ {
@ -1205,16 +1204,14 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template <class CompatibleArrayType, typename template<class CompatibleArrayType, typename std::enable_if<
std::enable_if< not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
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::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::reverse_iterator>::value and not std::is_same<CompatibleArrayType, typename basic_json_t::const_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::iterator>::value and not std::is_same<CompatibleArrayType, typename array_t::const_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>
std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
= 0>
basic_json(const CompatibleArrayType& val) basic_json(const CompatibleArrayType& val)
: m_type(value_t::array) : m_type(value_t::array)
{ {
@ -1300,10 +1297,8 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template <class CompatibleStringType, typename template<class CompatibleStringType, typename std::enable_if<
std::enable_if< std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
std::is_constructible<string_t, CompatibleStringType>::value, int>::type
= 0>
basic_json(const CompatibleStringType& val) basic_json(const CompatibleStringType& val)
: basic_json(string_t(val)) : basic_json(string_t(val))
{ {
@ -1353,12 +1348,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename T, template<typename T, typename std::enable_if<
typename std::enable_if< not (std::is_same<T, int>::value) and
not (std::is_same<T, int>::value) std::is_same<T, number_integer_t>::value, int>::type = 0>
and std::is_same<T, number_integer_t>::value
, int>::type
= 0>
basic_json(const number_integer_t val) noexcept basic_json(const number_integer_t val) noexcept
: m_type(value_t::number_integer), m_value(val) : m_type(value_t::number_integer), m_value(val)
{ {
@ -1422,13 +1414,11 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename CompatibleNumberIntegerType, typename template<typename CompatibleNumberIntegerType, typename std::enable_if<
std::enable_if<
std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
std::numeric_limits<CompatibleNumberIntegerType>::is_integer and std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
std::numeric_limits<CompatibleNumberIntegerType>::is_signed, std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
CompatibleNumberIntegerType>::type CompatibleNumberIntegerType>::type = 0>
= 0>
basic_json(const CompatibleNumberIntegerType val) noexcept basic_json(const CompatibleNumberIntegerType val) noexcept
: m_type(value_t::number_integer), : m_type(value_t::number_integer),
m_value(static_cast<number_integer_t>(val)) m_value(static_cast<number_integer_t>(val))
@ -1453,12 +1443,9 @@ class basic_json
@since version 2.0.0 @since version 2.0.0
*/ */
template<typename T, template<typename T, typename std::enable_if<
typename std::enable_if< not (std::is_same<T, int>::value) and
not (std::is_same<T, int>::value) std::is_same<T, number_unsigned_t>::value, int>::type = 0>
and std::is_same<T, number_unsigned_t>::value
, int>::type
= 0>
basic_json(const number_unsigned_t val) noexcept basic_json(const number_unsigned_t val) noexcept
: m_type(value_t::number_unsigned), m_value(val) : m_type(value_t::number_unsigned), m_value(val)
{ {
@ -1485,13 +1472,11 @@ class basic_json
@since version 2.0.0 @since version 2.0.0
*/ */
template <typename CompatibleNumberUnsignedType, typename template<typename CompatibleNumberUnsignedType, typename std::enable_if <
std::enable_if < std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, CompatibleNumberUnsignedType>::type = 0>
CompatibleNumberUnsignedType>::type
= 0>
basic_json(const CompatibleNumberUnsignedType val) noexcept basic_json(const CompatibleNumberUnsignedType val) noexcept
: m_type(value_t::number_unsigned), : m_type(value_t::number_unsigned),
m_value(static_cast<number_unsigned_t>(val)) m_value(static_cast<number_unsigned_t>(val))
@ -1567,11 +1552,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename CompatibleNumberFloatType, typename = typename template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
std::enable_if<
std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and 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(const CompatibleNumberFloatType val) noexcept
: basic_json(number_float_t(val)) : basic_json(number_float_t(val))
{ {
@ -1838,12 +1821,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template <class InputIT, typename template<class InputIT, typename std::enable_if<
std::enable_if< std::is_same<InputIT, typename basic_json_t::iterator>::value or
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>
std::is_same<InputIT, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
basic_json(InputIT first, InputIT last) basic_json(InputIT first, InputIT last)
{ {
assert(first.m_object != nullptr); assert(first.m_object != nullptr);
@ -2601,11 +2581,9 @@ class basic_json
////////////////// //////////////////
/// get an object (explicit) /// get an object (explicit)
template <class T, typename template<class T, typename std::enable_if<
std::enable_if< std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
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>
std::is_convertible<basic_json_t, typename T::mapped_type>::value
, int>::type = 0>
T get_impl(T*) const T get_impl(T*) const
{ {
if (is_object()) if (is_object())
@ -2632,14 +2610,12 @@ class basic_json
} }
/// get an array (explicit) /// get an array (explicit)
template <class T, typename template<class T, typename std::enable_if<
std::enable_if< std::is_convertible<basic_json_t, typename T::value_type>::value and
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_same<basic_json_t, typename T::value_type>::value and not std::is_arithmetic<T>::value and
not std::is_arithmetic<T>::value and not std::is_convertible<std::string, T>::value and
not std::is_convertible<std::string, T>::value and not has_mapped_type<T>::value, int>::type = 0>
not has_mapped_type<T>::value
, int>::type = 0>
T get_impl(T*) const T get_impl(T*) const
{ {
if (is_array()) if (is_array())
@ -2659,11 +2635,9 @@ class basic_json
} }
/// get an array (explicit) /// get an array (explicit)
template <class T, typename template<class T, typename std::enable_if<
std::enable_if< std::is_convertible<basic_json_t, T>::value and
std::is_convertible<basic_json_t, T>::value and not std::is_same<basic_json_t, T>::value, int>::type = 0>
not std::is_same<basic_json_t, T>::value
, int>::type = 0>
std::vector<T> get_impl(std::vector<T>*) const std::vector<T> get_impl(std::vector<T>*) const
{ {
if (is_array()) if (is_array())
@ -2684,11 +2658,9 @@ class basic_json
} }
/// get an array (explicit) /// get an array (explicit)
template <class T, typename template<class T, typename std::enable_if<
std::enable_if< std::is_same<basic_json, typename T::value_type>::value and
std::is_same<basic_json, typename T::value_type>::value and not has_mapped_type<T>::value, int>::type = 0>
not has_mapped_type<T>::value
, int>::type = 0>
T get_impl(T*) const T get_impl(T*) const
{ {
if (is_array()) if (is_array())
@ -2715,10 +2687,8 @@ class basic_json
} }
/// get a string (explicit) /// get a string (explicit)
template <typename T, typename template<typename T, typename std::enable_if<
std::enable_if< std::is_convertible<string_t, T>::value, int>::type = 0>
std::is_convertible<string_t, T>::value
, int>::type = 0>
T get_impl(T*) const T get_impl(T*) const
{ {
if (is_string()) if (is_string())
@ -2732,10 +2702,8 @@ class basic_json
} }
/// get a number (explicit) /// get a number (explicit)
template<typename T, typename template<typename T, typename std::enable_if<
std::enable_if< std::is_arithmetic<T>::value, int>::type = 0>
std::is_arithmetic<T>::value
, int>::type = 0>
T get_impl(T*) const T get_impl(T*) const
{ {
switch (m_type) switch (m_type)
@ -2924,10 +2892,8 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename ValueType, typename template<typename ValueType, typename std::enable_if<
std::enable_if< not std::is_pointer<ValueType>::value, int>::type = 0>
not std::is_pointer<ValueType>::value
, int>::type = 0>
ValueType get() const ValueType get() const
{ {
return get_impl(static_cast<ValueType*>(nullptr)); return get_impl(static_cast<ValueType*>(nullptr));
@ -2960,10 +2926,8 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename PointerType, typename template<typename PointerType, typename std::enable_if<
std::enable_if< std::is_pointer<PointerType>::value, int>::type = 0>
std::is_pointer<PointerType>::value
, int>::type = 0>
PointerType get() noexcept PointerType get() noexcept
{ {
// delegate the call to get_ptr // delegate the call to get_ptr
@ -2974,10 +2938,8 @@ class basic_json
@brief get a pointer value (explicit) @brief get a pointer value (explicit)
@copydoc get() @copydoc get()
*/ */
template<typename PointerType, typename template<typename PointerType, typename std::enable_if<
std::enable_if< std::is_pointer<PointerType>::value, int>::type = 0>
std::is_pointer<PointerType>::value
, int>::type = 0>
constexpr const PointerType get() const noexcept constexpr const PointerType get() const noexcept
{ {
// delegate the call to get_ptr // delegate the call to get_ptr
@ -3010,10 +2972,8 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename PointerType, typename template<typename PointerType, typename std::enable_if<
std::enable_if< std::is_pointer<PointerType>::value, int>::type = 0>
std::is_pointer<PointerType>::value
, int>::type = 0>
PointerType get_ptr() noexcept PointerType get_ptr() noexcept
{ {
// get the type of the PointerType (remove pointer and const) // get the type of the PointerType (remove pointer and const)
@ -3039,11 +2999,9 @@ class basic_json
@brief get a pointer value (implicit) @brief get a pointer value (implicit)
@copydoc get_ptr() @copydoc get_ptr()
*/ */
template<typename PointerType, typename template<typename PointerType, typename std::enable_if<
std::enable_if< std::is_pointer<PointerType>::value and
std::is_pointer<PointerType>::value std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
and std::is_const<typename std::remove_pointer<PointerType>::type>::value
, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept constexpr const PointerType get_ptr() const noexcept
{ {
// get the type of the PointerType (remove pointer and const) // get the type of the PointerType (remove pointer and const)
@ -3091,10 +3049,8 @@ class basic_json
@since version 1.1.0 @since version 1.1.0
*/ */
template<typename ReferenceType, typename template<typename ReferenceType, typename std::enable_if<
std::enable_if< std::is_reference<ReferenceType>::value, int>::type = 0>
std::is_reference<ReferenceType>::value
, int>::type = 0>
ReferenceType get_ref() ReferenceType get_ref()
{ {
// delegate call to get_ref_impl // delegate call to get_ref_impl
@ -3105,11 +3061,9 @@ class basic_json
@brief get a reference value (implicit) @brief get a reference value (implicit)
@copydoc get_ref() @copydoc get_ref()
*/ */
template<typename ReferenceType, typename template<typename ReferenceType, typename std::enable_if<
std::enable_if< std::is_reference<ReferenceType>::value and
std::is_reference<ReferenceType>::value std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
and std::is_const<typename std::remove_reference<ReferenceType>::type>::value
, int>::type = 0>
ReferenceType get_ref() const ReferenceType get_ref() const
{ {
// delegate call to get_ref_impl // delegate call to get_ref_impl
@ -3144,10 +3098,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template < typename ValueType, typename template < typename ValueType, typename std::enable_if <
std::enable_if < not std::is_pointer<ValueType>::value and
not std::is_pointer<ValueType>::value not std::is_same<ValueType, typename string_t::value_type>::value
and not std::is_same<ValueType, typename string_t::value_type>::value
#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 #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 and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif #endif
@ -3737,10 +3690,8 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template <class ValueType, typename template<class ValueType, typename std::enable_if<
std::enable_if< std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
std::is_convertible<basic_json_t, ValueType>::value
, int>::type = 0>
ValueType value(const typename object_t::key_type& key, ValueType default_value) const ValueType value(const typename object_t::key_type& key, ValueType default_value) const
{ {
// at only works for objects // at only works for objects
@ -3813,10 +3764,8 @@ class basic_json
@since version 2.0.2 @since version 2.0.2
*/ */
template <class ValueType, typename template<class ValueType, typename std::enable_if<
std::enable_if< std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
std::is_convertible<basic_json_t, ValueType>::value
, int>::type = 0>
ValueType value(const json_pointer& ptr, ValueType default_value) const ValueType value(const json_pointer& ptr, ValueType default_value) const
{ {
// at only works for objects // at only works for objects
@ -3946,7 +3895,7 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a @return Iterator following the last removed element. If the iterator @a
pos refers to the last element, the `end()` iterator is returned. 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 @post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator. erase, including the `end()` iterator.
@ -3968,7 +3917,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON @liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType} types.,erase__IteratorType}
@sa @ref erase(InteratorType, InteratorType) -- removes the elements in @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element @sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key from an object at the given key
@ -3977,13 +3926,11 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template <class InteratorType, typename template<class IteratorType, typename std::enable_if<
std::enable_if< std::is_same<IteratorType, typename basic_json_t::iterator>::value or
std::is_same<InteratorType, typename basic_json_t::iterator>::value or std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
std::is_same<InteratorType, typename basic_json_t::const_iterator>::value = 0>
, int>::type IteratorType erase(IteratorType pos)
= 0>
InteratorType erase(InteratorType pos)
{ {
// make sure iterator fits the current value // make sure iterator fits the current value
if (this != pos.m_object) if (this != pos.m_object)
@ -3991,7 +3938,7 @@ class basic_json
throw std::domain_error("iterator does not fit current value"); throw std::domain_error("iterator does not fit current value");
} }
InteratorType result = end(); IteratorType result = end();
switch (m_type) switch (m_type)
{ {
@ -4055,7 +4002,7 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a @return Iterator following the last removed element. If the iterator @a
second refers to the last element, the `end()` iterator is returned. second refers to the last element, the `end()` iterator is returned.
@tparam InteratorType an @ref iterator or @ref const_iterator @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the @post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator. erase, including the `end()` iterator.
@ -4078,7 +4025,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON @liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType} 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 @sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key from an object at the given key
@sa @ref erase(const size_type) -- removes the element from an array at @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 @since version 1.0.0
*/ */
template <class InteratorType, typename template<class IteratorType, typename std::enable_if<
std::enable_if< std::is_same<IteratorType, typename basic_json_t::iterator>::value or
std::is_same<InteratorType, typename basic_json_t::iterator>::value or std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
std::is_same<InteratorType, typename basic_json_t::const_iterator>::value = 0>
, int>::type IteratorType erase(IteratorType first, IteratorType last)
= 0>
InteratorType erase(InteratorType first, InteratorType last)
{ {
// make sure iterator fits the current value // make sure iterator fits the current value
if (this != first.m_object or this != last.m_object) 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"); throw std::domain_error("iterators do not fit current value");
} }
InteratorType result = end(); IteratorType result = end();
switch (m_type) switch (m_type)
{ {
@ -4172,8 +4117,8 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__key_type} @liveexample{The example shows the effect of `erase()`.,erase__key_type}
@sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(InteratorType, InteratorType) -- removes the elements in @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range the given range
@sa @ref erase(const size_type) -- removes the element from an array at @sa @ref erase(const size_type) -- removes the element from an array at
the given index the given index
@ -4209,8 +4154,8 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__size_type} @liveexample{The example shows the effect of `erase()`.,erase__size_type}
@sa @ref erase(InteratorType) -- removes the element at a given position @sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(InteratorType, InteratorType) -- removes the elements in @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element @sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key 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 This function reads from an array of 1-byte values.
@param[in] cb a parser callback function of type @ref parser_callback_t
@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 which is used to control the deserialization by filtering unwanted values
(optional) (optional)
@ -5933,18 +5884,54 @@ class basic_json
@note A UTF-8 byte order mark is silently ignored. @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 @liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__string__parser_callback_t} and without callback function.,parse__string__parser_callback_t}
@sa @ref parse(std::istream&, const parser_callback_t) for a version that @sa @ref parse(std::istream&, const parser_callback_t) for a version that
reads from an input stream 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) 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 @liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__istream__parser_callback_t} 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 that reads from a string
@since version 1.0.0 @since version 1.0.0
@ -5986,6 +5973,130 @@ class basic_json
return parser(i, cb).parse(); 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 @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 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. 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. @complexity Constant.
@ -7490,32 +7601,25 @@ class basic_json
/// the char type to use in the lexer /// the char type to use in the lexer
using lexer_char_t = unsigned char; using lexer_char_t = unsigned char;
/// constructor with a given buffer /// a lexer from a buffer with given length
explicit lexer(const string_t& s) noexcept lexer(const lexer_char_t* buff, const size_t len) noexcept
: m_stream(nullptr), m_buffer(s) : m_content(buff)
{ {
m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
assert(m_content != nullptr); assert(m_content != nullptr);
m_start = m_cursor = m_content; m_start = m_cursor = m_content;
m_limit = m_content + s.size(); m_limit = m_content + len;
} }
/// constructor with a given stream /// a lexer from an input stream
explicit lexer(std::istream* s) noexcept explicit lexer(std::istream& s)
: m_stream(s), m_buffer() : m_stream(&s), m_line_buffer()
{ {
assert(m_stream != nullptr); // fill buffer
std::getline(*m_stream, m_buffer); fill_line_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();
} }
/// default constructor // switch off unwanted functions (due to pointer members)
lexer() = default; lexer() = delete;
// switch off unwanted functions
lexer(const lexer&) = delete; lexer(const lexer&) = delete;
lexer operator=(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 infinite sequence of whitespace or byte-order-marks. This contradicts
the assumption of finite input, q.e.d. the assumption of finite input, q.e.d.
*/ */
token_type scan() noexcept token_type scan()
{ {
while (true) while (true)
{ {
@ -7684,7 +7788,7 @@ class basic_json
re2c:define:YYCURSOR = m_cursor; re2c:define:YYCURSOR = m_cursor;
re2c:define:YYLIMIT = m_limit; re2c:define:YYLIMIT = m_limit;
re2c:define:YYMARKER = m_marker; re2c:define:YYMARKER = m_marker;
re2c:define:YYFILL = "yyfill()"; re2c:define:YYFILL = "fill_line_buffer()";
re2c:yyfill:parameter = 0; re2c:yyfill:parameter = 0;
re2c:indent:string = " "; re2c:indent:string = " ";
re2c:indent:top = 1; re2c:indent:top = 1;
@ -7747,30 +7851,76 @@ class basic_json
return last_token_type; return last_token_type;
} }
/// append data from the stream to the internal buffer /*!
void yyfill() noexcept @brief append data from the stream to the line buffer
{
if (m_stream == nullptr or not * m_stream)
{
return;
}
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_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; const auto offset_cursor = m_cursor - m_start;
m_buffer.erase(0, static_cast<size_t>(offset_start)); // no stream is used or end of file is reached
std::string line; if (m_stream == nullptr or not * m_stream)
assert(m_stream != nullptr); {
std::getline(*m_stream, line); // copy unprocessed characters to line buffer
m_buffer += "\n" + line; // add line with newline symbol 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); assert(m_content != nullptr);
m_start = m_content; m_start = m_content;
m_marker = m_start + offset_marker; m_marker = m_start + offset_marker;
m_cursor = m_start + offset_cursor; 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 /// return string representation of last read token
@ -8112,8 +8262,8 @@ class basic_json
private: private:
/// optional input stream /// optional input stream
std::istream* m_stream = nullptr; std::istream* m_stream = nullptr;
/// the buffer /// line buffer buffer for m_stream
string_t m_buffer; string_t m_line_buffer {};
/// the buffer pointer /// the buffer pointer
const lexer_char_t* m_content = nullptr; const lexer_char_t* m_content = nullptr;
/// pointer to the beginning of the current symbol /// pointer to the beginning of the current symbol
@ -8136,25 +8286,34 @@ class basic_json
class parser class parser
{ {
public: public:
/// constructor for strings /// a parser reading from a string literal
parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept parser(const char* buff, const parser_callback_t cb = nullptr)
: callback(cb), m_lexer(s) : callback(cb),
{ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), strlen(buff))
// read first token {}
get_token();
}
/// a parser reading from an input stream /// a parser reading from an input stream
parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept parser(std::istream& is, const parser_callback_t cb = nullptr)
: callback(cb), m_lexer(&_is) : callback(cb), m_lexer(is)
{ {}
// read first token
get_token(); /// 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 /// public parser interface
basic_json parse() basic_json parse()
{ {
// read first token
get_token();
basic_json result = parse_internal(true); basic_json result = parse_internal(true);
result.assert_invariant(); result.assert_invariant();
@ -8361,7 +8520,7 @@ class basic_json
} }
/// get next token from lexer /// get next token from lexer
typename lexer::token_type get_token() noexcept typename lexer::token_type get_token()
{ {
last_token = m_lexer.scan(); last_token = m_lexer.scan();
return last_token; return last_token;
@ -9659,7 +9818,7 @@ namespace std
@since version 1.0.0 @since version 1.0.0
*/ */
template <> template<>
inline void swap(nlohmann::json& j1, inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept( nlohmann::json& j2) noexcept(
is_nothrow_move_constructible<nlohmann::json>::value and is_nothrow_move_constructible<nlohmann::json>::value and
@ -9670,7 +9829,7 @@ inline void swap(nlohmann::json& j1,
} }
/// hash value for JSON objects /// hash value for JSON objects
template <> template<>
struct hash<nlohmann::json> struct hash<nlohmann::json>
{ {
/*! /*!
@ -9701,7 +9860,7 @@ if no parse error occurred.
*/ */
inline nlohmann::json operator "" _json(const char* s, std::size_t) 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);
} }
/*! /*!

View file

@ -38,43 +38,67 @@ TEST_CASE("lexer class")
{ {
SECTION("structural characters") SECTION("structural characters")
{ {
CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array); 1).scan() == json::lexer::token_type::begin_array);
CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object); 1).scan() == json::lexer::token_type::end_array);
CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator); 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") SECTION("literal names")
{ {
CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true); 4).scan() == json::lexer::token_type::literal_null);
CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false); 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") SECTION("numbers")
{ {
CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number); 1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number); 1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number); 1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number); 1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number); 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") SECTION("whitespace")
{ {
// result is end_of_input, because not token is following // result is end_of_input, because not token is following
CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input); 1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input); CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input); 1).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*>("\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) 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) switch (c)
{ {
@ -122,7 +150,7 @@ TEST_CASE("lexer class")
case ('8'): case ('8'):
case ('9'): case ('9'):
{ {
CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); CHECK(res != json::lexer::token_type::parse_error);
break; break;
} }
@ -132,14 +160,14 @@ TEST_CASE("lexer class")
case ('\n'): case ('\n'):
case ('\r'): 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; break;
} }
// anything else is not expected // anything else is not expected
default: default:
{ {
CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error); CHECK(res == json::lexer::token_type::parse_error);
break; break;
} }
} }

View file

@ -32,6 +32,8 @@ SOFTWARE.
#include "json.hpp" #include "json.hpp"
using nlohmann::json; using nlohmann::json;
#include <valarray>
TEST_CASE("parser class") TEST_CASE("parser class")
{ {
SECTION("parse") SECTION("parse")
@ -487,7 +489,7 @@ TEST_CASE("parser class")
case ('r'): case ('r'):
case ('t'): case ('t'):
{ {
CHECK_NOTHROW(json::parser(s).parse()); CHECK_NOTHROW(json::parser(s.c_str()).parse());
break; break;
} }
@ -500,8 +502,8 @@ TEST_CASE("parser class")
// any other combination of backslash and character is invalid // any other combination of backslash and character is invalid
default: default:
{ {
CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser(s.c_str()).parse(), "parse error - unexpected '\"'");
break; break;
} }
} }
@ -559,22 +561,22 @@ TEST_CASE("parser class")
if (valid(c)) if (valid(c))
{ {
CHECK_NOTHROW(json::parser(s1).parse()); CHECK_NOTHROW(json::parser(s1.c_str()).parse());
CHECK_NOTHROW(json::parser(s2).parse()); CHECK_NOTHROW(json::parser(s2.c_str()).parse());
CHECK_NOTHROW(json::parser(s3).parse()); CHECK_NOTHROW(json::parser(s3.c_str()).parse());
CHECK_NOTHROW(json::parser(s4).parse()); CHECK_NOTHROW(json::parser(s4.c_str()).parse());
} }
else else
{ {
CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s1.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s4).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(s1.c_str()).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s4).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]"); SECTION("from std::vector")
json::parser p(*s); {
delete s; std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(p.parse() == json({1, 2, 3, 4})); 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));
}
} }
} }

View file

@ -31,6 +31,8 @@ SOFTWARE.
#include "json.hpp" #include "json.hpp"
using nlohmann::json; using nlohmann::json;
#include <valarray>
TEST_CASE("deserialization") TEST_CASE("deserialization")
{ {
SECTION("successful deserialization") SECTION("successful deserialization")
@ -43,7 +45,14 @@ TEST_CASE("deserialization")
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); 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::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}]";
json j = json::parse(s); json j = json::parse(s);
@ -74,7 +83,7 @@ TEST_CASE("deserialization")
} }
} }
SECTION("successful deserialization") SECTION("unsuccessful deserialization")
{ {
SECTION("stream") SECTION("stream")
{ {
@ -116,4 +125,103 @@ TEST_CASE("deserialization")
"parse error - unexpected end of input; expected ']'"); "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);
}
}
}
} }