1 | use std::slice::SliceIndex; |
2 | |
3 | #[cfg (feature = "gvariant" )] |
4 | use crate::signature_parser::SignatureParser; |
5 | use crate::{serialized::Format, Basic, Error, ObjectPath, Result, Signature}; |
6 | |
7 | #[cfg (unix)] |
8 | use crate::Fd; |
9 | |
10 | /// The prefix of ARRAY type signature, as a character. Provided for manual signature creation. |
11 | pub const ARRAY_SIGNATURE_CHAR: char = 'a' ; |
12 | /// The prefix of ARRAY type signature, as a string. Provided for manual signature creation. |
13 | pub const ARRAY_SIGNATURE_STR: &str = "a" ; |
14 | pub(crate) const ARRAY_ALIGNMENT_DBUS: usize = 4; |
15 | /// The opening character of STRUCT type signature. Provided for manual signature creation. |
16 | pub const STRUCT_SIG_START_CHAR: char = '(' ; |
17 | /// The closing character of STRUCT type signature. Provided for manual signature creation. |
18 | pub const STRUCT_SIG_END_CHAR: char = ')' ; |
19 | /// The opening character of STRUCT type signature, as a string. Provided for manual signature |
20 | /// creation. |
21 | pub const STRUCT_SIG_START_STR: &str = "(" ; |
22 | /// The closing character of STRUCT type signature, as a string. Provided for manual signature |
23 | /// creation. |
24 | pub const STRUCT_SIG_END_STR: &str = ")" ; |
25 | pub(crate) const STRUCT_ALIGNMENT_DBUS: usize = 8; |
26 | /// The opening character of DICT_ENTRY type signature. Provided for manual signature creation. |
27 | pub const DICT_ENTRY_SIG_START_CHAR: char = '{' ; |
28 | /// The closing character of DICT_ENTRY type signature. Provided for manual signature creation. |
29 | pub 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. |
32 | pub 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. |
35 | pub const DICT_ENTRY_SIG_END_STR: &str = "}" ; |
36 | pub(crate) const DICT_ENTRY_ALIGNMENT_DBUS: usize = 8; |
37 | /// The VARIANT type signature. Provided for manual signature creation. |
38 | pub const VARIANT_SIGNATURE_CHAR: char = 'v' ; |
39 | /// The VARIANT type signature, as a string. Provided for manual signature creation. |
40 | pub const VARIANT_SIGNATURE_STR: &str = "v" ; |
41 | pub(crate) const VARIANT_ALIGNMENT_DBUS: usize = 1; |
42 | #[cfg (feature = "gvariant" )] |
43 | pub(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" )] |
47 | pub 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" )] |
51 | pub const MAYBE_SIGNATURE_STR: &str = "m" ; |
52 | |
53 | pub(crate) fn padding_for_n_bytes(value: usize, align: usize) -> usize { |
54 | let len_rounded_up: usize = value.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); |
55 | |
56 | len_rounded_up.wrapping_sub(value) |
57 | } |
58 | |
59 | pub(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 | |
69 | pub(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 | |
75 | pub(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! |
82 | pub(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" )] |
125 | pub(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 |
150 | macro_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 | |
159 | macro_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 | |
173 | fn 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 | |
189 | fn 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" )] |
194 | fn alignment_for_maybe_signature(signature: &Signature<'_>, format: Format) -> Result<usize> { |
195 | alignment_for_single_child_type_signature(signature, format, 1) |
196 | } |
197 | |
198 | fn 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 | |
235 | fn 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" )] |
268 | fn 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" )] |
288 | fn 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. |
300 | pub(crate) fn subslice<I, T>(input: &[T], index: I) -> Result<&I::Output> |
301 | where |
302 | I: SliceIndex<[T]>, |
303 | { |
304 | input.get(index).ok_or(err:Error::OutOfBounds) |
305 | } |
306 | |