diff --git a/.idea/runConfigurations/Check.xml b/.idea/runConfigurations/Check.xml new file mode 100644 index 0000000..502aa84 --- /dev/null +++ b/.idea/runConfigurations/Check.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/src/cfdp/pdu/file_data.rs b/src/cfdp/pdu/file_data.rs index cece350..1dd7062 100644 --- a/src/cfdp/pdu/file_data.rs +++ b/src/cfdp/pdu/file_data.rs @@ -1,4 +1,4 @@ -use crate::cfdp::pdu::{write_file_size, PduError, PduHeader}; +use crate::cfdp::pdu::{read_fss_field, write_fss_field, PduError, PduHeader}; use crate::cfdp::{CrcFlag, LargeFileFlag, PduType, SegmentMetadataFlag}; use crate::{ByteConversionError, SizeMissmatch}; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -23,12 +23,12 @@ pub struct SegmentMetadata<'seg_meta> { metadata: Option<&'seg_meta [u8]>, } -impl SegmentMetadata<'_> { +impl<'seg_meta> SegmentMetadata<'seg_meta> { pub fn written_len(&self) -> usize { 1 + self.seg_metadata_len as usize } - pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result { + pub(crate) fn write_to_bytes(&self, buf: &mut [u8]) -> Result { if buf.len() < self.written_len() { return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { found: buf.len(), @@ -41,6 +41,27 @@ impl SegmentMetadata<'_> { } Ok(self.written_len()) } + + pub(crate) fn from_bytes(buf: &'seg_meta [u8]) -> Result { + if buf.is_empty() { + return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: 2, + })); + } + let mut metadata = None; + let seg_metadata_len = (buf[0] & 0b111111) as usize; + if seg_metadata_len > 0 { + metadata = Some(&buf[1..1 + seg_metadata_len]); + } + Ok(Self { + // Can't fail, only 2 bits + record_continuation_state: RecordContinuationState::try_from((buf[0] >> 6) & 0b11) + .unwrap(), + seg_metadata_len: seg_metadata_len as u8, + metadata, + }) + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -81,30 +102,33 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { if segment_metadata.is_some() { pdu_header.seg_metadata_flag = SegmentMetadataFlag::Present; } - Self { + let mut pdu = Self { pdu_header, segment_metadata, offset, file_data, - } + }; + pdu.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16; + pdu } - 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::(); + fn calc_pdu_datafield_len(&self) -> usize { + let mut len = core::mem::size_of::(); if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large { len += core::mem::size_of::(); } + if self.segment_metadata.is_some() { + len += self.segment_metadata.as_ref().unwrap().written_len() + } len += self.file_data.len(); if self.pdu_header.pdu_conf.crc_flag == CrcFlag::WithCrc { len += 2; } len } + pub fn written_len(&self) -> usize { + self.pdu_header.header_len() + self.calc_pdu_datafield_len() + } pub fn offset(&self) -> u64 { self.offset @@ -114,7 +138,7 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { self.file_data } - pub fn seg_metadata(&self) -> Option<&SegmentMetadata> { + pub fn segment_metadata(&self) -> Option<&SegmentMetadata> { self.segment_metadata.as_ref() } @@ -134,15 +158,51 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { .unwrap() .write_to_bytes(&mut buf[current_idx..])?; } - write_file_size( - &mut current_idx, + current_idx += write_fss_field( self.pdu_header.common_pdu_conf().file_flag, self.offset, - buf, + &mut buf[current_idx..], )?; buf[current_idx..current_idx + self.file_data.len()].copy_from_slice(self.file_data); + current_idx += self.file_data.len(); Ok(current_idx) } + + pub fn from_bytes<'longest: 'seg_meta + 'file_data>( + buf: &'longest [u8], + ) -> Result { + let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; + let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; + let mut min_expected_len = current_idx + core::mem::size_of::(); + min_expected_len = core::cmp::max(min_expected_len, pdu_header.pdu_len()); + if buf.len() < min_expected_len { + return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: min_expected_len, + }) + .into()); + } + let mut segment_metadata = None; + if pdu_header.seg_metadata_flag == SegmentMetadataFlag::Present { + segment_metadata = Some(SegmentMetadata::from_bytes(&buf[current_idx..])?); + current_idx += segment_metadata.as_ref().unwrap().written_len(); + } + let (fss, offset) = read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx..]); + current_idx += fss; + if current_idx > full_len_without_crc { + return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { + found: current_idx, + expected: full_len_without_crc, + }) + .into()); + } + Ok(Self { + pdu_header, + segment_metadata, + offset, + file_data: &buf[current_idx..full_len_without_crc], + }) + } } #[cfg(test)] @@ -163,5 +223,63 @@ mod tests { let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data); assert_eq!(fd_pdu.file_data(), file_data); assert_eq!(fd_pdu.offset(), 10); + assert!(fd_pdu.segment_metadata().is_none()); + assert_eq!( + fd_pdu.written_len(), + fd_pdu.pdu_header.header_len() + core::mem::size_of::() + 4 + ); + } + + #[test] + fn test_serialization() { + let src_id = UbfU8::new(1); + let dest_id = UbfU8::new(2); + let transaction_seq_num = UbfU8::new(3); + let common_conf = + CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_seq_num).unwrap(); + let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0); + let file_data: [u8; 4] = [1, 2, 3, 4]; + let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data); + let mut buf: [u8; 32] = [0; 32]; + let res = fd_pdu.write_to_bytes(&mut buf); + assert!(res.is_ok()); + let written = res.unwrap(); + assert_eq!( + written, + fd_pdu.pdu_header.header_len() + core::mem::size_of::() + 4 + ); + let mut current_idx = fd_pdu.pdu_header.header_len(); + let file_size = u32::from_be_bytes( + buf[fd_pdu.pdu_header.header_len()..fd_pdu.pdu_header.header_len() + 4] + .try_into() + .unwrap(), + ); + current_idx += 4; + assert_eq!(file_size, 10); + assert_eq!(buf[current_idx], 1); + current_idx += 1; + assert_eq!(buf[current_idx], 2); + current_idx += 1; + assert_eq!(buf[current_idx], 3); + current_idx += 1; + assert_eq!(buf[current_idx], 4); + } + + #[test] + fn test_deserialization() { + let src_id = UbfU8::new(1); + let dest_id = UbfU8::new(2); + let transaction_seq_num = UbfU8::new(3); + let common_conf = + CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_seq_num).unwrap(); + let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0); + let file_data: [u8; 4] = [1, 2, 3, 4]; + let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data); + let mut buf: [u8; 32] = [0; 32]; + fd_pdu.write_to_bytes(&mut buf).unwrap(); + let fd_pdu_read_back = FileDataPdu::from_bytes(&buf); + assert!(fd_pdu_read_back.is_ok()); + let fd_pdu_read_back = fd_pdu_read_back.unwrap(); + assert_eq!(fd_pdu_read_back, fd_pdu); } } diff --git a/src/cfdp/pdu/metadata.rs b/src/cfdp/pdu/metadata.rs index 6278335..5bfee82 100644 --- a/src/cfdp/pdu/metadata.rs +++ b/src/cfdp/pdu/metadata.rs @@ -1,5 +1,5 @@ use crate::cfdp::lv::Lv; -use crate::cfdp::pdu::{write_file_size, FileDirectiveType, PduError, PduHeader}; +use crate::cfdp::pdu::{read_fss_field, write_fss_field, FileDirectiveType, PduError, PduHeader}; use crate::cfdp::tlv::Tlv; use crate::cfdp::{ChecksumType, CrcFlag, LargeFileFlag, PduType}; use crate::{ByteConversionError, SizeMissmatch, CRC_CCITT_FALSE}; @@ -131,26 +131,15 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { 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 = - 2 + 4 + src_file_name.len_full() as u16 + dest_file_name.len_full() as u16; - if is_large_file { - pdu_header.pdu_datafield_len += 4; - } - if has_crc { - pdu_header.pdu_datafield_len += 2; - } - if let Some(opts) = options { - pdu_header.pdu_datafield_len += opts.len() as u16; - } - Self { + let mut pdu = Self { pdu_header, metadata_params, src_file_name, dest_file_name, options, - } + }; + pdu.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16; + pdu } pub fn src_file_name(&self) -> Lv<'src_name> { @@ -177,7 +166,12 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { pub fn written_len(&self) -> usize { // One directive type octet, and one byte of the parameter field. - let mut len = self.pdu_header.header_len() + 2; + self.pdu_header.header_len() + self.calc_pdu_datafield_len() + } + + fn calc_pdu_datafield_len(&self) -> usize { + // One directve type octet and one byte of the directive parameter field. + let mut len = 2; if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { len += 8; } else { @@ -214,11 +208,10 @@ 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; - write_file_size( - &mut current_idx, + current_idx += write_fss_field( self.pdu_header.common_pdu_conf().file_flag, self.metadata_params.file_size, - buf, + &mut buf[current_idx..], )?; current_idx += self .src_file_name @@ -241,7 +234,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { pub fn from_bytes<'longest: 'src_name + 'dest_name + 'opts>( buf: &'longest [u8], - ) -> Result, PduError> { + ) -> Result { let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large; @@ -268,22 +261,15 @@ impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { ))); } current_idx += 1; - - let file_size = if pdu_header.pdu_conf.file_flag == LargeFileFlag::Large { - u64::from_be_bytes(buf[current_idx + 1..current_idx + 9].try_into().unwrap()) - } else { - u32::from_be_bytes(buf[current_idx + 1..current_idx + 5].try_into().unwrap()) as u64 - }; + let (fss_len, file_size) = + read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx + 1..]); let metadata_params = MetadataGenericParams { closure_requested: ((buf[current_idx] >> 6) & 0b1) != 0, checksum_type: ChecksumType::try_from(buf[current_idx] & 0b1111) .map_err(|_| PduError::InvalidChecksumType(buf[current_idx] & 0b1111))?, file_size, }; - current_idx += 5; - if is_large_file { - current_idx += 4; - } + current_idx += 1 + fss_len; let src_file_name = Lv::from_bytes(&buf[current_idx..])?; current_idx += src_file_name.len_full(); let dest_file_name = Lv::from_bytes(&buf[current_idx..])?; @@ -343,7 +329,7 @@ pub mod tests { MetadataPdu<'static, 'static, 'opts>, ) { let pdu_header = PduHeader::new_no_file_data(common_pdu_conf(crc_flag, fss), 0); - let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 10); + let metadata_params = MetadataGenericParams::new(false, ChecksumType::Crc32, 0x1010); 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"); @@ -399,7 +385,7 @@ pub mod tests { assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8); assert_eq!(buf[8] >> 6, false as u8); assert_eq!(buf[8] & 0b1111, ChecksumType::Crc32 as u8); - assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 10); + assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010); let mut current_idx = 13; let src_name_from_raw = Lv::from_bytes(&buf[current_idx..]).expect("Creating source name LV failed"); diff --git a/src/cfdp/pdu/mod.rs b/src/cfdp/pdu/mod.rs index efca589..83c043a 100644 --- a/src/cfdp/pdu/mod.rs +++ b/src/cfdp/pdu/mod.rs @@ -468,25 +468,35 @@ impl PduHeader { } } -pub(crate) fn write_file_size( - current_idx: &mut usize, - fss: LargeFileFlag, +pub(crate) fn write_fss_field( + file_flag: 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::() +) -> Result { + Ok(if file_flag == LargeFileFlag::Large { + buf[..core::mem::size_of::()].copy_from_slice(&file_size.to_be_bytes()); + 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::() + buf[..core::mem::size_of::()].copy_from_slice(&(file_size as u32).to_be_bytes()); + core::mem::size_of::() + }) +} + +pub(crate) fn read_fss_field(file_flag: LargeFileFlag, buf: &[u8]) -> (usize, u64) { + if file_flag == LargeFileFlag::Large { + ( + core::mem::size_of::(), + u64::from_be_bytes(buf[..core::mem::size_of::()].try_into().unwrap()), + ) + } else { + ( + core::mem::size_of::(), + u32::from_be_bytes(buf[..core::mem::size_of::()].try_into().unwrap()).into(), + ) } - Ok(()) } #[cfg(test)]