Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion include/glaze/bson/read.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,18 @@ namespace glz
}
std::string_view sv{};
if (!bson_detail::read_bson_string(ctx, it, end, sv)) return;
if constexpr (requires { value.assign(sv.data(), sv.size()); }) {
if constexpr (array_char_t<std::remove_cvref_t<decltype(value)>>) {
// Fixed-size std::array<char, N>: bounds-check, copy, zero-fill remainder.
if (sv.size() > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data(), sv.data(), sv.size());
if (sv.size() < value.size()) {
std::memset(value.data() + sv.size(), 0, value.size() - sv.size());
}
}
else if constexpr (requires { value.assign(sv.data(), sv.size()); }) {
value.assign(sv.data(), sv.size());
}
else {
Expand Down
208 changes: 197 additions & 11 deletions include/glaze/cbor/read.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,10 @@ namespace glz
return;
}
else {
value.clear();
if constexpr (resizable<T>) {
value.clear();
}
size_t offset = 0; // fill position for fixed-size targets
while (true) {
if (it >= end) [[unlikely]] {
ctx.error = error_code::unexpected_end;
Expand Down Expand Up @@ -542,9 +545,26 @@ namespace glz
return;
}

value.append(reinterpret_cast<const char*>(it), chunk_len);
if constexpr (array_char_t<T>) {
// Fixed-size std::array<char, N>: accumulate with bounds checking.
if (offset + chunk_len > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data() + offset, it, chunk_len);
offset += static_cast<size_t>(chunk_len);
}
else {
value.append(reinterpret_cast<const char*>(it), chunk_len);
}
it += chunk_len;
}
if constexpr (array_char_t<T>) {
// Zero-fill any unused tail of the fixed-size buffer.
if (offset < value.size()) {
std::memset(value.data() + offset, 0, value.size() - offset);
}
}
}
}
else {
Expand Down Expand Up @@ -575,6 +595,18 @@ namespace glz
if constexpr (string_view_t<T>) {
value = {reinterpret_cast<const char*>(it), static_cast<size_t>(length)};
}
else if constexpr (array_char_t<T>) {
// Fixed-size std::array<char, N>: bounds-check, copy, zero-fill remainder.
if (length > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data(), it, length);
if (length < value.size()) {
std::memset(value.data() + static_cast<size_t>(length), 0,
value.size() - static_cast<size_t>(length));
}
}
else {
value.assign(reinterpret_cast<const char*>(it), length);
}
Expand All @@ -583,9 +615,10 @@ namespace glz
}
};

// Byte strings - std::vector<std::byte>
// Byte strings - contiguous std::byte ranges (std::vector<std::byte>, std::array<std::byte, N>, ...)
template <class T>
requires(std::same_as<typename T::value_type, std::byte> && resizable<T>)
requires(contiguous<std::remove_cvref_t<T>> && std::same_as<range_value_t<std::remove_cvref_t<T>>, std::byte> &&
!str_t<T>)
struct from<CBOR, T>
{
template <auto Opts>
Expand All @@ -612,7 +645,10 @@ namespace glz

if (additional_info == info::indefinite) {
// Indefinite-length byte string
value.clear();
if constexpr (resizable<T>) {
value.clear();
}
size_t offset = 0; // fill position for fixed-size targets
while (true) {
if (it >= end) [[unlikely]] {
ctx.error = error_code::unexpected_end;
Expand Down Expand Up @@ -649,11 +685,28 @@ namespace glz
return;
}

const size_t old_size = value.size();
value.resize(old_size + static_cast<size_t>(chunk_len));
std::memcpy(value.data() + old_size, it, chunk_len);
if constexpr (resizable<T>) {
const size_t old_size = value.size();
value.resize(old_size + static_cast<size_t>(chunk_len));
std::memcpy(value.data() + old_size, it, chunk_len);
}
else {
// Fixed-size std::array<std::byte, N>: accumulate with bounds checking.
if (offset + chunk_len > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data() + offset, it, chunk_len);
offset += static_cast<size_t>(chunk_len);
}
it += chunk_len;
}
if constexpr (!resizable<T>) {
// Zero-fill any unused tail of the fixed-size buffer.
if (offset < value.size()) {
std::memset(value.data() + offset, 0, value.size() - offset);
}
}
}
else {
uint64_t length = cbor_detail::decode_arg(ctx, it, end, additional_info);
Expand All @@ -679,8 +732,22 @@ namespace glz
}
}

value.resize(static_cast<size_t>(length));
std::memcpy(value.data(), it, length);
if constexpr (resizable<T>) {
value.resize(static_cast<size_t>(length));
std::memcpy(value.data(), it, length);
}
else {
// Fixed-size std::array<std::byte, N>: bounds-check, copy, zero-fill remainder.
if (length > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data(), it, length);
if (length < value.size()) {
std::memset(value.data() + static_cast<size_t>(length), 0,
value.size() - static_cast<size_t>(length));
}
}
it += length;
}
}
Expand Down Expand Up @@ -787,10 +854,129 @@ namespace glz
}
};

// Byte strings - std::array<uint8_t, N>
// The matching writer (to<CBOR, std::array<uint8_t, N>>) emits a byte string, so the reader
// must decode one into the fixed-size buffer (bounds-checked, remainder zero-filled), mirroring
// the std::vector<uint8_t> reader above and the std::array<std::byte, N> handling.
template <size_t N>
struct from<CBOR, std::array<uint8_t, N>>
{
template <auto Opts>
static void op(auto& value, is_context auto& ctx, auto& it, auto end)
{
using namespace cbor;

if (it >= end) [[unlikely]] {
ctx.error = error_code::unexpected_end;
return;
}

uint8_t initial;
std::memcpy(&initial, it, 1);
++it;

const uint8_t major_type = get_major_type(initial);
const uint8_t additional_info = get_additional_info(initial);

if (major_type != major::bstr) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}

if (additional_info == info::indefinite) {
size_t offset = 0; // fill position in the fixed-size buffer
while (true) {
if (it >= end) [[unlikely]] {
ctx.error = error_code::unexpected_end;
return;
}

uint8_t chunk_initial;
std::memcpy(&chunk_initial, it, 1);

if (chunk_initial == initial_byte(major::simple, simple::break_code)) {
++it;
break;
}

const uint8_t chunk_major = get_major_type(chunk_initial);
const uint8_t chunk_info = get_additional_info(chunk_initial);

if (chunk_major != major::bstr) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
if (chunk_info == info::indefinite) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}

++it;
uint64_t chunk_len = cbor_detail::decode_arg(ctx, it, end, chunk_info);
if (bool(ctx.error)) [[unlikely]]
return;

if (static_cast<uint64_t>(end - it) < chunk_len) [[unlikely]] {
ctx.error = error_code::unexpected_end;
return;
}

if (offset + chunk_len > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data() + offset, it, chunk_len);
offset += static_cast<size_t>(chunk_len);
it += chunk_len;
}
if (offset < value.size()) {
std::memset(value.data() + offset, 0, value.size() - offset);
}
}
else {
uint64_t length = cbor_detail::decode_arg(ctx, it, end, additional_info);
if (bool(ctx.error)) [[unlikely]]
return;

if (static_cast<uint64_t>(end - it) < length) [[unlikely]] {
ctx.error = error_code::unexpected_end;
return;
}

// Check user-configured array size limit
if constexpr (check_max_array_size(Opts) > 0) {
if (length > check_max_array_size(Opts)) [[unlikely]] {
ctx.error = error_code::invalid_length;
return;
}
}
if constexpr (has_runtime_max_array_size<std::decay_t<decltype(ctx)>>) {
if (ctx.max_array_size > 0 && length > ctx.max_array_size) [[unlikely]] {
ctx.error = error_code::invalid_length;
return;
}
}

if (length > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data(), it, length);
if (length < value.size()) {
std::memset(value.data() + static_cast<size_t>(length), 0,
value.size() - static_cast<size_t>(length));
}
it += length;
}
}
};

// Arrays (std::vector, std::deque, etc.)
// Note: eigen_t types have their own specialization in glaze/ext/eigen.hpp
// Contiguous std::byte ranges are excluded here; they are read as CBOR byte strings above.
template <readable_array_t T>
requires(!eigen_t<T>)
requires(!eigen_t<T> &&
!(contiguous<std::remove_cvref_t<T>> && std::same_as<range_value_t<std::remove_cvref_t<T>>, std::byte>))
struct from<CBOR, T> final
{
template <auto Opts>
Expand Down
7 changes: 5 additions & 2 deletions include/glaze/cbor/write.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ namespace glz

// Byte strings (std::vector<std::byte>, std::span<std::byte>, etc.)
template <class T>
requires(std::same_as<typename T::value_type, std::byte> && !str_t<T>)
requires(contiguous<std::remove_cvref_t<T>> && std::same_as<range_value_t<std::remove_cvref_t<T>>, std::byte> &&
!str_t<T>)
struct to<CBOR, T> final
{
template <auto Opts>
Expand Down Expand Up @@ -469,8 +470,10 @@ namespace glz

// Arrays (std::vector, std::array, std::deque, etc.)
// Note: eigen_t types have their own specialization in glaze/ext/eigen.hpp
// Contiguous std::byte ranges are excluded here; they are written as CBOR byte strings above.
template <writable_array_t T>
requires(!eigen_t<T>)
requires(!eigen_t<T> &&
!(contiguous<std::remove_cvref_t<T>> && std::same_as<range_value_t<std::remove_cvref_t<T>>, std::byte>))
struct to<CBOR, T> final
{
template <auto Opts>
Expand Down
23 changes: 23 additions & 0 deletions include/glaze/msgpack/read.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,29 @@ namespace glz
}
};

// Fixed-size std::array<char, N>: read the string into the buffer, bounds-checked,
// zero-filling any unused tail so a shorter payload yields a deterministic buffer.
template <array_char_t T>
struct from<MSGPACK, T>
{
template <auto Opts, class Value, is_context Ctx, class It, class End>
GLZ_ALWAYS_INLINE static void op(Value&& value, uint8_t tag, Ctx&& ctx, It& it, const End& end) noexcept
{
std::string_view sv{};
if (!msgpack::detail::read_string_view(ctx, tag, it, end, sv)) {
return;
}
if (sv.size() > value.size()) [[unlikely]] {
ctx.error = error_code::syntax_error;
return;
}
std::memcpy(value.data(), sv.data(), sv.size());
if (sv.size() < value.size()) {
std::memset(value.data() + sv.size(), 0, value.size() - sv.size());
}
}
};

template <glaze_object_t T>
requires(!custom_read<T>)
struct from<MSGPACK, T>
Expand Down
Loading
Loading