// Copyright (2019-2023) Paul Scherrer Institute // DECTRIS proprietary license #define __STDC_WANT_IEC_60559_TYPES_EXT__ #include "stream2.h" #include #include #if FLT16_MANT_DIG > 0 || __FLT16_MANT_DIG__ > 0 #else #include #if defined(_MSC_VER) #include #endif #endif #include #include #include #include "../frame_serialize/tinycbor/src/cbor.h" enum { MAX_KEY_LEN = 64 }; static const CborTag MULTI_DIMENSIONAL_ARRAY_ROW_MAJOR = 40; static const CborTag DECTRIS_COMPRESSION = 56500; static uint64_t read_u64_be(const uint8_t* buf) { return ((uint64_t)buf[0] << 56) | ((uint64_t)buf[1] << 48) | ((uint64_t)buf[2] << 40) | ((uint64_t)buf[3] << 32) | ((uint64_t)buf[4] << 24) | ((uint64_t)buf[5] << 16) | ((uint64_t)buf[6] << 8) | (uint64_t)buf[7]; } static enum stream2_result CBOR_RESULT(CborError e) { switch (e) { case CborNoError: return STREAM2_OK; case CborErrorUnknownLength: case CborErrorDataTooLarge: return STREAM2_ERROR_PARSE; case CborErrorOutOfMemory: return STREAM2_ERROR_OUT_OF_MEMORY; default: return STREAM2_ERROR_DECODE; } } static enum stream2_result consume_byte_string_nocopy(const CborValue* it, const uint8_t** bstr, size_t* bstr_len, CborValue* next) { enum stream2_result r; assert(cbor_value_is_byte_string(it)); if ((r = CBOR_RESULT(cbor_value_get_string_length(it, bstr_len)))) return r; const uint8_t* ptr = cbor_value_get_next_byte(it); assert(*ptr >= 0x40 && *ptr <= 0x5b); switch (*ptr++) { case 0x58: ptr += 1; break; case 0x59: ptr += 2; break; case 0x5a: ptr += 4; break; case 0x5b: ptr += 8; break; } *bstr = ptr; if (next) { *next = *it; if ((r = CBOR_RESULT(cbor_value_advance(next)))) return r; } return STREAM2_OK; } static enum stream2_result parse_key(CborValue* it, char key[MAX_KEY_LEN]) { if (!cbor_value_is_text_string(it)) return STREAM2_ERROR_PARSE; size_t key_len = MAX_KEY_LEN; CborError e = cbor_value_copy_text_string(it, key, &key_len, it); if (e == CborErrorOutOfMemory || key_len == MAX_KEY_LEN) { // The key is longer than any we suppport. Return the empty key which // should be handled by the caller like other unknown keys. key[0] = '\0'; return STREAM2_OK; } return CBOR_RESULT(e); } static enum stream2_result parse_tag(CborValue* it, CborTag* value) { enum stream2_result r; if (!cbor_value_is_tag(it)) return STREAM2_ERROR_PARSE; if ((r = CBOR_RESULT(cbor_value_get_tag(it, value)))) return r; return CBOR_RESULT(cbor_value_advance_fixed(it)); } static enum stream2_result parse_bool(CborValue* it, bool* value) { enum stream2_result r; if (!cbor_value_is_boolean(it)) return STREAM2_ERROR_PARSE; if ((r = CBOR_RESULT(cbor_value_get_boolean(it, value)))) return r; return CBOR_RESULT(cbor_value_advance_fixed(it)); } static float half_to_float(uint16_t x) { #if FLT16_MANT_DIG > 0 || __FLT16_MANT_DIG__ > 0 _Float16 f; memcpy(&f, &x, 2); return (float)f; #else return _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(x))); #endif } static enum stream2_result parse_double(CborValue* it, double* value) { enum stream2_result r; if (cbor_value_is_half_float(it)) { uint16_t h; if ((r = CBOR_RESULT(cbor_value_get_half_float(it, &h)))) return r; *value = (double)half_to_float(h); } else if (cbor_value_is_float(it)) { float f; if ((r = CBOR_RESULT(cbor_value_get_float(it, &f)))) return r; *value = (double)f; } else if (cbor_value_is_double(it)) { if ((r = CBOR_RESULT(cbor_value_get_double(it, value)))) return r; } else { return STREAM2_ERROR_PARSE; } return CBOR_RESULT(cbor_value_advance_fixed(it)); } static enum stream2_result parse_uint64(CborValue* it, uint64_t* value) { enum stream2_result r; if (!cbor_value_is_unsigned_integer(it)) return STREAM2_ERROR_PARSE; if ((r = CBOR_RESULT(cbor_value_get_uint64(it, value)))) return r; return CBOR_RESULT(cbor_value_advance_fixed(it)); } static enum stream2_result parse_text_string(CborValue* it, char** tstr) { if (!cbor_value_is_text_string(it)) return STREAM2_ERROR_PARSE; size_t len; return CBOR_RESULT(cbor_value_dup_text_string(it, tstr, &len, it)); } static enum stream2_result parse_array_2_uint64(CborValue* it, uint64_t array[2]) { enum stream2_result r; if (!cbor_value_is_array(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_array_length(it, &len)))) return r; if (len != 2) return STREAM2_ERROR_PARSE; CborValue elt; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &elt)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_uint64(&elt, &array[i]))) return r; } return CBOR_RESULT(cbor_value_leave_container(it, &elt)); } static enum stream2_result parse_dectris_compression( CborValue* it, struct stream2_compression* compression, const uint8_t** bstr, size_t* bstr_len) { enum stream2_result r; CborTag tag; if ((r = parse_tag(it, &tag))) return r; if (tag != DECTRIS_COMPRESSION) return STREAM2_ERROR_PARSE; if (!cbor_value_is_array(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_array_length(it, &len)))) return r; if (len != 3) return STREAM2_ERROR_PARSE; CborValue elt; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &elt)))) return r; if ((r = parse_text_string(&elt, &compression->algorithm))) return r; if ((r = parse_uint64(&elt, &compression->elem_size))) return r; if (!cbor_value_is_byte_string(&elt)) return STREAM2_ERROR_PARSE; if ((r = consume_byte_string_nocopy(&elt, bstr, bstr_len, &elt))) return r; // https://github.com/dectris/compression/blob/v0.2.3/src/compression.c#L42 if (strcmp(compression->algorithm, "bslz4") == 0 || strcmp(compression->algorithm, "lz4") == 0) { if (*bstr_len < 12) return STREAM2_ERROR_DECODE; compression->orig_size = read_u64_be(*bstr); } else { return STREAM2_ERROR_NOT_IMPLEMENTED; } return CBOR_RESULT(cbor_value_leave_container(it, &elt)); } static enum stream2_result parse_bytes(CborValue* it, struct stream2_bytes* bytes) { enum stream2_result r; if (cbor_value_is_tag(it)) { CborTag tag; if ((r = CBOR_RESULT(cbor_value_get_tag(it, &tag)))) return r; if (tag == DECTRIS_COMPRESSION) { return parse_dectris_compression(it, &bytes->compression, &bytes->ptr, &bytes->len); } else { return STREAM2_ERROR_PARSE; } } if (!cbor_value_is_byte_string(it)) return STREAM2_ERROR_PARSE; bytes->compression.algorithm = NULL; bytes->compression.elem_size = 0; bytes->compression.orig_size = 0; return consume_byte_string_nocopy(it, &bytes->ptr, &bytes->len, it); } // Parses a typed array from [RFC 8746 section 2]. // // [RFC 8746 section 2]: // https://www.rfc-editor.org/rfc/rfc8746.html#name-typed-arrays static enum stream2_result parse_typed_array(CborValue* it, struct stream2_typed_array* array, uint64_t* len) { enum stream2_result r; if ((r = parse_tag(it, &array->tag))) return r; if ((r = parse_bytes(it, &array->data))) return r; uint64_t elem_size; if ((r = stream2_typed_array_elem_size(array, &elem_size))) return r; uint64_t size; if (array->data.compression.algorithm == NULL) size = array->data.len; else size = array->data.compression.orig_size; if (size % elem_size != 0) return STREAM2_ERROR_PARSE; *len = size / elem_size; return STREAM2_OK; } // Parses a multi-dimensional array from [RFC 8746 section 3.1.1]. // // [RFC 8746 section 3.1.1]: // https://www.rfc-editor.org/rfc/rfc8746.html#name-row-major-order static enum stream2_result parse_multidim_array( CborValue* it, struct stream2_multidim_array* multidim) { enum stream2_result r; CborTag tag; if ((r = parse_tag(it, &tag))) return r; if (tag != MULTI_DIMENSIONAL_ARRAY_ROW_MAJOR) return STREAM2_ERROR_PARSE; if (!cbor_value_is_array(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_array_length(it, &len)))) return r; if (len != 2) return STREAM2_ERROR_PARSE; CborValue elt; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &elt)))) return r; if ((r = parse_array_2_uint64(&elt, multidim->dim))) return r; uint64_t array_len; if ((r = parse_typed_array(&elt, &multidim->array, &array_len))) return r; if (multidim->dim[0] * multidim->dim[1] != array_len) return STREAM2_ERROR_PARSE; return CBOR_RESULT(cbor_value_leave_container(it, &elt)); } static enum stream2_result parse_goniometer_axis( CborValue* it, struct stream2_goniometer_axis* axis) { enum stream2_result r; if (!cbor_value_is_map(it)) return STREAM2_ERROR_PARSE; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &field)))) return r; while (cbor_value_is_valid(&field)) { char key[MAX_KEY_LEN]; if ((r = parse_key(&field, key))) return r; if ((r = CBOR_RESULT(cbor_value_skip_tag(&field)))) return r; if (strcmp(key, "increment") == 0) { if ((r = parse_double(&field, &axis->increment))) return r; } else if (strcmp(key, "start") == 0) { if ((r = parse_double(&field, &axis->start))) return r; } else { if ((r = CBOR_RESULT(cbor_value_advance(&field)))) return r; } } return CBOR_RESULT(cbor_value_leave_container(it, &field)); } static enum stream2_result parse_goniometer( CborValue* it, struct stream2_goniometer* goniometer) { enum stream2_result r; if (!cbor_value_is_map(it)) return STREAM2_ERROR_PARSE; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &field)))) return r; while (cbor_value_is_valid(&field)) { char key[MAX_KEY_LEN]; if ((r = parse_key(&field, key))) return r; if ((r = CBOR_RESULT(cbor_value_skip_tag(&field)))) return r; if (strcmp(key, "chi") == 0) { if ((r = parse_goniometer_axis(&field, &goniometer->chi))) return r; } else if (strcmp(key, "kappa") == 0) { if ((r = parse_goniometer_axis(&field, &goniometer->kappa))) return r; } else if (strcmp(key, "omega") == 0) { if ((r = parse_goniometer_axis(&field, &goniometer->omega))) return r; } else if (strcmp(key, "phi") == 0) { if ((r = parse_goniometer_axis(&field, &goniometer->phi))) return r; } else if (strcmp(key, "two_theta") == 0) { if ((r = parse_goniometer_axis(&field, &goniometer->two_theta))) return r; } else { if ((r = CBOR_RESULT(cbor_value_advance(&field)))) return r; } } return CBOR_RESULT(cbor_value_leave_container(it, &field)); } static enum stream2_result parse_user_data( CborValue* it, struct stream2_user_data* user_data) { enum stream2_result r; const uint8_t* ptr = cbor_value_get_next_byte(it); if ((r = CBOR_RESULT(cbor_value_advance(it)))) return r; user_data->ptr = ptr; user_data->len = cbor_value_get_next_byte(it) - ptr; return STREAM2_OK; } static enum stream2_result parse_start_msg(CborValue* it, struct stream2_msg** msg_out) { enum stream2_result r; struct stream2_start_msg* msg = calloc(1, sizeof(struct stream2_start_msg)); *msg_out = (struct stream2_msg*)msg; if (msg == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->type = STREAM2_MSG_START; msg->countrate_correction_lookup_table.tag = UINT64_MAX; while (cbor_value_is_valid(it)) { char key[MAX_KEY_LEN]; if ((r = parse_key(it, key))) return r; // skip any tag for a value, except where verified if (strcmp(key, "countrate_correction_lookup_table") != 0) { if ((r = CBOR_RESULT(cbor_value_skip_tag(it)))) return r; } if (strcmp(key, "series_id") == 0) { if ((r = parse_uint64(it, &msg->series_id))) return r; } else if (strcmp(key, "series_unique_id") == 0) { if ((r = parse_text_string(it, &msg->series_unique_id))) return r; } else if (strcmp(key, "arm_date") == 0) { if ((r = parse_text_string(it, &msg->arm_date))) return r; } else if (strcmp(key, "beam_center_x") == 0) { if ((r = parse_double(it, &msg->beam_center_x))) return r; } else if (strcmp(key, "beam_center_y") == 0) { if ((r = parse_double(it, &msg->beam_center_y))) return r; } else if (strcmp(key, "channels") == 0) { if (!cbor_value_is_array(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_array_length(it, &len)))) return r; msg->channels.ptr = calloc(len, sizeof(char*)); if (msg->channels.ptr == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->channels.len = len; CborValue elt; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &elt)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_text_string(&elt, &msg->channels.ptr[i]))) return r; } if ((r = CBOR_RESULT(cbor_value_leave_container(it, &elt)))) return r; } else if (strcmp(key, "count_time") == 0) { if ((r = parse_double(it, &msg->count_time))) return r; } else if (strcmp(key, "countrate_correction_enabled") == 0) { if ((r = parse_bool(it, &msg->countrate_correction_enabled))) return r; } else if (strcmp(key, "countrate_correction_lookup_table") == 0) { uint64_t len; if ((r = parse_typed_array( it, &msg->countrate_correction_lookup_table, &len))) return r; } else if (strcmp(key, "detector_description") == 0) { if ((r = parse_text_string(it, &msg->detector_description))) return r; } else if (strcmp(key, "detector_serial_number") == 0) { if ((r = parse_text_string(it, &msg->detector_serial_number))) return r; } else if (strcmp(key, "detector_translation") == 0) { if (!cbor_value_is_array(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_array_length(it, &len)))) return r; if (len != 3) return STREAM2_ERROR_PARSE; CborValue elt; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &elt)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_double(&elt, &msg->detector_translation[i]))) return r; } if ((r = CBOR_RESULT(cbor_value_leave_container(it, &elt)))) return r; } else if (strcmp(key, "flatfield") == 0) { if (!cbor_value_is_map(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_map_length(it, &len)))) return r; msg->flatfield.ptr = calloc(len, sizeof(struct stream2_flatfield)); if (msg->flatfield.ptr == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->flatfield.len = len; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &field)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_text_string(&field, &msg->flatfield.ptr[i].channel))) return r; if ((r = parse_multidim_array( &field, &msg->flatfield.ptr[i].flatfield))) return r; } if ((r = CBOR_RESULT(cbor_value_leave_container(it, &field)))) return r; } else if (strcmp(key, "flatfield_enabled") == 0) { if ((r = parse_bool(it, &msg->flatfield_enabled))) return r; } else if (strcmp(key, "frame_time") == 0) { if ((r = parse_double(it, &msg->frame_time))) return r; } else if (strcmp(key, "goniometer") == 0) { if ((r = parse_goniometer(it, &msg->goniometer))) return r; } else if (strcmp(key, "image_size_x") == 0) { if ((r = parse_uint64(it, &msg->image_size_x))) return r; } else if (strcmp(key, "image_size_y") == 0) { if ((r = parse_uint64(it, &msg->image_size_y))) return r; } else if (strcmp(key, "incident_energy") == 0) { if ((r = parse_double(it, &msg->incident_energy))) return r; } else if (strcmp(key, "incident_wavelength") == 0) { if ((r = parse_double(it, &msg->incident_wavelength))) return r; } else if (strcmp(key, "number_of_images") == 0) { if ((r = parse_uint64(it, &msg->number_of_images))) return r; } else if (strcmp(key, "pixel_mask") == 0) { if (!cbor_value_is_map(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_map_length(it, &len)))) return r; msg->pixel_mask.ptr = calloc(len, sizeof(struct stream2_pixel_mask)); if (msg->pixel_mask.ptr == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->pixel_mask.len = len; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &field)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_text_string(&field, &msg->pixel_mask.ptr[i].channel))) return r; if ((r = parse_multidim_array( &field, &msg->pixel_mask.ptr[i].pixel_mask))) return r; } if ((r = CBOR_RESULT(cbor_value_leave_container(it, &field)))) return r; } else if (strcmp(key, "pixel_mask_enabled") == 0) { if ((r = parse_bool(it, &msg->pixel_mask_enabled))) return r; } else if (strcmp(key, "pixel_size_x") == 0) { if ((r = parse_double(it, &msg->pixel_size_x))) return r; } else if (strcmp(key, "pixel_size_y") == 0) { if ((r = parse_double(it, &msg->pixel_size_y))) return r; } else if (strcmp(key, "saturation_value") == 0) { if ((r = parse_uint64(it, &msg->saturation_value))) return r; } else if (strcmp(key, "sensor_material") == 0) { if ((r = parse_text_string(it, &msg->sensor_material))) return r; } else if (strcmp(key, "sensor_thickness") == 0) { if ((r = parse_double(it, &msg->sensor_thickness))) return r; } else if (strcmp(key, "threshold_energy") == 0) { if (!cbor_value_is_map(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_map_length(it, &len)))) return r; msg->threshold_energy.ptr = calloc(len, sizeof(struct stream2_threshold_energy)); if (msg->threshold_energy.ptr == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->threshold_energy.len = len; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &field)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_text_string( &field, &msg->threshold_energy.ptr[i].channel))) return r; if ((r = parse_double(&field, &msg->threshold_energy.ptr[i].energy))) return r; } if ((r = CBOR_RESULT(cbor_value_leave_container(it, &field)))) return r; } else if (strcmp(key, "user_data") == 0) { if ((r = parse_user_data(it, &msg->user_data))) return r; } else if (strcmp(key, "virtual_pixel_interpolation_enabled") == 0) { if ((r = parse_bool(it, &msg->virtual_pixel_interpolation_enabled))) return r; } else { if ((r = CBOR_RESULT(cbor_value_advance(it)))) return r; } } return STREAM2_OK; } static enum stream2_result parse_image_msg(CborValue* it, struct stream2_msg** msg_out) { enum stream2_result r; struct stream2_image_msg* msg = calloc(1, sizeof(struct stream2_image_msg)); *msg_out = (struct stream2_msg*)msg; if (msg == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->type = STREAM2_MSG_IMAGE; while (cbor_value_is_valid(it)) { char key[MAX_KEY_LEN]; if ((r = parse_key(it, key))) return r; if ((r = CBOR_RESULT(cbor_value_skip_tag(it)))) return r; if (strcmp(key, "series_id") == 0) { if ((r = parse_uint64(it, &msg->series_id))) return r; } else if (strcmp(key, "series_unique_id") == 0) { if ((r = parse_text_string(it, &msg->series_unique_id))) return r; } else if (strcmp(key, "image_id") == 0) { if ((r = parse_uint64(it, &msg->image_id))) return r; } else if (strcmp(key, "real_time") == 0) { if ((r = parse_array_2_uint64(it, msg->real_time))) return r; } else if (strcmp(key, "series_date") == 0) { if ((r = parse_text_string(it, &msg->series_date))) return r; } else if (strcmp(key, "start_time") == 0) { if ((r = parse_array_2_uint64(it, msg->start_time))) return r; } else if (strcmp(key, "stop_time") == 0) { if ((r = parse_array_2_uint64(it, msg->stop_time))) return r; } else if (strcmp(key, "user_data") == 0) { if ((r = parse_user_data(it, &msg->user_data))) return r; } else if (strcmp(key, "data") == 0) { if (!cbor_value_is_map(it)) return STREAM2_ERROR_PARSE; size_t len; if ((r = CBOR_RESULT(cbor_value_get_map_length(it, &len)))) return r; msg->data.ptr = calloc(len, sizeof(struct stream2_image_data)); if (msg->data.ptr == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->data.len = len; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(it, &field)))) return r; for (size_t i = 0; i < len; i++) { if ((r = parse_text_string(&field, &msg->data.ptr[i].channel))) return r; if ((r = parse_multidim_array(&field, &msg->data.ptr[i].data))) return r; } if ((r = CBOR_RESULT(cbor_value_leave_container(it, &field)))) return r; } else { if ((r = CBOR_RESULT(cbor_value_advance(it)))) return r; } } return STREAM2_OK; } static enum stream2_result parse_end_msg(CborValue* it, struct stream2_msg** msg_out) { enum stream2_result r; struct stream2_end_msg* msg = calloc(1, sizeof(struct stream2_end_msg)); *msg_out = (struct stream2_msg*)msg; if (msg == NULL) return STREAM2_ERROR_OUT_OF_MEMORY; msg->type = STREAM2_MSG_END; while (cbor_value_is_valid(it)) { char key[MAX_KEY_LEN]; if ((r = parse_key(it, key))) return r; if ((r = CBOR_RESULT(cbor_value_skip_tag(it)))) return r; if (strcmp(key, "series_id") == 0) { if ((r = parse_uint64(it, &msg->series_id))) return r; } else if (strcmp(key, "series_unique_id") == 0) { if ((r = parse_text_string(it, &msg->series_unique_id))) return r; } else { if ((r = CBOR_RESULT(cbor_value_advance(it)))) return r; } } return STREAM2_OK; } static enum stream2_result parse_msg_type(CborValue* it, char type[MAX_KEY_LEN]) { enum stream2_result r; char key[MAX_KEY_LEN]; if ((r = parse_key(it, key))) return r; if (strcmp(key, "type") != 0) return STREAM2_ERROR_PARSE; if ((r = CBOR_RESULT(cbor_value_skip_tag(it)))) return r; return parse_key(it, type); } static enum stream2_result parse_msg(const uint8_t* buffer, size_t size, struct stream2_msg** msg_out) { enum stream2_result r; // https://www.rfc-editor.org/rfc/rfc8949.html#name-self-described-cbor const uint8_t MAGIC[3] = {0xd9, 0xd9, 0xf7}; if (size < sizeof(MAGIC) || memcmp(buffer, MAGIC, sizeof(MAGIC)) != 0) return STREAM2_ERROR_SIGNATURE; buffer += sizeof(MAGIC); size -= sizeof(MAGIC); CborParser parser; CborValue it; if ((r = CBOR_RESULT(cbor_parser_init(buffer, size, 0, &parser, &it)))) return r; if (!cbor_value_is_map(&it)) return STREAM2_ERROR_PARSE; CborValue field; if ((r = CBOR_RESULT(cbor_value_enter_container(&it, &field)))) return r; char type[MAX_KEY_LEN]; if ((r = parse_msg_type(&field, type))) return r; if (strcmp(type, "start") == 0) { if ((r = parse_start_msg(&field, msg_out))) return r; } else if (strcmp(type, "image") == 0) { if ((r = parse_image_msg(&field, msg_out))) return r; } else if (strcmp(type, "end") == 0) { if ((r = parse_end_msg(&field, msg_out))) return r; } else { return STREAM2_ERROR_PARSE; } return CBOR_RESULT(cbor_value_leave_container(&it, &field)); } enum stream2_result stream2_parse_msg(const uint8_t* buffer, size_t size, struct stream2_msg** msg_out) { enum stream2_result r; *msg_out = NULL; if ((r = parse_msg(buffer, size, msg_out))) { if (*msg_out) { stream2_free_msg(*msg_out); *msg_out = NULL; } return r; } return STREAM2_OK; } static void free_start_msg(struct stream2_start_msg* msg) { free(msg->arm_date); for (size_t i = 0; i < msg->channels.len; i++) free(msg->channels.ptr[i]); free(msg->channels.ptr); free(msg->countrate_correction_lookup_table.data.compression.algorithm); free(msg->detector_description); free(msg->detector_serial_number); for (size_t i = 0; i < msg->flatfield.len; i++) { free(msg->flatfield.ptr[i].channel); free(msg->flatfield.ptr[i].flatfield.array.data.compression.algorithm); } free(msg->flatfield.ptr); for (size_t i = 0; i < msg->pixel_mask.len; i++) { free(msg->pixel_mask.ptr[i].channel); free(msg->pixel_mask.ptr[i] .pixel_mask.array.data.compression.algorithm); } free(msg->pixel_mask.ptr); free(msg->sensor_material); for (size_t i = 0; i < msg->threshold_energy.len; i++) free(msg->threshold_energy.ptr[i].channel); free(msg->threshold_energy.ptr); } static void free_image_msg(struct stream2_image_msg* msg) { free(msg->series_date); for (size_t i = 0; i < msg->data.len; i++) { free(msg->data.ptr[i].channel); free(msg->data.ptr[i].data.array.data.compression.algorithm); } free(msg->data.ptr); } void stream2_free_msg(struct stream2_msg* msg) { switch (msg->type) { case STREAM2_MSG_START: free_start_msg((struct stream2_start_msg*)msg); break; case STREAM2_MSG_IMAGE: free_image_msg((struct stream2_image_msg*)msg); break; case STREAM2_MSG_END: break; } free(msg->series_unique_id); free(msg); } enum stream2_result stream2_typed_array_elem_size( const struct stream2_typed_array* array, uint64_t* elem_size) { // https://www.rfc-editor.org/rfc/rfc8746.html#name-types-of-numbers if (array->tag >= 64 && array->tag <= 87) { const uint64_t f = (array->tag >> 4) & 1; const uint64_t ll = array->tag & 3; *elem_size = 1 << (f + ll); return STREAM2_OK; } return STREAM2_ERROR_NOT_IMPLEMENTED; }