mirror of
https://github.com/nlohmann/json.git
synced 2026-08-29 04:17:32 +00:00
Require a container's value_type to match what data() points at
is_contiguous_byte_container accepted any type with a data() returning a
pointer to a single-byte integral plus a size(). That is duck typing: the two
members say nothing about size() counting the units data() points at.
A type where it does not - fixed-size records, say - was routed to the
pointer-based adapter and parsed as [data(), data() + size()) bytes, silently
truncating input the iterator-based adapter had read in full:
struct record_buffer {
using value_type = std::array<char, 4>;
std::string bytes;
const char* data() const; // raw bytes
std::size_t size() const; // in records
const char* begin() const; const char* end() const;
};
json::parse(record_buffer{"[1,2,3,4,5]"}); // parse error at column 3
Requiring the container's own value_type to be that same element type ties the
two together. Every contiguous standard container satisfies it, so std::string,
std::vector<char>, std::array<char, N> and std::string_view keep the fast path;
anything else falls back to the iterator-based adapter, which is always correct.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MXDi7NTMKAmoArUZSKMc4T
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -18,6 +18,9 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t
|
||||
#include <list>
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
@@ -214,6 +217,65 @@ TEST_CASE("Parse with heterogeneous iterator and sentinel types")
|
||||
CHECK(j2.at(0) == 1);
|
||||
}
|
||||
|
||||
// A type whose data() hands out raw bytes but whose size() counts something
|
||||
// else - here fixed-size records. Reading [data(), data() + size()) as bytes
|
||||
// would silently truncate the input, so data() and size() alone must not be
|
||||
// taken as evidence of contiguous byte storage.
|
||||
struct record_buffer
|
||||
{
|
||||
using value_type = std::array<char, 4>;
|
||||
|
||||
std::string bytes;
|
||||
|
||||
const char* data() const noexcept
|
||||
{
|
||||
return bytes.data();
|
||||
}
|
||||
std::size_t size() const noexcept
|
||||
{
|
||||
return bytes.size() / sizeof(value_type);
|
||||
}
|
||||
const char* begin() const noexcept
|
||||
{
|
||||
return bytes.data();
|
||||
}
|
||||
const char* end() const noexcept
|
||||
{
|
||||
return bytes.data() + bytes.size();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("Contiguous byte containers take the pointer adapter")
|
||||
{
|
||||
// Containers with contiguous single-byte storage are routed through the
|
||||
// pointer-based adapter so the bulk fast paths apply in every standard, not
|
||||
// only in C++20 where the library iterators model std::contiguous_iterator.
|
||||
CHECK(nlohmann::detail::is_contiguous_byte_container<std::string>::value);
|
||||
CHECK(nlohmann::detail::is_contiguous_byte_container<std::vector<char>>::value);
|
||||
CHECK(nlohmann::detail::is_contiguous_byte_container<std::vector<std::uint8_t>>::value);
|
||||
CHECK(nlohmann::detail::is_contiguous_byte_container<std::array<char, 4>>::value);
|
||||
|
||||
// input_adapter() takes its container by forwarding reference, so the trait
|
||||
// is also asked about reference types
|
||||
CHECK(nlohmann::detail::is_contiguous_byte_container<std::string&>::value);
|
||||
CHECK(nlohmann::detail::is_contiguous_byte_container<const std::string&>::value);
|
||||
|
||||
// everything else keeps the iterator-based adapter
|
||||
CHECK_FALSE(nlohmann::detail::is_contiguous_byte_container<std::list<char>>::value);
|
||||
CHECK_FALSE(nlohmann::detail::is_contiguous_byte_container<std::vector<int>>::value);
|
||||
CHECK_FALSE(nlohmann::detail::is_contiguous_byte_container<const char*>::value);
|
||||
|
||||
// including a type that has data() and size() but whose size() does not
|
||||
// count the units data() points at: its value_type says so
|
||||
CHECK_FALSE(nlohmann::detail::is_contiguous_byte_container<record_buffer>::value);
|
||||
|
||||
// and such a container still parses through its iterators, in full - taking
|
||||
// it for a byte container would stop after data() + size() bytes
|
||||
const record_buffer buffer{"[1,2,3,4,5]"};
|
||||
CHECK(buffer.size() * sizeof(record_buffer::value_type) < buffer.bytes.size());
|
||||
CHECK(json::parse(buffer) == json({1, 2, 3, 4, 5}));
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
|
||||
TEST_CASE("Parse with std::counted_iterator and std::default_sentinel_t")
|
||||
|
||||
Reference in New Issue
Block a user