ud3tn/components/bundle7/parser.c
Maximilian Nitsch 611b67fb0e refactor: cleanup includes
Removes unused includes detected by the clangd LSP.

Signed-off-by: Maximilian Nitsch <maximilian.nitsch@d3tn.com>
2026-05-20 16:16:35 +02:00

910 lines
24 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
#include "bundle7/eid.h"
#include "bundle7/parser.h"
#include "bundle7/timestamp.h"
#include "ud3tn/common.h"
#include "compilersupport_p.h" // Private TinyCBOR header, used for endianness
#include <stdint.h>
#include <stdlib.h>
/**
* Shortcut access to current bundle block
*/
#define BLOCK(state) ((*((state)->current_block_entry))->data)
/**
* Shortcut to transition into error state
*/
#define FAIL(state) ((state)->basedata->status = PARSER_STATUS_ERROR)
// -----------------------------
// Cyclic Redundancy Check (CRC)
// -----------------------------
static void crc_verify(struct bundle7_parser *state, uint32_t checksum,
uint32_t expected)
{
if (checksum != expected)
state->basedata->flags |= PARSER_FLAG_CRC_INVALID;
}
// --------------------
// Bundle start and end
// --------------------
static CborError bundle_start(struct bundle7_parser *, CborValue *);
static CborError bundle_end(struct bundle7_parser *, CborValue *);
// -------------
// Primary block
// -------------
static CborError primary_block_start(struct bundle7_parser *, CborValue *);
static CborError protocol_version(struct bundle7_parser *, CborValue *);
static CborError primary_proc_flags(struct bundle7_parser *, CborValue *);
static CborError crc_type_field(struct bundle7_parser *, CborValue *);
static CborError destination_eid(struct bundle7_parser *, CborValue *);
static CborError source_eid(struct bundle7_parser *, CborValue *);
static CborError report_to_eid(struct bundle7_parser *, CborValue *);
static CborError creation_timestamp(struct bundle7_parser *, CborValue *);
static CborError lifetime(struct bundle7_parser *, CborValue *);
static CborError fragment_offset(struct bundle7_parser *, CborValue *);
static CborError total_adu_length(struct bundle7_parser *, CborValue *);
static CborError primary_block_crc(struct bundle7_parser *, CborValue *);
// ----------------
// Extension Blocks
// ----------------
static CborError block_start(struct bundle7_parser *, CborValue *);
static CborError block_type(struct bundle7_parser *, CborValue *);
static CborError block_number(struct bundle7_parser *, CborValue *);
static CborError block_proc_flags(struct bundle7_parser *, CborValue *);
static CborError block_crc_type(struct bundle7_parser *, CborValue *);
static CborError block_crc(struct bundle7_parser *, CborValue *);
static CborError block_data(struct bundle7_parser *, CborValue *);
static CborError block_end(struct bundle7_parser *, CborValue *);
// --------------------
// Bundle start and end
// --------------------
CborError bundle_start(struct bundle7_parser *state, CborValue *it)
{
if (!cbor_value_is_array(it) || cbor_value_is_length_known(it))
return CborErrorIllegalType;
state->next = primary_block_start;
return cbor_value_enter_container(it, it);
}
CborError bundle_end(struct bundle7_parser *state, CborValue *it)
{
struct bundle *bundle;
if (*it->source.ptr != 0xff)
return CborErrorIllegalType;
it->source.ptr++;
// Transition into "Done" state
// NOTE that it is expected that a transition to "DONE" also occurs if
// the CRC is invalid. In this case, however, the "send" callback
// is not called. The caller may check for this case by examining the
// parser flags for `PARSER_FLAG_CRC_INVALID` when the `status` is
// `PARSER_STATUS_DONE`.
state->basedata->status = PARSER_STATUS_DONE;
// Clear bundle reference
bundle = state->bundle;
state->bundle = NULL;
// Call "send" callback if set and all CRCs passed, otherwise discard
// parsed bundle silently
if (state->send_callback == NULL ||
state->basedata->flags & PARSER_FLAG_CRC_INVALID)
bundle_free(bundle);
else
state->send_callback(bundle, state->send_param);
return CborNoError;
}
// -------------
// Primary Block
// -------------
CborError primary_block_start(struct bundle7_parser *state, CborValue *it)
{
if (!cbor_value_is_array(it) || !cbor_value_is_length_known(it))
return CborErrorIllegalType;
size_t primary_array_len = 0;
if (cbor_value_get_array_length(it, &primary_array_len) != CborNoError)
return CborErrorIllegalType;
// RFC 9171, 4.3.1 - array length check
if (primary_array_len < 8 || primary_array_len > 11)
return CborErrorIllegalType;
state->primary_block_array_length = primary_array_len;
// Primary block CRC
state->flags |= BUNDLE_V7_PARSER_CRC_FEED;
crc_init(&state->crc16, CRC16_X25);
crc_init(&state->crc32, CRC32);
state->next = protocol_version;
return cbor_value_enter_container(it, it);
}
CborError protocol_version(struct bundle7_parser *state, CborValue *it)
{
uint64_t version;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &version);
// Validate that it is indeed BPv7. This parser can only handle RFC9171-conformant bundles.
if (version != 7)
return CborErrorImproperValue;
state->bundle->protocol_version = 7;
state->next = primary_proc_flags;
return cbor_value_advance_fixed(it);
}
CborError primary_proc_flags(struct bundle7_parser *state, CborValue *it)
{
uint64_t flags;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &flags);
state->bundle->proc_flags = flags;
state->next = crc_type_field;
return cbor_value_advance_fixed(it);
}
CborError crc_type_field(struct bundle7_parser *state, CborValue *it)
{
uint64_t type;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &type);
// Validate CRC type
if (type > BUNDLE_CRC_TYPE_32)
return CborErrorIllegalType;
state->bundle->crc_type = type;
const bool has_crc = state->bundle->crc_type != BUNDLE_CRC_TYPE_NONE;
// Deactivate CRC feeding if CRC is not enabled
if (!has_crc)
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
// RFC 9171, 4.3.1 - array length check
const bool is_fragment = HAS_FLAG(state->bundle->proc_flags, BUNDLE_FLAG_IS_FRAGMENT);
const bool is_valid_primary_block_array_length = (
(!is_fragment && !has_crc && state->primary_block_array_length == 8) ||
(!is_fragment && has_crc && state->primary_block_array_length == 9) ||
(is_fragment && !has_crc && state->primary_block_array_length == 10) ||
(is_fragment && has_crc && state->primary_block_array_length == 11)
);
if (!is_valid_primary_block_array_length)
return CborErrorIllegalType;
state->next = destination_eid;
return cbor_value_advance_fixed(it);
}
CborError parse_eid(struct bundle7_parser *state, CborValue *it, struct eid *eid,
CborError (*next)(struct bundle7_parser *, CborValue *))
{
CborError err = bundle7_eid_parse_cbor(it, eid);
if (err)
return err;
state->next = next;
return CborNoError;
}
CborError destination_eid(struct bundle7_parser *state, CborValue *it)
{
return parse_eid(state, it, &state->bundle->destination, source_eid);
}
CborError source_eid(struct bundle7_parser *state, CborValue *it)
{
return parse_eid(state, it, &state->bundle->source, report_to_eid);
}
CborError report_to_eid(struct bundle7_parser *state, CborValue *it)
{
return parse_eid(state, it, &state->bundle->report_to,
creation_timestamp);
}
CborError creation_timestamp(struct bundle7_parser *state, CborValue *it)
{
CborError err;
err = bundle7_timestamp_parse(it,
&state->bundle->creation_timestamp_ms,
&state->bundle->sequence_number);
if (err)
return err;
state->next = lifetime;
return CborNoError;
}
CborError lifetime(struct bundle7_parser *state, CborValue *it)
{
uint64_t value;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &value);
state->bundle->lifetime_ms = value;
const uint8_t *last_ptr = it->source.ptr;
CborError err = cbor_value_advance_fixed(it);
if (bundle_is_fragmented(state->bundle)) {
state->next = fragment_offset;
} else if (state->bundle->crc_type != BUNDLE_CRC_TYPE_NONE) {
state->next = primary_block_crc;
} else {
state->next = block_start;
// -1 Byte infinite array header
state->bundle->primary_block_length = state->bundle_size - 1 +
(it->source.ptr - last_ptr);
}
return err;
}
CborError fragment_offset(struct bundle7_parser *state, CborValue *it)
{
uint64_t offset;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &offset);
state->bundle->fragment_offset = offset;
state->next = total_adu_length;
return cbor_value_advance_fixed(it);
}
CborError total_adu_length(struct bundle7_parser *state, CborValue *it)
{
uint64_t length;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &length);
state->bundle->total_adu_length = length;
const uint8_t *last_ptr = it->source.ptr;
CborError err = cbor_value_advance_fixed(it);
if (state->bundle->crc_type != BUNDLE_CRC_TYPE_NONE) {
state->next = primary_block_crc;
} else {
state->next = block_start;
// -1 Byte infinite array header
state->bundle->primary_block_length = state->bundle_size - 1 +
(it->source.ptr - last_ptr);
}
return err;
}
CborError primary_block_crc(struct bundle7_parser *state, CborValue *it)
{
union crc crc;
size_t len = sizeof(crc);
CborError err;
if (!cbor_value_is_byte_string(it))
return CborErrorIllegalType;
err = cbor_value_copy_byte_string(it, crc.bytes, &len, it);
if (err)
return err;
// Deactivate CRC feeding for the CRC field itself
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
if (state->bundle->crc_type == BUNDLE_CRC_TYPE_16) {
// Ensure correct CRC 16 length
if (len != 2)
return CborErrorIllegalType;
// CRC field is populated with zero
state->crc16.feed(&state->crc16, 0x42); // CBOR 2-b array header
state->crc16.feed(&state->crc16, 0x00);
state->crc16.feed(&state->crc16, 0x00);
state->crc16.feed_eof(&state->crc16);
// Swap from network byte order to native order and clear all
// higher bits
state->bundle->crc.checksum = cbor_ntohs(crc.checksum & 0xffff);
crc_verify(state,
state->crc16.checksum,
state->bundle->crc.checksum);
// -1 Byte infinite array header + 3 Bytes CRC field
state->bundle->primary_block_length = state->bundle_size - 1 + 3;
} else {
// Ensure correct CRC 32 length
if (len != 4)
return CborErrorIllegalType;
// CRC field is populated with zero
state->crc32.feed(&state->crc32, 0x44); // CBOR 4-b array header
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed_eof(&state->crc32);
// Swap from network byte order to native order
state->bundle->crc.checksum = cbor_ntohl(crc.checksum);
crc_verify(state,
state->crc32.checksum,
state->bundle->crc.checksum);
// -1 Byte infinite array header + 5 Bytes CRC field
state->bundle->primary_block_length = state->bundle_size - 1 + 5;
}
state->next = block_start;
return CborNoError;
}
// ----------------
// Extension Blocks
// ----------------
CborError block_start(struct bundle7_parser *state, CborValue *it)
{
if (!cbor_value_is_array(it) || !cbor_value_is_length_known(it))
return CborErrorIllegalType;
size_t block_array_len = 0;
if (cbor_value_get_array_length(it, &block_array_len) != CborNoError)
return CborErrorIllegalType;
// RFC 9171, 4.3.2 - array length check
if (block_array_len < 5 || block_array_len > 6)
return CborErrorIllegalType;
state->current_block_array_length = block_array_len;
// Reset CRC streams
crc_init(&state->crc16, CRC16_X25);
crc_init(&state->crc32, CRC32);
// Enable CRC feeding again
state->flags |= BUNDLE_V7_PARSER_CRC_FEED;
state->next = block_type;
return cbor_value_enter_container(it, it);
}
CborError block_type(struct bundle7_parser *state, CborValue *it)
{
uint64_t type;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &type);
// Invalid block type
if (type > (uint64_t)BUNDLE_BLOCK_TYPE_MAX)
return CborErrorImproperValue;
if (*state->current_block_entry == NULL) {
// Create bundle block
struct bundle_block *block = bundle_block_create(type);
if (block == NULL)
return CborErrorOutOfMemory;
// Create bundle block list entry
*state->current_block_entry = bundle_block_entry_create(block);
if (*state->current_block_entry == NULL) {
bundle_block_free(block);
return CborErrorOutOfMemory;
}
} else {
// Was already created but the last operation failed with a
// too-short chunk (CborErrorUnexpectedEOF).
ASSERT(
(*state->current_block_entry)->data->type ==
(enum bundle_block_type)type
);
}
state->next = block_number;
return cbor_value_advance_fixed(it);
}
CborError block_number(struct bundle7_parser *state, CborValue *it)
{
uint64_t number;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &number);
// We do not support larger block numbers atm.!
if (number > UINT8_MAX)
return CborErrorImproperValue;
BLOCK(state)->number = number;
state->next = block_proc_flags;
return cbor_value_advance_fixed(it);
}
CborError block_proc_flags(struct bundle7_parser *state, CborValue *it)
{
uint64_t flags;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &flags);
BLOCK(state)->flags = flags;
state->next = block_crc_type;
return cbor_value_advance_fixed(it);
}
CborError block_crc_type(struct bundle7_parser *state, CborValue *it)
{
uint64_t crc_type;
if (!cbor_value_is_unsigned_integer(it))
return CborErrorIllegalType;
cbor_value_get_uint64(it, &crc_type);
BLOCK(state)->crc_type = crc_type;
state->next = block_data;
const bool has_crc = crc_type != BUNDLE_CRC_TYPE_NONE;
// Deactivate CRC feeding
if (!has_crc)
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
// RFC 9171, 4.3.2 - array length check
const bool is_valid_current_block_array_length = (
(!has_crc && state->current_block_array_length == 5) ||
(has_crc && state->current_block_array_length == 6)
);
if (!is_valid_current_block_array_length)
return CborErrorIllegalType;
return cbor_value_advance_fixed(it);
}
CborError block_crc(struct bundle7_parser *state, CborValue *it)
{
union crc crc;
size_t len = sizeof(crc);
CborError err;
if (!cbor_value_is_byte_string(it))
return CborErrorIllegalType;
// Deactivate CRC feeding for the CRC field itself
state->flags &= ~BUNDLE_V7_PARSER_CRC_FEED;
err = cbor_value_copy_byte_string(it, crc.bytes, &len, it);
if (err)
return err;
// Feed CRC with raw CBOR block data
if (BLOCK(state)->crc_type == BUNDLE_CRC_TYPE_16) {
// Ensure correct CRC 16 checksum length
if (len != 2)
return CborErrorIllegalType;
crc_feed_bytes(&state->crc16,
BLOCK(state)->data,
BLOCK(state)->length);
// CRC field is populated with zero
state->crc16.feed(&state->crc16, 0x42); // CBOR byte string(2)
state->crc16.feed(&state->crc16, 0x00);
state->crc16.feed(&state->crc16, 0x00);
state->crc16.feed_eof(&state->crc16);
// Swap from network byte order to native order and clear all
// higher bits
BLOCK(state)->crc.checksum = cbor_ntohs(crc.checksum & 0xffff);
crc_verify(state,
state->crc16.checksum,
BLOCK(state)->crc.checksum);
} else {
// Ensure correct CRC 32 checksum length
if (len != 4)
return CborErrorIllegalType;
crc_feed_bytes(&state->crc32,
BLOCK(state)->data,
BLOCK(state)->length);
// CRC field is populated with zero
state->crc32.feed(&state->crc32, 0x44); // CBOR byte string(4)
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed(&state->crc32, 0x00);
state->crc32.feed_eof(&state->crc32);
// Swap from network byte order to native order
BLOCK(state)->crc.checksum = cbor_ntohl(crc.checksum);
crc_verify(state,
state->crc32.checksum,
BLOCK(state)->crc.checksum);
}
block_end(state, it);
return CborNoError;
}
CborError block_data(struct bundle7_parser *state, CborValue *it)
{
size_t length;
CborError err;
if (!cbor_value_is_byte_string(it))
return CborErrorIllegalType;
err = cbor_value_get_string_length(it, &length);
// CBOR length may be unknown (which is not allowed by the BPbis spec)
if (err)
return err;
// Activate CRC feeding again
state->flags |= BUNDLE_V7_PARSER_CRC_FEED;
BLOCK(state)->length = length;
// Block-specific data
// -------------------
//
BLOCK(state)->data = malloc(length);
if (BLOCK(state)->data == NULL)
return CborErrorOutOfMemory;
// Enable "bulk read" mode
state->basedata->next_buffer = BLOCK(state)->data;
state->basedata->next_bytes = length;
state->basedata->flags |= PARSER_FLAG_BULK_READ;
state->bundle_size += length;
if (BLOCK(state)->crc_type != BUNDLE_CRC_TYPE_NONE)
state->next = block_crc;
else
state->next = block_end;
// The CBOR byte string length is represented in the same manner as a
// CBOR uint. We thus change the type to uint to allow cleanly
// advancing the iterator to just the start of the block payload.
it->type = CborIntegerType;
// NOTE: This may return an error depending on the following data
// which it tries to preparse. It is already ensured that the header
// and length can safely be read and advanced over by the result of
// cbor_value_get_string_length. As we have requested a BULK_READ,
// control will be handed over to the bulk_read handler and afterwards
// we re-initialize the parser.
cbor_value_advance_fixed(it);
return CborNoError;
}
/**
* Bulk read operation handler
*
* This function gets called with an null-pointer instead of an iterator after
* the "bulk read" operation was performed and the bulk buffer was filled.
*
* @return This function never fails and always returns CborNoError
*/
CborError block_end(struct bundle7_parser *state, CborValue *it)
{
(void)it;
// Update parsing callback directly because there is no transition for
// this callback as it gets called after the bulk read operation with
// a null pointer instead an CBOR iterator.
//
// Set the "next" callback to the same function to prevent undesirable
// transitions to an old callback of a previous stage
if (BLOCK(state)->type == BUNDLE_BLOCK_TYPE_PAYLOAD) {
state->bundle->payload_block = BLOCK(state);
state->parse = bundle_end;
state->next = bundle_end;
} else {
state->parse = block_start;
state->next = block_start;
}
// Iterate to next block entry element
state->current_block_entry = &(*state->current_block_entry)->next;
return CborNoError;
}
// -----------
// Parser Core
// -----------
struct parser *bundle7_parser_init(struct bundle7_parser *state,
void (*send_callback)(struct bundle *, void *), void *param)
{
state->basedata = malloc(sizeof(struct parser));
if (state->basedata == NULL)
return NULL;
state->bundle_quota = BUNDLE7_DEFAULT_BUNDLE_QUOTA;
state->send_callback = send_callback;
state->send_param = param;
state->bundle = NULL;
state->next = NULL; // force reset to do its job
// Set to error that the reset handler does not abort
state->basedata->status = PARSER_STATUS_ERROR;
if (bundle7_parser_reset(state) != UD3TN_OK) {
free(state->basedata);
return NULL;
}
return state->basedata;
}
enum ud3tn_result bundle7_parser_reset(struct bundle7_parser *state)
{
if (state->basedata->status == PARSER_STATUS_GOOD &&
state->next == bundle_start)
return UD3TN_OK;
state->basedata->status = PARSER_STATUS_GOOD;
state->basedata->flags = PARSER_FLAG_NONE;
state->parse = bundle_start;
state->flags = 0;
state->bundle_size = 0;
if (state->bundle != NULL)
bundle_reset(state->bundle);
else
state->bundle = bundle_init();
if (state->bundle == NULL)
return UD3TN_FAIL;
state->current_block_entry = &state->bundle->blocks;
state->primary_block_array_length = 0;
state->current_block_array_length = 0;
return UD3TN_OK;
}
enum ud3tn_result bundle7_parser_deinit(struct bundle7_parser *state)
{
free(state->basedata);
if (state->bundle != NULL)
bundle_free(state->bundle);
return UD3TN_OK;
}
size_t bundle7_parser_read(struct bundle7_parser *state,
const uint8_t *buffer, size_t length)
{
CborParser parser;
CborValue it;
CborError err;
size_t parsed = 0;
bool initialize_parser = true;
// Special case:
// Bulk read operation was performed and this function gets called
// with an empty buffer to continue processing
if (buffer == NULL) {
// If no CRC is to be read, block_end needs to be called now.
// "block_end" never fails - therefore no error handling
if (state->parse == block_end) {
state->parse(state, NULL);
state->parse = state->next;
}
return 0;
}
while (parsed < length && state->basedata->status == PARSER_STATUS_GOOD) {
// For arbitrarily-long data such as block payload data, a
// "BULK_READ" operation is requested, normally to be handled
// by the RX task. Though, sometimes we can handle this on our
// own (if we have enough data in the current buffer). Thus, we
// first check whether, given the bytes in the buffer, we can
// perform the bulk read operation on our own.
// NOTE: At this point the CBOR parser may be in an invalid
// state because we have to interrupt it before it starts
// trying to read the bytes from the array by itself. This is
// not possible as the array might be too short and we want to
// be able to "stream" the data.
if (state->basedata->flags & PARSER_FLAG_BULK_READ) {
if (parsed + state->basedata->next_bytes > length) {
// Bulk read operation was requested but cannot
// be performed by us. -> RX task will do it.
break;
}
memcpy(
state->basedata->next_buffer,
buffer + parsed,
state->basedata->next_bytes
);
parsed += state->basedata->next_bytes;
// Disables bulk read mode again
state->basedata->flags &= ~PARSER_FLAG_BULK_READ;
// Process copied data and proceed to next block if
// there is not CRC to be read.
// This call to "block_end" is always successful.
if (state->parse == block_end) {
state->parse(state, NULL);
state->parse = state->next;
}
// Re-initialize after the "bulk read".
initialize_parser = true;
// Force checking the loop condition again.
continue;
}
if (initialize_parser) {
err = cbor_parser_init(
buffer + parsed,
length - parsed,
0, &parser, &it
);
// CBOR decoding error when preparsing first byte
if (err) {
// Insufficient data
if (err == CborErrorUnexpectedEOF)
break;
// We allow "unexpected" breaks
//
// The bundle is an indefinite array and its end
// is marked by the "break" symbol.
// Therefore, the buffer could contain a single
// "break". This happens because we cut the
// indefinite array by the chunked reading
// operation.
if (err != CborErrorUnexpectedBreak) {
FAIL(state);
break;
}
}
initialize_parser = false;
}
// Parsing step
err = state->parse(state, &it);
// Parsing error
if (err) {
if (err != CborErrorUnexpectedEOF)
FAIL(state);
break;
}
// Transition to next stage
size_t new_parsed = (cbor_value_get_next_byte(&it) - buffer);
state->bundle_size += new_parsed - parsed;
// Bundle is larger than allowed
if (state->bundle_size > state->bundle_quota) {
FAIL(state);
break;
}
if (state->flags & BUNDLE_V7_PARSER_CRC_FEED) {
crc_feed_bytes(&state->crc16,
buffer + parsed, new_parsed - parsed);
crc_feed_bytes(&state->crc32,
buffer + parsed, new_parsed - parsed);
}
state->parse = state->next;
parsed = new_parsed;
// If we have reached the end of the current item/container,
// we re-initialize the parser as we might want to read more
// than that. The parser is not fully aware of the bundle
// CBOR structure because we interrupt the process due to
// the chunked and bulk read mechanics.
if (cbor_value_at_end(&it))
initialize_parser = true;
}
return parsed;
}