zvariant/
ser.rs

1use serde::Serialize;
2use std::io::{Seek, Write};
3
4#[cfg(unix)]
5use std::os::fd::OwnedFd;
6
7#[cfg(feature = "gvariant")]
8use crate::gvariant::Serializer as GVSerializer;
9use crate::{
10    container_depths::ContainerDepths,
11    dbus::Serializer as DBusSerializer,
12    serialized::{Context, Data, Format, Size, Written},
13    signature_parser::SignatureParser,
14    utils::*,
15    Basic, DynamicType, Error, Result, Signature, WriteBytes,
16};
17
18struct NullWriteSeek;
19
20impl Write for NullWriteSeek {
21    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
22        Ok(buf.len())
23    }
24
25    fn flush(&mut self) -> std::io::Result<()> {
26        Ok(())
27    }
28}
29
30impl Seek for NullWriteSeek {
31    fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
32        Ok(u64::MAX) // should never read the return value!
33    }
34}
35
36/// Calculate the serialized size of `T`.
37///
38/// # Examples
39///
40/// ```
41/// use zvariant::{serialized::Context, serialized_size, LE};
42///
43/// let ctxt = Context::new_dbus(LE, 0);
44/// let len = serialized_size(ctxt, "hello world").unwrap();
45/// assert_eq!(*len, 16);
46///
47/// let len = serialized_size(ctxt, &("hello world!", 42_u64)).unwrap();
48/// assert_eq!(*len, 32);
49/// ```
50pub fn serialized_size<T>(ctxt: Context, value: &T) -> Result<Size>
51where
52    T: ?Sized + Serialize + DynamicType,
53{
54    let mut null = NullWriteSeek;
55    let signature = value.dynamic_signature();
56    #[cfg(unix)]
57    let mut fds = FdList::Number(0);
58
59    let len = match ctxt.format() {
60        Format::DBus => {
61            let mut ser = DBusSerializer::<NullWriteSeek>::new(
62                signature,
63                &mut null,
64                #[cfg(unix)]
65                &mut fds,
66                ctxt,
67            )?;
68            value.serialize(&mut ser)?;
69            ser.0.bytes_written
70        }
71        #[cfg(feature = "gvariant")]
72        Format::GVariant => {
73            let mut ser = GVSerializer::<NullWriteSeek>::new(
74                signature,
75                &mut null,
76                #[cfg(unix)]
77                &mut fds,
78                ctxt,
79            )?;
80            value.serialize(&mut ser)?;
81            ser.0.bytes_written
82        }
83    };
84
85    let size = Size::new(len, ctxt);
86    #[cfg(unix)]
87    let size = match fds {
88        FdList::Number(n) => size.set_num_fds(n),
89        FdList::Fds(_) => unreachable!("`Fds::Fds` is not possible here"),
90    };
91
92    Ok(size)
93}
94
95/// Serialize `T` to the given `writer`.
96///
97/// # Examples
98///
99/// ```
100/// use zvariant::{serialized::{Context, Data}, to_writer, LE};
101///
102/// let ctxt = Context::new_dbus(LE, 0);
103/// let mut cursor = std::io::Cursor::new(vec![]);
104/// // SAFETY: No FDs are being serialized here so its completely safe.
105/// unsafe { to_writer(&mut cursor, ctxt, &42u32) }.unwrap();
106/// let encoded = Data::new(cursor.get_ref(), ctxt);
107/// let value: u32 = encoded.deserialize().unwrap().0;
108/// assert_eq!(value, 42);
109/// ```
110///
111/// # Safety
112///
113/// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore
114/// the caller is responsible for not dropping the returned [`Written`] instance before the
115/// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while
116/// serialized data will still refer to them. Hence why this function is marked unsafe.
117///
118/// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and
119/// hence is safe to drop.
120///
121/// [`to_writer_fds`]: fn.to_writer_fds.html
122pub unsafe fn to_writer<W, T>(writer: &mut W, ctxt: Context, value: &T) -> Result<Written>
123where
124    W: Write + Seek,
125    T: ?Sized + Serialize + DynamicType,
126{
127    let signature = value.dynamic_signature();
128
129    to_writer_for_signature(writer, ctxt, &signature, value)
130}
131
132/// Serialize `T` as a byte vector.
133///
134/// See [`Data::deserialize`] documentation for an example of how to use this function.
135pub fn to_bytes<T>(ctxt: Context, value: &T) -> Result<Data<'static, 'static>>
136where
137    T: ?Sized + Serialize + DynamicType,
138{
139    to_bytes_for_signature(ctxt, value.dynamic_signature(), value)
140}
141
142/// Serialize `T` that has the given signature, to the given `writer`.
143///
144/// Use this function instead of [`to_writer`] if the value being serialized does not implement
145/// [`DynamicType`].
146///
147/// # Safety
148///
149/// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore
150/// the caller is responsible for not dropping the returned [`Written`] instance before the
151/// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while
152/// serialized data will still refer to them. Hence why this function is marked unsafe.
153///
154/// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and
155/// hence is safe to drop.
156///
157/// [`to_writer`]: fn.to_writer.html
158pub unsafe fn to_writer_for_signature<'s, W, S, T>(
159    writer: &mut W,
160    ctxt: Context,
161    signature: S,
162    value: &T,
163) -> Result<Written>
164where
165    W: Write + Seek,
166    S: TryInto<Signature<'s>>,
167    S::Error: Into<Error>,
168    T: ?Sized + Serialize,
169{
170    #[cfg(unix)]
171    let mut fds = FdList::Fds(vec![]);
172
173    let len = match ctxt.format() {
174        Format::DBus => {
175            let mut ser = DBusSerializer::<W>::new(
176                signature,
177                writer,
178                #[cfg(unix)]
179                &mut fds,
180                ctxt,
181            )?;
182            value.serialize(&mut ser)?;
183            ser.0.bytes_written
184        }
185        #[cfg(feature = "gvariant")]
186        Format::GVariant => {
187            let mut ser = GVSerializer::<W>::new(
188                signature,
189                writer,
190                #[cfg(unix)]
191                &mut fds,
192                ctxt,
193            )?;
194            value.serialize(&mut ser)?;
195            ser.0.bytes_written
196        }
197    };
198
199    let written = Written::new(len, ctxt);
200    #[cfg(unix)]
201    let written = match fds {
202        FdList::Fds(fds) => written.set_fds(fds),
203        FdList::Number(_) => unreachable!("`Fds::Number` is not possible here"),
204    };
205
206    Ok(written)
207}
208
209/// Serialize `T` that has the given signature, to a new byte vector.
210///
211/// Use this function instead of [`to_bytes`] if the value being serialized does not implement
212/// [`DynamicType`]. See [`from_slice_for_signature`] documentation for an example of how to use
213/// this function.
214///
215/// [`to_bytes`]: fn.to_bytes.html
216/// [`from_slice_for_signature`]: fn.from_slice_for_signature.html#examples
217pub fn to_bytes_for_signature<'s, S, T>(
218    ctxt: Context,
219    signature: S,
220    value: &T,
221) -> Result<Data<'static, 'static>>
222where
223    S: TryInto<Signature<'s>>,
224    S::Error: Into<Error>,
225    T: ?Sized + Serialize,
226{
227    let mut cursor = std::io::Cursor::new(vec![]);
228    // SAFETY: We put the bytes and FDs in the `Data` to ensure that the data and FDs are only
229    // dropped together.
230    let ret = unsafe { to_writer_for_signature(&mut cursor, ctxt, signature, value) }?;
231    #[cfg(unix)]
232    let encoded = Data::new_fds(cursor.into_inner(), ctxt, ret.into_fds());
233    #[cfg(not(unix))]
234    let encoded = {
235        let _ = ret;
236        Data::new(cursor.into_inner(), ctxt)
237    };
238
239    Ok(encoded)
240}
241
242/// Context for all our serializers and provides shared functionality.
243pub(crate) struct SerializerCommon<'ser, 'sig, W> {
244    pub(crate) ctxt: Context,
245    pub(crate) writer: &'ser mut W,
246    pub(crate) bytes_written: usize,
247    #[cfg(unix)]
248    pub(crate) fds: &'ser mut FdList,
249
250    pub(crate) sig_parser: SignatureParser<'sig>,
251
252    pub(crate) value_sign: Option<Signature<'static>>,
253
254    pub(crate) container_depths: ContainerDepths,
255}
256
257#[cfg(unix)]
258pub(crate) enum FdList {
259    Fds(Vec<OwnedFd>),
260    Number(u32),
261}
262
263impl<'ser, 'sig, W> SerializerCommon<'ser, 'sig, W>
264where
265    W: Write + Seek,
266{
267    #[cfg(unix)]
268    pub(crate) fn add_fd(&mut self, fd: std::os::fd::RawFd) -> Result<u32> {
269        use std::os::fd::{AsRawFd, BorrowedFd};
270
271        match self.fds {
272            FdList::Fds(fds) => {
273                if let Some(idx) = fds.iter().position(|x| x.as_raw_fd() == fd) {
274                    return Ok(idx as u32);
275                }
276                let idx = fds.len();
277                // Cloning implies dup and is unfortunate but we need to return owned fds
278                // and dup is not expensive (at least on Linux).
279                let fd = unsafe { BorrowedFd::borrow_raw(fd) }.try_clone_to_owned()?;
280                fds.push(fd);
281
282                Ok(idx as u32)
283            }
284            FdList::Number(n) => {
285                let idx = *n;
286                *n += 1;
287
288                Ok(idx)
289            }
290        }
291    }
292
293    pub(crate) fn add_padding(&mut self, alignment: usize) -> Result<usize> {
294        let padding = padding_for_n_bytes(self.abs_pos(), alignment);
295        if padding > 0 {
296            let byte = [0_u8; 1];
297            for _ in 0..padding {
298                self.write_all(&byte)
299                    .map_err(|e| Error::InputOutput(e.into()))?;
300            }
301        }
302
303        Ok(padding)
304    }
305
306    pub(crate) fn prep_serialize_basic<T>(&mut self) -> Result<()>
307    where
308        T: Basic,
309    {
310        self.sig_parser.skip_char()?;
311        self.add_padding(T::alignment(self.ctxt.format()))?;
312
313        Ok(())
314    }
315
316    /// This starts the enum serialization.
317    ///
318    /// It's up to the caller to do the rest: serialize the variant payload and skip the `).
319    pub(crate) fn prep_serialize_enum_variant(&mut self, variant_index: u32) -> Result<()> {
320        // Encode enum variants as a struct with first field as variant index
321        let signature = self.sig_parser.next_signature()?;
322        if self.sig_parser.next_char()? != STRUCT_SIG_START_CHAR {
323            return Err(Error::SignatureMismatch(
324                signature.to_owned(),
325                format!("expected `{STRUCT_SIG_START_CHAR}`"),
326            ));
327        }
328
329        let alignment = alignment_for_signature(&signature, self.ctxt.format())?;
330        self.add_padding(alignment)?;
331
332        // Now serialize the veriant index.
333        self.write_u32(self.ctxt.endian(), variant_index)
334            .map_err(|e| Error::InputOutput(e.into()))?;
335
336        // Skip the `(`, `u`.
337        self.sig_parser.skip_chars(2)?;
338
339        Ok(())
340    }
341
342    fn abs_pos(&self) -> usize {
343        self.ctxt.position() + self.bytes_written
344    }
345}
346
347impl<'ser, 'sig, W> Write for SerializerCommon<'ser, 'sig, W>
348where
349    W: Write + Seek,
350{
351    /// Write `buf` and increment internal bytes written counter.
352    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
353        self.writer.write(buf).map(|n| {
354            self.bytes_written += n;
355
356            n
357        })
358    }
359
360    fn flush(&mut self) -> std::io::Result<()> {
361        self.writer.flush()
362    }
363}