NAK PDU reader update #134

Merged
muellerr merged 1 commits from nak-pdu-reader-refactoring into main 2025-08-20 17:53:33 +02:00
2 changed files with 87 additions and 126 deletions

View File

@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Changed ## Changed
- CFDP NAK PDU `SegmentRequestIter` is not generic over the file size anymore. Instead, the
iterator returns pairs of `u64` for both large and normal file size.
- `PusVersion::VersionNotSupported` contains raw version number instead of `PusVersion` enum now - `PusVersion::VersionNotSupported` contains raw version number instead of `PusVersion` enum now
to make it more flexible. to make it more flexible.
- `pus_version` API now returns a `Result<PusVersion, u8>` instead of a `PusVersion` to allow - `pus_version` API now returns a `Result<PusVersion, u8>` instead of a `PusVersion` to allow

View File

@@ -2,7 +2,6 @@ use crate::{
cfdp::{CrcFlag, Direction, LargeFileFlag}, cfdp::{CrcFlag, Direction, LargeFileFlag},
ByteConversionError, ByteConversionError,
}; };
use core::{marker::PhantomData, mem::size_of};
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,
@@ -247,85 +246,82 @@ 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)] #[derive(Debug)]
pub struct SegmentRequestIter<'a, T> { pub struct SegmentRequestIter<'a> {
seq_req_raw: &'a [u8], seq_req_raw: &'a [u8],
large_file: LargeFileFlag,
current_idx: usize, current_idx: usize,
phantom: core::marker::PhantomData<T>,
} }
pub trait SegReqFromBytes { impl Iterator for SegmentRequestIter<'_> {
fn from_bytes(bytes: &[u8]) -> Self; type Item = (u64, u64);
}
impl SegReqFromBytes for u32 {
fn from_bytes(bytes: &[u8]) -> u32 {
u32::from_be_bytes(bytes.try_into().unwrap())
}
}
impl SegReqFromBytes for u64 {
fn from_bytes(bytes: &[u8]) -> u64 {
u64::from_be_bytes(bytes.try_into().unwrap())
}
}
impl<T> Iterator for SegmentRequestIter<'_, T>
where
T: SegReqFromBytes,
{
type Item = (T, T);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let value = self.next_at_offset(self.current_idx); let value = self.next_at_offset(self.current_idx);
self.current_idx += 2 * size_of::<T>(); if value.is_none() {
return value;
}
self.current_idx += 2 * self.increment();
value value
} }
} }
impl<'a> PartialEq<SegmentRequests<'a>> for SegmentRequestIter<'_, u32> { impl SegmentRequestIter<'_> {
fn eq(&self, other: &SegmentRequests<'a>) -> bool { const fn increment(&self) -> usize {
match other { match self.large_file {
SegmentRequests::U32Pairs(pairs) => self.compare_pairs(pairs), LargeFileFlag::Normal => core::mem::size_of::<u32>(),
SegmentRequests::U64Pairs(pairs) => { LargeFileFlag::Large => core::mem::size_of::<u64>(),
if pairs.is_empty() && self.seq_req_raw.is_empty() {
return true;
} }
false
} }
fn next_at_offset(&self, mut offset: usize) -> Option<(u64, u64)> {
let increment = self.increment();
if offset + increment * 2 > self.seq_req_raw.len() {
return None;
}
match self.large_file {
LargeFileFlag::Normal => {
let start_offset = u32::from_be_bytes(
self.seq_req_raw[offset..offset + increment]
.try_into()
.unwrap(),
);
offset += increment;
let end_offset = u32::from_be_bytes(
self.seq_req_raw[offset..offset + increment]
.try_into()
.unwrap(),
);
Some((start_offset as u64, end_offset as u64))
}
LargeFileFlag::Large => {
let start_offset = u64::from_be_bytes(
self.seq_req_raw[offset..offset + increment]
.try_into()
.unwrap(),
);
offset += increment;
let end_offset = u64::from_be_bytes(
self.seq_req_raw[offset..offset + increment]
.try_into()
.unwrap(),
);
Some((start_offset, end_offset))
} }
} }
} }
impl<'a> PartialEq<SegmentRequests<'a>> for SegmentRequestIter<'_, u64> { fn compare_pairs<T: Into<u64> + Copy>(&self, pairs: &[(T, T)]) -> bool {
fn eq(&self, other: &SegmentRequests<'a>) -> bool {
match other {
SegmentRequests::U32Pairs(pairs) => {
if pairs.is_empty() && self.seq_req_raw.is_empty() { if pairs.is_empty() && self.seq_req_raw.is_empty() {
return true; return true;
} }
false if pairs.len() * 2 * self.increment() != self.seq_req_raw.len() {
}
SegmentRequests::U64Pairs(pairs) => self.compare_pairs(pairs),
}
}
}
impl<T> SegmentRequestIter<'_, T>
where
T: SegReqFromBytes + PartialEq,
{
fn compare_pairs(&self, pairs: &[(T, T)]) -> bool {
if pairs.is_empty() && self.seq_req_raw.is_empty() {
return true;
}
let size = size_of::<T>();
if pairs.len() * 2 * size != self.seq_req_raw.len() {
return false; return false;
} }
for (i, pair) in pairs.iter().enumerate() { for (i, pair) in pairs.iter().enumerate() {
let next_val = self.next_at_offset(i * 2 * size).unwrap(); let next_val = self.next_at_offset(i * 2 * self.increment()).unwrap();
if next_val != *pair { let pair = (pair.0.into(), pair.1.into());
if next_val != pair {
return false; return false;
} }
} }
@@ -334,17 +330,12 @@ where
} }
} }
impl<T: SegReqFromBytes> SegmentRequestIter<'_, T> { impl<'a> PartialEq<SegmentRequests<'a>> for SegmentRequestIter<'_> {
fn next_at_offset(&self, mut offset: usize) -> Option<(T, T)> { fn eq(&self, other: &SegmentRequests<'a>) -> bool {
if offset + size_of::<T>() * 2 > self.seq_req_raw.len() { match other {
return None; SegmentRequests::U32Pairs(pairs) => self.compare_pairs(pairs),
SegmentRequests::U64Pairs(pairs) => self.compare_pairs(pairs),
} }
let start_offset = T::from_bytes(&self.seq_req_raw[offset..offset + size_of::<T>()]);
offset += size_of::<T>();
let end_offset = T::from_bytes(&self.seq_req_raw[offset..offset + size_of::<T>()]);
Some((start_offset, end_offset))
} }
} }
@@ -443,33 +434,21 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
return 0; return 0;
} }
if self.file_flag() == LargeFileFlag::Normal { if self.file_flag() == LargeFileFlag::Normal {
self.seg_reqs_raw.len() / 8 self.seg_reqs_raw.len() / (2 * core::mem::size_of::<u32>())
} else { } else {
self.seg_reqs_raw.len() / 16 self.seg_reqs_raw.len() / (2 * core::mem::size_of::<u64>())
} }
} }
/// This function returns [None] if this NAK PDUs contains segment requests for a large file. /// Get a generic segment request iterator.
pub fn get_normal_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_, u32>> { pub fn get_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_>> {
if self.file_flag() == LargeFileFlag::Large { if self.seg_reqs_raw.is_empty() {
return None; return None;
} }
Some(SegmentRequestIter { Some(SegmentRequestIter {
seq_req_raw: self.seg_reqs_raw, seq_req_raw: self.seg_reqs_raw,
current_idx: 0, current_idx: 0,
phantom: PhantomData, large_file: self.file_flag(),
})
}
/// This function returns [None] if this NAK PDUs contains segment requests for a normal file.
pub fn get_large_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_, u64>> {
if self.file_flag() == LargeFileFlag::Normal {
return None;
}
Some(SegmentRequestIter {
seq_req_raw: self.seg_reqs_raw,
current_idx: 0,
phantom: PhantomData,
}) })
} }
} }
@@ -489,14 +468,8 @@ impl<'a> PartialEq<NakPduCreator<'a>> for NakPduReader<'_> {
(true, Some(seg_reqs)) => seg_reqs.is_empty(), (true, Some(seg_reqs)) => seg_reqs.is_empty(),
(false, None) => false, (false, None) => false,
_ => { _ => {
// Compare based on file_flag let normal_iter = self.get_segment_requests_iterator().unwrap();
if self.file_flag() == LargeFileFlag::Normal {
let normal_iter = self.get_normal_segment_requests_iterator().unwrap();
normal_iter == *other.segment_requests().unwrap() normal_iter == *other.segment_requests().unwrap()
} else {
let large_iter = self.get_large_segment_requests_iterator().unwrap();
large_iter == *other.segment_requests().unwrap()
}
} }
} }
} }
@@ -650,30 +623,23 @@ mod tests {
assert_eq!(nak_pdu_deser.start_of_scope(), 100); assert_eq!(nak_pdu_deser.start_of_scope(), 100);
assert_eq!(nak_pdu_deser.end_of_scope(), 300); assert_eq!(nak_pdu_deser.end_of_scope(), 300);
assert_eq!(nak_pdu_deser.num_segment_reqs(), 2); 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!( assert_eq!(
nak_pdu_deser nak_pdu_deser
.get_large_segment_requests_iterator() .get_segment_requests_iterator()
.unwrap() .unwrap()
.count(), .count(),
2 2
); );
for (idx, large_segments) in nak_pdu_deser let segment_iter = nak_pdu_deser.get_segment_requests_iterator();
.get_large_segment_requests_iterator() assert!(segment_iter.is_some());
.unwrap() let segment_iter = segment_iter.unwrap();
.enumerate() for (idx, segments) in segment_iter.enumerate() {
{
if idx == 0 { if idx == 0 {
assert_eq!(large_segments.0, 50); assert_eq!(segments.0, 50);
assert_eq!(large_segments.1, 100); assert_eq!(segments.1, 100);
} else { } else {
assert_eq!(large_segments.0, 200); assert_eq!(segments.0, 200);
assert_eq!(large_segments.1, 300); assert_eq!(segments.1, 300);
} }
} }
} }
@@ -693,30 +659,23 @@ mod tests {
assert_eq!(nak_pdu_deser.start_of_scope(), 100); assert_eq!(nak_pdu_deser.start_of_scope(), 100);
assert_eq!(nak_pdu_deser.end_of_scope(), 300); assert_eq!(nak_pdu_deser.end_of_scope(), 300);
assert_eq!(nak_pdu_deser.num_segment_reqs(), 2); 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!( assert_eq!(
nak_pdu_deser nak_pdu_deser
.get_normal_segment_requests_iterator() .get_segment_requests_iterator()
.unwrap() .unwrap()
.count(), .count(),
2 2
); );
for (idx, large_segments) in nak_pdu_deser let segment_iter = nak_pdu_deser.get_segment_requests_iterator();
.get_normal_segment_requests_iterator() assert!(segment_iter.is_some());
.unwrap() let segment_iter = segment_iter.unwrap();
.enumerate() for (idx, segments) in segment_iter.enumerate() {
{
if idx == 0 { if idx == 0 {
assert_eq!(large_segments.0, 50); assert_eq!(segments.0, 50);
assert_eq!(large_segments.1, 100); assert_eq!(segments.1, 100);
} else { } else {
assert_eq!(large_segments.0, 200); assert_eq!(segments.0, 200);
assert_eq!(large_segments.1, 300); assert_eq!(segments.1, 300);
} }
} }
} }