use std::slice::SliceIndex;
#[cfg(feature = "gvariant")]
use crate::signature_parser::SignatureParser;
use crate::{Basic, EncodingFormat, Error, ObjectPath, Result, Signature};
#[cfg(unix)]
use crate::Fd;
pub const ARRAY_SIGNATURE_CHAR: char = 'a';
pub const ARRAY_SIGNATURE_STR: &str = "a";
pub(crate) const ARRAY_ALIGNMENT_DBUS: usize = 4;
pub const STRUCT_SIG_START_CHAR: char = '(';
pub const STRUCT_SIG_END_CHAR: char = ')';
pub const STRUCT_SIG_START_STR: &str = "(";
pub const STRUCT_SIG_END_STR: &str = ")";
pub(crate) const STRUCT_ALIGNMENT_DBUS: usize = 8;
pub const DICT_ENTRY_SIG_START_CHAR: char = '{';
pub const DICT_ENTRY_SIG_END_CHAR: char = '}';
pub const DICT_ENTRY_SIG_START_STR: &str = "{";
pub const DICT_ENTRY_SIG_END_STR: &str = "}";
pub(crate) const DICT_ENTRY_ALIGNMENT_DBUS: usize = 8;
pub const VARIANT_SIGNATURE_CHAR: char = 'v';
pub const VARIANT_SIGNATURE_STR: &str = "v";
pub(crate) const VARIANT_ALIGNMENT_DBUS: usize = 1;
#[cfg(feature = "gvariant")]
pub(crate) const VARIANT_ALIGNMENT_GVARIANT: usize = 8;
#[cfg(feature = "gvariant")]
pub const MAYBE_SIGNATURE_CHAR: char = 'm';
#[cfg(feature = "gvariant")]
pub const MAYBE_SIGNATURE_STR: &str = "m";
pub(crate) fn padding_for_n_bytes(value: usize, align: usize) -> usize {
let len_rounded_up = value.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
len_rounded_up.wrapping_sub(value)
}
pub(crate) fn usize_to_u32(value: usize) -> u32 {
assert!(
value <= (std::u32::MAX as usize),
"{} too large for `u32`",
value,
);
value as u32
}
pub(crate) fn usize_to_u8(value: usize) -> u8 {
assert!(
value <= (std::u8::MAX as usize),
"{} too large for `u8`",
value,
);
value as u8
}
pub(crate) fn f64_to_f32(value: f64) -> f32 {
assert!(
value <= (std::f32::MAX as f64),
"{} too large for `f32`",
value,
);
value as f32
}
pub(crate) fn alignment_for_signature(
signature: &Signature<'_>,
format: EncodingFormat,
) -> Result<usize> {
let alignment = match signature
.as_bytes()
.first()
.map(|b| *b as char)
.ok_or_else(|| -> Error { serde::de::Error::invalid_length(0, &">= 1 character") })?
{
u8::SIGNATURE_CHAR => u8::alignment(format),
bool::SIGNATURE_CHAR => bool::alignment(format),
i16::SIGNATURE_CHAR => i16::alignment(format),
u16::SIGNATURE_CHAR => u16::alignment(format),
i32::SIGNATURE_CHAR => i32::alignment(format),
u32::SIGNATURE_CHAR => u32::alignment(format),
#[cfg(unix)]
Fd::SIGNATURE_CHAR => u32::alignment(format),
i64::SIGNATURE_CHAR => i64::alignment(format),
u64::SIGNATURE_CHAR => u64::alignment(format),
f64::SIGNATURE_CHAR => f64::alignment(format),
<&str>::SIGNATURE_CHAR => <&str>::alignment(format),
ObjectPath::SIGNATURE_CHAR => ObjectPath::alignment(format),
Signature::SIGNATURE_CHAR => Signature::alignment(format),
VARIANT_SIGNATURE_CHAR => match format {
EncodingFormat::DBus => VARIANT_ALIGNMENT_DBUS,
#[cfg(feature = "gvariant")]
EncodingFormat::GVariant => VARIANT_ALIGNMENT_GVARIANT,
},
ARRAY_SIGNATURE_CHAR => alignment_for_array_signature(signature, format)?,
STRUCT_SIG_START_CHAR => alignment_for_struct_signature(signature, format)?,
DICT_ENTRY_SIG_START_CHAR => alignment_for_dict_entry_signature(signature, format)?,
#[cfg(feature = "gvariant")]
MAYBE_SIGNATURE_CHAR => alignment_for_maybe_signature(signature, format)?,
_ => {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(signature),
&"a valid signature",
))
}
};
Ok(alignment)
}
#[cfg(feature = "gvariant")]
pub(crate) fn is_fixed_sized_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
match signature
.as_bytes()
.first()
.map(|b| *b as char)
.ok_or_else(|| -> Error { serde::de::Error::invalid_length(0, &">= 1 character") })?
{
u8::SIGNATURE_CHAR
| bool::SIGNATURE_CHAR
| i16::SIGNATURE_CHAR
| u16::SIGNATURE_CHAR
| i32::SIGNATURE_CHAR
| u32::SIGNATURE_CHAR
| i64::SIGNATURE_CHAR
| u64::SIGNATURE_CHAR
| f64::SIGNATURE_CHAR => Ok(true),
#[cfg(unix)]
Fd::SIGNATURE_CHAR => Ok(true),
STRUCT_SIG_START_CHAR => is_fixed_sized_struct_signature(signature),
DICT_ENTRY_SIG_START_CHAR => is_fixed_sized_dict_entry_signature(signature),
_ => Ok(false),
}
}
macro_rules! signature_string {
($signature:expr) => {{
let mut s = String::with_capacity(255);
s.push_str($signature);
Signature::from_string_unchecked(s)
}};
}
macro_rules! check_child_value_signature {
($expected_signature:expr, $child_signature:expr, $child_name:literal) => {{
if $child_signature != $expected_signature {
let unexpected = format!("{} with signature `{}`", $child_name, $child_signature,);
let expected = format!("{} with signature `{}`", $child_name, $expected_signature);
return Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Str(&unexpected),
&expected.as_str(),
));
}
}};
}
fn alignment_for_single_child_type_signature(
#[allow(unused)] signature: &Signature<'_>,
format: EncodingFormat,
dbus_align: usize,
) -> Result<usize> {
match format {
EncodingFormat::DBus => Ok(dbus_align),
#[cfg(feature = "gvariant")]
EncodingFormat::GVariant => {
let child_signature = signature.slice(1..);
alignment_for_signature(&child_signature, format)
}
}
}
fn alignment_for_array_signature(
signature: &Signature<'_>,
format: EncodingFormat,
) -> Result<usize> {
alignment_for_single_child_type_signature(signature, format, ARRAY_ALIGNMENT_DBUS)
}
#[cfg(feature = "gvariant")]
fn alignment_for_maybe_signature(
signature: &Signature<'_>,
format: EncodingFormat,
) -> Result<usize> {
alignment_for_single_child_type_signature(signature, format, 1)
}
fn alignment_for_struct_signature(
#[allow(unused)] signature: &Signature<'_>,
format: EncodingFormat,
) -> Result<usize> {
match format {
EncodingFormat::DBus => Ok(STRUCT_ALIGNMENT_DBUS),
#[cfg(feature = "gvariant")]
EncodingFormat::GVariant => {
if signature.len() < 3 {
return Err(serde::de::Error::invalid_length(
signature.len(),
&">= 3 characters in struct signature",
));
}
let inner_signature = Signature::from_str_unchecked(&signature[1..signature.len() - 1]);
let mut sig_parser = SignatureParser::new(inner_signature);
let mut alignment = 0;
while !sig_parser.done() {
let child_signature = sig_parser.parse_next_signature()?;
let child_alignment = alignment_for_signature(&child_signature, format)?;
if child_alignment > alignment {
alignment = child_alignment;
if alignment == 8 {
break;
}
}
}
Ok(alignment)
}
}
}
fn alignment_for_dict_entry_signature(
#[allow(unused)] signature: &Signature<'_>,
format: EncodingFormat,
) -> Result<usize> {
match format {
EncodingFormat::DBus => Ok(DICT_ENTRY_ALIGNMENT_DBUS),
#[cfg(feature = "gvariant")]
EncodingFormat::GVariant => {
if signature.len() < 4 {
return Err(serde::de::Error::invalid_length(
signature.len(),
&">= 4 characters in dict entry signature",
));
}
let key_signature = Signature::from_str_unchecked(&signature[1..2]);
let key_alignment = alignment_for_signature(&key_signature, format)?;
if key_alignment == 8 {
return Ok(8);
}
let value_signature = Signature::from_str_unchecked(&signature[2..signature.len() - 1]);
let value_alignment = alignment_for_signature(&value_signature, format)?;
if value_alignment > key_alignment {
Ok(value_alignment)
} else {
Ok(key_alignment)
}
}
}
}
#[cfg(feature = "gvariant")]
fn is_fixed_sized_struct_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
let inner_signature = Signature::from_str_unchecked(&signature[1..signature.len() - 1]);
let mut sig_parser = SignatureParser::new(inner_signature);
let mut fixed_sized = true;
while !sig_parser.done() {
let child_signature = sig_parser.parse_next_signature()?;
if !is_fixed_sized_signature(&child_signature)? {
fixed_sized = false;
break;
}
}
Ok(fixed_sized)
}
#[cfg(feature = "gvariant")]
fn is_fixed_sized_dict_entry_signature<'a>(signature: &'a Signature<'a>) -> Result<bool> {
let key_signature = Signature::from_str_unchecked(&signature[1..2]);
if !is_fixed_sized_signature(&key_signature)? {
return Ok(false);
}
let value_signature = Signature::from_str_unchecked(&signature[2..signature.len() - 1]);
is_fixed_sized_signature(&value_signature)
}
pub(crate) fn subslice<I, T>(input: &[T], index: I) -> Result<&I::Output>
where
I: SliceIndex<[T]>,
{
input.get(index).ok_or(Error::OutOfBounds)
}