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