Skip to content

Commit a91b761

Browse files
committed
Support skipping BEVE extensions
1 parent 45f6ded commit a91b761

File tree

2 files changed

+220
-3
lines changed

2 files changed

+220
-3
lines changed

include/glaze/beve/skip.hpp

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,65 @@ namespace glz
239239
}
240240

241241
template <auto Opts>
242-
GLZ_ALWAYS_INLINE void skip_additional_beve(is_context auto&& ctx, auto&& it, auto end) noexcept
242+
GLZ_ALWAYS_INLINE void skip_beve_extensions(is_context auto&& ctx, auto&& it, auto end) noexcept
243243
{
244+
const auto ext_tag = uint8_t(*it);
245+
const uint8_t subtype = (ext_tag >> 3) & 0b11;
244246
++it;
245-
skip_value<BEVE>::op<Opts>(ctx, it, end);
247+
248+
switch (subtype) {
249+
case 0: // delimiter: no payload
250+
return;
251+
case 1: { // variant: [compressed_int index] [value]
252+
skip_compressed_int(ctx, it, end);
253+
if (bool(ctx.error)) [[unlikely]] {
254+
return;
255+
}
256+
skip_value<BEVE>::op<Opts>(ctx, it, end);
257+
break;
258+
}
259+
case 2: { // matrix: [matrix_header] [extents (typed array)] [value (typed array)]
260+
++it; // skip matrix header
261+
skip_value<BEVE>::op<Opts>(ctx, it, end); // skip extents
262+
if (bool(ctx.error)) [[unlikely]] {
263+
return;
264+
}
265+
skip_value<BEVE>::op<Opts>(ctx, it, end); // skip value
266+
break;
267+
}
268+
case 3: { // complex: [complex_header] [data...]
269+
if (invalid_end(ctx, it, end)) {
270+
return;
271+
}
272+
const auto complex_header = uint8_t(*it);
273+
++it;
274+
const uint8_t elem_byte_count = byte_count_lookup[complex_header >> 5];
275+
const bool is_array = (complex_header & 1) != 0;
276+
if (is_array) {
277+
const auto n = int_from_compressed(ctx, it, end);
278+
if (bool(ctx.error)) [[unlikely]] {
279+
return;
280+
}
281+
const uint64_t total = uint64_t(elem_byte_count) * 2 * n;
282+
if (uint64_t(end - it) < total) [[unlikely]] {
283+
ctx.error = error_code::unexpected_end;
284+
return;
285+
}
286+
it += total;
287+
}
288+
else {
289+
const uint64_t total = uint64_t(elem_byte_count) * 2;
290+
if (uint64_t(end - it) < total) [[unlikely]] {
291+
ctx.error = error_code::unexpected_end;
292+
return;
293+
}
294+
it += total;
295+
}
296+
break;
297+
}
298+
default:
299+
ctx.error = error_code::syntax_error;
300+
}
246301
}
247302

248303
template <auto Opts>
@@ -279,7 +334,7 @@ namespace glz
279334
break;
280335
}
281336
case tag::extensions: {
282-
skip_additional_beve<Opts>(ctx, it, end);
337+
skip_beve_extensions<Opts>(ctx, it, end);
283338
break;
284339
}
285340
default:

tests/beve_test/beve_test.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6396,6 +6396,168 @@ suite expected_tests = [] {
63966396
};
63976397
};
63986398

6399+
// Test for https://github.com/stephenberry/glaze/issues/2422
6400+
// BEVE skip of variant-encoded values when error_on_unknown_keys is false
6401+
struct OldDiag
6402+
{
6403+
std::string rule_id{};
6404+
std::variant<std::monostate, std::string, int> args{};
6405+
std::string severity{};
6406+
};
6407+
6408+
struct NewDiag
6409+
{
6410+
std::string rule_id{};
6411+
std::string message{};
6412+
std::string severity{};
6413+
};
6414+
6415+
suite beve_skip_variant_suite = [] {
6416+
6417+
"skip variant string when key removed"_test = [] {
6418+
OldDiag old_diag{.rule_id = "RULE_001", .args = std::string{"field overlap"}, .severity = "error"};
6419+
std::string buffer{};
6420+
expect(not glz::write_beve(old_diag, buffer));
6421+
6422+
NewDiag new_diag{};
6423+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6424+
auto ec = glz::read<opts>(new_diag, buffer);
6425+
expect(!ec) << glz::format_error(ec, buffer);
6426+
expect(new_diag.rule_id == "RULE_001");
6427+
expect(new_diag.severity == "error");
6428+
expect(new_diag.message.empty());
6429+
};
6430+
6431+
"skip variant monostate when key removed"_test = [] {
6432+
OldDiag old_diag{.rule_id = "R2", .args = std::monostate{}, .severity = "warn"};
6433+
std::string buffer{};
6434+
expect(not glz::write_beve(old_diag, buffer));
6435+
6436+
NewDiag new_diag{};
6437+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6438+
auto ec = glz::read<opts>(new_diag, buffer);
6439+
expect(!ec) << glz::format_error(ec, buffer);
6440+
expect(new_diag.rule_id == "R2");
6441+
expect(new_diag.severity == "warn");
6442+
};
6443+
6444+
"skip variant int when key removed"_test = [] {
6445+
OldDiag old_diag{.rule_id = "R3", .args = 42, .severity = "info"};
6446+
std::string buffer{};
6447+
expect(not glz::write_beve(old_diag, buffer));
6448+
6449+
NewDiag new_diag{};
6450+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6451+
auto ec = glz::read<opts>(new_diag, buffer);
6452+
expect(!ec) << glz::format_error(ec, buffer);
6453+
expect(new_diag.rule_id == "R3");
6454+
expect(new_diag.severity == "info");
6455+
};
6456+
};
6457+
6458+
// Test BEVE skip of complex extension types when error_on_unknown_keys is false
6459+
struct WithComplex
6460+
{
6461+
int id{};
6462+
std::complex<double> value{};
6463+
std::string name{};
6464+
};
6465+
6466+
struct WithComplexFloat
6467+
{
6468+
int id{};
6469+
std::complex<float> value{};
6470+
std::string name{};
6471+
};
6472+
6473+
struct WithComplexArray
6474+
{
6475+
int id{};
6476+
std::vector<std::complex<double>> values{};
6477+
std::string name{};
6478+
};
6479+
6480+
struct WithComplexFloatArray
6481+
{
6482+
int id{};
6483+
std::vector<std::complex<float>> values{};
6484+
std::string name{};
6485+
};
6486+
6487+
struct SkipSimple
6488+
{
6489+
int id{};
6490+
std::string name{};
6491+
};
6492+
6493+
suite beve_skip_complex_suite = [] {
6494+
6495+
"skip complex<double> when key removed"_test = [] {
6496+
WithComplex src{.id = 1, .value = {3.14, 2.71}, .name = "test"};
6497+
std::string buffer{};
6498+
expect(not glz::write_beve(src, buffer));
6499+
6500+
SkipSimple dst{};
6501+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6502+
auto ec = glz::read<opts>(dst, buffer);
6503+
expect(!ec) << glz::format_error(ec, buffer);
6504+
expect(dst.id == 1);
6505+
expect(dst.name == "test");
6506+
};
6507+
6508+
"skip complex<float> when key removed"_test = [] {
6509+
WithComplexFloat src{.id = 2, .value = {1.0f, 0.5f}, .name = "float"};
6510+
std::string buffer{};
6511+
expect(not glz::write_beve(src, buffer));
6512+
6513+
SkipSimple dst{};
6514+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6515+
auto ec = glz::read<opts>(dst, buffer);
6516+
expect(!ec) << glz::format_error(ec, buffer);
6517+
expect(dst.id == 2);
6518+
expect(dst.name == "float");
6519+
};
6520+
6521+
"skip vector<complex<double>> when key removed"_test = [] {
6522+
WithComplexArray src{.id = 3, .values = {{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}}, .name = "array"};
6523+
std::string buffer{};
6524+
expect(not glz::write_beve(src, buffer));
6525+
6526+
SkipSimple dst{};
6527+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6528+
auto ec = glz::read<opts>(dst, buffer);
6529+
expect(!ec) << glz::format_error(ec, buffer);
6530+
expect(dst.id == 3);
6531+
expect(dst.name == "array");
6532+
};
6533+
6534+
"skip vector<complex<float>> when key removed"_test = [] {
6535+
WithComplexFloatArray src{.id = 4, .values = {{1.0f, 2.0f}}, .name = "farray"};
6536+
std::string buffer{};
6537+
expect(not glz::write_beve(src, buffer));
6538+
6539+
SkipSimple dst{};
6540+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6541+
auto ec = glz::read<opts>(dst, buffer);
6542+
expect(!ec) << glz::format_error(ec, buffer);
6543+
expect(dst.id == 4);
6544+
expect(dst.name == "farray");
6545+
};
6546+
6547+
"skip empty vector<complex<double>> when key removed"_test = [] {
6548+
WithComplexArray src{.id = 5, .values = {}, .name = "empty"};
6549+
std::string buffer{};
6550+
expect(not glz::write_beve(src, buffer));
6551+
6552+
SkipSimple dst{};
6553+
constexpr glz::opts opts = {.format = glz::BEVE, .error_on_unknown_keys = false};
6554+
auto ec = glz::read<opts>(dst, buffer);
6555+
expect(!ec) << glz::format_error(ec, buffer);
6556+
expect(dst.id == 5);
6557+
expect(dst.name == "empty");
6558+
};
6559+
};
6560+
63996561
int main()
64006562
{
64016563
trace.begin("binary_test");

0 commit comments

Comments
 (0)