Skip to main content

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")]
8#[allow(deprecated)]
9use crate::gvariant::Serializer as GVSerializer;
10use crate::{
11    Basic, DynamicType, Error, Result, Signature,
12    container_depths::ContainerDepths,
13    dbus::Serializer as DBusSerializer,
14    serialized::{Context, Data, Format, Size, Written},
15    utils::*,
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.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        #[allow(deprecated)]
73        Format::GVariant => {
74            let mut ser = GVSerializer::<NullWriteSeek>::new(
75                &signature,
76                &mut null,
77                #[cfg(unix)]
78                &mut fds,
79                ctxt,
80            )?;
81            value.serialize(&mut ser)?;
82            ser.0.bytes_written
83        }
84        // `Format` can still have a `GVariant` variant here even with `zvariant`'s own
85        // `gvariant` feature disabled: if some other crate in the dependency graph (e.g.
86        // `zgvariant`) enables `zvariant_utils/gvariant`, Cargo feature unification adds the
87        // variant to this build regardless. `zvariant`'s own `#[cfg(feature = ...)]` can't
88        // detect that (Cargo features don't propagate that way), so the variant can't be
89        // named explicitly here without breaking the common case where it doesn't exist at
90        // all. Fall back to a wildcard instead: it's unreachable whenever `gvariant` is
91        // enabled (the two arms above are then exhaustive) or the variant doesn't exist.
92        #[cfg(not(feature = "gvariant"))]
93        #[allow(unreachable_patterns)]
94        _ => {
95            return Err(Error::Message(
96                "GVariant support has moved to the `zgvariant` crate; enable `zvariant`'s \
97                 deprecated `gvariant` feature only for legacy compatibility"
98                    .to_owned(),
99            ));
100        }
101    };
102
103    let size = Size::new(len, ctxt);
104    #[cfg(unix)]
105    let size = match fds {
106        FdList::Number(n) => size.set_num_fds(n),
107        FdList::Fds(_) => unreachable!("`Fds::Fds` is not possible here"),
108    };
109
110    Ok(size)
111}
112
113/// Serialize `T` to the given `writer`.
114///
115/// # Examples
116///
117/// ```
118/// use zvariant::{serialized::{Context, Data}, to_writer, LE};
119///
120/// let ctxt = Context::new_dbus(LE, 0);
121/// let mut cursor = std::io::Cursor::new(vec![]);
122/// // SAFETY: No FDs are being serialized here so its completely safe.
123/// unsafe { to_writer(&mut cursor, ctxt, &42u32) }.unwrap();
124/// let encoded = Data::new(cursor.get_ref(), ctxt);
125/// let value: u32 = encoded.deserialize().unwrap().0;
126/// assert_eq!(value, 42);
127/// ```
128///
129/// # Safety
130///
131/// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore
132/// the caller is responsible for not dropping the returned [`Written`] instance before the
133/// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while
134/// serialized data will still refer to them. Hence why this function is marked unsafe.
135///
136/// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and
137/// hence is safe to drop.
138pub unsafe fn to_writer<W, T>(writer: &mut W, ctxt: Context, value: &T) -> Result<Written>
139where
140    W: Write + Seek,
141    T: ?Sized + Serialize + DynamicType,
142{
143    unsafe {
144        let signature = value.signature();
145
146        to_writer_for_signature(writer, ctxt, signature, value)
147    }
148}
149
150/// Serialize `T` as a byte vector.
151///
152/// See [`Data::deserialize`] documentation for an example of how to use this function.
153pub fn to_bytes<T>(ctxt: Context, value: &T) -> Result<Data<'static, 'static>>
154where
155    T: ?Sized + Serialize + DynamicType,
156{
157    to_bytes_for_signature(ctxt, value.signature(), value)
158}
159
160/// Serialize `T` that has the given signature, to the given `writer`.
161///
162/// Use this function instead of [`to_writer`] if the value being serialized does not implement
163/// [`DynamicType`].
164///
165/// # Safety
166///
167/// On Unix systems, the returned [`Written`] instance can contain file descriptors and therefore
168/// the caller is responsible for not dropping the returned [`Written`] instance before the
169/// `writer`. Otherwise, the file descriptors in the `Written` instance will be closed while
170/// serialized data will still refer to them. Hence why this function is marked unsafe.
171///
172/// On non-Unix systems, the returned [`Written`] instance will not contain any file descriptors and
173/// hence is safe to drop.
174///
175/// [`to_writer`]: fn.to_writer.html
176pub unsafe fn to_writer_for_signature<W, S, T>(
177    writer: &mut W,
178    ctxt: Context,
179    signature: S,
180    value: &T,
181) -> Result<Written>
182where
183    W: Write + Seek,
184    S: TryInto<Signature>,
185    S::Error: Into<Error>,
186    T: ?Sized + Serialize,
187{
188    let signature = signature.try_into().map_err(Into::into)?;
189
190    #[cfg(unix)]
191    let mut fds = FdList::Fds(vec![]);
192
193    let len = match ctxt.format() {
194        Format::DBus => {
195            let mut ser = DBusSerializer::<W>::new(
196                &signature,
197                writer,
198                #[cfg(unix)]
199                &mut fds,
200                ctxt,
201            )?;
202            value.serialize(&mut ser)?;
203            ser.0.bytes_written
204        }
205        #[cfg(feature = "gvariant")]
206        #[allow(deprecated)]
207        Format::GVariant => {
208            let mut ser = GVSerializer::<W>::new(
209                &signature,
210                writer,
211                #[cfg(unix)]
212                &mut fds,
213                ctxt,
214            )?;
215            value.serialize(&mut ser)?;
216            ser.0.bytes_written
217        }
218        // See the comment on the equivalent arm in `serialized_size` above.
219        #[cfg(not(feature = "gvariant"))]
220        #[allow(unreachable_patterns)]
221        _ => {
222            return Err(Error::Message(
223                "GVariant support has moved to the `zgvariant` crate; enable `zvariant`'s \
224                 deprecated `gvariant` feature only for legacy compatibility"
225                    .to_owned(),
226            ));
227        }
228    };
229
230    let written = Written::new(len, ctxt);
231    #[cfg(unix)]
232    let written = match fds {
233        FdList::Fds(fds) => written.set_fds(fds),
234        FdList::Number(_) => unreachable!("`Fds::Number` is not possible here"),
235    };
236
237    Ok(written)
238}
239
240/// Serialize `T` that has the given signature, to a new byte vector.
241///
242/// Use this function instead of [`to_bytes`] if the value being serialized does not implement
243/// [`DynamicType`]. See [`Data::deserialize_for_signature`] documentation for an example of how to
244/// use this function.
245pub fn to_bytes_for_signature<S, T>(
246    ctxt: Context,
247    signature: S,
248    value: &T,
249) -> Result<Data<'static, 'static>>
250where
251    S: TryInto<Signature>,
252    S::Error: Into<Error>,
253    T: ?Sized + Serialize,
254{
255    let mut cursor = std::io::Cursor::new(vec![]);
256    // SAFETY: We put the bytes and FDs in the `Data` to ensure that the data and FDs are only
257    // dropped together.
258    let ret = unsafe { to_writer_for_signature(&mut cursor, ctxt, signature, value) }?;
259    #[cfg(unix)]
260    let encoded = Data::new_fds(cursor.into_inner(), ctxt, ret.into_fds());
261    #[cfg(not(unix))]
262    let encoded = {
263        let _ = ret;
264        Data::new(cursor.into_inner(), ctxt)
265    };
266
267    Ok(encoded)
268}
269
270/// Context for all our serializers and provides shared functionality.
271pub(crate) struct SerializerCommon<'ser, W> {
272    pub(crate) ctxt: Context,
273    pub(crate) writer: &'ser mut W,
274    pub(crate) bytes_written: usize,
275    #[cfg(unix)]
276    pub(crate) fds: &'ser mut FdList,
277
278    pub(crate) signature: &'ser Signature,
279
280    pub(crate) value_sign: Option<Signature>,
281
282    pub(crate) container_depths: ContainerDepths,
283}
284
285#[cfg(unix)]
286pub(crate) enum FdList {
287    Fds(Vec<OwnedFd>),
288    Number(u32),
289}
290
291impl<W> SerializerCommon<'_, W>
292where
293    W: Write + Seek,
294{
295    #[cfg(unix)]
296    pub(crate) fn add_fd(&mut self, fd: std::os::fd::RawFd) -> Result<u32> {
297        use std::os::fd::{AsRawFd, BorrowedFd};
298
299        match self.fds {
300            FdList::Fds(fds) => {
301                if let Some(idx) = fds.iter().position(|x| x.as_raw_fd() == fd) {
302                    return Ok(idx as u32);
303                }
304                let idx = fds.len();
305                // Cloning implies dup and is unfortunate but we need to return owned fds
306                // and dup is not expensive (at least on Linux).
307                let fd = unsafe { BorrowedFd::borrow_raw(fd) }.try_clone_to_owned()?;
308                fds.push(fd);
309
310                Ok(idx as u32)
311            }
312            FdList::Number(n) => {
313                let idx = *n;
314                *n += 1;
315
316                Ok(idx)
317            }
318        }
319    }
320
321    pub(crate) fn add_padding(&mut self, alignment: usize) -> Result<usize> {
322        let padding = padding_for_n_bytes(self.abs_pos(), alignment);
323        if padding > 0 {
324            self.write_all(&[0u8; 8][..padding])?;
325        }
326
327        Ok(padding)
328    }
329
330    pub(crate) fn prep_serialize_basic<T>(&mut self) -> Result<()>
331    where
332        T: Basic,
333    {
334        self.add_padding(T::alignment(self.ctxt.format()))?;
335
336        Ok(())
337    }
338
339    fn abs_pos(&self) -> usize {
340        self.ctxt.position() + self.bytes_written
341    }
342}
343
344impl<W> Write for SerializerCommon<'_, W>
345where
346    W: Write + Seek,
347{
348    /// Write `buf` and increment internal bytes written counter.
349    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
350        self.writer.write(buf).inspect(|&n| {
351            self.bytes_written += n;
352        })
353    }
354
355    fn flush(&mut self) -> std::io::Result<()> {
356        self.writer.flush()
357    }
358}