rustbus/wire/marshal/
traits.rs

1//! Marshal trait and implementations for the basic types
2use crate::wire::marshal::MarshalContext;
3
4mod base;
5mod container;
6pub use base::*;
7pub use container::*;
8
9/// The Marshal trait allows to push any type onto an message_builder::OutMessage as a parameter.
10/// There are some useful implementations here for slices and hashmaps which map to arrays and dicts in the dbus message.
11///
12/// The way dbus structs are represented is with rust tuples. This lib provides Marshal impls for tuples with up to 5 elements.
13/// If you need more you can just copy the impl and extend it to how many different entries you need.
14///
15/// There is a crate (rustbus_derive) for deriving Marshal impls with #[derive(rustbus_derive::Marshal)]. This should work for most of your needs.
16/// You can of course derive Signature as well.
17///
18/// If there are special needs, you can implement Marshal for your own structs:
19/// ```rust
20/// struct MyStruct {
21///     x: u64,
22///     y: String,
23/// }
24///
25/// use rustbus::ByteOrder;
26/// use rustbus::signature;
27/// use rustbus::wire::util;
28/// use rustbus::Marshal;
29/// use rustbus::wire::marshal::MarshalContext;
30/// use rustbus::wire::marshal::traits::SignatureBuffer;
31/// use rustbus::Signature;
32/// impl Signature for &MyStruct {
33///     fn signature() -> signature::Type {
34///         signature::Type::Container(signature::Container::Struct(signature::StructTypes::new(vec![
35///             u64::signature(),
36///             String::signature(),
37///         ]).unwrap()))
38///     }
39///
40///     fn alignment() -> usize {
41///         8
42///     }
43///     fn sig_str(s_buf: &mut SignatureBuffer) {
44///         s_buf.push_static("(ts)");
45///     }
46///     fn has_sig(sig: &str) -> bool {
47///         sig == "(ts)"
48///     }
49/// }    
50/// impl Marshal for &MyStruct {
51///     fn marshal(
52///         &self,
53///         ctx: &mut MarshalContext,
54///     ) -> Result<(), rustbus::wire::errors::MarshalError> {
55///         // always align to 8 at the start of a struct!
56///         ctx.align_to(8);
57///         self.x.marshal(ctx)?;
58///         self.y.marshal(ctx)?;
59///         Ok(())
60///     }
61/// }
62/// ```
63/// # Implementing for your own structs
64/// There are some rules you need to follow, or the messages will be malformed:
65/// 1. Structs need to be aligned to 8 bytes. Use `ctx.align_to(8);` to do that. If your type is marshalled as a primitive type
66///     you still need to align to that types alignment.
67/// 1. If you write your own dict type, you need to align every key-value pair at 8 bytes like a struct
68/// 1. The signature needs to be correct, or the message will be malformed
69/// 1. The alignment must report the correct number. This does not need to be a constant like in the example, but it needs to be consistent with the type
70///     the signature() function returns. If you are not sure, just use Self::signature().get_alignment().
71pub trait Marshal: Signature {
72    fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), crate::wire::errors::MarshalError>;
73    fn marshal_as_variant(
74        &self,
75        ctx: &mut MarshalContext,
76    ) -> Result<(), crate::wire::errors::MarshalError> {
77        let mut sig = SignatureBuffer::new();
78        Self::sig_str(&mut sig);
79        if sig.len() > 255 {
80            let sig_err = crate::signature::Error::SignatureTooLong;
81            return Err(sig_err.into());
82        }
83        debug_assert!(crate::params::validation::validate_signature(&sig).is_ok());
84        crate::wire::util::write_signature(&sig, ctx.buf);
85        self.marshal(ctx)
86    }
87}
88
89/// `SignatureBuffer` is used to store static or dynamic signatures and avoid allocations if possible.
90/// It is a wrapper around Cow.
91#[derive(Debug)]
92pub struct SignatureBuffer(Cow<'static, str>);
93
94impl SignatureBuffer {
95    #[inline]
96    pub fn new() -> Self {
97        Self(Cow::Borrowed(""))
98    }
99    /// Pushes a `&str` into the signature buffer.
100    ///
101    /// Avoids an allocation if the `self` was empty and was not allocated already,
102    /// by storing the `&'static str` inside a `Cow::Borrowed` variant.
103    #[inline]
104    pub fn push_static(&mut self, sig: &'static str) {
105        match &mut self.0 {
106            Cow::Borrowed("") => self.0 = Cow::Borrowed(sig),
107            Cow::Owned(s) if s.capacity() == 0 => self.0 = Cow::Borrowed(sig),
108            cow => cow.to_mut().push_str(sig),
109        }
110    }
111
112    /// Pushes a `&str` into the signature buffer.
113    ///
114    /// If `sig` has a `'static` lifetime then [`SignatureBuffer::push_static`] should almost always be used
115    /// instead of this, because it can provide a performance benefit by avoiding allocation.
116    #[inline]
117    pub fn push_str(&mut self, sig: &str) {
118        self.0.to_mut().push_str(sig);
119    }
120
121    /// Return a `&mut String` which can be used to modify the signature.
122    ///
123    /// Internally this is just a call to `Cow::to_mut`.
124    #[inline]
125    pub fn to_string_mut(&mut self) -> &mut String {
126        self.0.to_mut()
127    }
128
129    /// Clear the signature.
130    ///
131    /// If an allocation was already made it is retained for future use.
132    /// If you wish to deallocate when clearing, then simply use [`SignatureBuffer::new`].
133    #[inline]
134    pub fn clear(&mut self) {
135        match &mut self.0 {
136            Cow::Borrowed(_) => *self = Self::new(),
137            Cow::Owned(s) => s.clear(),
138        }
139    }
140    #[inline]
141    pub fn from_string(sig: String) -> Self {
142        Self(Cow::Owned(sig))
143    }
144    #[inline]
145    pub fn as_str(&self) -> &str {
146        &self.0
147    }
148
149    pub fn truncate(&mut self, new_len: usize) -> Result<(), crate::params::validation::Error> {
150        if new_len > 0 {
151            crate::params::validate_signature(&self.0.as_ref()[0..new_len])?;
152        }
153        self.0.to_mut().truncate(new_len);
154        Ok(())
155    }
156}
157impl std::ops::Deref for SignatureBuffer {
158    type Target = str;
159    #[inline]
160    fn deref(&self) -> &Self::Target {
161        self.as_str()
162    }
163}
164impl AsRef<str> for SignatureBuffer {
165    #[inline]
166    fn as_ref(&self) -> &str {
167        self.as_str()
168    }
169}
170impl Default for SignatureBuffer {
171    #[inline]
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177use std::borrow::Cow;
178pub trait Signature {
179    fn signature() -> crate::signature::Type;
180    fn alignment() -> usize;
181    /// If this returns `true`,
182    /// it indicates that for implementing type `T`,
183    /// Rust's `[T]` is identical to DBus's array format
184    /// and can be copied into a message after aligning the first element.
185    ///
186    /// The default implementation is `false` but can be overridden for a performance gain
187    /// if it is valid.
188    ///
189    /// # Safety
190    /// Calls to this function should always be safe. Implementors should respect this property.
191    /// The reason this method is `unsafe` is to indicate to people implementing `Signature` that
192    /// overriding it has the potential to induce unsound behavior
193    /// if the following rules aren't followed:
194    /// 1. The type `T` implementing `Signature` must be `Copy`.
195    /// 2. The size of `T` must be **equivalent** to it's DBus alignment (see [here]).
196    /// 3. Every possible bit-pattern must represent a valid instance of `T`.
197    ///    For example `std::num::NonZeroU32` does not meet this requirement `0` is invalid.
198    /// 4. The type should not contain an Fd receieved from the message.
199    ///    When implementing `Unmarshal` the type should only dependent the `'buf` lifetime.
200    ///    It should never require the use of `'fds`.
201    ///
202    /// # Notes
203    /// * This method exists because of limitiation with Rust type system.
204    ///   Should `#[feature(specialization)]` ever become stablized this will hopefully be unnecessary.
205    /// * This method should use the `ByteOrder` to check if it matches native order before returning `true`.
206    ///   `ByteOrder::NATIVE` can be used to detect the native order.
207    ///
208    /// [here]: https://dbus.freedesktop.org/doc/dbus-specification.html#idm702
209    #[inline]
210    unsafe fn valid_slice(_bo: crate::ByteOrder) -> bool {
211        false
212    }
213    /// Appends the signature of the type to the `SignatureBuffer`.
214    ///
215    /// By using `SignatureBuffer`, implementations of this method can avoid unnecessary allocations
216    /// by only allocating if a signature is dynamic.
217    ///
218    /// The default implementation of `sig_str` can be pretty slow.
219    /// If type, that `Signature` is being implemented for, has a static (unchanging) signature
220    /// then overriding this method can have a significant performance benefit when marshal/unmarshalling
221    /// the type inside variants.
222    fn sig_str(s_buf: &mut SignatureBuffer) {
223        let s_buf = s_buf.to_string_mut();
224        let typ = Self::signature();
225        typ.to_str(s_buf);
226    }
227
228    /// Check if this type fulfills this signature. This may expect to only be called with valid signatures.
229    /// But it might be called with the wrong signature. This means for example you must check the length before indexing.
230    ///
231    /// The default impl uses Signature::sig_str and compares it to the given signature. The same performance
232    /// implications as for Signature::sig_str apply here.
233    fn has_sig(sig: &str) -> bool {
234        let mut s_buf = SignatureBuffer::new();
235        Self::sig_str(&mut s_buf);
236        sig == s_buf.as_str()
237    }
238}
239
240impl<S: Signature> Signature for &S {
241    fn signature() -> crate::signature::Type {
242        S::signature()
243    }
244    fn alignment() -> usize {
245        S::alignment()
246    }
247    fn sig_str(s_buf: &mut SignatureBuffer) {
248        S::sig_str(s_buf)
249    }
250    fn has_sig(sig: &str) -> bool {
251        S::has_sig(sig)
252    }
253}
254
255impl<P: Marshal> Marshal for &P {
256    fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), crate::wire::errors::MarshalError> {
257        (*self).marshal(ctx)
258    }
259}
260
261#[cfg(test)]
262mod test {
263    use crate::wire::marshal::MarshalContext;
264    use crate::wire::ObjectPath;
265    use crate::wire::SignatureWrapper;
266
267    #[test]
268    fn test_trait_signature_creation() {
269        let mut msg = crate::message_builder::MarshalledMessage::new();
270        let body = &mut msg.body;
271
272        body.push_param("a").unwrap();
273        body.push_param(ObjectPath::new("/a/b").unwrap()).unwrap();
274        body.push_param(SignatureWrapper::new("(a{su})").unwrap())
275            .unwrap();
276
277        let fd = crate::wire::UnixFd::new(nix::unistd::dup(1).unwrap());
278        body.push_param(&fd).unwrap();
279        body.push_param(true).unwrap();
280        body.push_param(0u8).unwrap();
281        body.push_param(0u16).unwrap();
282        body.push_param(0u32).unwrap();
283        body.push_param(0u64).unwrap();
284        body.push_param(0i16).unwrap();
285        body.push_param(0i32).unwrap();
286        body.push_param(0i64).unwrap();
287        body.push_param(&[0u8][..]).unwrap();
288
289        let map: std::collections::HashMap<String, (u64, u32, u16, u8)> =
290            std::collections::HashMap::new();
291        body.push_param(&map).unwrap();
292
293        assert_eq!("soghbyqutnixaya{s(tuqy)}", msg.get_sig());
294    }
295
296    #[test]
297    fn test_empty_array_padding() {
298        use crate::wire::marshal::container::marshal_container_param;
299
300        let mut msg = crate::message_builder::MarshalledMessage::new();
301        let body = &mut msg.body;
302        let empty = vec![0u64; 0];
303        body.push_param(&empty[..]).unwrap();
304
305        let empty = crate::params::Container::make_array_with_sig(
306            crate::signature::Type::Base(crate::signature::Base::Uint64),
307            empty.into_iter(),
308        )
309        .unwrap();
310
311        let mut fds = Vec::new();
312        let mut buf = Vec::new();
313        let mut ctx = MarshalContext {
314            fds: &mut fds,
315            buf: &mut buf,
316            byteorder: crate::ByteOrder::LittleEndian,
317        };
318
319        marshal_container_param(&empty, &mut ctx).unwrap();
320
321        // 0 length and padded to 8 bytes even if there are no elements
322        assert_eq!(msg.get_buf(), &[0, 0, 0, 0, 0, 0, 0, 0]);
323        assert_eq!(buf.as_slice(), &[0, 0, 0, 0, 0, 0, 0, 0]);
324    }
325
326    #[test]
327    fn test_variant_marshalling() {
328        let mut msg = crate::message_builder::MarshalledMessage::new();
329        let body = &mut msg.body;
330
331        let original = (100u64, "ABCD", true);
332        body.push_variant(original).unwrap();
333
334        assert_eq!(
335            msg.get_buf(),
336            // signature ++ padding ++ u64 ++ string ++ padding ++ boolean
337            &[
338                5, b'(', b't', b's', b'b', b')', b'\0', 0, 100, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
339                b'A', b'B', b'C', b'D', b'\0', 0, 0, 0, 1, 0, 0, 0
340            ]
341        )
342    }
343}