CFDP initial packet support #14
19
.idea/runConfigurations/Check.xml
Normal file
19
.idea/runConfigurations/Check.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Check" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="check --all-features" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="true" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -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<usize, ByteConversionError> {
|
||||
pub(crate) fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
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<Self, ByteConversionError> {
|
||||
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::<u32>();
|
||||
fn calc_pdu_datafield_len(&self) -> usize {
|
||||
let mut len = core::mem::size_of::<u32>();
|
||||
if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
|
||||
len += core::mem::size_of::<u32>();
|
||||
}
|
||||
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<Self, PduError> {
|
||||
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::<u32>();
|
||||
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::<u32>() + 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::<u32>() + 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<MetadataPdu<'src_name, 'dest_name, 'opts>, PduError> {
|
||||
) -> Result<Self, PduError> {
|
||||
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");
|
||||
|
@ -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::<u64>()]
|
||||
.copy_from_slice(&file_size.to_be_bytes());
|
||||
*current_idx += core::mem::size_of::<u64>()
|
||||
) -> Result<usize, PduError> {
|
||||
Ok(if file_flag == LargeFileFlag::Large {
|
||||
buf[..core::mem::size_of::<u64>()].copy_from_slice(&file_size.to_be_bytes());
|
||||
core::mem::size_of::<u64>()
|
||||
} else {
|
||||
if file_size > u32::MAX as u64 {
|
||||
return Err(PduError::FileSizeTooLarge(file_size));
|
||||
}
|
||||
buf[*current_idx..*current_idx + core::mem::size_of::<u32>()]
|
||||
.copy_from_slice(&(file_size as u32).to_be_bytes());
|
||||
*current_idx += core::mem::size_of::<u32>()
|
||||
buf[..core::mem::size_of::<u32>()].copy_from_slice(&(file_size as u32).to_be_bytes());
|
||||
core::mem::size_of::<u32>()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn read_fss_field(file_flag: LargeFileFlag, buf: &[u8]) -> (usize, u64) {
|
||||
if file_flag == LargeFileFlag::Large {
|
||||
(
|
||||
core::mem::size_of::<u64>(),
|
||||
u64::from_be_bytes(buf[..core::mem::size_of::<u64>()].try_into().unwrap()),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
core::mem::size_of::<u32>(),
|
||||
u32::from_be_bytes(buf[..core::mem::size_of::<u32>()].try_into().unwrap()).into(),
|
||||
)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user