diff --git a/src/cfdp/pdu/file_data.rs b/src/cfdp/pdu/file_data.rs new file mode 100644 index 0000000..59a53ea --- /dev/null +++ b/src/cfdp/pdu/file_data.rs @@ -0,0 +1,129 @@ +use crate::cfdp::pdu::{write_file_size, PduError, PduHeader}; +use crate::cfdp::{CrcFlag, LargeFileFlag, PduType, SegmentMetadataFlag}; +use crate::{ByteConversionError, SizeMissmatch}; +use num_enum::{IntoPrimitive, TryFromPrimitive}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u8)] +pub enum RecordContinuationState { + NoStartNoEnd = 0b00, + StartWithoutEnd = 0b01, + EndWithoutStart = 0b10, + StartAndEnd = 0b11, +} + +pub struct SegmentMetadata<'seg_meta> { + record_continuation_state: RecordContinuationState, + seg_metadata_len: u8, + metadata: Option<&'seg_meta [u8]>, +} + +impl SegmentMetadata<'_> { + pub fn written_len(&self) -> usize { + 1 + self.seg_metadata_len as usize + } + + pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { + if buf.len() < self.written_len() { + return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: self.written_len(), + })); + } + buf[0] = ((self.record_continuation_state as u8) << 6) | self.seg_metadata_len; + if self.metadata.is_some() { + buf[1..].copy_from_slice(self.metadata.unwrap()) + } + Ok(self.written_len()) + } +} + +pub struct FileDataPdu<'seg_meta, 'file_data> { + pdu_header: PduHeader, + segment_metadata: Option>, + offset: u64, + file_data: &'file_data [u8], +} + +impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { + pub fn new_with_seg_metadata( + pdu_header: PduHeader, + segment_metadata: SegmentMetadata<'seg_meta>, + offset: u64, + file_data: &'file_data [u8], + ) -> Self { + Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data) + } + + pub fn new_no_seg_metadata( + pdu_header: PduHeader, + offset: u64, + file_data: &'file_data [u8], + ) -> Self { + Self::new_generic(pdu_header, None, offset, file_data) + } + + pub fn new_generic( + mut pdu_header: PduHeader, + segment_metadata: Option>, + offset: u64, + file_data: &'file_data [u8], + ) -> Self { + pdu_header.pdu_type = PduType::FileData; + if segment_metadata.is_some() { + pdu_header.seg_metadata_flag = SegmentMetadataFlag::Present; + } + Self { + pdu_header, + segment_metadata, + offset, + file_data, + } + } + + pub fn written_len(&self) -> usize { + let mut len = self.pdu_header.header_len(); + if self.segment_metadata.is_some() { + len += self.segment_metadata.as_ref().unwrap().written_len() + } + // Regular file size + len += core::mem::size_of::(); + if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large { + len += core::mem::size_of::(); + } + len += self.file_data.len(); + if self.pdu_header.pdu_conf.crc_flag == CrcFlag::WithCrc { + len += 2; + } + len + } + + pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { + if buf.len() < self.written_len() { + return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: self.written_len(), + }) + .into()); + } + let mut current_idx = self.pdu_header.write_to_bytes(buf)?; + if self.segment_metadata.is_some() { + current_idx += self + .segment_metadata + .as_ref() + .unwrap() + .write_to_bytes(&mut buf[current_idx..])?; + } + write_file_size( + &mut current_idx, + self.pdu_header.common_pdu_conf().file_flag, + self.offset, + buf, + )?; + buf[current_idx..current_idx + self.file_data.len()].copy_from_slice(self.file_data); + Ok(current_idx) + } +} diff --git a/src/cfdp/pdu/metadata.rs b/src/cfdp/pdu/metadata.rs index dd6589c..ce0a70a 100644 --- a/src/cfdp/pdu/metadata.rs +++ b/src/cfdp/pdu/metadata.rs @@ -1,7 +1,7 @@ use crate::cfdp::lv::Lv; -use crate::cfdp::pdu::{FileDirectiveType, PduError, PduHeader}; +use crate::cfdp::pdu::{write_file_size, FileDirectiveType, PduError, PduHeader}; use crate::cfdp::tlv::Tlv; -use crate::cfdp::{ChecksumType, CrcFlag, LargeFileFlag}; +use crate::cfdp::{ChecksumType, CrcFlag, LargeFileFlag, PduType}; use crate::{ByteConversionError, SizeMissmatch, CRC_CCITT_FALSE}; #[cfg(feature = "alloc")] use alloc::vec::Vec; @@ -130,6 +130,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { dest_file_name: Lv<'dest_name>, options: Option<&'opts [u8]>, ) -> Self { + pdu_header.pdu_type = PduType::FileDirective; let is_large_file = pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large; let has_crc = pdu_header.common_pdu_conf().crc_flag == CrcFlag::WithCrc; pdu_header.pdu_datafield_len = @@ -213,18 +214,12 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 7) | (self.metadata_params.checksum_type as u8); current_idx += 1; - if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { - buf[current_idx..current_idx + core::mem::size_of::()] - .copy_from_slice(&self.metadata_params.file_size.to_be_bytes()); - current_idx += core::mem::size_of::() - } else { - if self.metadata_params.file_size > u32::MAX as u64 { - return Err(PduError::FileSizeTooLarge(self.metadata_params.file_size)); - } - buf[current_idx..current_idx + core::mem::size_of::()] - .copy_from_slice(&(self.metadata_params.file_size as u32).to_be_bytes()); - current_idx += core::mem::size_of::() - } + write_file_size( + &mut current_idx, + self.pdu_header.common_pdu_conf().file_flag, + self.metadata_params.file_size, + buf, + )?; current_idx += self .src_file_name .write_to_be_bytes(&mut buf[current_idx..])?; @@ -318,7 +313,9 @@ pub mod tests { use crate::cfdp::pdu::tests::verify_raw_header; use crate::cfdp::pdu::{CommonPduConfig, FileDirectiveType, PduHeader}; use crate::cfdp::tlv::{Tlv, TlvType}; - use crate::cfdp::{ChecksumType, CrcFlag, LargeFileFlag}; + use crate::cfdp::{ + ChecksumType, CrcFlag, LargeFileFlag, PduType, SegmentMetadataFlag, SegmentationControl, + }; use crate::util::UbfU8; use std::vec; @@ -544,4 +541,21 @@ pub mod tests { } assert_eq!(accumulated_len, pdu_read_back.options().unwrap().len()); } + + #[test] + fn test_corrects_pdu_header() { + let pdu_header = PduHeader::new_for_file_data( + common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal), + 0, + SegmentMetadataFlag::NotPresent, + SegmentationControl::NoRecordBoundaryPreservation, + ); + let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 10); + let src_filename = Lv::new_from_str(SRC_FILENAME).expect("Generating string LV failed"); + let dest_filename = + Lv::new_from_str(DEST_FILENAME).expect("Generating destination LV failed"); + let metadata_pdu = + MetadataPdu::new(pdu_header, metadata_params, src_filename, dest_filename); + assert_eq!(metadata_pdu.pdu_header().pdu_type(), PduType::FileDirective); + } } diff --git a/src/cfdp/pdu/mod.rs b/src/cfdp/pdu/mod.rs index 1643b5d..31fafb4 100644 --- a/src/cfdp/pdu/mod.rs +++ b/src/cfdp/pdu/mod.rs @@ -7,6 +7,7 @@ use core::fmt::{Display, Formatter}; #[cfg(feature = "std")] use std::error::Error; +pub mod file_data; pub mod metadata; #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] @@ -442,6 +443,27 @@ impl PduHeader { } } +pub(crate) fn write_file_size( + current_idx: &mut usize, + fss: LargeFileFlag, + file_size: u64, + buf: &mut [u8], +) -> Result<(), PduError> { + if fss == LargeFileFlag::Large { + buf[*current_idx..*current_idx + core::mem::size_of::()] + .copy_from_slice(&file_size.to_be_bytes()); + *current_idx += core::mem::size_of::() + } else { + if file_size > u32::MAX as u64 { + return Err(PduError::FileSizeTooLarge(file_size)); + } + buf[*current_idx..*current_idx + core::mem::size_of::()] + .copy_from_slice(&(file_size as u32).to_be_bytes()); + *current_idx += core::mem::size_of::() + } + Ok(()) +} + #[cfg(test)] mod tests { use crate::cfdp::pdu::{CommonPduConfig, PduError, PduHeader, FIXED_HEADER_LEN};