#pragma once #include // assert #include // size_t #include // strlen #include // istream #include // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include // shared_ptr, make_shared, addressof #include // accumulate #include // string, char_traits #include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include // pair, declval #include namespace nlohmann { namespace detail { /// the supported input formats enum class input_format_t { json, cbor, msgpack, ubjson, bson }; //////////////////// // input adapters // //////////////////// /*! @brief abstract input adapter interface Produces a stream of std::char_traits::int_type characters from a std::istream, a buffer, or some other input type. Accepts the return of exactly one non-EOF character for future input. The int_type characters returned consist of all valid char values as positive values (typically unsigned char), plus an EOF value outside that range, specified by the value of the function std::char_traits::eof(). This value is typically -1, but could be any arbitrary value which is not a valid char value. */ struct input_adapter_protocol { /// get a character [0,255] or std::char_traits::eof(). virtual std::char_traits::int_type get_character() = 0; virtual ~input_adapter_protocol() = default; }; /// a type to simplify interfaces using input_adapter_t = std::shared_ptr; /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at beginning of input. Does not support changing the underlying std::streambuf in mid-input. Maintains underlying std::istream and std::streambuf to support subsequent use of standard std::istream operations to process any input characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ class input_stream_adapter : public input_adapter_protocol { public: ~input_stream_adapter() override { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof is.clear(is.rdstate() & std::ios::eofbit); } explicit input_stream_adapter(std::istream& i) : is(i), sb(*i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; input_stream_adapter(input_stream_adapter&&) = delete; input_stream_adapter& operator=(input_stream_adapter&&) = delete; // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. std::char_traits::int_type get_character() override { auto res = sb.sbumpc(); // set eof manually, as we don't use the istream interface. if (res == EOF) { is.clear(is.rdstate() | std::ios::eofbit); } return res; } private: /// the associated input stream std::istream& is; std::streambuf& sb; }; /// input adapter for buffer input class input_buffer_adapter : public input_adapter_protocol { public: input_buffer_adapter(const char* b, const std::size_t l) noexcept : cursor(b), limit(b + l) {} // 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&&) = delete; input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; ~input_buffer_adapter() override = default; std::char_traits::int_type get_character() noexcept override { if (JSON_LIKELY(cursor < limit)) { return std::char_traits::to_int_type(*(cursor++)); } return std::char_traits::eof(); } private: /// pointer to the current character const char* cursor; /// pointer past the last character const char* const limit; }; template struct wide_string_input_helper { // UTF-32 static void fill_buffer(const WideStringType& str, size_t& current_wchar, 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()) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; } else { // get the current character const auto wc = static_cast(str[current_wchar++]); // UTF-32 to UTF-8 encoding if (wc < 0x80) { utf8_bytes[0] = wc; utf8_bytes_filled = 1; } else if (wc <= 0x7FF) { utf8_bytes[0] = 0xC0 | ((wc >> 6) & 0x1F); utf8_bytes[1] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 2; } else if (wc <= 0xFFFF) { utf8_bytes[0] = 0xE0 | ((wc >> 12) & 0x0F); utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F); utf8_bytes[2] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 3; } else if (wc <= 0x10FFFF) { utf8_bytes[0] = 0xF0 | ((wc >> 18) & 0x07); utf8_bytes[1] = 0x80 | ((wc >> 12) & 0x3F); utf8_bytes[2] = 0x80 | ((wc >> 6) & 0x3F); utf8_bytes[3] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 4; } else { // unknown character utf8_bytes[0] = wc; utf8_bytes_filled = 1; } } } }; template struct wide_string_input_helper { // UTF-16 static void fill_buffer(const WideStringType& str, size_t& current_wchar, 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()) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; } else { // get the current character const auto wc = static_cast(str[current_wchar++]); // UTF-16 to UTF-8 encoding if (wc < 0x80) { utf8_bytes[0] = wc; utf8_bytes_filled = 1; } else if (wc <= 0x7FF) { utf8_bytes[0] = 0xC0 | ((wc >> 6)); utf8_bytes[1] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 2; } else if (0xD800 > wc or wc >= 0xE000) { utf8_bytes[0] = 0xE0 | ((wc >> 12)); utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F); utf8_bytes[2] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 3; } else { if (current_wchar < str.size()) { const auto wc2 = static_cast(str[current_wchar++]); const int charcode = 0x10000 + (((wc & 0x3FF) << 10) | (wc2 & 0x3FF)); utf8_bytes[0] = 0xf0 | (charcode >> 18); utf8_bytes[1] = 0x80 | ((charcode >> 12) & 0x3F); utf8_bytes[2] = 0x80 | ((charcode >> 6) & 0x3F); utf8_bytes[3] = 0x80 | (charcode & 0x3F); utf8_bytes_filled = 4; } else { // unknown character ++current_wchar; utf8_bytes[0] = wc; utf8_bytes_filled = 1; } } } } }; template class wide_string_input_adapter : public input_adapter_protocol { public: explicit wide_string_input_adapter(const WideStringType& w) noexcept : str(w) {} std::char_traits::int_type get_character() noexcept override { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { fill_buffer(); assert(utf8_bytes_filled > 0); assert(utf8_bytes_index == 0); } // use buffer assert(utf8_bytes_filled > 0); assert(utf8_bytes_index < utf8_bytes_filled); return utf8_bytes[utf8_bytes_index++]; } private: template void fill_buffer() { wide_string_input_helper::fill_buffer(str, current_wchar, 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}}; /// index to the utf8_codes array for the next valid byte std::size_t utf8_bytes_index = 0; /// number of valid bytes in the utf8_codes array std::size_t utf8_bytes_filled = 0; }; class input_adapter { public: // native support /// input adapter for input stream input_adapter(std::istream& i) : ia(std::make_shared(i)) {} /// input adapter for input stream input_adapter(std::istream&& i) : ia(std::make_shared(i)) {} input_adapter(const std::wstring& ws) : ia(std::make_shared>(ws)) {} input_adapter(const std::u16string& ws) : ia(std::make_shared>(ws)) {} input_adapter(const std::u32string& ws) : ia(std::make_shared>(ws)) {} /// input adapter for buffer template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> input_adapter(CharT b, std::size_t l) : ia(std::make_shared(reinterpret_cast(b), l)) {} // derived support /// input adapter for string literal template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> input_adapter(CharT b) : input_adapter(reinterpret_cast(b), std::strlen(reinterpret_cast(b))) {} /// input adapter for iterator range with contiguous storage template::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> input_adapter(IteratorType first, IteratorType last) { #ifndef NDEBUG // assertion to check that the iterator range is indeed contiguous, // see http://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 std::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_LIKELY(len > 0)) { // there is at least one element: use the address of first ia = std::make_shared(reinterpret_cast(&(*first)), len); } else { // the address of first cannot be used: use nullptr ia = std::make_shared(nullptr, len); } } /// input adapter for array template input_adapter(T (&array)[N]) : 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> input_adapter(const ContiguousContainer& c) : input_adapter(std::begin(c), std::end(c)) {} operator input_adapter_t() { return ia; } private: /// the actual adapter input_adapter_t ia = nullptr; }; } // namespace detail } // namespace nlohmann