From b9d18ae0a3898e27e74fc8eedca8ea2987d14b49 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 10 Dec 2025 13:42:34 +0100 Subject: [PATCH] add creator with reserved datafield --- src/uslp/mod.rs | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/src/uslp/mod.rs b/src/uslp/mod.rs index 0408e71..e7b9641 100644 --- a/src/uslp/mod.rs +++ b/src/uslp/mod.rs @@ -1,5 +1,6 @@ //! # Support of the CCSDS Unified Space Data Link Protocol (USLP) #![deny(missing_docs)] + use arbitrary_int::{prelude::*, u4, u6}; use crate::{crc::CRC_CCITT_FALSE, ByteConversionError}; @@ -577,6 +578,100 @@ impl<'data> TransferFrameCreator<'data> { } } +/// USLP transfer frame creator which can reduce copy operations. +/// +/// This structure provides direct mutable access to the data zone. This avoids the need for an +/// additional slice which would be required to hold the transfer data. Instead, user data can be +/// copied into the frame field directly. The full length of the user data needs to be known +/// beforehand. +pub struct TransferFrameCreatorWithReservedData<'data> { + buf: &'data mut [u8], + len_written: usize, + data_index: usize, + data_size: usize, + has_fecf: bool, +} + +impl<'data> TransferFrameCreatorWithReservedData<'data> { + /// Constructor. + /// + /// If the operational control field is present, the OCF flag in the primary header will + /// be set accordingly. The frame length field of the [PrimaryHeader] is also updated according + /// to the provided arguments. + pub fn new( + buf: &'data mut [u8], + mut primary_header: PrimaryHeader, + data_field_header: TransferFrameDataFieldHeader, + data_size: usize, + op_control_field: Option, + has_fecf: bool, + ) -> Result, ByteConversionError> { + let len_written = primary_header.len_header() + + data_field_header.len_header() + + data_size + + if op_control_field.is_some() { 4 } else { 0 } + + if has_fecf { 2 } else { 0 }; + primary_header.set_frame_len(len_written); + if op_control_field.is_some() { + primary_header.ocf_flag = true; + } + if len_written > buf.len() { + return Err(ByteConversionError::ToSliceTooSmall { + found: buf.len(), + expected: len_written, + }); + } + let mut current_index = 0; + current_index += primary_header.write_to_bytes(buf)?; + + current_index += + data_field_header.write_to_bytes(&mut buf[primary_header.len_header()..])?; + let data_index = current_index; + //buf[current_index..current_index + self.data.len()].copy_from_slice(self.data); + current_index += data_size; + + if let Some(ocf) = op_control_field { + buf[current_index..current_index + 4].copy_from_slice(&ocf.to_be_bytes()); + } + Ok(Self { + buf, + data_size, + data_index, + len_written, + has_fecf, + }) + } + + /// Length of the frame when written to bytes. + pub fn len_written(&self) -> usize { + self.len_written + } + + /// Mutable accesss to user data buffer. + pub fn data_mut(&mut self) -> &mut [u8] { + &mut self.buf[self.data_index..self.data_index + self.data_size] + } + + /// Shared accesss to user data buffer. + pub fn data(&self) -> &[u8] { + &self.buf[self.data_index..self.data_index + self.data_size] + } + + /// Write [self] to the provided byte buffer. + /// + /// Calculates and writes the checksum if the `has_fecf` field is set. + /// Returns the frame size. + pub fn finish(self) -> usize { + if self.has_fecf { + let mut digest = CRC_CCITT_FALSE.digest(); + digest.update(&self.buf[0..self.len_written - 2]); + let crc = digest.finalize(); + self.buf[self.len_written - 2..self.len_written].copy_from_slice(&crc.to_be_bytes()); + } + self.len_written + } +} + /// Simple USLP transfer frame reader. /// /// Currently, only insert zone lengths of 0 are supported. @@ -1199,6 +1294,37 @@ mod tests { assert_eq!(reader.len_frame(), 14); } + #[test] + fn test_frame_creator_no_fecf() { + let mut primary_header = PrimaryHeader::new( + 0x1234, + SourceOrDestField::Source, + u6::new(0b101010), + u4::new(0b0101), + 0, + ); + let data_field_header = TransferFrameDataFieldHeader { + construction_rule: ConstructionRule::NoSegmentation, + uslp_protocol_id: UslpProtocolId::UserDefinedOctetStream, + fhp_or_lvo: None, + }; + let data = [1, 2, 3, 4]; + let frame_creator = + TransferFrameCreator::new(primary_header, data_field_header, &data, None, false); + let mut buf: [u8; 64] = [0; 64]; + assert_eq!(frame_creator.len_written(), 12); + let written = frame_creator.write_to_bytes(&mut buf).unwrap(); + assert_eq!(written, 12); + assert_eq!(written, frame_creator.len_written()); + let reader = TransferFrameReader::from_bytes(&buf, false).unwrap(); + primary_header.set_frame_len(written); + assert_eq!(reader.primary_header(), &primary_header); + assert_eq!(reader.data_field_header(), &data_field_header); + assert_eq!(reader.data(), &data); + assert!(reader.operational_control_field().is_none()); + assert_eq!(reader.len_frame(), 12); + } + #[test] fn test_frame_creator_using_vec() { // Relying on the reader implementation for now. @@ -1263,4 +1389,116 @@ mod tests { assert_eq!(reader.operational_control_field().unwrap(), 4); assert_eq!(reader.len_frame(), 18); } + + #[test] + fn test_creator_with_reserved_data() { + let mut primary_header = PrimaryHeader::new( + 0x1234, + SourceOrDestField::Source, + u6::new(0b101010), + u4::new(0b0101), + 0, + ); + let data_field_header = TransferFrameDataFieldHeader::new( + ConstructionRule::NoSegmentation, + UslpProtocolId::UserDefinedOctetStream, + None, + ) + .unwrap(); + let mut buf: [u8; 32] = [0; 32]; + let mut frame_creator = TransferFrameCreatorWithReservedData::new( + &mut buf, + primary_header, + data_field_header, + 4, + None, + true, + ) + .unwrap(); + assert_eq!(frame_creator.data().len(), 4); + frame_creator.data_mut().copy_from_slice(&[1, 2, 3, 4]); + let frame_size = frame_creator.finish(); + let reader = TransferFrameReader::from_bytes(&buf[0..frame_size], true).unwrap(); + primary_header.set_frame_len(frame_size); + assert_eq!(reader.primary_header(), &primary_header); + assert_eq!(reader.data_field_header(), &data_field_header); + assert_eq!(reader.data(), &[1, 2, 3, 4]); + assert!(reader.operational_control_field().is_none()); + assert_eq!(reader.len_frame(), 14); + } + + #[test] + fn test_creator_with_reserved_data_and_ocf_field() { + let mut primary_header = PrimaryHeader::new( + 0x1234, + SourceOrDestField::Source, + u6::new(0b101010), + u4::new(0b0101), + 0, + ); + let data_field_header = TransferFrameDataFieldHeader::new( + ConstructionRule::NoSegmentation, + UslpProtocolId::UserDefinedOctetStream, + None, + ) + .unwrap(); + let mut buf: [u8; 32] = [0; 32]; + let mut frame_creator = TransferFrameCreatorWithReservedData::new( + &mut buf, + primary_header, + data_field_header, + 4, + Some(4), + true, + ) + .unwrap(); + assert_eq!(frame_creator.data().len(), 4); + frame_creator.data_mut().copy_from_slice(&[1, 2, 3, 4]); + let frame_size = frame_creator.finish(); + let reader = TransferFrameReader::from_bytes(&buf[0..frame_size], true).unwrap(); + primary_header.set_frame_len(frame_size); + primary_header.ocf_flag = true; + assert_eq!(reader.primary_header(), &primary_header); + assert_eq!(reader.data_field_header(), &data_field_header); + assert_eq!(reader.data(), &[1, 2, 3, 4]); + assert_eq!(reader.operational_control_field().unwrap(), 4); + assert_eq!(reader.len_frame(), 18); + } + + #[test] + fn test_creator_no_fecf() { + let mut primary_header = PrimaryHeader::new( + 0x1234, + SourceOrDestField::Source, + u6::new(0b101010), + u4::new(0b0101), + 0, + ); + let data_field_header = TransferFrameDataFieldHeader::new( + ConstructionRule::NoSegmentation, + UslpProtocolId::UserDefinedOctetStream, + None, + ) + .unwrap(); + let mut buf: [u8; 32] = [0; 32]; + let mut frame_creator = TransferFrameCreatorWithReservedData::new( + &mut buf, + primary_header, + data_field_header, + 4, + None, + false, + ) + .unwrap(); + assert_eq!(frame_creator.data().len(), 4); + frame_creator.data_mut().copy_from_slice(&[1, 2, 3, 4]); + let frame_size = frame_creator.finish(); + let reader = TransferFrameReader::from_bytes(&buf[0..frame_size], false).unwrap(); + primary_header.set_frame_len(frame_size); + assert_eq!(reader.primary_header(), &primary_header); + assert_eq!(reader.data_field_header(), &data_field_header); + assert_eq!(reader.data(), &[1, 2, 3, 4]); + assert!(reader.operational_control_field().is_none()); + assert_eq!(reader.len_frame(), 12); + } } -- 2.43.0