dlt_parse/
dlt_header.rs

1use super::*;
2
3///A dlt message header
4#[derive(Debug, PartialEq, Eq, Clone, Default)]
5pub struct DltHeader {
6    ///If true the payload is encoded in big endian. This does not influence the fields of the dlt header, which is always encoded in big endian.
7    pub is_big_endian: bool,
8    pub message_counter: u8,
9    pub length: u16,
10    pub ecu_id: Option<[u8; 4]>,
11    pub session_id: Option<u32>,
12    pub timestamp: Option<u32>,
13    pub extended_header: Option<DltExtendedHeader>,
14}
15
16impl DltHeader {
17    /// Versions of the DLT header that can be decoded by the decoding
18    /// functions in this library.
19    pub const SUPPORTED_DECODABLE_VERSIONS: [u8; 2] = [0, 1];
20
21    /// The maximum size in bytes/octets a V1 DLT header can be when encoded.
22    ///
23    /// The number is calculated by adding
24    ///
25    /// * 4 bytes for the base header
26    /// * 4 bytes for the ECU id
27    /// * 4 bytes for the session id
28    /// * 4 bytes for the timestamp
29    /// * 10 bytes for the extended header
30    pub const MAX_SERIALIZED_SIZE: usize = 4 + 4 + 4 + 4 + 10;
31
32    /// Version that will be written into the DLT header version field when writing this header.
33    pub const VERSION: u8 = 1;
34
35    pub fn from_slice(slice: &[u8]) -> Result<DltHeader, error::PacketSliceError> {
36        use error::{PacketSliceError::*, *};
37
38        if slice.len() < 4 {
39            return Err(UnexpectedEndOfSlice(UnexpectedEndOfSliceError {
40                layer: error::Layer::DltHeader,
41                minimum_size: 4,
42                actual_size: slice.len(),
43            }));
44        }
45
46        // SAFETY:
47        // Safe as it is checked beforehand that the slice
48        // has at least 4 bytes.
49        let header_type = unsafe { *slice.get_unchecked(0) };
50
51        // check version
52        let version = (header_type >> 5) & MAX_VERSION;
53        if 0 != version && 1 != version {
54            return Err(UnsupportedDltVersion(UnsupportedDltVersionError {
55                unsupported_version: version,
56            }));
57        }
58
59        // calculate the minimum size based on the header flags
60        // the header size has at least 4 bytes
61        let header_len = if 0 != header_type & ECU_ID_FLAG {
62            4 + 4
63        } else {
64            4
65        };
66
67        let header_len = if 0 != header_type & SESSION_ID_FLAG {
68            header_len + 4
69        } else {
70            header_len
71        };
72
73        let header_len = if 0 != header_type & TIMESTAMP_FLAG {
74            header_len + 4
75        } else {
76            header_len
77        };
78
79        let header_len = if 0 != header_type & EXTDENDED_HEADER_FLAG {
80            header_len + 10
81        } else {
82            header_len
83        };
84
85        // check that enough data based on the header size is available
86        if slice.len() < header_len {
87            return Err(UnexpectedEndOfSlice(UnexpectedEndOfSliceError {
88                layer: error::Layer::DltHeader,
89                minimum_size: header_len,
90                actual_size: slice.len(),
91            }));
92        }
93
94        // SAFETY: Safe as the slice lenght has been verfied to be long
95        // enough for the optional header parts.
96        let mut next_option_ptr = unsafe { slice.as_ptr().add(4) };
97
98        let ecu_id = if 0 != header_type & ECU_ID_FLAG {
99            // SAFETY: Safe as header_len was extended by 4 if the ECU_ID_FLAG
100            // is set & the slice len is verfied to be at least as long as
101            // the header_len.
102            unsafe {
103                let ecu_id_ptr = next_option_ptr;
104                next_option_ptr = next_option_ptr.add(4);
105                Some([
106                    *ecu_id_ptr,
107                    *ecu_id_ptr.add(1),
108                    *ecu_id_ptr.add(2),
109                    *ecu_id_ptr.add(3),
110                ])
111            }
112        } else {
113            None
114        };
115
116        let session_id = if 0 != header_type & SESSION_ID_FLAG {
117            // SAFETY: Safe as header_len was extended by 4 if the SESSION_ID_FLAG
118            // is set & the slice len is verfied to be at least as long as
119            // the header_len.
120            unsafe {
121                let session_id_ptr = next_option_ptr;
122                next_option_ptr = next_option_ptr.add(4);
123                Some(u32::from_be_bytes([
124                    *session_id_ptr,
125                    *session_id_ptr.add(1),
126                    *session_id_ptr.add(2),
127                    *session_id_ptr.add(3),
128                ]))
129            }
130        } else {
131            None
132        };
133
134        let timestamp = if 0 != header_type & TIMESTAMP_FLAG {
135            // SAFETY: Safe as header_len was extended by 4 if the TIMESTAMP_FLAG
136            // is set & the slice len is verfied to be at least as long as
137            // the header_len.
138            unsafe {
139                let timestamp_id_ptr = next_option_ptr;
140                next_option_ptr = next_option_ptr.add(4);
141                Some(u32::from_be_bytes([
142                    *timestamp_id_ptr,
143                    *timestamp_id_ptr.add(1),
144                    *timestamp_id_ptr.add(2),
145                    *timestamp_id_ptr.add(3),
146                ]))
147            }
148        } else {
149            None
150        };
151
152        let extended_header = if 0 != header_type & EXTDENDED_HEADER_FLAG {
153            Some(DltExtendedHeader {
154                // SAFETY: Safe as header_len was extended by 4 if the EXTDENDED_HEADER_FLAG
155                // is set & the slice len is verfied to be at least as long as
156                // the header_len.
157                message_info: DltMessageInfo(unsafe { *next_option_ptr }),
158                number_of_arguments: unsafe { *next_option_ptr.add(1) },
159                application_id: unsafe {
160                    [
161                        *next_option_ptr.add(2),
162                        *next_option_ptr.add(3),
163                        *next_option_ptr.add(4),
164                        *next_option_ptr.add(5),
165                    ]
166                },
167                context_id: unsafe {
168                    [
169                        *next_option_ptr.add(6),
170                        *next_option_ptr.add(7),
171                        *next_option_ptr.add(8),
172                        *next_option_ptr.add(9),
173                    ]
174                },
175            })
176        } else {
177            None
178        };
179
180        Ok(DltHeader {
181            // If true the payload is encoded in big endian. This does not influence the fields of the dlt header, which is always encoded in big endian.
182            is_big_endian: 0 != header_type & BIG_ENDIAN_FLAG,
183            // SAFETY:
184            // Safe, as the slice length was checked at the start of the function
185            // to be at least 4.
186            message_counter: unsafe { *slice.get_unchecked(1) },
187            length: u16::from_be_bytes(
188                // SAFETY:
189                // Safe, as the slice length was checked at the start of the function
190                // to be at least 4.
191                unsafe { [*slice.get_unchecked(2), *slice.get_unchecked(3)] },
192            ),
193            ecu_id,
194            session_id,
195            timestamp,
196            extended_header,
197        })
198    }
199
200    /// Encodes the header to the on the wire format.
201    pub fn to_bytes(&self) -> ArrayVec<u8, { DltHeader::MAX_SERIALIZED_SIZE }> {
202        // encode values
203        let length_be = self.length.to_be_bytes();
204        let mut bytes: [u8; 26] = [
205            //header type bitfield
206            {
207                let mut result = 0;
208                if self.extended_header.is_some() {
209                    result |= EXTDENDED_HEADER_FLAG;
210                }
211                if self.is_big_endian {
212                    result |= BIG_ENDIAN_FLAG;
213                }
214                if self.ecu_id.is_some() {
215                    result |= ECU_ID_FLAG;
216                }
217                if self.session_id.is_some() {
218                    result |= SESSION_ID_FLAG;
219                }
220                if self.timestamp.is_some() {
221                    result |= TIMESTAMP_FLAG;
222                }
223                result |= (DltHeader::VERSION << 5) & 0b1110_0000;
224                result
225            },
226            self.message_counter,
227            length_be[0],
228            length_be[1],
229            // 4 bytes ECU id
230            0,
231            0,
232            0,
233            0,
234            // 4 bytes for session id
235            0,
236            0,
237            0,
238            0,
239            // 4 bytes for timestamp
240            0,
241            0,
242            0,
243            0,
244            // 10 bytes for extension header
245            0,
246            0,
247            0,
248            0,
249            0,
250            0,
251            0,
252            0,
253            0,
254            0,
255        ];
256
257        let mut offset = 4;
258        let mut add_4bytes = |data: [u8; 4]| {
259            // SAFETY: add_4bytes not called more then 4 times
260            // as and the
261            unsafe {
262                let ptr = bytes.as_mut_slice().as_mut_ptr().add(offset);
263                *ptr = data[0];
264                *ptr.add(1) = data[1];
265                *ptr.add(2) = data[2];
266                *ptr.add(3) = data[3];
267            }
268            offset += 4;
269        };
270
271        // insert optional headers
272        if let Some(value) = self.ecu_id {
273            add_4bytes(value);
274        }
275
276        if let Some(value) = self.session_id {
277            add_4bytes(value.to_be_bytes());
278        }
279
280        if let Some(value) = self.timestamp {
281            add_4bytes(value.to_be_bytes());
282        }
283
284        if let Some(value) = &self.extended_header {
285            // SAFETY: 10 bytes are guranteed to be left over.
286            unsafe {
287                let ptr = bytes.as_mut_slice().as_mut_ptr().add(offset);
288                *ptr = value.message_info.0;
289                *ptr.add(1) = value.number_of_arguments;
290                *ptr.add(2) = value.application_id[0];
291                *ptr.add(3) = value.application_id[1];
292                *ptr.add(4) = value.application_id[2];
293                *ptr.add(5) = value.application_id[3];
294                *ptr.add(6) = value.context_id[0];
295                *ptr.add(7) = value.context_id[1];
296                *ptr.add(8) = value.context_id[2];
297                *ptr.add(9) = value.context_id[3];
298            }
299            offset += 10;
300        }
301        let mut result = ArrayVec::from(bytes);
302        unsafe {
303            result.set_len(offset);
304        }
305        result
306    }
307
308    ///Deserialize a DltHeader & TpHeader from the given reader.
309    #[cfg(feature = "std")]
310    pub fn read<T: io::Read + Sized>(reader: &mut T) -> Result<DltHeader, error::ReadError> {
311        use crate::error::UnsupportedDltVersionError;
312
313        // read the standard header that is always present
314        let standard_header_start = {
315            let mut standard_header_start: [u8; 4] = [0; 4];
316            reader.read_exact(&mut standard_header_start)?;
317            standard_header_start
318        };
319
320        //first lets read the header type
321        let header_type = standard_header_start[0];
322
323        // check version
324        let version = (header_type >> 5) & MAX_VERSION;
325        if 0 != version && 1 != version {
326            return Err(error::ReadError::UnsupportedDltVersion(
327                UnsupportedDltVersionError {
328                    unsupported_version: version,
329                },
330            ));
331        }
332
333        //let extended_header = 0 != header_type & EXTDENDED_HEADER_FLAG;
334        Ok(DltHeader {
335            is_big_endian: 0 != header_type & BIG_ENDIAN_FLAG,
336            message_counter: standard_header_start[1],
337            length: u16::from_be_bytes([standard_header_start[2], standard_header_start[3]]),
338            ecu_id: if 0 != header_type & ECU_ID_FLAG {
339                Some({
340                    let mut buffer: [u8; 4] = [0; 4];
341                    reader.read_exact(&mut buffer)?;
342                    buffer
343                })
344            } else {
345                None
346            },
347            session_id: if 0 != header_type & SESSION_ID_FLAG {
348                Some({
349                    let mut buffer: [u8; 4] = [0; 4];
350                    reader.read_exact(&mut buffer)?;
351                    u32::from_be_bytes(buffer)
352                })
353            } else {
354                None
355            },
356            timestamp: if 0 != header_type & TIMESTAMP_FLAG {
357                Some({
358                    let mut buffer: [u8; 4] = [0; 4];
359                    reader.read_exact(&mut buffer)?;
360                    u32::from_be_bytes(buffer)
361                })
362            } else {
363                None
364            },
365            extended_header: if 0 != header_type & EXTDENDED_HEADER_FLAG {
366                Some({
367                    let mut buffer: [u8; 10] = [0; 10];
368                    reader.read_exact(&mut buffer)?;
369
370                    DltExtendedHeader {
371                        message_info: DltMessageInfo(buffer[0]),
372                        number_of_arguments: buffer[1],
373                        application_id: [buffer[2], buffer[3], buffer[4], buffer[5]],
374                        context_id: [buffer[6], buffer[7], buffer[8], buffer[9]],
375                    }
376                })
377            } else {
378                None
379            },
380        })
381    }
382
383    ///Serializes the header to the given writer.
384    #[cfg(feature = "std")]
385    pub fn write<T: io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
386        {
387            let length_be = self.length.to_be_bytes();
388            let standard_header_start: [u8; 4] = [
389                //header type bitfield
390                {
391                    let mut result = 0;
392                    if self.extended_header.is_some() {
393                        result |= EXTDENDED_HEADER_FLAG;
394                    }
395                    if self.is_big_endian {
396                        result |= BIG_ENDIAN_FLAG;
397                    }
398                    if self.ecu_id.is_some() {
399                        result |= ECU_ID_FLAG;
400                    }
401                    if self.session_id.is_some() {
402                        result |= SESSION_ID_FLAG;
403                    }
404                    if self.timestamp.is_some() {
405                        result |= TIMESTAMP_FLAG;
406                    }
407                    result |= (DltHeader::VERSION << 5) & 0b1110_0000;
408                    result
409                },
410                self.message_counter,
411                length_be[0],
412                length_be[1],
413            ];
414
415            writer.write_all(&standard_header_start)?;
416        }
417
418        if let Some(value) = self.ecu_id {
419            writer.write_all(&value)?;
420        }
421
422        if let Some(value) = self.session_id {
423            writer.write_all(&value.to_be_bytes())?;
424        }
425
426        if let Some(value) = self.timestamp {
427            writer.write_all(&value.to_be_bytes())?;
428        }
429
430        //write the extended header if it exists
431        if let Some(value) = &self.extended_header {
432            let bytes: [u8; 10] = [
433                value.message_info.0,
434                value.number_of_arguments,
435                value.application_id[0],
436                value.application_id[1],
437                value.application_id[2],
438                value.application_id[3],
439                value.context_id[0],
440                value.context_id[1],
441                value.context_id[2],
442                value.context_id[3],
443            ];
444            writer.write_all(&bytes)?;
445        }
446        Ok(())
447    }
448
449    ///Returns if the package is a verbose package
450    #[inline]
451    pub fn is_verbose(&self) -> bool {
452        match &self.extended_header {
453            None => false, //only packages with extended headers can be verbose
454            Some(ext) => ext.is_verbose(),
455        }
456    }
457
458    ///Return the byte/octed size of the serialized header (including extended header)
459    #[inline]
460    pub fn header_len(&self) -> u16 {
461        4 + match self.ecu_id {
462            Some(_) => 4,
463            None => 0,
464        } + match self.session_id {
465            Some(_) => 4,
466            None => 0,
467        } + match self.timestamp {
468            Some(_) => 4,
469            None => 0,
470        } + match self.extended_header {
471            Some(_) => 10,
472            None => 0,
473        }
474    }
475}
476
477#[cfg(test)]
478mod dlt_header_tests {
479
480    use super::*;
481    use crate::proptest_generators::*;
482    use proptest::prelude::*;
483
484    proptest! {
485        #[test]
486        fn to_bytes_from_slice(
487            version in 0..=1u8,
488            ref dlt_header in dlt_header_any(),
489            unsupported_version in (0u8..0b111u8).prop_filter(
490                "version must be unknown",
491                |v| !DltHeader::SUPPORTED_DECODABLE_VERSIONS.iter().any(|&x| v == &x)
492            )
493        ) {
494            use error::PacketSliceError::*;
495            // ok case
496            {
497                let bytes = {
498                    let mut bytes = dlt_header.to_bytes();
499                    // inject the supported version number
500                    bytes[0] = (bytes[0] & 0b0001_1111) | ((version << 5) & 0b1110_0000);
501                    bytes
502                };
503                assert_eq!(
504                    dlt_header.clone(),
505                    DltHeader::from_slice(&bytes[..]).unwrap()
506                );
507            }
508            // from_slice unexpected end of slice error
509            {
510                for l in 0..dlt_header.header_len() as usize {
511                    let bytes = dlt_header.to_bytes();
512                    assert_eq!(
513                        UnexpectedEndOfSlice(
514                            error::UnexpectedEndOfSliceError{
515                                minimum_size: if l < 4 {
516                                    4
517                                } else {
518                                    dlt_header.header_len() as usize
519                                },
520                                actual_size: l,
521                                layer: error::Layer::DltHeader,
522                            }
523                        ),
524                        DltHeader::from_slice(&bytes[..l]).unwrap_err()
525                    );
526                }
527            }
528            // from_slice unsupported version
529            {
530                let mut bytes = dlt_header.to_bytes();
531                // modify the version in the encoded version
532                // directly
533                bytes[0] = (bytes[0] & 0b0001_1111) | ((unsupported_version << 5) & 0b1110_0000);
534                assert_eq!(
535                    UnsupportedDltVersion(
536                        error::UnsupportedDltVersionError{
537                            unsupported_version,
538                        }
539                    ),
540                    DltHeader::from_slice(&bytes[..]).unwrap_err()
541                );
542            }
543        }
544    }
545
546    proptest! {
547        #[test]
548        #[cfg(feature = "std")]
549        fn write_read(ref dlt_header in dlt_header_any()) {
550            use std::io::Cursor;
551
552            let mut buffer = Vec::new();
553            dlt_header.write(&mut buffer).unwrap();
554            let mut reader = Cursor::new(&buffer[..]);
555            let result = DltHeader::read(&mut reader).unwrap();
556            assert_eq!(dlt_header, &result);
557        }
558    }
559
560    proptest! {
561        #[test]
562        #[cfg(feature = "std")]
563        fn read_length_error(ref dlt_header in dlt_header_any()) {
564            use std::io::Cursor;
565
566            let mut buffer = Vec::new();
567            dlt_header.write(&mut buffer).unwrap();
568            let reduced_len = buffer.len() - 1;
569            let mut reader = Cursor::new(&buffer[..reduced_len]);
570            assert_matches!(DltHeader::read(&mut reader), Err(error::ReadError::IoError(_)));
571        }
572    }
573
574    proptest! {
575        #[test]
576        #[cfg(feature = "std")]
577        fn write_io_error(ref header in dlt_header_any()) {
578            use std::io::Cursor;
579
580            let mut buffer: Vec<u8> = Vec::with_capacity(
581                header.header_len().into()
582            );
583            for len in 0..header.header_len() {
584                buffer.resize(len.into(), 0);
585                let mut writer = Cursor::new(&mut buffer[..]);
586                assert_matches!(header.write(&mut writer), Err(_));
587            }
588        }
589    }
590
591    #[test]
592    fn is_verbose() {
593        let mut header: DltHeader = Default::default();
594        assert_eq!(false, header.is_verbose());
595        //add an extended header without the verbose flag
596        header.extended_header = Some(Default::default());
597        assert_eq!(false, header.is_verbose());
598        //set the verbose flag
599        header
600            .extended_header
601            .as_mut()
602            .unwrap()
603            .set_is_verbose(true);
604        assert_eq!(true, header.is_verbose());
605    }
606
607    #[test]
608    fn header_len() {
609        struct Test {
610            expected: u16,
611            ecu_id: Option<[u8; 4]>,
612            session_id: Option<u32>,
613            timestamp: Option<u32>,
614            extended_header: Option<DltExtendedHeader>,
615        }
616
617        let tests = [
618            Test {
619                expected: 4,
620                ecu_id: None,
621                session_id: None,
622                timestamp: None,
623                extended_header: None,
624            },
625            Test {
626                expected: 4 + 4 + 4 + 4 + 10,
627                ecu_id: Some([0; 4]),
628                session_id: Some(0),
629                timestamp: Some(0),
630                extended_header: Some(Default::default()),
631            },
632            Test {
633                expected: 4 + 4,
634                ecu_id: Some([0; 4]),
635                session_id: None,
636                timestamp: None,
637                extended_header: None,
638            },
639            Test {
640                expected: 4 + 4,
641                ecu_id: None,
642                session_id: Some(0),
643                timestamp: None,
644                extended_header: None,
645            },
646            Test {
647                expected: 4 + 4,
648                ecu_id: None,
649                session_id: None,
650                timestamp: Some(0),
651                extended_header: None,
652            },
653            Test {
654                expected: 4 + 10,
655                ecu_id: None,
656                session_id: None,
657                timestamp: None,
658                extended_header: Some(Default::default()),
659            },
660        ];
661
662        for test in tests {
663            assert_eq!(
664                test.expected,
665                DltHeader {
666                    is_big_endian: false,
667                    message_counter: 123,
668                    length: 123,
669                    ecu_id: test.ecu_id,
670                    session_id: test.session_id,
671                    timestamp: test.timestamp,
672                    extended_header: test.extended_header,
673                }
674                .header_len()
675            );
676        }
677    }
678
679    #[test]
680    fn debug() {
681        let header: DltHeader = Default::default();
682        assert_eq!(
683            format!(
684                "DltHeader {{ is_big_endian: {}, message_counter: {}, length: {}, ecu_id: {:?}, session_id: {:?}, timestamp: {:?}, extended_header: {:?} }}",
685                header.is_big_endian,
686                header.message_counter,
687                header.length,
688                header.ecu_id,
689                header.session_id,
690                header.timestamp,
691                header.extended_header,
692            ),
693            format!("{:?}", header)
694        );
695    }
696
697    proptest! {
698        #[test]
699        fn clone_eq(ref header in dlt_header_any()) {
700            assert_eq!(*header, header.clone());
701        }
702    }
703
704    #[test]
705    fn default() {
706        let header: DltHeader = Default::default();
707        assert_eq!(header.is_big_endian, false);
708        assert_eq!(header.message_counter, 0);
709        assert_eq!(header.length, 0);
710        assert_eq!(header.ecu_id, None);
711        assert_eq!(header.session_id, None);
712        assert_eq!(header.timestamp, None);
713        assert_eq!(header.extended_header, None);
714    }
715} // mod dlt_header_tests