zvariant/
utils.rs

1use std::slice::SliceIndex;
2
3#[cfg(feature = "gvariant")]
4use crate::signature_parser::SignatureParser;
5use crate::{Basic, EncodingFormat, 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 <= (std::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!(
71        value <= (std::u8::MAX as usize),
72        "{} too large for `u8`",
73        value,
74    );
75
76    value as u8
77}
78
79pub(crate) fn f64_to_f32(value: f64) -> f32 {
80    assert!(
81        value <= (std::f32::MAX as f64),
82        "{} too large for `f32`",
83        value,
84    );
85
86    value as f32
87}
88
89// `signature` must be **one** complete and correct signature. Expect panics otherwise!
90pub(crate) fn alignment_for_signature(
91    signature: &Signature<'_>,
92    format: EncodingFormat,
93) -> Result<usize> {
94    let alignment = match signature
95        .as_bytes()
96        .first()
97        .map(|b| *b as char)
98        .ok_or_else(|| -> Error { serde::de::Error::invalid_length(0, &">= 1 character") })?
99    {
100        u8::SIGNATURE_CHAR => u8::alignment(format),
101        bool::SIGNATURE_CHAR => bool::alignment(format),
102        i16::SIGNATURE_CHAR => i16::alignment(format),
103        u16::SIGNATURE_CHAR => u16::alignment(format),
104        i32::SIGNATURE_CHAR => i32::alignment(format),
105        u32::SIGNATURE_CHAR => u32::alignment(format),
106        #[cfg(unix)]
107        Fd::SIGNATURE_CHAR => u32::alignment(format),
108        i64::SIGNATURE_CHAR => i64::alignment(format),
109        u64::SIGNATURE_CHAR => u64::alignment(format),
110        f64::SIGNATURE_CHAR => f64::alignment(format),
111        <&str>::SIGNATURE_CHAR => <&str>::alignment(format),
112        ObjectPath::SIGNATURE_CHAR => ObjectPath::alignment(format),
113        Signature::SIGNATURE_CHAR => Signature::alignment(format),
114        VARIANT_SIGNATURE_CHAR => match format {
115            EncodingFormat::DBus => VARIANT_ALIGNMENT_DBUS,
116            #[cfg(feature = "gvariant")]
117            EncodingFormat::GVariant => VARIANT_ALIGNMENT_GVARIANT,
118        },
119        ARRAY_SIGNATURE_CHAR => alignment_for_array_signature(signature, format)?,
120        STRUCT_SIG_START_CHAR => alignment_for_struct_signature(signature, format)?,
121        DICT_ENTRY_SIG_START_CHAR => alignment_for_dict_entry_signature(signature, format)?,
122        #[cfg(feature = "gvariant")]
123        MAYBE_SIGNATURE_CHAR => alignment_for_maybe_signature(signature, format)?,
124        _ => {
125            return Err(serde::de::Error::invalid_value(
126                serde::de::Unexpected::Str(signature),
127                &"a valid signature",
128            ))
129        }
130    };
131
132    Ok(alignment)
133}
134
135#[cfg(feature = "gvariant")]
136pub(crate) fn is_fixed_sized_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
137    match signature
138        .as_bytes()
139        .first()
140        .map(|b| *b as char)
141        .ok_or_else(|| -> Error { serde::de::Error::invalid_length(0, &">= 1 character") })?
142    {
143        u8::SIGNATURE_CHAR
144        | bool::SIGNATURE_CHAR
145        | i16::SIGNATURE_CHAR
146        | u16::SIGNATURE_CHAR
147        | i32::SIGNATURE_CHAR
148        | u32::SIGNATURE_CHAR
149        | i64::SIGNATURE_CHAR
150        | u64::SIGNATURE_CHAR
151        | f64::SIGNATURE_CHAR => Ok(true),
152        #[cfg(unix)]
153        Fd::SIGNATURE_CHAR => Ok(true),
154        STRUCT_SIG_START_CHAR => is_fixed_sized_struct_signature(signature),
155        DICT_ENTRY_SIG_START_CHAR => is_fixed_sized_dict_entry_signature(signature),
156        _ => Ok(false),
157    }
158}
159
160// Given an &str, create an owned (String-based) Signature w/ appropriate capacity
161macro_rules! signature_string {
162    ($signature:expr) => {{
163        let mut s = String::with_capacity(255);
164        s.push_str($signature);
165
166        Signature::from_string_unchecked(s)
167    }};
168}
169
170macro_rules! check_child_value_signature {
171    ($expected_signature:expr, $child_signature:expr, $child_name:literal) => {{
172        if $child_signature != $expected_signature {
173            let unexpected = format!("{} with signature `{}`", $child_name, $child_signature,);
174            let expected = format!("{} with signature `{}`", $child_name, $expected_signature);
175
176            return Err(serde::de::Error::invalid_type(
177                serde::de::Unexpected::Str(&unexpected),
178                &expected.as_str(),
179            ));
180        }
181    }};
182}
183
184fn alignment_for_single_child_type_signature(
185    #[allow(unused)] signature: &Signature<'_>,
186    format: EncodingFormat,
187    dbus_align: usize,
188) -> Result<usize> {
189    match format {
190        EncodingFormat::DBus => Ok(dbus_align),
191        #[cfg(feature = "gvariant")]
192        EncodingFormat::GVariant => {
193            let child_signature = signature.slice(1..);
194
195            alignment_for_signature(&child_signature, format)
196        }
197    }
198}
199
200fn alignment_for_array_signature(
201    signature: &Signature<'_>,
202    format: EncodingFormat,
203) -> Result<usize> {
204    alignment_for_single_child_type_signature(signature, format, ARRAY_ALIGNMENT_DBUS)
205}
206
207#[cfg(feature = "gvariant")]
208fn alignment_for_maybe_signature(
209    signature: &Signature<'_>,
210    format: EncodingFormat,
211) -> Result<usize> {
212    alignment_for_single_child_type_signature(signature, format, 1)
213}
214
215fn alignment_for_struct_signature(
216    #[allow(unused)] signature: &Signature<'_>,
217    format: EncodingFormat,
218) -> Result<usize> {
219    match format {
220        EncodingFormat::DBus => Ok(STRUCT_ALIGNMENT_DBUS),
221        #[cfg(feature = "gvariant")]
222        EncodingFormat::GVariant => {
223            if signature.len() < 3 {
224                return Err(serde::de::Error::invalid_length(
225                    signature.len(),
226                    &">= 3 characters in struct signature",
227                ));
228            }
229            let inner_signature = Signature::from_str_unchecked(&signature[1..signature.len() - 1]);
230            let mut sig_parser = SignatureParser::new(inner_signature);
231            let mut alignment = 0;
232
233            while !sig_parser.done() {
234                let child_signature = sig_parser.parse_next_signature()?;
235
236                let child_alignment = alignment_for_signature(&child_signature, format)?;
237                if child_alignment > alignment {
238                    alignment = child_alignment;
239
240                    if alignment == 8 {
241                        // 8 bytes is max alignment so we can short-circuit here
242                        break;
243                    }
244                }
245            }
246
247            Ok(alignment)
248        }
249    }
250}
251
252fn alignment_for_dict_entry_signature(
253    #[allow(unused)] signature: &Signature<'_>,
254    format: EncodingFormat,
255) -> Result<usize> {
256    match format {
257        EncodingFormat::DBus => Ok(DICT_ENTRY_ALIGNMENT_DBUS),
258        #[cfg(feature = "gvariant")]
259        EncodingFormat::GVariant => {
260            if signature.len() < 4 {
261                return Err(serde::de::Error::invalid_length(
262                    signature.len(),
263                    &">= 4 characters in dict entry signature",
264                ));
265            }
266            let key_signature = Signature::from_str_unchecked(&signature[1..2]);
267            let key_alignment = alignment_for_signature(&key_signature, format)?;
268            if key_alignment == 8 {
269                // 8 bytes is max alignment so we can short-circuit here
270                return Ok(8);
271            }
272
273            let value_signature = Signature::from_str_unchecked(&signature[2..signature.len() - 1]);
274            let value_alignment = alignment_for_signature(&value_signature, format)?;
275            if value_alignment > key_alignment {
276                Ok(value_alignment)
277            } else {
278                Ok(key_alignment)
279            }
280        }
281    }
282}
283
284#[cfg(feature = "gvariant")]
285fn is_fixed_sized_struct_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
286    let inner_signature = Signature::from_str_unchecked(&signature[1..signature.len() - 1]);
287    let mut sig_parser = SignatureParser::new(inner_signature);
288    let mut fixed_sized = true;
289
290    while !sig_parser.done() {
291        let child_signature = sig_parser.parse_next_signature()?;
292
293        if !is_fixed_sized_signature(&child_signature)? {
294            // STRUCT is fixed-sized only if all its children are
295            fixed_sized = false;
296
297            break;
298        }
299    }
300
301    Ok(fixed_sized)
302}
303
304#[cfg(feature = "gvariant")]
305fn is_fixed_sized_dict_entry_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
306    let key_signature = Signature::from_str_unchecked(&signature[1..2]);
307    if !is_fixed_sized_signature(&key_signature)? {
308        return Ok(false);
309    }
310
311    let value_signature = Signature::from_str_unchecked(&signature[2..signature.len() - 1]);
312
313    is_fixed_sized_signature(&value_signature)
314}
315
316/// Slice the given slice of bytes safely and return an error if the slice is too small.
317pub(crate) fn subslice<I, T>(input: &[T], index: I) -> Result<&I::Output>
318where
319    I: SliceIndex<[T]>,
320{
321    input.get(index).ok_or(Error::OutOfBounds)
322}