// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "CBORStream2Serializer.h" #include "tinycbor/cbor.h" #include "CborErr.h" #include "CborUtil.h" #include inline void CBOR_ENC(CborEncoder &encoder, const char* key, const char* value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_text_stringz(&encoder, value)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::string &value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_text_stringz(&encoder, value.c_str())); } inline void CBOR_ENC_DATE(CborEncoder &encoder, const char* key, const std::string &value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cbor_encode_tag(&encoder, CborDateTimeStringTag); cborErr(cbor_encode_text_stringz(&encoder, value.c_str())); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, float value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_float(&encoder, value)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, bool value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_boolean(&encoder, value)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, uint64_t value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_uint(&encoder, value)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, int64_t value) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_int(&encoder, value)); } template void CBOR_ENC(CborEncoder &encoder, const char* key, const std::optional &value) { if (value) CBOR_ENC(encoder, key, value.value()); } void CBOR_ENC_COMPRESSED(CborEncoder &encoder, const void *image, size_t image_size, CompressionAlgorithm algorithm, size_t elem_size) { if (algorithm == CompressionAlgorithm::NO_COMPRESSION) cborErr(cbor_encode_byte_string(&encoder, (uint8_t *) image, image_size)); else { cbor_encode_tag(&encoder, TagDECTRISCompression); CborEncoder arrayEncoder; cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, 3)); switch (algorithm) { case CompressionAlgorithm::BSHUF_LZ4: cborErr(cbor_encode_text_stringz(&arrayEncoder, "bslz4")); break; case CompressionAlgorithm::BSHUF_ZSTD: case CompressionAlgorithm::BSHUF_ZSTD_RLE: cborErr(cbor_encode_text_stringz(&arrayEncoder, "bszstd")); break; default: throw JFJochException(JFJochExceptionCategory::CBORError, "Unsupported compression algorithm"); } cborErr(cbor_encode_uint(&arrayEncoder, elem_size)); cborErr(cbor_encode_byte_string(&arrayEncoder, (uint8_t *) image, image_size)); cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } } inline void CBOR_ENC_2D_TYPED_ARRAY(CborEncoder &encoder, const CompressedImage& image) { //if ((algorithm == CompressionAlgorithm::NO_COMPRESSION) && (xpixel * ypixel != image_size / elem_size)) // throw JFJochException(JFJochExceptionCategory::CBORError, "Mismatch in array size"); CborEncoder arrayEncoder, arrayEncoder_2; cborErr(cbor_encode_text_stringz(&encoder, image.channel.c_str())); cbor_encode_tag(&encoder, TagMultiDimArray); cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, 2)); cborErr(cbor_encoder_create_array(&arrayEncoder, &arrayEncoder_2, 2)); cborErr(cbor_encode_uint(&arrayEncoder_2, image.ypixel)); cborErr(cbor_encode_uint(&arrayEncoder_2, image.xpixel)); cborErr(cbor_encoder_close_container(&arrayEncoder, &arrayEncoder_2)); CborTag typed_array_tag; if (image.pixel_is_float) { if (image.pixel_depth_bytes == 4) typed_array_tag = TagFloatLE; else throw JFJochException(JFJochExceptionCategory::CBORError, "Array size not supported"); } else if (image.pixel_is_signed) { if (image.pixel_depth_bytes == 4) typed_array_tag = TagSignedInt32BitLE; else if (image.pixel_depth_bytes == 2) typed_array_tag = TagSignedInt16BitLE; else if (image.pixel_depth_bytes == 1) typed_array_tag = TagSignedInt8Bit; else throw JFJochException(JFJochExceptionCategory::CBORError, "Array size not supported"); } else { if (image.pixel_depth_bytes == 4) typed_array_tag = TagUnsignedInt32BitLE; else if (image.pixel_depth_bytes == 2) typed_array_tag = TagUnsignedInt16BitLE; else if (image.pixel_depth_bytes == 1) typed_array_tag = TagUnsignedInt8Bit; else throw JFJochException(JFJochExceptionCategory::CBORError, "Array size not supported"); } cbor_encode_tag(&arrayEncoder, typed_array_tag); CBOR_ENC_COMPRESSED(arrayEncoder, image.data, image.size, image.algorithm, image.pixel_depth_bytes); cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector& v) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_tag(&encoder, TagFloatLE)); cborErr(cbor_encode_byte_string(&encoder, (uint8_t *) v.data(), v.size() * sizeof(float))); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector& v) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encode_tag(&encoder, TagUnsignedInt64BitLE)); cborErr(cbor_encode_byte_string(&encoder, (uint8_t *) v.data(), v.size() * sizeof(uint64_t))); } inline void CBOR_ENC_RATIONAL(CborEncoder &encoder, const char* key, uint64_t numerator, uint64_t denominator) { CborEncoder arrayEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, 2)); cborErr(cbor_encode_uint(&arrayEncoder, numerator)); cborErr(cbor_encode_uint(&arrayEncoder, denominator)); cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } inline void CBOR_ENC_RAD_INT_RESULT(CborEncoder &encoder, const char* key, const std::map> &az_int_result) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, az_int_result.size())); for (auto &[map_key, map_val]: az_int_result) CBOR_ENC(mapEncoder, map_key.c_str(), map_val); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC_ADU_HIST(CborEncoder &encoder, const char* key, const std::map> &adu_histogram) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, adu_histogram.size())); for (auto &[map_key, map_val]: adu_histogram) CBOR_ENC(mapEncoder, map_key.c_str(), map_val); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const SpotToSave& spot) { CborEncoder mapEncoder; cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 4)); CBOR_ENC(mapEncoder, "x", spot.x); CBOR_ENC(mapEncoder, "y", spot.y); CBOR_ENC(mapEncoder, "I", spot.intensity); CBOR_ENC(mapEncoder, "indexed", spot.indexed); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector& spots) { CborEncoder arrayEncoder, mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, spots.size())); for (auto spot : spots) CBOR_ENC(arrayEncoder, spot); cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const CompressedImage& message) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 1)); CBOR_ENC_2D_TYPED_ARRAY(mapEncoder, message); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC_AXIS(CborEncoder &encoder, const char* key, const float det_translation[3]) { CborEncoder arrayEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, 3)); for (int i = 0; i < 3; i++) cborErr(cbor_encode_float(&arrayEncoder, det_translation[i])); cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } inline void CBOR_ENC_GONIOMETER(CborEncoder &encoder, const GoniometerAxis &g) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, g.name.c_str())); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 2)); CBOR_ENC(mapEncoder, "increment", g.increment); CBOR_ENC(mapEncoder, "start", g.start); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC_GONIOMETER_MAP(CborEncoder &encoder, const char* key, const StartMessage &msg) { CborEncoder mapEncoder; if (msg.goniometer) { cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 1)); CBOR_ENC_GONIOMETER(mapEncoder, msg.goniometer.value()); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector &v) { CborEncoder arrayEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, v.size())); for (const auto &i: v) cborErr(cbor_encode_text_stringz(&arrayEncoder, i.c_str())); cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const ROIMessage &val) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 4)); CBOR_ENC(mapEncoder, "sum", val.sum); CBOR_ENC(mapEncoder, "sum_square", val.sum_square); CBOR_ENC(mapEncoder, "max_count", val.max_count); CBOR_ENC(mapEncoder, "pixels", val.pixels); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::map &map) { CborEncoder mapEncoder; if (map.empty()) return; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, map.size())); for (const auto &[x, y]: map) CBOR_ENC(mapEncoder, x.c_str(), y); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const UnitCell &val) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 6)); CBOR_ENC(mapEncoder, "a", val.a); CBOR_ENC(mapEncoder, "b", val.b); CBOR_ENC(mapEncoder, "c", val.c); CBOR_ENC(mapEncoder, "alpha", val.alpha); CBOR_ENC(mapEncoder, "beta", val.beta); CBOR_ENC(mapEncoder, "gamma", val.gamma); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector &v) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, v.size())); for (const auto &i: v) CBOR_ENC_2D_TYPED_ARRAY(mapEncoder, i); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } inline void CBOR_ENC_START_USER_DATA(CborEncoder& encoder, const char* key, const StartMessage& message) { nlohmann::json j; j["file_prefix"] = message.file_prefix; j["images_per_file"] = message.images_per_file; j["source_name"] = message.source_name; if (!message.source_type.empty()) j["source_type"] = message.source_type; j["instrument_name"] = message.instrument_name; j["sample_name"] = message.sample_name; if (!message.user_data.empty()) j["user"] = message.user_data; if (message.attenuator_transmission) j["attenuator_transmission"] = message.attenuator_transmission.value(); if (message.total_flux) j["total_flux"] = message.total_flux.value(); j["rotation_axis"] = {message.rotation_axis[0], message.rotation_axis[1], message.rotation_axis[2]}; j["space_group_number"] = message.space_group_number; j["roi_names"] = message.roi_names; j["gain_file_names"] = message.gain_file_names; if (message.write_master_file) j["write_master_file"] = message.write_master_file.value(); if (message.data_reduction_factor_serialmx) j["data_reduction_factor_serialmx"] = message.data_reduction_factor_serialmx.value(); j["experiment_group"] = message.experiment_group; j["jfjoch_release"] = message.jfjoch_release; if (message.socket_number) j["socket_number"] = message.socket_number.value(); if (message.bit_depth_readout) j["bit_depth_readout"] = message.bit_depth_readout.value(); if (!message.writer_notification_zmq_addr.empty()) j["writer_notification_zmq_addr"] = message.writer_notification_zmq_addr; if (message.summation_mode.has_value()) j["summation_mode"] = message.summation_mode.value(); if (message.overwrite.has_value()) j["overwrite"] = message.overwrite.value(); if (message.xfel_pulse_id.has_value()) j["xfel_pulse_id"] = message.xfel_pulse_id.value(); if (message.hdf5_format_version.has_value()) j["hdf5_format_version"] = message.hdf5_format_version.value(); auto str = j.dump(); CBOR_ENC(encoder, key, str); } CBORStream2Serializer::CBORStream2Serializer(uint8_t *in_buffer, size_t buffer_size) : buffer(in_buffer), max_buffer_size(buffer_size), curr_size(0) {} size_t CBORStream2Serializer::GetBufferSize() const { return curr_size; } void CBORStream2Serializer::SerializeSequenceStart(const StartMessage& message) { CborEncoder encoder, mapEncoder; cbor_encoder_init(&encoder, buffer, max_buffer_size, 0); cborErr(cbor_encode_tag(&encoder, CborSignatureTag )); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); CBOR_ENC(mapEncoder, "type", "start"); CBOR_ENC(mapEncoder, "magic_number", user_data_magic_number); CBOR_ENC(mapEncoder, "detector_distance", message.detector_distance); CBOR_ENC_AXIS(mapEncoder, "detector_translation", message.detector_translation); CBOR_ENC(mapEncoder, "beam_center_x", message.beam_center_x); CBOR_ENC(mapEncoder, "beam_center_y", message.beam_center_y); CBOR_ENC(mapEncoder, "countrate_correction_enabled", message.countrate_correction_enabled); CBOR_ENC(mapEncoder, "flatfield_enabled", message.flatfield_enabled); CBOR_ENC(mapEncoder, "number_of_images", message.number_of_images); CBOR_ENC(mapEncoder, "image_size_x", message.image_size_x); CBOR_ENC(mapEncoder, "image_size_y", message.image_size_y); CBOR_ENC(mapEncoder, "incident_energy", message.incident_energy); CBOR_ENC(mapEncoder, "incident_wavelength", message.incident_wavelength); CBOR_ENC(mapEncoder, "frame_time", message.frame_time); CBOR_ENC(mapEncoder, "count_time", message.count_time); CBOR_ENC(mapEncoder, "saturation_value", message.saturation_value); CBOR_ENC(mapEncoder, "error_value", message.error_value); CBOR_ENC(mapEncoder, "pixel_size_x", message.pixel_size_x); CBOR_ENC(mapEncoder, "pixel_size_y", message.pixel_size_y); CBOR_ENC(mapEncoder, "sensor_thickness", message.sensor_thickness); CBOR_ENC(mapEncoder, "sensor_material", message.sensor_material); CBOR_ENC_DATE(mapEncoder, "arm_date", message.arm_date); CBOR_ENC(mapEncoder, "pixel_mask_enabled", message.pixel_mask_enabled); CBOR_ENC(mapEncoder, "detector_description", message.detector_description); CBOR_ENC(mapEncoder, "detector_serial_number", message.detector_serial_number); CBOR_ENC(mapEncoder, "series_unique_id", message.run_name); CBOR_ENC(mapEncoder, "series_id", message.run_number); CBOR_ENC_GONIOMETER_MAP(mapEncoder, "goniometer", message); CBOR_ENC(mapEncoder, "jungfrau_conversion_enabled", message.jungfrau_conversion_enabled); CBOR_ENC(mapEncoder, "jungfrau_conversion_factor", message.jungfrau_conversion_factor); CBOR_ENC(mapEncoder, "geometry_transformation_enabled", message.geometry_transformation_enabled); CBOR_ENC(mapEncoder, "pixel_mask", message.pixel_mask); CBOR_ENC(mapEncoder, "channels", message.channels); CBOR_ENC(mapEncoder, "max_spot_count", message.max_spot_count); CBOR_ENC(mapEncoder, "storage_cell_number", message.storage_cell_number); CBOR_ENC_RATIONAL(mapEncoder, "storage_cell_delay", message.storage_cell_delay_ns, 1000*1000*1000UL); switch (message.bit_depth_image) { case 8: CBOR_ENC(mapEncoder, "image_dtype", message.pixel_signed ? "int8" : "uint8"); break; case 16: CBOR_ENC(mapEncoder, "image_dtype", message.pixel_signed ? "int16" : "uint16"); break; case 32: CBOR_ENC(mapEncoder, "image_dtype", message.pixel_signed ? "int32" : "uint32"); break; } CBOR_ENC(mapEncoder, "unit_cell", message.unit_cell); CBOR_ENC(mapEncoder, "az_int_bin_number", message.az_int_bin_number); CBOR_ENC(mapEncoder, "az_int_bin_to_q", message.az_int_bin_to_q); CBOR_ENC(mapEncoder, "summation", message.summation); CBOR_ENC_START_USER_DATA(mapEncoder, "user_data", message); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); curr_size = cbor_encoder_get_buffer_size(&encoder, buffer); } void CBORStream2Serializer::SerializeSequenceEnd(const EndMessage& message) { CborEncoder encoder, mapEncoder; cbor_encoder_init(&encoder, buffer, max_buffer_size, 0); cborErr(cbor_encode_tag(&encoder, CborSignatureTag)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); CBOR_ENC(mapEncoder, "type", "end"); CBOR_ENC(mapEncoder, "magic_number", user_data_magic_number); CBOR_ENC(mapEncoder, "series_unique_id", message.run_name); CBOR_ENC(mapEncoder, "series_id", message.run_number); CBOR_ENC(mapEncoder, "end_date", message.end_date); CBOR_ENC(mapEncoder, "max_image_number", message.max_image_number); CBOR_ENC(mapEncoder, "images_collected", message.images_collected_count); CBOR_ENC(mapEncoder, "images_sent_to_write", message.images_sent_to_write_count); CBOR_ENC(mapEncoder, "data_collection_efficiency", message.efficiency); CBOR_ENC_RAD_INT_RESULT(mapEncoder, "az_int_result", message.az_int_result); CBOR_ENC_ADU_HIST(mapEncoder, "adu_histogram", message.adu_histogram); CBOR_ENC(mapEncoder, "adu_histogram_bin_width", message.adu_histogram_bin_width); CBOR_ENC(mapEncoder, "max_receiver_delay", message.max_receiver_delay); CBOR_ENC(mapEncoder, "indexing_rate", message.indexing_rate); CBOR_ENC(mapEncoder, "bkg_estimate", message.bkg_estimate); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); curr_size = cbor_encoder_get_buffer_size(&encoder, buffer); } void CBORStream2Serializer::SerializeImageInternal(CborEncoder &mapEncoder, const DataMessage &message, bool metadata_only) { CBOR_ENC(mapEncoder, "image_id", message.number); CBOR_ENC(mapEncoder, "original_image_id", message.original_number); CBOR_ENC_RATIONAL(mapEncoder, "real_time", message.exptime, message.exptime_base); CBOR_ENC_RATIONAL(mapEncoder, "start_time", message.timestamp, message.timestamp_base); CBOR_ENC_RATIONAL(mapEncoder, "end_time", message.timestamp + message.exptime, message.timestamp_base); if (!metadata_only) CBOR_ENC(mapEncoder, "spots", message.spots); CBOR_ENC(mapEncoder, "spot_count", message.spots.size()); CBOR_ENC(mapEncoder, "spot_count_in_rings", message.spot_count_in_rings); CBOR_ENC(mapEncoder, "az_int_profile", message.az_int_profile); CBOR_ENC(mapEncoder, "indexing_result", message.indexing_result); if (!message.indexing_lattice.empty()) CBOR_ENC(mapEncoder, "indexing_lattice", message.indexing_lattice); CBOR_ENC(mapEncoder, "indexing_unit_cell", message.indexing_unit_cell); CBOR_ENC(mapEncoder, "xfel_pulse_id", message.xfel_pulse_id); CBOR_ENC(mapEncoder, "xfel_event_code", message.xfel_event_code); CBOR_ENC(mapEncoder, "jf_info", message.jf_info); CBOR_ENC(mapEncoder, "receiver_aq_dev_delay", message.receiver_aq_dev_delay); CBOR_ENC(mapEncoder, "receiver_free_send_buf", message.receiver_free_send_buf); CBOR_ENC(mapEncoder, "storage_cell", message.storage_cell); CBOR_ENC(mapEncoder, "saturated_pixel_count", message.saturated_pixel_count); CBOR_ENC(mapEncoder, "error_pixel_count", message.error_pixel_count); CBOR_ENC(mapEncoder, "strong_pixel_count", message.strong_pixel_count); CBOR_ENC(mapEncoder, "min_viable_pixel_value", message.min_viable_pixel_value); CBOR_ENC(mapEncoder, "max_viable_pixel_value", message.max_viable_pixel_value); CBOR_ENC(mapEncoder, "data_collection_efficiency", message.image_collection_efficiency); CBOR_ENC(mapEncoder, "packets_expected", message.packets_expected); CBOR_ENC(mapEncoder, "packets_received", message.packets_received); CBOR_ENC(mapEncoder, "bkg_estimate", message.bkg_estimate); CBOR_ENC(mapEncoder, "adu_histogram", message.adu_histogram); CBOR_ENC(mapEncoder, "roi_integrals", message.roi); CBOR_ENC(mapEncoder, "user_data", message.user_data.dump()); if (!metadata_only) CBOR_ENC(mapEncoder, "data", message.image); } void CBORStream2Serializer::SerializeImage(const DataMessage& message) { CborEncoder encoder, mapEncoder; cbor_encoder_init(&encoder, buffer, max_buffer_size, 0); cborErr(cbor_encode_tag(&encoder, CborSignatureTag )); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); CBOR_ENC(mapEncoder, "type", "image"); CBOR_ENC(mapEncoder, "magic_number", user_data_magic_number); CBOR_ENC(mapEncoder, "series_unique_id", message.run_name); CBOR_ENC(mapEncoder, "series_id", message.run_number); SerializeImageInternal(mapEncoder, message, false); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); curr_size = cbor_encoder_get_buffer_size(&encoder, buffer); } void CBORStream2Serializer::SerializeMetadata(const MetadataMessage &messages) { if (messages.images.empty()) throw JFJochException(JFJochExceptionCategory::CBORError, "Cannot serialize empty metadata packet"); CborEncoder encoder, mapEncoder, arrayEncoder; cbor_encoder_init(&encoder, buffer, max_buffer_size, 0); cborErr(cbor_encode_tag(&encoder, CborSignatureTag )); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); CBOR_ENC(mapEncoder, "type", "metadata"); CBOR_ENC(mapEncoder, "magic_number", user_data_magic_number); CBOR_ENC(mapEncoder, "series_unique_id", messages.run_name); CBOR_ENC(mapEncoder, "series_id", messages.run_number); cborErr(cbor_encode_text_stringz(&mapEncoder, "images")); cborErr(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, messages.images.size())); for (const auto &image: messages.images) { CborEncoder localEncoder; cborErr(cbor_encoder_create_map(&arrayEncoder, &localEncoder, CborIndefiniteLength)); SerializeImageInternal(localEncoder, image, true); cborErr(cbor_encoder_close_container(&arrayEncoder, &localEncoder)); } cborErr(cbor_encoder_close_container(&mapEncoder, &arrayEncoder)); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); curr_size = cbor_encoder_get_buffer_size(&encoder, buffer); } void CBORStream2Serializer::SerializeCalibration(const CompressedImage &image) { CborEncoder encoder, mapEncoder; cbor_encoder_init(&encoder, buffer, max_buffer_size, 0); cborErr(cbor_encode_tag(&encoder, CborSignatureTag )); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength)); CBOR_ENC(mapEncoder, "type", "calibration"); CBOR_ENC(mapEncoder, "magic_number", user_data_magic_number); CBOR_ENC(mapEncoder, "data", image); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); curr_size = cbor_encoder_get_buffer_size(&encoder, buffer); } size_t CBORStream2Serializer::GetImageAppendOffset() const { return curr_size + sizeof(size_t) - 1; } void CBORStream2Serializer::AppendImage(size_t image_size) { if (curr_size + image_size + sizeof(size_t) + 1 >= max_buffer_size) throw JFJochException(JFJochExceptionCategory::CBORError, "No space to extend the image"); buffer[curr_size - 2] = 0x40 + 27; curr_size--; #ifdef LITTLE_ENDIAN size_t image_size_be = __builtin_bswap64(image_size); #else size_t image_size_be = image_size; #endif memcpy(buffer + curr_size, &image_size_be, sizeof(size_t)); curr_size += sizeof(size_t); curr_size += image_size + 0; buffer[curr_size] = 0xFF; curr_size++; }