Skip to main content

rsomeip_bytes/
ser.rs

1//! Serialization according to the SOME/IP protocol.
2//!
3//! Provides the [`Serialize`] trait and several implementations for types of the standard library.
4
5use crate::{BufMut, Bytes, BytesMut};
6use alloc::{borrow::Cow, boxed::Box, rc::Rc, sync::Arc};
7
8/// Serialize according to the SOME/IP on-wire format.
9pub trait Serialize {
10    /// Serializes `self` into the given `buffer`.
11    ///
12    /// Returns the length of the serialized data.
13    ///
14    /// # Errors
15    ///
16    /// Returns a [`SerializeError`] if the serialization fails. Some data may still be written to
17    /// the buffer if an error occurs.
18    ///
19    /// # Panics
20    ///
21    /// Panics if the buffer doesn't have enough capacity for the serialized type. It's advised to
22    /// ensure that the buffer has at least [`Serialize::size`] capacity.
23    ///
24    /// # Examples
25    ///
26    /// ```rust
27    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
28    /// use rsomeip_bytes::Serialize;
29    ///
30    /// let mut buffer = [0_u8; 4];
31    /// let size = 0x1234_5678_u32.serialize(&mut buffer.as_mut_slice())?;
32    /// assert_eq!(size, 4);
33    /// assert_eq!(buffer.as_slice(), [0x12_u8, 0x34, 0x56, 0x78].as_slice());
34    /// # Ok(()) }
35    /// ```
36    ///
37    /// # Implementation notes
38    ///
39    /// Care should be taken so that the serialized data matches the interface definition and that
40    /// it's compatible with the SOME/IP on-wire format to prevent issues during deserialization.
41    fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
42    where
43        Buffer: BufMut + ?Sized;
44
45    /// Returns the size of `self` when serialized.
46    ///
47    /// Returns [`None`] if the size is out of bounds.
48    ///
49    /// This method is suitable for calculating the value of a length field or the capacity of a
50    /// buffer prior to serializing `self`.
51    ///
52    /// # Examples
53    ///
54    /// ```rust
55    /// use rsomeip_bytes::Serialize as _;
56    ///
57    /// assert_eq!(1_u8.size(), Some(1));
58    /// assert_eq!(1_u16.size(), Some(2));
59    /// assert_eq!(1_u32.size(), Some(4));
60    /// ```
61    ///
62    /// # Implementation notes
63    ///
64    /// Care should be taken so that the output of this method exactly matches the output of the
65    /// [`serialize`] method.
66    ///
67    /// [`serialize`]: [`Serialize::serialize`]
68    fn size(&self) -> Option<usize>;
69
70    /// Returns `self` serialized into [`Bytes`].
71    ///
72    /// # Errors
73    ///
74    /// Returns a [`SerializeError`] if the serialization fails.
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
80    /// use rsomeip_bytes::Serialize;
81    ///
82    /// let bytes = 0x1234_5678_u32.to_bytes()?;
83    /// assert_eq!(bytes, [0x12_u8, 0x34, 0x56, 0x78].as_slice());
84    /// # Ok(()) }
85    /// ```
86    fn to_bytes(&self) -> Result<Bytes, SerializeError> {
87        let Some(size) = self.size() else {
88            return Err(SerializeError::SizeOverflow);
89        };
90        let mut buffer = BytesMut::with_capacity(size);
91        self.serialize(&mut buffer).map(|_total| buffer.freeze())
92    }
93}
94
95/// Error when serializing data.
96#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
97#[non_exhaustive]
98pub enum SerializeError {
99    /// The target buffer doesn't have enough capacity for `self`.
100    #[error("buffer would overflow")]
101    BufferOverflow,
102    /// An invariant of the serialized type wasn't upheld.
103    #[error("invariant failed: {0}")]
104    InvariantFailed(Cow<'static, str>),
105    /// Length exceeds the capacity of the length field.
106    #[error("length exceeds capacity of length field")]
107    LengthOverflow,
108    /// Size of the serialized data doesn't match value returned by [`Serialize::size`].
109    #[error("size doesn't match expected value")]
110    SizeMismatch,
111    /// Size exceeds the capacity of [`usize`].
112    #[error("size exceeds capacity of `usize`")]
113    SizeOverflow,
114}
115
116impl SerializeError {
117    /// Creates a new [`SerializeError::InvariantFailed`] with the given `message`.
118    ///
119    /// # Examples
120    ///
121    /// ```rust
122    /// use rsomeip_bytes::SerializeError;
123    ///
124    /// // Can use static error messages.
125    /// let borrowed = SerializeError::invariant("generic error");
126    /// assert_eq!(borrowed.to_string(), "invariant failed: generic error");
127    ///
128    /// // Or dynamic error messages.
129    /// let owned = SerializeError::invariant(format!("specific error: {}", 42));
130    /// assert_eq!(owned.to_string(), "invariant failed: specific error: 42");
131    /// ```
132    #[inline]
133    #[must_use]
134    pub fn invariant(message: impl Into<Cow<'static, str>>) -> Self {
135        Self::InvariantFailed(message.into())
136    }
137}
138
139/// Implements [`Serialize`] for references and pointers using a forwarding call.
140macro_rules! impl_serialize_forward {
141    ($name:ty) => {
142        impl<T: Serialize + ?Sized> Serialize for $name {
143            fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
144            where
145                Buffer: BufMut + ?Sized,
146            {
147                (**self).serialize(buffer)
148            }
149
150            fn size(&self) -> Option<usize> {
151                (**self).size()
152            }
153        }
154    };
155}
156
157impl_serialize_forward!(&T);
158impl_serialize_forward!(&mut T);
159impl_serialize_forward!(Box<T>);
160impl_serialize_forward!(Rc<T>);
161impl_serialize_forward!(Arc<T>);
162
163/// Implements the [`Serialize`] trait for tuples.
164///
165/// Each method calls itself on each member of the tuple.
166macro_rules! impl_serialize_tuple {
167    ($( $name:ident )+) => {
168        #[expect(non_snake_case, reason = "generic parameters")]
169        #[expect(clippy::min_ident_chars, reason = "generic parameters")]
170        impl<$($name: Serialize),+> Serialize for ($($name,)+) {
171            fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
172            where
173                Buffer: BufMut + ?Sized
174            {
175                let &($(ref $name,)+) = self;
176                Ok(0_usize)
177                $(
178                    .and_then(|total| {
179                        $name.serialize(buffer).and_then(|size|
180                            total.checked_add(size).ok_or(SerializeError::SizeOverflow)
181                        )
182                    })
183                )+
184            }
185
186            fn size(&self) -> Option<usize> {
187                let &($(ref $name,)+) = self;
188                Some(0_usize)
189                $(
190                    .and_then(|total| {
191                        $name.size().and_then(|size| total.checked_add(size))
192                    })
193                )+
194            }
195        }
196    };
197}
198
199impl_serialize_tuple! { A }
200impl_serialize_tuple! { A B }
201impl_serialize_tuple! { A B C }
202impl_serialize_tuple! { A B C D }
203impl_serialize_tuple! { A B C D E }
204impl_serialize_tuple! { A B C D E F }
205impl_serialize_tuple! { A B C D E F G }
206impl_serialize_tuple! { A B C D E F G H }
207impl_serialize_tuple! { A B C D E F G H I }
208impl_serialize_tuple! { A B C D E F G H I J }
209impl_serialize_tuple! { A B C D E F G H I J K }
210impl_serialize_tuple! { A B C D E F G H I J K L }
211
212/// Implements the [`Serialize`] trait for basic types.
213///
214/// In order to improve performance, the [`serialize`] method doesn't do any checks before writing
215/// to the buffer. Depending on the type of the actual buffer, this might cause a panic if it
216/// doesn't have enough capacity.
217///
218/// [`serialize`]: [`Serialize::serialize`]
219macro_rules! impl_serialize_basic_type {
220    ($name:ty, $method:ident) => {
221        impl Serialize for $name {
222            /// Serializes `self` into the given `buffer`.
223            ///
224            /// Returns the length of the serialized data.
225            ///
226            /// # Panics
227            ///
228            /// Panics if `buffer` doesn't have enough capacity for `self`.
229            fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
230            where
231                Buffer: BufMut + ?Sized,
232            {
233                buffer.$method(*self);
234                Ok(size_of::<$name>())
235            }
236
237            /// Returns the size of `self` when serialized.
238            ///
239            /// Never returns [`None`].
240            fn size(&self) -> Option<usize> {
241                Some(size_of::<$name>())
242            }
243        }
244    };
245}
246impl_serialize_basic_type!(u8, put_u8);
247impl_serialize_basic_type!(u16, put_u16);
248impl_serialize_basic_type!(u32, put_u32);
249impl_serialize_basic_type!(u64, put_u64);
250impl_serialize_basic_type!(i8, put_i8);
251impl_serialize_basic_type!(i16, put_i16);
252impl_serialize_basic_type!(i32, put_i32);
253impl_serialize_basic_type!(i64, put_i64);
254impl_serialize_basic_type!(f32, put_f32);
255impl_serialize_basic_type!(f64, put_f64);
256
257impl Serialize for bool {
258    fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
259    where
260        Buffer: BufMut + ?Sized,
261    {
262        if *self {
263            buffer.put_u8(1);
264        } else {
265            buffer.put_u8(0);
266        }
267        Ok(size_of::<u8>())
268    }
269
270    fn size(&self) -> Option<usize> {
271        Some(1)
272    }
273}
274
275impl<T: Serialize, const N: usize> Serialize for [T; N] {
276    fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
277    where
278        Buffer: BufMut + ?Sized,
279    {
280        let mut iterator = self.iter();
281        iterator.try_fold(0_usize, |acc, elem| {
282            elem.serialize(buffer)
283                .and_then(|elem| acc.checked_add(elem).ok_or(SerializeError::SizeOverflow))
284        })
285    }
286
287    fn size(&self) -> Option<usize> {
288        let mut iterator = self.iter();
289        iterator.try_fold(0_usize, |acc, elem| {
290            elem.size().and_then(|elem| acc.checked_add(elem))
291        })
292    }
293}
294
295impl Serialize for Bytes {
296    fn serialize<Buffer>(&self, buffer: &mut Buffer) -> Result<usize, SerializeError>
297    where
298        Buffer: BufMut + ?Sized,
299    {
300        buffer.put_slice(self);
301        Ok(self.len())
302    }
303
304    fn size(&self) -> Option<usize> {
305        Some(self.len())
306    }
307}
308
309/// Wrapper for serializing a value with a pair of functions.
310///
311/// # Examples
312///
313/// ```rust
314/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
315/// use rsomeip_bytes::{SerializeWithFn, BufMut, Serialize as _};
316///
317/// // Type that needs custom serialization.
318/// struct Foo {
319///     bar: u8,
320///     baz: u16,
321/// }
322///
323/// let wrapper = SerializeWithFn::new(
324///         &Foo{ bar: 1_u8, baz: 2_u16 },
325///         |value, buffer| (&value.bar, &value.baz).serialize(buffer),
326///         |value| (&value.bar, &value.baz).size(),
327///     );
328///
329/// let bytes = wrapper.to_bytes()?;
330/// assert_eq!(&bytes, [1_u8, 0, 2].as_slice());
331/// # Ok(()) }
332/// ```
333pub struct SerializeWithFn<'value, Value, SerializeFn, SizeFn> {
334    /// Value to serialize.
335    value: &'value Value,
336    /// Serialization function. Equivalent to [`Serialize::serialize`].
337    serialize: SerializeFn,
338    /// Size function. Equivalent to [`Serialize::size`].
339    size: SizeFn,
340}
341
342impl<'value, Value, SerializeFn, SizeFn> SerializeWithFn<'value, Value, SerializeFn, SizeFn>
343where
344    for<'any> SerializeFn: Fn(&Value, &mut dyn BufMut) -> Result<usize, SerializeError>,
345    for<'any> SizeFn: Fn(&Value) -> Option<usize>,
346{
347    /// Creates a new [`SerializeWithFn`].
348    #[inline]
349    #[must_use]
350    pub const fn new(value: &'value Value, serialize: SerializeFn, size: SizeFn) -> Self {
351        Self {
352            value,
353            serialize,
354            size,
355        }
356    }
357}
358
359impl<Value, SerializeFn, SizeFn> Serialize for SerializeWithFn<'_, Value, SerializeFn, SizeFn>
360where
361    for<'any> SerializeFn: Fn(&Value, &mut dyn BufMut) -> Result<usize, SerializeError>,
362    for<'any> SizeFn: Fn(&Value) -> Option<usize>,
363{
364    fn serialize<Buffer>(&self, mut buffer: &mut Buffer) -> Result<usize, SerializeError>
365    where
366        Buffer: BufMut + ?Sized,
367    {
368        (self.serialize)(self.value, &mut buffer)
369    }
370
371    fn size(&self) -> Option<usize> {
372        (self.size)(self.value)
373    }
374}
375
376#[cfg(test)]
377#[expect(clippy::inline_modules, reason = "rust-clippy#17342")]
378mod tests {
379    use super::*;
380
381    macro_rules! test_serialize_basic_type {
382        ($t:ty, $name:ident) => {
383            #[test]
384            fn $name() {
385                let mut buffer = BytesMut::with_capacity(size_of::<$t>());
386                let result = <$t>::MAX.serialize(&mut buffer);
387                assert_eq!(result, Ok(size_of::<$t>()));
388                assert_eq!(result.ok(), <$t>::MAX.size());
389                assert_eq!(buffer.freeze(), <$t>::MAX.to_be_bytes().as_slice());
390            }
391        };
392    }
393
394    test_serialize_basic_type!(u8, serialize_u8);
395    test_serialize_basic_type!(u16, serialize_u16);
396    test_serialize_basic_type!(u32, serialize_u32);
397    test_serialize_basic_type!(u64, serialize_u64);
398    test_serialize_basic_type!(i8, serialize_i8);
399    test_serialize_basic_type!(i16, serialize_i16);
400    test_serialize_basic_type!(i32, serialize_i32);
401    test_serialize_basic_type!(i64, serialize_i64);
402    test_serialize_basic_type!(f32, serialize_f32);
403    test_serialize_basic_type!(f64, serialize_f64);
404
405    #[test]
406    fn serialize_bool() {
407        let mut buffer = BytesMut::with_capacity(2);
408        for value in [true, false] {
409            let size = value
410                .serialize(&mut buffer)
411                .expect("should serialize the bool");
412            assert_eq!(size, 1);
413            assert_eq!(value.size(), Some(1));
414        }
415        assert_eq!(buffer.freeze(), [1_u8, 0_u8].as_slice());
416    }
417
418    #[test]
419    fn serialize_array() {
420        let mut buffer = BytesMut::with_capacity(2);
421        let array = [1_u8, 2_u8];
422        let size = array
423            .serialize(&mut buffer)
424            .expect("should serialize the array");
425        assert_eq!(size, 2);
426        assert_eq!(Some(size), array.size());
427        assert_eq!(buffer.freeze(), [1_u8, 2_u8].as_slice());
428    }
429
430    #[test]
431    fn serialize_tuple() {
432        let mut buffer = BytesMut::with_capacity(2);
433        let tuple = (1_u8, 2_u8);
434        let size = tuple
435            .serialize(&mut buffer)
436            .expect("should serialize the tuple");
437        assert_eq!(size, 2);
438        assert_eq!(Some(size), tuple.size());
439        assert_eq!(buffer.freeze(), [1_u8, 2_u8].as_slice());
440    }
441
442    #[test]
443    fn serialize_bytes() {
444        let mut buffer = BytesMut::with_capacity(2);
445        let bytes = Bytes::copy_from_slice(&[1_u8, 2_u8]);
446        let size = bytes
447            .serialize(&mut buffer)
448            .expect("should serialize the buffer");
449        assert_eq!(size, 2);
450        assert_eq!(Some(size), bytes.size());
451        assert_eq!(buffer.freeze(), [1_u8, 2].as_slice());
452    }
453
454    #[test]
455    fn serialize_box() {
456        let mut buffer = BytesMut::with_capacity(2);
457        let value = Box::new(0x0102_u16);
458        let size = value
459            .serialize(&mut buffer)
460            .expect("should serialize the buffer");
461        assert_eq!(size, 2);
462        assert_eq!(Some(size), value.size());
463        assert_eq!(buffer.freeze(), [1_u8, 2].as_slice());
464    }
465}