1
use crate::cfdp::pdu::{
2
    add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
3
    PduError, PduHeader,
4
};
5
use crate::cfdp::{CrcFlag, LargeFileFlag, PduType, SegmentMetadataFlag};
6
use crate::ByteConversionError;
7
use num_enum::{IntoPrimitive, TryFromPrimitive};
8
#[cfg(feature = "serde")]
9
use serde::{Deserialize, Serialize};
10

            
11
use super::{CfdpPdu, FileDirectiveType, WritablePduPacket};
12

            
13
4
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
14
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15
#[repr(u8)]
16
pub enum RecordContinuationState {
17
    NoStartNoEnd = 0b00,
18
    StartWithoutEnd = 0b01,
19
    EndWithoutStart = 0b10,
20
    StartAndEnd = 0b11,
21
}
22

            
23
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24
2
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25
pub struct SegmentMetadata<'seg_meta> {
26
    record_continuation_state: RecordContinuationState,
27
    metadata: Option<&'seg_meta [u8]>,
28
}
29

            
30
impl<'seg_meta> SegmentMetadata<'seg_meta> {
31
6
    pub fn new(
32
6
        record_continuation_state: RecordContinuationState,
33
6
        metadata: Option<&'seg_meta [u8]>,
34
6
    ) -> Option<Self> {
35
6
        if let Some(metadata) = metadata {
36
6
            if metadata.len() > 2_usize.pow(6) - 1 {
37
                return None;
38
6
            }
39
        }
40
6
        Some(Self {
41
6
            record_continuation_state,
42
6
            metadata,
43
6
        })
44
6
    }
45

            
46
    pub fn record_continuation_state(&self) -> RecordContinuationState {
47
        self.record_continuation_state
48
    }
49

            
50
    pub fn metadata(&self) -> Option<&'seg_meta [u8]> {
51
        self.metadata
52
    }
53

            
54
24
    pub fn written_len(&self) -> usize {
55
24
        // Map empty metadata to 0 and slice to its length.
56
36
        1 + self.metadata.map_or(0, |meta| meta.len())
57
24
    }
58

            
59
4
    pub(crate) fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
60
4
        if buf.len() < self.written_len() {
61
            return Err(ByteConversionError::ToSliceTooSmall {
62
                found: buf.len(),
63
                expected: self.written_len(),
64
            });
65
4
        }
66
4
        buf[0] = ((self.record_continuation_state as u8) << 6)
67
6
            | self.metadata.map_or(0, |meta| meta.len() as u8);
68
4
        if let Some(metadata) = self.metadata {
69
4
            buf[1..1 + metadata.len()].copy_from_slice(metadata)
70
        }
71
4
        Ok(self.written_len())
72
4
    }
73

            
74
2
    pub(crate) fn from_bytes(buf: &'seg_meta [u8]) -> Result<Self, ByteConversionError> {
75
2
        if buf.is_empty() {
76
            return Err(ByteConversionError::FromSliceTooSmall {
77
                found: buf.len(),
78
                expected: 2,
79
            });
80
2
        }
81
2
        let mut metadata = None;
82
2
        let seg_metadata_len = (buf[0] & 0b111111) as usize;
83
2
        if seg_metadata_len > 0 {
84
2
            metadata = Some(&buf[1..1 + seg_metadata_len]);
85
2
        }
86
2
        Ok(Self {
87
2
            // Can't fail, only 2 bits
88
2
            record_continuation_state: RecordContinuationState::try_from((buf[0] >> 6) & 0b11)
89
2
                .unwrap(),
90
2
            metadata,
91
2
        })
92
2
    }
93
}
94

            
95
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
96
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97
struct FdPduBase<'seg_meta> {
98
    pdu_header: PduHeader,
99
    #[cfg_attr(feature = "serde", serde(borrow))]
100
    segment_metadata: Option<SegmentMetadata<'seg_meta>>,
101
    offset: u64,
102
}
103

            
104
impl CfdpPdu for FdPduBase<'_> {
105
54
    fn pdu_header(&self) -> &PduHeader {
106
54
        &self.pdu_header
107
54
    }
108

            
109
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
110
        None
111
    }
112
}
113

            
114
impl FdPduBase<'_> {
115
54
    fn calc_pdu_datafield_len(&self, file_data_len: u64) -> usize {
116
54
        let mut len = core::mem::size_of::<u32>();
117
54
        if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
118
            len += 4;
119
54
        }
120
54
        if self.segment_metadata.is_some() {
121
14
            len += self.segment_metadata.as_ref().unwrap().written_len()
122
40
        }
123
54
        len += file_data_len as usize;
124
54
        if self.crc_flag() == CrcFlag::WithCrc {
125
14
            len += 2;
126
40
        }
127
54
        len
128
54
    }
129

            
130
20
    fn write_common_fields_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
131
20
        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
132
20
        if self.segment_metadata.is_some() {
133
4
            current_idx += self
134
4
                .segment_metadata
135
4
                .as_ref()
136
4
                .unwrap()
137
4
                .write_to_bytes(&mut buf[current_idx..])?;
138
16
        }
139
20
        current_idx += write_fss_field(
140
20
            self.pdu_header.common_pdu_conf().file_flag,
141
20
            self.offset,
142
20
            &mut buf[current_idx..],
143
20
        )?;
144
20
        Ok(current_idx)
145
20
    }
146
}
147

            
148
/// File Data PDU abstraction.
149
///
150
/// For more information, refer to CFDP chapter 5.3.
151
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
152
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
153
pub struct FileDataPdu<'seg_meta, 'file_data> {
154
    #[cfg_attr(feature = "serde", serde(borrow))]
155
    common: FdPduBase<'seg_meta>,
156
    file_data: &'file_data [u8],
157
}
158

            
159
impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
160
6
    pub fn new_with_seg_metadata(
161
6
        pdu_header: PduHeader,
162
6
        segment_metadata: SegmentMetadata<'seg_meta>,
163
6
        offset: u64,
164
6
        file_data: &'file_data [u8],
165
6
    ) -> Self {
166
6
        Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data)
167
6
    }
168

            
169
12
    pub fn new_no_seg_metadata(
170
12
        pdu_header: PduHeader,
171
12
        offset: u64,
172
12
        file_data: &'file_data [u8],
173
12
    ) -> Self {
174
12
        Self::new_generic(pdu_header, None, offset, file_data)
175
12
    }
176

            
177
18
    pub fn new_generic(
178
18
        mut pdu_header: PduHeader,
179
18
        segment_metadata: Option<SegmentMetadata<'seg_meta>>,
180
18
        offset: u64,
181
18
        file_data: &'file_data [u8],
182
18
    ) -> Self {
183
18
        pdu_header.pdu_type = PduType::FileData;
184
18
        if segment_metadata.is_some() {
185
6
            pdu_header.seg_metadata_flag = SegmentMetadataFlag::Present;
186
12
        }
187
18
        let mut pdu = Self {
188
18
            common: FdPduBase {
189
18
                pdu_header,
190
18
                segment_metadata,
191
18
                offset,
192
18
            },
193
18
            file_data,
194
18
        };
195
18
        pdu.common.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16;
196
18
        pdu
197
18
    }
198

            
199
42
    fn calc_pdu_datafield_len(&self) -> usize {
200
42
        self.common
201
42
            .calc_pdu_datafield_len(self.file_data.len() as u64)
202
42
    }
203

            
204
6
    pub fn segment_metadata(&self) -> Option<&SegmentMetadata> {
205
6
        self.common.segment_metadata.as_ref()
206
6
    }
207

            
208
2
    pub fn offset(&self) -> u64 {
209
2
        self.common.offset
210
2
    }
211

            
212
6
    pub fn file_data(&self) -> &'file_data [u8] {
213
6
        self.file_data
214
6
    }
215

            
216
14
    pub fn from_bytes<'buf: 'seg_meta + 'file_data>(buf: &'buf [u8]) -> Result<Self, PduError> {
217
14
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
218
14
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
219
10
        let min_expected_len = current_idx + core::mem::size_of::<u32>();
220
10
        generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
221
10
        let mut segment_metadata = None;
222
10
        if pdu_header.seg_metadata_flag == SegmentMetadataFlag::Present {
223
2
            segment_metadata = Some(SegmentMetadata::from_bytes(&buf[current_idx..])?);
224
2
            current_idx += segment_metadata.as_ref().unwrap().written_len();
225
8
        }
226
10
        let (fss, offset) = read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx..]);
227
10
        current_idx += fss;
228
10
        if current_idx > full_len_without_crc {
229
            return Err(ByteConversionError::FromSliceTooSmall {
230
                found: current_idx,
231
                expected: full_len_without_crc,
232
            }
233
            .into());
234
10
        }
235
10
        Ok(Self {
236
10
            common: FdPduBase {
237
10
                pdu_header,
238
10
                segment_metadata,
239
10
                offset,
240
10
            },
241
10
            file_data: &buf[current_idx..full_len_without_crc],
242
10
        })
243
14
    }
244
}
245
impl CfdpPdu for FileDataPdu<'_, '_> {
246
44
    fn pdu_header(&self) -> &PduHeader {
247
44
        &self.common.pdu_header
248
44
    }
249

            
250
2
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
251
2
        None
252
2
    }
253
}
254

            
255
impl WritablePduPacket for FileDataPdu<'_, '_> {
256
14
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
257
14
        if buf.len() < self.len_written() {
258
            return Err(ByteConversionError::ToSliceTooSmall {
259
                found: buf.len(),
260
                expected: self.len_written(),
261
            }
262
            .into());
263
14
        }
264

            
265
14
        let mut current_idx = self.common.write_common_fields_to_bytes(buf)?;
266
14
        buf[current_idx..current_idx + self.file_data.len()].copy_from_slice(self.file_data);
267
14
        current_idx += self.file_data.len();
268
14
        if self.crc_flag() == CrcFlag::WithCrc {
269
2
            current_idx = add_pdu_crc(buf, current_idx);
270
12
        }
271
14
        Ok(current_idx)
272
14
    }
273

            
274
24
    fn len_written(&self) -> usize {
275
24
        self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
276
24
    }
277
}
278

            
279
/// File Data PDU creator abstraction.
280
///
281
/// This special creator object allows to read into the file data buffer directly. This avoids
282
/// the need of an additional buffer to create a file data PDU. This structure therefore
283
/// does not implement the regular [WritablePduPacket] trait.
284
///
285
/// For more information, refer to CFDP chapter 5.3.
286
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
287
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
288
pub struct FileDataPduCreatorWithReservedDatafield<'seg_meta> {
289
    #[cfg_attr(feature = "serde", serde(borrow))]
290
    common: FdPduBase<'seg_meta>,
291
    file_data_len: u64,
292
}
293

            
294
impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> {
295
    pub fn new_with_seg_metadata(
296
        pdu_header: PduHeader,
297
        segment_metadata: SegmentMetadata<'seg_meta>,
298
        offset: u64,
299
        file_data_len: u64,
300
    ) -> Self {
301
        Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data_len)
302
    }
303

            
304
6
    pub fn new_no_seg_metadata(pdu_header: PduHeader, offset: u64, file_data_len: u64) -> Self {
305
6
        Self::new_generic(pdu_header, None, offset, file_data_len)
306
6
    }
307

            
308
6
    pub fn new_generic(
309
6
        mut pdu_header: PduHeader,
310
6
        segment_metadata: Option<SegmentMetadata<'seg_meta>>,
311
6
        offset: u64,
312
6
        file_data_len: u64,
313
6
    ) -> Self {
314
6
        pdu_header.pdu_type = PduType::FileData;
315
6
        if segment_metadata.is_some() {
316
            pdu_header.seg_metadata_flag = SegmentMetadataFlag::Present;
317
6
        }
318
6
        let mut pdu = Self {
319
6
            common: FdPduBase {
320
6
                pdu_header,
321
6
                segment_metadata,
322
6
                offset,
323
6
            },
324
6
            file_data_len,
325
6
        };
326
6
        pdu.common.pdu_header.pdu_datafield_len = pdu.calc_pdu_datafield_len() as u16;
327
6
        pdu
328
6
    }
329

            
330
12
    fn calc_pdu_datafield_len(&self) -> usize {
331
12
        self.common.calc_pdu_datafield_len(self.file_data_len)
332
12
    }
333

            
334
6
    pub fn len_written(&self) -> usize {
335
6
        self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
336
6
    }
337

            
338
    /// This function performs a partial write by writing all data except the file data
339
    /// and the CRC.
340
    ///
341
    /// It returns a [FileDataPduCreatorWithUnwrittenData] which provides a mutable slice to
342
    /// the reserved file data field. The user can read file data into this field directly and
343
    /// then finish the PDU creation using the [FileDataPduCreatorWithUnwrittenData::finish] call.
344
6
    pub fn write_to_bytes_partially<'buf>(
345
6
        &self,
346
6
        buf: &'buf mut [u8],
347
6
    ) -> Result<FileDataPduCreatorWithUnwrittenData<'buf>, PduError> {
348
6
        if buf.len() < self.len_written() {
349
            return Err(ByteConversionError::ToSliceTooSmall {
350
                found: buf.len(),
351
                expected: self.len_written(),
352
            }
353
            .into());
354
6
        }
355
6
        let mut current_idx = self.common.write_common_fields_to_bytes(buf)?;
356
6
        let file_data_offset = current_idx as u64;
357
6
        current_idx += self.file_data_len as usize;
358
6
        if self.crc_flag() == CrcFlag::WithCrc {
359
4
            current_idx += 2;
360
4
        }
361
6
        Ok(FileDataPduCreatorWithUnwrittenData {
362
6
            write_buf: &mut buf[0..current_idx],
363
6
            file_data_offset,
364
6
            file_data_len: self.file_data_len,
365
6
            needs_crc: self.crc_flag() == CrcFlag::WithCrc,
366
6
        })
367
6
    }
368
}
369

            
370
impl CfdpPdu for FileDataPduCreatorWithReservedDatafield<'_> {
371
12
    fn pdu_header(&self) -> &PduHeader {
372
12
        &self.common.pdu_header
373
12
    }
374

            
375
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
376
        None
377
    }
378
}
379

            
380
/// This structure is created with [FileDataPduCreatorReservedDatafield::write_to_bytes_partially]
381
/// and provides an API to read file data from the virtual filesystem into the file data PDU buffer
382
/// directly.
383
///
384
/// This structure provides a mutable slice to the reserved file data field. The user can read
385
/// file data into this field directly and then finish the PDU creation using the
386
/// [FileDataPduCreatorWithUnwrittenData::finish] call.
387
pub struct FileDataPduCreatorWithUnwrittenData<'buf> {
388
    write_buf: &'buf mut [u8],
389
    file_data_offset: u64,
390
    file_data_len: u64,
391
    needs_crc: bool,
392
}
393

            
394
impl FileDataPduCreatorWithUnwrittenData<'_> {
395
6
    pub fn file_data_field_mut(&mut self) -> &mut [u8] {
396
6
        &mut self.write_buf[self.file_data_offset as usize
397
6
            ..self.file_data_offset as usize + self.file_data_len as usize]
398
6
    }
399

            
400
    /// This functio needs to be called to add a CRC to the file data PDU where applicable.
401
    ///
402
    /// It returns the full written size of the PDU.
403
4
    pub fn finish(self) -> usize {
404
4
        if self.needs_crc {
405
2
            add_pdu_crc(
406
2
                self.write_buf,
407
2
                self.file_data_offset as usize + self.file_data_len as usize,
408
2
            );
409
2
        }
410
4
        self.write_buf.len()
411
4
    }
412
}
413

            
414
/// This function can be used to calculate the maximum allowed file segment size for
415
/// a given maximum packet length and the segment metadata if there is any.
416
10
pub fn calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(
417
10
    pdu_header: &PduHeader,
418
10
    max_packet_len: usize,
419
10
    segment_metadata: Option<&SegmentMetadata>,
420
10
) -> usize {
421
10
    let mut subtract = pdu_header.header_len();
422
10
    if segment_metadata.is_some() {
423
        subtract += 1 + segment_metadata.as_ref().unwrap().metadata().unwrap().len();
424
10
    }
425
10
    if pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
426
6
        subtract += 8;
427
6
    } else {
428
4
        subtract += 4;
429
4
    }
430
10
    if pdu_header.common_pdu_conf().crc_flag == CrcFlag::WithCrc {
431
2
        subtract += 2;
432
8
    }
433
10
    max_packet_len.saturating_sub(subtract)
434
10
}
435

            
436
#[cfg(test)]
437
mod tests {
438
    use super::*;
439
    use crate::cfdp::pdu::tests::{TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID};
440
    use crate::cfdp::pdu::{CommonPduConfig, PduHeader};
441
    use crate::cfdp::{Direction, SegmentMetadataFlag, SegmentationControl, TransmissionMode};
442
    #[cfg(feature = "serde")]
443
    use postcard::{from_bytes, to_allocvec};
444

            
445
    #[test]
446
2
    fn test_basic() {
447
2
        let common_conf =
448
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
449
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
450
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
451
2
        let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
452
2
        assert_eq!(fd_pdu.file_data(), file_data);
453
2
        assert_eq!(fd_pdu.offset(), 10);
454
2
        assert!(fd_pdu.segment_metadata().is_none());
455
2
        assert_eq!(
456
2
            fd_pdu.len_written(),
457
2
            fd_pdu.pdu_header().header_len() + core::mem::size_of::<u32>() + 4
458
2
        );
459

            
460
2
        assert_eq!(fd_pdu.crc_flag(), CrcFlag::NoCrc);
461
2
        assert_eq!(fd_pdu.file_flag(), LargeFileFlag::Normal);
462
2
        assert_eq!(fd_pdu.pdu_type(), PduType::FileData);
463
2
        assert_eq!(fd_pdu.file_directive_type(), None);
464
2
        assert_eq!(fd_pdu.transmission_mode(), TransmissionMode::Acknowledged);
465
2
        assert_eq!(fd_pdu.direction(), Direction::TowardsReceiver);
466
2
        assert_eq!(fd_pdu.source_id(), TEST_SRC_ID.into());
467
2
        assert_eq!(fd_pdu.dest_id(), TEST_DEST_ID.into());
468
2
        assert_eq!(fd_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
469
2
    }
470

            
471
    #[test]
472
2
    fn test_serialization() {
473
2
        let common_conf =
474
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
475
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
476
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
477
2
        let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
478
2
        let mut buf: [u8; 32] = [0; 32];
479
2
        let res = fd_pdu.write_to_bytes(&mut buf);
480
2
        assert!(res.is_ok());
481
2
        let written = res.unwrap();
482
2
        assert_eq!(
483
2
            written,
484
2
            fd_pdu.pdu_header().header_len() + core::mem::size_of::<u32>() + 4
485
2
        );
486
2
        let mut current_idx = fd_pdu.pdu_header().header_len();
487
2
        let file_size = u32::from_be_bytes(
488
2
            buf[fd_pdu.pdu_header().header_len()..fd_pdu.pdu_header().header_len() + 4]
489
2
                .try_into()
490
2
                .unwrap(),
491
2
        );
492
2
        current_idx += 4;
493
2
        assert_eq!(file_size, 10);
494
2
        assert_eq!(buf[current_idx], 1);
495
2
        current_idx += 1;
496
2
        assert_eq!(buf[current_idx], 2);
497
2
        current_idx += 1;
498
2
        assert_eq!(buf[current_idx], 3);
499
2
        current_idx += 1;
500
2
        assert_eq!(buf[current_idx], 4);
501
2
    }
502

            
503
    #[test]
504
2
    fn test_write_to_vec() {
505
2
        let common_conf =
506
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
507
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
508
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
509
2
        let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
510
2
        let mut buf: [u8; 64] = [0; 64];
511
2
        let written = fd_pdu.write_to_bytes(&mut buf).unwrap();
512
2
        let pdu_vec = fd_pdu.to_vec().unwrap();
513
2
        assert_eq!(buf[0..written], pdu_vec);
514
2
    }
515

            
516
    #[test]
517
2
    fn test_deserialization() {
518
2
        let common_conf =
519
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
520
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
521
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
522
2
        let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
523
2
        let mut buf: [u8; 32] = [0; 32];
524
2
        fd_pdu.write_to_bytes(&mut buf).unwrap();
525
2
        let fd_pdu_read_back = FileDataPdu::from_bytes(&buf);
526
2
        assert!(fd_pdu_read_back.is_ok());
527
2
        let fd_pdu_read_back = fd_pdu_read_back.unwrap();
528
2
        assert_eq!(fd_pdu_read_back, fd_pdu);
529
2
    }
530

            
531
    #[test]
532
2
    fn test_with_crc() {
533
2
        let mut common_conf =
534
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
535
2
        common_conf.crc_flag = true.into();
536
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
537
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
538
2
        let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
539
2
        let mut buf: [u8; 64] = [0; 64];
540
2
        let written = fd_pdu.write_to_bytes(&mut buf).unwrap();
541
2
        assert_eq!(written, fd_pdu.len_written());
542
2
        let finished_pdu_from_raw = FileDataPdu::from_bytes(&buf).unwrap();
543
2
        assert_eq!(finished_pdu_from_raw, fd_pdu);
544
2
        buf[written - 1] -= 1;
545
2
        let crc: u16 = ((buf[written - 2] as u16) << 8) | buf[written - 1] as u16;
546
2
        let error = FileDataPdu::from_bytes(&buf).unwrap_err();
547
2
        if let PduError::ChecksumError(e) = error {
548
2
            assert_eq!(e, crc);
549
        } else {
550
            panic!("expected crc error");
551
        }
552
2
    }
553

            
554
    #[test]
555
2
    fn test_with_seg_metadata_serialization() {
556
2
        let common_conf =
557
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
558
2
        let pdu_header = PduHeader::new_for_file_data(
559
2
            common_conf,
560
2
            0,
561
2
            SegmentMetadataFlag::Present,
562
2
            SegmentationControl::WithRecordBoundaryPreservation,
563
2
        );
564
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
565
2
        let seg_metadata: [u8; 4] = [4, 3, 2, 1];
566
2
        let segment_meta =
567
2
            SegmentMetadata::new(RecordContinuationState::StartAndEnd, Some(&seg_metadata))
568
2
                .unwrap();
569
2
        let fd_pdu = FileDataPdu::new_with_seg_metadata(pdu_header, segment_meta, 10, &file_data);
570
2
        assert!(fd_pdu.segment_metadata().is_some());
571
2
        assert_eq!(*fd_pdu.segment_metadata().unwrap(), segment_meta);
572
2
        assert_eq!(
573
2
            fd_pdu.len_written(),
574
2
            fd_pdu.pdu_header().header_len()
575
2
                + 1
576
2
                + seg_metadata.len()
577
2
                + core::mem::size_of::<u32>()
578
2
                + 4
579
2
        );
580
2
        let mut buf: [u8; 32] = [0; 32];
581
2
        fd_pdu
582
2
            .write_to_bytes(&mut buf)
583
2
            .expect("writing FD PDU failed");
584
2
        let mut current_idx = fd_pdu.pdu_header().header_len();
585
2
        assert_eq!(
586
2
            RecordContinuationState::try_from((buf[current_idx] >> 6) & 0b11).unwrap(),
587
2
            RecordContinuationState::StartAndEnd
588
2
        );
589
2
        assert_eq!((buf[current_idx] & 0b111111) as usize, seg_metadata.len());
590
2
        current_idx += 1;
591
2
        assert_eq!(buf[current_idx], 4);
592
2
        current_idx += 1;
593
2
        assert_eq!(buf[current_idx], 3);
594
2
        current_idx += 1;
595
2
        assert_eq!(buf[current_idx], 2);
596
2
        current_idx += 1;
597
2
        assert_eq!(buf[current_idx], 1);
598
2
        current_idx += 1;
599
2
        // Still verify that the rest is written correctly.
600
2
        assert_eq!(
601
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
602
2
            10
603
2
        );
604
2
        current_idx += 4;
605
2
        assert_eq!(buf[current_idx], 1);
606
2
        current_idx += 1;
607
2
        assert_eq!(buf[current_idx], 2);
608
2
        current_idx += 1;
609
2
        assert_eq!(buf[current_idx], 3);
610
2
        current_idx += 1;
611
2
        assert_eq!(buf[current_idx], 4);
612
2
        current_idx += 1;
613
2
        assert_eq!(current_idx, fd_pdu.len_written());
614
2
    }
615

            
616
    #[test]
617
2
    fn test_with_seg_metadata_deserialization() {
618
2
        let common_conf =
619
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
620
2
        let pdu_header = PduHeader::new_for_file_data(
621
2
            common_conf,
622
2
            0,
623
2
            SegmentMetadataFlag::Present,
624
2
            SegmentationControl::WithRecordBoundaryPreservation,
625
2
        );
626
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
627
2
        let seg_metadata: [u8; 4] = [4, 3, 2, 1];
628
2
        let segment_meta =
629
2
            SegmentMetadata::new(RecordContinuationState::StartAndEnd, Some(&seg_metadata))
630
2
                .unwrap();
631
2
        let fd_pdu = FileDataPdu::new_with_seg_metadata(pdu_header, segment_meta, 10, &file_data);
632
2
        let mut buf: [u8; 32] = [0; 32];
633
2
        fd_pdu
634
2
            .write_to_bytes(&mut buf)
635
2
            .expect("writing FD PDU failed");
636
2
        let fd_pdu_read_back = FileDataPdu::from_bytes(&buf);
637
2
        assert!(fd_pdu_read_back.is_ok());
638
2
        let fd_pdu_read_back = fd_pdu_read_back.unwrap();
639
2
        assert_eq!(fd_pdu_read_back, fd_pdu);
640
2
    }
641

            
642
    #[test]
643
    #[cfg(feature = "serde")]
644
2
    fn test_serde_serialization() {
645
2
        let common_conf =
646
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
647
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
648
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
649
2
        let fd_pdu = FileDataPdu::new_no_seg_metadata(pdu_header, 10, &file_data);
650
2
        let output = to_allocvec(&fd_pdu).unwrap();
651
2
        let output_converted_back: FileDataPdu = from_bytes(&output).unwrap();
652
2
        assert_eq!(output_converted_back, fd_pdu);
653
2
    }
654

            
655
    #[test]
656
    #[cfg(feature = "serde")]
657
2
    fn test_serde_serialization_with_seg_metadata() {
658
2
        let common_conf =
659
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
660
2
        let pdu_header = PduHeader::new_for_file_data(
661
2
            common_conf,
662
2
            0,
663
2
            SegmentMetadataFlag::Present,
664
2
            SegmentationControl::WithRecordBoundaryPreservation,
665
2
        );
666
2
        let file_data: [u8; 4] = [1, 2, 3, 4];
667
2
        let seg_metadata: [u8; 4] = [4, 3, 2, 1];
668
2
        let segment_meta =
669
2
            SegmentMetadata::new(RecordContinuationState::StartAndEnd, Some(&seg_metadata))
670
2
                .unwrap();
671
2
        let fd_pdu = FileDataPdu::new_with_seg_metadata(pdu_header, segment_meta, 10, &file_data);
672
2
        let output = to_allocvec(&fd_pdu).unwrap();
673
2
        let output_converted_back: FileDataPdu = from_bytes(&output).unwrap();
674
2
        assert_eq!(output_converted_back, fd_pdu);
675
2
    }
676

            
677
    #[test]
678
2
    fn test_fd_pdu_creator_with_reserved_field_no_crc() {
679
2
        let common_conf =
680
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
681
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
682
2
        let test_str = "hello world!";
683
2
        let fd_pdu = FileDataPduCreatorWithReservedDatafield::new_no_seg_metadata(
684
2
            pdu_header,
685
2
            10,
686
2
            test_str.len() as u64,
687
2
        );
688
2
        let mut write_buf: [u8; 64] = [0; 64];
689
2
        let mut pdu_unwritten = fd_pdu
690
2
            .write_to_bytes_partially(&mut write_buf)
691
2
            .expect("partial write failed");
692
2
        pdu_unwritten
693
2
            .file_data_field_mut()
694
2
            .copy_from_slice(test_str.as_bytes());
695
2
        pdu_unwritten.finish();
696
2

            
697
2
        let pdu_reader = FileDataPdu::from_bytes(&write_buf).expect("reading file data PDU failed");
698
2
        assert_eq!(
699
2
            core::str::from_utf8(pdu_reader.file_data()).expect("reading utf8 string failed"),
700
2
            "hello world!"
701
2
        );
702
2
    }
703

            
704
    #[test]
705
2
    fn test_fd_pdu_creator_with_reserved_field_with_crc() {
706
2
        let mut common_conf =
707
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
708
2
        common_conf.crc_flag = true.into();
709
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
710
2
        let test_str = "hello world!";
711
2
        let fd_pdu = FileDataPduCreatorWithReservedDatafield::new_no_seg_metadata(
712
2
            pdu_header,
713
2
            10,
714
2
            test_str.len() as u64,
715
2
        );
716
2
        let mut write_buf: [u8; 64] = [0; 64];
717
2
        let mut pdu_unwritten = fd_pdu
718
2
            .write_to_bytes_partially(&mut write_buf)
719
2
            .expect("partial write failed");
720
2
        pdu_unwritten
721
2
            .file_data_field_mut()
722
2
            .copy_from_slice(test_str.as_bytes());
723
2
        pdu_unwritten.finish();
724
2

            
725
2
        let pdu_reader = FileDataPdu::from_bytes(&write_buf).expect("reading file data PDU failed");
726
2
        assert_eq!(
727
2
            core::str::from_utf8(pdu_reader.file_data()).expect("reading utf8 string failed"),
728
2
            "hello world!"
729
2
        );
730
2
    }
731

            
732
    #[test]
733
2
    fn test_fd_pdu_creator_with_reserved_field_with_crc_without_finish_fails() {
734
2
        let mut common_conf =
735
2
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM).unwrap();
736
2
        common_conf.crc_flag = true.into();
737
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
738
2
        let test_str = "hello world!";
739
2
        let fd_pdu = FileDataPduCreatorWithReservedDatafield::new_no_seg_metadata(
740
2
            pdu_header,
741
2
            10,
742
2
            test_str.len() as u64,
743
2
        );
744
2
        let mut write_buf: [u8; 64] = [0; 64];
745
2
        let mut pdu_unwritten = fd_pdu
746
2
            .write_to_bytes_partially(&mut write_buf)
747
2
            .expect("partial write failed");
748
2
        pdu_unwritten
749
2
            .file_data_field_mut()
750
2
            .copy_from_slice(test_str.as_bytes());
751
2

            
752
2
        let pdu_reader_error = FileDataPdu::from_bytes(&write_buf);
753
2
        assert!(pdu_reader_error.is_err());
754
2
        let error = pdu_reader_error.unwrap_err();
755
2
        match error {
756
2
            PduError::ChecksumError(_) => (),
757
            _ => {
758
                panic!("unexpected PDU error {}", error)
759
            }
760
        }
761
2
    }
762

            
763
    #[test]
764
2
    fn test_max_file_seg_calculator_0() {
765
2
        let pdu_header = PduHeader::new_for_file_data_default(CommonPduConfig::default(), 0);
766
2
        assert_eq!(
767
2
            calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(&pdu_header, 64, None),
768
2
            53
769
2
        );
770
2
    }
771

            
772
    #[test]
773
2
    fn test_max_file_seg_calculator_1() {
774
2
        let common_conf = CommonPduConfig {
775
2
            crc_flag: CrcFlag::WithCrc,
776
2
            ..Default::default()
777
2
        };
778
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
779
2
        assert_eq!(
780
2
            calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(&pdu_header, 64, None),
781
2
            51
782
2
        );
783
2
    }
784

            
785
    #[test]
786
2
    fn test_max_file_seg_calculator_2() {
787
2
        let common_conf = CommonPduConfig {
788
2
            file_flag: LargeFileFlag::Large,
789
2
            ..Default::default()
790
2
        };
791
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
792
2
        assert_eq!(
793
2
            calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(&pdu_header, 64, None),
794
2
            49
795
2
        );
796
2
    }
797

            
798
    #[test]
799
2
    fn test_max_file_seg_calculator_saturating_sub() {
800
2
        let common_conf = CommonPduConfig {
801
2
            file_flag: LargeFileFlag::Large,
802
2
            ..Default::default()
803
2
        };
804
2
        let pdu_header = PduHeader::new_for_file_data_default(common_conf, 0);
805
2
        assert_eq!(
806
2
            calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(&pdu_header, 15, None),
807
2
            0
808
2
        );
809
2
        assert_eq!(
810
2
            calculate_max_file_seg_len_for_max_packet_len_and_pdu_header(&pdu_header, 14, None),
811
2
            0
812
2
        );
813
2
    }
814
}