Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
759a847178 | ||
|
|
a33af0a51b | ||
|
|
baf0bee38f |
No files matched your search
@@ -4887,6 +4887,14 @@ class binary_reader
|
|||||||
const char* context,
|
const char* context,
|
||||||
ContainerType& result)
|
ContainerType& result)
|
||||||
{
|
{
|
||||||
|
// a string that is in the block of input as a whole is appended from
|
||||||
|
// it, without the zero-filling resize() below
|
||||||
|
constexpr bool from_block = bulk_scan && std::is_same<ContainerType, string_t>::value;
|
||||||
|
if (append_from_block(len, result, std::integral_constant<bool, from_block> {}))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// upper bound on the number of bytes read (and allocated) per chunk
|
// upper bound on the number of bytes read (and allocated) per chunk
|
||||||
constexpr std::size_t chunk_size = 4096;
|
constexpr std::size_t chunk_size = 4096;
|
||||||
|
|
||||||
@@ -4923,6 +4931,36 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// append the @a len bytes at the current position to @a result, if the
|
||||||
|
/// input holds them in one block; false (with nothing read) otherwise
|
||||||
|
template<typename NumberType>
|
||||||
|
bool append_from_block(const NumberType len, string_t& result, std::true_type /*block*/)
|
||||||
|
{
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// (len > 0, so the unsigned type holds it; casting a signed char
|
||||||
|
// straight to std::size_t would trip bugprone-signed-char-misuse)
|
||||||
|
const auto ulen = static_cast<typename std::make_unsigned<NumberType>::type>(len);
|
||||||
|
const std::size_t remaining = ia.bulk_remaining();
|
||||||
|
if (static_cast<std::uintmax_t>(ulen) > static_cast<std::uintmax_t>(remaining))
|
||||||
|
{
|
||||||
|
return false; // (the loop reports the premature end)
|
||||||
|
}
|
||||||
|
const auto n = static_cast<std::size_t>(ulen);
|
||||||
|
result.append(reinterpret_cast<const typename string_t::value_type*>(ia.bulk_data()), n);
|
||||||
|
ia.bulk_skip(n);
|
||||||
|
chars_read += n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NumberType, typename ContainerType>
|
||||||
|
static bool append_from_block(const NumberType /*len*/, ContainerType& /*result*/, std::false_type /*block*/) noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] context further context information (for diagnostics)
|
@param[in] context further context information (for diagnostics)
|
||||||
@return whether the last read character is not EOF
|
@return whether the last read character is not EOF
|
||||||
|
|||||||
@@ -1173,55 +1173,6 @@ struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief whether @a IteratorType is an iterator of std::string or of a
|
|
||||||
std::vector of 1-byte elements
|
|
||||||
|
|
||||||
Their elements are contiguous, but before C++20 that cannot be detected from
|
|
||||||
the iterator type (std::contiguous_iterator), so an iterator pair of them would
|
|
||||||
be read one character at a time through the iterators. They are read as the
|
|
||||||
pointer range they are instead, as the containers themselves are (see the
|
|
||||||
container overload of input_adapter()). The types are matched exactly, so no
|
|
||||||
other iterator can be mistaken for one. (std::basic_string is only matched for
|
|
||||||
char: other character types need std::char_traits specializations that the
|
|
||||||
standard library need not have.)
|
|
||||||
*/
|
|
||||||
template < typename IteratorType, typename CharType, bool IsByte = sizeof(CharType) == 1 && std::is_integral<CharType>::value && !std::is_same<CharType, bool>::value >
|
|
||||||
struct is_std_contiguous_byte_iterator_impl : std::false_type {};
|
|
||||||
|
|
||||||
// (the containers are only named for byte types)
|
|
||||||
template<typename IteratorType, typename CharType>
|
|
||||||
struct is_std_contiguous_byte_iterator_impl<IteratorType, CharType, true>
|
|
||||||
: std::integral_constant < bool, !std::is_pointer<IteratorType>::value
|
|
||||||
&& (std::is_same<IteratorType, typename std::vector<CharType>::iterator>::value
|
|
||||||
|| std::is_same<IteratorType, typename std::vector<CharType>::const_iterator>::value
|
|
||||||
|| std::is_same<IteratorType, std::string::iterator>::value
|
|
||||||
|| std::is_same<IteratorType, std::string::const_iterator>::value) > {};
|
|
||||||
|
|
||||||
template<typename IteratorType>
|
|
||||||
struct is_std_contiguous_byte_iterator
|
|
||||||
: is_std_contiguous_byte_iterator_impl<IteratorType, typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type> {};
|
|
||||||
|
|
||||||
template<typename IteratorType>
|
|
||||||
struct iterator_input_adapter_factory<IteratorType, IteratorType, enable_if_t<is_std_contiguous_byte_iterator<IteratorType>::value>>
|
|
||||||
{
|
|
||||||
using iterator_type = IteratorType;
|
|
||||||
using sentinel_type = IteratorType;
|
|
||||||
using char_type = typename std::remove_cv<typename std::iterator_traits<iterator_type>::value_type>::type;
|
|
||||||
using adapter_type = iterator_input_adapter<const char_type*>;
|
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
|
||||||
{
|
|
||||||
if (first == last)
|
|
||||||
{
|
|
||||||
// (an empty range must not be dereferenced)
|
|
||||||
return adapter_type(nullptr, nullptr);
|
|
||||||
}
|
|
||||||
const char_type* const begin = std::addressof(*first);
|
|
||||||
return adapter_type(begin, begin + (last - first));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||||
// defaults to IteratorType for the common same-type case, but may differ for
|
// defaults to IteratorType for the common same-type case, but may differ for
|
||||||
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||||
|
|||||||
Generated
+38
-49
@@ -10282,55 +10282,6 @@ struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief whether @a IteratorType is an iterator of std::string or of a
|
|
||||||
std::vector of 1-byte elements
|
|
||||||
|
|
||||||
Their elements are contiguous, but before C++20 that cannot be detected from
|
|
||||||
the iterator type (std::contiguous_iterator), so an iterator pair of them would
|
|
||||||
be read one character at a time through the iterators. They are read as the
|
|
||||||
pointer range they are instead, as the containers themselves are (see the
|
|
||||||
container overload of input_adapter()). The types are matched exactly, so no
|
|
||||||
other iterator can be mistaken for one. (std::basic_string is only matched for
|
|
||||||
char: other character types need std::char_traits specializations that the
|
|
||||||
standard library need not have.)
|
|
||||||
*/
|
|
||||||
template < typename IteratorType, typename CharType, bool IsByte = sizeof(CharType) == 1 && std::is_integral<CharType>::value && !std::is_same<CharType, bool>::value >
|
|
||||||
struct is_std_contiguous_byte_iterator_impl : std::false_type {};
|
|
||||||
|
|
||||||
// (the containers are only named for byte types)
|
|
||||||
template<typename IteratorType, typename CharType>
|
|
||||||
struct is_std_contiguous_byte_iterator_impl<IteratorType, CharType, true>
|
|
||||||
: std::integral_constant < bool, !std::is_pointer<IteratorType>::value
|
|
||||||
&& (std::is_same<IteratorType, typename std::vector<CharType>::iterator>::value
|
|
||||||
|| std::is_same<IteratorType, typename std::vector<CharType>::const_iterator>::value
|
|
||||||
|| std::is_same<IteratorType, std::string::iterator>::value
|
|
||||||
|| std::is_same<IteratorType, std::string::const_iterator>::value) > {};
|
|
||||||
|
|
||||||
template<typename IteratorType>
|
|
||||||
struct is_std_contiguous_byte_iterator
|
|
||||||
: is_std_contiguous_byte_iterator_impl<IteratorType, typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type> {};
|
|
||||||
|
|
||||||
template<typename IteratorType>
|
|
||||||
struct iterator_input_adapter_factory<IteratorType, IteratorType, enable_if_t<is_std_contiguous_byte_iterator<IteratorType>::value>>
|
|
||||||
{
|
|
||||||
using iterator_type = IteratorType;
|
|
||||||
using sentinel_type = IteratorType;
|
|
||||||
using char_type = typename std::remove_cv<typename std::iterator_traits<iterator_type>::value_type>::type;
|
|
||||||
using adapter_type = iterator_input_adapter<const char_type*>;
|
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
|
||||||
{
|
|
||||||
if (first == last)
|
|
||||||
{
|
|
||||||
// (an empty range must not be dereferenced)
|
|
||||||
return adapter_type(nullptr, nullptr);
|
|
||||||
}
|
|
||||||
const char_type* const begin = std::addressof(*first);
|
|
||||||
return adapter_type(begin, begin + (last - first));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||||
// defaults to IteratorType for the common same-type case, but may differ for
|
// defaults to IteratorType for the common same-type case, but may differ for
|
||||||
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||||
@@ -22952,6 +22903,14 @@ class binary_reader
|
|||||||
const char* context,
|
const char* context,
|
||||||
ContainerType& result)
|
ContainerType& result)
|
||||||
{
|
{
|
||||||
|
// a string that is in the block of input as a whole is appended from
|
||||||
|
// it, without the zero-filling resize() below
|
||||||
|
constexpr bool from_block = bulk_scan && std::is_same<ContainerType, string_t>::value;
|
||||||
|
if (append_from_block(len, result, std::integral_constant<bool, from_block> {}))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// upper bound on the number of bytes read (and allocated) per chunk
|
// upper bound on the number of bytes read (and allocated) per chunk
|
||||||
constexpr std::size_t chunk_size = 4096;
|
constexpr std::size_t chunk_size = 4096;
|
||||||
|
|
||||||
@@ -22988,6 +22947,36 @@ class binary_reader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// append the @a len bytes at the current position to @a result, if the
|
||||||
|
/// input holds them in one block; false (with nothing read) otherwise
|
||||||
|
template<typename NumberType>
|
||||||
|
bool append_from_block(const NumberType len, string_t& result, std::true_type /*block*/)
|
||||||
|
{
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// (len > 0, so the unsigned type holds it; casting a signed char
|
||||||
|
// straight to std::size_t would trip bugprone-signed-char-misuse)
|
||||||
|
const auto ulen = static_cast<typename std::make_unsigned<NumberType>::type>(len);
|
||||||
|
const std::size_t remaining = ia.bulk_remaining();
|
||||||
|
if (static_cast<std::uintmax_t>(ulen) > static_cast<std::uintmax_t>(remaining))
|
||||||
|
{
|
||||||
|
return false; // (the loop reports the premature end)
|
||||||
|
}
|
||||||
|
const auto n = static_cast<std::size_t>(ulen);
|
||||||
|
result.append(reinterpret_cast<const typename string_t::value_type*>(ia.bulk_data()), n);
|
||||||
|
ia.bulk_skip(n);
|
||||||
|
chars_read += n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NumberType, typename ContainerType>
|
||||||
|
static bool append_from_block(const NumberType /*len*/, ContainerType& /*result*/, std::false_type /*block*/) noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] context further context information (for diagnostics)
|
@param[in] context further context information (for diagnostics)
|
||||||
@return whether the last read character is not EOF
|
@return whether the last read character is not EOF
|
||||||
|
|||||||
@@ -1674,8 +1674,6 @@ TEST_CASE("BSON input that cannot be read is discarded by every overload")
|
|||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::from_bson(input.begin(), input.end()), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::from_bson(input.begin(), input.end()), json::parse_error&);
|
||||||
CHECK(json::from_bson(input, true, false).is_discarded());
|
CHECK(json::from_bson(input, true, false).is_discarded());
|
||||||
// false positive: Infer takes std::integral_constant<bool, false>::value for a pointer
|
|
||||||
// @infer-ignore NULLPTR_DEREFERENCE
|
|
||||||
CHECK(json::from_bson(input.begin(), input.end(), true, false).is_discarded());
|
CHECK(json::from_bson(input.begin(), input.end(), true, false).is_discarded());
|
||||||
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
#ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED
|
||||||
CHECK(json::from_bson(input.data(), input.size(), true, false).is_discarded());
|
CHECK(json::from_bson(input.data(), input.size(), true, false).is_discarded());
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <list>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <valarray>
|
#include <valarray>
|
||||||
@@ -1410,64 +1409,3 @@ TEST_CASE("indentation runs of every length around the 8-byte skip")
|
|||||||
CHECK(parse_result(indented("[\n~1]", std::string(17, ' ')), true) == "[1]");
|
CHECK(parse_result(indented("[\n~1]", std::string(17, ' ')), true) == "[1]");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
/// the value of parsing [first, last), or the message of the exception
|
|
||||||
template<typename Iterator>
|
|
||||||
std::string outcome_of(Iterator first, Iterator last)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return json::parse(first, last).dump();
|
|
||||||
}
|
|
||||||
catch (const json::exception& e)
|
|
||||||
{
|
|
||||||
return e.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// a std::string, std::vector<char>, <unsigned char> and <signed char> as
|
|
||||||
/// iterator pairs: the same as the byte path (std::list iterators)
|
|
||||||
void check_iterator_pairs(const std::string& text)
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
const std::list<char> bytes(text.begin(), text.end());
|
|
||||||
const std::string expected = outcome_of(bytes.begin(), bytes.end());
|
|
||||||
const bool expected_accept = json::accept(bytes.begin(), bytes.end());
|
|
||||||
|
|
||||||
std::string s = text;
|
|
||||||
CHECK(outcome_of(s.begin(), s.end()) == expected);
|
|
||||||
CHECK(outcome_of(s.cbegin(), s.cend()) == expected);
|
|
||||||
CHECK(json::accept(s.cbegin(), s.cend()) == expected_accept);
|
|
||||||
|
|
||||||
const std::vector<char> vc(text.begin(), text.end());
|
|
||||||
CHECK(outcome_of(vc.begin(), vc.end()) == expected);
|
|
||||||
const std::vector<unsigned char> vu(text.begin(), text.end());
|
|
||||||
CHECK(outcome_of(vu.begin(), vu.end()) == expected);
|
|
||||||
CHECK(json::accept(vu.begin(), vu.end()) == expected_accept);
|
|
||||||
const std::vector<signed char> vs(text.begin(), text.end());
|
|
||||||
CHECK(outcome_of(vs.begin(), vs.end()) == expected);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("deserialization from iterator pairs of std::string and std::vector")
|
|
||||||
{
|
|
||||||
// these are read as the pointer range they are, also before C++20
|
|
||||||
for (const char* text :
|
|
||||||
{
|
|
||||||
"", " ", R"([1, 2.5, "a", true, null, {"k": ["v"]}])", "[1, 2", R"({"a" 1})", "\"\xC3\xA4\"", "\"\xC3\"", "[1]x", "1e400"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_iterator_pairs(text);
|
|
||||||
}
|
|
||||||
check_iterator_pairs(std::string("[1]" "\0" "x", 5));
|
|
||||||
|
|
||||||
// an empty range is not dereferenced
|
|
||||||
std::string empty;
|
|
||||||
CHECK_FALSE(json::accept(empty.begin(), empty.end()));
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_AS(_ = json::parse(empty.begin(), empty.end()), json::parse_error&);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Reference in new issue
Block a user