use std::{
ffi::{c_char, c_double, c_float, c_int, c_long, c_void, CStr},
marker::PhantomData,
mem::MaybeUninit,
};
use nix::errno::Errno;
use crate::utils::{Fraction, Id, Rectangle};
#[repr(transparent)]
pub struct Parser<'d> {
parser: spa_sys::spa_pod_parser,
data: PhantomData<&'d [u8]>,
}
impl<'d> Parser<'d> {
pub fn new(data: &'d [u8]) -> Self {
unsafe {
let mut parser: MaybeUninit<spa_sys::spa_pod_parser> = MaybeUninit::uninit();
spa_sys::spa_pod_parser_init(
parser.as_mut_ptr(),
data.as_ptr().cast(),
data.len()
.try_into()
.expect("data length does not fit in a u32"),
);
Self {
parser: parser.assume_init(),
data: PhantomData,
}
}
}
pub fn from_pod(pod: &'d crate::pod::Pod) -> Self {
unsafe {
let mut parser: MaybeUninit<spa_sys::spa_pod_parser> = MaybeUninit::uninit();
spa_sys::spa_pod_parser_pod(parser.as_mut_ptr(), pod.as_raw_ptr());
Self {
parser: parser.assume_init(),
data: PhantomData,
}
}
}
pub fn as_raw(&self) -> &spa_sys::spa_pod_parser {
&self.parser
}
pub fn as_raw_ptr(&self) -> *mut spa_sys::spa_pod_parser {
std::ptr::addr_of!(self.parser).cast_mut()
}
pub fn into_raw(self) -> spa_sys::spa_pod_parser {
self.parser
}
pub unsafe fn state(&self) -> spa_sys::spa_pod_parser_state {
let mut state: MaybeUninit<spa_sys::spa_pod_parser_state> = MaybeUninit::uninit();
spa_sys::spa_pod_parser_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_parser_state) {
spa_sys::spa_pod_parser_reset(self.as_raw_ptr(), state)
}
pub unsafe fn deref(&mut self, offset: u32, size: u32) -> *mut spa_sys::spa_pod {
spa_sys::spa_pod_parser_deref(self.as_raw_ptr(), offset, size)
}
pub unsafe fn frame(&mut self, frame: *mut spa_sys::spa_pod_frame) -> *mut spa_sys::spa_pod {
spa_sys::spa_pod_parser_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_parser_push(self.as_raw_ptr(), frame, pod, offset)
}
pub fn current(&mut self) -> *mut spa_sys::spa_pod {
unsafe { spa_sys::spa_pod_parser_current(self.as_raw_ptr()) }
}
pub unsafe fn advance(&mut self, pod: *const spa_sys::spa_pod) {
spa_sys::spa_pod_parser_advance(self.as_raw_ptr(), pod)
}
pub unsafe fn next(&mut self) -> *mut spa_sys::spa_pod {
spa_sys::spa_pod_parser_next(self.as_raw_ptr())
}
pub unsafe fn pop(&mut self, frame: &mut spa_sys::spa_pod_frame) -> Result<(), Errno> {
let res = spa_sys::spa_pod_parser_pop(self.as_raw_ptr(), frame as *mut _);
if res >= 0 {
Ok(())
} else {
Err(Errno::from_i32(-res))
}
}
pub fn get_bool(&mut self) -> Result<bool, Errno> {
unsafe {
let mut b: MaybeUninit<bool> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_bool(self.as_raw_ptr(), b.as_mut_ptr());
if res >= 0 {
Ok(b.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_id(&mut self) -> Result<Id, Errno> {
unsafe {
let mut id: MaybeUninit<u32> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_id(self.as_raw_ptr(), id.as_mut_ptr());
if res >= 0 {
Ok(Id(id.assume_init()))
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_int(&mut self) -> Result<c_int, Errno> {
unsafe {
let mut int: MaybeUninit<c_int> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_int(self.as_raw_ptr(), int.as_mut_ptr());
if res >= 0 {
Ok(int.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_long(&mut self) -> Result<c_long, Errno> {
unsafe {
let mut long: MaybeUninit<c_long> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_long(self.as_raw_ptr(), long.as_mut_ptr());
if res >= 0 {
Ok(long.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_float(&mut self) -> Result<c_float, Errno> {
unsafe {
let mut float: MaybeUninit<c_float> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_float(self.as_raw_ptr(), float.as_mut_ptr());
if res >= 0 {
Ok(float.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_double(&mut self) -> Result<c_double, Errno> {
unsafe {
let mut double: MaybeUninit<c_double> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_double(self.as_raw_ptr(), double.as_mut_ptr());
if res >= 0 {
Ok(double.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_string_raw(&mut self) -> Result<&'d CStr, Errno> {
unsafe {
let mut string: MaybeUninit<*const c_char> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_string(self.as_raw_ptr(), string.as_mut_ptr());
if res >= 0 {
let string = string.assume_init();
let string = CStr::from_ptr(string);
Ok(string)
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_bytes(&mut self) -> Result<&'d [u8], Errno> {
unsafe {
let mut bytes: MaybeUninit<*const u8> = MaybeUninit::uninit();
let mut len: MaybeUninit<u32> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_bytes(
self.as_raw_ptr(),
bytes.as_mut_ptr().cast(),
len.as_mut_ptr(),
);
if res >= 0 {
let bytes = bytes.assume_init();
let len = len.assume_init();
let bytes = std::slice::from_raw_parts(bytes, len.try_into().unwrap());
Ok(bytes)
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_pointer(&mut self) -> Result<(*const c_void, Id), Errno> {
unsafe {
let mut ptr: MaybeUninit<*const c_void> = MaybeUninit::uninit();
let mut type_: MaybeUninit<u32> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_pointer(
self.as_raw_ptr(),
type_.as_mut_ptr(),
ptr.as_mut_ptr(),
);
if res >= 0 {
Ok((ptr.assume_init(), Id(type_.assume_init())))
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_fd(&mut self) -> Result<i64, Errno> {
unsafe {
let mut fd: MaybeUninit<i64> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_fd(self.as_raw_ptr(), fd.as_mut_ptr());
if res >= 0 {
Ok(fd.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_rectangle(&mut self) -> Result<Rectangle, Errno> {
unsafe {
let mut rect: MaybeUninit<spa_sys::spa_rectangle> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_rectangle(self.as_raw_ptr(), rect.as_mut_ptr());
if res >= 0 {
Ok(rect.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_fraction(&mut self) -> Result<Fraction, Errno> {
unsafe {
let mut frac: MaybeUninit<spa_sys::spa_fraction> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_fraction(self.as_raw_ptr(), frac.as_mut_ptr());
if res >= 0 {
Ok(frac.assume_init())
} else {
Err(Errno::from_i32(-res))
}
}
}
pub fn get_pod(&mut self) -> Result<&'d crate::pod::Pod, Errno> {
unsafe {
let mut pod: MaybeUninit<*mut spa_sys::spa_pod> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_get_pod(self.as_raw_ptr(), pod.as_mut_ptr());
if res >= 0 {
let pod = crate::pod::Pod::from_raw(pod.assume_init());
Ok(pod)
} else {
Err(Errno::from_i32(-res))
}
}
}
pub unsafe fn push_struct(
&mut self,
frame: &mut MaybeUninit<spa_sys::spa_pod_frame>,
) -> Result<(), Errno> {
let res = spa_sys::spa_pod_parser_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,
) -> Result<Id, Errno> {
let mut id: MaybeUninit<u32> = MaybeUninit::uninit();
let res = spa_sys::spa_pod_parser_push_object(
self.as_raw_ptr(),
frame.as_mut_ptr(),
_type,
id.as_mut_ptr(),
);
if res >= 0 {
Ok(Id(id.assume_init()))
} else {
Err(Errno::from_i32(-res))
}
}
}
#[macro_export]
macro_rules! __parser_get__ {
($parser:expr, Bool($val:expr)) => {
{
let val: &mut bool = $val;
let res = $crate::pod::parser::Parser::get_bool($parser);
if let Ok(bool) = res {
*val = bool;
}
res.map(|_| {})
}
};
($parser:expr, Id($val:expr)) => {
{
let val: &mut $crate::utils::Id = $val;
let res = $crate::pod::parser::Parser::get_id($parser);
if let Ok(id) = res {
*val = id;
}
res.map(|_| {})
}
};
($parser:expr, Int($val:expr)) => {
{
let val: &mut i32 = $val;
let res = $crate::pod::parser::Parser::get_int($parser);
if let Ok(int) = res {
*val = int;
}
res.map(|_| {})
}
};
($parser:expr, Long($val:expr)) => {
{
let val: &mut i64 = $val;
let res = $crate::pod::parser::Parser::get_long($parser);
if let Ok(long) = res {
*val = long;
}
res.map(|_| {})
}
};
($parser:expr, Float($val:expr)) => {
{
let val: &mut f32 = $val;
let res = $crate::pod::parser::Parser::get_float($parser);
if let Ok(float) = res {
*val = float;
}
res.map(|_| {})
}
};
($parser:expr, Double($val:expr)) => {
{
let val: &mut f64 = $val;
let res = $crate::pod::parser::Parser::get_double($parser);
if let Ok(double) = res {
*val = double;
}
res.map(|_| {})
}
};
($parser:expr, Bytes($val:expr)) => {
{
let val: &mut &[u8] = $val;
let res = $crate::pod::parser::Parser::get_bytes($parser);
if let Ok(bytes) = res {
*val = bytes;
}
res.map(|_| {})
}
};
($parser:expr, Pointer($val:expr)) => {
{
let val: &mut (*const c_void, Id) = $val;
let res = $crate::pod::parser::Parser::get_pointer($parser);
if let Ok(ptr) = res {
*val = ptr;
}
res.map(|_| {})
}
};
($parser:expr, Fd($val:expr)) => {
{
let val: &mut i64 = $val;
let res = $crate::pod::parser::Parser::get_fd($parser);
if let Ok(fd) = res {
*val = fd;
}
res.map(|_| {})
}
};
($parser:expr, Rectangle($val:expr)) => {
{
let val: &mut $crate::utils::Rectangle = $val;
let res = $crate::pod::parser::Parser::get_rectangle($parser);
if let Ok(rect) = res {
*val = rect;
}
res.map(|_| {})
}
};
($parser:expr, Fraction($val:expr)) => {
{
let val: &mut $crate::utils::Fraction = $val;
let res = $crate::pod::parser::Parser::get_fraction($parser);
if let Ok(fraction) = res {
*val = fraction;
}
res.map(|_| {})
}
};
($parser:expr, Pod($val:expr)) => {
{
let val: &mut $crate::pod::Pod = $val;
let res = $crate::pod::parser::Parser::get_pod($parser);
if let Ok(pod) = res {
*val = pod;
}
res.map(|_| {})
}
};
($parser: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::parser::Parser::push_struct($parser, &mut frame) };
if res.is_err() {
break 'outer res;
}
$(
let res = $crate::__parser_get__!($parser, $field_type $field);
if res.is_err() {
break 'outer res.map(|_| {});
}
)*
unsafe { $crate::pod::parser::Parser::pop($parser, frame.assume_init_mut()) }
}
};
}
pub use __parser_get__ as parser_get;
#[cfg(test)]
mod tests {
use super::{parser_get, Parser};
#[test]
#[cfg_attr(miri, ignore)]
fn parse_bool() {
let pod: Vec<u8> = [
&4u32.to_ne_bytes(), &2u32.to_ne_bytes(), &1u32.to_ne_bytes(), &[0, 0, 0, 0], ]
.into_iter()
.flatten()
.copied()
.collect();
let mut parser = Parser::new(&pod);
let mut bool = false;
let res = parser_get!(&mut parser, Bool(&mut bool));
assert!(res.is_ok());
assert!(bool);
}
#[test]
#[cfg_attr(miri, ignore)]
fn parse_empty_struct() {
let pod: Vec<u8> = [
&0u32.to_ne_bytes(), &14u32.to_ne_bytes(), ]
.into_iter()
.flatten()
.copied()
.collect();
let mut parser = Parser::new(&pod);
let res = parser_get!(&mut parser, Struct {});
assert!(res.is_ok());
}
#[test]
#[cfg_attr(miri, ignore)]
fn parse_complicated_struct() {
let pod: &[&[u8]] = &[
&168u32.to_ne_bytes(), &14u32.to_ne_bytes(), &96u32.to_ne_bytes(), &14u32.to_ne_bytes(), &4u32.to_ne_bytes(), &2u32.to_ne_bytes(), &1u32.to_ne_bytes(), &[0, 0, 0, 0], &4u32.to_ne_bytes(), &3u32.to_ne_bytes(), &313u32.to_ne_bytes(), &[0, 0, 0, 0], &4u32.to_ne_bytes(), &4u32.to_ne_bytes(), &313i32.to_ne_bytes(), &[0, 0, 0, 0], &8u32.to_ne_bytes(), &5u32.to_ne_bytes(), &313i64.to_ne_bytes(), &4u32.to_ne_bytes(), &6u32.to_ne_bytes(), &31.3f32.to_ne_bytes(), &[0, 0, 0, 0], &8u32.to_ne_bytes(), &7u32.to_ne_bytes(), &31.3f64.to_ne_bytes(), &3u32.to_ne_bytes(), &9u32.to_ne_bytes(), &[3, 1, 3], &[0, 0, 0, 0, 0], &8u32.to_ne_bytes(), &18u32.to_ne_bytes(), &313i64.to_ne_bytes(), &8u32.to_ne_bytes(), &10u32.to_ne_bytes(), &313u32.to_ne_bytes(), &131u32.to_ne_bytes(), &8u32.to_ne_bytes(), &11u32.to_ne_bytes(), &313u32.to_ne_bytes(), &131u32.to_ne_bytes(), ];
let pod: Vec<u8> = pod.iter().flat_map(|f| (*f)).copied().collect();
let mut parser = Parser::new(&pod);
let mut bool = false;
let mut id = crate::utils::Id(0);
let mut int = 0i32;
let mut long = 0i64;
let mut float = 0.0f32;
let mut double = 0.0f64;
let mut bytes: &[u8] = &[];
let mut fd = 0i64;
let mut rect = crate::utils::Rectangle {
width: 0,
height: 0,
};
let mut frac = crate::utils::Fraction { num: 0, denom: 1 };
let res = parser_get!(
&mut parser,
Struct {
Struct {
Bool(&mut bool),
Id(&mut id),
Int(&mut int),
Long(&mut long),
Float(&mut float),
Double(&mut double),
},
Bytes(&mut bytes),
Fd(&mut fd),
Rectangle(&mut rect),
Fraction(&mut frac),
}
);
assert!(res.is_ok());
assert!(bool);
assert_eq!(id, crate::utils::Id(313));
assert_eq!(int, 313);
assert_eq!(long, 313);
assert_eq!(float, 31.3);
assert_eq!(double, 31.3);
assert_eq!(bytes, &[3, 1, 3]);
assert_eq!(fd, 313);
assert_eq!(
rect,
crate::utils::Rectangle {
width: 313,
height: 131
}
);
assert_eq!(
frac,
crate::utils::Fraction {
num: 313,
denom: 131
}
);
}
}