zvariant/
utils.rs

1use std::slice::SliceIndex;
2
3#[cfg(feature = "gvariant")]
4use crate::signature_parser::SignatureParser;
5use crate::{serialized::Format, Basic, Error, ObjectPath, Result, Signature};
6
7#[cfg(unix)]
8use crate::Fd;
9
10/// The prefix of ARRAY type signature, as a character. Provided for manual signature creation.
11pub const ARRAY_SIGNATURE_CHAR: char = 'a';
12/// The prefix of ARRAY type signature, as a string. Provided for manual signature creation.
13pub const ARRAY_SIGNATURE_STR: &str = "a";
14pub(crate) const ARRAY_ALIGNMENT_DBUS: usize = 4;
15/// The opening character of STRUCT type signature. Provided for manual signature creation.
16pub const STRUCT_SIG_START_CHAR: char = '(';
17/// The closing character of STRUCT type signature. Provided for manual signature creation.
18pub const STRUCT_SIG_END_CHAR: char = ')';
19/// The opening character of STRUCT type signature, as a string. Provided for manual signature
20/// creation.
21pub const STRUCT_SIG_START_STR: &str = "(";
22/// The closing character of STRUCT type signature, as a string. Provided for manual signature
23/// creation.
24pub const STRUCT_SIG_END_STR: &str = ")";
25pub(crate) const STRUCT_ALIGNMENT_DBUS: usize = 8;
26/// The opening character of DICT_ENTRY type signature. Provided for manual signature creation.
27pub const DICT_ENTRY_SIG_START_CHAR: char = '{';
28/// The closing character of DICT_ENTRY type signature. Provided for manual signature creation.
29pub const DICT_ENTRY_SIG_END_CHAR: char = '}';
30/// The opening character of DICT_ENTRY type signature, as a string. Provided for manual signature
31/// creation.
32pub const DICT_ENTRY_SIG_START_STR: &str = "{";
33/// The closing character of DICT_ENTRY type signature, as a string. Provided for manual signature
34/// creation.
35pub const DICT_ENTRY_SIG_END_STR: &str = "}";
36pub(crate) const DICT_ENTRY_ALIGNMENT_DBUS: usize = 8;
37/// The VARIANT type signature. Provided for manual signature creation.
38pub const VARIANT_SIGNATURE_CHAR: char = 'v';
39/// The VARIANT type signature, as a string. Provided for manual signature creation.
40pub const VARIANT_SIGNATURE_STR: &str = "v";
41pub(crate) const VARIANT_ALIGNMENT_DBUS: usize = 1;
42#[cfg(feature = "gvariant")]
43pub(crate) const VARIANT_ALIGNMENT_GVARIANT: usize = 8;
44/// The prefix of MAYBE (GVariant-specific) type signature, as a character. Provided for manual
45/// signature creation.
46#[cfg(feature = "gvariant")]
47pub const MAYBE_SIGNATURE_CHAR: char = 'm';
48/// The prefix of MAYBE (GVariant-specific) type signature, as a string. Provided for manual
49/// signature creation.
50#[cfg(feature = "gvariant")]
51pub const MAYBE_SIGNATURE_STR: &str = "m";
52
53pub(crate) fn padding_for_n_bytes(value: usize, align: usize) -> usize {
54    let len_rounded_up = value.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
55
56    len_rounded_up.wrapping_sub(value)
57}
58
59pub(crate) fn usize_to_u32(value: usize) -> u32 {
60    assert!(
61        value <= (u32::MAX as usize),
62        "{} too large for `u32`",
63        value,
64    );
65
66    value as u32
67}
68
69pub(crate) fn usize_to_u8(value: usize) -> u8 {
70    assert!(value <= (u8::MAX as usize), "{} too large for `u8`", value,);
71
72    value as u8
73}
74
75pub(crate) fn f64_to_f32(value: f64) -> f32 {
76    assert!(value <= (f32::MAX as f64), "{} too large for `f32`", value,);
77
78    value as f32
79}
80
81// `signature` must be **one** complete and correct signature. Expect panics otherwise!
82pub(crate) fn alignment_for_signature(signature: &Signature<'_>, format: Format) -> Result<usize> {
83    let alignment = match signature
84        .as_bytes()
85        .first()
86        .map(|b| *b as char)
87        .ok_or_else(|| -> Error { serde::de::Error::invalid_length(0, &">= 1 character") })?
88    {
89        u8::SIGNATURE_CHAR => u8::alignment(format),
90        bool::SIGNATURE_CHAR => bool::alignment(format),
91        i16::SIGNATURE_CHAR => i16::alignment(format),
92        u16::SIGNATURE_CHAR => u16::alignment(format),
93        i32::SIGNATURE_CHAR => i32::alignment(format),
94        u32::SIGNATURE_CHAR => u32::alignment(format),
95        #[cfg(unix)]
96        Fd::SIGNATURE_CHAR => u32::alignment(format),
97        i64::SIGNATURE_CHAR => i64::alignment(format),
98        u64::SIGNATURE_CHAR => u64::alignment(format),
99        f64::SIGNATURE_CHAR => f64::alignment(format),
100        <&str>::SIGNATURE_CHAR => <&str>::alignment(format),
101        ObjectPath::SIGNATURE_CHAR => ObjectPath::alignment(format),
102        Signature::SIGNATURE_CHAR => Signature::alignment(format),
103        VARIANT_SIGNATURE_CHAR => match format {
104            Format::DBus => VARIANT_ALIGNMENT_DBUS,
105            #[cfg(feature = "gvariant")]
106            Format::GVariant => VARIANT_ALIGNMENT_GVARIANT,
107        },
108        ARRAY_SIGNATURE_CHAR => alignment_for_array_signature(signature, format)?,
109        STRUCT_SIG_START_CHAR => alignment_for_struct_signature(signature, format)?,
110        DICT_ENTRY_SIG_START_CHAR => alignment_for_dict_entry_signature(signature, format)?,
111        #[cfg(feature = "gvariant")]
112        MAYBE_SIGNATURE_CHAR => alignment_for_maybe_signature(signature, format)?,
113        _ => {
114            return Err(serde::de::Error::invalid_value(
115                serde::de::Unexpected::Str(signature),
116                &"a valid signature",
117            ))
118        }
119    };
120
121    Ok(alignment)
122}
123
124#[cfg(feature = "gvariant")]
125pub(crate) fn is_fixed_sized_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
126    match signature
127        .as_bytes()
128        .first()
129        .map(|b| *b as char)
130        .ok_or_else(|| -> Error { serde::de::Error::invalid_length(0, &">= 1 character") })?
131    {
132        u8::SIGNATURE_CHAR
133        | bool::SIGNATURE_CHAR
134        | i16::SIGNATURE_CHAR
135        | u16::SIGNATURE_CHAR
136        | i32::SIGNATURE_CHAR
137        | u32::SIGNATURE_CHAR
138        | i64::SIGNATURE_CHAR
139        | u64::SIGNATURE_CHAR
140        | f64::SIGNATURE_CHAR => Ok(true),
141        #[cfg(unix)]
142        Fd::SIGNATURE_CHAR => Ok(true),
143        STRUCT_SIG_START_CHAR => is_fixed_sized_struct_signature(signature),
144        DICT_ENTRY_SIG_START_CHAR => is_fixed_sized_dict_entry_signature(signature),
145        _ => Ok(false),
146    }
147}
148
149// Given an &str, create an owned (String-based) Signature w/ appropriate capacity
150macro_rules! signature_string {
151    ($signature:expr) => {{
152        let mut s = String::with_capacity(255);
153        s.push_str($signature);
154
155        Signature::from_string_unchecked(s)
156    }};
157}
158
159macro_rules! check_child_value_signature {
160    ($expected_signature:expr, $child_signature:expr, $child_name:literal) => {{
161        if $child_signature != $expected_signature {
162            let unexpected = format!("{} with signature `{}`", $child_name, $child_signature,);
163            let expected = format!("{} with signature `{}`", $child_name, $expected_signature);
164
165            return Err(serde::de::Error::invalid_type(
166                serde::de::Unexpected::Str(&unexpected),
167                &expected.as_str(),
168            ));
169        }
170    }};
171}
172
173fn alignment_for_single_child_type_signature(
174    #[allow(unused)] signature: &Signature<'_>,
175    format: Format,
176    dbus_align: usize,
177) -> Result<usize> {
178    match format {
179        Format::DBus => Ok(dbus_align),
180        #[cfg(feature = "gvariant")]
181        Format::GVariant => {
182            let child_signature = signature.slice(1..);
183
184            alignment_for_signature(&child_signature, format)
185        }
186    }
187}
188
189fn alignment_for_array_signature(signature: &Signature<'_>, format: Format) -> Result<usize> {
190    alignment_for_single_child_type_signature(signature, format, ARRAY_ALIGNMENT_DBUS)
191}
192
193#[cfg(feature = "gvariant")]
194fn alignment_for_maybe_signature(signature: &Signature<'_>, format: Format) -> Result<usize> {
195    alignment_for_single_child_type_signature(signature, format, 1)
196}
197
198fn alignment_for_struct_signature(
199    #[allow(unused)] signature: &Signature<'_>,
200    format: Format,
201) -> Result<usize> {
202    match format {
203        Format::DBus => Ok(STRUCT_ALIGNMENT_DBUS),
204        #[cfg(feature = "gvariant")]
205        Format::GVariant => {
206            if signature.len() < 3 {
207                return Err(serde::de::Error::invalid_length(
208                    signature.len(),
209                    &">= 3 characters in struct signature",
210                ));
211            }
212            let inner_signature = Signature::from_str_unchecked(&signature[1..signature.len() - 1]);
213            let mut sig_parser = SignatureParser::new(inner_signature);
214            let mut alignment = 0;
215
216            while !sig_parser.done() {
217                let child_signature = sig_parser.parse_next_signature()?;
218
219                let child_alignment = alignment_for_signature(&child_signature, format)?;
220                if child_alignment > alignment {
221                    alignment = child_alignment;
222
223                    if alignment == 8 {
224                        // 8 bytes is max alignment so we can short-circuit here
225                        break;
226                    }
227                }
228            }
229
230            Ok(alignment)
231        }
232    }
233}
234
235fn alignment_for_dict_entry_signature(
236    #[allow(unused)] signature: &Signature<'_>,
237    format: Format,
238) -> Result<usize> {
239    match format {
240        Format::DBus => Ok(DICT_ENTRY_ALIGNMENT_DBUS),
241        #[cfg(feature = "gvariant")]
242        Format::GVariant => {
243            if signature.len() < 4 {
244                return Err(serde::de::Error::invalid_length(
245                    signature.len(),
246                    &">= 4 characters in dict entry signature",
247                ));
248            }
249            let key_signature = Signature::from_str_unchecked(&signature[1..2]);
250            let key_alignment = alignment_for_signature(&key_signature, format)?;
251            if key_alignment == 8 {
252                // 8 bytes is max alignment so we can short-circuit here
253                return Ok(8);
254            }
255
256            let value_signature = Signature::from_str_unchecked(&signature[2..signature.len() - 1]);
257            let value_alignment = alignment_for_signature(&value_signature, format)?;
258            if value_alignment > key_alignment {
259                Ok(value_alignment)
260            } else {
261                Ok(key_alignment)
262            }
263        }
264    }
265}
266
267#[cfg(feature = "gvariant")]
268fn is_fixed_sized_struct_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
269    let inner_signature = Signature::from_str_unchecked(&signature[1..signature.len() - 1]);
270    let mut sig_parser = SignatureParser::new(inner_signature);
271    let mut fixed_sized = true;
272
273    while !sig_parser.done() {
274        let child_signature = sig_parser.parse_next_signature()?;
275
276        if !is_fixed_sized_signature(&child_signature)? {
277            // STRUCT is fixed-sized only if all its children are
278            fixed_sized = false;
279
280            break;
281        }
282    }
283
284    Ok(fixed_sized)
285}
286
287#[cfg(feature = "gvariant")]
288fn is_fixed_sized_dict_entry_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
289    let key_signature = Signature::from_str_unchecked(&signature[1..2]);
290    if !is_fixed_sized_signature(&key_signature)? {
291        return Ok(false);
292    }
293
294    let value_signature = Signature::from_str_unchecked(&signature[2..signature.len() - 1]);
295
296    is_fixed_sized_signature(&value_signature)
297}
298
299/// Slice the given slice of bytes safely and return an error if the slice is too small.
300pub(crate) fn subslice<I, T>(input: &[T], index: I) -> Result<&I::Output>
301where
302    I: SliceIndex<[T]>,
303{
304    input.get(index).ok_or(Error::OutOfBounds)
305}