Skip to main content

simple_someip/protocol/
message.rs

1use crate::{
2    protocol::{Error, Header, MessageType, ReturnCode, header::HeaderView, sd::SdHeaderView},
3    traits::PayloadWireFormat,
4};
5use automotive_wire_codec::{Decode, Encode};
6
7/// A SOME/IP message consisting of a [`Header`] and a payload.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct Message<PayloadDefinition> {
10    header: Header,
11    payload: PayloadDefinition,
12}
13
14impl<PayloadDefinition: PayloadWireFormat> Message<PayloadDefinition> {
15    /// Creates a new message from a header and payload.
16    pub const fn new(header: Header, payload: PayloadDefinition) -> Self {
17        Self { header, payload }
18    }
19
20    /// Creates a new SOME/IP-SD message from a request ID and SD header.
21    ///
22    /// # Errors
23    ///
24    /// Returns the error from [`Encode::encoded_size`] on the SD header. No
25    /// in-tree `SdHeader` can fail this -- `sd::Header::encoded_size` is
26    /// unconditionally `Ok` -- but [`PayloadWireFormat`] is public, and a
27    /// downstream implementation may.
28    pub fn new_sd(
29        request_id: u32,
30        sd_header: &<PayloadDefinition as PayloadWireFormat>::SdHeader,
31    ) -> Result<Self, Error> {
32        // Propagated rather than defaulted. `unwrap_or(0)` produced
33        // `Header::new_sd(request_id, 0)` -- a header declaring the bare
34        // 8-byte SD length -- and `encode` then wrote the full payload after
35        // it. Receivers truncate at the declared length, so a failure here
36        // used to become silent wire corruption instead of an error.
37        let sd_header_size = sd_header.encoded_size()?;
38        Ok(Self::new(
39            Header::new_sd(request_id, sd_header_size),
40            PayloadDefinition::new_sd_payload(sd_header),
41        ))
42    }
43
44    /// Returns a reference to the message header.
45    pub const fn header(&self) -> &Header {
46        &self.header
47    }
48
49    /// Returns `true` if this is a SOME/IP-SD message.
50    pub const fn is_sd(&self) -> bool {
51        self.header.is_sd()
52    }
53
54    /// Sets the request ID in the header.
55    pub const fn set_request_id(&mut self, request_id: u32) {
56        self.header.set_request_id(request_id);
57    }
58
59    /// Returns the SD header if this is an SD message, or `None` otherwise.
60    pub fn sd_header(&self) -> Option<&<PayloadDefinition as PayloadWireFormat>::SdHeader> {
61        if !self.header().message_id().is_sd() || self.header().message_type().is_tp() {
62            return None;
63        }
64        self.payload.as_sd_header()
65    }
66
67    /// Returns a reference to the payload.
68    pub const fn payload(&self) -> &PayloadDefinition {
69        &self.payload
70    }
71
72    /// Returns a mutable reference to the payload.
73    pub const fn payload_mut(&mut self) -> &mut PayloadDefinition {
74        &mut self.payload
75    }
76}
77
78/// Zero-copy view into a complete SOME/IP message (header + payload).
79#[derive(Clone, Copy, Debug)]
80pub struct MessageView<'a> {
81    header: HeaderView<'a>,
82    payload: &'a [u8],
83}
84
85impl<'a> MessageView<'a> {
86    /// Parse a complete SOME/IP message from `buf`.
87    ///
88    /// Validates the header, checks that the buffer contains enough data for
89    /// the declared payload, and for SD messages validates SD-specific constraints.
90    ///
91    /// # Errors
92    ///
93    /// Returns an error if the header is invalid, the buffer is too short for the
94    /// declared payload, or SD-specific validation fails.
95    ///
96    /// Any bytes past the declared payload are silently discarded. Use the
97    /// [`Decode`] impl's [`decode`](Decode::decode) to recover the trailing
98    /// bytes (the next message in a multi-message datagram), or
99    /// [`decode_exact`](Decode::decode_exact) to reject them.
100    ///
101    /// This is a thin wrapper over the [`Decode`] impl, which is the single
102    /// source of decode logic for this type.
103    pub fn parse(buf: &'a [u8]) -> Result<Self, Error> {
104        Ok(Self::decode(buf)?.0)
105    }
106
107    /// Returns the header view.
108    #[must_use]
109    pub fn header(&self) -> HeaderView<'a> {
110        self.header
111    }
112
113    /// Returns the raw payload bytes.
114    #[must_use]
115    pub fn payload_bytes(&self) -> &'a [u8] {
116        self.payload
117    }
118
119    /// Returns `true` if this is a SOME/IP-SD message.
120    #[must_use]
121    pub fn is_sd(&self) -> bool {
122        self.header.is_sd()
123    }
124
125    /// Parse the payload as an SD header.
126    /// The caller should check `is_sd()` first; this method returns an error
127    /// if the message is not an SD message (the SD validation in `parse` must
128    /// have already passed).
129    ///
130    /// # Errors
131    ///
132    /// Returns an error if this is not an SD message or the SD payload is malformed.
133    pub fn sd_header(&self) -> Result<SdHeaderView<'a>, Error> {
134        if !self.is_sd() {
135            return Err(crate::protocol::sd::Error::InvalidMessage("Not an SD message").into());
136        }
137        SdHeaderView::parse(self.payload)
138    }
139}
140
141impl<'a> Decode<'a> for MessageView<'a> {
142    type Error = Error;
143
144    /// Decode a single SOME/IP message from the front of `buf`.
145    ///
146    /// Validates the header, checks that the buffer contains enough data for the
147    /// declared payload, and for SD messages validates SD-specific constraints.
148    /// Returns `(message, remaining_bytes)`, where the remainder is any bytes
149    /// past this message's declared payload (the next message in a
150    /// multi-message datagram).
151    ///
152    /// # Errors
153    ///
154    /// Returns an error if the header is invalid, the buffer is too short for the
155    /// declared payload, or SD-specific validation fails.
156    fn decode(buf: &'a [u8]) -> Result<(Self, &'a [u8]), Error> {
157        let (header, remaining) = HeaderView::decode(buf)?;
158        if header.length() < 8 {
159            return Err(Error::InvalidLength(header.length()));
160        }
161        let payload_size = header.payload_size();
162
163        if remaining.len() < payload_size {
164            return Err(automotive_wire_codec::Incomplete {
165                needed: payload_size,
166                available: remaining.len(),
167            }
168            .into());
169        }
170
171        // SD-specific validation
172        if header.is_sd() {
173            if payload_size < 12 {
174                return Err(
175                    crate::protocol::sd::Error::InvalidMessage("SD message too short").into(),
176                );
177            }
178            if header.interface_version() != 0x01 {
179                return Err(crate::protocol::sd::Error::InvalidMessage(
180                    "SD interface version mismatch",
181                )
182                .into());
183            }
184            if header.message_type().message_type() != MessageType::Notification {
185                return Err(
186                    crate::protocol::sd::Error::InvalidMessage("SD message type mismatch").into(),
187                );
188            }
189            if header.return_code() != ReturnCode::Ok {
190                return Err(
191                    crate::protocol::sd::Error::InvalidMessage("SD return code mismatch").into(),
192                );
193            }
194        }
195
196        let payload = &remaining[..payload_size];
197        let rest = &remaining[payload_size..];
198        Ok((Self { header, payload }, rest))
199    }
200}
201
202impl<PayloadDefinition: PayloadWireFormat> Encode for Message<PayloadDefinition> {
203    type Error = Error;
204
205    fn encoded_size(&self) -> Result<usize, Self::Error> {
206        Ok(self.header.encoded_size()? + self.payload.encoded_size()?)
207    }
208
209    fn encode(&self, writer: &mut impl embedded_io::Write) -> Result<usize, Error> {
210        Ok(self.header.encode(writer)? + self.payload.encode(writer)?)
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use crate::protocol::sd::test_support::{TestPayload, TestSdHeader, empty_sd_header};
218    use crate::protocol::{MessageId, sd, sd::RebootFlag};
219
220    type Msg = Message<TestPayload>;
221
222    fn minimal_sd_header() -> TestSdHeader {
223        empty_sd_header()
224    }
225
226    fn make_sd_message() -> Msg {
227        Msg::new_sd(0x0000_0001, &minimal_sd_header()).expect("in-tree SdHeader cannot fail")
228    }
229
230    /// A failing `SdHeader::encoded_size` must surface as an error, not as a
231    /// header declaring the bare 8-byte SD length.
232    ///
233    /// `unwrap_or(0)` built `Header::new_sd(request_id, 0)` on `Err`, and
234    /// `Message::encode` then wrote the full payload after it. Receivers
235    /// truncate at the declared length, so the failure mode was silent wire
236    /// corruption rather than an error. (PR #153 review.)
237    #[test]
238    fn new_sd_surfaces_a_failing_sd_header_size() {
239        use crate::protocol::sd::test_support::{FailingPayload, FailingSdHeader};
240
241        assert!(
242            FailingSdHeader.encoded_size().is_err(),
243            "fixture must actually fail, or this test proves nothing",
244        );
245        assert!(Message::<FailingPayload>::new_sd(0x1, &FailingSdHeader).is_err());
246    }
247
248    // --- new ---
249
250    #[test]
251    fn new_stores_header_and_payload() {
252        let header = Header::new_sd(0x42, 12);
253        let payload = TestPayload::new_sd_payload(&minimal_sd_header());
254        let msg = Msg::new(header.clone(), payload.clone());
255        assert_eq!(*msg.header(), header);
256        assert_eq!(*msg.payload(), payload);
257    }
258
259    // --- new_sd ---
260
261    #[test]
262    fn new_sd_creates_valid_message() {
263        let msg = make_sd_message();
264        assert!(msg.is_sd());
265        assert_eq!(msg.header().message_id(), MessageId::SD);
266    }
267
268    // --- header / payload / payload_mut ---
269
270    #[test]
271    fn header_returns_reference() {
272        let msg = make_sd_message();
273        assert_eq!(msg.header().protocol_version(), 0x01);
274    }
275
276    #[test]
277    fn payload_returns_reference() {
278        let sd_hdr = minimal_sd_header();
279        let msg = make_sd_message();
280        assert_eq!(msg.payload().as_sd_header().unwrap(), &sd_hdr);
281    }
282
283    #[test]
284    fn payload_mut_allows_modification() {
285        let mut msg = make_sd_message();
286        let _p = msg.payload_mut();
287        // Just verify we get a mutable reference without panic
288    }
289
290    // --- is_sd ---
291
292    #[test]
293    fn is_sd_true_for_sd_message() {
294        assert!(make_sd_message().is_sd());
295    }
296
297    // --- set_request_id ---
298
299    #[test]
300    fn set_request_id_updates_header() {
301        let mut msg = make_sd_message();
302        msg.set_request_id(0xDEAD_BEEF);
303        assert_eq!(msg.header().request_id(), 0xDEAD_BEEF);
304    }
305
306    // --- get_sd_header ---
307
308    #[test]
309    fn get_sd_header_returns_some_for_sd() {
310        let sd_hdr = minimal_sd_header();
311        let msg = make_sd_message();
312        assert_eq!(msg.sd_header().unwrap(), &sd_hdr);
313    }
314
315    // --- Encode: encoded_size ---
316
317    #[test]
318    fn required_size_is_header_plus_payload() {
319        let msg = make_sd_message();
320        let expected = msg.header().encoded_size().unwrap() + msg.payload().encoded_size().unwrap();
321        assert_eq!(msg.encoded_size().unwrap(), expected);
322    }
323
324    // --- Encode: encode / MessageView::parse round-trip ---
325
326    #[test]
327    fn encode_parse_round_trip() {
328        let msg = make_sd_message();
329        let mut buf = [0u8; 64];
330        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
331        assert_eq!(n, msg.encoded_size().unwrap());
332        let view = MessageView::parse(&buf[..n]).unwrap();
333        assert!(view.is_sd());
334        assert_eq!(view.header().to_owned(), *msg.header());
335    }
336
337    #[test]
338    fn encode_parse_with_entries() {
339        let mut entries = heapless::Vec::<sd::Entry, 4>::new();
340        entries
341            .push(sd::Entry::FindService(sd::ServiceEntry::find(0xABCD)))
342            .unwrap();
343        let sd_hdr = TestSdHeader {
344            flags: sd::Flags::new_sd(RebootFlag::RecentlyRebooted),
345            entries,
346            options: heapless::Vec::new(),
347        };
348        let msg = Msg::new_sd(0x42, &sd_hdr).expect("in-tree SdHeader cannot fail");
349        let mut buf = [0u8; 64];
350        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
351        let view = MessageView::parse(&buf[..n]).unwrap();
352        let sd_view = view.sd_header().unwrap();
353        assert_eq!(sd_view.entry_count(), 1);
354        let entry = sd_view.entries().next().unwrap();
355        assert_eq!(entry.service_id(), 0xABCD);
356    }
357
358    // --- Decode: trailing bytes are the next message ---
359
360    #[test]
361    fn decode_returns_trailing_bytes_as_remainder() {
362        let msg = make_sd_message();
363        let mut buf = [0u8; 128];
364        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
365        // Append 5 trailing bytes past the message.
366        for (i, b) in [0xDE, 0xAD, 0xBE, 0xEF, 0x42].into_iter().enumerate() {
367            buf[n + i] = b;
368        }
369        let (view, rest) = MessageView::decode(&buf[..n + 5]).unwrap();
370        assert_eq!(view.header().to_owned(), *msg.header());
371        assert_eq!(rest, &[0xDE, 0xAD, 0xBE, 0xEF, 0x42]);
372    }
373
374    #[test]
375    fn parse_silently_discards_trailing_bytes() {
376        let msg = make_sd_message();
377        let mut buf = [0u8; 128];
378        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
379        buf[n] = 0xFF;
380        // parse (the thin wrapper) drops the remainder without error.
381        let view = MessageView::parse(&buf[..=n]).unwrap();
382        assert_eq!(view.header().to_owned(), *msg.header());
383    }
384
385    #[test]
386    fn decode_exact_rejects_trailing_bytes() {
387        let msg = make_sd_message();
388        let mut buf = [0u8; 128];
389        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
390        buf[n] = 0xFF;
391        assert!(matches!(
392            MessageView::decode_exact(&buf[..=n]),
393            Err(Error::Trailing(_))
394        ));
395        // Exactly-sized succeeds.
396        assert!(MessageView::decode_exact(&buf[..n]).is_ok());
397    }
398
399    // --- parse with exactly-sized slice ---
400
401    #[test]
402    fn parse_exact_size_slice_succeeds() {
403        let msg = make_sd_message();
404        let mut buf = [0u8; 64];
405        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
406        // Pass exactly n bytes — no extra data beyond the message
407        let view = MessageView::parse(&buf[..n]).unwrap();
408        assert!(view.is_sd());
409        assert_eq!(view.header().to_owned(), *msg.header());
410    }
411
412    // --- parse error paths ---
413
414    #[test]
415    fn parse_truncated_returns_eof() {
416        let buf: [u8; 4] = [0; 4];
417        assert!(matches!(
418            MessageView::parse(&buf[..]),
419            Err(Error::Incomplete(automotive_wire_codec::Incomplete {
420                needed: 16,
421                available: 4,
422            }))
423        ));
424    }
425
426    #[test]
427    fn parse_payload_truncated_reports_needed_and_available() {
428        let msg = make_sd_message();
429        let mut buf = [0u8; 64];
430        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
431        let payload_size = msg.header().payload_size();
432        // Keep the full 16-byte header but chop one byte off the payload.
433        let short = &buf[..n - 1];
434        assert!(matches!(
435            MessageView::parse(short),
436            Err(Error::Incomplete(automotive_wire_codec::Incomplete {
437                needed,
438                available,
439            })) if needed == payload_size && available == payload_size - 1
440        ));
441    }
442
443    #[test]
444    fn decode_rejects_length_below_8() {
445        let msg = make_sd_message();
446        let mut buf = [0u8; 64];
447        msg.encode(&mut buf.as_mut_slice()).unwrap();
448        // Overwrite the length field (bytes 4..8) with a value below the
449        // 8-byte minimum. This must be rejected, not underflow/panic.
450        let bad_len: u32 = 4;
451        buf[4..8].copy_from_slice(&bad_len.to_be_bytes());
452        assert!(matches!(
453            MessageView::decode(&buf[..]),
454            Err(Error::InvalidLength(4))
455        ));
456        assert!(matches!(
457            MessageView::decode_exact(&buf[..16]),
458            Err(Error::InvalidLength(4))
459        ));
460    }
461
462    // --- parse SD validation errors ---
463
464    #[test]
465    fn parse_sd_payload_too_short_returns_error() {
466        let msg = make_sd_message();
467        let mut buf = [0u8; 64];
468        msg.encode(&mut buf.as_mut_slice()).unwrap();
469        // Overwrite the length field (bytes 4..8) to make payload_size < 12
470        // length = 8 + payload_size, so length=19 → payload_size=11
471        let bad_len: u32 = 19;
472        buf[4..8].copy_from_slice(&bad_len.to_be_bytes());
473        assert!(matches!(
474            MessageView::parse(&buf[..]),
475            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
476                "SD message too short"
477            )))
478        ));
479    }
480
481    #[test]
482    fn parse_sd_wrong_interface_version_returns_error() {
483        let msg = make_sd_message();
484        let mut buf = [0u8; 64];
485        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
486        buf[13] = 0x02; // interface_version at byte 13
487        assert!(matches!(
488            MessageView::parse(&buf[..n]),
489            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
490                "SD interface version mismatch"
491            )))
492        ));
493    }
494
495    #[test]
496    fn parse_sd_wrong_message_type_returns_error() {
497        let msg = make_sd_message();
498        let mut buf = [0u8; 64];
499        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
500        buf[14] = 0x00; // Request instead of Notification
501        assert!(matches!(
502            MessageView::parse(&buf[..n]),
503            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
504                "SD message type mismatch"
505            )))
506        ));
507    }
508
509    #[test]
510    fn parse_sd_wrong_return_code_returns_error() {
511        let msg = make_sd_message();
512        let mut buf = [0u8; 64];
513        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
514        buf[15] = 0x01; // NotOk instead of Ok
515        assert!(matches!(
516            MessageView::parse(&buf[..n]),
517            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
518                "SD return code mismatch"
519            )))
520        ));
521    }
522
523    // --- MessageView accessors ---
524
525    #[test]
526    fn message_view_payload_bytes() {
527        let msg = make_sd_message();
528        let mut buf = [0u8; 64];
529        let n = msg.encode(&mut buf.as_mut_slice()).unwrap();
530        let view = MessageView::parse(&buf[..n]).unwrap();
531        assert_eq!(view.payload_bytes().len(), msg.header().payload_size());
532    }
533
534    #[test]
535    fn message_view_sd_header_on_non_sd_returns_error() {
536        // Build a non-SD message
537        let header = Header::new(
538            MessageId::new_from_service_and_method(0x1234, 0x0001),
539            0x0001,
540            0x01,
541            0x01,
542            crate::protocol::MessageTypeField::try_from(0x00).unwrap(),
543            ReturnCode::Ok,
544            0,
545        );
546        let mut buf = [0u8; 16];
547        header.encode(&mut buf.as_mut_slice()).unwrap();
548        let view = MessageView::parse(&buf).unwrap();
549        assert!(matches!(
550            view.sd_header(),
551            Err(Error::Sd(crate::protocol::sd::Error::InvalidMessage(
552                "Not an SD message"
553            )))
554        ));
555    }
556}