-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abstract reader/writer issue with non-contigous input data #250
Comments
Getting it into CE looked like a lot of effort, so I just added a test case to my project. Here is the test case code. Only requires tinycbor and catch2. And I guess you could always replace all the catch2 test cases with asserts :) EDIT: just FYI, I changed #include <catch2/catch_test_macros.hpp>
#include <cbor.h>
#include <cassert>
#include <iterator>
#include <array>
#include <vector>
#include <algorithm>
namespace {
template <std::input_iterator InItr, std::sentinel_for<InItr> S>
struct ParserState {
InItr itr;
S end;
CborParser parser_;
std::array<uint8_t, 16> str_buff_;
static inline CborParserOperations ops_{
.can_read_bytes=[](void* token, size_t len) ->bool {
auto state = static_cast<ParserState *>(token);
return state->itr != state->end;
},
.read_bytes=[](void* token, void* dst, size_t offset, size_t len) ->void* {
auto* state = static_cast<ParserState *>(token);
auto* dest = static_cast<uint8_t*>(dst);
auto local_itr = state->itr;
std::advance(local_itr, offset);
std::copy_n(local_itr, len, dest);
return dst;
},
.advance_bytes=[](void *token, size_t len) ->void {
auto state = static_cast<ParserState *>(token);
std::advance(state->itr, len);
},
.transfer_string=[](void *token, const void **userptr, size_t offset, size_t len) ->CborError {
auto state = static_cast<ParserState *>(token);
assert(len <= state->str_buff_.size());
*userptr = state->str_buff_.data();
std::advance(state->itr, offset);
for (int i = 0; i < len; ++i) {
if (state->itr == state->end) {
return CborErrorUnexpectedEOF;
}
state->str_buff_[i] = *state->itr++;
}
return CborNoError;
}
};
CborError init(InItr b, S e, CborValue& val) {
itr = b;
end = e;
return cbor_parser_init_reader(&ops_, &parser_, &val, this);
}
};
} // namespace
TEST_CASE("CBOR stream reader") {
// [90, "inf", null]
std::vector<uint8_t> data{0x83,0x18,0x5a,0x63,0x69,0x6e,0x66,0xf6};
using I = decltype(data.begin());
using S = decltype(data.end());
ParserState<I,S> cbor_parse;
CborValue it;
REQUIRE(cbor_parse.init(data.begin(), data.end(), it) == CborNoError);
// make sure we have an array of 3 items as expected, and then enter it
REQUIRE(cbor_value_is_array(&it));
size_t sz = 0;
cbor_value_get_array_length(&it, &sz);
REQUIRE(sz == 3);
REQUIRE(cbor_value_enter_container(&it, &it) == CborNoError);
// extract the first item: integer that should be 90
REQUIRE(cbor_value_is_integer(&it));
uint64_t number = 0;
REQUIRE(cbor_value_get_uint64(&it, &number) == CborNoError);
REQUIRE(number == 90);
// Second item should be a string that reads 'inf'
REQUIRE(cbor_value_advance(&it) == CborNoError);
REQUIRE(cbor_value_is_text_string(&it));
char func_name_buff[10]{};
size_t name_len = 10;
CborValue it2;
REQUIRE(cbor_value_copy_text_string(&it, func_name_buff, &name_len, &it2) == CborNoError);
REQUIRE(name_len == 3);
REQUIRE(strncmp(func_name_buff, "inf", 3) == 0);
// after that operation 'it' should still be the string,
// and 'it2' should point to the next item which is NULL
REQUIRE(cbor_value_is_text_string(&it));
REQUIRE(cbor_value_is_null(&it2));
// Advance the value to the last item, which is a NULL value
// this is where it all falls apart!
CHECK(cbor_value_advance(&it) == CborNoError); // ERROR: CborErrorIllegalType
CHECK(cbor_value_is_null(&it)); // CborTextStringType, should be NullType!
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So I think this may be related to and/or covered by #208 , but I wanted to open an issue to report the specifics of what i am seeing.
I think the root of the issue is that in the structure
CborParserOperations
the function pointertransfer_string
should advance the cursor but when callingCborError cbor_value_copy_text_string(const CborValue *value, char *buffer, size_t *buflen, CborValue *next)
the cursor should be advanced for the output parameternext
but not advanced in the in-out parametervalue
. I think the user datatoken*
is shared between copies ofCborValue
.I am working in an embedded system and transitioning some existing code to use a
struct netbuff
like structure instead of just copying around worst-case sized buffers. To do this i need to move all my code to work on iterators instead of just taking astd::span<uint8_t>
(we are on C++20). The iterators to this new structure arebidirectional_iterator
, so no random access.A simple (exposition only) example of the code that fails:
I will work on getting a minimal reproducible example into compiler explorer, especially if this is a new case.
The text was updated successfully, but these errors were encountered: