unified input API

This commit is contained in:
Francois Chabot 2020-05-27 12:40:04 -04:00
parent d70d06ae41
commit 5684d9a498
6 changed files with 402 additions and 366 deletions

View file

@ -34,6 +34,8 @@ Input adapter for stdio file access. This adapter read only 1 byte and do not us
class file_input_adapter class file_input_adapter
{ {
public: public:
using char_type = char;
JSON_HEDLEY_NON_NULL(2) JSON_HEDLEY_NON_NULL(2)
explicit file_input_adapter(std::FILE* f) noexcept explicit file_input_adapter(std::FILE* f) noexcept
: m_file(f) : m_file(f)
@ -68,6 +70,8 @@ subsequent call for input from the std::istream.
class input_stream_adapter class input_stream_adapter
{ {
public: public:
using char_type = char;
~input_stream_adapter() ~input_stream_adapter()
{ {
// clear stream flags; we use underlying streambuf I/O, do not // clear stream flags; we use underlying streambuf I/O, do not
@ -113,51 +117,61 @@ class input_stream_adapter
std::streambuf* sb = nullptr; std::streambuf* sb = nullptr;
}; };
/// input adapter for buffer input // General-purpose iterator-based adapter. It might not be as fast as
class input_buffer_adapter // theoretically possible for some containers, but it is extremely versatile.
template<typename IteratorType>
class iterator_input_adapter
{ {
public: public:
input_buffer_adapter(const char* b, const std::size_t l) noexcept using char_type = typename std::iterator_traits<IteratorType>::value_type;
: cursor(b), limit(b == nullptr ? nullptr : (b + l))
{}
// delete because of pointer members iterator_input_adapter(IteratorType first, IteratorType last)
input_buffer_adapter(const input_buffer_adapter&) = delete; : current(std::move(first)), end(std::move(last)) {}
input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
input_buffer_adapter(input_buffer_adapter&&) = default;
input_buffer_adapter& operator=(input_buffer_adapter&&) = delete;
std::char_traits<char>::int_type get_character() noexcept typename std::char_traits<char_type>::int_type get_character()
{ {
if (JSON_HEDLEY_LIKELY(cursor < limit)) if (current != end)
{ {
assert(cursor != nullptr and limit != nullptr); auto result = std::char_traits<char_type>::to_int_type(*current);
return std::char_traits<char>::to_int_type(*(cursor++)); std::advance(current, 1);
return result;
}
else
{
return std::char_traits<char_type>::eof();
} }
return std::char_traits<char>::eof();
} }
private: private:
/// pointer to the current character IteratorType current;
const char* cursor; IteratorType end;
/// pointer past the last character
const char* const limit; template<typename BaseInputAdapter, size_t T>
friend class wide_string_input_helper;
bool empty() const
{
return current == end;
}
}; };
template<typename WideStringType, size_t T>
struct wide_string_input_helper template<typename BaseInputAdapter, size_t T>
struct wide_string_input_helper;
template<typename BaseInputAdapter>
struct wide_string_input_helper<BaseInputAdapter, 4>
{ {
// UTF-32 // UTF-32
static void fill_buffer(const WideStringType& str, static void fill_buffer(BaseInputAdapter& input,
size_t& current_wchar,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index, size_t& utf8_bytes_index,
size_t& utf8_bytes_filled) size_t& utf8_bytes_filled)
{ {
utf8_bytes_index = 0; utf8_bytes_index = 0;
if (current_wchar == str.size()) if (input.empty())
{ {
utf8_bytes[0] = std::char_traits<char>::eof(); utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1; utf8_bytes_filled = 1;
@ -165,7 +179,7 @@ struct wide_string_input_helper
else else
{ {
// get the current character // get the current character
const auto wc = static_cast<unsigned int>(str[current_wchar++]); const auto wc = input.get_character();
// UTF-32 to UTF-8 encoding // UTF-32 to UTF-8 encoding
if (wc < 0x80) if (wc < 0x80)
@ -204,19 +218,18 @@ struct wide_string_input_helper
} }
}; };
template<typename WideStringType> template<typename BaseInputAdapter>
struct wide_string_input_helper<WideStringType, 2> struct wide_string_input_helper<BaseInputAdapter, 2>
{ {
// UTF-16 // UTF-16
static void fill_buffer(const WideStringType& str, static void fill_buffer(BaseInputAdapter& input,
size_t& current_wchar,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index, size_t& utf8_bytes_index,
size_t& utf8_bytes_filled) size_t& utf8_bytes_filled)
{ {
utf8_bytes_index = 0; utf8_bytes_index = 0;
if (current_wchar == str.size()) if (input.empty())
{ {
utf8_bytes[0] = std::char_traits<char>::eof(); utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1; utf8_bytes_filled = 1;
@ -224,7 +237,7 @@ struct wide_string_input_helper<WideStringType, 2>
else else
{ {
// get the current character // get the current character
const auto wc = static_cast<unsigned int>(str[current_wchar++]); const auto wc = input.get_character();
// UTF-16 to UTF-8 encoding // UTF-16 to UTF-8 encoding
if (wc < 0x80) if (wc < 0x80)
@ -247,9 +260,9 @@ struct wide_string_input_helper<WideStringType, 2>
} }
else else
{ {
if (current_wchar < str.size()) if (!input.empty())
{ {
const auto wc2 = static_cast<unsigned int>(str[current_wchar++]); const auto wc2 = static_cast<unsigned int>(input.get_character());
const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
@ -259,8 +272,6 @@ struct wide_string_input_helper<WideStringType, 2>
} }
else else
{ {
// unknown character
++current_wchar;
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc); utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1; utf8_bytes_filled = 1;
} }
@ -269,20 +280,20 @@ struct wide_string_input_helper<WideStringType, 2>
} }
}; };
template<typename WideStringType> // Wraps another input apdater to convert wide character types into individual bytes.
template<typename BaseInputAdapter, typename WideCharType>
class wide_string_input_adapter class wide_string_input_adapter
{ {
public: public:
explicit wide_string_input_adapter(const WideStringType& w) noexcept wide_string_input_adapter(BaseInputAdapter base)
: str(w) : base_adapter(base) {}
{}
std::char_traits<char>::int_type get_character() noexcept typename std::char_traits<char>::int_type get_character() noexcept
{ {
// check if buffer needs to be filled // check if buffer needs to be filled
if (utf8_bytes_index == utf8_bytes_filled) if (utf8_bytes_index == utf8_bytes_filled)
{ {
fill_buffer<sizeof(typename WideStringType::value_type)>(); fill_buffer<sizeof(WideCharType)>();
assert(utf8_bytes_filled > 0); assert(utf8_bytes_filled > 0);
assert(utf8_bytes_index == 0); assert(utf8_bytes_index == 0);
@ -295,18 +306,14 @@ class wide_string_input_adapter
} }
private: private:
BaseInputAdapter base_adapter;
template<size_t T> template<size_t T>
void fill_buffer() void fill_buffer()
{ {
wide_string_input_helper<WideStringType, T>::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
} }
/// the wstring to process
const WideStringType& str;
/// index of the current wchar in str
std::size_t current_wchar = 0;
/// a buffer for UTF-8 bytes /// a buffer for UTF-8 bytes
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
@ -316,6 +323,52 @@ class wide_string_input_adapter
std::size_t utf8_bytes_filled = 0; std::size_t utf8_bytes_filled = 0;
}; };
template<typename IteratorType, typename Enable = void>
struct iterator_input_adapter_factory
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using adapter_type = iterator_input_adapter<iterator_type>;
static adapter_type create(IteratorType begin, IteratorType end)
{
return adapter_type(std::move(begin), std::move(end));
}
};
template<typename IteratorType>
struct iterator_input_adapter_factory<IteratorType,
typename std::enable_if<(sizeof(typename std::iterator_traits<IteratorType>::value_type)>1)>::type >
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using base_adapter_type = iterator_input_adapter<iterator_type>;
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
static adapter_type create(IteratorType begin, IteratorType end)
{
return adapter_type(base_adapter_type(std::move(begin), std::move(end)));
}
};
// General purpose iterator-based input
template<typename IteratorType>
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType begin, IteratorType end)
{
using factory_type = iterator_input_adapter_factory<IteratorType>;
return factory_type::create(begin, end);
}
// Convenience shorthand from container to iterator
template<typename ContainerType>
auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container)))
{
return input_adapter(begin(container), end(container));
}
// Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file) inline file_input_adapter input_adapter(std::FILE* file)
{ {
return file_input_adapter(file); return file_input_adapter(file);
@ -331,97 +384,22 @@ inline input_stream_adapter input_adapter(std::istream&& stream)
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
// Null-delimited strings, and the like.
template<typename CharT, template<typename CharT,
typename std::enable_if< typename std::enable_if<
std::is_pointer<CharT>::value and std::is_pointer<CharT>::value and
std::is_integral<typename std::remove_pointer<CharT>::type>::value and std::is_integral<typename std::remove_pointer<CharT>::type>::value and
sizeof(typename std::remove_pointer<CharT>::type) == 1, sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0> int>::type = 0>
input_buffer_adapter input_adapter(CharT b, std::size_t l) contiguous_bytes_input_adapter input_adapter(CharT b)
{ {
return input_buffer_adapter(reinterpret_cast<const char*>(b), l); auto length = std::strlen(reinterpret_cast<const char*>(b));
auto ptr = reinterpret_cast<const char*>(b);
return input_adapter(ptr, ptr + length);
} }
template<typename CharT,
typename std::enable_if<
std::is_pointer<CharT>::value and
std::is_integral<typename std::remove_pointer<CharT>::type>::value and
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0>
input_buffer_adapter input_adapter(CharT b)
{
return input_adapter(reinterpret_cast<const char*>(b),
std::strlen(reinterpret_cast<const char*>(b)));
}
template<class IteratorType,
typename std::enable_if<
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
input_buffer_adapter input_adapter(IteratorType first, IteratorType last)
{
#ifndef NDEBUG
// assertion to check that the iterator range is indeed contiguous,
// see https://stackoverflow.com/a/35008842/266378 for more discussion
const auto is_contiguous = std::accumulate(
first, last, std::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;
assert(is_contiguous);
#endif
// assertion to check that each element is 1 byte long
static_assert(
sizeof(typename iterator_traits<IteratorType>::value_type) == 1,
"each element in the iterator range must have the size of 1 byte");
const auto len = static_cast<size_t>(std::distance(first, last));
if (JSON_HEDLEY_LIKELY(len > 0))
{
// there is at least one element: use the address of first
return input_buffer_adapter(reinterpret_cast<const char*>(&(*first)), len);
}
else
{
// the address of first cannot be used: use nullptr
return input_buffer_adapter(nullptr, len);
}
}
inline wide_string_input_adapter<std::wstring> input_adapter(const std::wstring& ws)
{
return wide_string_input_adapter<std::wstring>(ws);
}
inline wide_string_input_adapter<std::u16string> input_adapter(const std::u16string& ws)
{
return wide_string_input_adapter<std::u16string>(ws);
}
inline wide_string_input_adapter<std::u32string> input_adapter(const std::u32string& ws)
{
return wide_string_input_adapter<std::u32string>(ws);
}
template<class ContiguousContainer, typename
std::enable_if<not std::is_pointer<ContiguousContainer>::value and
std::is_base_of<std::random_access_iterator_tag, typename iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
int>::type = 0>
input_buffer_adapter input_adapter(const ContiguousContainer& c)
{
return input_adapter(std::begin(c), std::end(c));
}
template<class T, std::size_t N>
input_buffer_adapter input_adapter(T (&array)[N])
{
return input_adapter(std::begin(array), std::end(array));
}
// This class only handles inputs of input_buffer_adapter type. // This class only handles inputs of input_buffer_adapter type.
// It's required so that expressions like {ptr, len} can be implicitely casted // It's required so that expressions like {ptr, len} can be implicitely casted
@ -436,7 +414,7 @@ class span_input_adapter
sizeof(typename std::remove_pointer<CharT>::type) == 1, sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0> int>::type = 0>
span_input_adapter(CharT b, std::size_t l) span_input_adapter(CharT b, std::size_t l)
: ia(reinterpret_cast<const char*>(b), l) {} : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
template<typename CharT, template<typename CharT,
typename std::enable_if< typename std::enable_if<
@ -445,8 +423,7 @@ class span_input_adapter
sizeof(typename std::remove_pointer<CharT>::type) == 1, sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0> int>::type = 0>
span_input_adapter(CharT b) span_input_adapter(CharT b)
: span_input_adapter(reinterpret_cast<const char*>(b), : span_input_adapter(b, std::strlen(reinterpret_cast<const char*>(b))) {}
std::strlen(reinterpret_cast<const char*>(b))) {}
template<class IteratorType, template<class IteratorType,
typename std::enable_if< typename std::enable_if<
@ -467,13 +444,13 @@ class span_input_adapter
span_input_adapter(const ContiguousContainer& c) span_input_adapter(const ContiguousContainer& c)
: span_input_adapter(std::begin(c), std::end(c)) {} : span_input_adapter(std::begin(c), std::end(c)) {}
input_buffer_adapter&& get() contiguous_bytes_input_adapter&& get()
{ {
return std::move(ia); return std::move(ia);
} }
private: private:
input_buffer_adapter ia; contiguous_bytes_input_adapter ia;
}; };
} // namespace detail } // namespace detail
} // namespace nlohmann } // namespace nlohmann

View file

@ -6621,6 +6621,17 @@ class basic_json
} }
template<typename IteratorType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(IteratorType begin,
IteratorType end,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true)
{
basic_json result;
parser(detail::input_adapter(std::move(begin), std::move(end)), cb, allow_exceptions).parse(true, result);
return result;
}
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(detail::span_input_adapter&& i, static basic_json parse(detail::span_input_adapter&& i,
@ -6638,10 +6649,17 @@ class basic_json
return parser(detail::input_adapter(std::forward<InputType>(i))).accept(true); return parser(detail::input_adapter(std::forward<InputType>(i))).accept(true);
} }
template<typename IteratorType>
static bool accept(IteratorType begin, IteratorType end)
{
return parser(detail::input_adapter(std::move(begin), std::move(end))).accept(true);
}
static bool accept(detail::span_input_adapter&& i) static bool accept(detail::span_input_adapter&& i)
{ {
return parser(i.get()).accept(true); return parser(i.get()).accept(true);
} }
/*! /*!
@brief generate SAX events @brief generate SAX events
@ -6695,7 +6713,7 @@ class basic_json
@since version 3.2.0 @since version 3.2.0
*/ */
template <typename SAX, typename InputType> template <typename InputType, typename SAX>
JSON_HEDLEY_NON_NULL(2) JSON_HEDLEY_NON_NULL(2)
static bool sax_parse(InputType&& i, SAX* sax, static bool sax_parse(InputType&& i, SAX* sax,
input_format_t format = input_format_t::json, input_format_t format = input_format_t::json,
@ -6707,6 +6725,18 @@ class basic_json
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict); : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
} }
template<class IteratorType, class SAX>
JSON_HEDLEY_NON_NULL(3)
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true)
{
auto ia = detail::input_adapter(std::move(first), std::move(last));
return format == input_format_t::json
? parser(std::move(ia)).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
}
template <typename SAX> template <typename SAX>
JSON_HEDLEY_NON_NULL(2) JSON_HEDLEY_NON_NULL(2)
static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
@ -6720,86 +6750,7 @@ class basic_json
} }
/*!
@brief deserialize from an iterator range with contiguous storage
This function reads from an iterator range of a container with contiguous
storage of 1-byte values. Compatible container types include
`std::vector`, `std::string`, `std::array`, `std::valarray`, and
`std::initializer_list`. Furthermore, C-style arrays can be used with
`std::begin()`/`std::end()`. User-defined containers can be used as long
as they implement random-access iterators and a contiguous storage.
@pre The iterator range is contiguous. Violating this precondition yields
undefined behavior. **This precondition is enforced with an assertion.**
@pre Each element in the range has a size of 1 byte. Violating this
precondition yields undefined behavior. **This precondition is enforced
with a static assertion.**
@warning There is no way to enforce all preconditions at compile-time. If
the function is called with noncompliant iterators and with
assertions switched off, the behavior is undefined and will most
likely yield segmentation violation.
@tparam IteratorType iterator of container with contiguous storage
@param[in] first begin of the range to parse (included)
@param[in] last end of the range to parse (excluded)
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@param[in] allow_exceptions whether to throw exceptions in case of a
parse error (optional, true by default)
@return deserialized JSON value; in case of a parse error and
@a allow_exceptions set to `false`, the return value will be
value_t::discarded.
@throw parse_error.101 in case of an unexpected token
@throw parse_error.102 if to_unicode fails or surrogate error
@throw parse_error.103 if to_unicode fails
@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,
const bool allow_exceptions = true)
{
basic_json result;
parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result);
return result;
}
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 bool accept(IteratorType first, IteratorType last)
{
return parser(detail::input_adapter(first, last)).accept(true);
}
template<class IteratorType, class SAX, typename std::enable_if<
std::is_base_of<
std::random_access_iterator_tag,
typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
JSON_HEDLEY_NON_NULL(3)
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax)
{
return parser(detail::input_adapter(first, last)).sax_parse(sax);
}
/*! /*!
@brief deserialize from stream @brief deserialize from stream
@ -7449,16 +7400,16 @@ class basic_json
/*! /*!
@copydoc from_cbor(detail::input_adapter&&, const bool, const bool) @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)
*/ */
template<typename A1, typename A2, template<typename IteratorType>
detail::enable_if_t<std::is_constructible<detail::span_input_adapter, A1, A2>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_cbor(A1 && a1, A2 && a2, static basic_json from_cbor(IteratorType first, IteratorType last,
const bool strict = true, const bool strict = true,
const bool allow_exceptions = true) const bool allow_exceptions = true)
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(detail::span_input_adapter(std::forward<A1>(a1), std::forward<A2>(a2)).get()).sax_parse(input_format_t::cbor, &sdp, strict); auto ia = detail::input_adapter(std::move(first), std::move(last));
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7469,7 +7420,8 @@ class basic_json
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(i.get()).sax_parse(input_format_t::cbor, &sdp, strict); auto ia = i.get();
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7575,16 +7527,16 @@ class basic_json
/*! /*!
@copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)
*/ */
template<typename A1, typename A2, template<typename IteratorType>
detail::enable_if_t<std::is_constructible<detail::span_input_adapter, A1, A2>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_msgpack(A1 && a1, A2 && a2, static basic_json from_msgpack(IteratorType first, IteratorType last,
const bool strict = true, const bool strict = true,
const bool allow_exceptions = true) const bool allow_exceptions = true)
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(detail::span_input_adapter(std::forward<A1>(a1), std::forward<A2>(a2)).get()).sax_parse(input_format_t::msgpack, &sdp, strict); auto ia = detail::input_adapter(std::move(first), std::move(last));
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7596,7 +7548,8 @@ class basic_json
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(i.get()).sax_parse(input_format_t::msgpack, &sdp, strict); auto ia = i.get();
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7678,16 +7631,16 @@ class basic_json
/*! /*!
@copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)
*/ */
template<typename A1, typename A2, template<typename IteratorType>
detail::enable_if_t<std::is_constructible<detail::span_input_adapter, A1, A2>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_ubjson(A1 && a1, A2 && a2, static basic_json from_ubjson(IteratorType first, IteratorType last,
const bool strict = true, const bool strict = true,
const bool allow_exceptions = true) const bool allow_exceptions = true)
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(detail::span_input_adapter(std::forward<A1>(a1), std::forward<A2>(a2)).get()).sax_parse(input_format_t::ubjson, &sdp, strict); auto ia = detail::input_adapter(std::move(first), std::move(last));
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7698,7 +7651,8 @@ class basic_json
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(i.get()).sax_parse(input_format_t::ubjson, &sdp, strict); auto ia = i.get();
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7779,16 +7733,16 @@ class basic_json
/*! /*!
@copydoc from_bson(detail::input_adapter&&, const bool, const bool) @copydoc from_bson(detail::input_adapter&&, const bool, const bool)
*/ */
template<typename A1, typename A2, template<typename IteratorType>
detail::enable_if_t<std::is_constructible<detail::span_input_adapter, A1, A2>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bson(A1 && a1, A2 && a2, static basic_json from_bson(IteratorType first, IteratorType last,
const bool strict = true, const bool strict = true,
const bool allow_exceptions = true) const bool allow_exceptions = true)
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(detail::span_input_adapter(std::forward<A1>(a1), std::forward<A2>(a2)).get()).sax_parse(input_format_t::bson, &sdp, strict); auto ia = detail::input_adapter(std::move(first), std::move(last));
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
@ -7799,7 +7753,8 @@ class basic_json
{ {
basic_json result; basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions); detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
const bool res = binary_reader<detail::input_buffer_adapter>(i.get()).sax_parse(input_format_t::bson, &sdp, strict); auto ia = i.get();
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);
return res ? result : basic_json(value_t::discarded); return res ? result : basic_json(value_t::discarded);
} }
/// @} /// @}

View file

@ -4456,6 +4456,8 @@ Input adapter for stdio file access. This adapter read only 1 byte and do not us
class file_input_adapter class file_input_adapter
{ {
public: public:
using char_type = char;
JSON_HEDLEY_NON_NULL(2) JSON_HEDLEY_NON_NULL(2)
explicit file_input_adapter(std::FILE* f) noexcept explicit file_input_adapter(std::FILE* f) noexcept
: m_file(f) : m_file(f)
@ -4490,6 +4492,8 @@ subsequent call for input from the std::istream.
class input_stream_adapter class input_stream_adapter
{ {
public: public:
using char_type = char;
~input_stream_adapter() ~input_stream_adapter()
{ {
// clear stream flags; we use underlying streambuf I/O, do not // clear stream flags; we use underlying streambuf I/O, do not
@ -4535,51 +4539,55 @@ class input_stream_adapter
std::streambuf* sb = nullptr; std::streambuf* sb = nullptr;
}; };
/// input adapter for buffer input // General-purpose iterator-based adapter. It might not be as fast as
class input_buffer_adapter // theoretically possible for some containers, but it is extremely versatile.
template<typename IteratorType>
class iterator_input_adapter
{ {
public: public:
input_buffer_adapter(const char* b, const std::size_t l) noexcept using char_type = typename std::iterator_traits<IteratorType>::value_type;
: cursor(b), limit(b == nullptr ? nullptr : (b + l))
{}
// delete because of pointer members iterator_input_adapter(IteratorType begin_ite, IteratorType end_ite)
input_buffer_adapter(const input_buffer_adapter&) = delete; : current(std::move(begin_ite)), end(std::move(end_ite)) {}
input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
input_buffer_adapter(input_buffer_adapter&&) = default;
input_buffer_adapter& operator=(input_buffer_adapter&&) = delete;
std::char_traits<char>::int_type get_character() noexcept typename std::char_traits<char_type>::int_type get_character()
{ {
if (JSON_HEDLEY_LIKELY(cursor < limit)) if (current != end)
{ {
assert(cursor != nullptr and limit != nullptr); return *current++;
return std::char_traits<char>::to_int_type(*(cursor++)); }
else
{
return std::char_traits<char_type>::eof();
}
} }
return std::char_traits<char>::eof(); bool empty() const
{
return current == end;
} }
private: private:
/// pointer to the current character IteratorType current;
const char* cursor; IteratorType end;
/// pointer past the last character
const char* const limit;
}; };
template<typename WideStringType, size_t T>
struct wide_string_input_helper template<typename BaseInputAdapter, size_t T>
struct wide_string_input_helper;
template<typename BaseInputAdapter>
struct wide_string_input_helper<BaseInputAdapter, 4>
{ {
// UTF-32 // UTF-32
static void fill_buffer(const WideStringType& str, static void fill_buffer(BaseInputAdapter& input,
size_t& current_wchar,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index, size_t& utf8_bytes_index,
size_t& utf8_bytes_filled) size_t& utf8_bytes_filled)
{ {
utf8_bytes_index = 0; utf8_bytes_index = 0;
if (current_wchar == str.size()) if (input.empty())
{ {
utf8_bytes[0] = std::char_traits<char>::eof(); utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1; utf8_bytes_filled = 1;
@ -4587,7 +4595,7 @@ struct wide_string_input_helper
else else
{ {
// get the current character // get the current character
const auto wc = static_cast<unsigned int>(str[current_wchar++]); const auto wc = input.get_character();
// UTF-32 to UTF-8 encoding // UTF-32 to UTF-8 encoding
if (wc < 0x80) if (wc < 0x80)
@ -4626,19 +4634,18 @@ struct wide_string_input_helper
} }
}; };
template<typename WideStringType> template<typename BaseInputAdapter>
struct wide_string_input_helper<WideStringType, 2> struct wide_string_input_helper<BaseInputAdapter, 2>
{ {
// UTF-16 // UTF-16
static void fill_buffer(const WideStringType& str, static void fill_buffer(BaseInputAdapter& input,
size_t& current_wchar,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index, size_t& utf8_bytes_index,
size_t& utf8_bytes_filled) size_t& utf8_bytes_filled)
{ {
utf8_bytes_index = 0; utf8_bytes_index = 0;
if (current_wchar == str.size()) if (input.empty())
{ {
utf8_bytes[0] = std::char_traits<char>::eof(); utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1; utf8_bytes_filled = 1;
@ -4646,7 +4653,7 @@ struct wide_string_input_helper<WideStringType, 2>
else else
{ {
// get the current character // get the current character
const auto wc = static_cast<unsigned int>(str[current_wchar++]); const auto wc = input.get_character();
// UTF-16 to UTF-8 encoding // UTF-16 to UTF-8 encoding
if (wc < 0x80) if (wc < 0x80)
@ -4669,9 +4676,9 @@ struct wide_string_input_helper<WideStringType, 2>
} }
else else
{ {
if (current_wchar < str.size()) if (!input.empty())
{ {
const auto wc2 = static_cast<unsigned int>(str[current_wchar++]); const auto wc2 = static_cast<unsigned int>(input.get_character());
const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
@ -4681,8 +4688,6 @@ struct wide_string_input_helper<WideStringType, 2>
} }
else else
{ {
// unknown character
++current_wchar;
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc); utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1; utf8_bytes_filled = 1;
} }
@ -4691,20 +4696,20 @@ struct wide_string_input_helper<WideStringType, 2>
} }
}; };
template<typename WideStringType> // Wraps another input apdater to convert wide character types into individual bytes.
template<typename BaseInputAdapter, typename WideCharType>
class wide_string_input_adapter class wide_string_input_adapter
{ {
public: public:
explicit wide_string_input_adapter(const WideStringType& w) noexcept wide_string_input_adapter(BaseInputAdapter base)
: str(w) : base_adapter(base) {}
{}
std::char_traits<char>::int_type get_character() noexcept typename std::char_traits<char>::int_type get_character() noexcept
{ {
// check if buffer needs to be filled // check if buffer needs to be filled
if (utf8_bytes_index == utf8_bytes_filled) if (utf8_bytes_index == utf8_bytes_filled)
{ {
fill_buffer<sizeof(typename WideStringType::value_type)>(); fill_buffer<sizeof(WideCharType)>();
assert(utf8_bytes_filled > 0); assert(utf8_bytes_filled > 0);
assert(utf8_bytes_index == 0); assert(utf8_bytes_index == 0);
@ -4717,18 +4722,14 @@ class wide_string_input_adapter
} }
private: private:
BaseInputAdapter base_adapter;
template<size_t T> template<size_t T>
void fill_buffer() void fill_buffer()
{ {
wide_string_input_helper<WideStringType, T>::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
} }
/// the wstring to process
const WideStringType& str;
/// index of the current wchar in str
std::size_t current_wchar = 0;
/// a buffer for UTF-8 bytes /// a buffer for UTF-8 bytes
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
@ -4736,8 +4737,53 @@ class wide_string_input_adapter
std::size_t utf8_bytes_index = 0; std::size_t utf8_bytes_index = 0;
/// number of valid bytes in the utf8_codes array /// number of valid bytes in the utf8_codes array
std::size_t utf8_bytes_filled = 0; std::size_t utf8_bytes_filled = 0;
}
template<typename IteratorType, typename Enable = void>
struct iterator_input_adapter_factory
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using adapter_type = iterator_input_adapter<iterator_type>;
adapter_type create(IteratorType begin, IteratorType end)
{
return adapter_type(std::move(begin), std::mve(end));
}
}; };
template<typename IteratorType>
struct iterator_input_adapter_factory<IteratorType,
typename std::enable_if<(sizeof(typename std::iterator_traits<iterator_type>::value_type)>1)>::type >
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using base_adapter_type = iterator_input_adapter<iterator_type>;
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
adapter_type create(IteratorType begin, IteratorType end)
{
return adapter_type(base_adapter_type(std::move(begin), std::mve(end)));
}
};
// General purpose iterator-based input
template<typename IteratorType>
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType begin, IteratorType end)
{
return iterator_input_adapter_factory<IteratorType>::create(begin, end);
}
// Convenience shorthand from container to iterator
template<typename ContainerType>
decltype(input_adapter(begin(container), end(container))) input_adapter(const T& container)
{
return input_adapter(begin(container), end(container));
}
// Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file) inline file_input_adapter input_adapter(std::FILE* file)
{ {
return file_input_adapter(file); return file_input_adapter(file);
@ -4753,17 +4799,7 @@ inline input_stream_adapter input_adapter(std::istream&& stream)
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
template<typename CharT, // Null-delimited strings, and the like.
typename std::enable_if<
std::is_pointer<CharT>::value and
std::is_integral<typename std::remove_pointer<CharT>::type>::value and
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int>::type = 0>
input_buffer_adapter input_adapter(CharT b, std::size_t l)
{
return input_buffer_adapter(reinterpret_cast<const char*>(b), l);
}
template<typename CharT, template<typename CharT,
typename std::enable_if< typename std::enable_if<
std::is_pointer<CharT>::value and std::is_pointer<CharT>::value and
@ -4772,78 +4808,11 @@ template<typename CharT,
int>::type = 0> int>::type = 0>
input_buffer_adapter input_adapter(CharT b) input_buffer_adapter input_adapter(CharT b)
{ {
return input_adapter(reinterpret_cast<const char*>(b), auto length = std::strlen(reinterpret_cast<const char*>(b));
std::strlen(reinterpret_cast<const char*>(b))); auto ptr = reinterpret_cast<const char*>(b);
return input_adapter(ptr, ptr + length);
} }
template<class IteratorType,
typename std::enable_if<
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
input_buffer_adapter input_adapter(IteratorType first, IteratorType last)
{
#ifndef NDEBUG
// assertion to check that the iterator range is indeed contiguous,
// see https://stackoverflow.com/a/35008842/266378 for more discussion
const auto is_contiguous = std::accumulate(
first, last, std::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;
assert(is_contiguous);
#endif
// assertion to check that each element is 1 byte long
static_assert(
sizeof(typename iterator_traits<IteratorType>::value_type) == 1,
"each element in the iterator range must have the size of 1 byte");
const auto len = static_cast<size_t>(std::distance(first, last));
if (JSON_HEDLEY_LIKELY(len > 0))
{
// there is at least one element: use the address of first
return input_buffer_adapter(reinterpret_cast<const char*>(&(*first)), len);
}
else
{
// the address of first cannot be used: use nullptr
return input_buffer_adapter(nullptr, len);
}
}
inline wide_string_input_adapter<std::wstring> input_adapter(const std::wstring& ws)
{
return wide_string_input_adapter<std::wstring>(ws);
}
inline wide_string_input_adapter<std::u16string> input_adapter(const std::u16string& ws)
{
return wide_string_input_adapter<std::u16string>(ws);
}
inline wide_string_input_adapter<std::u32string> input_adapter(const std::u32string& ws)
{
return wide_string_input_adapter<std::u32string>(ws);
}
template<class ContiguousContainer, typename
std::enable_if<not std::is_pointer<ContiguousContainer>::value and
std::is_base_of<std::random_access_iterator_tag, typename iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
int>::type = 0>
input_buffer_adapter input_adapter(const ContiguousContainer& c)
{
return input_adapter(std::begin(c), std::end(c));
}
template<class T, std::size_t N>
input_buffer_adapter input_adapter(T (&array)[N])
{
return input_adapter(std::begin(array), std::end(array));
}
// This class only handles inputs of input_buffer_adapter type. // This class only handles inputs of input_buffer_adapter type.
// It's required so that expressions like {ptr, len} can be implicitely casted // It's required so that expressions like {ptr, len} can be implicitely casted
@ -22413,6 +22382,17 @@ class basic_json
} }
template<typename IteratorType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(IteratorType begin,
IteratorType end,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true)
{
basic_json result;
parser(detail::iterator_input_adapter(std::move(begin), std::move(end)), cb, allow_exceptions).parse(true, result);
return result;
}
JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(detail::span_input_adapter&& i, static basic_json parse(detail::span_input_adapter&& i,
@ -22430,10 +22410,17 @@ class basic_json
return parser(detail::input_adapter(std::forward<InputType>(i))).accept(true); return parser(detail::input_adapter(std::forward<InputType>(i))).accept(true);
} }
template<typename IteratorType>
static bool accept(IteratorType begin, IteratorType end)
{
return parser(detail::iterator_input_adapter(std::move(begin), std::move(end))).accept(true);
}
static bool accept(detail::span_input_adapter&& i) static bool accept(detail::span_input_adapter&& i)
{ {
return parser(i.get()).accept(true); return parser(i.get()).accept(true);
} }
/*! /*!
@brief generate SAX events @brief generate SAX events

View file

@ -133,6 +133,7 @@ set(files
src/unit-ubjson.cpp src/unit-ubjson.cpp
src/unit-udt.cpp src/unit-udt.cpp
src/unit-unicode.cpp src/unit-unicode.cpp
src/unit-user_defined_input.cpp
src/unit-wstring.cpp) src/unit-wstring.cpp)
foreach(file ${files}) foreach(file ${files})

View file

@ -44,6 +44,7 @@ SOURCES = src/unit.cpp \
src/unit-testsuites.cpp \ src/unit-testsuites.cpp \
src/unit-ubjson.cpp \ src/unit-ubjson.cpp \
src/unit-unicode.cpp \ src/unit-unicode.cpp \
src/unit-user_defined_input.cpp \
src/unit-wstring.cpp src/unit-wstring.cpp
OBJECTS = $(SOURCES:.cpp=.o) OBJECTS = $(SOURCES:.cpp=.o)

View file

@ -0,0 +1,115 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 3.7.3
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <list>
namespace
{
TEST_CASE("Use arbitrary stdlib container")
{
std::string raw_data = "[1,2,3,4]";
std::list<char> data(raw_data.begin(), raw_data.end());
json as_json = json::parse(data.begin(), data.end());
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
struct MyContainer
{
const char* data;
};
const char* begin(const MyContainer& c)
{
return c.data;
}
const char* end(const MyContainer& c)
{
return c.data + strlen(c.data);
}
TEST_CASE("Custom container")
{
MyContainer data{"[1,2,3,4]"};
json as_json = json::parse(data);
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
TEST_CASE("Custom iterator")
{
const char* raw_data = "[1,2,3,4]";
struct MyIterator {
using difference_type = std::size_t;
using value_type = char;
using pointer = const char*;
using reference = const char&;
using iterator_category = std::input_iterator_tag;
MyIterator& operator++() {
++ptr;
return *this;
}
reference operator*() const {return *ptr;}
bool operator!=(const MyIterator& rhs) const {
return ptr != rhs.ptr;
}
const char* ptr;
};
MyIterator begin{raw_data};
MyIterator end{raw_data + strlen(raw_data)};
json as_json = json::parse(begin, end);
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
}