1
//! CFDP Packet Data Unit (PDU) support.
2
use crate::cfdp::*;
3
use crate::util::{UnsignedByteField, UnsignedByteFieldU8, UnsignedEnum};
4
use crate::ByteConversionError;
5
use crate::CRC_CCITT_FALSE;
6
#[cfg(feature = "alloc")]
7
use alloc::vec::Vec;
8
use core::fmt::{Display, Formatter};
9
#[cfg(feature = "std")]
10
use std::error::Error;
11

            
12
pub mod ack;
13
pub mod eof;
14
pub mod file_data;
15
pub mod finished;
16
pub mod metadata;
17
pub mod nak;
18

            
19
48
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
20
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22
#[repr(u8)]
23
pub enum FileDirectiveType {
24
    EofPdu = 0x04,
25
    FinishedPdu = 0x05,
26
    AckPdu = 0x06,
27
    MetadataPdu = 0x07,
28
    NakPdu = 0x08,
29
    PromptPdu = 0x09,
30
    KeepAlivePdu = 0x0c,
31
}
32

            
33
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35
pub enum PduError {
36
    ByteConversion(ByteConversionError),
37
    /// Found version ID invalid, not equal to [CFDP_VERSION_2].
38
    CfdpVersionMissmatch(u8),
39
    /// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
40
    InvalidEntityLen(u8),
41
    /// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
42
    InvalidTransactionSeqNumLen(u8),
43
    SourceDestIdLenMissmatch {
44
        src_id_len: usize,
45
        dest_id_len: usize,
46
    },
47
    /// Wrong directive type, for example when parsing the directive field for a file directive
48
    /// PDU.
49
    WrongDirectiveType {
50
        found: FileDirectiveType,
51
        expected: FileDirectiveType,
52
    },
53
    /// The directive type field contained a value not in the range of permitted values. This can
54
    /// also happen if an invalid value is passed to the ACK PDU constructor.
55
    InvalidDirectiveType {
56
        found: u8,
57
        expected: Option<FileDirectiveType>,
58
    },
59
    InvalidStartOrEndOfScopeValue,
60
    /// Invalid condition code. Contains the raw detected value.
61
    InvalidConditionCode(u8),
62
    /// Invalid checksum type which is not part of the checksums listed in the
63
    /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/).
64
    InvalidChecksumType(u8),
65
    FileSizeTooLarge(u64),
66
    /// If the CRC flag for a PDU is enabled and the checksum check fails. Contains raw 16-bit CRC.
67
    ChecksumError(u16),
68
    /// Generic error for invalid PDU formats.
69
    FormatError,
70
    /// Error handling a TLV field.
71
    TlvLvError(TlvLvError),
72
}
73

            
74
impl Display for PduError {
75
14
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
76
14
        match self {
77
2
            PduError::InvalidEntityLen(raw_id) => {
78
2
                write!(
79
2
                    f,
80
2
                    "invalid PDU entity ID length {raw_id}, only [1, 2, 4, 8] are allowed"
81
2
                )
82
            }
83
            PduError::InvalidStartOrEndOfScopeValue => {
84
2
                write!(f, "invalid start or end of scope for NAK PDU")
85
            }
86
            PduError::InvalidTransactionSeqNumLen(raw_id) => {
87
                write!(
88
                    f,
89
                    "invalid PDUtransaction seq num length {raw_id}, only [1, 2, 4, 8] are allowed"
90
                )
91
            }
92
2
            PduError::CfdpVersionMissmatch(raw) => {
93
2
                write!(
94
2
                    f,
95
2
                    "cfdp version missmatch, found {raw}, expected {CFDP_VERSION_2}"
96
2
                )
97
            }
98
            PduError::SourceDestIdLenMissmatch {
99
2
                src_id_len,
100
2
                dest_id_len,
101
2
            } => {
102
2
                write!(
103
2
                    f,
104
2
                    "missmatch of PDU source length {src_id_len} and destination length {dest_id_len}"
105
2
                )
106
            }
107
2
            PduError::ByteConversion(e) => {
108
2
                write!(f, "{}", e)
109
            }
110
            PduError::FileSizeTooLarge(value) => {
111
                write!(f, "file size value {value} exceeds allowed 32 bit width")
112
            }
113
2
            PduError::WrongDirectiveType { found, expected } => {
114
2
                write!(f, "found directive type {found:?}, expected {expected:?}")
115
            }
116
            PduError::InvalidConditionCode(raw_code) => {
117
                write!(f, "found invalid condition code with raw value {raw_code}")
118
            }
119
2
            PduError::InvalidDirectiveType { found, expected } => {
120
2
                write!(
121
2
                    f,
122
2
                    "invalid directive type value {found}, expected {expected:?}"
123
2
                )
124
            }
125
            PduError::InvalidChecksumType(checksum_type) => {
126
                write!(f, "invalid checksum type {checksum_type}")
127
            }
128
            PduError::ChecksumError(checksum) => {
129
                write!(f, "checksum error for CRC {checksum:#04x}")
130
            }
131
            PduError::TlvLvError(error) => {
132
                write!(f, "pdu tlv error: {error}")
133
            }
134
            PduError::FormatError => {
135
                write!(f, "generic PDU format error")
136
            }
137
        }
138
14
    }
139
}
140

            
141
#[cfg(feature = "std")]
142
impl Error for PduError {
143
    fn source(&self) -> Option<&(dyn Error + 'static)> {
144
        match self {
145
            PduError::ByteConversion(e) => Some(e),
146
            PduError::TlvLvError(e) => Some(e),
147
            _ => None,
148
        }
149
    }
150
}
151

            
152
impl From<ByteConversionError> for PduError {
153
    #[inline]
154
6
    fn from(value: ByteConversionError) -> Self {
155
6
        Self::ByteConversion(value)
156
6
    }
157
}
158

            
159
impl From<TlvLvError> for PduError {
160
    #[inline]
161
    fn from(e: TlvLvError) -> Self {
162
        Self::TlvLvError(e)
163
    }
164
}
165

            
166
pub trait WritablePduPacket {
167
    fn len_written(&self) -> usize;
168
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError>;
169
    #[cfg(feature = "alloc")]
170
30
    fn to_vec(&self) -> Result<Vec<u8>, PduError> {
171
30
        // This is the correct way to do this. See
172
30
        // [this issue](https://github.com/rust-lang/rust-clippy/issues/4483) for caveats of more
173
30
        // "efficient" implementations.
174
30
        let mut vec = alloc::vec![0; self.len_written()];
175
30
        self.write_to_bytes(&mut vec)?;
176
30
        Ok(vec)
177
30
    }
178
}
179

            
180
/// Abstraction trait for fields and properties common for all PDUs.
181
pub trait CfdpPdu {
182
    fn pdu_header(&self) -> &PduHeader;
183

            
184
    #[inline]
185
22
    fn source_id(&self) -> UnsignedByteField {
186
22
        self.pdu_header().common_pdu_conf().source_entity_id
187
22
    }
188

            
189
    #[inline]
190
22
    fn dest_id(&self) -> UnsignedByteField {
191
22
        self.pdu_header().common_pdu_conf().dest_entity_id
192
22
    }
193

            
194
    #[inline]
195
22
    fn transaction_seq_num(&self) -> UnsignedByteField {
196
22
        self.pdu_header().common_pdu_conf().transaction_seq_num
197
22
    }
198

            
199
    #[inline]
200
22
    fn transmission_mode(&self) -> TransmissionMode {
201
22
        self.pdu_header().common_pdu_conf().trans_mode
202
22
    }
203

            
204
    #[inline]
205
22
    fn direction(&self) -> Direction {
206
22
        self.pdu_header().common_pdu_conf().direction
207
22
    }
208

            
209
    #[inline]
210
442
    fn crc_flag(&self) -> CrcFlag {
211
442
        self.pdu_header().common_pdu_conf().crc_flag
212
442
    }
213

            
214
    #[inline]
215
102
    fn file_flag(&self) -> LargeFileFlag {
216
102
        self.pdu_header().common_pdu_conf().file_flag
217
102
    }
218

            
219
    #[inline]
220
22
    fn pdu_type(&self) -> PduType {
221
22
        self.pdu_header().pdu_type()
222
22
    }
223

            
224
    fn file_directive_type(&self) -> Option<FileDirectiveType>;
225
}
226

            
227
/// Common configuration fields for a PDU.
228
///
229
/// Please note that this structure has a custom implementation of [PartialEq] which only
230
/// compares the values for source entity ID, destination entity ID and transaction sequence
231
/// number. This permits that those fields can have different widths, as long as the value is the
232
/// same.
233
#[derive(Debug, Copy, Clone, Eq)]
234
8
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
235
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
236
pub struct CommonPduConfig {
237
    source_entity_id: UnsignedByteField,
238
    dest_entity_id: UnsignedByteField,
239
    pub transaction_seq_num: UnsignedByteField,
240
    pub trans_mode: TransmissionMode,
241
    pub file_flag: LargeFileFlag,
242
    pub crc_flag: CrcFlag,
243
    pub direction: Direction,
244
}
245

            
246
// TODO: Builder pattern might be applicable here..
247
impl CommonPduConfig {
248
    #[inline]
249
234
    pub fn new(
250
234
        source_id: impl Into<UnsignedByteField>,
251
234
        dest_id: impl Into<UnsignedByteField>,
252
234
        transaction_seq_num: impl Into<UnsignedByteField>,
253
234
        trans_mode: TransmissionMode,
254
234
        file_flag: LargeFileFlag,
255
234
        crc_flag: CrcFlag,
256
234
        direction: Direction,
257
234
    ) -> Result<Self, PduError> {
258
234
        let (source_id, dest_id) = Self::source_dest_id_check(source_id, dest_id)?;
259
230
        let transaction_seq_num = transaction_seq_num.into();
260
230
        if transaction_seq_num.size() != 1
261
10
            && transaction_seq_num.size() != 2
262
2
            && transaction_seq_num.size() != 4
263
2
            && transaction_seq_num.size() != 8
264
        {
265
2
            return Err(PduError::InvalidTransactionSeqNumLen(
266
2
                transaction_seq_num.size() as u8,
267
2
            ));
268
228
        }
269
228
        Ok(Self {
270
228
            source_entity_id: source_id,
271
228
            dest_entity_id: dest_id,
272
228
            transaction_seq_num,
273
228
            trans_mode,
274
228
            file_flag,
275
228
            crc_flag,
276
228
            direction,
277
228
        })
278
234
    }
279

            
280
    #[inline]
281
156
    pub fn new_with_byte_fields(
282
156
        source_id: impl Into<UnsignedByteField>,
283
156
        dest_id: impl Into<UnsignedByteField>,
284
156
        transaction_seq_num: impl Into<UnsignedByteField>,
285
156
    ) -> Result<Self, PduError> {
286
156
        Self::new(
287
156
            source_id,
288
156
            dest_id,
289
156
            transaction_seq_num,
290
156
            TransmissionMode::Acknowledged,
291
156
            LargeFileFlag::Normal,
292
156
            CrcFlag::NoCrc,
293
156
            Direction::TowardsReceiver,
294
156
        )
295
156
    }
296

            
297
    #[inline]
298
4
    pub fn source_id(&self) -> UnsignedByteField {
299
4
        self.source_entity_id
300
4
    }
301

            
302
    #[inline]
303
236
    fn source_dest_id_check(
304
236
        source_id: impl Into<UnsignedByteField>,
305
236
        dest_id: impl Into<UnsignedByteField>,
306
236
    ) -> Result<(UnsignedByteField, UnsignedByteField), PduError> {
307
236
        let source_id = source_id.into();
308
236
        let dest_id = dest_id.into();
309
236
        if source_id.size() != dest_id.size() {
310
2
            return Err(PduError::SourceDestIdLenMissmatch {
311
2
                src_id_len: source_id.size(),
312
2
                dest_id_len: dest_id.size(),
313
2
            });
314
234
        }
315
234
        if source_id.size() != 1
316
12
            && source_id.size() != 2
317
2
            && source_id.size() != 4
318
2
            && source_id.size() != 8
319
        {
320
2
            return Err(PduError::InvalidEntityLen(source_id.size() as u8));
321
232
        }
322
232
        Ok((source_id, dest_id))
323
236
    }
324

            
325
    #[inline]
326
2
    pub fn set_source_and_dest_id(
327
2
        &mut self,
328
2
        source_id: impl Into<UnsignedByteField>,
329
2
        dest_id: impl Into<UnsignedByteField>,
330
2
    ) -> Result<(), PduError> {
331
2
        let (source_id, dest_id) = Self::source_dest_id_check(source_id, dest_id)?;
332
2
        self.source_entity_id = source_id;
333
2
        self.dest_entity_id = dest_id;
334
2
        Ok(())
335
2
    }
336

            
337
    #[inline]
338
4
    pub fn dest_id(&self) -> UnsignedByteField {
339
4
        self.dest_entity_id
340
4
    }
341
}
342

            
343
impl Default for CommonPduConfig {
344
    /// The defaults for the source ID, destination ID and the transaction sequence number is the
345
    /// [UnsignedByteFieldU8] with an intitial value of 0
346
    #[inline]
347
10
    fn default() -> Self {
348
10
        // The new function can not fail for these input parameters.
349
10
        Self::new(
350
10
            UnsignedByteFieldU8::new(0),
351
10
            UnsignedByteFieldU8::new(0),
352
10
            UnsignedByteFieldU8::new(0),
353
10
            TransmissionMode::Acknowledged,
354
10
            LargeFileFlag::Normal,
355
10
            CrcFlag::NoCrc,
356
10
            Direction::TowardsReceiver,
357
10
        )
358
10
        .unwrap()
359
10
    }
360
}
361

            
362
impl PartialEq for CommonPduConfig {
363
    #[inline]
364
58
    fn eq(&self, other: &Self) -> bool {
365
58
        self.source_entity_id.value() == other.source_entity_id.value()
366
58
            && self.dest_entity_id.value() == other.dest_entity_id.value()
367
58
            && self.transaction_seq_num.value() == other.transaction_seq_num.value()
368
58
            && self.trans_mode == other.trans_mode
369
58
            && self.file_flag == other.file_flag
370
58
            && self.crc_flag == other.crc_flag
371
58
            && self.direction == other.direction
372
58
    }
373
}
374

            
375
pub const FIXED_HEADER_LEN: usize = 4;
376

            
377
/// Abstraction for the PDU header common to all CFDP PDUs.
378
///
379
/// For detailed information, refer to chapter 5.1 of the CFDP standard.
380
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
381
8
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
382
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
383
pub struct PduHeader {
384
    pdu_type: PduType,
385
    pdu_conf: CommonPduConfig,
386
    seg_metadata_flag: SegmentMetadataFlag,
387
    seg_ctrl: SegmentationControl,
388
    pdu_datafield_len: u16,
389
}
390

            
391
impl PduHeader {
392
    #[inline]
393
12
    pub fn new_for_file_data(
394
12
        pdu_conf: CommonPduConfig,
395
12
        pdu_datafield_len: u16,
396
12
        seg_metadata_flag: SegmentMetadataFlag,
397
12
        seg_ctrl: SegmentationControl,
398
12
    ) -> Self {
399
12
        Self::new_generic(
400
12
            PduType::FileData,
401
12
            pdu_conf,
402
12
            pdu_datafield_len,
403
12
            seg_metadata_flag,
404
12
            seg_ctrl,
405
12
        )
406
12
    }
407

            
408
    #[inline]
409
26
    pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
410
26
        Self::new_generic(
411
26
            PduType::FileData,
412
26
            pdu_conf,
413
26
            pdu_datafield_len,
414
26
            SegmentMetadataFlag::NotPresent,
415
26
            SegmentationControl::NoRecordBoundaryPreservation,
416
26
        )
417
26
    }
418
    #[inline]
419
112
    pub fn new_no_file_data(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
420
112
        Self::new_generic(
421
112
            PduType::FileDirective,
422
112
            pdu_conf,
423
112
            pdu_datafield_len,
424
112
            SegmentMetadataFlag::NotPresent,
425
112
            SegmentationControl::NoRecordBoundaryPreservation,
426
112
        )
427
112
    }
428

            
429
    #[inline]
430
150
    pub fn new_generic(
431
150
        pdu_type: PduType,
432
150
        pdu_conf: CommonPduConfig,
433
150
        pdu_datafield_len: u16,
434
150
        seg_metadata_flag: SegmentMetadataFlag,
435
150
        seg_ctrl: SegmentationControl,
436
150
    ) -> Self {
437
150
        Self {
438
150
            pdu_type,
439
150
            pdu_conf,
440
150
            seg_metadata_flag,
441
150
            seg_ctrl,
442
150
            pdu_datafield_len,
443
150
        }
444
150
    }
445

            
446
    /// Returns only the length of the PDU header when written to a raw buffer.
447
    #[inline]
448
420
    pub fn header_len(&self) -> usize {
449
420
        FIXED_HEADER_LEN
450
420
            + self.pdu_conf.source_entity_id.size()
451
420
            + self.pdu_conf.transaction_seq_num.size()
452
420
            + self.pdu_conf.dest_entity_id.size()
453
420
    }
454

            
455
    #[inline]
456
2
    pub fn pdu_datafield_len(&self) -> usize {
457
2
        self.pdu_datafield_len.into()
458
2
    }
459

            
460
    /// Returns the full length of the PDU when written to a raw buffer, which is the header length
461
    /// plus the PDU datafield length.
462
    #[inline]
463
164
    pub fn pdu_len(&self) -> usize {
464
164
        self.header_len() + self.pdu_datafield_len as usize
465
164
    }
466

            
467
114
    pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
468
114
        // Internal note: There is currently no way to pass a PDU configuration like this, but
469
114
        // this check is still kept for defensive programming.
470
114
        if self.pdu_conf.source_entity_id.size() != self.pdu_conf.dest_entity_id.size() {
471
            return Err(PduError::SourceDestIdLenMissmatch {
472
                src_id_len: self.pdu_conf.source_entity_id.size(),
473
                dest_id_len: self.pdu_conf.dest_entity_id.size(),
474
            });
475
114
        }
476
114
        if buf.len()
477
114
            < FIXED_HEADER_LEN
478
114
                + self.pdu_conf.source_entity_id.size()
479
114
                + self.pdu_conf.transaction_seq_num.size()
480
        {
481
            return Err(ByteConversionError::ToSliceTooSmall {
482
                found: buf.len(),
483
                expected: FIXED_HEADER_LEN,
484
            }
485
            .into());
486
114
        }
487
114
        let mut current_idx = 0;
488
114
        buf[current_idx] = (CFDP_VERSION_2 << 5)
489
114
            | ((self.pdu_type as u8) << 4)
490
114
            | ((self.pdu_conf.direction as u8) << 3)
491
114
            | ((self.pdu_conf.trans_mode as u8) << 2)
492
114
            | ((self.pdu_conf.crc_flag as u8) << 1)
493
114
            | (self.pdu_conf.file_flag as u8);
494
114
        current_idx += 1;
495
114
        buf[current_idx..current_idx + 2].copy_from_slice(&self.pdu_datafield_len.to_be_bytes());
496
114
        current_idx += 2;
497
114
        buf[current_idx] = ((self.seg_ctrl as u8) << 7)
498
114
            | (((self.pdu_conf.source_entity_id.size() - 1) as u8) << 4)
499
114
            | ((self.seg_metadata_flag as u8) << 3)
500
114
            | ((self.pdu_conf.transaction_seq_num.size() - 1) as u8);
501
114
        current_idx += 1;
502
114
        self.pdu_conf.source_entity_id.write_to_be_bytes(
503
114
            &mut buf[current_idx..current_idx + self.pdu_conf.source_entity_id.size()],
504
114
        )?;
505
114
        current_idx += self.pdu_conf.source_entity_id.size();
506
114
        self.pdu_conf.transaction_seq_num.write_to_be_bytes(
507
114
            &mut buf[current_idx..current_idx + self.pdu_conf.transaction_seq_num.size()],
508
114
        )?;
509
114
        current_idx += self.pdu_conf.transaction_seq_num.size();
510
114
        self.pdu_conf.dest_entity_id.write_to_be_bytes(
511
114
            &mut buf[current_idx..current_idx + self.pdu_conf.dest_entity_id.size()],
512
114
        )?;
513
114
        current_idx += self.pdu_conf.dest_entity_id.size();
514
114
        Ok(current_idx)
515
114
    }
516

            
517
    /// This function first verifies that the buffer can hold the full length of the PDU parsed from
518
    /// the header. Then, it verifies the checksum as specified in the standard if the CRC flag
519
    /// of the PDU header is set.
520
    ///
521
    /// This function will return the PDU length excluding the 2 CRC bytes on success. If the CRC
522
    /// flag is not set, it will simply return the PDU length.
523
64
    pub fn verify_length_and_checksum(&self, buf: &[u8]) -> Result<usize, PduError> {
524
64
        if buf.len() < self.pdu_len() {
525
            return Err(ByteConversionError::FromSliceTooSmall {
526
                found: buf.len(),
527
                expected: self.pdu_len(),
528
            }
529
            .into());
530
64
        }
531
64
        if self.pdu_conf.crc_flag == CrcFlag::WithCrc {
532
26
            let mut digest = CRC_CCITT_FALSE.digest();
533
26
            digest.update(&buf[..self.pdu_len()]);
534
26
            if digest.finalize() != 0 {
535
10
                return Err(PduError::ChecksumError(u16::from_be_bytes(
536
10
                    buf[self.pdu_len() - 2..self.pdu_len()].try_into().unwrap(),
537
10
                )));
538
16
            }
539
16
            return Ok(self.pdu_len() - 2);
540
38
        }
541
38
        Ok(self.pdu_len())
542
64
    }
543

            
544
    /// Please note that this function will not verify that the passed buffer can hold the full
545
    /// PDU length. This allows recovering the header portion even if the data field length is
546
    /// invalid. This function will also not do the CRC procedure specified in chapter 4.1.1
547
    /// and 4.1.2 because performing the CRC procedure requires the buffer to be large enough
548
    /// to hold the full PDU.
549
    ///
550
    /// Both functions can however be performed with the [Self::verify_length_and_checksum]
551
    /// function.
552
78
    pub fn from_bytes(buf: &[u8]) -> Result<(Self, usize), PduError> {
553
78
        if buf.len() < FIXED_HEADER_LEN {
554
2
            return Err(PduError::ByteConversion(
555
2
                ByteConversionError::FromSliceTooSmall {
556
2
                    found: buf.len(),
557
2
                    expected: FIXED_HEADER_LEN,
558
2
                },
559
2
            ));
560
76
        }
561
76
        let cfdp_version_raw = (buf[0] >> 5) & 0b111;
562
76
        if cfdp_version_raw != CFDP_VERSION_2 {
563
2
            return Err(PduError::CfdpVersionMissmatch(cfdp_version_raw));
564
74
        }
565
74
        // unwrap for single bit fields: This operation will always succeed.
566
74
        let pdu_type = PduType::try_from((buf[0] >> 4) & 0b1).unwrap();
567
74
        let direction = Direction::try_from((buf[0] >> 3) & 0b1).unwrap();
568
74
        let trans_mode = TransmissionMode::try_from((buf[0] >> 2) & 0b1).unwrap();
569
74
        let crc_flag = CrcFlag::try_from((buf[0] >> 1) & 0b1).unwrap();
570
74
        let file_flag = LargeFileFlag::try_from(buf[0] & 0b1).unwrap();
571
74
        let pdu_datafield_len = u16::from_be_bytes(buf[1..3].try_into().unwrap());
572
74
        let seg_ctrl = SegmentationControl::try_from((buf[3] >> 7) & 0b1).unwrap();
573
74
        let expected_len_entity_ids = (((buf[3] >> 4) & 0b111) + 1) as usize;
574
74
        if (expected_len_entity_ids != 1)
575
4
            && (expected_len_entity_ids != 2)
576
2
            && (expected_len_entity_ids != 4)
577
2
            && (expected_len_entity_ids != 8)
578
        {
579
2
            return Err(PduError::InvalidEntityLen(expected_len_entity_ids as u8));
580
72
        }
581
72
        let seg_metadata_flag = SegmentMetadataFlag::try_from((buf[3] >> 3) & 0b1).unwrap();
582
72
        let expected_len_seq_num = ((buf[3] & 0b111) + 1) as usize;
583
72
        if (expected_len_seq_num != 1)
584
4
            && (expected_len_seq_num != 2)
585
2
            && (expected_len_seq_num != 4)
586
2
            && (expected_len_seq_num != 8)
587
        {
588
2
            return Err(PduError::InvalidTransactionSeqNumLen(
589
2
                expected_len_seq_num as u8,
590
2
            ));
591
70
        }
592
70
        if buf.len() < (4 + 2 * expected_len_entity_ids + expected_len_seq_num) {
593
2
            return Err(ByteConversionError::FromSliceTooSmall {
594
2
                found: buf.len(),
595
2
                expected: 4 + 2 * expected_len_entity_ids + expected_len_seq_num,
596
2
            }
597
2
            .into());
598
68
        }
599
68
        let mut current_idx = 4;
600
68
        // It is okay to unwrap here because we checked the validity of the expected length and of
601
68
        // the remaining buffer length.
602
68
        let source_id =
603
68
            UnsignedByteField::new_from_be_bytes(expected_len_entity_ids, &buf[current_idx..])
604
68
                .unwrap();
605
68
        current_idx += expected_len_entity_ids;
606
68
        let transaction_seq_num =
607
68
            UnsignedByteField::new_from_be_bytes(expected_len_seq_num, &buf[current_idx..])
608
68
                .unwrap();
609
68
        current_idx += expected_len_seq_num;
610
68
        let dest_id =
611
68
            UnsignedByteField::new_from_be_bytes(expected_len_entity_ids, &buf[current_idx..])
612
68
                .unwrap();
613
68
        current_idx += expected_len_entity_ids;
614
68
        let common_pdu_conf = CommonPduConfig::new(
615
68
            source_id,
616
68
            dest_id,
617
68
            transaction_seq_num,
618
68
            trans_mode,
619
68
            file_flag,
620
68
            crc_flag,
621
68
            direction,
622
68
        )
623
68
        .unwrap();
624
68
        Ok((
625
68
            PduHeader {
626
68
                pdu_type,
627
68
                pdu_conf: common_pdu_conf,
628
68
                seg_metadata_flag,
629
68
                seg_ctrl,
630
68
                pdu_datafield_len,
631
68
            },
632
68
            current_idx,
633
68
        ))
634
78
    }
635

            
636
    #[inline]
637
26
    pub fn pdu_type(&self) -> PduType {
638
26
        self.pdu_type
639
26
    }
640

            
641
    #[inline]
642
782
    pub fn common_pdu_conf(&self) -> &CommonPduConfig {
643
782
        &self.pdu_conf
644
782
    }
645

            
646
2
    pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag {
647
2
        self.seg_metadata_flag
648
2
    }
649

            
650
    #[inline]
651
2
    pub fn seg_ctrl(&self) -> SegmentationControl {
652
2
        self.seg_ctrl
653
2
    }
654
}
655

            
656
56
pub(crate) fn write_fss_field(
657
56
    file_flag: LargeFileFlag,
658
56
    file_size: u64,
659
56
    buf: &mut [u8],
660
56
) -> Result<usize, PduError> {
661
56
    Ok(if file_flag == LargeFileFlag::Large {
662
6
        buf[..core::mem::size_of::<u64>()].copy_from_slice(&file_size.to_be_bytes());
663
6
        core::mem::size_of::<u64>()
664
    } else {
665
50
        if file_size > u32::MAX as u64 {
666
            return Err(PduError::FileSizeTooLarge(file_size));
667
50
        }
668
50
        buf[..core::mem::size_of::<u32>()].copy_from_slice(&(file_size as u32).to_be_bytes());
669
50
        core::mem::size_of::<u32>()
670
    })
671
56
}
672

            
673
28
pub(crate) fn read_fss_field(file_flag: LargeFileFlag, buf: &[u8]) -> (usize, u64) {
674
28
    if file_flag == LargeFileFlag::Large {
675
2
        (
676
2
            core::mem::size_of::<u64>(),
677
2
            u64::from_be_bytes(buf[..core::mem::size_of::<u64>()].try_into().unwrap()),
678
2
        )
679
    } else {
680
26
        (
681
26
            core::mem::size_of::<u32>(),
682
26
            u32::from_be_bytes(buf[..core::mem::size_of::<u32>()].try_into().unwrap()).into(),
683
26
        )
684
    }
685
28
}
686

            
687
// This is a generic length check applicable to most PDU deserializations. It first checks whether
688
// a given buffer can hold an expected minimum size, and then it checks whether the PDU datafield
689
// length is larger than that expected minimum size.
690
54
pub(crate) fn generic_length_checks_pdu_deserialization(
691
54
    buf: &[u8],
692
54
    min_expected_len: usize,
693
54
    full_len_without_crc: usize,
694
54
) -> Result<(), ByteConversionError> {
695
54
    // Buffer too short to hold additional expected minimum datasize.
696
54
    if buf.len() < min_expected_len {
697
        return Err(ByteConversionError::FromSliceTooSmall {
698
            found: buf.len(),
699
            expected: min_expected_len,
700
        });
701
54
    }
702
54
    // This can happen if the PDU datafield length value is invalid.
703
54
    if full_len_without_crc < min_expected_len {
704
        return Err(ByteConversionError::FromSliceTooSmall {
705
            found: full_len_without_crc,
706
            expected: min_expected_len,
707
        });
708
54
    }
709
54
    Ok(())
710
54
}
711

            
712
16
pub(crate) fn add_pdu_crc(buf: &mut [u8], mut current_idx: usize) -> usize {
713
16
    let mut digest = CRC_CCITT_FALSE.digest();
714
16
    digest.update(&buf[..current_idx]);
715
16
    buf[current_idx..current_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
716
16
    current_idx += 2;
717
16
    current_idx
718
16
}
719

            
720
#[cfg(test)]
721
mod tests {
722
    use alloc::string::ToString;
723

            
724
    use crate::cfdp::pdu::{CommonPduConfig, PduError, PduHeader, FIXED_HEADER_LEN};
725
    use crate::cfdp::{
726
        CrcFlag, Direction, LargeFileFlag, PduType, SegmentMetadataFlag, SegmentationControl,
727
        TransmissionMode, CFDP_VERSION_2,
728
    };
729
    use crate::util::{
730
        UbfU16, UbfU8, UnsignedByteField, UnsignedByteFieldU16, UnsignedByteFieldU8, UnsignedEnum,
731
    };
732
    use crate::ByteConversionError;
733
    use std::format;
734

            
735
    pub(crate) const TEST_SRC_ID: UbfU8 = UbfU8::new(5);
736
    pub(crate) const TEST_DEST_ID: UbfU8 = UbfU8::new(10);
737
    pub(crate) const TEST_SEQ_NUM: UbfU8 = UbfU8::new(20);
738

            
739
100
    pub(crate) fn common_pdu_conf(crc_flag: CrcFlag, fss: LargeFileFlag) -> CommonPduConfig {
740
100
        let mut pdu_conf =
741
100
            CommonPduConfig::new_with_byte_fields(TEST_SRC_ID, TEST_DEST_ID, TEST_SEQ_NUM)
742
100
                .expect("Generating common PDU config");
743
100
        pdu_conf.crc_flag = crc_flag;
744
100
        pdu_conf.file_flag = fss;
745
100
        pdu_conf
746
100
    }
747

            
748
24
    pub(crate) fn verify_raw_header(pdu_conf: &PduHeader, buf: &[u8]) {
749
24
        assert_eq!((buf[0] >> 5) & 0b111, CFDP_VERSION_2);
750
        // File directive
751
24
        assert_eq!((buf[0] >> 4) & 1, pdu_conf.pdu_type as u8);
752
24
        assert_eq!((buf[0] >> 3) & 1, pdu_conf.pdu_conf.direction as u8);
753
        // Acknowledged
754
24
        assert_eq!((buf[0] >> 2) & 1, pdu_conf.pdu_conf.trans_mode as u8);
755
        // No CRC
756
24
        assert_eq!((buf[0] >> 1) & 1, pdu_conf.pdu_conf.crc_flag as u8);
757
        // Regular file size
758
24
        assert_eq!(buf[0] & 1, pdu_conf.pdu_conf.file_flag as u8);
759
24
        let pdu_datafield_len = u16::from_be_bytes(buf[1..3].try_into().unwrap());
760
24
        assert_eq!(pdu_datafield_len, pdu_conf.pdu_datafield_len);
761
        // No record boundary preservation
762
24
        assert_eq!((buf[3] >> 7) & 1, pdu_conf.seg_ctrl as u8);
763
        // Entity ID length raw value is actual number of octets - 1 => 0
764
24
        let entity_id_len = pdu_conf.pdu_conf.source_entity_id.size();
765
24
        assert_eq!((buf[3] >> 4) & 0b111, entity_id_len as u8 - 1);
766
        // No segment metadata
767
24
        assert_eq!((buf[3] >> 3) & 0b1, pdu_conf.seg_metadata_flag as u8);
768
        // Transaction Sequence ID length raw value is actual number of octets - 1 => 0
769
24
        let seq_num_len = pdu_conf.pdu_conf.transaction_seq_num.size();
770
24
        assert_eq!(buf[3] & 0b111, seq_num_len as u8 - 1);
771
24
        let mut current_idx = 4;
772
84
        let mut byte_field_check = |field_len: usize, ubf: &UnsignedByteField| {
773
72
            match field_len {
774
66
                1 => assert_eq!(buf[current_idx], ubf.value() as u8),
775
6
                2 => assert_eq!(
776
6
                    u16::from_be_bytes(
777
6
                        buf[current_idx..current_idx + field_len]
778
6
                            .try_into()
779
6
                            .unwrap()
780
6
                    ),
781
6
                    ubf.value() as u16
782
6
                ),
783
                4 => assert_eq!(
784
                    u32::from_be_bytes(
785
                        buf[current_idx..current_idx + field_len]
786
                            .try_into()
787
                            .unwrap()
788
                    ),
789
                    ubf.value() as u32
790
                ),
791
                8 => assert_eq!(
792
                    u64::from_be_bytes(
793
                        buf[current_idx..current_idx + field_len]
794
                            .try_into()
795
                            .unwrap()
796
                    ),
797
                    ubf.value()
798
                ),
799
                _ => panic!("invalid entity ID length"),
800
            }
801
72
            current_idx += field_len
802
72
        };
803
24
        byte_field_check(entity_id_len, &pdu_conf.pdu_conf.source_entity_id);
804
24
        byte_field_check(seq_num_len, &pdu_conf.pdu_conf.transaction_seq_num);
805
24
        byte_field_check(entity_id_len, &pdu_conf.pdu_conf.dest_entity_id);
806
24
    }
807

            
808
    #[test]
809
2
    fn test_basic_state() {
810
2
        let src_id = UnsignedByteFieldU8::new(1);
811
2
        let dest_id = UnsignedByteFieldU8::new(2);
812
2
        let transaction_id = UnsignedByteFieldU8::new(3);
813
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
814
2
            .expect("common config creation failed");
815
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
816
2
        assert_eq!(pdu_header.pdu_type(), PduType::FileDirective);
817
2
        let common_conf_ref = pdu_header.common_pdu_conf();
818
2
        assert_eq!(*common_conf_ref, common_pdu_cfg);
819
        // These should be 0 and ignored for non-filedata PDUs
820
2
        assert_eq!(
821
2
            pdu_header.seg_metadata_flag(),
822
2
            SegmentMetadataFlag::NotPresent
823
2
        );
824
2
        assert_eq!(
825
2
            pdu_header.seg_ctrl(),
826
2
            SegmentationControl::NoRecordBoundaryPreservation
827
2
        );
828
2
        assert_eq!(pdu_header.pdu_datafield_len, 5);
829
2
        assert_eq!(pdu_header.header_len(), 7);
830
2
    }
831

            
832
    #[test]
833
2
    fn test_common_pdu_conf_partial_eq() {
834
2
        let common_pdu_cfg_0 =
835
2
            CommonPduConfig::new_with_byte_fields(UbfU8::new(1), UbfU8::new(2), UbfU8::new(3))
836
2
                .expect("common config creation failed");
837
2
        let common_pdu_cfg_1 =
838
2
            CommonPduConfig::new_with_byte_fields(UbfU16::new(1), UbfU16::new(2), UbfU16::new(3))
839
2
                .expect("common config creation failed");
840
2
        assert_eq!(common_pdu_cfg_0, common_pdu_cfg_1);
841
2
    }
842

            
843
    #[test]
844
2
    fn test_basic_state_default() {
845
2
        let default_conf = CommonPduConfig::default();
846
2
        assert_eq!(default_conf.source_id(), UnsignedByteFieldU8::new(0).into());
847
2
        assert_eq!(default_conf.dest_id(), UnsignedByteFieldU8::new(0).into());
848
2
        assert_eq!(
849
2
            default_conf.transaction_seq_num,
850
2
            UnsignedByteFieldU8::new(0).into()
851
2
        );
852
2
        assert_eq!(default_conf.trans_mode, TransmissionMode::Acknowledged);
853
2
        assert_eq!(default_conf.direction, Direction::TowardsReceiver);
854
2
        assert_eq!(default_conf.crc_flag, CrcFlag::NoCrc);
855
2
        assert_eq!(default_conf.file_flag, LargeFileFlag::Normal);
856
2
    }
857

            
858
    #[test]
859
2
    fn test_pdu_header_setter() {
860
2
        let src_id = UnsignedByteFieldU8::new(1);
861
2
        let dest_id = UnsignedByteFieldU8::new(2);
862
2
        let transaction_id = UnsignedByteFieldU8::new(3);
863
2
        let mut common_pdu_cfg =
864
2
            CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
865
2
                .expect("common config creation failed");
866
2
        let other_src_id = UnsignedByteFieldU16::new(5);
867
2
        let other_dest_id = UnsignedByteFieldU16::new(6);
868
2
        let set_result = common_pdu_cfg.set_source_and_dest_id(other_src_id, other_dest_id);
869
2
        assert!(set_result.is_ok());
870
2
        assert_eq!(common_pdu_cfg.source_id(), other_src_id.into());
871
2
        assert_eq!(common_pdu_cfg.dest_id(), other_dest_id.into());
872
2
    }
873

            
874
    #[test]
875
2
    fn test_serialization_1() {
876
2
        let src_id = UnsignedByteFieldU8::new(1);
877
2
        let dest_id = UnsignedByteFieldU8::new(2);
878
2
        let transaction_id = UnsignedByteFieldU8::new(3);
879
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
880
2
            .expect("common config creation failed");
881
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
882
2
        let mut buf: [u8; 7] = [0; 7];
883
2
        let res = pdu_header.write_to_bytes(&mut buf);
884
2
        assert!(res.is_ok());
885
        // 4 byte fixed header plus three bytes src, dest ID and transaction ID
886
2
        assert_eq!(res.unwrap(), 7);
887
2
        verify_raw_header(&pdu_header, &buf);
888
2
        assert_eq!(pdu_header.pdu_datafield_len(), 5);
889
2
    }
890

            
891
    #[test]
892
2
    fn test_deserialization_1() {
893
2
        let src_id = UnsignedByteFieldU8::new(1);
894
2
        let dest_id = UnsignedByteFieldU8::new(2);
895
2
        let transaction_id = UnsignedByteFieldU8::new(3);
896
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
897
2
            .expect("common config creation failed");
898
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
899
2
        let mut buf: [u8; 7] = [0; 7];
900
2
        let res = pdu_header.write_to_bytes(&mut buf);
901
2
        assert!(res.is_ok());
902
2
        let deser_res = PduHeader::from_bytes(&buf);
903
2
        assert!(deser_res.is_ok());
904
2
        let (header_read_back, read_size) = deser_res.unwrap();
905
2
        assert_eq!(read_size, 7);
906
2
        assert_eq!(header_read_back, pdu_header);
907
2
    }
908

            
909
    #[test]
910
2
    fn test_serialization_2() {
911
2
        let src_id = UnsignedByteFieldU16::new(0x0001);
912
2
        let dest_id = UnsignedByteFieldU16::new(0x0203);
913
2
        let transaction_id = UnsignedByteFieldU16::new(0x0405);
914
2
        let mut common_pdu_cfg =
915
2
            CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
916
2
                .expect("common config creation failed");
917
2
        common_pdu_cfg.crc_flag = CrcFlag::WithCrc;
918
2
        common_pdu_cfg.direction = Direction::TowardsSender;
919
2
        common_pdu_cfg.trans_mode = TransmissionMode::Unacknowledged;
920
2
        common_pdu_cfg.file_flag = LargeFileFlag::Large;
921
2
        let pdu_header = PduHeader::new_for_file_data(
922
2
            common_pdu_cfg,
923
2
            5,
924
2
            SegmentMetadataFlag::Present,
925
2
            SegmentationControl::WithRecordBoundaryPreservation,
926
2
        );
927
2
        assert_eq!(pdu_header.header_len(), 10);
928
2
        let mut buf: [u8; 16] = [0; 16];
929
2
        let res = pdu_header.write_to_bytes(&mut buf);
930
2
        assert!(res.is_ok(), "{}", format!("Result {res:?} not okay"));
931
        // 4 byte fixed header, 6 bytes additional fields
932
2
        assert_eq!(res.unwrap(), 10);
933
2
        verify_raw_header(&pdu_header, &buf);
934
2
    }
935

            
936
    #[test]
937
2
    fn test_deserialization_2() {
938
2
        let src_id = UnsignedByteFieldU16::new(0x0001);
939
2
        let dest_id = UnsignedByteFieldU16::new(0x0203);
940
2
        let transaction_id = UnsignedByteFieldU16::new(0x0405);
941
2
        let mut common_pdu_cfg =
942
2
            CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
943
2
                .expect("common config creation failed");
944
2
        common_pdu_cfg.crc_flag = CrcFlag::WithCrc;
945
2
        common_pdu_cfg.direction = Direction::TowardsSender;
946
2
        common_pdu_cfg.trans_mode = TransmissionMode::Unacknowledged;
947
2
        common_pdu_cfg.file_flag = LargeFileFlag::Large;
948
2
        let pdu_header = PduHeader::new_for_file_data(
949
2
            common_pdu_cfg,
950
2
            5,
951
2
            SegmentMetadataFlag::Present,
952
2
            SegmentationControl::WithRecordBoundaryPreservation,
953
2
        );
954
2
        let mut buf: [u8; 16] = [0; 16];
955
2
        let res = pdu_header.write_to_bytes(&mut buf);
956
2
        assert!(res.is_ok());
957
2
        let deser_res = PduHeader::from_bytes(&buf);
958
2
        assert!(deser_res.is_ok());
959
2
        let (header_read_back, read_size) = deser_res.unwrap();
960
2
        assert_eq!(read_size, 10);
961
2
        assert_eq!(header_read_back, pdu_header);
962
2
    }
963

            
964
    #[test]
965
2
    fn test_invalid_raw_version() {
966
2
        let src_id = UnsignedByteFieldU8::new(1);
967
2
        let dest_id = UnsignedByteFieldU8::new(2);
968
2
        let transaction_id = UnsignedByteFieldU8::new(3);
969
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
970
2
            .expect("common config creation failed");
971
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
972
2
        let mut buf: [u8; 7] = [0; 7];
973
2
        let res = pdu_header.write_to_bytes(&mut buf);
974
2
        assert!(res.is_ok());
975
2
        buf[0] &= !0b1110_0000;
976
2
        buf[0] |= (CFDP_VERSION_2 + 1) << 5;
977
2
        let res = PduHeader::from_bytes(&buf);
978
2
        assert!(res.is_err());
979
2
        let error = res.unwrap_err();
980
2
        if let PduError::CfdpVersionMissmatch(raw_version) = error {
981
2
            assert_eq!(raw_version, CFDP_VERSION_2 + 1);
982
2
            assert_eq!(
983
2
                error.to_string(),
984
2
                "cfdp version missmatch, found 2, expected 1"
985
2
            );
986
        } else {
987
            panic!("invalid exception: {}", error);
988
        }
989
2
    }
990

            
991
    #[test]
992
2
    fn test_buf_too_small_1() {
993
2
        let buf: [u8; 3] = [0; 3];
994
2
        let res = PduHeader::from_bytes(&buf);
995
2
        assert!(res.is_err());
996
2
        let error = res.unwrap_err();
997
        if let PduError::ByteConversion(ByteConversionError::FromSliceTooSmall {
998
2
            found,
999
2
            expected,
2
        }) = error
        {
2
            assert_eq!(found, 3);
2
            assert_eq!(expected, FIXED_HEADER_LEN);
        } else {
            panic!("invalid exception: {}", error);
        }
2
    }
    #[test]
2
    fn test_buf_too_small_2() {
2
        let src_id = UnsignedByteFieldU8::new(1);
2
        let dest_id = UnsignedByteFieldU8::new(2);
2
        let transaction_id = UnsignedByteFieldU8::new(3);
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
2
            .expect("common config creation failed");
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
2
        let mut buf: [u8; 7] = [0; 7];
2
        let res = pdu_header.write_to_bytes(&mut buf);
2
        assert!(res.is_ok());
2
        let header = PduHeader::from_bytes(&buf[0..6]);
2
        assert!(header.is_err());
2
        let error = header.unwrap_err();
        if let PduError::ByteConversion(ByteConversionError::FromSliceTooSmall {
2
            found,
2
            expected,
2
        }) = error
        {
2
            assert_eq!(found, 6);
2
            assert_eq!(expected, 7);
2
            assert_eq!(
2
                error.to_string(),
2
                "source slice with size 6 too small, expected at least 7 bytes"
2
            );
        }
2
    }
    #[test]
2
    fn test_invalid_seq_len() {
2
        let src_id = UbfU8::new(1);
2
        let dest_id = UbfU8::new(2);
2
        let transaction_seq_id = UbfU8::new(3);
2
        let invalid_byte_field = UnsignedByteField::new(3, 5);
2
        let pdu_conf_res =
2
            CommonPduConfig::new_with_byte_fields(src_id, dest_id, invalid_byte_field);
2
        assert!(pdu_conf_res.is_err());
2
        let error = pdu_conf_res.unwrap_err();
2
        if let PduError::InvalidTransactionSeqNumLen(len) = error {
2
            assert_eq!(len, 3);
        } else {
            panic!("Invalid exception: {}", error)
        }
2
        let pdu_conf_res = CommonPduConfig::new_with_byte_fields(
2
            invalid_byte_field,
2
            invalid_byte_field,
2
            transaction_seq_id,
2
        );
2
        assert!(pdu_conf_res.is_err());
2
        let error = pdu_conf_res.unwrap_err();
2
        if let PduError::InvalidEntityLen(len) = error {
2
            assert_eq!(len, 3);
2
            assert_eq!(
2
                error.to_string(),
2
                "invalid PDU entity ID length 3, only [1, 2, 4, 8] are allowed"
2
            );
        } else {
            panic!("Invalid exception: {}", error)
        }
2
    }
    #[test]
2
    fn test_missmatch_src_dest_id() {
2
        let src_id = UnsignedByteField::new(1, 5);
2
        let dest_id = UnsignedByteField::new(2, 5);
2
        let transaction_seq_id = UbfU8::new(3);
2
        let pdu_conf_res =
2
            CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_seq_id);
2
        assert!(pdu_conf_res.is_err());
2
        let error = pdu_conf_res.unwrap_err();
        if let PduError::SourceDestIdLenMissmatch {
2
            src_id_len,
2
            dest_id_len,
2
        } = error
        {
2
            assert_eq!(src_id_len, 1);
2
            assert_eq!(dest_id_len, 2);
2
            assert_eq!(
2
                error.to_string(),
2
                "missmatch of PDU source length 1 and destination length 2"
2
            );
        }
2
    }
    #[test]
2
    fn test_invalid_raw_src_id_len() {
2
        let src_id = UnsignedByteFieldU8::new(1);
2
        let dest_id = UnsignedByteFieldU8::new(2);
2
        let transaction_id = UnsignedByteFieldU8::new(3);
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
2
            .expect("common config creation failed");
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
2
        let mut buf: [u8; 7] = [0; 7];
2
        let res = pdu_header.write_to_bytes(&mut buf);
2
        assert!(res.is_ok());
2
        buf[3] &= !0b0111_0000;
2
        // Equivalent to the length of three
2
        buf[3] |= 0b10 << 4;
2
        let header_res = PduHeader::from_bytes(&buf);
2
        assert!(header_res.is_err());
2
        let error = header_res.unwrap_err();
2
        if let PduError::InvalidEntityLen(len) = error {
2
            assert_eq!(len, 3);
        } else {
            panic!("invalid exception {:?}", error)
        }
2
    }
    #[test]
2
    fn test_invalid_transaction_seq_id_len() {
2
        let src_id = UnsignedByteFieldU8::new(1);
2
        let dest_id = UnsignedByteFieldU8::new(2);
2
        let transaction_id = UnsignedByteFieldU8::new(3);
2
        let common_pdu_cfg = CommonPduConfig::new_with_byte_fields(src_id, dest_id, transaction_id)
2
            .expect("common config creation failed");
2
        let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
2
        let mut buf: [u8; 7] = [0; 7];
2
        let res = pdu_header.write_to_bytes(&mut buf);
2
        assert!(res.is_ok());
2
        buf[3] &= !0b0000_0111;
2
        // Equivalent to the length of three
2
        buf[3] |= 0b10;
2
        let header_res = PduHeader::from_bytes(&buf);
2
        assert!(header_res.is_err());
2
        let error = header_res.unwrap_err();
2
        if let PduError::InvalidTransactionSeqNumLen(len) = error {
2
            assert_eq!(len, 3);
        } else {
            panic!("invalid exception {:?}", error)
        }
2
    }
    #[test]
2
    fn test_pdu_error_clonable_and_comparable() {
2
        let pdu_error = PduError::InvalidEntityLen(0);
2
        let pdu_error_2 = pdu_error;
2
        assert_eq!(pdu_error, pdu_error_2);
2
    }
    #[test]
2
    fn test_pdu_config_clonable_and_comparable() {
2
        let common_pdu_cfg_0 =
2
            CommonPduConfig::new_with_byte_fields(UbfU8::new(1), UbfU8::new(2), UbfU8::new(3))
2
                .expect("common config creation failed");
2
        let common_pdu_cfg_1 = common_pdu_cfg_0;
2
        assert_eq!(common_pdu_cfg_0, common_pdu_cfg_1);
2
    }
}