Skip to main content

tokio_dbus/signature/
mod.rs

1#[cfg(test)]
2mod tests;
3
4#[cfg(feature = "alloc")]
5mod stack;
6
7#[doc(inline)]
8pub(crate) use tokio_dbus_core::signature::MAX_DEPTH;
9#[doc(inline)]
10pub use tokio_dbus_core::signature::{Signature, SignatureBuf, SignatureBuilder, SignatureError};
11
12use crate::error::Result;
13
14use crate::{Alignment, Body, Read, Write, WriteAligned, WriteUnaligned};
15
16impl crate::write::sealed::Sealed for Signature {}
17
18impl Write for Signature {
19    const SIGNATURE: &'static Signature = Signature::SIGNATURE;
20
21    #[inline]
22    fn write_to<B>(&self, buf: &mut B)
23    where
24        B: ?Sized + WriteAligned,
25    {
26        buf.store_frame(self.len() as u8);
27        buf.extend_from_slice_nul(self.as_bytes());
28    }
29
30    #[inline]
31    fn write_to_unaligned<B>(&self, buf: &mut B)
32    where
33        B: ?Sized + WriteUnaligned,
34    {
35        buf.store(self.len() as u8);
36        buf.extend_from_slice_nul(self.as_bytes());
37    }
38}
39
40impl_traits_for_write!(Signature, Signature::new("us")?, "qg", Signature);
41
42impl crate::read::sealed::Sealed for Signature {}
43
44impl Read for Signature {
45    #[inline]
46    fn read_from<'de>(buf: &mut Body<'de>) -> Result<&'de Self> {
47        let len = buf.load::<u8>()? as usize;
48        let bytes = buf.load_slice_nul(len)?;
49        Ok(Signature::new(bytes)?)
50    }
51}
52
53/// The alignment of the type introduced by the given type code.
54///
55/// An absent type code cannot legally occur in a validated signature, but is
56/// treated as being byte-aligned so that the caller doesn't have to handle it.
57#[cfg(feature = "alloc")]
58fn alignment_of(byte: Option<u8>) -> Alignment {
59    use crate::proto::Type;
60
61    let Some(byte) = byte else {
62        return Alignment::BYTE;
63    };
64
65    match Type::new(byte) {
66        Type::BYTE | Type::SIGNATURE | Type::VARIANT => Alignment::BYTE,
67        Type::INT16 | Type::UINT16 => Alignment::U16,
68        Type::INT64 | Type::UINT64 | Type::DOUBLE | Type::OPEN_PAREN | Type::OPEN_BRACE => {
69            Alignment::U64
70        }
71        // NB: Covers `bihu`, `so` and nested arrays, all of which are aligned
72        // to 4 bytes.
73        _ => Alignment::U32,
74    }
75}
76
77/// Return the stride needed to skip over read buffer.
78#[cfg(feature = "alloc")]
79pub(crate) fn skip(this: &Signature, read: &mut Body<'_>) -> Result<()> {
80    use crate::proto::Type;
81
82    use self::stack::Stack;
83
84    #[derive(Debug, Clone, Copy)]
85    enum Step {
86        Fixed(usize),
87        StringNul,
88        Variant,
89        ByteNul,
90    }
91
92    let mut stack = Stack::<bool, MAX_DEPTH>::new();
93    let mut arrays = 0;
94
95    let bytes = this.as_bytes();
96
97    for (n, &b) in bytes.iter().enumerate() {
98        let t = Type::new(b);
99
100        let step = match t {
101            Type::BYTE => Step::Fixed(1),
102            Type::BOOLEAN => Step::Fixed(1),
103            Type::INT16 => Step::Fixed(2),
104            Type::UINT16 => Step::Fixed(2),
105            Type::INT32 => Step::Fixed(4),
106            Type::UINT32 => Step::Fixed(4),
107            Type::INT64 => Step::Fixed(8),
108            Type::UINT64 => Step::Fixed(8),
109            Type::DOUBLE => Step::Fixed(8),
110            Type::STRING => Step::StringNul,
111            Type::OBJECT_PATH => Step::StringNul,
112            Type::SIGNATURE => Step::ByteNul,
113            Type::VARIANT => Step::Variant,
114            Type::UNIX_FD => Step::Fixed(4),
115            Type::ARRAY => {
116                if arrays == 0 {
117                    let len = read.load::<u32>()? as usize;
118                    // The length prefix is followed by padding up to the
119                    // alignment of the element type, which is not counted
120                    // towards the length.
121                    read.align_to(alignment_of(bytes.get(n + 1).copied()))?;
122                    read.advance(len)?;
123                }
124
125                arrays += 1;
126                stack.try_push(true);
127                continue;
128            }
129            Type::OPEN_PAREN | Type::OPEN_BRACE => {
130                // NB: Structs and dict entries are aligned to 8 bytes. When
131                // we're inside of an array the whole array has already been
132                // skipped over, so there is nothing to align.
133                if arrays == 0 {
134                    read.align::<u64>()?;
135                }
136
137                stack.try_push(false);
138                continue;
139            }
140            Type::CLOSE_PAREN => {
141                stack.pop();
142                Step::Fixed(0)
143            }
144            Type::CLOSE_BRACE => {
145                stack.pop();
146                Step::Fixed(0)
147            }
148            _ => unreachable!(),
149        };
150
151        let in_array = arrays > 0;
152
153        // Unwind arrays.
154        while let Some(true) = stack.peek() {
155            arrays -= 1;
156            stack.pop();
157        }
158
159        if in_array {
160            continue;
161        }
162
163        match step {
164            Step::Fixed(n) => {
165                read.advance(n)?;
166            }
167            Step::StringNul => {
168                let n = read.load::<u32>()? as usize;
169                read.advance(n.saturating_add(1))?;
170            }
171            Step::ByteNul => {
172                let n = read.load::<u8>()? as usize;
173                read.advance(n.saturating_add(1))?;
174            }
175            Step::Variant => {
176                // NB: Reading the signature consumes the length prefix.
177                let sig = read.read::<Signature>()?;
178                skip(sig, read)?;
179            }
180        }
181    }
182
183    Ok(())
184}