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)]