diff --git a/README.md b/README.md index 8b351e51d..6a9b1d145 100644 --- a/README.md +++ b/README.md @@ -416,7 +416,7 @@ Please note that setting the exception bit for `failbit` is inappropriate for th #### Read from iterator range -You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose content is stored as contiguous byte sequence, for instance a `std::vector`: +You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose `value_type` is an integral type of 1, 2 or 4 bytes, which will be interpreted as UTF-8, UTF-16 and UTF-32 respectively. For instance, a `std::vector`, or a `std::list`: ```cpp std::vector v = {'t', 'r', 'u', 'e'}; @@ -430,6 +430,53 @@ std::vector v = {'t', 'r', 'u', 'e'}; json j = json::parse(v); ``` +#### Custom data source + +Since the parse function accepts arbitrary iterator ranges, you can provide your own data sources by implementing the `LegacyInputIterator` concept. + +```cpp +struct MyContainer { + void advance(); + const char& get_current(); +}; + +struct MyIterator { + using difference_type = std::ptrdiff_t; + using value_type = char; + using pointer = const char*; + using reference = const char&; + using iterator_category = std::input_iterator_tag; + + MyIterator& operator++() { + MyContainer.advance(); + return *this; + } + + bool operator!=(const MyIterator& rhs) const { + return rhs.target != target; + } + + reference operator*() const { + return target.get_current(); + } + + MyContainer* target = nullptr; +}; + +MyIterator begin(MyContainer& tgt) { + return MyIterator{&tgt}; +} + +MyIterator end(const MyContainer&) { + return {}; +} + +void foo() { + MyContainer c; + json j = json::parse(c); +} +``` + #### SAX interface The library uses a SAX-like interface with the following functions: diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 1a27cc9dd..725ff1fe4 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -34,6 +34,8 @@ Input adapter for stdio file access. This adapter read only 1 byte and do not us class file_input_adapter { public: + using char_type = char; + JSON_HEDLEY_NON_NULL(2) explicit file_input_adapter(std::FILE* f) noexcept : m_file(f) @@ -68,6 +70,8 @@ subsequent call for input from the std::istream. class input_stream_adapter { public: + using char_type = char; + ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not @@ -100,7 +104,7 @@ class input_stream_adapter { auto res = sb->sbumpc(); // set eof manually, as we don't use the istream interface. - if (res == EOF) + if (JSON_HEDLEY_UNLIKELY(res == EOF)) { is->clear(is->rdstate() | std::ios::eofbit); } @@ -113,51 +117,61 @@ class input_stream_adapter std::streambuf* sb = nullptr; }; -/// input adapter for buffer input -class input_buffer_adapter +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter { public: - input_buffer_adapter(const char* b, const std::size_t l) noexcept - : cursor(b), limit(b == nullptr ? nullptr : (b + l)) - {} + using char_type = typename std::iterator_traits::value_type; - // delete because of pointer members - input_buffer_adapter(const input_buffer_adapter&) = delete; - input_buffer_adapter& operator=(input_buffer_adapter&) = delete; - input_buffer_adapter(input_buffer_adapter&&) = default; - input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) {} - std::char_traits::int_type get_character() noexcept + typename std::char_traits::int_type get_character() { - if (JSON_HEDLEY_LIKELY(cursor < limit)) + if (JSON_HEDLEY_LIKELY(current != end)) { - assert(cursor != nullptr and limit != nullptr); - return std::char_traits::to_int_type(*(cursor++)); + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + else + { + return std::char_traits::eof(); } - - return std::char_traits::eof(); } private: - /// pointer to the current character - const char* cursor; - /// pointer past the last character - const char* const limit; + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } + }; -template -struct wide_string_input_helper + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper { // UTF-32 - static void fill_buffer(const WideStringType& str, - size_t& current_wchar, + static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -165,7 +179,7 @@ struct wide_string_input_helper else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-32 to UTF-8 encoding if (wc < 0x80) @@ -204,19 +218,18 @@ struct wide_string_input_helper } }; -template -struct wide_string_input_helper +template +struct wide_string_input_helper { // UTF-16 - static void fill_buffer(const WideStringType& str, - size_t& current_wchar, + static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -224,7 +237,7 @@ struct wide_string_input_helper else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-16 to UTF-8 encoding if (wc < 0x80) @@ -247,9 +260,9 @@ struct wide_string_input_helper } else { - if (current_wchar < str.size()) + if (JSON_HEDLEY_UNLIKELY(not input.empty())) { - const auto wc2 = static_cast(str[current_wchar++]); + const auto wc2 = static_cast(input.get_character()); const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); @@ -259,8 +272,6 @@ struct wide_string_input_helper } else { - // unknown character - ++current_wchar; utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } @@ -269,20 +280,20 @@ struct wide_string_input_helper } }; -template +// Wraps another input apdater to convert wide character types into individual bytes. +template class wide_string_input_adapter { public: - explicit wide_string_input_adapter(const WideStringType& w) noexcept - : str(w) - {} + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} - std::char_traits::int_type get_character() noexcept + typename std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { - fill_buffer(); + fill_buffer(); assert(utf8_bytes_filled > 0); assert(utf8_bytes_index == 0); @@ -295,18 +306,14 @@ class wide_string_input_adapter } private: + BaseInputAdapter base_adapter; + template void fill_buffer() { - wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + wide_string_input_helper::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 std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; @@ -316,6 +323,61 @@ class wide_string_input_adapter std::size_t utf8_bytes_filled = 0; }; + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +// This test breaks astyle formatting when inlined in a template specialization. +template +inline constexpr bool is_iterator_of_multibyte() +{ + return sizeof(typename std::iterator_traits::value_type) > 1; +} + +template +struct iterator_input_adapter_factory()>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +template +auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) +{ + // Enable ADL + using std::begin; + using std::end; + + return input_adapter(begin(container), end(container)); +} + +// Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) { return file_input_adapter(file); @@ -331,97 +393,27 @@ inline input_stream_adapter input_adapter(std::istream&& stream) return input_stream_adapter(stream); } -template::value and - std::is_integral::type>::value and - not std::is_same::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> -input_buffer_adapter input_adapter(CharT b, SizeT l) +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value and + not std::is_array::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) { - return input_buffer_adapter(reinterpret_cast(b), l); + auto length = std::strlen(reinterpret_cast(b)); + auto ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); } -template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> -input_buffer_adapter input_adapter(CharT b) +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) { - return input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))); -} - -template::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(true, 0), - [&first](std::pair 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::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); - - const auto len = static_cast(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(&(*first)), len); - } - else - { - // the address of first cannot be used: use nullptr - return input_buffer_adapter(nullptr, len); - } -} - -inline wide_string_input_adapter input_adapter(const std::wstring& ws) -{ - return wide_string_input_adapter(ws); -} - - -inline wide_string_input_adapter input_adapter(const std::u16string& ws) -{ - return wide_string_input_adapter(ws); -} - -inline wide_string_input_adapter input_adapter(const std::u32string& ws) -{ - return wide_string_input_adapter(ws); -} - -template::value and - std::is_base_of()))>::iterator_category>::value, - int>::type = 0> -input_buffer_adapter input_adapter(const ContiguousContainer& c) -{ - return input_adapter(std::begin(c), std::end(c)); -} - - -template -input_buffer_adapter input_adapter(T (&array)[N]) -{ - return input_adapter(std::begin(array), std::end(array)); + return input_adapter(array, array + N); } // This class only handles inputs of input_buffer_adapter type. @@ -437,17 +429,7 @@ class span_input_adapter sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> span_input_adapter(CharT b, std::size_t l) - : ia(reinterpret_cast(b), l) {} - - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> - span_input_adapter(CharT b) - : span_input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))) {} + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} template - span_input_adapter(T (&array)[N]) - : span_input_adapter(std::begin(array), std::end(array)) {} - - /// input adapter for contiguous container - template::value and - std::is_base_of()))>::iterator_category>::value, - int>::type = 0> - span_input_adapter(const ContiguousContainer& c) - : span_input_adapter(std::begin(c), std::end(c)) {} - - input_buffer_adapter&& get() + contiguous_bytes_input_adapter&& get() { return std::move(ia); } private: - input_buffer_adapter ia; + contiguous_bytes_input_adapter ia; }; } // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index f65323376..592d19667 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -6550,28 +6550,12 @@ class basic_json @brief deserialize from a compatible input This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - 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 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.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - 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. + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. @param[in] i input to read from @param[in] cb a parser callback function of type @ref parser_callback_t @@ -6591,7 +6575,7 @@ class basic_json @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. + @a cb or reading from the input @a i has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @@ -6620,9 +6604,43 @@ class basic_json return result; } + /*! + @brief deserialize from a pair of character iterators + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @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 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + 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(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + return result; + } JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) @@ -6638,38 +6656,31 @@ class basic_json return parser(detail::input_adapter(std::forward(i))).accept(true); } + template + static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) static bool accept(detail::span_input_adapter&& i) { return parser(i.get()).accept(true); } + /*! @brief generate SAX events The SAX event lister must follow the interface of @ref json_sax. This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - 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 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.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - 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. + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. @param[in] i input to read from @param[in,out] sax SAX event listener @@ -6695,7 +6706,7 @@ class basic_json @since version 3.2.0 */ - template + template JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, @@ -6707,8 +6718,21 @@ class basic_json : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } + template + 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(std::move(ia)).sax_parse(format, sax, strict); + } + template JSON_HEDLEY_NON_NULL(2) + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true) @@ -6720,86 +6744,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::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::iterator_category>::value, int>::type = 0> - static bool accept(IteratorType first, IteratorType last) - { - return parser(detail::input_adapter(first, last)).accept(true); - } - - template::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 @@ -7449,27 +7394,40 @@ class basic_json /*! @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7575,28 +7533,40 @@ class basic_json /*! @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) static basic_json from_msgpack(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7678,27 +7648,39 @@ class basic_json /*! @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) static basic_json from_ubjson(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7779,27 +7761,39 @@ class basic_json /*! @copydoc from_bson(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) static basic_json from_bson(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::bson, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /// @} diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 035107415..a24a08b5a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4456,6 +4456,8 @@ Input adapter for stdio file access. This adapter read only 1 byte and do not us class file_input_adapter { public: + using char_type = char; + JSON_HEDLEY_NON_NULL(2) explicit file_input_adapter(std::FILE* f) noexcept : m_file(f) @@ -4490,6 +4492,8 @@ subsequent call for input from the std::istream. class input_stream_adapter { public: + using char_type = char; + ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not @@ -4522,7 +4526,7 @@ class input_stream_adapter { auto res = sb->sbumpc(); // set eof manually, as we don't use the istream interface. - if (res == EOF) + if (JSON_HEDLEY_UNLIKELY(res == EOF)) { is->clear(is->rdstate() | std::ios::eofbit); } @@ -4535,51 +4539,61 @@ class input_stream_adapter std::streambuf* sb = nullptr; }; -/// input adapter for buffer input -class input_buffer_adapter +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter { public: - input_buffer_adapter(const char* b, const std::size_t l) noexcept - : cursor(b), limit(b == nullptr ? nullptr : (b + l)) - {} + using char_type = typename std::iterator_traits::value_type; - // delete because of pointer members - input_buffer_adapter(const input_buffer_adapter&) = delete; - input_buffer_adapter& operator=(input_buffer_adapter&) = delete; - input_buffer_adapter(input_buffer_adapter&&) = default; - input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) {} - std::char_traits::int_type get_character() noexcept + typename std::char_traits::int_type get_character() { - if (JSON_HEDLEY_LIKELY(cursor < limit)) + if (JSON_HEDLEY_LIKELY(current != end)) { - assert(cursor != nullptr and limit != nullptr); - return std::char_traits::to_int_type(*(cursor++)); + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + else + { + return std::char_traits::eof(); } - - return std::char_traits::eof(); } private: - /// pointer to the current character - const char* cursor; - /// pointer past the last character - const char* const limit; + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } + }; -template -struct wide_string_input_helper + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper { // UTF-32 - static void fill_buffer(const WideStringType& str, - size_t& current_wchar, + static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -4587,7 +4601,7 @@ struct wide_string_input_helper else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-32 to UTF-8 encoding if (wc < 0x80) @@ -4626,19 +4640,18 @@ struct wide_string_input_helper } }; -template -struct wide_string_input_helper +template +struct wide_string_input_helper { // UTF-16 - static void fill_buffer(const WideStringType& str, - size_t& current_wchar, + static void fill_buffer(BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -4646,7 +4659,7 @@ struct wide_string_input_helper else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-16 to UTF-8 encoding if (wc < 0x80) @@ -4669,9 +4682,9 @@ struct wide_string_input_helper } else { - if (current_wchar < str.size()) + if (JSON_HEDLEY_UNLIKELY(not input.empty())) { - const auto wc2 = static_cast(str[current_wchar++]); + const auto wc2 = static_cast(input.get_character()); const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); @@ -4681,8 +4694,6 @@ struct wide_string_input_helper } else { - // unknown character - ++current_wchar; utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } @@ -4691,20 +4702,20 @@ struct wide_string_input_helper } }; -template +// Wraps another input apdater to convert wide character types into individual bytes. +template class wide_string_input_adapter { public: - explicit wide_string_input_adapter(const WideStringType& w) noexcept - : str(w) - {} + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} - std::char_traits::int_type get_character() noexcept + typename std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { - fill_buffer(); + fill_buffer(); assert(utf8_bytes_filled > 0); assert(utf8_bytes_index == 0); @@ -4717,18 +4728,14 @@ class wide_string_input_adapter } private: + BaseInputAdapter base_adapter; + template void fill_buffer() { - wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + wide_string_input_helper::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 std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; @@ -4738,6 +4745,61 @@ class wide_string_input_adapter std::size_t utf8_bytes_filled = 0; }; + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +// This test breaks astyle formatting when inlined in a template specialization. +template +inline constexpr bool is_iterator_of_multibyte() +{ + return sizeof(typename std::iterator_traits::value_type) > 1; +} + +template +struct iterator_input_adapter_factory()>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +template +auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) +{ + // Enable ADL + using std::begin; + using std::end; + + return input_adapter(begin(container), end(container)); +} + +// Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) { return file_input_adapter(file); @@ -4753,97 +4815,27 @@ inline input_stream_adapter input_adapter(std::istream&& stream) return input_stream_adapter(stream); } -template::value and - std::is_integral::type>::value and - not std::is_same::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> -input_buffer_adapter input_adapter(CharT b, SizeT l) +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value and + not std::is_array::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) { - return input_buffer_adapter(reinterpret_cast(b), l); + auto length = std::strlen(reinterpret_cast(b)); + auto ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); } -template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> -input_buffer_adapter input_adapter(CharT b) +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) { - return input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))); -} - -template::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(true, 0), - [&first](std::pair 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::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); - - const auto len = static_cast(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(&(*first)), len); - } - else - { - // the address of first cannot be used: use nullptr - return input_buffer_adapter(nullptr, len); - } -} - -inline wide_string_input_adapter input_adapter(const std::wstring& ws) -{ - return wide_string_input_adapter(ws); -} - - -inline wide_string_input_adapter input_adapter(const std::u16string& ws) -{ - return wide_string_input_adapter(ws); -} - -inline wide_string_input_adapter input_adapter(const std::u32string& ws) -{ - return wide_string_input_adapter(ws); -} - -template::value and - std::is_base_of()))>::iterator_category>::value, - int>::type = 0> -input_buffer_adapter input_adapter(const ContiguousContainer& c) -{ - return input_adapter(std::begin(c), std::end(c)); -} - - -template -input_buffer_adapter input_adapter(T (&array)[N]) -{ - return input_adapter(std::begin(array), std::end(array)); + return input_adapter(array, array + N); } // This class only handles inputs of input_buffer_adapter type. @@ -4859,17 +4851,7 @@ class span_input_adapter sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> span_input_adapter(CharT b, std::size_t l) - : ia(reinterpret_cast(b), l) {} - - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, - int>::type = 0> - span_input_adapter(CharT b) - : span_input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))) {} + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} template - span_input_adapter(T (&array)[N]) - : span_input_adapter(std::begin(array), std::end(array)) {} - - /// input adapter for contiguous container - template::value and - std::is_base_of()))>::iterator_category>::value, - int>::type = 0> - span_input_adapter(const ContiguousContainer& c) - : span_input_adapter(std::begin(c), std::end(c)) {} - - input_buffer_adapter&& get() + contiguous_bytes_input_adapter&& get() { return std::move(ia); } private: - input_buffer_adapter ia; + contiguous_bytes_input_adapter ia; }; } // namespace detail } // namespace nlohmann @@ -22343,28 +22313,12 @@ class basic_json @brief deserialize from a compatible input This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - 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 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.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - 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. + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. @param[in] i input to read from @param[in] cb a parser callback function of type @ref parser_callback_t @@ -22384,7 +22338,7 @@ class basic_json @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. + @a cb or reading from the input @a i has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. @@ -22413,9 +22367,43 @@ class basic_json return result; } + /*! + @brief deserialize from a pair of character iterators + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @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 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + 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(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + return result; + } JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) @@ -22431,38 +22419,31 @@ class basic_json return parser(detail::input_adapter(std::forward(i))).accept(true); } + template + static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) static bool accept(detail::span_input_adapter&& i) { return parser(i.get()).accept(true); } + /*! @brief generate SAX events The SAX event lister must follow the interface of @ref json_sax. This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - 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 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.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - 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. + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. @param[in] i input to read from @param[in,out] sax SAX event listener @@ -22488,7 +22469,7 @@ class basic_json @since version 3.2.0 */ - template + template JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, @@ -22500,8 +22481,21 @@ class basic_json : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } + template + 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(std::move(ia)).sax_parse(format, sax, strict); + } + template JSON_HEDLEY_NON_NULL(2) + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true) @@ -22513,86 +22507,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::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::iterator_category>::value, int>::type = 0> - static bool accept(IteratorType first, IteratorType last) - { - return parser(detail::input_adapter(first, last)).accept(true); - } - - template::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 @@ -23242,27 +23157,40 @@ class basic_json /*! @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23368,28 +23296,40 @@ class basic_json /*! @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) static basic_json from_msgpack(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23471,27 +23411,39 @@ class basic_json /*! @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) static basic_json from_ubjson(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23572,27 +23524,39 @@ class basic_json /*! @copydoc from_bson(detail::input_adapter&&, const bool, const bool) */ - template::value, int> = 0> + template 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 allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(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(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + template JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) static basic_json from_bson(detail::span_input_adapter&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(i.get()).sax_parse(input_format_t::bson, &sdp, strict); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } /// @} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3864923e5..fdf5ac5b3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -133,6 +133,7 @@ set(files src/unit-ubjson.cpp src/unit-udt.cpp src/unit-unicode.cpp + src/unit-user_defined_input.cpp src/unit-wstring.cpp) foreach(file ${files}) diff --git a/test/Makefile b/test/Makefile index 7bf0fef98..bcf8de7de 100644 --- a/test/Makefile +++ b/test/Makefile @@ -44,6 +44,7 @@ SOURCES = src/unit.cpp \ src/unit-testsuites.cpp \ src/unit-ubjson.cpp \ src/unit-unicode.cpp \ + src/unit-user_defined_input.cpp \ src/unit-wstring.cpp OBJECTS = $(SOURCES:.cpp=.o) diff --git a/test/src/unit-user_defined_input.cpp b/test/src/unit-user_defined_input.cpp new file mode 100644 index 000000000..86f763d5a --- /dev/null +++ b/test/src/unit-user_defined_input.cpp @@ -0,0 +1,122 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.7.3 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +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 +using nlohmann::json; + +#include + +namespace +{ +TEST_CASE("Use arbitrary stdlib container") +{ + std::string raw_data = "[1,2,3,4]"; + std::list 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); +} + + + +} \ No newline at end of file