Add ACK and NAK PDU abstractions #41
@ -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()) {
|
||||||
|
(true, None) => true,
|
||||||
|
(true, Some(seg_reqs)) => seg_reqs.is_empty(),
|
||||||
|
(false, None) => false,
|
||||||
|
_ => {
|
||||||
|
// Compare based on file_flag
|
||||||
if self.file_flag() == LargeFileFlag::Normal {
|
if self.file_flag() == LargeFileFlag::Normal {
|
||||||
let normal_iter = self.get_normal_segment_requests_iterator().unwrap();
|
let normal_iter = self.get_normal_segment_requests_iterator().unwrap();
|
||||||
if normal_iter != *other.segment_requests().unwrap() {
|
normal_iter == *other.segment_requests().unwrap()
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let large_iter = self.get_large_segment_requests_iterator().unwrap();
|
let large_iter = self.get_large_segment_requests_iterator().unwrap();
|
||||||
if large_iter != *other.segment_requests().unwrap() {
|
large_iter == *other.segment_requests().unwrap()
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user