Skip to main content

leodos_protocols/transport/cfdp/pdu/file_data/
without_meta.rs

1use crate::transport::cfdp::pdu::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::header::Direction;
7use crate::transport::cfdp::pdu::header::PduType;
8use crate::transport::cfdp::pdu::header::TransmissionMode;
9
10use bon::bon;
11use zerocopy::FromBytes;
12use zerocopy::Immutable;
13use zerocopy::IntoBytes;
14use zerocopy::KnownLayout;
15use zerocopy::Unaligned;
16use zerocopy::network_endian::U32;
17use zerocopy::network_endian::U64;
18
19/// A zero-copy representation of the data field of a File Data PDU that
20/// **does not** have segment metadata.
21///
22/// It consists of a `rest` slice containing the FSS `Offset` and the `file_data`.
23/// ```text
24/// +------------------------------------+----------------+--------------------------------------+
25/// | Field Name                         | Size           | Notes                                |
26/// +------------------------------------+----------------+--------------------------------------+
27/// | -- Start of PDU Data Field ------- | -------------- | ------------------------------------ |
28/// | Offset                             | 32 or 64 bits  | FSS field. Byte offset into the file.|
29/// |                                    | (FSS)          | Size depends on PDU Header's         |
30/// |                                    |                | `Large File Flag`.                   |
31/// | File data                          | Variable       | A chunk of the file's content.       |
32/// +------------------------------------+----------------+--------------------------------------+
33/// ```
34#[repr(C)]
35#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
36pub struct FileDataPduWithoutMeta {
37    /// Contains the FSS `Offset` followed by the file data.
38    rest: [u8],
39}
40
41impl FileDataPduWithoutMeta {
42    /// Parses the FSS `Offset` from the start of the `rest` slice.
43    pub fn offset(&self, large_file_flag: bool) -> Result<u64, CfdpError> {
44        if large_file_flag {
45            U64::ref_from_prefix(&self.rest)
46                .map(|(r, _)| r.get())
47                .map_err(|_| CfdpError::Custom("Invalid FSS Offset"))
48        } else {
49            U32::ref_from_prefix(&self.rest)
50                .map(|(r, _)| r.get() as u64)
51                .map_err(|_| CfdpError::Custom("Invalid FSS Offset"))
52        }
53    }
54    /// Sets the FSS byte offset into the file.
55    pub fn set_offset(&mut self, offset: u64, large_file_flag: bool) -> Result<(), CfdpError> {
56        if large_file_flag {
57            U64::mut_from_prefix(&mut self.rest)
58                .map(|(r, _)| r.set(offset))
59                .map_err(|_| CfdpError::Custom("Invalid FSS Offset"))
60        } else {
61            if offset > u32::MAX as u64 {
62                return Err(CfdpError::DataTooLarge {
63                    field: "offset",
64                    max: u32::MAX as usize,
65                });
66            }
67            U32::mut_from_prefix(&mut self.rest)
68                .map(|(r, _)| r.set(offset as u32))
69                .map_err(|_| CfdpError::Custom("Invalid FSS Offset"))
70        }
71    }
72
73    /// Returns the slice containing the actual file data.
74    pub fn file_data(&self, large_file_flag: bool) -> Result<&[u8], CfdpError> {
75        let offset_len = if large_file_flag { 8 } else { 4 };
76        self.rest
77            .get(offset_len..)
78            .ok_or_else(|| CfdpError::Custom("Invalid file data slice"))
79    }
80    /// Returns a mutable slice containing the actual file data.
81    pub fn file_data_mut(&mut self, large_file_flag: bool) -> Result<&mut [u8], CfdpError> {
82        let offset_len = if large_file_flag { 8 } else { 4 };
83        self.rest
84            .get_mut(offset_len..)
85            .ok_or_else(|| CfdpError::Custom("Invalid file data slice"))
86    }
87
88    /// Get the raw rest field as a byte slice.
89    pub fn rest(&self) -> &[u8] {
90        &self.rest
91    }
92}
93
94#[bon]
95impl FileDataPduWithoutMeta {
96    /// Builds a complete File Data PDU (without metadata).
97    /// The caller is responsible for writing the file data into the buffer after this function returns.
98    #[builder]
99    pub fn new<'a>(
100        buffer: &'a mut [u8],
101        // Transaction Context
102        source_entity_id: EntityId,
103        dest_entity_id: EntityId,
104        transaction_seq_num: TransactionSeqNum,
105        transmission_mode: TransmissionMode,
106        large_file_flag: bool,
107        crc_flag: bool,
108        // FileData Specific
109        segmentation_control: bool,
110        offset: u64,
111        file_data_len: usize,
112    ) -> Result<&'a mut Pdu, CfdpError> {
113        let offset_len = if large_file_flag { 8 } else { 4 };
114        let data_field_len = (offset_len + file_data_len) as u16;
115
116        let header = PduHeaderFixedPart::builder()
117            .version(1)
118            .pdu_type(PduType::FileData)
119            .direction(Direction::TowardReceiver)
120            .tx_mode(transmission_mode)
121            .crc_flag(crc_flag)
122            .large_file_flag(large_file_flag)
123            .data_field_len(data_field_len)
124            .seg_ctrl(segmentation_control)
125            .seg_meta_flag(false) // No metadata for this PDU type
126            .build()?;
127
128        let pdu = Pdu::builder()
129            .buffer(buffer)
130            .header_fixed(header)
131            .source_entity_id(source_entity_id)
132            .destination_entity_id(dest_entity_id)
133            .transaction_seq_num(transaction_seq_num)
134            .build()?;
135
136        // The header is complete. Now write the offset into the data field.
137        let data_field = pdu.data_field_mut()?;
138        let actual_data_field_len = data_field.len();
139        let fd_pdu = FileDataPduWithoutMeta::mut_from_bytes(data_field).map_err(|_| {
140            CfdpError::BufferTooSmall {
141                required: data_field_len as usize,
142                provided: actual_data_field_len,
143            }
144        })?;
145        fd_pdu.set_offset(offset, large_file_flag)?;
146
147        // The PDU is now fully constructed up to the point where file data should be written.
148        // The caller can get `pdu.data_field_mut()` and write into the slice starting at `offset_len`.
149        Ok(pdu)
150    }
151}