Add ACK and NAK PDU abstractions #41

Merged
muellerr merged 19 commits from add-ack-nak-pdus into main 2023-12-01 15:33:36 +01:00
Showing only changes of commit 2ba2998426 - Show all commits

View File

@ -1,9 +1,8 @@
use core::marker::PhantomData;
use crate::{ use crate::{
cfdp::{CrcFlag, Direction, LargeFileFlag}, cfdp::{CrcFlag, Direction, LargeFileFlag},
ByteConversionError, ByteConversionError,
}; };
use core::marker::PhantomData;
use super::{ use super::{
add_pdu_crc, generic_length_checks_pdu_deserialization, CfdpPdu, FileDirectiveType, PduError, add_pdu_crc, generic_length_checks_pdu_deserialization, CfdpPdu, FileDirectiveType, PduError,
@ -19,6 +18,15 @@ pub enum SegmentRequests<'a> {
U64Pairs(&'a [(u64, u64)]), U64Pairs(&'a [(u64, u64)]),
} }
impl SegmentRequests<'_> {
pub fn is_empty(&self) -> bool {
match self {
SegmentRequests::U32Pairs(pairs) => pairs.is_empty(),
SegmentRequests::U64Pairs(pairs) => pairs.is_empty(),
}
}
}
/// NAK PDU abstraction specialized in the creation of NAK PDUs. /// NAK PDU abstraction specialized in the creation of NAK PDUs.
/// ///
/// It exposes a specialized API which simplifies to generate these NAK PDUs with the /// It exposes a specialized API which simplifies to generate these NAK PDUs with the
@ -54,11 +62,15 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
end_of_scope: u32, end_of_scope: u32,
segment_requests: &'seg_reqs [(u32, u32)], segment_requests: &'seg_reqs [(u32, u32)],
) -> Result<NakPduCreator, PduError> { ) -> Result<NakPduCreator, PduError> {
let mut passed_segment_requests = None;
if !segment_requests.is_empty() {
passed_segment_requests = Some(SegmentRequests::U32Pairs(segment_requests));
}
Self::new_generic( Self::new_generic(
pdu_header, pdu_header,
start_of_scope.into(), start_of_scope.into(),
end_of_scope.into(), end_of_scope.into(),
Some(SegmentRequests::U32Pairs(segment_requests)), passed_segment_requests,
) )
} }
@ -68,11 +80,15 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
end_of_scope: u64, end_of_scope: u64,
segment_requests: &'seg_reqs [(u64, u64)], segment_requests: &'seg_reqs [(u64, u64)],
) -> Result<NakPduCreator, PduError> { ) -> Result<NakPduCreator, PduError> {
let mut passed_segment_requests = None;
if !segment_requests.is_empty() {
passed_segment_requests = Some(SegmentRequests::U64Pairs(segment_requests));
}
Self::new_generic( Self::new_generic(
pdu_header, pdu_header,
start_of_scope, start_of_scope,
end_of_scope, end_of_scope,
Some(SegmentRequests::U64Pairs(segment_requests)), passed_segment_requests,
) )
} }
@ -230,6 +246,7 @@ impl WritablePduPacket for NakPduCreator<'_> {
/// Special iterator type for the NAK PDU which allows to iterate over both normal and large file /// Special iterator type for the NAK PDU which allows to iterate over both normal and large file
/// segment requests. /// segment requests.
#[derive(Debug)]
pub struct SegmentRequestIter<'a, T> { pub struct SegmentRequestIter<'a, T> {
seq_req_raw: &'a [u8], seq_req_raw: &'a [u8],
current_idx: usize, current_idx: usize,
@ -422,6 +439,17 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
self.end_of_scope self.end_of_scope
} }
pub fn num_segment_reqs(&self) -> usize {
if self.seg_reqs_raw.is_empty() {
return 0;
}
if self.file_flag() == LargeFileFlag::Normal {
self.seg_reqs_raw.len() / 8
} else {
self.seg_reqs_raw.len() / 16
}
}
/// This function returns [None] if this NAK PDUs contains segment requests for a large file. /// This function returns [None] if this NAK PDUs contains segment requests for a large file.
pub fn get_normal_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_, u32>> { pub fn get_normal_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_, u32>> {
if self.file_flag() == LargeFileFlag::Large { if self.file_flag() == LargeFileFlag::Large {
@ -448,27 +476,30 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
} }
impl<'a, 'b> PartialEq<NakPduCreator<'a>> for NakPduReader<'b> { impl<'a, 'b> PartialEq<NakPduCreator<'a>> for NakPduReader<'b> {
fn eq(&self, other: &NakPduCreator) -> bool { fn eq(&self, other: &NakPduCreator<'a>) -> bool {
if (self.pdu_header() != other.pdu_header() || self.end_of_scope() != other.end_of_scope()) if self.pdu_header() != other.pdu_header()
|| (self.start_of_scope() != other.start_of_scope()) || self.end_of_scope() != other.end_of_scope()
|| self.start_of_scope() != other.start_of_scope()
{ {
return false; return false;
} }
if other.segment_requests().is_none() && self.seg_reqs_raw.is_empty() {
return true; // Check if both segment requests are empty or None
} match (self.seg_reqs_raw.is_empty(), other.segment_requests()) {
if self.file_flag() == LargeFileFlag::Normal { (true, None) => true,
let normal_iter = self.get_normal_segment_requests_iterator().unwrap(); (true, Some(seg_reqs)) => seg_reqs.is_empty(),
if normal_iter != *other.segment_requests().unwrap() { (false, None) => false,
return false; _ => {
} // Compare based on file_flag
} else { if self.file_flag() == LargeFileFlag::Normal {
let large_iter = self.get_large_segment_requests_iterator().unwrap(); let normal_iter = self.get_normal_segment_requests_iterator().unwrap();
if large_iter != *other.segment_requests().unwrap() { normal_iter == *other.segment_requests().unwrap()
return false; } else {
let large_iter = self.get_large_segment_requests_iterator().unwrap();
large_iter == *other.segment_requests().unwrap()
}
} }
} }
true
} }
} }
@ -512,6 +543,8 @@ mod tests {
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0); let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300) let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300)
.expect("creating NAK PDU creator failed"); .expect("creating NAK PDU creator failed");
assert_eq!(nak_pdu.start_of_scope(), 100);
assert_eq!(nak_pdu.end_of_scope(), 300);
let mut buf: [u8; 64] = [0; 64]; let mut buf: [u8; 64] = [0; 64];
nak_pdu nak_pdu
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
@ -589,7 +622,7 @@ mod tests {
} }
#[test] #[test]
fn test_deserialization_one_file_segment() { fn test_deserialization_large_segments() {
let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Large); let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Large);
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0); let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
let nak_pdu = let nak_pdu =
@ -601,5 +634,90 @@ mod tests {
.expect("writing NAK PDU to buffer failed"); .expect("writing NAK PDU to buffer failed");
let nak_pdu_deser = NakPduReader::from_bytes(&buf).expect("deserializing NAK PDU failed"); let nak_pdu_deser = NakPduReader::from_bytes(&buf).expect("deserializing NAK PDU failed");
assert_eq!(nak_pdu_deser, nak_pdu); assert_eq!(nak_pdu_deser, nak_pdu);
assert_eq!(nak_pdu_deser.start_of_scope(), 100);
assert_eq!(nak_pdu_deser.end_of_scope(), 300);
assert_eq!(nak_pdu_deser.num_segment_reqs(), 2);
assert!(nak_pdu_deser
.get_large_segment_requests_iterator()
.is_some());
assert!(nak_pdu_deser
.get_normal_segment_requests_iterator()
.is_none());
assert_eq!(
nak_pdu_deser
.get_large_segment_requests_iterator()
.unwrap()
.count(),
2
);
for (idx, large_segments) in nak_pdu_deser
.get_large_segment_requests_iterator()
.unwrap()
.enumerate()
{
if idx == 0 {
assert_eq!(large_segments.0, 50);
assert_eq!(large_segments.1, 100);
} else {
assert_eq!(large_segments.0, 200);
assert_eq!(large_segments.1, 300);
}
}
}
#[test]
fn test_deserialization_normal_segments() {
let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
let nak_pdu = NakPduCreator::new(pdu_header, 100, 300, &[(50, 100), (200, 300)])
.expect("creating NAK PDU creator failed");
let mut buf: [u8; 128] = [0; 128];
nak_pdu
.write_to_bytes(&mut buf)
.expect("writing NAK PDU to buffer failed");
let nak_pdu_deser = NakPduReader::from_bytes(&buf).expect("deserializing NAK PDU failed");
assert_eq!(nak_pdu_deser, nak_pdu);
assert_eq!(nak_pdu_deser.start_of_scope(), 100);
assert_eq!(nak_pdu_deser.end_of_scope(), 300);
assert_eq!(nak_pdu_deser.num_segment_reqs(), 2);
assert!(nak_pdu_deser
.get_normal_segment_requests_iterator()
.is_some());
assert!(nak_pdu_deser
.get_large_segment_requests_iterator()
.is_none());
assert_eq!(
nak_pdu_deser
.get_normal_segment_requests_iterator()
.unwrap()
.count(),
2
);
for (idx, large_segments) in nak_pdu_deser
.get_normal_segment_requests_iterator()
.unwrap()
.enumerate()
{
if idx == 0 {
assert_eq!(large_segments.0, 50);
assert_eq!(large_segments.1, 100);
} else {
assert_eq!(large_segments.0, 200);
assert_eq!(large_segments.1, 300);
}
}
}
#[test]
fn test_empty_is_empty() {
let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
let nak_pdu_0 =
NakPduCreator::new(pdu_header, 100, 300, &[]).expect("creating NAK PDU creator failed");
let nak_pdu_1 = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300)
.expect("creating NAK PDU creator failed");
assert_eq!(nak_pdu_0, nak_pdu_1);
// Assert the segment request is mapped to None.
assert!(nak_pdu_0.segment_requests().is_none());
} }
} }