mirror of
https://github.com/nlohmann/json.git
synced 2026-10-22 14:08:13 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9666b0e888 | ||
|
|
7cb3814308 | ||
|
|
16de05b930 | ||
|
|
419133b2af |
No files matched your search
@@ -8,17 +8,18 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // count, find, min
|
#include <algorithm> // count, find, max, min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint32_t
|
#include <cstdint> // uint32_t
|
||||||
#include <cstring> // strlen
|
#include <cstring> // strlen
|
||||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next, reverse_iterator
|
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next, reverse_iterator
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
|
#include <memory> // unique_ptr
|
||||||
#include <streambuf> // streambuf
|
#include <streambuf> // streambuf
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||||
#include <utility> // pair, declval
|
#include <utility> // declval, move, pair
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
@@ -72,6 +73,81 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 };
|
|||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
/*!
|
/*!
|
||||||
|
A growable array of characters for the part of a token that an input adapter
|
||||||
|
copied out of its window: unlike std::vector<char>::push_back(), appending a
|
||||||
|
character takes no reference to a local, and is a few instructions when there
|
||||||
|
is room.
|
||||||
|
*/
|
||||||
|
class token_carry_buffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
token_carry_buffer() = default;
|
||||||
|
token_carry_buffer(const token_carry_buffer&) = delete;
|
||||||
|
token_carry_buffer& operator=(const token_carry_buffer&) = delete;
|
||||||
|
token_carry_buffer(token_carry_buffer&& rhs) noexcept
|
||||||
|
: m_data(std::move(rhs.m_data)), m_end(rhs.m_end), m_capacity_end(rhs.m_capacity_end)
|
||||||
|
{
|
||||||
|
rhs.m_end = rhs.m_capacity_end = nullptr;
|
||||||
|
}
|
||||||
|
token_carry_buffer& operator=(token_carry_buffer&&) = delete;
|
||||||
|
~token_carry_buffer() = default;
|
||||||
|
|
||||||
|
void clear() noexcept
|
||||||
|
{
|
||||||
|
m_end = m_data.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(char c)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(m_end == m_capacity_end))
|
||||||
|
{
|
||||||
|
grow(1);
|
||||||
|
}
|
||||||
|
*m_end++ = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const char* first, const char* last)
|
||||||
|
{
|
||||||
|
const auto n = static_cast<std::size_t>(last - first);
|
||||||
|
if (n > static_cast<std::size_t>(m_capacity_end - m_end))
|
||||||
|
{
|
||||||
|
grow(n);
|
||||||
|
}
|
||||||
|
if (n != 0)
|
||||||
|
{
|
||||||
|
std::memcpy(m_end, first, n);
|
||||||
|
}
|
||||||
|
m_end += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// replace the contents of @a out
|
||||||
|
void copy_to(std::vector<char>& out) const
|
||||||
|
{
|
||||||
|
out.assign(m_data.get(), m_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// make room for @a extra more characters
|
||||||
|
JSON_HEDLEY_NEVER_INLINE void grow(std::size_t extra)
|
||||||
|
{
|
||||||
|
const auto size = static_cast<std::size_t>(m_end - m_data.get());
|
||||||
|
const auto capacity = static_cast<std::size_t>(m_capacity_end - m_data.get());
|
||||||
|
const std::size_t new_capacity = (std::max)(capacity < 32 ? static_cast<std::size_t>(32) : 2 * capacity, size + extra);
|
||||||
|
std::unique_ptr<char[]> data(new char[new_capacity]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
|
if (size != 0)
|
||||||
|
{
|
||||||
|
std::memcpy(data.get(), m_data.get(), size);
|
||||||
|
}
|
||||||
|
m_data = std::move(data);
|
||||||
|
m_end = m_data.get() + size;
|
||||||
|
m_capacity_end = m_data.get() + new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> m_data{}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,readability-redundant-member-init)
|
||||||
|
char* m_end = nullptr;
|
||||||
|
char* m_capacity_end = nullptr;
|
||||||
|
};
|
||||||
|
/*!
|
||||||
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
|
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
|
||||||
buffer. This adapter is a very low level adapter.
|
buffer. This adapter is a very low level adapter.
|
||||||
*/
|
*/
|
||||||
@@ -209,6 +285,7 @@ class input_stream_adapter
|
|||||||
, token_segment(rhs.token_segment)
|
, token_segment(rhs.token_segment)
|
||||||
, current_in_get_area(rhs.current_in_get_area)
|
, current_in_get_area(rhs.current_in_get_area)
|
||||||
, tracking_token(rhs.tracking_token)
|
, tracking_token(rhs.tracking_token)
|
||||||
|
, no_get_area(rhs.no_get_area)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
rhs.is = nullptr;
|
rhs.is = nullptr;
|
||||||
@@ -295,37 +372,20 @@ class input_stream_adapter
|
|||||||
std::char_traits<char>::int_type get_character()
|
std::char_traits<char>::int_type get_character()
|
||||||
{
|
{
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
// sbumpc() refills the get area if it is exhausted
|
// the character is in the get area: step over it, as sbumpc() does
|
||||||
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
save_token_segment();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto res = sb->sbumpc();
|
|
||||||
// set eof manually, as we don't use the istream interface.
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
|
||||||
{
|
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
current_in_get_area = false;
|
|
||||||
#endif
|
|
||||||
is->clear(is->rdstate() | std::ios::eofbit);
|
|
||||||
}
|
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
else if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
// the consumed character is at gptr() - 1, unless the streambuf
|
|
||||||
// has no get area and returned it from uflow()
|
|
||||||
const char* const next = streambuf_access::get_ptr(sb);
|
const char* const next = streambuf_access::get_ptr(sb);
|
||||||
after_refill(res, next != nullptr && next != streambuf_access::get_begin(sb));
|
if (JSON_HEDLEY_LIKELY(next != streambuf_access::get_end(sb)))
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
current_in_get_area = true;
|
current_in_get_area = true;
|
||||||
|
streambuf_access::bump(sb, 1);
|
||||||
|
return std::char_traits<char>::to_int_type(*next);
|
||||||
|
}
|
||||||
|
if (no_get_area)
|
||||||
|
{
|
||||||
|
return get_character_no_get_area();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return res;
|
return get_character_slow();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -334,6 +394,18 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
#if JSON_PRECISE_STREAM_POSITION
|
||||||
commit_lookahead();
|
commit_lookahead();
|
||||||
|
#endif
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
// the elements are in the get area: copy them, as sgetn() does, but
|
||||||
|
// without the virtual call
|
||||||
|
const char* const first = streambuf_access::get_ptr(sb);
|
||||||
|
const auto available = static_cast<std::size_t>(streambuf_access::get_end(sb) - first);
|
||||||
|
if (JSON_HEDLEY_LIKELY(count * sizeof(T) <= available && count * sizeof(T) <= static_cast<std::size_t>((std::numeric_limits<int>::max)())))
|
||||||
|
{
|
||||||
|
std::memcpy(dest, first, count * sizeof(T));
|
||||||
|
streambuf_access::bump(sb, static_cast<int>(count * sizeof(T)));
|
||||||
|
return count * sizeof(T);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
||||||
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
||||||
@@ -421,7 +493,7 @@ class input_stream_adapter
|
|||||||
// last one if @a drop_last.
|
// last one if @a drop_last.
|
||||||
void copy_token(std::vector<char>& out, bool drop_last) const
|
void copy_token(std::vector<char>& out, bool drop_last) const
|
||||||
{
|
{
|
||||||
out = token_carry;
|
token_carry.copy_to(out);
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
out.insert(out.end(), token_segment, current_end());
|
out.insert(out.end(), token_segment, current_end());
|
||||||
@@ -434,6 +506,47 @@ class input_stream_adapter
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#if !JSON_PRECISE_STREAM_POSITION
|
||||||
|
// the get area is exhausted, or there is none
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
#endif
|
||||||
|
std::char_traits<char>::int_type get_character_slow()
|
||||||
|
{
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
// sbumpc() refills the get area if it is exhausted
|
||||||
|
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(refill))
|
||||||
|
{
|
||||||
|
save_token_segment();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto res = sb->sbumpc();
|
||||||
|
// set eof manually, as we don't use the istream interface.
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
||||||
|
{
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
current_in_get_area = false;
|
||||||
|
#endif
|
||||||
|
is->clear(is->rdstate() | std::ios::eofbit);
|
||||||
|
}
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
else if (JSON_HEDLEY_UNLIKELY(refill))
|
||||||
|
{
|
||||||
|
// the consumed character is at gptr() - 1, unless the streambuf
|
||||||
|
// has no get area and returned it from uflow()
|
||||||
|
const char* const next = streambuf_access::get_ptr(sb);
|
||||||
|
after_refill(res, next != nullptr && next != streambuf_access::get_begin(sb));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_in_get_area = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
// Reads the protected get-area pointers of any std::streambuf. Naming the
|
// Reads the protected get-area pointers of any std::streambuf. Naming the
|
||||||
// members through a derived class makes forming the member pointers
|
// members through a derived class makes forming the member pointers
|
||||||
@@ -457,6 +570,11 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
(s->*(&streambuf_access::gbump))(n);
|
(s->*(&streambuf_access::gbump))(n);
|
||||||
}
|
}
|
||||||
|
// sbumpc() for an exhausted get area
|
||||||
|
static std::streambuf::int_type next_character(std::streambuf* s)
|
||||||
|
{
|
||||||
|
return (s->*(&streambuf_access::uflow))();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// one past the character last returned by get_character(), if that is in
|
// one past the character last returned by get_character(), if that is in
|
||||||
@@ -477,16 +595,53 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
token_carry.insert(token_carry.end(), token_segment, streambuf_access::get_end(sb));
|
token_carry.append(token_segment, streambuf_access::get_end(sb));
|
||||||
token_segment = nullptr;
|
token_segment = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !JSON_PRECISE_STREAM_POSITION
|
||||||
|
// The streambuf has no get area (e.g., std::cin synchronized with stdio):
|
||||||
|
// every character comes from uflow(), and the token is kept by value.
|
||||||
|
JSON_HEDLEY_NEVER_INLINE std::char_traits<char>::int_type get_character_no_get_area()
|
||||||
|
{
|
||||||
|
// the get area is exhausted, so this is what sbumpc() does
|
||||||
|
const auto res = streambuf_access::next_character(sb);
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof() || streambuf_access::get_end(sb) != nullptr))
|
||||||
|
{
|
||||||
|
return get_character_no_get_area_other(res);
|
||||||
|
}
|
||||||
|
// current_in_get_area is false: only the first character of a get
|
||||||
|
// area, or the end of the input, is read here
|
||||||
|
if (tracking_token)
|
||||||
|
{
|
||||||
|
token_carry.push_back(std::char_traits<char>::to_char_type(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @a res came from uflow() of a streambuf without a get area: it is the
|
||||||
|
// end of the input, or the streambuf has a get area now
|
||||||
|
JSON_HEDLEY_NEVER_INLINE std::char_traits<char>::int_type get_character_no_get_area_other(std::char_traits<char>::int_type res)
|
||||||
|
{
|
||||||
|
if (res == std::char_traits<char>::eof())
|
||||||
|
{
|
||||||
|
current_in_get_area = false;
|
||||||
|
is->clear(is->rdstate() | std::ios::eofbit);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// the consumed character is at gptr() - 1
|
||||||
|
after_refill(res, streambuf_access::get_ptr(sb) != streambuf_access::get_begin(sb));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// character @a c was just read into a new get area (@a in_get_area) or
|
// character @a c was just read into a new get area (@a in_get_area) or
|
||||||
// returned without one
|
// returned without one
|
||||||
void after_refill(std::char_traits<char>::int_type c, bool in_get_area)
|
void after_refill(std::char_traits<char>::int_type c, bool in_get_area)
|
||||||
{
|
{
|
||||||
current_in_get_area = in_get_area;
|
current_in_get_area = in_get_area;
|
||||||
|
no_get_area = !in_get_area;
|
||||||
if (!tracking_token)
|
if (!tracking_token)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -525,13 +680,16 @@ class input_stream_adapter
|
|||||||
#endif
|
#endif
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
/// the part of the current token that was in earlier get areas
|
/// the part of the current token that was in earlier get areas
|
||||||
std::vector<char> token_carry{}; // NOLINT(readability-redundant-member-init)
|
token_carry_buffer token_carry{}; // NOLINT(readability-redundant-member-init)
|
||||||
/// start of the current token's part in the get area, or nullptr
|
/// start of the current token's part in the get area, or nullptr
|
||||||
const char* token_segment = nullptr;
|
const char* token_segment = nullptr;
|
||||||
/// whether the character last returned by get_character() is in the get area
|
/// whether the character last returned by get_character() is in the get area
|
||||||
bool current_in_get_area = false;
|
bool current_in_get_area = false;
|
||||||
/// whether a token is tracked at all; the binary readers never start one
|
/// whether a token is tracked at all; the binary readers never start one
|
||||||
bool tracking_token = false;
|
bool tracking_token = false;
|
||||||
|
/// whether the streambuf had no get area when it was last asked for a
|
||||||
|
/// character
|
||||||
|
bool no_get_area = false;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
|
|||||||
Generated
+189
-31
@@ -8468,17 +8468,18 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <algorithm> // count, find, min
|
#include <algorithm> // count, find, max, min
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint32_t
|
#include <cstdint> // uint32_t
|
||||||
#include <cstring> // strlen
|
#include <cstring> // strlen
|
||||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next, reverse_iterator
|
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next, reverse_iterator
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
|
#include <memory> // unique_ptr
|
||||||
#include <streambuf> // streambuf
|
#include <streambuf> // streambuf
|
||||||
#include <string> // string, char_traits
|
#include <string> // string, char_traits
|
||||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||||
#include <utility> // pair, declval
|
#include <utility> // declval, move, pair
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
@@ -8537,6 +8538,81 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 };
|
|||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
/*!
|
/*!
|
||||||
|
A growable array of characters for the part of a token that an input adapter
|
||||||
|
copied out of its window: unlike std::vector<char>::push_back(), appending a
|
||||||
|
character takes no reference to a local, and is a few instructions when there
|
||||||
|
is room.
|
||||||
|
*/
|
||||||
|
class token_carry_buffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
token_carry_buffer() = default;
|
||||||
|
token_carry_buffer(const token_carry_buffer&) = delete;
|
||||||
|
token_carry_buffer& operator=(const token_carry_buffer&) = delete;
|
||||||
|
token_carry_buffer(token_carry_buffer&& rhs) noexcept
|
||||||
|
: m_data(std::move(rhs.m_data)), m_end(rhs.m_end), m_capacity_end(rhs.m_capacity_end)
|
||||||
|
{
|
||||||
|
rhs.m_end = rhs.m_capacity_end = nullptr;
|
||||||
|
}
|
||||||
|
token_carry_buffer& operator=(token_carry_buffer&&) = delete;
|
||||||
|
~token_carry_buffer() = default;
|
||||||
|
|
||||||
|
void clear() noexcept
|
||||||
|
{
|
||||||
|
m_end = m_data.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(char c)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(m_end == m_capacity_end))
|
||||||
|
{
|
||||||
|
grow(1);
|
||||||
|
}
|
||||||
|
*m_end++ = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const char* first, const char* last)
|
||||||
|
{
|
||||||
|
const auto n = static_cast<std::size_t>(last - first);
|
||||||
|
if (n > static_cast<std::size_t>(m_capacity_end - m_end))
|
||||||
|
{
|
||||||
|
grow(n);
|
||||||
|
}
|
||||||
|
if (n != 0)
|
||||||
|
{
|
||||||
|
std::memcpy(m_end, first, n);
|
||||||
|
}
|
||||||
|
m_end += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// replace the contents of @a out
|
||||||
|
void copy_to(std::vector<char>& out) const
|
||||||
|
{
|
||||||
|
out.assign(m_data.get(), m_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// make room for @a extra more characters
|
||||||
|
JSON_HEDLEY_NEVER_INLINE void grow(std::size_t extra)
|
||||||
|
{
|
||||||
|
const auto size = static_cast<std::size_t>(m_end - m_data.get());
|
||||||
|
const auto capacity = static_cast<std::size_t>(m_capacity_end - m_data.get());
|
||||||
|
const std::size_t new_capacity = (std::max)(capacity < 32 ? static_cast<std::size_t>(32) : 2 * capacity, size + extra);
|
||||||
|
std::unique_ptr<char[]> data(new char[new_capacity]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
|
if (size != 0)
|
||||||
|
{
|
||||||
|
std::memcpy(data.get(), m_data.get(), size);
|
||||||
|
}
|
||||||
|
m_data = std::move(data);
|
||||||
|
m_end = m_data.get() + size;
|
||||||
|
m_capacity_end = m_data.get() + new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> m_data{}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,readability-redundant-member-init)
|
||||||
|
char* m_end = nullptr;
|
||||||
|
char* m_capacity_end = nullptr;
|
||||||
|
};
|
||||||
|
/*!
|
||||||
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
|
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
|
||||||
buffer. This adapter is a very low level adapter.
|
buffer. This adapter is a very low level adapter.
|
||||||
*/
|
*/
|
||||||
@@ -8674,6 +8750,7 @@ class input_stream_adapter
|
|||||||
, token_segment(rhs.token_segment)
|
, token_segment(rhs.token_segment)
|
||||||
, current_in_get_area(rhs.current_in_get_area)
|
, current_in_get_area(rhs.current_in_get_area)
|
||||||
, tracking_token(rhs.tracking_token)
|
, tracking_token(rhs.tracking_token)
|
||||||
|
, no_get_area(rhs.no_get_area)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
rhs.is = nullptr;
|
rhs.is = nullptr;
|
||||||
@@ -8760,37 +8837,20 @@ class input_stream_adapter
|
|||||||
std::char_traits<char>::int_type get_character()
|
std::char_traits<char>::int_type get_character()
|
||||||
{
|
{
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
// sbumpc() refills the get area if it is exhausted
|
// the character is in the get area: step over it, as sbumpc() does
|
||||||
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
save_token_segment();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto res = sb->sbumpc();
|
|
||||||
// set eof manually, as we don't use the istream interface.
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
|
||||||
{
|
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
current_in_get_area = false;
|
|
||||||
#endif
|
|
||||||
is->clear(is->rdstate() | std::ios::eofbit);
|
|
||||||
}
|
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
|
||||||
else if (JSON_HEDLEY_UNLIKELY(refill))
|
|
||||||
{
|
|
||||||
// the consumed character is at gptr() - 1, unless the streambuf
|
|
||||||
// has no get area and returned it from uflow()
|
|
||||||
const char* const next = streambuf_access::get_ptr(sb);
|
const char* const next = streambuf_access::get_ptr(sb);
|
||||||
after_refill(res, next != nullptr && next != streambuf_access::get_begin(sb));
|
if (JSON_HEDLEY_LIKELY(next != streambuf_access::get_end(sb)))
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
current_in_get_area = true;
|
current_in_get_area = true;
|
||||||
|
streambuf_access::bump(sb, 1);
|
||||||
|
return std::char_traits<char>::to_int_type(*next);
|
||||||
|
}
|
||||||
|
if (no_get_area)
|
||||||
|
{
|
||||||
|
return get_character_no_get_area();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return res;
|
return get_character_slow();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -8799,6 +8859,18 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
#if JSON_PRECISE_STREAM_POSITION
|
#if JSON_PRECISE_STREAM_POSITION
|
||||||
commit_lookahead();
|
commit_lookahead();
|
||||||
|
#endif
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
// the elements are in the get area: copy them, as sgetn() does, but
|
||||||
|
// without the virtual call
|
||||||
|
const char* const first = streambuf_access::get_ptr(sb);
|
||||||
|
const auto available = static_cast<std::size_t>(streambuf_access::get_end(sb) - first);
|
||||||
|
if (JSON_HEDLEY_LIKELY(count * sizeof(T) <= available && count * sizeof(T) <= static_cast<std::size_t>((std::numeric_limits<int>::max)())))
|
||||||
|
{
|
||||||
|
std::memcpy(dest, first, count * sizeof(T));
|
||||||
|
streambuf_access::bump(sb, static_cast<int>(count * sizeof(T)));
|
||||||
|
return count * sizeof(T);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
||||||
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
||||||
@@ -8886,7 +8958,7 @@ class input_stream_adapter
|
|||||||
// last one if @a drop_last.
|
// last one if @a drop_last.
|
||||||
void copy_token(std::vector<char>& out, bool drop_last) const
|
void copy_token(std::vector<char>& out, bool drop_last) const
|
||||||
{
|
{
|
||||||
out = token_carry;
|
token_carry.copy_to(out);
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
out.insert(out.end(), token_segment, current_end());
|
out.insert(out.end(), token_segment, current_end());
|
||||||
@@ -8899,6 +8971,47 @@ class input_stream_adapter
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#if !JSON_PRECISE_STREAM_POSITION
|
||||||
|
// the get area is exhausted, or there is none
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
|
#endif
|
||||||
|
std::char_traits<char>::int_type get_character_slow()
|
||||||
|
{
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
// sbumpc() refills the get area if it is exhausted
|
||||||
|
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(refill))
|
||||||
|
{
|
||||||
|
save_token_segment();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto res = sb->sbumpc();
|
||||||
|
// set eof manually, as we don't use the istream interface.
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
||||||
|
{
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
current_in_get_area = false;
|
||||||
|
#endif
|
||||||
|
is->clear(is->rdstate() | std::ios::eofbit);
|
||||||
|
}
|
||||||
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
|
else if (JSON_HEDLEY_UNLIKELY(refill))
|
||||||
|
{
|
||||||
|
// the consumed character is at gptr() - 1, unless the streambuf
|
||||||
|
// has no get area and returned it from uflow()
|
||||||
|
const char* const next = streambuf_access::get_ptr(sb);
|
||||||
|
after_refill(res, next != nullptr && next != streambuf_access::get_begin(sb));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_in_get_area = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
// Reads the protected get-area pointers of any std::streambuf. Naming the
|
// Reads the protected get-area pointers of any std::streambuf. Naming the
|
||||||
// members through a derived class makes forming the member pointers
|
// members through a derived class makes forming the member pointers
|
||||||
@@ -8922,6 +9035,11 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
(s->*(&streambuf_access::gbump))(n);
|
(s->*(&streambuf_access::gbump))(n);
|
||||||
}
|
}
|
||||||
|
// sbumpc() for an exhausted get area
|
||||||
|
static std::streambuf::int_type next_character(std::streambuf* s)
|
||||||
|
{
|
||||||
|
return (s->*(&streambuf_access::uflow))();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// one past the character last returned by get_character(), if that is in
|
// one past the character last returned by get_character(), if that is in
|
||||||
@@ -8942,16 +9060,53 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
token_carry.insert(token_carry.end(), token_segment, streambuf_access::get_end(sb));
|
token_carry.append(token_segment, streambuf_access::get_end(sb));
|
||||||
token_segment = nullptr;
|
token_segment = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !JSON_PRECISE_STREAM_POSITION
|
||||||
|
// The streambuf has no get area (e.g., std::cin synchronized with stdio):
|
||||||
|
// every character comes from uflow(), and the token is kept by value.
|
||||||
|
JSON_HEDLEY_NEVER_INLINE std::char_traits<char>::int_type get_character_no_get_area()
|
||||||
|
{
|
||||||
|
// the get area is exhausted, so this is what sbumpc() does
|
||||||
|
const auto res = streambuf_access::next_character(sb);
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof() || streambuf_access::get_end(sb) != nullptr))
|
||||||
|
{
|
||||||
|
return get_character_no_get_area_other(res);
|
||||||
|
}
|
||||||
|
// current_in_get_area is false: only the first character of a get
|
||||||
|
// area, or the end of the input, is read here
|
||||||
|
if (tracking_token)
|
||||||
|
{
|
||||||
|
token_carry.push_back(std::char_traits<char>::to_char_type(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @a res came from uflow() of a streambuf without a get area: it is the
|
||||||
|
// end of the input, or the streambuf has a get area now
|
||||||
|
JSON_HEDLEY_NEVER_INLINE std::char_traits<char>::int_type get_character_no_get_area_other(std::char_traits<char>::int_type res)
|
||||||
|
{
|
||||||
|
if (res == std::char_traits<char>::eof())
|
||||||
|
{
|
||||||
|
current_in_get_area = false;
|
||||||
|
is->clear(is->rdstate() | std::ios::eofbit);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// the consumed character is at gptr() - 1
|
||||||
|
after_refill(res, streambuf_access::get_ptr(sb) != streambuf_access::get_begin(sb));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// character @a c was just read into a new get area (@a in_get_area) or
|
// character @a c was just read into a new get area (@a in_get_area) or
|
||||||
// returned without one
|
// returned without one
|
||||||
void after_refill(std::char_traits<char>::int_type c, bool in_get_area)
|
void after_refill(std::char_traits<char>::int_type c, bool in_get_area)
|
||||||
{
|
{
|
||||||
current_in_get_area = in_get_area;
|
current_in_get_area = in_get_area;
|
||||||
|
no_get_area = !in_get_area;
|
||||||
if (!tracking_token)
|
if (!tracking_token)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -8990,13 +9145,16 @@ class input_stream_adapter
|
|||||||
#endif
|
#endif
|
||||||
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
#if !JSON_DISABLE_STREAM_BULK_SCAN
|
||||||
/// the part of the current token that was in earlier get areas
|
/// the part of the current token that was in earlier get areas
|
||||||
std::vector<char> token_carry{}; // NOLINT(readability-redundant-member-init)
|
token_carry_buffer token_carry{}; // NOLINT(readability-redundant-member-init)
|
||||||
/// start of the current token's part in the get area, or nullptr
|
/// start of the current token's part in the get area, or nullptr
|
||||||
const char* token_segment = nullptr;
|
const char* token_segment = nullptr;
|
||||||
/// whether the character last returned by get_character() is in the get area
|
/// whether the character last returned by get_character() is in the get area
|
||||||
bool current_in_get_area = false;
|
bool current_in_get_area = false;
|
||||||
/// whether a token is tracked at all; the binary readers never start one
|
/// whether a token is tracked at all; the binary readers never start one
|
||||||
bool tracking_token = false;
|
bool tracking_token = false;
|
||||||
|
/// whether the streambuf had no get area when it was last asked for a
|
||||||
|
/// character
|
||||||
|
bool no_get_area = false;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
|
|||||||
@@ -48,14 +48,16 @@ namespace
|
|||||||
class chunked_streambuf : public std::streambuf
|
class chunked_streambuf : public std::streambuf
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
chunked_streambuf(std::string s, std::size_t chunk_size)
|
// The first @a unbuffered characters are served without a get area, the
|
||||||
: m_data(std::move(s)), m_chunk_size(chunk_size)
|
// rest in get areas of @a chunk_size.
|
||||||
|
chunked_streambuf(std::string s, std::size_t chunk_size, std::size_t unbuffered = 0)
|
||||||
|
: m_data(std::move(s)), m_chunk_size(chunk_size), m_unbuffered(unbuffered)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int_type underflow() override
|
int_type underflow() override
|
||||||
{
|
{
|
||||||
if (m_chunk_size == 0)
|
if (m_chunk_size == 0 || m_pos < m_unbuffered)
|
||||||
{
|
{
|
||||||
return m_pos < m_data.size() ? traits_type::to_int_type(m_data[m_pos]) : traits_type::eof();
|
return m_pos < m_data.size() ? traits_type::to_int_type(m_data[m_pos]) : traits_type::eof();
|
||||||
}
|
}
|
||||||
@@ -77,7 +79,7 @@ class chunked_streambuf : public std::streambuf
|
|||||||
|
|
||||||
int_type uflow() override
|
int_type uflow() override
|
||||||
{
|
{
|
||||||
if (m_chunk_size == 0)
|
if (m_chunk_size == 0 || m_pos < m_unbuffered)
|
||||||
{
|
{
|
||||||
return m_pos < m_data.size() ? traits_type::to_int_type(m_data[m_pos++]) : traits_type::eof();
|
return m_pos < m_data.size() ? traits_type::to_int_type(m_data[m_pos++]) : traits_type::eof();
|
||||||
}
|
}
|
||||||
@@ -88,6 +90,7 @@ class chunked_streambuf : public std::streambuf
|
|||||||
std::string m_data;
|
std::string m_data;
|
||||||
std::size_t m_pos = 0;
|
std::size_t m_pos = 0;
|
||||||
std::size_t m_chunk_size;
|
std::size_t m_chunk_size;
|
||||||
|
std::size_t m_unbuffered;
|
||||||
};
|
};
|
||||||
|
|
||||||
// how a test reads from the stream
|
// how a test reads from the stream
|
||||||
@@ -101,9 +104,9 @@ enum class read_mode
|
|||||||
// Read @a input from a chunked_streambuf and describe everything observable:
|
// Read @a input from a chunked_streambuf and describe everything observable:
|
||||||
// the values or the exception, the stream state after each value, and the
|
// the values or the exception, the stream state after each value, and the
|
||||||
// characters left in the stream.
|
// characters left in the stream.
|
||||||
std::string observe(const std::string& input, std::size_t chunk_size, read_mode mode)
|
std::string observe(const std::string& input, std::size_t chunk_size, read_mode mode, std::size_t unbuffered = 0)
|
||||||
{
|
{
|
||||||
chunked_streambuf buf(input, chunk_size);
|
chunked_streambuf buf(input, chunk_size, unbuffered);
|
||||||
std::istream is(&buf);
|
std::istream is(&buf);
|
||||||
std::string result;
|
std::string result;
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
@@ -212,6 +215,31 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("results do not depend on a get area that appears late")
|
||||||
|
{
|
||||||
|
// the first characters come without a get area, then the streambuf
|
||||||
|
// starts to use one: the token that is read at that point is complete
|
||||||
|
for (const auto& input : corpus())
|
||||||
|
{
|
||||||
|
const std::string expected = observe(input, 0, read_mode::extract);
|
||||||
|
for (const std::size_t unbuffered :
|
||||||
|
{
|
||||||
|
1u, 2u, 3u, 5u, 8u, 13u, 21u
|
||||||
|
})
|
||||||
|
{
|
||||||
|
for (const std::size_t chunk_size :
|
||||||
|
{
|
||||||
|
1u, 2u, 3u, 7u, 64u
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(input)
|
||||||
|
CAPTURE(unbuffered)
|
||||||
|
CAPTURE(chunk_size)
|
||||||
|
CHECK(observe(input, chunk_size, read_mode::extract, unbuffered) == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // !defined(JSON_NOEXCEPTION)
|
#endif // !defined(JSON_NOEXCEPTION)
|
||||||
|
|
||||||
SECTION("values are the same as when parsing from a string")
|
SECTION("values are the same as when parsing from a string")
|
||||||
|
|||||||
Reference in new issue
Block a user