use pipewire_native_macros as macros;
use pipewire_native_spa as spa;
use super::{Marshallable, HEADER_LEN};
pub(crate) struct Message<T: Marshallable, F: spa::pod::Pod> {
pub(crate) header: Header,
pub(crate) object: T,
pub(crate) footer: Option<F>,
}
pub(crate) struct Header {
pub(crate) id: u32,
pub(crate) opcode: u8,
pub(crate) size: u32, pub(crate) seq: u32,
pub(crate) n_fds: u32,
}
impl<T: Marshallable, F: spa::pod::Pod<DecodesTo = F>> spa::pod::Pod for Message<T, F> {
type DecodesTo = Self;
fn encode(&self, data: &mut [u8]) -> Result<usize, spa::pod::Error> {
if data.len() < HEADER_LEN + 1 {
return Err(spa::pod::Error::NoSpace);
}
let payload_size = self.object.encode(&mut data[HEADER_LEN..])?;
let footer_size = if let Some(footer) = &self.footer {
footer.encode(&mut data[HEADER_LEN + payload_size..])?
} else {
0
};
let size = payload_size + footer_size;
let header = Header {
size: size as u32,
..self.header
};
header.encode(data)?;
Ok(HEADER_LEN + size)
}
fn decode(data: &[u8]) -> Result<(Self::DecodesTo, usize), spa::pod::Error> {
if data.len() < HEADER_LEN {
return Err(spa::pod::Error::Invalid(
"Not enough data for header".to_string(),
));
}
let (header, header_size) = Header::decode(data)?;
let size = header.size as usize;
let (object, payload_size) = T::decode(header.opcode, &data[header_size..])?;
let (footer, footer_size) = if size > header_size + payload_size {
let (f, s) = F::decode(&data[header_size + payload_size..size])?;
(Some(f), s)
} else {
(None, 0)
};
if size != header_size + payload_size + footer_size {
Ok((
Message {
header,
object,
footer,
},
size,
))
} else {
Err(spa::pod::Error::Invalid(format!(
"Data left over in message: {} != {}",
size,
header_size + payload_size + footer_size
)))
}
}
}
impl spa::pod::Pod for Header {
type DecodesTo = Self;
fn encode(&self, data: &mut [u8]) -> Result<usize, spa::pod::Error> {
if data.len() < 16 {
return Err(spa::pod::Error::NoSpace);
}
data[0..4].copy_from_slice(&self.id.to_ne_bytes());
let word = (self.opcode as u32) << 24 | (self.size & ((1 << 24) - 1));
data[4..8].copy_from_slice(&word.to_ne_bytes());
data[8..12].copy_from_slice(&self.seq.to_ne_bytes());
data[12..16].copy_from_slice(&self.n_fds.to_ne_bytes());
Ok(16)
}
fn decode(data: &[u8]) -> Result<(Self::DecodesTo, usize), spa::pod::Error> {
if data.len() < 16 {
return Err(spa::pod::Error::Invalid(
"Insufficent data for header".to_string(),
));
}
let id = u32::from_ne_bytes(data[0..4].try_into().unwrap());
let word = u32::from_ne_bytes(data[4..8].try_into().unwrap());
let opcode = (word >> 24) as u8;
let size = word & ((1 << 24) - 1);
let seq = u32::from_ne_bytes(data[8..12].try_into().unwrap());
let n_fds = u32::from_ne_bytes(data[12..16].try_into().unwrap());
Ok((
Header {
id,
opcode,
size,
seq,
n_fds,
},
16,
))
}
}
pub(crate) struct CoreFooter {
pub(crate) payloads: Vec<CoreFooterPayload>,
}
impl CoreFooter {
pub(crate) fn new() -> Self {
CoreFooter { payloads: vec![] }
}
#[allow(unused)]
pub(crate) fn push(&mut self, payload: CoreFooterPayload) {
self.payloads.push(payload);
}
}
pub(crate) struct ClientFooter {
pub(crate) payloads: Vec<ClientFooterPayload>,
}
impl ClientFooter {
pub(crate) fn new() -> Self {
ClientFooter { payloads: vec![] }
}
pub(crate) fn push(&mut self, payload: ClientFooterPayload) {
self.payloads.push(payload);
}
}
pub(crate) enum CoreFooterPayload {
Generation(CoreGeneration),
}
pub(crate) enum ClientFooterPayload {
Generation(ClientGeneration),
}
#[derive(macros::PodStruct)]
pub(crate) struct CoreGeneration {
pub(crate) registry_generation: i64,
}
#[derive(macros::PodStruct)]
pub(crate) struct ClientGeneration {
pub(crate) client_generation: i64,
}
impl spa::pod::Pod for CoreFooter {
type DecodesTo = Self;
fn encode(&self, data: &mut [u8]) -> Result<usize, spa::pod::Error> {
let mut builder = spa::pod::builder::Builder::new(data);
builder = builder.push_struct(|mut sb| {
for p in &self.payloads {
sb = match p {
CoreFooterPayload::Generation(g) => {
sb.push_id(spa::pod::types::Id(0u32)).push_pod(g)
}
};
}
sb
});
let out = builder.build()?;
Ok(out.len())
}
fn decode(data: &[u8]) -> Result<(Self::DecodesTo, usize), spa::pod::Error> {
let mut parser = spa::pod::parser::Parser::new(data);
parser.pop_struct(|sp| {
let mut footer = CoreFooter::new();
while sp.available() > 0 {
let opcode = sp.pop_id::<u32>()?;
let payload = match opcode.0 {
0 => {
let g = sp.pop_pod::<CoreGeneration>()?;
CoreFooterPayload::Generation(g)
}
opcode => {
return Err(spa::pod::Error::Invalid(format!(
"Invalid footer opcode {opcode}"
)))
}
};
footer.payloads.push(payload);
}
Ok(footer)
})
}
}
impl spa::pod::Pod for ClientFooter {
type DecodesTo = Self;
fn encode(&self, data: &mut [u8]) -> Result<usize, spa::pod::Error> {
let mut builder = spa::pod::builder::Builder::new(data);
builder = builder.push_struct(|mut sb| {
for p in &self.payloads {
sb = match p {
ClientFooterPayload::Generation(g) => {
sb.push_id(spa::pod::types::Id(0u32)).push_pod(g)
}
};
}
sb
});
let out = builder.build()?;
Ok(out.len())
}
fn decode(data: &[u8]) -> Result<(Self::DecodesTo, usize), spa::pod::Error> {
let mut parser = spa::pod::parser::Parser::new(data);
parser.pop_struct(|sp| {
let mut footer = ClientFooter::new();
while sp.available() > 0 {
let opcode = sp.pop_id::<u32>()?;
let payload = match opcode.0 {
0 => {
let g = sp.pop_pod::<ClientGeneration>()?;
ClientFooterPayload::Generation(g)
}
opcode => {
return Err(spa::pod::Error::Invalid(format!(
"Invalid footer opcode {opcode}"
)))
}
};
footer.payloads.push(payload);
}
Ok(footer)
})
}
}