use std::{
ffi::{c_int, c_void, CString},
mem::MaybeUninit,
};
use nix::errno::Errno;
use crate::utils::{Fraction, Id, Rectangle};
static CALLBACKS: spa_sys::spa_pod_builder_callbacks = spa_sys::spa_pod_builder_callbacks {
version: spa_sys::SPA_VERSION_POD_BUILDER_CALLBACKS,
overflow: Some(Builder::overflow),
};
struct BuilderInner<'d> {
builder: spa_sys::spa_pod_builder,
data: &'d mut Vec<u8>,
}
pub struct Builder<'d> {
inner: Box<BuilderInner<'d>>,
}
impl<'d> Builder<'d> {
unsafe extern "C" fn overflow(data: *mut c_void, size: u32) -> c_int {
let this: *mut BuilderInner = data.cast();
assert!(!this.is_null());
assert!(size as usize > (*this).data.len());
(*this).data.resize(size as usize, 0);
(*this).builder.data = (*this).data.as_mut_ptr().cast::<c_void>();
(*this).builder.size = (*this)
.data
.len()
.try_into()
.expect("data length does not fit in a u32");
0
}
pub fn new(data: &'d mut Vec<u8>) -> Self {
unsafe {
let mut builder: MaybeUninit<spa_sys::spa_pod_builder> = MaybeUninit::uninit();
spa_sys::spa_pod_builder_init(
builder.as_mut_ptr(),
data.as_mut_ptr().cast(),
data.len()
.try_into()
.expect("data length does not fit in a u32"),
);
let inner = Box::new(BuilderInner {
builder: builder.assume_init(),
data,
});
spa_sys::spa_pod_builder_set_callbacks(
std::ptr::addr_of!(inner.builder).cast_mut(),
std::ptr::addr_of!(CALLBACKS),
std::ptr::addr_of!(*inner).cast::<c_void>().cast_mut(),
);
Self { inner }
}
}
pub fn as_raw(&self) -> &spa_sys::spa_pod_builder {
&self.inner.builder
}
pub fn as_raw_ptr(&self) -> *mut spa_sys::spa_pod_builder {
std::ptr::addr_of!(self.inner.builder).cast_mut()
}
pub unsafe fn state(&self) -> spa_sys::spa_pod_builder_state {
let mut state: MaybeUninit<spa_sys::spa_pod_builder_state> = MaybeUninit::uninit();
spa_sys::spa_pod_builder_get_state(self.as_raw_ptr(), state.as_mut_ptr());
state.assume_init()
}
pub unsafe fn reset(&mut self, state: *mut spa_sys::spa_pod_builder_state) {
spa_sys::spa_pod_builder_reset(self.as_raw_ptr(), state)
}
pub unsafe fn deref(&mut self, offset: u32) -> *mut spa_sys::spa_pod {
spa_sys::spa_pod_builder_deref(self.as_raw_ptr(), offset)
}
pub unsafe fn frame(&mut self, frame: *mut spa_sys::spa_pod_frame) -> *mut spa_sys::spa_pod {
spa_sys::spa_pod_builder_frame(self.as_raw_ptr(), frame)
}
pub unsafe fn push(
&mut self,
frame: *mut spa_sys::spa_pod_frame,
pod: *const spa_sys::spa_pod,
offset: u32,
) {
spa_sys::spa_pod_builder_push(self.as_raw_ptr(), frame, pod, offset)
}
pub fn raw_padded(&mut self, data: &[u8]) -> Result<(), Errno> {
let res = unsafe {
spa_sys::spa_pod_builder_raw_padded(
self.as_raw_ptr(),
data.as_ptr().cast::<c_void>(),
data.len().try_into().unwrap(),
)
};
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub unsafe fn pop(&mut self, frame: &mut spa_sys::spa_pod_frame) {
unsafe {
spa_sys::spa_pod_builder_pop(self.as_raw_ptr(), frame as *mut _);
}
}
pub fn add_none(&mut self) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_none(self.as_raw_ptr());
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_bool(&mut self, val: bool) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_bool(self.as_raw_ptr(), val);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_id(&mut self, val: Id) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_id(self.as_raw_ptr(), val.0);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_int(&mut self, val: i32) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_int(self.as_raw_ptr(), val);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_long(&mut self, val: i64) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_long(self.as_raw_ptr(), val);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_float(&mut self, val: f32) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_float(self.as_raw_ptr(), val);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_double(&mut self, val: f64) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_double(self.as_raw_ptr(), val);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_string(&mut self, string: &str) -> Result<(), Errno> {
let c_str = CString::new(string).expect("string should not contain an interior null byte");
let res = unsafe { spa_sys::spa_pod_builder_string(self.as_raw_ptr(), c_str.as_ptr()) };
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub fn add_bytes(&mut self, bytes: &[u8]) -> Result<(), Errno> {
let res = unsafe {
spa_sys::spa_pod_builder_bytes(
self.as_raw_ptr(),
bytes.as_ptr().cast::<c_void>(),
bytes.len().try_into().unwrap(),
)
};
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub unsafe fn add_pointer(&mut self, type_: Id, val: *const c_void) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_pointer(self.as_raw_ptr(), type_.0, val);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_fd(&mut self, val: std::os::fd::RawFd) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_fd(self.as_raw_ptr(), val.into());
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_rectangle(&mut self, val: Rectangle) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_rectangle(self.as_raw_ptr(), val.width, val.height);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_fraction(&mut self, val: Fraction) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_fraction(self.as_raw_ptr(), val.num, val.denom);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub unsafe fn push_array(
&mut self,
frame: &mut MaybeUninit<spa_sys::spa_pod_frame>,
) -> Result<(), Errno> {
let res = spa_sys::spa_pod_builder_push_array(self.as_raw_ptr(), frame.as_mut_ptr());
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub unsafe fn add_array(
&mut self,
child_size: u32,
child_type: u32,
n_elems: u32,
elems: *const c_void,
) -> Result<(), Errno> {
let res = spa_sys::spa_pod_builder_array(
self.as_raw_ptr(),
child_size,
child_type,
n_elems,
elems,
);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub unsafe fn push_choice(
&mut self,
frame: &mut MaybeUninit<spa_sys::spa_pod_frame>,
type_: u32,
flags: u32, ) -> Result<(), Errno> {
let res = spa_sys::spa_pod_builder_push_choice(
self.as_raw_ptr(),
frame.as_mut_ptr(),
type_,
flags,
);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub unsafe fn push_struct(
&mut self,
frame: &mut MaybeUninit<spa_sys::spa_pod_frame>,
) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_push_struct(self.as_raw_ptr(), frame.as_mut_ptr());
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub unsafe fn push_object(
&mut self,
frame: &mut MaybeUninit<spa_sys::spa_pod_frame>,
type_: u32,
id: u32,
) -> Result<(), Errno> {
unsafe {
let res = spa_sys::spa_pod_builder_push_object(
self.as_raw_ptr(),
frame.as_mut_ptr(),
type_,
id,
);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn add_prop(&mut self, key: u32, flags: u32) -> Result<(), Errno> {
let res = unsafe { spa_sys::spa_pod_builder_prop(self.as_raw_ptr(), key, flags) };
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub unsafe fn push_sequence(
&mut self,
frame: &mut MaybeUninit<spa_sys::spa_pod_frame>,
unit: u32,
) -> Result<(), Errno> {
let res =
spa_sys::spa_pod_builder_push_sequence(self.as_raw_ptr(), frame.as_mut_ptr(), unit);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub fn add_control(&mut self, offset: u32, type_: u32) -> u32 {
unsafe { spa_sys::spa_pod_builder_control(self.as_raw_ptr(), offset, type_) }
}
}
#[macro_export]
macro_rules! __builder_add__ {
($builder:expr, None) => {
$crate::pod::builder::Builder::add_none($builder)
};
($builder:expr, Bool($val:expr)) => {
$crate::pod::builder::Builder::add_bool($builder, $val)
};
($builder:expr, Id($val:expr)) => {
$crate::pod::builder::Builder::add_id($builder, $val)
};
($builder:expr, Int($val:expr)) => {
$crate::pod::builder::Builder::add_int($builder, $val)
};
($builder:expr, Long($val:expr)) => {
$crate::pod::builder::Builder::add_long($builder, $val)
};
($builder:expr, Float($val:expr)) => {
$crate::pod::builder::Builder::add_float($builder, $val)
};
($builder:expr, Double($val:expr)) => {
$crate::pod::builder::Builder::add_double($builder, $val)
};
($builder:expr, String($val:expr)) => {
$crate::pod::builder::Builder::add_string($builder, $val)
};
($builder:expr, Bytes($val:expr)) => {
$crate::pod::builder::Builder::add_bytes($builder, $val)
};
($builder:expr, Pointer($type_:expr, $val:expr)) => {
$crate::pod::builder::Builder::add_bool($builder, $type_, $val)
};
($builder:expr, Fd($val:expr)) => {
$crate::pod::builder::Builder::add_fd($builder, $val)
};
($builder:expr, Rectangle($val:expr)) => {
$crate::pod::builder::Builder::add_rectangle($builder, $val)
};
($builder:expr, Fraction($val:expr)) => {
$crate::pod::builder::Builder::add_fraction($builder, $val)
};
(
$builder:expr,
Struct {
$( $field_type:tt $field:tt ),* $(,)?
}
) => {
'outer: {
let mut frame: ::std::mem::MaybeUninit<$crate::sys::spa_pod_frame> = ::std::mem::MaybeUninit::uninit();
let res = unsafe { $crate::pod::builder::Builder::push_struct($builder, &mut frame) };
if res.is_err() {
break 'outer res;
}
$(
let res = $crate::__builder_add__!($builder, $field_type $field);
if res.is_err() {
break 'outer res;
}
)*
unsafe { $crate::pod::builder::Builder::pop($builder, frame.assume_init_mut()) }
Ok(())
}
};
(
$builder:expr,
Object($type_:expr, $id:expr $(,)?) {
$( $key:expr => $value_type:tt $value:tt ),* $(,)?
}
) => {
'outer: {
let mut frame: ::std::mem::MaybeUninit<$crate::sys::spa_pod_frame> = ::std::mem::MaybeUninit::uninit();
let res = unsafe { $crate::pod::builder::Builder::push_object($builder, &mut frame, $type_, $id) };
if res.is_err() {
break 'outer res;
}
$(
let res = $crate::pod::builder::Builder::add_prop($builder, $key, 0);
if res.is_err() {
break 'outer res;
}
let res = $crate::__builder_add__!($builder, $value_type $value);
if res.is_err() {
break 'outer res;
}
)*
unsafe { $crate::pod::builder::Builder::pop($builder, frame.assume_init_mut()) }
Ok(())
}
};
}
pub use __builder_add__ as builder_add;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg_attr(miri, ignore)]
fn build_empty_struct() {
let mut data = Vec::new();
let mut builder = Builder::new(&mut data);
let res = builder_add!(&mut builder, Struct {});
assert!(res.is_ok());
let other: Vec<u8> = [
0u32.to_ne_bytes(), 14u32.to_ne_bytes(), ]
.iter()
.copied()
.flatten()
.collect();
assert_eq!(&data, &other)
}
#[test]
#[cfg_attr(miri, ignore)]
fn build_small_struct() {
let mut data = Vec::new();
let mut builder = Builder::new(&mut data);
let res = builder_add!(
&mut builder,
Struct {
Int(3),
}
);
assert!(res.is_ok());
let other: Vec<u8> = [
16u32.to_ne_bytes(), 14u32.to_ne_bytes(), 4u32.to_ne_bytes(), 4u32.to_ne_bytes(), 3i32.to_ne_bytes(), [0, 0, 0, 0], ]
.iter()
.copied()
.flatten()
.collect();
assert_eq!(&data, &other)
}
#[test]
#[cfg_attr(miri, ignore)]
fn build_complex_struct() {
let mut data = Vec::new();
let mut builder = Builder::new(&mut data);
let res = builder_add!(
&mut builder,
Struct {
Struct {
Float(31.3),
String("foo")
},
Int(3),
}
);
dbg!(res.unwrap());
assert!(res.is_ok());
}
#[test]
#[cfg_attr(miri, ignore)]
fn build_empty_object() {
use crate::param::ParamType;
let mut data = Vec::new();
let mut builder = Builder::new(&mut data);
let res = builder_add!(
&mut builder,
Object(
ParamType::Format.as_raw(),
0,
) {}
);
assert!(res.is_ok());
}
#[test]
#[cfg_attr(miri, ignore)]
fn build_object() {
use crate::format::FormatProperties;
use crate::format::{MediaSubtype, MediaType};
use crate::param::ParamType;
let mut data = Vec::new();
let mut builder = Builder::new(&mut data);
let res = builder_add!(
&mut builder,
Object(
ParamType::Format.as_raw(),
0,
) {
FormatProperties::MediaType.as_raw() => Id(crate::utils::Id(MediaType::Audio.as_raw())),
FormatProperties::MediaSubtype.as_raw() => Id(crate::utils::Id(MediaSubtype::Raw.as_raw())),
}
);
assert!(res.is_ok());
}
}