Skip to main content

leodos_protocols/transport/cfdp/pdu/file_directive/nak/
large.rs

1use crate::transport::cfdp::CfdpError;
2use crate::transport::cfdp::pdu::EntityId;
3use crate::transport::cfdp::pdu::Pdu;
4use crate::transport::cfdp::pdu::PduHeaderFixedPart;
5use crate::transport::cfdp::pdu::TransactionSeqNum;
6use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
7use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
8use crate::transport::cfdp::pdu::file_directive::nak::NakSegmentRequest;
9use crate::transport::cfdp::pdu::header::Direction;
10use crate::transport::cfdp::pdu::header::PduType;
11use crate::transport::cfdp::pdu::header::TransmissionMode;
12
13use bon::bon;
14use zerocopy::FromBytes;
15use zerocopy::Immutable;
16use zerocopy::IntoBytes;
17use zerocopy::KnownLayout;
18use zerocopy::Unaligned;
19use zerocopy::network_endian::U32;
20use zerocopy::network_endian::U64;
21
22/// A zero-copy representation of the data field of a NAK PDU for **large files**.
23///
24/// ```text
25/// +------------------------------------+-----------+--------------------------------------+
26/// | Field Name                         | Size      | Notes                                |
27/// +------------------------------------+-----------+--------------------------------------+
28/// | Start of scope                     | 64 bits   | FSS field.                           |
29/// | End of scope                       | 64 bits   | FSS field.                           |
30/// | -- Start of `rest` slice --------- | --------- | ------------------------------------ |
31/// | Segment Requests                   | Variable  | Zero or more `NakSegmentSmall`s.     |
32/// +------------------------------------+-----------+--------------------------------------+
33/// ```
34#[repr(C)]
35#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
36pub struct NakPduLarge {
37    /// The 64-bit start of scope offset.
38    start_of_scope: U64,
39    /// The 64-bit end of scope offset.
40    end_of_scope: U64,
41    /// Contains a sequence of zero or more `NakSegmentLarge` structs.
42    rest: [u8],
43}
44
45impl NakPduLarge {
46    /// Parses the `rest` slice into a slice of `NakSegmentLarge`s.
47    pub fn segment_requests(&self) -> Result<&[NakSegmentLarge], CfdpError> {
48        <[NakSegmentLarge]>::ref_from_bytes(&self.rest)
49            .map_err(|_| CfdpError::Custom("Invalid NAK segment requests"))
50    }
51
52    /// Get the start_of_scope field as a u64.
53    pub fn start_of_scope(&self) -> u64 {
54        self.start_of_scope.get()
55    }
56    /// Sets the start of scope field.
57    pub fn set_start_of_scope(&mut self, scope: u64) {
58        self.start_of_scope.set(scope);
59    }
60
61    /// Get the end_of_scope field as a u64.
62    pub fn end_of_scope(&self) -> u64 {
63        self.end_of_scope.get()
64    }
65    /// Sets the end of scope field.
66    pub fn set_end_of_scope(&mut self, scope: u64) {
67        self.end_of_scope.set(scope);
68    }
69
70    /// Get the raw rest field as a byte slice.
71    pub fn rest(&self) -> &[u8] {
72        &self.rest
73    }
74}
75
76/// A `zerocopy`-compatible struct representing a single missing segment in a NAK PDU
77/// for a **large file** transaction (64-bit offsets).
78#[repr(C)]
79#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
80pub struct NakSegmentLarge {
81    /// Start offset of the missing data segment.
82    start_offset: U64,
83    /// End offset of the missing data segment.
84    end_offset: U64,
85}
86
87impl NakSegmentLarge {
88    /// Get the start_offset field as a u64.
89    pub fn start_offset(&self) -> u64 {
90        self.start_offset.get()
91    }
92    /// Sets the start offset of the missing segment.
93    pub fn set_start_offset(&mut self, offset: u64) {
94        self.start_offset.set(offset);
95    }
96
97    /// Get the end_offset field as a u64.
98    pub fn end_offset(&self) -> u64 {
99        self.end_offset.get()
100    }
101    /// Sets the end offset of the missing segment.
102    pub fn set_end_offset(&mut self, offset: u64) {
103        self.end_offset.set(offset);
104    }
105}
106
107#[bon]
108impl NakPduLarge {
109    /// Builds a new large-file NAK PDU in the given buffer.
110    #[builder]
111    pub fn new<'a>(
112        buffer: &'a mut [u8],
113        source_entity_id: EntityId,
114        dest_entity_id: EntityId,
115        transaction_seq_num: TransactionSeqNum,
116        transmission_mode: TransmissionMode,
117        crc_flag: bool,
118        start_of_scope: u64,
119        end_of_scope: u64,
120        segment_requests: &'a [NakSegmentRequest],
121    ) -> Result<&'a mut Pdu, CfdpError> {
122        let fixed_part_len = size_of::<U32>() * 2;
123        let segments_len = segment_requests.len() * size_of::<NakSegmentLarge>();
124        let specific_data_len = fixed_part_len + segments_len;
125        let data_field_len = (1 + specific_data_len) as u16;
126
127        let header = PduHeaderFixedPart::builder()
128            .version(1)
129            .pdu_type(PduType::FileDirective)
130            .direction(Direction::TowardSender)
131            .tx_mode(transmission_mode)
132            .crc_flag(crc_flag)
133            .large_file_flag(true)
134            .data_field_len(data_field_len)
135            .seg_ctrl(false)
136            .seg_meta_flag(false)
137            .build()?;
138
139        let pdu = Pdu::builder()
140            .buffer(buffer)
141            .header_fixed(header)
142            .source_entity_id(source_entity_id)
143            .destination_entity_id(dest_entity_id)
144            .transaction_seq_num(transaction_seq_num)
145            .build()?;
146
147        let data_field = pdu.data_field_mut().or_else(|_| {
148            Err(CfdpError::Custom(
149                "Failed to get mutable data field for NAK PDU",
150            ))
151        })?;
152        let directive_pdu = FileDirectivePdu::mut_from_bytes(data_field).or_else(|_| {
153            Err(CfdpError::Custom(
154                "Failed to get mutable directive PDU for NAK PDU",
155            ))
156        })?;
157        directive_pdu.set_directive_code(DirectiveCode::Nak);
158
159        let nak_pdu =
160            NakPduLarge::mut_from_bytes_with_elems(&mut directive_pdu.rest, segments_len)
161                .or_else(|_| Err(CfdpError::Custom("Failed to build NAK PDU")))?;
162        nak_pdu.set_start_of_scope(start_of_scope);
163        nak_pdu.set_end_of_scope(end_of_scope);
164
165        let segments_slice =
166            <[NakSegmentLarge]>::mut_from_bytes(&mut nak_pdu.rest).or_else(|_| {
167                Err(CfdpError::Custom(
168                    "Failed to get mutable segment requests slice for NAK PDU",
169                ))
170            })?;
171        for (req, seg) in segment_requests.iter().zip(segments_slice.iter_mut()) {
172            seg.set_start_offset(req.start_offset);
173            seg.set_end_offset(req.end_offset);
174        }
175
176        Ok(pdu)
177    }
178}