Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ed3feca3 | ||
|
|
eb2a092d70 | ||
|
|
2a2c7d30f0 |
No files matched your search
@@ -8,18 +8,17 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // count, find, max, min
|
#include <algorithm> // count, find, 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> // declval, move, pair
|
#include <utility> // pair, declval
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
@@ -73,81 +72,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -285,7 +209,6 @@ 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;
|
||||||
@@ -372,20 +295,37 @@ 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
|
||||||
// the character is in the get area: step over it, as sbumpc() does
|
// sbumpc() refills the get area if it is exhausted
|
||||||
const char* const next = streambuf_access::get_ptr(sb);
|
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
||||||
if (JSON_HEDLEY_LIKELY(next != streambuf_access::get_end(sb)))
|
if (JSON_HEDLEY_UNLIKELY(refill))
|
||||||
{
|
{
|
||||||
current_in_get_area = true;
|
save_token_segment();
|
||||||
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 get_character_slow();
|
|
||||||
|
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
|
#endif
|
||||||
|
|
||||||
@@ -394,18 +334,6 @@ 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)))
|
||||||
@@ -493,7 +421,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
|
||||||
{
|
{
|
||||||
token_carry.copy_to(out);
|
out = token_carry;
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
out.insert(out.end(), token_segment, current_end());
|
out.insert(out.end(), token_segment, current_end());
|
||||||
@@ -506,47 +434,6 @@ 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
|
||||||
@@ -570,11 +457,6 @@ 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
|
||||||
@@ -595,53 +477,16 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
token_carry.append(token_segment, streambuf_access::get_end(sb));
|
token_carry.insert(token_carry.end(), 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;
|
||||||
@@ -680,16 +525,13 @@ 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
|
||||||
token_carry_buffer token_carry{}; // NOLINT(readability-redundant-member-init)
|
std::vector<char> 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
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // all_of
|
#include <algorithm> // all_of, copy
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cctype> // isdigit
|
#include <cctype> // isdigit
|
||||||
#include <cerrno> // errno, ERANGE
|
#include <cerrno> // errno, ERANGE
|
||||||
@@ -82,12 +82,34 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
// one buffer: every token adds its escaped form (at least its length) and a slash
|
// one buffer: every token adds its escaped form (at least its length) and a slash
|
||||||
std::size_t length = 0;
|
std::size_t length = 0;
|
||||||
|
bool plain = true;
|
||||||
for (const auto& reference_token : reference_tokens)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
length += reference_token.size() + 1;
|
length += reference_token.size() + 1;
|
||||||
|
const char* const data = reference_token.data();
|
||||||
|
for (std::size_t i = 0, size = reference_token.size(); plain && i < size; ++i)
|
||||||
|
{
|
||||||
|
plain = data[i] != '~' && data[i] != '/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t result;
|
string_t result;
|
||||||
|
if (JSON_HEDLEY_LIKELY(plain))
|
||||||
|
{
|
||||||
|
// nothing to escape: the tokens are copied into the string
|
||||||
|
result.resize(length);
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
char* out = &result[0];
|
||||||
|
for (const auto& reference_token : reference_tokens)
|
||||||
|
{
|
||||||
|
*out++ = '/';
|
||||||
|
out = std::copy(reference_token.data(), reference_token.data() + reference_token.size(), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
result.reserve(length);
|
result.reserve(length);
|
||||||
for (const auto& reference_token : reference_tokens)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
@@ -1198,33 +1220,32 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
result.reserve(token_count);
|
result.reserve(token_count);
|
||||||
|
|
||||||
// extract the reference tokens:
|
// extract the reference tokens: every one runs from after a slash to
|
||||||
// - slash: position of the last read slash (or end of string)
|
// the next slash or the end of the string; a token without a '~' (the
|
||||||
// - start: position after the previous slash
|
// common case) is stored as it is, others are checked and unescaped
|
||||||
for (
|
const char* const data = reference_string.data();
|
||||||
// search for the first slash after the first character
|
const char* const end = data + size;
|
||||||
std::size_t slash = reference_string.find_first_of('/', 1),
|
for (const char* start = data + 1;; ++start)
|
||||||
// set the beginning of the first reference token
|
|
||||||
start = 1;
|
|
||||||
// we can stop if start == 0 (if slash == string_t::npos)
|
|
||||||
start != 0;
|
|
||||||
// set the beginning of the next reference token
|
|
||||||
// (will eventually be 0 if slash == string_t::npos)
|
|
||||||
start = (slash == string_t::npos) ? 0 : slash + 1,
|
|
||||||
// find next slash
|
|
||||||
slash = reference_string.find_first_of('/', start))
|
|
||||||
{
|
{
|
||||||
// use the text between the beginning of the reference token
|
const char* slash = start;
|
||||||
// (start) and the last slash (slash).
|
bool escaped = false;
|
||||||
const auto count = (slash == string_t::npos ? reference_string.size() : slash) - start;
|
while (slash != end && *slash != '/')
|
||||||
auto reference_token = string_t(reference_string.data() + start, count);
|
{
|
||||||
|
escaped = escaped || *slash == '~';
|
||||||
|
++slash;
|
||||||
|
}
|
||||||
|
|
||||||
// check reference tokens are properly escaped; a token without
|
const auto count = static_cast<std::size_t>(slash - start);
|
||||||
// a '~' (the common case) needs neither check nor unescaping
|
if (JSON_HEDLEY_LIKELY(!escaped))
|
||||||
std::size_t pos = reference_token.find_first_of('~');
|
|
||||||
if (pos != string_t::npos)
|
|
||||||
{
|
{
|
||||||
for (; pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
result.emplace_back(start, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto reference_token = string_t(start, count);
|
||||||
|
|
||||||
|
// check reference tokens are properly escaped
|
||||||
|
for (std::size_t pos = reference_token.find_first_of('~'); pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(reference_token[pos] == '~');
|
JSON_ASSERT(reference_token[pos] == '~');
|
||||||
|
|
||||||
@@ -1238,10 +1259,14 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
|
|
||||||
detail::unescape(reference_token);
|
detail::unescape(reference_token);
|
||||||
|
result.push_back(std::move(reference_token));
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally, store the reference token
|
if (slash == end)
|
||||||
result.push_back(std::move(reference_token));
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = slash;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Generated
+85
-218
@@ -8468,18 +8468,17 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <algorithm> // count, find, max, min
|
#include <algorithm> // count, find, 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> // declval, move, pair
|
#include <utility> // pair, declval
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
@@ -8538,81 +8537,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -8750,7 +8674,6 @@ 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;
|
||||||
@@ -8837,20 +8760,37 @@ 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
|
||||||
// the character is in the get area: step over it, as sbumpc() does
|
// sbumpc() refills the get area if it is exhausted
|
||||||
const char* const next = streambuf_access::get_ptr(sb);
|
const bool refill = streambuf_access::get_ptr(sb) == streambuf_access::get_end(sb);
|
||||||
if (JSON_HEDLEY_LIKELY(next != streambuf_access::get_end(sb)))
|
if (JSON_HEDLEY_UNLIKELY(refill))
|
||||||
{
|
{
|
||||||
current_in_get_area = true;
|
save_token_segment();
|
||||||
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 get_character_slow();
|
|
||||||
|
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
|
#endif
|
||||||
|
|
||||||
@@ -8859,18 +8799,6 @@ 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)))
|
||||||
@@ -8958,7 +8886,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
|
||||||
{
|
{
|
||||||
token_carry.copy_to(out);
|
out = token_carry;
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
out.insert(out.end(), token_segment, current_end());
|
out.insert(out.end(), token_segment, current_end());
|
||||||
@@ -8971,47 +8899,6 @@ 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
|
||||||
@@ -9035,11 +8922,6 @@ 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
|
||||||
@@ -9060,53 +8942,16 @@ class input_stream_adapter
|
|||||||
{
|
{
|
||||||
if (token_segment != nullptr)
|
if (token_segment != nullptr)
|
||||||
{
|
{
|
||||||
token_carry.append(token_segment, streambuf_access::get_end(sb));
|
token_carry.insert(token_carry.end(), 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;
|
||||||
@@ -9145,16 +8990,13 @@ 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
|
||||||
token_carry_buffer token_carry{}; // NOLINT(readability-redundant-member-init)
|
std::vector<char> 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
|
||||||
@@ -22508,7 +22350,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <algorithm> // all_of
|
#include <algorithm> // all_of, copy
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cctype> // isdigit
|
#include <cctype> // isdigit
|
||||||
#include <cerrno> // errno, ERANGE
|
#include <cerrno> // errno, ERANGE
|
||||||
@@ -22590,12 +22432,34 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
// one buffer: every token adds its escaped form (at least its length) and a slash
|
// one buffer: every token adds its escaped form (at least its length) and a slash
|
||||||
std::size_t length = 0;
|
std::size_t length = 0;
|
||||||
|
bool plain = true;
|
||||||
for (const auto& reference_token : reference_tokens)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
length += reference_token.size() + 1;
|
length += reference_token.size() + 1;
|
||||||
|
const char* const data = reference_token.data();
|
||||||
|
for (std::size_t i = 0, size = reference_token.size(); plain && i < size; ++i)
|
||||||
|
{
|
||||||
|
plain = data[i] != '~' && data[i] != '/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t result;
|
string_t result;
|
||||||
|
if (JSON_HEDLEY_LIKELY(plain))
|
||||||
|
{
|
||||||
|
// nothing to escape: the tokens are copied into the string
|
||||||
|
result.resize(length);
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
char* out = &result[0];
|
||||||
|
for (const auto& reference_token : reference_tokens)
|
||||||
|
{
|
||||||
|
*out++ = '/';
|
||||||
|
out = std::copy(reference_token.data(), reference_token.data() + reference_token.size(), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
result.reserve(length);
|
result.reserve(length);
|
||||||
for (const auto& reference_token : reference_tokens)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
@@ -23706,33 +23570,32 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
result.reserve(token_count);
|
result.reserve(token_count);
|
||||||
|
|
||||||
// extract the reference tokens:
|
// extract the reference tokens: every one runs from after a slash to
|
||||||
// - slash: position of the last read slash (or end of string)
|
// the next slash or the end of the string; a token without a '~' (the
|
||||||
// - start: position after the previous slash
|
// common case) is stored as it is, others are checked and unescaped
|
||||||
for (
|
const char* const data = reference_string.data();
|
||||||
// search for the first slash after the first character
|
const char* const end = data + size;
|
||||||
std::size_t slash = reference_string.find_first_of('/', 1),
|
for (const char* start = data + 1;; ++start)
|
||||||
// set the beginning of the first reference token
|
|
||||||
start = 1;
|
|
||||||
// we can stop if start == 0 (if slash == string_t::npos)
|
|
||||||
start != 0;
|
|
||||||
// set the beginning of the next reference token
|
|
||||||
// (will eventually be 0 if slash == string_t::npos)
|
|
||||||
start = (slash == string_t::npos) ? 0 : slash + 1,
|
|
||||||
// find next slash
|
|
||||||
slash = reference_string.find_first_of('/', start))
|
|
||||||
{
|
{
|
||||||
// use the text between the beginning of the reference token
|
const char* slash = start;
|
||||||
// (start) and the last slash (slash).
|
bool escaped = false;
|
||||||
const auto count = (slash == string_t::npos ? reference_string.size() : slash) - start;
|
while (slash != end && *slash != '/')
|
||||||
auto reference_token = string_t(reference_string.data() + start, count);
|
{
|
||||||
|
escaped = escaped || *slash == '~';
|
||||||
|
++slash;
|
||||||
|
}
|
||||||
|
|
||||||
// check reference tokens are properly escaped; a token without
|
const auto count = static_cast<std::size_t>(slash - start);
|
||||||
// a '~' (the common case) needs neither check nor unescaping
|
if (JSON_HEDLEY_LIKELY(!escaped))
|
||||||
std::size_t pos = reference_token.find_first_of('~');
|
|
||||||
if (pos != string_t::npos)
|
|
||||||
{
|
{
|
||||||
for (; pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
result.emplace_back(start, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto reference_token = string_t(start, count);
|
||||||
|
|
||||||
|
// check reference tokens are properly escaped
|
||||||
|
for (std::size_t pos = reference_token.find_first_of('~'); pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(reference_token[pos] == '~');
|
JSON_ASSERT(reference_token[pos] == '~');
|
||||||
|
|
||||||
@@ -23746,10 +23609,14 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
|
|
||||||
detail::unescape(reference_token);
|
detail::unescape(reference_token);
|
||||||
|
result.push_back(std::move(reference_token));
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally, store the reference token
|
if (slash == end)
|
||||||
result.push_back(std::move(reference_token));
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = slash;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -48,16 +48,14 @@ namespace
|
|||||||
class chunked_streambuf : public std::streambuf
|
class chunked_streambuf : public std::streambuf
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// The first @a unbuffered characters are served without a get area, the
|
chunked_streambuf(std::string s, std::size_t chunk_size)
|
||||||
// rest in get areas of @a chunk_size.
|
: m_data(std::move(s)), m_chunk_size(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 || m_pos < m_unbuffered)
|
if (m_chunk_size == 0)
|
||||||
{
|
{
|
||||||
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();
|
||||||
}
|
}
|
||||||
@@ -79,7 +77,7 @@ class chunked_streambuf : public std::streambuf
|
|||||||
|
|
||||||
int_type uflow() override
|
int_type uflow() override
|
||||||
{
|
{
|
||||||
if (m_chunk_size == 0 || m_pos < m_unbuffered)
|
if (m_chunk_size == 0)
|
||||||
{
|
{
|
||||||
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();
|
||||||
}
|
}
|
||||||
@@ -90,7 +88,6 @@ 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
|
||||||
@@ -104,9 +101,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::size_t unbuffered = 0)
|
std::string observe(const std::string& input, std::size_t chunk_size, read_mode mode)
|
||||||
{
|
{
|
||||||
chunked_streambuf buf(input, chunk_size, unbuffered);
|
chunked_streambuf buf(input, chunk_size);
|
||||||
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)
|
||||||
@@ -215,31 +212,6 @@ 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