use std::{borrow::Cow, num::NonZeroU32, ops::Range, str};
use winnow::stream::Accumulate;
use crate::{
Error, ObjectPath, Signature,
message::{FieldCode, Fields, Type, header::PRIMARY_HEADER_SIZE},
names::{BusName, ErrorName, InterfaceName, MemberName, UniqueName, WellKnownName},
};
#[derive(Debug, Default, Clone)]
pub(crate) struct QuickFields {
path: Option<FieldPos>,
interface: Option<FieldPos>,
member: Option<FieldPos>,
error_name: Option<FieldPos>,
reply_serial: Option<NonZeroU32>,
destination: Option<FieldPos>,
unique_destination: bool,
sender: Option<FieldPos>,
signature: Signature,
#[cfg(feature = "bus-impl")]
signature_pos: Option<FieldPos>,
unix_fds: Option<u32>,
codes: u16,
}
impl QuickFields {
#[cfg(unix)]
pub(crate) fn unix_fds(&self) -> Option<u32> {
self.unix_fds
}
pub(crate) fn check_required(&self, msg_type: Type) -> Result<(), Error> {
let present = match msg_type {
Type::MethodCall => self.path.is_some() && self.member.is_some(),
Type::MethodReturn => self.reply_serial.is_some(),
Type::Error => self.error_name.is_some() && self.reply_serial.is_some(),
Type::Signal => {
self.path.is_some() && self.interface.is_some() && self.member.is_some()
}
};
if present {
Ok(())
} else {
Err(Error::MissingField)
}
}
pub(in crate::message) unsafe fn fields<'m>(&'m self, bytes: &'m [u8]) -> Fields<'m> {
let read = |pos: Option<FieldPos>| {
pos.map(|pos| {
unsafe { str::from_utf8_unchecked(&bytes[pos.range()]) }
})
};
Fields {
path: read(self.path).map(ObjectPath::from_str_unchecked),
interface: read(self.interface).map(InterfaceName::from_str_unchecked),
member: read(self.member).map(MemberName::from_str_unchecked),
error_name: read(self.error_name).map(ErrorName::from_str_unchecked),
reply_serial: self.reply_serial,
destination: read(self.destination).map(|name| {
if self.unique_destination {
BusName::Unique(UniqueName::from_str_unchecked(name))
} else {
BusName::WellKnown(WellKnownName::from_str_unchecked(name))
}
}),
sender: read(self.sender).map(UniqueName::from_str_unchecked),
signature: Cow::Borrowed(&self.signature),
unix_fds: self.unix_fds,
}
}
pub(in crate::message) fn signature(&self) -> &Signature {
&self.signature
}
#[cfg(feature = "bus-impl")]
pub(in crate::message) fn signature_bytes<'m>(&self, bytes: &'m [u8]) -> &'m [u8] {
self.signature_pos.map_or(&[], |pos| &bytes[pos.range()])
}
#[cfg(feature = "bus-impl")]
pub(in crate::message) fn sender_range(&self) -> Option<Range<usize>> {
self.sender.map(FieldPos::range)
}
#[cfg(feature = "bus-impl")]
pub(in crate::message) fn with_sender(&self, name: Range<usize>) -> Self {
Self {
sender: Some(FieldPos::new(name.start, name.end)),
..self.clone()
}
}
pub(super) fn repeated(&self) -> bool {
self.codes & REPEATED != 0
}
}
impl Accumulate<Field> for QuickFields {
fn initial(_capacity: Option<usize>) -> Self {
Self::default()
}
#[inline(always)]
fn accumulate(&mut self, field: Field) {
let code = 1 << field.code() as u16;
self.codes |= code | (u16::from(self.codes & code != 0) * REPEATED);
match field {
Field::Path(pos) => self.path = Some(pos),
Field::Interface(pos) => self.interface = Some(pos),
Field::Member(pos) => self.member = Some(pos),
Field::ErrorName(pos) => self.error_name = Some(pos),
Field::ReplySerial(serial) => self.reply_serial = Some(serial),
Field::Destination { pos, unique } => {
self.destination = Some(pos);
self.unique_destination = unique;
}
Field::Sender(pos) => self.sender = Some(pos),
Field::Signature {
signature,
#[cfg(feature = "bus-impl")]
pos,
} => {
self.signature = signature;
#[cfg(feature = "bus-impl")]
{
self.signature_pos = Some(pos);
}
}
Field::UnixFds(unix_fds) => self.unix_fds = Some(unix_fds),
}
}
}
const REPEATED: u16 = 1;
pub(super) enum Field {
Path(FieldPos),
Interface(FieldPos),
Member(FieldPos),
ErrorName(FieldPos),
ReplySerial(NonZeroU32),
Destination {
pos: FieldPos,
unique: bool,
},
Sender(FieldPos),
Signature {
signature: Signature,
#[cfg(feature = "bus-impl")]
pos: FieldPos,
},
UnixFds(u32),
}
impl Field {
#[inline(always)]
fn code(&self) -> FieldCode {
match self {
Self::Path(_) => FieldCode::Path,
Self::Interface(_) => FieldCode::Interface,
Self::Member(_) => FieldCode::Member,
Self::ErrorName(_) => FieldCode::ErrorName,
Self::ReplySerial(_) => FieldCode::ReplySerial,
Self::Destination { .. } => FieldCode::Destination,
Self::Sender(_) => FieldCode::Sender,
Self::Signature { .. } => FieldCode::Signature,
Self::UnixFds(_) => FieldCode::UnixFDs,
}
}
#[cfg_attr(not(feature = "bus-impl"), expect(unused_variables))]
pub(super) fn signature(signature: Signature, pos: FieldPos) -> Self {
Self::Signature {
signature,
#[cfg(feature = "bus-impl")]
pos,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(super) struct FieldPos {
start: NonZeroU32,
end: u32,
}
impl FieldPos {
pub(super) fn new(start: usize, end: usize) -> Self {
debug_assert!(start >= PRIMARY_HEADER_SIZE);
Self {
start: unsafe { NonZeroU32::new_unchecked(start as u32) },
end: end as u32,
}
}
fn range(self) -> Range<usize> {
self.start.get() as usize..self.end as usize
}
}