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}