veecle_os_data_support_someip/
parse.rs

1//! Trait for parsing of SOME/IP data types.
2
3// Re-export the derive macro.
4pub use veecle_os_data_support_someip_macros::Parse;
5
6/// An error while parsing a SOME/IP payload type.
7#[derive(Debug, Clone, Copy, thiserror::Error, PartialEq, Eq)]
8pub enum ParseError {
9    /// The payload slice has too few bytes to parse the SOME/IP payload type.
10    #[error("the payload is too short")]
11    PayloadTooShort,
12    /// The payload slice has more bytes than the SOME/IP payload type expected.
13    #[error("the payload is too long. Expected {expected} found {found}")]
14    PayloadTooLong {
15        /// Expected payload size.
16        expected: usize,
17        /// Provided payload size.
18        found: usize,
19    },
20    /// The message is malformed.
21    #[error("malformed message. Failed to parse {failed_at}")]
22    MalformedMessage {
23        /// Name of the type that was malformed.
24        failed_at: &'static str,
25    },
26}
27
28/// Reads bytes from an underlying byte-slice.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ByteReader<'a> {
31    /// Slice of bytes the reader reads from.
32    data: &'a [u8],
33    /// Reader offset into the slice.
34    offset: usize,
35}
36
37impl<'a> ByteReader<'a> {
38    /// Creates a new reader for a slice of bytes.
39    pub fn new(data: &'a [u8]) -> Self {
40        Self { data, offset: 0 }
41    }
42
43    /// Creates a second reader for a sub-slice of this reader. The slice of the second reader starts at the current
44    /// `offset` and ends at `offset + length`. Also advances this readers offset by `length`.
45    ///
46    /// A lot of variable length SOME/IP types are prefixed with their length in bytes, so this allows creating a
47    /// sub-reader and reading bytes until it [is empty](Self::is_empty).
48    pub fn sub_reader(&mut self, length: usize) -> Result<Self, ParseError> {
49        let Some(new_offset) = self.offset.checked_add(length) else {
50            return Err(ParseError::PayloadTooShort);
51        };
52
53        if new_offset > self.data.len() {
54            return Err(ParseError::PayloadTooShort);
55        }
56
57        let current_offset = self.offset;
58        self.offset = new_offset;
59
60        let data = &self.data[current_offset..self.offset];
61
62        Ok(Self { offset: 0, data })
63    }
64
65    /// Returns a new sub-reader with the remaining slice and advances the reader.
66    pub fn take_remaining(&mut self) -> Self {
67        let data = &self.data[self.offset..];
68
69        self.offset = self.data.len();
70
71        Self { data, offset: 0 }
72    }
73
74    /// Reads a single byte and advances the reader.
75    pub fn read_byte(&mut self) -> Result<u8, ParseError> {
76        if self.offset >= self.data.len() {
77            return Err(ParseError::PayloadTooShort);
78        }
79
80        let byte = self.data[self.offset];
81
82        self.offset += 1;
83
84        Ok(byte)
85    }
86
87    /// Returns a slice of bytes with the given length and advances the reader.
88    pub fn read_slice(&mut self, length: usize) -> Result<&'a [u8], ParseError> {
89        let Some(new_offset) = self.offset.checked_add(length) else {
90            return Err(ParseError::PayloadTooShort);
91        };
92
93        if new_offset > self.data.len() {
94            return Err(ParseError::PayloadTooShort);
95        }
96
97        let result = &self.data[self.offset..self.offset + length];
98
99        self.offset += length;
100
101        Ok(result)
102    }
103
104    /// Reads an array of `N` bytes and advances the reader.
105    pub fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ParseError> {
106        let Some(new_offset) = self.offset.checked_add(N) else {
107            return Err(ParseError::PayloadTooShort);
108        };
109
110        if new_offset > self.data.len() {
111            return Err(ParseError::PayloadTooShort);
112        }
113
114        let result = self.data[self.offset..self.offset + N]
115            .try_into()
116            .expect("returned slice should always be N bytes long");
117
118        self.offset += N;
119
120        Ok(result)
121    }
122
123    /// Returns the remaining slice without advancing the offset.
124    pub fn remaining_slice(&self) -> &'a [u8] {
125        &self.data[self.offset..]
126    }
127
128    /// Consumes bytes matching the provided input. Returns whether or not there was a match.
129    ///
130    /// Returns false if are there not enough bytes to compare to.
131    pub fn consume_matching_bytes(&mut self, compare_to: &[u8]) -> bool {
132        let length = compare_to.len();
133
134        let Some(new_offset) = self.offset.checked_add(length) else {
135            return false;
136        };
137
138        if new_offset > self.data.len() || &self.data[self.offset..][..length] != compare_to {
139            return false;
140        }
141
142        self.offset += length;
143
144        true
145    }
146
147    /// Returns the length of the remaining slice.
148    pub fn len(&self) -> usize {
149        self.data.len().saturating_sub(self.offset)
150    }
151
152    /// Returns true if there are no bytes left to read.
153    pub fn is_empty(&self) -> bool {
154        self.offset >= self.data.len()
155    }
156}
157
158/// A trait for parsing SOME/IP payload types from a slice of bytes.
159pub trait Parse<'a>: Sized {
160    /// Parses a SOME/IP payload type from a given slice of bytes.
161    ///
162    /// For parsing using the entire slice, see [`ParseExt`].
163    fn parse_partial(reader: &mut ByteReader<'a>) -> Result<Self, ParseError>;
164}
165
166/// An extension trait to expose a nicer API to the user.
167pub trait ParseExt<'a>: Sized {
168    /// Parses a SOME/IP payload type from a given slice of bytes using [`Parse`] and
169    /// validates all the bytes of the slice were used during parsing.
170    fn parse(slice: &'a [u8]) -> Result<Self, ParseError>;
171}
172
173impl<'a, T> ParseExt<'a> for T
174where
175    T: Parse<'a>,
176{
177    fn parse(slice: &'a [u8]) -> Result<Self, ParseError> {
178        let mut reader = ByteReader::new(slice);
179        let this = Self::parse_partial(&mut reader)?;
180
181        match reader.is_empty() {
182            true => Ok(this),
183            false => Err(ParseError::PayloadTooLong {
184                expected: reader.offset,
185                found: slice.len(),
186            }),
187        }
188    }
189}
190
191#[cfg(test)]
192#[cfg_attr(coverage_nightly, coverage(off))]
193mod parse_ext {
194    use pretty_assertions::assert_eq;
195
196    use super::{ParseError, ParseExt};
197
198    #[test]
199    fn payload_too_long() {
200        assert_eq!(
201            u32::parse(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
202            Err(ParseError::PayloadTooLong {
203                expected: 4,
204                found: 5
205            })
206        );
207    }
208}