zvariant/
ser.rs

1use byteorder::WriteBytesExt;
2use serde::{ser, Serialize};
3use static_assertions::assert_impl_all;
4use std::{
5    io::{Seek, Write},
6    marker::PhantomData,
7    str,
8};
9
10#[cfg(unix)]
11use std::os::unix::io::RawFd;
12
13#[cfg(feature = "gvariant")]
14use crate::gvariant::{self, Serializer as GVSerializer};
15use crate::{
16    container_depths::ContainerDepths,
17    dbus::{self, Serializer as DBusSerializer},
18    signature_parser::SignatureParser,
19    utils::*,
20    Basic, DynamicType, EncodingContext, EncodingFormat, Error, Result, Signature,
21};
22
23struct NullWriteSeek;
24
25impl Write for NullWriteSeek {
26    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
27        Ok(buf.len())
28    }
29
30    fn flush(&mut self) -> std::io::Result<()> {
31        Ok(())
32    }
33}
34
35impl Seek for NullWriteSeek {
36    fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
37        Ok(std::u64::MAX) // should never read the return value!
38    }
39}
40
41/// Calculate the serialized size of `T`.
42///
43/// # Panics
44///
45/// This function will panic if the value to serialize contains file descriptors. Use
46/// [`serialized_size_fds`] if `T` (potentially) contains FDs.
47///
48/// # Examples
49///
50/// ```
51/// use zvariant::{EncodingContext, serialized_size};
52///
53/// let ctxt = EncodingContext::<byteorder::LE>::new_dbus(0);
54/// let len = serialized_size(ctxt, "hello world").unwrap();
55/// assert_eq!(len, 16);
56///
57/// let len = serialized_size(ctxt, &("hello world!", 42_u64)).unwrap();
58/// assert_eq!(len, 32);
59/// ```
60///
61/// [`serialized_size_fds`]: fn.serialized_size_fds.html
62pub fn serialized_size<B, T: ?Sized>(ctxt: EncodingContext<B>, value: &T) -> Result<usize>
63where
64    B: byteorder::ByteOrder,
65    T: Serialize + DynamicType,
66{
67    let mut null = NullWriteSeek;
68
69    to_writer(&mut null, ctxt, value)
70}
71
72/// Calculate the serialized size of `T` that (potentially) contains FDs.
73///
74/// Returns the serialized size of `T` and the number of FDs.
75///
76/// This function is not available on Windows.
77#[cfg(unix)]
78pub fn serialized_size_fds<B, T: ?Sized>(
79    ctxt: EncodingContext<B>,
80    value: &T,
81) -> Result<(usize, usize)>
82where
83    B: byteorder::ByteOrder,
84    T: Serialize + DynamicType,
85{
86    let mut null = NullWriteSeek;
87
88    let (len, fds) = to_writer_fds(&mut null, ctxt, value)?;
89    Ok((len, fds.len()))
90}
91
92/// Serialize `T` to the given `writer`.
93///
94/// This function returns the number of bytes written to the given `writer`.
95///
96/// # Panics
97///
98/// This function will panic if the value to serialize contains file descriptors. Use
99/// [`to_writer_fds`] if you'd want to potentially pass FDs.
100///
101/// # Examples
102///
103/// ```
104/// use zvariant::{EncodingContext, from_slice, to_writer};
105///
106/// let ctxt = EncodingContext::<byteorder::LE>::new_dbus(0);
107/// let mut cursor = std::io::Cursor::new(vec![]);
108/// to_writer(&mut cursor, ctxt, &42u32).unwrap();
109/// let value: u32 = from_slice(cursor.get_ref(), ctxt).unwrap();
110/// assert_eq!(value, 42);
111/// ```
112///
113/// [`to_writer_fds`]: fn.to_writer_fds.html
114pub fn to_writer<B, W, T: ?Sized>(
115    writer: &mut W,
116    ctxt: EncodingContext<B>,
117    value: &T,
118) -> Result<usize>
119where
120    B: byteorder::ByteOrder,
121    W: Write + Seek,
122    T: Serialize + DynamicType,
123{
124    let signature = value.dynamic_signature();
125
126    to_writer_for_signature(writer, ctxt, &signature, value)
127}
128
129/// Serialize `T` that (potentially) contains FDs, to the given `writer`.
130///
131/// This function returns the number of bytes written to the given `writer` and the file descriptor
132/// vector, which needs to be transferred via an out-of-band platform specific mechanism.
133///
134/// This function is not available on Windows.
135#[cfg(unix)]
136pub fn to_writer_fds<B, W, T: ?Sized>(
137    writer: &mut W,
138    ctxt: EncodingContext<B>,
139    value: &T,
140) -> Result<(usize, Vec<RawFd>)>
141where
142    B: byteorder::ByteOrder,
143    W: Write + Seek,
144    T: Serialize + DynamicType,
145{
146    let signature = value.dynamic_signature();
147
148    to_writer_fds_for_signature(writer, ctxt, &signature, value)
149}
150
151/// Serialize `T` as a byte vector.
152///
153/// See [`from_slice`] documentation for an example of how to use this function.
154///
155/// # Panics
156///
157/// This function will panic if the value to serialize contains file descriptors. Use
158/// [`to_bytes_fds`] if you'd want to potentially pass FDs.
159///
160/// [`to_bytes_fds`]: fn.to_bytes_fds.html
161/// [`from_slice`]: fn.from_slice.html#examples
162pub fn to_bytes<B, T: ?Sized>(ctxt: EncodingContext<B>, value: &T) -> Result<Vec<u8>>
163where
164    B: byteorder::ByteOrder,
165    T: Serialize + DynamicType,
166{
167    let mut cursor = std::io::Cursor::new(vec![]);
168    to_writer(&mut cursor, ctxt, value)?;
169    Ok(cursor.into_inner())
170}
171
172/// Serialize `T` that (potentially) contains FDs, as a byte vector.
173///
174/// The returned file descriptor needs to be transferred via an out-of-band platform specific
175/// mechanism.
176///
177/// This function is not available on Windows.
178#[cfg(unix)]
179pub fn to_bytes_fds<B, T: ?Sized>(
180    ctxt: EncodingContext<B>,
181    value: &T,
182) -> Result<(Vec<u8>, Vec<RawFd>)>
183where
184    B: byteorder::ByteOrder,
185    T: Serialize + DynamicType,
186{
187    let mut cursor = std::io::Cursor::new(vec![]);
188    let (_, fds) = to_writer_fds(&mut cursor, ctxt, value)?;
189    Ok((cursor.into_inner(), fds))
190}
191
192/// Serialize `T` that has the given signature, to the given `writer`.
193///
194/// Use this function instead of [`to_writer`] if the value being serialized does not implement
195/// [`Type`].
196///
197/// This function returns the number of bytes written to the given `writer`.
198///
199/// [`to_writer`]: fn.to_writer.html
200/// [`Type`]: trait.Type.html
201pub fn to_writer_for_signature<B, W, T: ?Sized>(
202    writer: &mut W,
203    ctxt: EncodingContext<B>,
204    signature: &Signature<'_>,
205    value: &T,
206) -> Result<usize>
207where
208    B: byteorder::ByteOrder,
209    W: Write + Seek,
210    T: Serialize,
211{
212    #[cfg(unix)]
213    {
214        let (len, fds) = to_writer_fds_for_signature(writer, ctxt, signature, value)?;
215        if !fds.is_empty() {
216            panic!("can't serialize with FDs")
217        }
218        Ok(len)
219    }
220
221    #[cfg(not(unix))]
222    {
223        match ctxt.format() {
224            EncodingFormat::DBus => {
225                let mut ser = DBusSerializer::<B, W>::new(signature, writer, ctxt);
226                value.serialize(&mut ser)?;
227                Ok(ser.0.bytes_written)
228            }
229            #[cfg(feature = "gvariant")]
230            EncodingFormat::GVariant => {
231                let mut ser = GVSerializer::<B, W>::new(signature, writer, ctxt);
232                value.serialize(&mut ser)?;
233                Ok(ser.0.bytes_written)
234            }
235        }
236    }
237}
238
239/// Serialize `T` that (potentially) contains FDs and has the given signature, to the given
240/// `writer`.
241///
242/// Use this function instead of [`to_writer_fds`] if the value being serialized does not implement
243/// [`Type`].
244///
245/// This function returns the number of bytes written to the given `writer` and the file descriptor
246/// vector, which needs to be transferred via an out-of-band platform specific mechanism.
247///
248/// This function is not available on Windows.
249///
250/// [`to_writer_fds`]: fn.to_writer_fds.html
251/// [`Type`]: trait.Type.html
252#[cfg(unix)]
253pub fn to_writer_fds_for_signature<B, W, T: ?Sized>(
254    writer: &mut W,
255    ctxt: EncodingContext<B>,
256    signature: &Signature<'_>,
257    value: &T,
258) -> Result<(usize, Vec<RawFd>)>
259where
260    B: byteorder::ByteOrder,
261    W: Write + Seek,
262    T: Serialize,
263{
264    let mut fds = vec![];
265    match ctxt.format() {
266        EncodingFormat::DBus => {
267            let mut ser = DBusSerializer::<B, W>::new(signature, writer, &mut fds, ctxt);
268            value.serialize(&mut ser)?;
269            Ok((ser.0.bytes_written, fds))
270        }
271        #[cfg(feature = "gvariant")]
272        EncodingFormat::GVariant => {
273            let mut ser = GVSerializer::<B, W>::new(signature, writer, &mut fds, ctxt);
274            value.serialize(&mut ser)?;
275            Ok((ser.0.bytes_written, fds))
276        }
277    }
278}
279
280/// Serialize `T` that has the given signature, to a new byte vector.
281///
282/// Use this function instead of [`to_bytes`] if the value being serialized does not implement
283/// [`Type`]. See [`from_slice_for_signature`] documentation for an example of how to use this
284/// function.
285///
286/// # Panics
287///
288/// This function will panic if the value to serialize contains file descriptors. Use
289/// [`to_bytes_fds_for_signature`] if you'd want to potentially pass FDs.
290///
291/// [`to_bytes`]: fn.to_bytes.html
292/// [`Type`]: trait.Type.html
293/// [`from_slice_for_signature`]: fn.from_slice_for_signature.html#examples
294pub fn to_bytes_for_signature<B, T: ?Sized>(
295    ctxt: EncodingContext<B>,
296    signature: &Signature<'_>,
297    value: &T,
298) -> Result<Vec<u8>>
299where
300    B: byteorder::ByteOrder,
301    T: Serialize,
302{
303    #[cfg(unix)]
304    {
305        let (bytes, fds) = to_bytes_fds_for_signature(ctxt, signature, value)?;
306        if !fds.is_empty() {
307            panic!("can't serialize with FDs")
308        }
309        Ok(bytes)
310    }
311
312    #[cfg(not(unix))]
313    {
314        let mut cursor = std::io::Cursor::new(vec![]);
315        to_writer_for_signature(&mut cursor, ctxt, signature, value)?;
316        Ok(cursor.into_inner())
317    }
318}
319
320/// Serialize `T` that (potentially) contains FDs and has the given signature, to a new byte vector.
321///
322/// Use this function instead of [`to_bytes_fds`] if the value being serialized does not implement
323/// [`Type`].
324///
325/// Please note that the serialized bytes only contain the indices of the file descriptors from the
326/// returned file descriptor vector, which needs to be transferred via an out-of-band platform
327/// specific mechanism.
328///
329/// This function is not available on Windows.
330///
331/// [`to_bytes_fds`]: fn.to_bytes_fds.html
332/// [`Type`]: trait.Type.html
333#[cfg(unix)]
334pub fn to_bytes_fds_for_signature<B, T: ?Sized>(
335    ctxt: EncodingContext<B>,
336    signature: &Signature<'_>,
337    value: &T,
338) -> Result<(Vec<u8>, Vec<RawFd>)>
339where
340    B: byteorder::ByteOrder,
341    T: Serialize,
342{
343    let mut cursor = std::io::Cursor::new(vec![]);
344    let (_, fds) = to_writer_fds_for_signature(&mut cursor, ctxt, signature, value)?;
345    Ok((cursor.into_inner(), fds))
346}
347
348/// Context for all our serializers and provides shared functionality.
349pub(crate) struct SerializerCommon<'ser, 'sig, B, W> {
350    pub(crate) ctxt: EncodingContext<B>,
351    pub(crate) writer: &'ser mut W,
352    pub(crate) bytes_written: usize,
353    #[cfg(unix)]
354    pub(crate) fds: &'ser mut Vec<RawFd>,
355
356    pub(crate) sig_parser: SignatureParser<'sig>,
357
358    pub(crate) value_sign: Option<Signature<'static>>,
359
360    pub(crate) container_depths: ContainerDepths,
361
362    pub(crate) b: PhantomData<B>,
363}
364
365/// Our serialization implementation.
366///
367/// Using this serializer involves an redirection to the actual serializer. It's best to use the
368/// serialization functions, e.g [`to_bytes`] or specific serializers, [`dbus::Serializer`] or
369/// [`zvariant::Serializer`].
370pub enum Serializer<'ser, 'sig, B, W> {
371    DBus(DBusSerializer<'ser, 'sig, B, W>),
372    #[cfg(feature = "gvariant")]
373    GVariant(GVSerializer<'ser, 'sig, B, W>),
374}
375
376assert_impl_all!(Serializer<'_, '_, i32, i32>: Send, Sync, Unpin);
377
378impl<'ser, 'sig, B, W> Serializer<'ser, 'sig, B, W>
379where
380    B: byteorder::ByteOrder,
381    W: Write + Seek,
382{
383    /// Create a Serializer struct instance.
384    pub fn new<'w: 'ser, 'f: 'ser>(
385        signature: &Signature<'sig>,
386        writer: &'w mut W,
387        #[cfg(unix)] fds: &'f mut Vec<RawFd>,
388        ctxt: EncodingContext<B>,
389    ) -> Self {
390        match ctxt.format() {
391            #[cfg(feature = "gvariant")]
392            EncodingFormat::GVariant => Self::GVariant(GVSerializer::new(
393                signature,
394                writer,
395                #[cfg(unix)]
396                fds,
397                ctxt,
398            )),
399            EncodingFormat::DBus => Self::DBus(DBusSerializer::new(
400                signature,
401                writer,
402                #[cfg(unix)]
403                fds,
404                ctxt,
405            )),
406        }
407    }
408
409    /// Unwrap the `Writer` reference from the `Serializer`.
410    #[inline]
411    pub fn into_inner(self) -> &'ser mut W {
412        match self {
413            #[cfg(feature = "gvariant")]
414            Self::GVariant(ser) => ser.0.writer,
415            Self::DBus(ser) => ser.0.writer,
416        }
417    }
418}
419
420impl<'ser, 'sig, B, W> SerializerCommon<'ser, 'sig, B, W>
421where
422    B: byteorder::ByteOrder,
423    W: Write + Seek,
424{
425    #[cfg(unix)]
426    pub(crate) fn add_fd(&mut self, fd: RawFd) -> u32 {
427        if let Some(idx) = self.fds.iter().position(|&x| x == fd) {
428            return idx as u32;
429        }
430        let idx = self.fds.len();
431        self.fds.push(fd);
432
433        idx as u32
434    }
435
436    pub(crate) fn add_padding(&mut self, alignment: usize) -> Result<usize> {
437        let padding = padding_for_n_bytes(self.abs_pos(), alignment);
438        if padding > 0 {
439            let byte = [0_u8; 1];
440            for _ in 0..padding {
441                self.write_all(&byte)
442                    .map_err(|e| Error::InputOutput(e.into()))?;
443            }
444        }
445
446        Ok(padding)
447    }
448
449    pub(crate) fn prep_serialize_basic<T>(&mut self) -> Result<()>
450    where
451        T: Basic,
452    {
453        self.sig_parser.skip_char()?;
454        self.add_padding(T::alignment(self.ctxt.format()))?;
455
456        Ok(())
457    }
458
459    /// This starts the enum serialization.
460    ///
461    /// It's up to the caller to do the rest: serialize the variant payload and skip the `).
462    pub(crate) fn prep_serialize_enum_variant(&mut self, variant_index: u32) -> Result<()> {
463        // Encode enum variants as a struct with first field as variant index
464        let signature = self.sig_parser.next_signature()?;
465        if self.sig_parser.next_char()? != STRUCT_SIG_START_CHAR {
466            return Err(Error::SignatureMismatch(
467                signature.to_owned(),
468                format!("expected `{STRUCT_SIG_START_CHAR}`"),
469            ));
470        }
471
472        let alignment = alignment_for_signature(&signature, self.ctxt.format())?;
473        self.add_padding(alignment)?;
474
475        // Now serialize the veriant index.
476        self.write_u32::<B>(variant_index)
477            .map_err(|e| Error::InputOutput(e.into()))?;
478
479        // Skip the `(`, `u`.
480        self.sig_parser.skip_chars(2)?;
481
482        Ok(())
483    }
484
485    fn abs_pos(&self) -> usize {
486        self.ctxt.position() + self.bytes_written
487    }
488}
489
490impl<'ser, 'sig, B, W> Write for SerializerCommon<'ser, 'sig, B, W>
491where
492    B: byteorder::ByteOrder,
493    W: Write + Seek,
494{
495    /// Write `buf` and increment internal bytes written counter.
496    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
497        self.writer.write(buf).map(|n| {
498            self.bytes_written += n;
499
500            n
501        })
502    }
503
504    fn flush(&mut self) -> std::io::Result<()> {
505        self.writer.flush()
506    }
507}
508
509macro_rules! serialize_method {
510    ($method:ident($($arg:ident: $type:ty),*)) => {
511        serialize_method!(; $method($($arg: $type),*) => () =);
512    };
513    ($($generic:ident),* ; $method:ident($($arg:ident: $type:ty),*)) => {
514        serialize_method!($($generic),*; $method($($arg: $type),*) => () =);
515    };
516    ($($generic:ident),* ; $method:ident($($arg:ident: $type:ty),*) => $ret:ty = $($map:ident)*) => {
517        #[inline]
518        fn $method<$($generic),*>(self, $($arg: $type),*) -> Result<$ret>
519        where
520            $($generic: ?Sized + Serialize),*
521        {
522            match self {
523                #[cfg(feature = "gvariant")]
524                Serializer::GVariant(ser) => {
525                    ser.$method($($arg),*)$(.map($map::GVariant))*
526                }
527                Serializer::DBus(ser) => {
528                    ser.$method($($arg),*)$(.map($map::DBus))*
529                }
530            }
531        }
532    }
533}
534
535impl<'ser, 'sig, 'b, B, W> ser::Serializer for &'b mut Serializer<'ser, 'sig, B, W>
536where
537    B: byteorder::ByteOrder,
538    W: Write + Seek,
539{
540    type Ok = ();
541    type Error = Error;
542
543    type SerializeSeq = SeqSerializer<'ser, 'sig, 'b, B, W>;
544    type SerializeTuple = StructSerializer<'ser, 'sig, 'b, B, W>;
545    type SerializeTupleStruct = StructSerializer<'ser, 'sig, 'b, B, W>;
546    type SerializeTupleVariant = StructSerializer<'ser, 'sig, 'b, B, W>;
547    type SerializeMap = SeqSerializer<'ser, 'sig, 'b, B, W>;
548    type SerializeStruct = StructSerializer<'ser, 'sig, 'b, B, W>;
549    type SerializeStructVariant = StructSerializer<'ser, 'sig, 'b, B, W>;
550
551    serialize_method!(serialize_bool(b: bool));
552    serialize_method!(serialize_i8(i: i8));
553    serialize_method!(serialize_i16(i: i16));
554    serialize_method!(serialize_i32(i: i32));
555    serialize_method!(serialize_i64(i: i64));
556    serialize_method!(serialize_u8(u: u8));
557    serialize_method!(serialize_u16(u: u16));
558    serialize_method!(serialize_u32(u: u32));
559    serialize_method!(serialize_u64(u: u64));
560    serialize_method!(serialize_f32(f: f32));
561    serialize_method!(serialize_f64(f: f64));
562    serialize_method!(serialize_char(c: char));
563    serialize_method!(serialize_str(s: &str));
564    serialize_method!(serialize_bytes(b: &[u8]));
565    serialize_method!(T; serialize_some(v: &T));
566    serialize_method!(serialize_none());
567    serialize_method!(serialize_unit());
568    serialize_method!(serialize_unit_struct(s: &'static str));
569    serialize_method!(serialize_unit_variant(
570        n: &'static str,
571        i: u32,
572        v: &'static str
573    ));
574    serialize_method!(T; serialize_newtype_struct(n: &'static str, v: &T));
575    serialize_method!(T; serialize_newtype_variant(n: &'static str, i: u32, va: &'static str, v: &T));
576    serialize_method!(; serialize_seq(l: Option<usize>) => Self::SerializeSeq = SeqSerializer);
577    serialize_method!(; serialize_tuple_variant(
578        n: &'static str,
579        i: u32,
580        v: &'static str,
581        l: usize
582    ) => Self::SerializeTupleVariant = StructSerializer);
583    serialize_method!(;serialize_struct_variant(
584        n: &'static str,
585        i: u32,
586        v: &'static str,
587        l: usize
588    ) => Self::SerializeStructVariant = StructSerializer);
589    serialize_method!(; serialize_tuple(l: usize) => Self::SerializeTuple = StructSerializer);
590    serialize_method!(; serialize_tuple_struct(
591        n: &'static str,
592        l: usize
593    ) => Self::SerializeTupleStruct = StructSerializer);
594    serialize_method!(; serialize_map(l: Option<usize>) => Self::SerializeMap = SeqSerializer);
595    serialize_method!(; serialize_struct(
596        n: &'static str,
597        l: usize
598    ) => Self::SerializeStruct = StructSerializer);
599
600    fn is_human_readable(&self) -> bool {
601        false
602    }
603}
604
605macro_rules! serialize_impl {
606    ($trait:ident, $impl:ident, $($method:ident($($arg:ident: $type:ty),*))+) => {
607        impl<'ser, 'sig, 'b, B, W> ser::$trait for $impl<'ser, 'sig, 'b, B, W>
608        where
609            B: byteorder::ByteOrder,
610            W: Write + Seek,
611        {
612            type Ok = ();
613            type Error = Error;
614
615            $(
616                fn $method<T>(&mut self, $($arg: $type),*) -> Result<()>
617                where
618                    T: ?Sized + Serialize,
619                {
620                    match self {
621                        #[cfg(feature = "gvariant")]
622                        $impl::GVariant(ser) => ser.$method($($arg),*),
623                        $impl::DBus(ser) => ser.$method($($arg),*),
624                    }
625                }
626            )*
627
628            fn end(self) -> Result<()> {
629                match self {
630                    #[cfg(feature = "gvariant")]
631                    $impl::GVariant(ser) => ser.end(),
632                    $impl::DBus(ser) => ser.end(),
633                }
634            }
635        }
636    }
637}
638
639#[doc(hidden)]
640pub enum SeqSerializer<'ser, 'sig, 'b, B, W> {
641    DBus(dbus::SeqSerializer<'ser, 'sig, 'b, B, W>),
642    #[cfg(feature = "gvariant")]
643    GVariant(gvariant::SeqSerializer<'ser, 'sig, 'b, B, W>),
644}
645
646serialize_impl!(SerializeSeq, SeqSerializer, serialize_element(value: &T));
647
648#[doc(hidden)]
649pub enum StructSerializer<'ser, 'sig, 'b, B, W> {
650    DBus(dbus::StructSeqSerializer<'ser, 'sig, 'b, B, W>),
651    #[cfg(feature = "gvariant")]
652    GVariant(gvariant::StructSeqSerializer<'ser, 'sig, 'b, B, W>),
653}
654
655serialize_impl!(SerializeTuple, StructSerializer, serialize_element(v: &T));
656serialize_impl!(
657    SerializeTupleStruct,
658    StructSerializer,
659    serialize_field(v: &T)
660);
661serialize_impl!(
662    SerializeTupleVariant,
663    StructSerializer,
664    serialize_field(v: &T)
665);
666serialize_impl!(
667    SerializeStruct,
668    StructSerializer,
669    serialize_field(k: &'static str, v: &T)
670);
671serialize_impl!(
672    SerializeStructVariant,
673    StructSerializer,
674    serialize_field(k: &'static str, v: &T)
675);
676serialize_impl!(SerializeMap, SeqSerializer, serialize_key(v: &T) serialize_value(v: &T));