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    utils::*,
14    Basic, DynamicType, Error, Result, Signature,
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    let signature = value.signature();
125
126    to_writer_for_signature(writer, ctxt, signature, value)
127}
128
129/// Serialize `T` as a byte vector.
130///
131/// See [`Data::deserialize`] documentation for an example of how to use this function.
132pub fn to_bytes<T>(ctxt: Context, value: &T) -> Result<Data<'static, 'static>>
133where
134    T: ?Sized + Serialize + DynamicType,
135{
136    to_bytes_for_signature(ctxt, value.signature(), value)
137}
138
139/// Serialize `T` that has the given signature, to the given `writer`.
140///
141/// Use this function instead of [`to_writer`] if the value being serialized does not implement
142/// [`DynamicType`].
143///
144/// # Safety
145///
146/// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore
147/// the caller is responsible for not dropping the returned [`Written`] instance before the
148/// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while
149/// serialized data will still refer to them. Hence why this function is marked unsafe.
150///
151/// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and
152/// hence is safe to drop.
153///
154/// [`to_writer`]: fn.to_writer.html
155pub unsafe fn to_writer_for_signature<W, S, T>(
156    writer: &mut W,
157    ctxt: Context,
158    signature: S,
159    value: &T,
160) -> Result<Written>
161where
162    W: Write + Seek,
163    S: TryInto<Signature>,
164    S::Error: Into<Error>,
165    T: ?Sized + Serialize,
166{
167    let signature = signature.try_into().map_err(Into::into)?;
168
169    #[cfg(unix)]
170    let mut fds = FdList::Fds(vec![]);
171
172    let len = match ctxt.format() {
173        Format::DBus => {
174            let mut ser = DBusSerializer::<W>::new(
175                &signature,
176                writer,
177                #[cfg(unix)]
178                &mut fds,
179                ctxt,
180            )?;
181            value.serialize(&mut ser)?;
182            ser.0.bytes_written
183        }
184        #[cfg(feature = "gvariant")]
185        Format::GVariant => {
186            let mut ser = GVSerializer::<W>::new(
187                &signature,
188                writer,
189                #[cfg(unix)]
190                &mut fds,
191                ctxt,
192            )?;
193            value.serialize(&mut ser)?;
194            ser.0.bytes_written
195        }
196    };
197
198    let written = Written::new(len, ctxt);
199    #[cfg(unix)]
200    let written = match fds {
201        FdList::Fds(fds) => written.set_fds(fds),
202        FdList::Number(_) => unreachable!("`Fds::Number` is not possible here"),
203    };
204
205    Ok(written)
206}
207
208/// Serialize `T` that has the given signature, to a new byte vector.
209///
210/// Use this function instead of [`to_bytes`] if the value being serialized does not implement
211/// [`DynamicType`]. See [`from_slice_for_signature`] documentation for an example of how to use
212/// this function.
213///
214/// [`to_bytes`]: fn.to_bytes.html
215/// [`from_slice_for_signature`]: fn.from_slice_for_signature.html#examples
216pub fn to_bytes_for_signature<S, T>(
217    ctxt: Context,
218    signature: S,
219    value: &T,
220) -> Result<Data<'static, 'static>>
221where
222    S: TryInto<Signature>,
223    S::Error: Into<Error>,
224    T: ?Sized + Serialize,
225{
226    let mut cursor = std::io::Cursor::new(vec![]);
227    // SAFETY: We put the bytes and FDs in the `Data` to ensure that the data and FDs are only
228    // dropped together.
229    let ret = unsafe { to_writer_for_signature(&mut cursor, ctxt, signature, value) }?;
230    #[cfg(unix)]
231    let encoded = Data::new_fds(cursor.into_inner(), ctxt, ret.into_fds());
232    #[cfg(not(unix))]
233    let encoded = {
234        let _ = ret;
235        Data::new(cursor.into_inner(), ctxt)
236    };
237
238    Ok(encoded)
239}
240
241/// Context for all our serializers and provides shared functionality.
242pub(crate) struct SerializerCommon<'ser, W> {
243    pub(crate) ctxt: Context,
244    pub(crate) writer: &'ser mut W,
245    pub(crate) bytes_written: usize,
246    #[cfg(unix)]
247    pub(crate) fds: &'ser mut FdList,
248
249    pub(crate) signature: &'ser Signature,
250
251    pub(crate) value_sign: Option<Signature>,
252
253    pub(crate) container_depths: ContainerDepths,
254}
255
256#[cfg(unix)]
257pub(crate) enum FdList {
258    Fds(Vec<OwnedFd>),
259    Number(u32),
260}
261
262impl<W> SerializerCommon<'_, W>
263where
264    W: Write + Seek,
265{
266    #[cfg(unix)]
267    pub(crate) fn add_fd(&mut self, fd: std::os::fd::RawFd) -> Result<u32> {
268        use std::os::fd::{AsRawFd, BorrowedFd};
269
270        match self.fds {
271            FdList::Fds(fds) => {
272                if let Some(idx) = fds.iter().position(|x| x.as_raw_fd() == fd) {
273                    return Ok(idx as u32);
274                }
275                let idx = fds.len();
276                // Cloning implies dup and is unfortunate but we need to return owned fds
277                // and dup is not expensive (at least on Linux).
278                let fd = unsafe { BorrowedFd::borrow_raw(fd) }.try_clone_to_owned()?;
279                fds.push(fd);
280
281                Ok(idx as u32)
282            }
283            FdList::Number(n) => {
284                let idx = *n;
285                *n += 1;
286
287                Ok(idx)
288            }
289        }
290    }
291
292    pub(crate) fn add_padding(&mut self, alignment: usize) -> Result<usize> {
293        let padding = padding_for_n_bytes(self.abs_pos(), alignment);
294        if padding > 0 {
295            self.write_all(&[0u8; 8][..padding])?;
296        }
297
298        Ok(padding)
299    }
300
301    pub(crate) fn prep_serialize_basic<T>(&mut self) -> Result<()>
302    where
303        T: Basic,
304    {
305        self.add_padding(T::alignment(self.ctxt.format()))?;
306
307        Ok(())
308    }
309
310    fn abs_pos(&self) -> usize {
311        self.ctxt.position() + self.bytes_written
312    }
313}
314
315impl<W> Write for SerializerCommon<'_, W>
316where
317    W: Write + Seek,
318{
319    /// Write `buf` and increment internal bytes written counter.
320    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
321        self.writer.write(buf).inspect(|&n| {
322            self.bytes_written += n;
323        })
324    }
325
326    fn flush(&mut self) -> std::io::Result<()> {
327        self.writer.flush()
328    }
329}