1
use crate::{
2
    cfdp::{CrcFlag, Direction, LargeFileFlag},
3
    ByteConversionError,
4
};
5
use core::{marker::PhantomData, mem::size_of};
6

            
7
use super::{
8
    add_pdu_crc, generic_length_checks_pdu_deserialization, CfdpPdu, FileDirectiveType, PduError,
9
    PduHeader, WritablePduPacket,
10
};
11

            
12
/// Helper type to encapsulate both normal file size segment requests and large file size segment
13
/// requests.
14
#[derive(Debug, PartialEq, Eq, Clone)]
15
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16
pub enum SegmentRequests<'a> {
17
    U32Pairs(&'a [(u32, u32)]),
18
    U64Pairs(&'a [(u64, u64)]),
19
}
20

            
21
impl SegmentRequests<'_> {
22
4
    pub fn is_empty(&self) -> bool {
23
4
        match self {
24
2
            SegmentRequests::U32Pairs(pairs) => pairs.is_empty(),
25
2
            SegmentRequests::U64Pairs(pairs) => pairs.is_empty(),
26
        }
27
4
    }
28
}
29

            
30
/// NAK PDU abstraction specialized in the creation of NAK PDUs.
31
///
32
/// It exposes a specialized API which simplifies to generate these NAK PDUs with the
33
/// format according to CFDP chapter 5.2.6.
34
#[derive(Debug, Clone, PartialEq, Eq)]
35
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36
pub struct NakPduCreator<'seg_reqs> {
37
    pdu_header: PduHeader,
38
    start_of_scope: u64,
39
    end_of_scope: u64,
40
    segment_requests: Option<SegmentRequests<'seg_reqs>>,
41
}
42

            
43
impl<'seg_reqs> NakPduCreator<'seg_reqs> {
44
    /// Please note that the start of scope and the end of scope need to be smaller or equal
45
    /// to [u32::MAX] if the large file flag of the passed PDU configuration is
46
    /// [LargeFileFlag::Normal].
47
    ///
48
    /// ## Errrors
49
    ///
50
12
    pub fn new_no_segment_requests(
51
12
        pdu_header: PduHeader,
52
12
        start_of_scope: u64,
53
12
        end_of_scope: u64,
54
12
    ) -> Result<NakPduCreator<'seg_reqs>, PduError> {
55
12
        Self::new_generic(pdu_header, start_of_scope, end_of_scope, None)
56
12
    }
57

            
58
    /// Default constructor for normal file sizes.
59
6
    pub fn new(
60
6
        pdu_header: PduHeader,
61
6
        start_of_scope: u32,
62
6
        end_of_scope: u32,
63
6
        segment_requests: &'seg_reqs [(u32, u32)],
64
6
    ) -> Result<NakPduCreator, PduError> {
65
6
        let mut passed_segment_requests = None;
66
6
        if !segment_requests.is_empty() {
67
4
            passed_segment_requests = Some(SegmentRequests::U32Pairs(segment_requests));
68
4
        }
69
6
        Self::new_generic(
70
6
            pdu_header,
71
6
            start_of_scope.into(),
72
6
            end_of_scope.into(),
73
6
            passed_segment_requests,
74
6
        )
75
6
    }
76

            
77
2
    pub fn new_large_file_size(
78
2
        pdu_header: PduHeader,
79
2
        start_of_scope: u64,
80
2
        end_of_scope: u64,
81
2
        segment_requests: &'seg_reqs [(u64, u64)],
82
2
    ) -> Result<NakPduCreator, PduError> {
83
2
        let mut passed_segment_requests = None;
84
2
        if !segment_requests.is_empty() {
85
2
            passed_segment_requests = Some(SegmentRequests::U64Pairs(segment_requests));
86
2
        }
87
2
        Self::new_generic(
88
2
            pdu_header,
89
2
            start_of_scope,
90
2
            end_of_scope,
91
2
            passed_segment_requests,
92
2
        )
93
2
    }
94

            
95
22
    fn new_generic(
96
22
        mut pdu_header: PduHeader,
97
22
        start_of_scope: u64,
98
22
        end_of_scope: u64,
99
22
        segment_requests: Option<SegmentRequests<'seg_reqs>>,
100
22
    ) -> Result<NakPduCreator, PduError> {
101
22
        // Force correct direction flag.
102
22
        pdu_header.pdu_conf.direction = Direction::TowardsSender;
103
22
        if let Some(ref segment_requests) = segment_requests {
104
8
            match segment_requests {
105
                SegmentRequests::U32Pairs(_) => {
106
6
                    if start_of_scope > u32::MAX as u64 || end_of_scope > u32::MAX as u64 {
107
2
                        return Err(PduError::InvalidStartOrEndOfScopeValue);
108
4
                    }
109
4
                    pdu_header.pdu_conf.file_flag = LargeFileFlag::Normal;
110
                }
111
2
                SegmentRequests::U64Pairs(_) => {
112
2
                    pdu_header.pdu_conf.file_flag = LargeFileFlag::Large;
113
2
                }
114
            }
115
14
        };
116
20
        let mut nak_pdu = Self {
117
20
            pdu_header,
118
20
            start_of_scope,
119
20
            end_of_scope,
120
20
            segment_requests,
121
20
        };
122
20
        nak_pdu.pdu_header.pdu_datafield_len = nak_pdu.calc_pdu_datafield_len() as u16;
123
20
        Ok(nak_pdu)
124
22
    }
125

            
126
14
    pub fn start_of_scope(&self) -> u64 {
127
14
        self.start_of_scope
128
14
    }
129

            
130
14
    pub fn end_of_scope(&self) -> u64 {
131
14
        self.end_of_scope
132
14
    }
133

            
134
16
    pub fn segment_requests(&self) -> Option<&SegmentRequests> {
135
16
        self.segment_requests.as_ref()
136
16
    }
137

            
138
54
    pub fn num_segment_reqs(&self) -> usize {
139
54
        match &self.segment_requests {
140
16
            Some(seg_reqs) => match seg_reqs {
141
12
                SegmentRequests::U32Pairs(pairs) => pairs.len(),
142
4
                SegmentRequests::U64Pairs(pairs) => pairs.len(),
143
            },
144
38
            None => 0,
145
        }
146
54
    }
147

            
148
16
    pub fn pdu_header(&self) -> &PduHeader {
149
16
        &self.pdu_header
150
16
    }
151

            
152
52
    fn calc_pdu_datafield_len(&self) -> usize {
153
52
        let mut datafield_len = 1;
154
52
        if self.file_flag() == LargeFileFlag::Normal {
155
48
            datafield_len += 8;
156
48
            datafield_len += self.num_segment_reqs() * 8;
157
48
        } else {
158
4
            datafield_len += 16;
159
4
            datafield_len += self.num_segment_reqs() * 16;
160
4
        }
161
52
        if self.crc_flag() == CrcFlag::WithCrc {
162
12
            datafield_len += 2;
163
40
        }
164
52
        datafield_len
165
52
    }
166
}
167

            
168
impl CfdpPdu for NakPduCreator<'_> {
169
132
    fn pdu_header(&self) -> &PduHeader {
170
132
        &self.pdu_header
171
132
    }
172

            
173
2
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
174
2
        Some(FileDirectiveType::NakPdu)
175
2
    }
176
}
177

            
178
impl WritablePduPacket for NakPduCreator<'_> {
179
14
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
180
14
        let expected_len = self.len_written();
181
14
        if buf.len() < expected_len {
182
2
            return Err(ByteConversionError::ToSliceTooSmall {
183
2
                found: buf.len(),
184
2
                expected: expected_len,
185
2
            }
186
2
            .into());
187
12
        }
188
12
        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
189
12
        buf[current_idx] = FileDirectiveType::NakPdu as u8;
190
12
        current_idx += 1;
191
12

            
192
17
        let mut write_start_end_of_scope_normal = || {
193
10
            let start_of_scope = u32::try_from(self.start_of_scope).unwrap();
194
10
            let end_of_scope = u32::try_from(self.end_of_scope).unwrap();
195
10
            buf[current_idx..current_idx + 4].copy_from_slice(&start_of_scope.to_be_bytes());
196
10
            current_idx += 4;
197
10
            buf[current_idx..current_idx + 4].copy_from_slice(&end_of_scope.to_be_bytes());
198
10
            current_idx += 4;
199
10
        };
200
12
        if let Some(ref seg_reqs) = self.segment_requests {
201
6
            match seg_reqs {
202
4
                SegmentRequests::U32Pairs(pairs) => {
203
4
                    // Unwrap is okay here, the API should prevent invalid values which would trigger a
204
4
                    // panic here.
205
4
                    write_start_end_of_scope_normal();
206
12
                    for (next_start_offset, next_end_offset) in *pairs {
207
8
                        buf[current_idx..current_idx + 4]
208
8
                            .copy_from_slice(&next_start_offset.to_be_bytes());
209
8
                        current_idx += 4;
210
8
                        buf[current_idx..current_idx + 4]
211
8
                            .copy_from_slice(&next_end_offset.to_be_bytes());
212
8
                        current_idx += 4;
213
8
                    }
214
                }
215
2
                SegmentRequests::U64Pairs(pairs) => {
216
2
                    buf[current_idx..current_idx + 8]
217
2
                        .copy_from_slice(&self.start_of_scope.to_be_bytes());
218
2
                    current_idx += 8;
219
2
                    buf[current_idx..current_idx + 8]
220
2
                        .copy_from_slice(&self.end_of_scope.to_be_bytes());
221
2
                    current_idx += 8;
222
6
                    for (next_start_offset, next_end_offset) in *pairs {
223
4
                        buf[current_idx..current_idx + 8]
224
4
                            .copy_from_slice(&next_start_offset.to_be_bytes());
225
4
                        current_idx += 8;
226
4
                        buf[current_idx..current_idx + 8]
227
4
                            .copy_from_slice(&next_end_offset.to_be_bytes());
228
4
                        current_idx += 8;
229
4
                    }
230
                }
231
            }
232
6
        } else {
233
6
            write_start_end_of_scope_normal();
234
6
        }
235

            
236
12
        if self.crc_flag() == CrcFlag::WithCrc {
237
2
            current_idx = add_pdu_crc(buf, current_idx);
238
10
        }
239
12
        Ok(current_idx)
240
14
    }
241

            
242
32
    fn len_written(&self) -> usize {
243
32
        self.pdu_header.header_len() + self.calc_pdu_datafield_len()
244
32
    }
245
}
246

            
247
/// Special iterator type for the NAK PDU which allows to iterate over both normal and large file
248
/// segment requests.
249
#[derive(Debug)]
250
pub struct SegmentRequestIter<'a, T> {
251
    seq_req_raw: &'a [u8],
252
    current_idx: usize,
253
    phantom: core::marker::PhantomData<T>,
254
}
255

            
256
pub trait SegReqFromBytes {
257
    fn from_bytes(bytes: &[u8]) -> Self;
258
}
259

            
260
impl SegReqFromBytes for u32 {
261
24
    fn from_bytes(bytes: &[u8]) -> u32 {
262
24
        u32::from_be_bytes(bytes.try_into().unwrap())
263
24
    }
264
}
265

            
266
impl SegReqFromBytes for u64 {
267
24
    fn from_bytes(bytes: &[u8]) -> u64 {
268
24
        u64::from_be_bytes(bytes.try_into().unwrap())
269
24
    }
270
}
271

            
272
impl<'a, T> Iterator for SegmentRequestIter<'a, T>
273
where
274
    T: SegReqFromBytes,
275
{
276
    type Item = (T, T);
277

            
278
24
    fn next(&mut self) -> Option<Self::Item> {
279
24
        let value = self.next_at_offset(self.current_idx);
280
24
        self.current_idx += 2 * size_of::<T>();
281
24
        value
282
24
    }
283
}
284

            
285
impl<'a, 'b> PartialEq<SegmentRequests<'a>> for SegmentRequestIter<'b, u32> {
286
2
    fn eq(&self, other: &SegmentRequests) -> bool {
287
2
        match other {
288
2
            SegmentRequests::U32Pairs(pairs) => self.compare_pairs(pairs),
289
            SegmentRequests::U64Pairs(pairs) => {
290
                if pairs.is_empty() && self.seq_req_raw.is_empty() {
291
                    return true;
292
                }
293
                false
294
            }
295
        }
296
2
    }
297
}
298

            
299
impl<'a, 'b> PartialEq<SegmentRequests<'a>> for SegmentRequestIter<'b, u64> {
300
2
    fn eq(&self, other: &SegmentRequests) -> bool {
301
2
        match other {
302
            SegmentRequests::U32Pairs(pairs) => {
303
                if pairs.is_empty() && self.seq_req_raw.is_empty() {
304
                    return true;
305
                }
306
                false
307
            }
308
2
            SegmentRequests::U64Pairs(pairs) => self.compare_pairs(pairs),
309
        }
310
2
    }
311
}
312

            
313
impl<'a, T> SegmentRequestIter<'a, T>
314
where
315
    T: SegReqFromBytes + PartialEq,
316
{
317
4
    fn compare_pairs(&self, pairs: &[(T, T)]) -> bool {
318
4
        if pairs.is_empty() && self.seq_req_raw.is_empty() {
319
            return true;
320
4
        }
321
4
        let size = size_of::<T>();
322
4
        if pairs.len() * 2 * size != self.seq_req_raw.len() {
323
            return false;
324
4
        }
325

            
326
8
        for (i, pair) in pairs.iter().enumerate() {
327
8
            let next_val = self.next_at_offset(i * 2 * size).unwrap();
328
8
            if next_val != *pair {
329
                return false;
330
8
            }
331
        }
332

            
333
4
        true
334
4
    }
335
}
336

            
337
impl<T: SegReqFromBytes> SegmentRequestIter<'_, T> {
338
32
    fn next_at_offset(&self, mut offset: usize) -> Option<(T, T)> {
339
32
        if offset + size_of::<T>() * 2 > self.seq_req_raw.len() {
340
8
            return None;
341
24
        }
342
24

            
343
24
        let start_offset = T::from_bytes(&self.seq_req_raw[offset..offset + size_of::<T>()]);
344
24
        offset += size_of::<T>();
345
24

            
346
24
        let end_offset = T::from_bytes(&self.seq_req_raw[offset..offset + size_of::<T>()]);
347
24
        Some((start_offset, end_offset))
348
32
    }
349
}
350

            
351
/// NAK PDU abstraction specialized in the reading NAK PDUs from a raw bytestream.
352
///
353
/// This is a zero-copy class where the segment requests can be read using a special iterator
354
/// API without the need to copy them.
355
///
356
/// The NAK format is expected to be conforming to CFDP chapter 5.2.6.
357
#[derive(Debug, PartialEq, Eq)]
358
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
359
pub struct NakPduReader<'seg_reqs> {
360
    pdu_header: PduHeader,
361
    start_of_scope: u64,
362
    end_of_scope: u64,
363
    seg_reqs_raw: &'seg_reqs [u8],
364
}
365

            
366
impl CfdpPdu for NakPduReader<'_> {
367
52
    fn pdu_header(&self) -> &PduHeader {
368
52
        &self.pdu_header
369
52
    }
370

            
371
2
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
372
2
        Some(FileDirectiveType::NakPdu)
373
2
    }
374
}
375

            
376
impl<'seg_reqs> NakPduReader<'seg_reqs> {
377
4
    pub fn new(buf: &'seg_reqs [u8]) -> Result<NakPduReader, PduError> {
378
4
        Self::from_bytes(buf)
379
4
    }
380

            
381
10
    pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result<NakPduReader, PduError> {
382
10
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
383
10
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
384
        // Minimum length of 9: 1 byte directive field and start and end of scope for normal file
385
        // size.
386
8
        generic_length_checks_pdu_deserialization(buf, 9, full_len_without_crc)?;
387
8
        let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
388
            PduError::InvalidDirectiveType {
389
                found: buf[current_idx],
390
                expected: Some(FileDirectiveType::NakPdu),
391
            }
392
8
        })?;
393
8
        if directive_type != FileDirectiveType::NakPdu {
394
            return Err(PduError::WrongDirectiveType {
395
                found: directive_type,
396
                expected: FileDirectiveType::AckPdu,
397
            });
398
8
        }
399
8
        current_idx += 1;
400
8
        let start_of_scope;
401
8
        let end_of_scope;
402
8
        if pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
403
2
            if current_idx + 16 > buf.len() {
404
                return Err(PduError::ByteConversion(
405
                    ByteConversionError::FromSliceTooSmall {
406
                        found: buf.len(),
407
                        expected: current_idx + 16,
408
                    },
409
                ));
410
2
            }
411
2
            start_of_scope =
412
2
                u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
413
2
            current_idx += 8;
414
2
            end_of_scope =
415
2
                u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
416
2
            current_idx += 8;
417
6
        } else {
418
6
            start_of_scope =
419
6
                u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()) as u64;
420
6
            current_idx += 4;
421
6
            end_of_scope =
422
6
                u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()) as u64;
423
6
            current_idx += 4;
424
6
        }
425
8
        Ok(Self {
426
8
            pdu_header,
427
8
            start_of_scope,
428
8
            end_of_scope,
429
8
            seg_reqs_raw: &buf[current_idx..full_len_without_crc],
430
8
        })
431
10
    }
432

            
433
12
    pub fn start_of_scope(&self) -> u64 {
434
12
        self.start_of_scope
435
12
    }
436

            
437
12
    pub fn end_of_scope(&self) -> u64 {
438
12
        self.end_of_scope
439
12
    }
440

            
441
4
    pub fn num_segment_reqs(&self) -> usize {
442
4
        if self.seg_reqs_raw.is_empty() {
443
            return 0;
444
4
        }
445
4
        if self.file_flag() == LargeFileFlag::Normal {
446
2
            self.seg_reqs_raw.len() / 8
447
        } else {
448
2
            self.seg_reqs_raw.len() / 16
449
        }
450
4
    }
451

            
452
    /// This function returns [None] if this NAK PDUs contains segment requests for a large file.
453
10
    pub fn get_normal_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_, u32>> {
454
10
        if self.file_flag() == LargeFileFlag::Large {
455
2
            return None;
456
8
        }
457
8
        Some(SegmentRequestIter {
458
8
            seq_req_raw: self.seg_reqs_raw,
459
8
            current_idx: 0,
460
8
            phantom: PhantomData,
461
8
        })
462
10
    }
463

            
464
    /// This function returns [None] if this NAK PDUs contains segment requests for a normal file.
465
10
    pub fn get_large_segment_requests_iterator(&self) -> Option<SegmentRequestIter<'_, u64>> {
466
10
        if self.file_flag() == LargeFileFlag::Normal {
467
2
            return None;
468
8
        }
469
8
        Some(SegmentRequestIter {
470
8
            seq_req_raw: self.seg_reqs_raw,
471
8
            current_idx: 0,
472
8
            phantom: PhantomData,
473
8
        })
474
10
    }
475
}
476

            
477
impl<'a, 'b> PartialEq<NakPduCreator<'a>> for NakPduReader<'b> {
478
8
    fn eq(&self, other: &NakPduCreator<'a>) -> bool {
479
8
        if self.pdu_header() != other.pdu_header()
480
8
            || self.end_of_scope() != other.end_of_scope()
481
8
            || self.start_of_scope() != other.start_of_scope()
482
        {
483
            return false;
484
8
        }
485
8

            
486
8
        // Check if both segment requests are empty or None
487
8
        match (self.seg_reqs_raw.is_empty(), other.segment_requests()) {
488
4
            (true, None) => true,
489
            (true, Some(seg_reqs)) => seg_reqs.is_empty(),
490
            (false, None) => false,
491
            _ => {
492
                // Compare based on file_flag
493
4
                if self.file_flag() == LargeFileFlag::Normal {
494
2
                    let normal_iter = self.get_normal_segment_requests_iterator().unwrap();
495
2
                    normal_iter == *other.segment_requests().unwrap()
496
                } else {
497
2
                    let large_iter = self.get_large_segment_requests_iterator().unwrap();
498
2
                    large_iter == *other.segment_requests().unwrap()
499
                }
500
            }
501
        }
502
8
    }
503
}
504

            
505
#[cfg(test)]
506
mod tests {
507
    use alloc::string::ToString;
508

            
509
    use crate::cfdp::{
510
        pdu::tests::{common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID},
511
        PduType, TransmissionMode,
512
    };
513

            
514
    use super::*;
515

            
516
4
    fn check_generic_fields(nak_pdu: &impl CfdpPdu) {
517
4
        assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc);
518
4
        assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal);
519
4
        assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective);
520
4
        assert_eq!(
521
4
            nak_pdu.file_directive_type(),
522
4
            Some(FileDirectiveType::NakPdu),
523
4
        );
524
4
        assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged);
525
4
        assert_eq!(nak_pdu.direction(), Direction::TowardsSender);
526
4
        assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into());
527
4
        assert_eq!(nak_pdu.dest_id(), TEST_DEST_ID.into());
528
4
        assert_eq!(nak_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
529
4
    }
530

            
531
    #[test]
532
2
    fn test_seg_request_api() {
533
2
        let seg_req = SegmentRequests::U32Pairs(&[]);
534
2
        assert!(seg_req.is_empty());
535
2
        let seg_req = SegmentRequests::U64Pairs(&[]);
536
2
        assert!(seg_req.is_empty());
537
2
    }
538

            
539
    #[test]
540
2
    fn test_basic_creator() {
541
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
542
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
543
2
        let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 0, 0)
544
2
            .expect("creating NAK PDU creator failed");
545
2
        assert_eq!(nak_pdu.start_of_scope(), 0);
546
2
        assert_eq!(nak_pdu.end_of_scope(), 0);
547
2
        assert_eq!(nak_pdu.segment_requests(), None);
548
2
        assert_eq!(nak_pdu.num_segment_reqs(), 0);
549
2
        check_generic_fields(&nak_pdu);
550
2
    }
551

            
552
    #[test]
553
2
    fn test_serialization_empty() {
554
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
555
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
556
2
        let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300)
557
2
            .expect("creating NAK PDU creator failed");
558
2
        assert_eq!(nak_pdu.start_of_scope(), 100);
559
2
        assert_eq!(nak_pdu.end_of_scope(), 300);
560
2
        let mut buf: [u8; 64] = [0; 64];
561
2
        nak_pdu
562
2
            .write_to_bytes(&mut buf)
563
2
            .expect("writing NAK PDU to buffer failed");
564
2
        verify_raw_header(nak_pdu.pdu_header(), &buf);
565
2
        let mut current_idx = nak_pdu.pdu_header().header_len();
566
2
        assert_eq!(current_idx + 9, nak_pdu.len_written());
567
2
        assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8);
568
2
        current_idx += 1;
569
2
        let start_of_scope =
570
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
571
2
        assert_eq!(start_of_scope, 100);
572
2
        current_idx += 4;
573
2
        let end_of_scope =
574
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
575
2
        assert_eq!(end_of_scope, 300);
576
2
        current_idx += 4;
577
2
        assert_eq!(current_idx, nak_pdu.len_written());
578
2
    }
579

            
580
    #[test]
581
2
    fn test_serialization_two_segments() {
582
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
583
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
584
2
        let nak_pdu = NakPduCreator::new(pdu_header, 100, 300, &[(0, 0), (32, 64)])
585
2
            .expect("creating NAK PDU creator failed");
586
2
        let mut buf: [u8; 64] = [0; 64];
587
2
        nak_pdu
588
2
            .write_to_bytes(&mut buf)
589
2
            .expect("writing NAK PDU to buffer failed");
590
2
        verify_raw_header(nak_pdu.pdu_header(), &buf);
591
2
        let mut current_idx = nak_pdu.pdu_header().header_len();
592
2
        assert_eq!(current_idx + 9 + 16, nak_pdu.len_written());
593
2
        assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8);
594
2
        current_idx += 1;
595
2
        let start_of_scope =
596
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
597
2
        assert_eq!(start_of_scope, 100);
598
2
        current_idx += 4;
599
2
        let end_of_scope =
600
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
601
2
        assert_eq!(end_of_scope, 300);
602
2
        current_idx += 4;
603
2
        let first_seg_start =
604
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
605
2
        assert_eq!(first_seg_start, 0);
606
2
        current_idx += 4;
607
2
        let first_seg_end =
608
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
609
2
        assert_eq!(first_seg_end, 0);
610
2
        current_idx += 4;
611
2
        let second_seg_start =
612
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
613
2
        assert_eq!(second_seg_start, 32);
614
2
        current_idx += 4;
615
2
        let second_seg_end =
616
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
617
2
        assert_eq!(second_seg_end, 64);
618
2
        current_idx += 4;
619
2
        assert_eq!(current_idx, nak_pdu.len_written());
620
2
    }
621

            
622
    #[test]
623
2
    fn test_deserialization_empty() {
624
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
625
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
626
2
        let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300)
627
2
            .expect("creating NAK PDU creator failed");
628
2
        let mut buf: [u8; 64] = [0; 64];
629
2
        nak_pdu
630
2
            .write_to_bytes(&mut buf)
631
2
            .expect("writing NAK PDU to buffer failed");
632
2
        let nak_pdu_deser = NakPduReader::from_bytes(&buf).expect("deserializing NAK PDU failed");
633
2
        assert_eq!(nak_pdu_deser, nak_pdu);
634
2
        check_generic_fields(&nak_pdu_deser);
635
2
    }
636

            
637
    #[test]
638
2
    fn test_deserialization_large_segments() {
639
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Large);
640
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
641
2
        let nak_pdu =
642
2
            NakPduCreator::new_large_file_size(pdu_header, 100, 300, &[(50, 100), (200, 300)])
643
2
                .expect("creating NAK PDU creator failed");
644
2
        let mut buf: [u8; 128] = [0; 128];
645
2
        nak_pdu
646
2
            .write_to_bytes(&mut buf)
647
2
            .expect("writing NAK PDU to buffer failed");
648
2
        let nak_pdu_deser = NakPduReader::from_bytes(&buf).expect("deserializing NAK PDU failed");
649
2
        assert_eq!(nak_pdu_deser, nak_pdu);
650
2
        assert_eq!(nak_pdu_deser.start_of_scope(), 100);
651
2
        assert_eq!(nak_pdu_deser.end_of_scope(), 300);
652
2
        assert_eq!(nak_pdu_deser.num_segment_reqs(), 2);
653
2
        assert!(nak_pdu_deser
654
2
            .get_large_segment_requests_iterator()
655
2
            .is_some());
656
2
        assert!(nak_pdu_deser
657
2
            .get_normal_segment_requests_iterator()
658
2
            .is_none());
659
2
        assert_eq!(
660
2
            nak_pdu_deser
661
2
                .get_large_segment_requests_iterator()
662
2
                .unwrap()
663
2
                .count(),
664
2
            2
665
2
        );
666
4
        for (idx, large_segments) in nak_pdu_deser
667
2
            .get_large_segment_requests_iterator()
668
2
            .unwrap()
669
2
            .enumerate()
670
        {
671
4
            if idx == 0 {
672
2
                assert_eq!(large_segments.0, 50);
673
2
                assert_eq!(large_segments.1, 100);
674
            } else {
675
2
                assert_eq!(large_segments.0, 200);
676
2
                assert_eq!(large_segments.1, 300);
677
            }
678
        }
679
2
    }
680

            
681
    #[test]
682
2
    fn test_deserialization_normal_segments() {
683
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
684
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
685
2
        let nak_pdu = NakPduCreator::new(pdu_header, 100, 300, &[(50, 100), (200, 300)])
686
2
            .expect("creating NAK PDU creator failed");
687
2
        let mut buf: [u8; 128] = [0; 128];
688
2
        nak_pdu
689
2
            .write_to_bytes(&mut buf)
690
2
            .expect("writing NAK PDU to buffer failed");
691
2
        let nak_pdu_deser = NakPduReader::from_bytes(&buf).expect("deserializing NAK PDU failed");
692
2
        assert_eq!(nak_pdu_deser, nak_pdu);
693
2
        assert_eq!(nak_pdu_deser.start_of_scope(), 100);
694
2
        assert_eq!(nak_pdu_deser.end_of_scope(), 300);
695
2
        assert_eq!(nak_pdu_deser.num_segment_reqs(), 2);
696
2
        assert!(nak_pdu_deser
697
2
            .get_normal_segment_requests_iterator()
698
2
            .is_some());
699
2
        assert!(nak_pdu_deser
700
2
            .get_large_segment_requests_iterator()
701
2
            .is_none());
702
2
        assert_eq!(
703
2
            nak_pdu_deser
704
2
                .get_normal_segment_requests_iterator()
705
2
                .unwrap()
706
2
                .count(),
707
2
            2
708
2
        );
709
4
        for (idx, large_segments) in nak_pdu_deser
710
2
            .get_normal_segment_requests_iterator()
711
2
            .unwrap()
712
2
            .enumerate()
713
        {
714
4
            if idx == 0 {
715
2
                assert_eq!(large_segments.0, 50);
716
2
                assert_eq!(large_segments.1, 100);
717
            } else {
718
2
                assert_eq!(large_segments.0, 200);
719
2
                assert_eq!(large_segments.1, 300);
720
            }
721
        }
722
2
    }
723

            
724
    #[test]
725
2
    fn test_empty_is_empty() {
726
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
727
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
728
2
        let nak_pdu_0 =
729
2
            NakPduCreator::new(pdu_header, 100, 300, &[]).expect("creating NAK PDU creator failed");
730
2
        let nak_pdu_1 = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300)
731
2
            .expect("creating NAK PDU creator failed");
732
2
        assert_eq!(nak_pdu_0, nak_pdu_1);
733
        // Assert the segment request is mapped to None.
734
2
        assert!(nak_pdu_0.segment_requests().is_none());
735
2
    }
736

            
737
    #[test]
738
2
    fn test_new_generic_invalid_input() {
739
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
740
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
741
2
        let u32_list = SegmentRequests::U32Pairs(&[(0, 50), (50, 100)]);
742
2
        //let error = NakPduCreator::new_generic(pdu_header, 100, 300, Some(u32_list));
743
2
        let error = NakPduCreator::new_generic(
744
2
            pdu_header,
745
2
            u32::MAX as u64 + 1,
746
2
            u32::MAX as u64 + 2,
747
2
            Some(u32_list),
748
2
        );
749
2
        assert!(error.is_err());
750
2
        let error = error.unwrap_err();
751
2
        if let PduError::InvalidStartOrEndOfScopeValue = error {
752
2
            assert_eq!(
753
2
                error.to_string(),
754
2
                "invalid start or end of scope for NAK PDU"
755
2
            );
756
        } else {
757
            panic!("unexpected error {error}");
758
        }
759
2
    }
760

            
761
    #[test]
762
2
    fn test_target_buf_too_small() {
763
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
764
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
765
2
        let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 100, 300)
766
2
            .expect("creating NAK PDU creator failed");
767
2
        assert_eq!(nak_pdu.start_of_scope(), 100);
768
2
        assert_eq!(nak_pdu.end_of_scope(), 300);
769
2
        let mut buf: [u8; 5] = [0; 5];
770
2
        let error = nak_pdu.write_to_bytes(&mut buf);
771
2
        assert!(error.is_err());
772
2
        let e = error.unwrap_err();
773
2
        match e {
774
2
            PduError::ByteConversion(conv_error) => match conv_error {
775
2
                ByteConversionError::ToSliceTooSmall { found, expected } => {
776
2
                    assert_eq!(expected, nak_pdu.len_written());
777
2
                    assert_eq!(found, 5);
778
                }
779
                _ => panic!("unexpected error {conv_error}"),
780
            },
781
            _ => panic!("unexpected error {e}"),
782
        }
783
2
    }
784

            
785
    #[test]
786
2
    fn test_with_crc() {
787
2
        let pdu_conf = common_pdu_conf(CrcFlag::WithCrc, LargeFileFlag::Normal);
788
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
789
2
        let nak_pdu = NakPduCreator::new_no_segment_requests(pdu_header, 0, 0)
790
2
            .expect("creating NAK PDU creator failed");
791
2
        let mut nak_vec = nak_pdu.to_vec().expect("writing NAK to vector failed");
792
2
        assert_eq!(nak_vec.len(), pdu_header.header_len() + 9 + 2);
793
2
        assert_eq!(nak_vec.len(), nak_pdu.len_written());
794
2
        let nak_pdu_deser = NakPduReader::new(&nak_vec).expect("reading NAK PDU failed");
795
2
        assert_eq!(nak_pdu_deser, nak_pdu);
796
2
        nak_vec[nak_pdu.len_written() - 1] -= 1;
797
2
        let nak_pdu_deser = NakPduReader::new(&nak_vec);
798
2
        assert!(nak_pdu_deser.is_err());
799
2
        if let Err(PduError::ChecksumError(raw)) = nak_pdu_deser {
800
2
            assert_eq!(
801
2
                raw,
802
2
                u16::from_be_bytes(nak_vec[nak_pdu.len_written() - 2..].try_into().unwrap())
803
2
            );
804
        }
805
2
    }
806
}