libspa-0.8.0/.cargo_vcs_info.json0000644000000001440000000000100123120ustar { "git": { "sha1": "449bf53f5d5edc8d0be6c0c80bc19d882f712dd7" }, "path_in_vcs": "libspa" }libspa-0.8.0/Cargo.toml0000644000000032060000000000100103120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.65" name = "libspa" version = "0.8.0" authors = ["Guillaume Desmottes "] description = "Rust bindings for libspa" homepage = "https://pipewire.org" documentation = "https://pipewire.pages.freedesktop.org/pipewire-rs/libspa/" readme = "README.md" keywords = [ "pipewire", "multimedia", "audio", "video", ] categories = [ "api-bindings", "multimedia", ] license = "MIT" repository = "https://gitlab.freedesktop.org/pipewire/pipewire-rs" [package.metadata.system-deps.libspa] name = "libspa-0.2" version = "0.2" [dependencies.bitflags] version = "2" [dependencies.convert_case] version = "0.6" [dependencies.cookie-factory] version = "0.3.2" [dependencies.libc] version = "0.2" [dependencies.nix] version = "0.27" [dependencies.nom] version = "7" [dependencies.spa_sys] version = "0.8" package = "libspa-sys" [dev-dependencies.pipewire] version = "0.8" [dev-dependencies.pipewire-sys] version = "0.8" [build-dependencies.cc] version = "1.0.66" [build-dependencies.system-deps] version = "6" [features] v0_3_33 = [] v0_3_65 = [ "v0_3_33", "spa_sys/v0_3_65", ] v0_3_75 = ["v0_3_65"] libspa-0.8.0/Cargo.toml.orig000064400000000000000000000020421046102023000137700ustar 00000000000000[package] name = "libspa" version = "0.8.0" authors = ["Guillaume Desmottes "] rust-version = "1.65" edition = "2021" categories = ["api-bindings", "multimedia"] description = "Rust bindings for libspa" repository = "https://gitlab.freedesktop.org/pipewire/pipewire-rs" license = "MIT" readme = "README.md" homepage = "https://pipewire.org" documentation = "https://pipewire.pages.freedesktop.org/pipewire-rs/libspa/" keywords = ["pipewire", "multimedia", "audio", "video"] [dependencies] spa_sys = { package = "libspa-sys", version = "0.8", path = "../libspa-sys" } bitflags = "2" libc = "0.2" nix = "0.27" cookie-factory = "0.3.2" nom = "7" convert_case = "0.6" [dev-dependencies] pipewire-sys = { version = "0.8", path = "../pipewire-sys" } pipewire = { version = "0.8", path = "../pipewire" } [build-dependencies] system-deps = "6" cc = "1.0.66" [package.metadata.system-deps] libspa = { name = "libspa-0.2", version = "0.2" } [features] v0_3_33 = [] v0_3_65 = ["v0_3_33", "spa_sys/v0_3_65"] v0_3_75 = ["v0_3_65"] libspa-0.8.0/LICENSE000064400000000000000000000021101046102023000121020ustar 00000000000000Copyright The pipewire-rs Contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libspa-0.8.0/README.md000064400000000000000000000006501046102023000123630ustar 00000000000000# libspa [![](https://img.shields.io/crates/v/libspa.svg)](https://crates.io/crates/libspa) [![](https://docs.rs/libspa/badge.svg)](https://docs.rs/libspa) [libspa](https://pipewire.org) bindings for Rust. These bindings are providing a safe API that can be used to interface with [libspa](https://pipewire.org). ## Documentation See the [crate documentation](https://pipewire.pages.freedesktop.org/pipewire-rs/libspa/).libspa-0.8.0/build.rs000064400000000000000000000007401046102023000125510ustar 00000000000000fn main() { // FIXME: It would be nice to run this only when tests are run. println!("cargo:rerun-if-changed=tests/pod.c"); let libs = system_deps::Config::new() .probe() .expect("Cannot find libspa"); let libspa = libs.get_by_name("libspa").unwrap(); cc::Build::new() .file("tests/pod.c") .shared_flag(true) .flag("-Wno-missing-field-initializers") .includes(&libspa.include_paths) .compile("pod"); } libspa-0.8.0/src/buffer/mod.rs000064400000000000000000000112141046102023000142670ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT use std::{convert::TryFrom, fmt::Debug}; #[derive(Copy, Clone, PartialEq, Eq)] pub struct DataType(spa_sys::spa_data_type); #[allow(non_upper_case_globals)] impl DataType { pub const Invalid: Self = Self(spa_sys::SPA_DATA_Invalid); /// Pointer to memory, the data field in struct [`Data`] is set. pub const MemPtr: Self = Self(spa_sys::SPA_DATA_MemPtr); /// Generic fd, `mmap` to get to memory pub const MemFd: Self = Self(spa_sys::SPA_DATA_MemFd); /// Fd to `dmabuf` memory pub const DmaBuf: Self = Self(spa_sys::SPA_DATA_DmaBuf); /// Memory is identified with an id pub const MemId: Self = Self(spa_sys::SPA_DATA_MemId); pub fn from_raw(raw: spa_sys::spa_data_type) -> Self { Self(raw) } pub fn as_raw(&self) -> spa_sys::spa_data_type { self.0 } } impl std::fmt::Debug for DataType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = format!( "DataType::{}", match *self { Self::Invalid => "Invalid", Self::MemPtr => "MemPtr", Self::MemFd => "MemFd", Self::DmaBuf => "DmaBuf", Self::MemId => "MemId", _ => "Unknown", } ); f.write_str(&name) } } bitflags::bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct DataFlags: u32 { /// Data is readable const READABLE = 1<<0; /// Data is writable const WRITABLE = 1<<1; /// Data pointer can be changed const DYNAMIC = 1<<2; const READWRITE = Self::READABLE.bits() | Self::WRITABLE.bits(); } } #[repr(transparent)] pub struct Data(spa_sys::spa_data); impl Data { pub fn as_raw(&self) -> &spa_sys::spa_data { &self.0 } pub fn type_(&self) -> DataType { DataType::from_raw(self.0.type_) } pub fn flags(&self) -> DataFlags { DataFlags::from_bits_retain(self.0.flags) } // FIXME: Add bindings for the fd field, but how to detect when it is not set / invalid? pub fn data(&mut self) -> Option<&mut [u8]> { // FIXME: For safety, perhaps only return a non-mut slice when DataFlags::WRITABLE is not set? if self.0.data.is_null() { None } else { unsafe { Some(std::slice::from_raw_parts_mut( self.0.data as *mut u8, usize::try_from(self.0.maxsize).unwrap(), )) } } } pub fn chunk(&self) -> &Chunk { assert_ne!(self.0.chunk, std::ptr::null_mut()); unsafe { let chunk: *const spa_sys::spa_chunk = self.0.chunk; &*(chunk as *const Chunk) } } pub fn chunk_mut(&mut self) -> &mut Chunk { assert_ne!(self.0.chunk, std::ptr::null_mut()); unsafe { let chunk: *mut spa_sys::spa_chunk = self.0.chunk; &mut *(chunk as *mut Chunk) } } } impl Debug for Data { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Data") .field("type", &self.type_()) .field("flags", &self.flags()) // FIXME: Add fd .field("data", &self.0.data) // Only print the pointer here, as we don't want to print a (potentially very big) slice. .field("chunk", &self.chunk()) .finish() } } bitflags::bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct ChunkFlags: i32 { /// Chunk data is corrupted in some way const CORRUPTED = 1<<0; } } #[repr(transparent)] pub struct Chunk(spa_sys::spa_chunk); impl Chunk { pub fn as_raw(&self) -> &spa_sys::spa_chunk { &self.0 } pub fn size(&self) -> u32 { self.0.size } pub fn size_mut(&mut self) -> &mut u32 { &mut self.0.size } pub fn offset(&self) -> u32 { self.0.offset } pub fn offset_mut(&mut self) -> &mut u32 { &mut self.0.offset } pub fn stride(&self) -> i32 { self.0.stride } pub fn stride_mut(&mut self) -> &mut i32 { &mut self.0.stride } pub fn flags(&self) -> ChunkFlags { ChunkFlags::from_bits_retain(self.0.flags) } } impl Debug for Chunk { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Chunk") .field("offset", &self.offset()) .field("size", &self.size()) .field("stride", &self.stride()) .field("flags", &self.flags()) .finish() } } libspa-0.8.0/src/lib.rs000064400000000000000000000006421046102023000130100ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! The `libspa` crate provides a high-level API to interact with //! [libspa](https://gitlab.freedesktop.org/pipewire/pipewire/-/tree/master/doc/spa). pub mod buffer; pub mod param; pub mod pod; pub mod support; pub mod utils; pub use spa_sys as sys; /// prelude module re-exporing all the traits providing public API. pub mod prelude {} libspa-0.8.0/src/param/audio/mod.rs000064400000000000000000000120601046102023000152170ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT mod raw; pub use raw::*; use std::ffi::CStr; use std::fmt::Debug; use std::ops::Range; pub const MAX_CHANNELS: usize = spa_sys::SPA_AUDIO_MAX_CHANNELS as usize; #[repr(transparent)] #[derive(PartialEq, PartialOrd, Eq, Clone, Copy)] pub struct AudioFormat(pub spa_sys::spa_audio_format); #[allow(non_upper_case_globals)] impl AudioFormat { pub const Unknown: Self = Self(spa_sys::SPA_AUDIO_FORMAT_UNKNOWN); pub const Encoded: Self = Self(spa_sys::SPA_AUDIO_FORMAT_ENCODED); pub const S8: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S8); pub const U8: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U8); pub const S16LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S16_LE); pub const S16BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S16_BE); pub const U16LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U16_LE); pub const U16BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U16_BE); pub const S24_32LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S24_32_LE); pub const S24_32BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S24_32_BE); pub const U24_32LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U24_32_LE); pub const U24_32BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U24_32_BE); pub const S32LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S32_LE); pub const S32BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S32_BE); pub const U32LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U32_LE); pub const U32BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U32_BE); pub const S24LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S24_LE); pub const S24BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S24_BE); pub const U24LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U24_LE); pub const U24BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U24_BE); pub const S20LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S20_LE); pub const S20BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S20_BE); pub const U20LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U20_LE); pub const U20BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U20_BE); pub const S18LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S18_LE); pub const S18BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S18_BE); pub const U18LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U18_LE); pub const U18BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U18_BE); pub const F32LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_F32_LE); pub const F32BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_F32_BE); pub const F64LE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_F64_LE); pub const F64BE: Self = Self(spa_sys::SPA_AUDIO_FORMAT_F64_BE); pub const ULAW: Self = Self(spa_sys::SPA_AUDIO_FORMAT_ULAW); pub const ALAW: Self = Self(spa_sys::SPA_AUDIO_FORMAT_ALAW); pub const U8P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_U8P); pub const S16P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S16P); pub const S24_32P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S24_32P); pub const S32P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S32P); pub const S24P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S24P); pub const F32P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_F32P); pub const F64P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_F64P); pub const S8P: Self = Self(spa_sys::SPA_AUDIO_FORMAT_S8P); const INTERLEAVED_RANGE: Range = Self::S8..Self(spa_sys::SPA_AUDIO_FORMAT_START_Planar); const PLANAR_RANGE: Range = Self::U8P..Self(spa_sys::SPA_AUDIO_FORMAT_START_Other); pub fn is_interleaved(&self) -> bool { Self::INTERLEAVED_RANGE.contains(self) } pub fn is_planar(&self) -> bool { Self::PLANAR_RANGE.contains(self) } /// Obtain an [`AudioFormat`] from a raw `spa_audio_format` variant. pub fn from_raw(raw: spa_sys::spa_audio_format) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_audio_format`] representing this `AudioFormat`. pub fn as_raw(&self) -> spa_sys::spa_audio_format { self.0 } } impl Debug for AudioFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { AudioFormat::Unknown => f.write_str("AudioFormat::Unknown"), AudioFormat::Encoded => f.write_str("AudioFormat::Encoded"), _ => { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_short_name( spa_sys::spa_type_audio_format, self.as_raw(), ); if c_buf.is_null() { return f.write_str("Unsupported"); } CStr::from_ptr(c_buf) }; let name = format!("AudioFormat::{}", c_str.to_str().unwrap()); f.write_str(&name) } } } } #[cfg(test)] mod tests { use super::*; #[test] #[cfg_attr(miri, ignore)] fn debug_format() { assert_eq!( "AudioFormat::Unknown", format!("{:?}", AudioFormat::Unknown) ); assert_eq!( "AudioFormat::S24_32LE", format!("{:?}", AudioFormat::S24_32LE) ); } } libspa-0.8.0/src/param/audio/raw.rs000064400000000000000000000112141046102023000152310ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT use crate::param::audio::AudioFormat; use crate::pod::{Property, Value, ValueArray}; use crate::utils::{ self, result::{Error, SpaResult, SpaSuccess}, }; use std::fmt::Debug; bitflags::bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct AudioInfoRawFlags: u32 { /// the position array explicitly contains unpositioned channels. const UNPOSITIONED = 1<<0; } } /// Rust representation of [`spa_sys::spa_audio_info_raw`]. #[repr(transparent)] #[derive(PartialEq, Eq, Clone, Copy)] pub struct AudioInfoRaw(spa_sys::spa_audio_info_raw); impl AudioInfoRaw { pub fn new() -> Self { Self(spa_sys::spa_audio_info_raw { format: AudioFormat::Unknown.as_raw(), flags: AudioInfoRawFlags::UNPOSITIONED.bits(), rate: 0, channels: 0, position: [0; 64usize], }) } pub fn set_format(&mut self, format: AudioFormat) { self.0.format = format.as_raw(); } pub fn format(&self) -> AudioFormat { AudioFormat::from_raw(self.0.format) } pub fn set_flags(&mut self, flags: AudioInfoRawFlags) { self.0.flags = flags.bits(); } pub fn flags(&self) -> AudioInfoRawFlags { AudioInfoRawFlags::from_bits_retain(self.0.flags) } pub fn set_rate(&mut self, rate: u32) { self.0.rate = rate; } pub fn rate(&self) -> u32 { self.0.rate } pub fn set_channels(&mut self, channels: u32) { self.0.channels = channels; } pub fn channels(&self) -> u32 { self.0.channels } pub fn set_position(&mut self, position: [u32; 64usize]) { self.0.position = position; if position[0] == 0 { self.0.flags |= AudioInfoRawFlags::UNPOSITIONED.bits(); } else { self.0.flags &= AudioInfoRawFlags::UNPOSITIONED.complement().bits(); }; } pub fn position(&self) -> [u32; 64usize] { self.0.position } /// helper function to parse format properties type pub fn parse(&mut self, format: &crate::pod::Pod) -> Result { let res = unsafe { spa_sys::spa_format_audio_raw_parse(format.as_raw_ptr(), &mut self.0) }; SpaResult::from_c(res).into_result() } /// Obtain an [`AudioInfoRaw`] from a raw `spa_audio_info_raw` variant. pub fn from_raw(raw: spa_sys::spa_audio_info_raw) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_audio_info_raw`] representing this `AudioInfoRaw`. pub fn as_raw(&self) -> spa_sys::spa_audio_info_raw { self.0 } } impl Default for AudioInfoRaw { fn default() -> Self { Self::new() } } impl From for Vec { fn from(value: AudioInfoRaw) -> Self { let mut props = Vec::with_capacity(6); props.push(Property::new( spa_sys::SPA_FORMAT_mediaType, Value::Id(utils::Id(spa_sys::SPA_MEDIA_TYPE_audio)), )); props.push(Property::new( spa_sys::SPA_FORMAT_mediaSubtype, Value::Id(utils::Id(spa_sys::SPA_MEDIA_SUBTYPE_raw)), )); if value.format() != AudioFormat::Unknown { props.push(Property::new( spa_sys::SPA_FORMAT_AUDIO_format, Value::Id(utils::Id(value.format().as_raw())), )); } if value.rate() != 0 { props.push(Property::new( spa_sys::SPA_FORMAT_AUDIO_rate, Value::Int(value.rate() as i32), )); } if value.channels() != 0 { props.push(Property::new( spa_sys::SPA_FORMAT_AUDIO_channels, Value::Int(value.channels() as i32), )); if !value.flags().contains(AudioInfoRawFlags::UNPOSITIONED) { let array = value.position()[0..value.channels() as usize] .iter() .copied() .map(utils::Id) .collect(); props.push(Property::new( spa_sys::SPA_FORMAT_AUDIO_position, Value::ValueArray(ValueArray::Id(array)), )); } } props } } impl Debug for AudioInfoRaw { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("AudioInfoRaw") .field("format", &self.format()) .field("flags", &self.flags()) .field("rate", &self.rate()) .field("channels", &self.channels()) .field("position", &self.position()) .finish() } } libspa-0.8.0/src/param/format.rs000064400000000000000000000330401046102023000146300ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! Types for dealing with SPA formats. use convert_case::{Case, Casing}; use std::ffi::CStr; use std::fmt::Debug; use std::ops::Range; /// Different media types #[derive(PartialEq, Eq, Clone, Copy)] pub struct MediaType(pub spa_sys::spa_media_type); #[allow(non_upper_case_globals)] impl MediaType { pub const Unknown: Self = Self(spa_sys::SPA_MEDIA_TYPE_unknown); pub const Audio: Self = Self(spa_sys::SPA_MEDIA_TYPE_audio); pub const Video: Self = Self(spa_sys::SPA_MEDIA_TYPE_video); pub const Image: Self = Self(spa_sys::SPA_MEDIA_TYPE_image); pub const Binary: Self = Self(spa_sys::SPA_MEDIA_TYPE_binary); pub const Stream: Self = Self(spa_sys::SPA_MEDIA_TYPE_stream); pub const Application: Self = Self(spa_sys::SPA_MEDIA_TYPE_application); /// Obtain a [`MediaType`] from a raw `spa_media_type` variant. pub fn from_raw(raw: spa_sys::spa_media_type) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_media_type`] representing this `MediaType`. pub fn as_raw(&self) -> spa_sys::spa_media_type { self.0 } } impl Debug for MediaType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_short_name( spa_sys::spa_type_media_type, self.as_raw(), ); if c_buf.is_null() { return f.write_str("Unsupported media type"); } CStr::from_ptr(c_buf) }; let name = format!( "MediaType::{}", c_str.to_string_lossy().to_case(Case::Pascal) ); f.write_str(&name) } } /// Different media sub-types #[derive(PartialEq, PartialOrd, Eq, Clone, Copy)] pub struct MediaSubtype(pub spa_sys::spa_media_subtype); #[allow(non_upper_case_globals)] impl MediaSubtype { pub const Unknown: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_unknown); pub const Raw: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_raw); pub const Dsp: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_dsp); /// S/PDIF pub const Iec958: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_iec958); pub const Dsd: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_dsd); pub const Mp3: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_mp3); pub const Aac: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_aac); pub const Vorbis: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_vorbis); pub const Wma: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_wma); pub const Ra: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_ra); pub const Sbc: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_sbc); pub const Adpcm: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_adpcm); pub const G723: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_g723); pub const G726: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_g726); pub const G729: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_g729); pub const Amr: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_amr); pub const Gsm: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_gsm); #[cfg(feature = "v0_3_65")] pub const Alac: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_alac); #[cfg(feature = "v0_3_65")] pub const Flac: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_flac); #[cfg(feature = "v0_3_65")] pub const Ape: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_ape); #[cfg(feature = "v0_3_68")] pub const Opus: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_opus); pub const H264: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_h264); pub const Mjpg: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_mjpg); pub const Dv: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_dv); pub const Mpegts: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_mpegts); pub const H263: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_h263); pub const Mpeg1: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_mpeg1); pub const Mpeg2: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_mpeg2); pub const Mpeg4: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_mpeg4); pub const Xvid: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_xvid); pub const Vc1: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_vc1); pub const Vp8: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_vp8); pub const Vp9: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_vp9); pub const Bayer: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_bayer); pub const Jpeg: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_jpeg); pub const Midi: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_midi); /// control stream, data contains spa_pod_sequence with control info. pub const Control: Self = Self(spa_sys::SPA_MEDIA_SUBTYPE_control); const AUDIO_RANGE: Range = Self::Mp3..Self(spa_sys::SPA_MEDIA_SUBTYPE_START_Video); const VIDEO_RANGE: Range = Self::H264..Self(spa_sys::SPA_MEDIA_SUBTYPE_START_Image); const IMAGE_RANGE: Range = Self::Jpeg..Self(spa_sys::SPA_MEDIA_SUBTYPE_START_Binary); const BINARY_RANGE: Range = Self(spa_sys::SPA_MEDIA_SUBTYPE_START_Binary) ..Self(spa_sys::SPA_MEDIA_SUBTYPE_START_Stream); const STREAM_RANGE: Range = Self::Midi..Self(spa_sys::SPA_MEDIA_SUBTYPE_START_Application); const APPLICATION_RANGE: Range = Self::Control..Self(spa_sys::spa_media_subtype::MAX); pub fn is_audio(&self) -> bool { Self::AUDIO_RANGE.contains(self) } pub fn is_video(&self) -> bool { Self::VIDEO_RANGE.contains(self) } pub fn is_image(&self) -> bool { Self::IMAGE_RANGE.contains(self) } pub fn is_binary(&self) -> bool { Self::BINARY_RANGE.contains(self) } pub fn is_stream(&self) -> bool { Self::STREAM_RANGE.contains(self) } pub fn is_application(&self) -> bool { Self::APPLICATION_RANGE.contains(self) } /// Obtain a [`MediaSubtype`] from a raw `spa_media_subtype` variant. pub fn from_raw(raw: spa_sys::spa_media_subtype) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_media_subtype`] representing this `MediaSubtype`. pub fn as_raw(&self) -> spa_sys::spa_media_subtype { self.0 } } impl Debug for MediaSubtype { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_short_name( spa_sys::spa_type_media_subtype, self.as_raw(), ); if c_buf.is_null() { return f.write_str("Unsupported media subtype"); } CStr::from_ptr(c_buf) }; let name = format!( "MediaSubtype::{}", c_str.to_string_lossy().to_case(Case::Pascal) ); f.write_str(&name) } } #[derive(PartialEq, PartialOrd, Eq, Clone, Copy)] pub struct FormatProperties(pub spa_sys::spa_format); #[allow(non_upper_case_globals)] impl FormatProperties { /// media type (Id enum spa_media_type) pub const MediaType: Self = Self(spa_sys::SPA_FORMAT_mediaType); /// media subtype (Id enum spa_media_subtype) pub const MediaSubtype: Self = Self(spa_sys::SPA_FORMAT_mediaSubtype); /// audio format, (Id enum spa_audio_format) pub const AudioFormat: Self = Self(spa_sys::SPA_FORMAT_AUDIO_format); /// optional flags (Int) pub const AudioFlags: Self = Self(spa_sys::SPA_FORMAT_AUDIO_flags); /// sample rate (Int) pub const AudioRate: Self = Self(spa_sys::SPA_FORMAT_AUDIO_rate); /// number of audio channels (Int) pub const AudioChannels: Self = Self(spa_sys::SPA_FORMAT_AUDIO_channels); /// channel positions (Id enum spa_audio_position) pub const AudioPosition: Self = Self(spa_sys::SPA_FORMAT_AUDIO_position); /// codec used (IEC958) (Id enum spa_audio_iec958_codec) pub const AudioIec958Codec: Self = Self(spa_sys::SPA_FORMAT_AUDIO_iec958Codec); /// bit order (Id enum spa_param_bitorder) pub const AudioBitorder: Self = Self(spa_sys::SPA_FORMAT_AUDIO_bitorder); /// Interleave bytes (Int) pub const AudioInterleave: Self = Self(spa_sys::SPA_FORMAT_AUDIO_interleave); /// bit rate (Int) #[cfg(feature = "v0_3_65")] pub const AudioBitrate: Self = Self(spa_sys::SPA_FORMAT_AUDIO_bitrate); /// audio data block alignment (Int) #[cfg(feature = "v0_3_65")] pub const AudioBlockAlign: Self = Self(spa_sys::SPA_FORMAT_AUDIO_blockAlign); /// AAC stream format, (Id enum spa_audio_aac_stream_format) #[cfg(feature = "v0_3_65")] pub const AudioAacStreamFormat: Self = Self(spa_sys::SPA_FORMAT_AUDIO_AAC_streamFormat); /// WMA profile (Id enum spa_audio_wma_profile) #[cfg(feature = "v0_3_65")] pub const AudioWmaProfile: Self = Self(spa_sys::SPA_FORMAT_AUDIO_WMA_profile); /// AMR band mode (Id enum spa_audio_amr_band_mode) #[cfg(feature = "v0_3_65")] pub const AudioAmrBandMode: Self = Self(spa_sys::SPA_FORMAT_AUDIO_AMR_bandMode); /// video format (Id enum spa_video_format) pub const VideoFormat: Self = Self(spa_sys::SPA_FORMAT_VIDEO_format); /// format modifier (Long), use only with DMA-BUF and omit for other buffer types pub const VideoModifier: Self = Self(spa_sys::SPA_FORMAT_VIDEO_modifier); /// size (Rectangle) pub const VideoSize: Self = Self(spa_sys::SPA_FORMAT_VIDEO_size); /// frame rate (Fraction) pub const VideoFramerate: Self = Self(spa_sys::SPA_FORMAT_VIDEO_framerate); /// maximum frame rate (Fraction) pub const VideoMaxFramerate: Self = Self(spa_sys::SPA_FORMAT_VIDEO_maxFramerate); /// number of views (Int) pub const VideoViews: Self = Self(spa_sys::SPA_FORMAT_VIDEO_views); /// (Id enum spa_video_interlace_mode) pub const VideoInterlaceMode: Self = Self(spa_sys::SPA_FORMAT_VIDEO_interlaceMode); /// (Rectangle) pub const VideoPixelAspectRatio: Self = Self(spa_sys::SPA_FORMAT_VIDEO_pixelAspectRatio); /// (Id enum spa_video_multiview_mode) pub const VideoMultiviewMode: Self = Self(spa_sys::SPA_FORMAT_VIDEO_multiviewMode); /// (Id enum spa_video_multiview_flags) pub const VideoMultiviewFlags: Self = Self(spa_sys::SPA_FORMAT_VIDEO_multiviewFlags); /// /Id enum spa_video_chroma_site) pub const VideoChromaSite: Self = Self(spa_sys::SPA_FORMAT_VIDEO_chromaSite); /// /Id enum spa_video_color_range) pub const VideoColorRange: Self = Self(spa_sys::SPA_FORMAT_VIDEO_colorRange); /// /Id enum spa_video_color_matrix) pub const VideoColorMatrix: Self = Self(spa_sys::SPA_FORMAT_VIDEO_colorMatrix); /// /Id enum spa_video_transfer_function) pub const VideoTransferFunction: Self = Self(spa_sys::SPA_FORMAT_VIDEO_transferFunction); /// /Id enum spa_video_color_primaries) pub const VideoColorPrimaries: Self = Self(spa_sys::SPA_FORMAT_VIDEO_colorPrimaries); /// (Int) pub const VideoProfile: Self = Self(spa_sys::SPA_FORMAT_VIDEO_profile); /// (Int) pub const VideoLevel: Self = Self(spa_sys::SPA_FORMAT_VIDEO_level); /// (Id enum spa_h264_stream_format) pub const VideoH264StreamFormat: Self = Self(spa_sys::SPA_FORMAT_VIDEO_H264_streamFormat); /// (Id enum spa_h264_alignment) pub const VideoH264Alignment: Self = Self(spa_sys::SPA_FORMAT_VIDEO_H264_alignment); const AUDIO_RANGE: Range = Self::AudioFormat..Self(spa_sys::SPA_FORMAT_START_Video); const VIDEO_RANGE: Range = Self::VideoFormat..Self(spa_sys::SPA_FORMAT_START_Image); const IMAGE_RANGE: Range = Self(spa_sys::SPA_FORMAT_START_Image)..Self(spa_sys::SPA_FORMAT_START_Binary); const BINARY_RANGE: Range = Self(spa_sys::SPA_FORMAT_START_Binary)..Self(spa_sys::SPA_FORMAT_START_Stream); const STREAM_RANGE: Range = Self(spa_sys::SPA_FORMAT_START_Stream)..Self(spa_sys::SPA_FORMAT_START_Application); const APPLICATION_RANGE: Range = Self(spa_sys::SPA_FORMAT_START_Application)..Self(spa_sys::spa_format::MAX); pub fn is_audio(&self) -> bool { Self::AUDIO_RANGE.contains(self) } pub fn is_video(&self) -> bool { Self::VIDEO_RANGE.contains(self) } pub fn is_image(&self) -> bool { Self::IMAGE_RANGE.contains(self) } pub fn is_binary(&self) -> bool { Self::BINARY_RANGE.contains(self) } pub fn is_stream(&self) -> bool { Self::STREAM_RANGE.contains(self) } pub fn is_application(&self) -> bool { Self::APPLICATION_RANGE.contains(self) } /// Obtain a [`FormatProperties`] from a raw `spa_format` variant. pub fn from_raw(raw: spa_sys::spa_format) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_format`] representing this `FormatProperties`. pub fn as_raw(&self) -> spa_sys::spa_format { self.0 } } impl Debug for FormatProperties { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_name(spa_sys::spa_type_format, self.as_raw()); if c_buf.is_null() { return f.write_str("Unsupported format"); } CStr::from_ptr(c_buf) }; let name = format!( "FormatProperties::{}", c_str .to_string_lossy() .replace("Spa:Pod:Object:Param:Format:", "") .replace(':', " ") .to_case(Case::Pascal) ); f.write_str(&name) } } #[cfg(test)] mod tests { use super::*; #[test] #[cfg_attr(miri, ignore)] fn debug_format() { assert_eq!("MediaType::Audio", format!("{:?}", MediaType::Audio)); assert_eq!("MediaSubtype::Raw", format!("{:?}", MediaSubtype::Raw)); assert_eq!( "FormatProperties::VideoTransferFunction", format!("{:?}", FormatProperties::VideoTransferFunction) ); } } libspa-0.8.0/src/param/format_utils.rs000064400000000000000000000017031046102023000160510ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT use std::mem::MaybeUninit; use crate::{ param::format::{MediaSubtype, MediaType}, pod::Pod, utils::result::{Error, SpaResult}, }; /// helper function to parse format properties type pub fn parse_format(format: &Pod) -> Result<(MediaType, MediaSubtype), Error> { let mut media_type: MaybeUninit = MaybeUninit::uninit(); let mut media_subtype: MaybeUninit = MaybeUninit::uninit(); let res = unsafe { spa_sys::spa_format_parse( format.as_raw_ptr(), media_type.as_mut_ptr(), media_subtype.as_mut_ptr(), ) }; match SpaResult::from_c(res).into_sync_result() { Err(e) => Err(e), Ok(_) => Ok(unsafe { ( MediaType::from_raw(media_type.assume_init()), MediaSubtype::from_raw(media_subtype.assume_init()), ) }), } } libspa-0.8.0/src/param/mod.rs000064400000000000000000000100341046102023000141150ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! Types for dealing with SPA parameters. pub mod audio; pub mod format; pub mod format_utils; pub mod video; use std::ffi::CStr; use std::fmt::Debug; /// Different parameter types that can be queried #[derive(Copy, Clone, PartialEq, Eq)] pub struct ParamType(pub spa_sys::spa_param_type); #[allow(non_upper_case_globals)] impl ParamType { /// invalid pub const Invalid: Self = Self(spa_sys::SPA_PARAM_Invalid); /// property information as SPA_TYPE_OBJECT_PropInfo pub const PropInfo: Self = Self(spa_sys::SPA_PARAM_PropInfo); /// properties as SPA_TYPE_OBJECT_Props pub const Props: Self = Self(spa_sys::SPA_PARAM_Props); /// available formats as SPA_TYPE_OBJECT_Format pub const EnumFormat: Self = Self(spa_sys::SPA_PARAM_EnumFormat); /// configured format as SPA_TYPE_OBJECT_Format pub const Format: Self = Self(spa_sys::SPA_PARAM_Format); /// buffer configurations as SPA_TYPE_OBJECT_ParamBuffers pub const Buffers: Self = Self(spa_sys::SPA_PARAM_Buffers); /// allowed metadata for buffers as SPA_TYPE_OBJECT_ParamMeta pub const Meta: Self = Self(spa_sys::SPA_PARAM_Meta); /// configurable IO areas as SPA_TYPE_OBJECT_ParamIO pub const IO: Self = Self(spa_sys::SPA_PARAM_IO); /// profile enumeration as SPA_TYPE_OBJECT_ParamProfile pub const EnumProfile: Self = Self(spa_sys::SPA_PARAM_EnumProfile); /// profile configuration as SPA_TYPE_OBJECT_ParamProfile pub const Profile: Self = Self(spa_sys::SPA_PARAM_Profile); /// port configuration enumeration as SPA_TYPE_OBJECT_ParamPortConfig pub const EnumPortConfig: Self = Self(spa_sys::SPA_PARAM_EnumPortConfig); /// port configuration as SPA_TYPE_OBJECT_ParamPortConfig pub const PortConfig: Self = Self(spa_sys::SPA_PARAM_PortConfig); /// routing enumeration as SPA_TYPE_OBJECT_ParamRoute pub const EnumRoute: Self = Self(spa_sys::SPA_PARAM_EnumRoute); /// routing configuration as SPA_TYPE_OBJECT_ParamRoute pub const Route: Self = Self(spa_sys::SPA_PARAM_Route); /// Control parameter, a SPA_TYPE_Sequence pub const Control: Self = Self(spa_sys::SPA_PARAM_Control); /// latency reporting, a SPA_TYPE_OBJECT_ParamLatency pub const Latency: Self = Self(spa_sys::SPA_PARAM_Latency); /// processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency pub const ProcessLatency: Self = Self(spa_sys::SPA_PARAM_ProcessLatency); /// Obtain a [`ParamType`] from a raw `spa_param_type` variant. pub fn from_raw(raw: spa_sys::spa_param_type) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_param_type`] representing this `ParamType`. pub fn as_raw(&self) -> spa_sys::spa_param_type { self.0 } } impl Debug for ParamType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_short_name(spa_sys::spa_type_param, self.as_raw()); if c_buf.is_null() { return f.write_str("Unknown"); } CStr::from_ptr(c_buf) }; let name = format!("ParamType::{}", c_str.to_string_lossy()); f.write_str(&name) } } bitflags::bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct ParamInfoFlags: u32 { const SERIAL = 1<<0; const READ = 1<<1; const WRITE = 1<<2; const READWRITE = Self::READ.bits() | Self::WRITE.bits(); } } /// Information about a parameter #[repr(transparent)] pub struct ParamInfo(spa_sys::spa_param_info); impl ParamInfo { pub fn id(&self) -> ParamType { ParamType::from_raw(self.0.id) } pub fn flags(&self) -> ParamInfoFlags { ParamInfoFlags::from_bits_truncate(self.0.flags) } } impl Debug for ParamInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ParamInfo") .field("id", &self.id()) .field("flags", &self.flags()) .finish() } } libspa-0.8.0/src/param/video/mod.rs000064400000000000000000000001451046102023000152250ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT mod raw; pub use raw::*; libspa-0.8.0/src/param/video/raw.rs000064400000000000000000000430241046102023000152420ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT use crate::utils::{ result::{Error, SpaResult, SpaSuccess}, Fraction, Rectangle, }; #[cfg(feature = "v0_3_65")] use convert_case::{Case, Casing}; use std::{ffi::CStr, fmt::Debug}; #[derive(Copy, Clone, PartialEq, Eq)] pub struct VideoFormat(pub spa_sys::spa_video_format); #[allow(non_upper_case_globals)] impl VideoFormat { pub const Unknown: Self = Self(spa_sys::SPA_VIDEO_FORMAT_UNKNOWN); pub const Encoded: Self = Self(spa_sys::SPA_VIDEO_FORMAT_ENCODED); pub const I420: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I420); pub const YV12: Self = Self(spa_sys::SPA_VIDEO_FORMAT_YV12); pub const YUY2: Self = Self(spa_sys::SPA_VIDEO_FORMAT_YUY2); pub const UYVY: Self = Self(spa_sys::SPA_VIDEO_FORMAT_UYVY); pub const AYUV: Self = Self(spa_sys::SPA_VIDEO_FORMAT_AYUV); pub const RGBx: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGBx); pub const BGRx: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGRx); pub const xRGB: Self = Self(spa_sys::SPA_VIDEO_FORMAT_xRGB); pub const xBGR: Self = Self(spa_sys::SPA_VIDEO_FORMAT_xBGR); pub const RGBA: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGBA); pub const BGRA: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGRA); pub const ARGB: Self = Self(spa_sys::SPA_VIDEO_FORMAT_ARGB); pub const ABGR: Self = Self(spa_sys::SPA_VIDEO_FORMAT_ABGR); pub const RGB: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGB); pub const BGR: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGR); pub const Y41B: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y41B); pub const Y42B: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y42B); pub const YVYU: Self = Self(spa_sys::SPA_VIDEO_FORMAT_YVYU); pub const Y444: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y444); pub const v210: Self = Self(spa_sys::SPA_VIDEO_FORMAT_v210); pub const v216: Self = Self(spa_sys::SPA_VIDEO_FORMAT_v216); pub const NV12: Self = Self(spa_sys::SPA_VIDEO_FORMAT_NV12); pub const NV21: Self = Self(spa_sys::SPA_VIDEO_FORMAT_NV21); pub const GRAY8: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GRAY8); pub const GRAY16_BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GRAY16_BE); pub const GRAY16_LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GRAY16_LE); pub const v308: Self = Self(spa_sys::SPA_VIDEO_FORMAT_v308); pub const RGB16: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGB16); pub const BGR16: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGR16); pub const RGB15: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGB15); pub const BGR15: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGR15); pub const UYVP: Self = Self(spa_sys::SPA_VIDEO_FORMAT_UYVP); pub const A420: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A420); pub const RGB8P: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGB8P); pub const YUV9: Self = Self(spa_sys::SPA_VIDEO_FORMAT_YUV9); pub const YVU9: Self = Self(spa_sys::SPA_VIDEO_FORMAT_YVU9); pub const IYU1: Self = Self(spa_sys::SPA_VIDEO_FORMAT_IYU1); pub const ARGB64: Self = Self(spa_sys::SPA_VIDEO_FORMAT_ARGB64); pub const AYUV64: Self = Self(spa_sys::SPA_VIDEO_FORMAT_AYUV64); pub const r210: Self = Self(spa_sys::SPA_VIDEO_FORMAT_r210); pub const I420_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I420_10BE); pub const I420_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I420_10LE); pub const I422_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I422_10BE); pub const I422_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I422_10LE); pub const Y444_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y444_10BE); pub const Y444_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y444_10LE); pub const GBR: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBR); pub const GBR_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBR_10BE); pub const GBR_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBR_10LE); pub const NV16: Self = Self(spa_sys::SPA_VIDEO_FORMAT_NV16); pub const NV24: Self = Self(spa_sys::SPA_VIDEO_FORMAT_NV24); pub const NV12_64Z32: Self = Self(spa_sys::SPA_VIDEO_FORMAT_NV12_64Z32); pub const A420_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A420_10BE); pub const A420_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A420_10LE); pub const A422_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A422_10BE); pub const A422_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A422_10LE); pub const A444_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A444_10BE); pub const A444_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_A444_10LE); pub const NV61: Self = Self(spa_sys::SPA_VIDEO_FORMAT_NV61); pub const P010_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_P010_10BE); pub const P010_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_P010_10LE); pub const IYU2: Self = Self(spa_sys::SPA_VIDEO_FORMAT_IYU2); pub const VYUY: Self = Self(spa_sys::SPA_VIDEO_FORMAT_VYUY); pub const GBRA: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBRA); pub const GBRA_10BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBRA_10BE); pub const GBRA_10LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBRA_10LE); pub const GBR_12BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBR_12BE); pub const GBR_12LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBR_12LE); pub const GBRA_12BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBRA_12BE); pub const GBRA_12LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_GBRA_12LE); pub const I420_12BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I420_12BE); pub const I420_12LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I420_12LE); pub const I422_12BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I422_12BE); pub const I422_12LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_I422_12LE); pub const Y444_12BE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y444_12BE); pub const Y444_12LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_Y444_12LE); pub const RGBA_F16: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGBA_F16); pub const RGBA_F32: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGBA_F32); /// 32-bit x:R:G:B 2:10:10:10 little endian pub const xRGB_210LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_xRGB_210LE); ///32-bit x:B:G:R 2:10:10:10 little endian pub const xBGR_210LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_xBGR_210LE); ///32-bit R:G:B:x 10:10:10:2 little endian pub const RGBx_102LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGBx_102LE); /// 32-bit B:G:R:x 10:10:10:2 little endian pub const BGRx_102LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGRx_102LE); /// 32-bit A:R:G:B 2:10:10:10 little endian pub const ARGB_210LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_ARGB_210LE); /// 32-bit A:B:G:R 2:10:10:10 little endian pub const ABGR_210LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_ABGR_210LE); /// 32-bit R:G:B:A 10:10:10:2 little endian pub const RGBA_102LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_RGBA_102LE); /// 32-bit B:G:R:A 10:10:10:2 little endian pub const BGRA_102LE: Self = Self(spa_sys::SPA_VIDEO_FORMAT_BGRA_102LE); /* Aliases */ pub const DSP_F32: Self = Self(spa_sys::SPA_VIDEO_FORMAT_DSP_F32); /// Obtain a [`VideoFormat`] from a raw `spa_video_format` variant. pub fn from_raw(raw: spa_sys::spa_video_format) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_video_format`] representing this `VideoFormat`. pub fn as_raw(&self) -> spa_sys::spa_video_format { self.0 } } impl Debug for VideoFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { VideoFormat::Unknown => f.write_str("VideoFormat::Unknown"), _ => { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_short_name( spa_sys::spa_type_video_format, self.as_raw(), ); if c_buf.is_null() { return f.write_str("Unsupported"); } CStr::from_ptr(c_buf) }; let name = format!("VideoFormat::{}", c_str.to_string_lossy()); f.write_str(&name) } } } } bitflags::bitflags! { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct VideoFlags: u32 { /// no flags const NONE = spa_sys::SPA_VIDEO_FLAG_NONE; /// a variable fps is selected, fps_n and fps_d denote the maximum fps of the video const VARIABLE_FPS = spa_sys::SPA_VIDEO_FLAG_VARIABLE_FPS; /// Each color has been scaled by the alpha value. const PREMULTIPLIED_ALPHA = spa_sys::SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA; /// use the format modifier #[cfg(feature = "v0_3_65")] const MODIFIER = spa_sys::SPA_VIDEO_FLAG_MODIFIER; #[cfg(feature = "v0_3_75")] /// format modifier was not fixated yet const MODIFIER_FIXATION_REQUIRED = spa_sys::SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED; } } #[derive(Copy, Clone, PartialEq, Eq)] pub struct VideoInterlaceMode(pub spa_sys::spa_video_interlace_mode); #[allow(non_upper_case_globals)] impl VideoInterlaceMode { /// all frames are progressive pub const Progressive: Self = Self(spa_sys::SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE); /// 2 fields are interleaved in one video frame. /// Extra buffer flags describe the field order. pub const Interleaved: Self = Self(spa_sys::SPA_VIDEO_INTERLACE_MODE_INTERLEAVED); /// frames contains both interlaced and progressive video, the buffer flags describe the frame fields. pub const Mixed: Self = Self(spa_sys::SPA_VIDEO_INTERLACE_MODE_MIXED); /// 2 fields are stored in one buffer, use the frame ID to get access to the required field. For multiview (the 'views' /// property > 1) the fields of view N can be found at frame ID (N * 2) and (N * 2) + 1. Each field has only half the /// amount of lines as noted in the height property. This mode requires multiple spa_data to describe the fields. pub const Fields: Self = Self(spa_sys::SPA_VIDEO_INTERLACE_MODE_FIELDS); /// Obtain a [`VideoInterlaceMode`] from a raw `spa_video_interlace_mode` variant. pub fn from_raw(raw: spa_sys::spa_video_interlace_mode) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_video_interlace_mode`] representing this `VideoInterlaceMode`. pub fn as_raw(&self) -> spa_sys::spa_video_interlace_mode { self.0 } } #[cfg(feature = "v0_3_65")] impl Debug for VideoInterlaceMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_short_name( spa_sys::spa_type_video_interlace_mode, self.as_raw(), ); if c_buf.is_null() { return f.write_str("Unsupported"); } CStr::from_ptr(c_buf) }; let name = format!( "VideoInterlaceMode::{}", c_str.to_string_lossy().to_case(Case::Pascal) ); f.write_str(&name) } } /// Rust representation of [`spa_sys::spa_video_info_raw`]. #[repr(transparent)] #[derive(PartialEq, Eq, Clone, Copy)] pub struct VideoInfoRaw(spa_sys::spa_video_info_raw); impl VideoInfoRaw { pub fn new() -> Self { Self(spa_sys::spa_video_info_raw { format: VideoFormat::Unknown.as_raw(), flags: 0, modifier: 0, size: Rectangle { width: 0, height: 0, }, framerate: Fraction { num: 0, denom: 0 }, max_framerate: Fraction { num: 0, denom: 0 }, views: 0, interlace_mode: VideoInterlaceMode::Progressive.as_raw(), pixel_aspect_ratio: Fraction { num: 0, denom: 0 }, multiview_mode: 0, multiview_flags: 0, chroma_site: 0, color_range: 0, color_matrix: 0, transfer_function: 0, color_primaries: 0, }) } pub fn set_format(&mut self, format: VideoFormat) { self.0.format = format.as_raw(); } pub fn format(self) -> VideoFormat { VideoFormat::from_raw(self.0.format) } pub fn set_flags(&mut self, flags: VideoFlags) { self.0.flags = flags.bits(); } pub fn flags(self) -> VideoFlags { VideoFlags::from_bits_retain(self.0.flags) } pub fn set_modifier(&mut self, modifier: u64) { self.0.modifier = modifier; } pub fn modifier(self) -> u64 { self.0.modifier } pub fn set_size(&mut self, size: Rectangle) { self.0.size = size; } pub fn size(self) -> Rectangle { self.0.size } pub fn set_framerate(&mut self, framerate: Fraction) { self.0.framerate = framerate; } pub fn framerate(self) -> Fraction { self.0.framerate } pub fn set_max_framerate(&mut self, max_framerate: Fraction) { self.0.max_framerate = max_framerate; } pub fn max_framerate(self) -> Fraction { self.0.max_framerate } pub fn set_views(&mut self, views: u32) { self.0.views = views; } pub fn views(self) -> u32 { self.0.views } pub fn set_interlace_mode(&mut self, interlace_mode: VideoInterlaceMode) { self.0.interlace_mode = interlace_mode.as_raw(); } pub fn interlace_mode(self) -> VideoInterlaceMode { VideoInterlaceMode::from_raw(self.0.interlace_mode) } pub fn set_pixel_aspect_ratio(&mut self, pixel_aspect_ratio: Fraction) { self.0.pixel_aspect_ratio = pixel_aspect_ratio; } pub fn pixel_aspect_ratio(self) -> Fraction { self.0.pixel_aspect_ratio } pub fn set_multiview_mode(&mut self, multiview_mode: i32) { self.0.multiview_mode = multiview_mode; } pub fn multiview_mode(self) -> i32 { self.0.multiview_mode } pub fn set_multiview_flags(&mut self, multiview_flags: u32) { self.0.multiview_flags = multiview_flags; } pub fn multiview_flags(self) -> u32 { self.0.multiview_flags } pub fn set_chroma_site(&mut self, chroma_site: u32) { self.0.chroma_site = chroma_site; } pub fn chroma_site(self) -> u32 { self.0.chroma_site } pub fn set_color_range(&mut self, color_range: u32) { self.0.color_range = color_range; } pub fn color_range(self) -> u32 { self.0.color_range } pub fn set_color_matrix(&mut self, color_matrix: u32) { self.0.color_matrix = color_matrix; } pub fn color_matrix(self) -> u32 { self.0.color_matrix } pub fn set_transfer_function(&mut self, transfer_function: u32) { self.0.transfer_function = transfer_function; } pub fn transfer_function(self) -> u32 { self.0.transfer_function } pub fn set_color_primaries(&mut self, color_primaries: u32) { self.0.color_primaries = color_primaries; } pub fn color_primaries(self) -> u32 { self.0.color_primaries } /// helper function to parse format properties type pub fn parse(&mut self, format: &crate::pod::Pod) -> Result { let res = unsafe { spa_sys::spa_format_video_raw_parse(format.as_raw_ptr(), &mut self.0) }; SpaResult::from_c(res).into_result() } /// Obtain a [`VideoInfoRaw`] from a raw `spa_video_info_raw` variant. pub fn from_raw(raw: spa_sys::spa_video_info_raw) -> Self { Self(raw) } /// Get the raw [`spa_sys::spa_video_info_raw`] representing this `VideoInfoRaw`. pub fn as_raw(&self) -> spa_sys::spa_video_info_raw { self.0 } } impl Default for VideoInfoRaw { fn default() -> Self { Self::new() } } impl Debug for VideoInfoRaw { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { #[cfg(feature = "v0_3_65")] let interlace_mode = self.interlace_mode(); #[cfg(not(feature = "v0_3_65"))] let interlace_mode = self.interlace_mode().as_raw(); f.debug_struct("VideoInfoRaw") .field("format", &self.format()) .field("flags", &self.flags()) .field("modifier", &self.modifier()) .field("size", &self.size()) .field("framerate", &self.framerate()) .field("max_framerate", &self.max_framerate()) .field("views", &self.views()) .field("interlace_mode", &interlace_mode) .field("pixel_aspect_ratio", &self.pixel_aspect_ratio()) .field("multiview_mode", &self.multiview_mode()) .field("multiview_flags", &self.multiview_flags()) .field("chroma_site", &self.chroma_site()) .field("color_range", &self.color_range()) .field("color_matrix", &self.color_matrix()) .field("transfer_function", &self.transfer_function()) .field("color_primaries", &self.color_primaries()) .finish() } } #[cfg(test)] mod tests { use super::*; #[test] #[cfg_attr(miri, ignore)] fn debug_format() { assert_eq!( "VideoFormat::Unknown", format!("{:?}", VideoFormat::Unknown) ); assert_eq!("VideoFormat::YV12", format!("{:?}", VideoFormat::YV12)); assert_eq!("VideoFormat::RGBx", format!("{:?}", VideoFormat::RGBx)); assert_eq!("VideoFormat::xRGB", format!("{:?}", VideoFormat::xRGB)); assert_eq!( "VideoFormat::GRAY16_BE", format!("{:?}", VideoFormat::GRAY16_BE) ); assert_eq!( "VideoFormat::xRGB_210LE", format!("{:?}", VideoFormat::xRGB_210LE) ); #[cfg(feature = "v0_3_65")] assert_eq!( "VideoInterlaceMode::Progressive", format!("{:?}", VideoInterlaceMode::Progressive) ); } } libspa-0.8.0/src/pod/builder.rs000064400000000000000000000532701046102023000144570ustar 00000000000000use 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, } pub struct Builder<'d> { // Keep the actual state in a box, so that // we can be sure that it does not move while the builder is in use // This lets us access it via pointer in the overflow callback inner: Box>, } 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()); // Resize the vec to be `size` longer, so that the new value fits, // then update the builders internal data size and also the data pointer // in case the vec had to reallocate (*this).data.resize(size as usize, 0); (*this).builder.data = (*this).data.as_mut_ptr().cast::(); (*this).builder.size = (*this) .data .len() .try_into() .expect("data length does not fit in a u32"); // Return zero to indicate that we successfully resized our data 0 } pub fn new(data: &'d mut Vec) -> Self { unsafe { let mut builder: MaybeUninit = 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::().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() } /// # Safety /// /// The builder state may only be used as long as all frames that were pushed /// to the builder at the time of this call are alive and not moved pub unsafe fn state(&self) -> spa_sys::spa_pod_builder_state { let mut state: MaybeUninit = MaybeUninit::uninit(); spa_sys::spa_pod_builder_get_state(self.as_raw_ptr(), state.as_mut_ptr()); state.assume_init() } // not bound: set_callbacks // we set those ourselves to resize the Vec /// # Safety /// /// TODO: Constraints unknown, use at own risk 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) } /// # Safety /// /// TODO: Constraints unknown, use at own risk pub unsafe fn deref(&mut self, offset: u32) -> *mut spa_sys::spa_pod { spa_sys::spa_pod_builder_deref(self.as_raw_ptr(), offset) } /// # Safety /// /// TODO: Constraints unknown, use at own risk 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) } /// # Safety /// /// TODO: Constraints unknown, use at own risk 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) } // TODO: raw, pad 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::(), data.len().try_into().unwrap(), ) }; if res >= 0 { Ok(()) } else { Err(Errno::from_i32(-res)) } } /// # Safety /// /// Only the last added frame may be popped 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 _); } } // TODO: primitive 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)) } } } // todo: child 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)) } } } // TODO: write_string, string_len, // TODO: add_string_raw variant? /// # Panics /// /// If `string` contains an interior null byte 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)) } } // TODO: raw bytes variant? 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::(), bytes.len().try_into().unwrap(), ) }; if res >= 0 { Ok(()) } else { Err(Errno::from_i32(-res)) } } // TODO: reserve_bytes /// # Safety /// /// The pointer must be pointing to valid, well-aligned data which has the type as specified by `type_`. 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)) } } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_array( &mut self, frame: &mut MaybeUninit, ) -> 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)) } } /// # Safety /// /// `elems` must point to a valid array containing at least `n_elems` /// with each child having exactly the size as specified by `child_size` and the type `child_type`. 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)) } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_choice( &mut self, frame: &mut MaybeUninit, type_: u32, flags: u32, // FIXME: Make dedicated flag type ) -> 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)) } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_struct( &mut self, frame: &mut MaybeUninit, ) -> 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)) } } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_object( &mut self, frame: &mut MaybeUninit, 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)) } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_sequence( &mut self, frame: &mut MaybeUninit, 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) -> c_int { // Older versions of pipewire mistakenly had the return type as uint32_t, // so we need to use try_into().unwrap() to ensure those versions also work unsafe { spa_sys::spa_pod_builder_control(self.as_raw_ptr(), offset, type_) .try_into() .unwrap() } } } /// Convenience macro to build a pod from values using a spa pod builder. /// /// For arguments, the macro accepts the builder, and then the structure of the desired pod: /// /// ```ignore /// builder_add!(<&mut libspa::pod::builder::Builder>, Bool()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Id()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Int()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Long()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Float()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Double()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Bytes(<&[u8]>)); /// // Macro using `Pointer` can only be called in `unsafe` block. /// // Safety rules from `Builder::add_pointer()` apply. /// builder_add!(<&mut libspa::pod::builder::Builder>, Pointer(<*const c_void>)); /// builder_add!(<&mut libspa::pod::builder::Builder>, Fd()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Rectangle()); /// builder_add!(<&mut libspa::pod::builder::Builder>, Fraction()); /// builder_add!(<&mut libspa::pod::builder::Builder>, /// Struct { /// // 0 to n fields, e.g.: /// Struct { /// Int(), /// Float(), /// }, /// Bytes(<&[u8]>), /// } /// ); /// builder_add(<&mut libspa::pod::builder::Builder>, /// Object( /// , /// /// ) { /// // 0 to n properties of format /// // ` => ` /// // e.g. /// 0 => Bool(false), /// 313 => String("313"), /// } /// ); /// ``` /// /// # Returns /// /// The macro returns a `Result<(), Errno>`. /// If building succeeds, an `Ok(())` is returned. /// Otherwise, the `Err(Errno)` from the point where building failed is returned, and the rest of the values are not added. #[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) }; // TODO: Choice ( $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(()) } }; // TODO: Sequence // TODO: Control } 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 = [ 0u32.to_ne_bytes(), // body has size 16 14u32.to_ne_bytes(), // struct type is 14 ] .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 = [ 16u32.to_ne_bytes(), // body has size 16 14u32.to_ne_bytes(), // struct type is 14 4u32.to_ne_bytes(), // child body size is 4 4u32.to_ne_bytes(), // Int child type is 4 3i32.to_ne_bytes(), // the integer [0, 0, 0, 0], // padding ] .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::param::{ format::{FormatProperties, MediaSubtype, MediaType}, 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()); } } libspa-0.8.0/src/pod/deserialize.rs000064400000000000000000001734641046102023000153410ustar 00000000000000//! This module deals with deserializing raw SPA pods into rust types. //! //! A raw pod can be deserialized into any implementor of the [`PodDeserialize`] trait //! by using [`PodDeserializer::deserialize_from`]. //! //! The crate provides a number of implementors of this trait either directly, //! or through [`FixedSizedPod`](`super::FixedSizedPod`). //! //! You can also implement the [`PodDeserialize`] trait on another type yourself. See the traits documentation for more //! information on how to do that. use std::{convert::Infallible, ffi::c_void, marker::PhantomData, ptr}; use nom::{ bytes::complete::{tag, take}, combinator::{map, map_res, verify}, number::{complete::u32, complete::u64, Endianness}, sequence::{delimited, pair, preceded, terminated}, IResult, }; use super::{ CanonicalFixedSizedPod, ChoiceValue, FixedSizedPod, Object, PropertyFlags, Value, ValueArray, }; use crate::{ pod::Property, utils::{Choice, ChoiceEnum, ChoiceFlags, Fd, Fraction, Id, Rectangle}, }; /// Implementors of this trait can be deserialized from the raw SPA Pod format using a [`PodDeserializer`]- /// /// Their [`deserialize`](`PodDeserialize::deserialize`) method should invoke exactly one of the `deserialize_*()` methods /// of the provided [`PodDeserializer`] that fits the type that should be deserialized. /// /// If you want to deserialize from a pod that always has the same size, implement [`super::FixedSizedPod`] instead /// and this trait will be implemented for you automatically. /// /// # Examples /// Deserialize a `String` pod without copying: /// ```rust /// use std::io; /// use libspa::pod::deserialize::{PodDeserialize, PodDeserializer, DeserializeError, DeserializeSuccess, StringVisitor}; /// /// struct ContainsStr<'s>(&'s str); /// /// impl<'de> PodDeserialize<'de> for ContainsStr<'de> { /// fn deserialize( /// deserializer: PodDeserializer<'de>, /// ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> /// where /// Self: Sized, /// { /// deserializer.deserialize_str(StringVisitor).map(|(s, success)| (ContainsStr(s), success)) /// } /// } /// ``` /// `Bytes` pods are created in the same way, but with the `serialize_bytes` method. /// /// Deserialize an `Array` pod with `Int` elements: /// ```rust /// use std::io; /// use std::io::Cursor; /// use libspa::pod::deserialize::{PodDeserialize, PodDeserializer, DeserializeError, DeserializeSuccess, Visitor}; /// use libspa::pod::serialize::PodSerializer; /// /// struct Numbers(Vec); /// /// impl<'de> PodDeserialize<'de> for Numbers { /// fn deserialize( /// deserializer: PodDeserializer<'de>, /// ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> /// where /// Self: Sized, /// { /// struct NumbersVisitor; /// /// impl<'de> Visitor<'de> for NumbersVisitor { /// type Value = Numbers; /// type ArrayElem = i32; /// /// fn visit_array( /// &self, /// elements: Vec, /// ) -> Result> { /// Ok(Numbers(elements)) /// } /// } /// /// deserializer.deserialize_array(NumbersVisitor) /// } /// } /// ``` /// /// Make a struct deserialize from a `Struct` pod: /// ```rust /// use std::{convert::TryInto, io}; /// use libspa::pod::deserialize::{PodDeserialize, PodDeserializer, DeserializeError, DeserializeSuccess, Visitor, StructPodDeserializer}; /// /// struct Animal { /// name: String, /// feet: u8, /// can_fly: bool, /// } /// /// impl<'de> PodDeserialize<'de> for Animal { /// fn deserialize( /// deserializer: PodDeserializer<'de>, /// ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> /// where /// Self: Sized, /// { /// struct AnimalVisitor; /// /// impl<'de> Visitor<'de> for AnimalVisitor { /// type Value = Animal; /// type ArrayElem = std::convert::Infallible; /// /// fn visit_struct( /// &self, /// struct_deserializer: &mut StructPodDeserializer<'de>, /// ) -> Result> { /// Ok(Animal { /// name: struct_deserializer /// .deserialize_field()? /// .expect("Input has too few fields"), /// feet: struct_deserializer /// .deserialize_field::()? /// .expect("Input has too few fields") /// .try_into() /// .expect("Animal is a millipede, has too many feet for a u8."), /// can_fly: struct_deserializer /// .deserialize_field()? /// .expect("Input has too few fields"), /// }) /// } /// } /// /// deserializer.deserialize_struct(AnimalVisitor) /// } /// } /// ``` pub trait PodDeserialize<'de> { /// Deserialize the type by using the provided [`PodDeserializer`] fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized; } // Deserialize a `String` pod. Returned `&str` is zero-copy (is a slice of the input). impl<'de> PodDeserialize<'de> for &'de str { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { deserializer.deserialize_str(StringVisitor) } } // Deserialize a `String` pod. The returned string is an owned copy. impl<'de> PodDeserialize<'de> for String { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { deserializer .deserialize_str(StringVisitor) .map(|(s, success)| (s.to_owned(), success)) } } // Deserialize a `Bytes` pod. Returned `&[u8]` is zero-copy (is a slice of the input). impl<'de> PodDeserialize<'de> for &'de [u8] { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { deserializer.deserialize_bytes(BytesVisitor) } } // Deserialize a `Bytes` pod. The returned bytes array is an owned copy. impl<'de> PodDeserialize<'de> for Vec { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { deserializer .deserialize_bytes(BytesVisitor) .map(|(b, success)| (b.to_owned(), success)) } } // Deserialize an `Array` type pod. impl<'de, P: FixedSizedPod + CanonicalFixedSizedPod + std::marker::Copy> PodDeserialize<'de> for Vec

{ fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { deserializer.deserialize_array::<_, P>(VecVisitor::

::default()) } } /// This struct is returned by [`PodDeserialize`] implementors on deserialization success. /// /// Because this can only be constructed by the [`PodDeserializer`], [`PodDeserialize`] implementors are forced /// to finish deserialization of their pod instead of stopping after deserializing only part of a pod. pub struct DeserializeSuccess<'de>(PodDeserializer<'de>); /// This struct is responsible for deserializing a raw pod into a [`PodDeserialize`] implementor. pub struct PodDeserializer<'de> { input: &'de [u8], } impl<'de> PodDeserializer<'de> { /// Deserialize a [`PodDeserialize`] implementor from a raw pod. /// /// Deserialization will only succeed if the raw pod matches the kind of pod expected by the [`PodDeserialize`] /// implementor. /// /// # Returns /// /// The remaining input and the type on success, /// or an error that specifies where parsing failed. pub fn deserialize_from>( input: &'de [u8], ) -> Result<(&'de [u8], P), DeserializeError<&'de [u8]>> { let deserializer = Self { input }; P::deserialize(deserializer).map(|(res, success)| (success.0.input, res)) } /// Deserialize a `spa_sys::spa_pod` pointer. /// /// # Safety /// /// - The provided pointer must point to a valid, well-aligned `spa_pod` struct. /// - The pod pointed to must be kept valid for the entire lifetime of the deserialized object if // it has been created using zero-copy deserialization. pub unsafe fn deserialize_ptr>( ptr: ptr::NonNull, ) -> Result> { let len = ptr.as_ref().size; let pod = ptr.as_ptr() as *const _ as *const u8; let slice = std::slice::from_raw_parts(pod, len as usize + 8); let res = PodDeserializer::deserialize_from(slice)?; Ok(res.1) } /// Execute the provide parse function, returning the parsed value or an error. fn parse(&mut self, mut f: F) -> Result>> where F: FnMut(&'de [u8]) -> IResult<&'de [u8], T>, { f(self.input).map(|(input, result)| { self.input = input; result }) } /// Variant of [`Self::parse`] not consuming the parsed data fn peek(&self, mut f: F) -> Result>> where F: FnMut(&'de [u8]) -> IResult<&'de [u8], T>, { f(self.input).map(|(_input, result)| result) } /// Returns the amount of padding needed to align a pod with the provided size to 8 bytes. /// /// In other words, this returns the difference between the provided size and the next multiple of 8. fn calc_padding_needed(size: u32) -> u32 { (8 - (size % 8)) % 8 } /// Parse the size from the header and ensure it has the correct type. pub(super) fn header<'b>(type_: u32) -> impl FnMut(&'b [u8]) -> IResult<&'b [u8], u32> { terminated(u32(Endianness::Native), tag(type_.to_ne_bytes())) } /// Parse and return the type from the header pub(super) fn type_<'b>() -> impl FnMut(&'b [u8]) -> IResult<&'b [u8], u32> { preceded(u32(Endianness::Native), u32(Endianness::Native)) } /// Deserialize any fixed size pod. /// /// Deserialization will only succeed if the [`FixedSizedPod::CanonicalType`] of the requested type matches the type /// of the pod. fn deserialize_fixed_sized_pod( mut self, ) -> Result<(P, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> { let padding = Self::calc_padding_needed(P::CanonicalType::SIZE); self.parse(delimited( Self::header(P::CanonicalType::TYPE), map(P::CanonicalType::deserialize_body, |res| { P::from_canonical_type(&res) }), take(padding), )) .map(|res| (res, DeserializeSuccess(self))) .map_err(|err| err.into()) } /// Deserialize a `none` pod. pub fn deserialize_none( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod::<()>().unwrap(); Ok((visitor.visit_none()?, res.1)) } /// Deserialize a `boolean` pod. pub fn deserialize_bool( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_bool(res.0)?, res.1)) } /// Deserialize an `int` pod. pub fn deserialize_int( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_int(res.0)?, res.1)) } /// Deserialize a `long` pod. pub fn deserialize_long( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_long(res.0)?, res.1)) } /// Deserialize a `float` pod. pub fn deserialize_float( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_float(res.0)?, res.1)) } /// Deserialize a `double` pod. pub fn deserialize_double( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_double(res.0)?, res.1)) } /// Deserialize a `String` pod. pub fn deserialize_str( mut self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let len = self.parse(Self::header(spa_sys::SPA_TYPE_String))?; let padding = Self::calc_padding_needed(len); let res = self.parse(terminated( map_res(terminated(take(len - 1), tag([b'\0'])), std::str::from_utf8), take(padding), ))?; Ok((visitor.visit_string(res)?, DeserializeSuccess(self))) } /// Deserialize a `Bytes` pod. pub fn deserialize_bytes( mut self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let len = self.parse(Self::header(spa_sys::SPA_TYPE_Bytes))?; let padding = Self::calc_padding_needed(len); let res = self.parse(terminated(take(len), take(padding)))?; Ok((visitor.visit_bytes(res)?, DeserializeSuccess(self))) } /// Start parsing an array pod containing elements of type `E`. /// /// # Returns /// - The array deserializer and the number of elements in the array on success /// - An error if the header could not be parsed pub fn new_array_deserializer( mut self, ) -> Result<(ArrayPodDeserializer<'de, E>, u32), DeserializeError<&'de [u8]>> where E: FixedSizedPod, { let len = self.parse(Self::header(spa_sys::SPA_TYPE_Array))?; self.parse(verify(Self::header(E::CanonicalType::TYPE), |len| { *len == E::CanonicalType::SIZE }))?; let num_elems = if E::CanonicalType::SIZE != 0 { (len - 8) / E::CanonicalType::SIZE } else { 0 }; Ok(( ArrayPodDeserializer { deserializer: self, length: num_elems, deserialized: 0, _phantom: PhantomData, }, num_elems, )) } /// Start parsing a struct pod. /// /// # Errors /// Returns a parsing error if input does not start with a struct pod. fn new_struct_deserializer( mut self, ) -> Result, DeserializeError<&'de [u8]>> { let len = self.parse(Self::header(spa_sys::SPA_TYPE_Struct))?; Ok(StructPodDeserializer { deserializer: Some(self), remaining: len, }) } /// Start parsing an object pod. /// /// # Errors /// Returns a parsing error if input does not start with an object pod. fn new_object_deserializer( mut self, ) -> Result, DeserializeError<&'de [u8]>> { let len = self.parse(Self::header(spa_sys::SPA_TYPE_Object))?; let (object_type, object_id) = self.parse(pair(u32(Endianness::Native), u32(Endianness::Native)))?; Ok(ObjectPodDeserializer { deserializer: Some(self), remaining: len - 8, object_type, object_id, }) } /// Deserialize a `Rectangle` pod. pub fn deserialize_rectangle( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_rectangle(res.0)?, res.1)) } /// Deserialize a `Fraction` pod. pub fn deserialize_fraction( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_fraction(res.0)?, res.1)) } /// Deserialize an `Id` pod. pub fn deserialize_id( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_id(res.0)?, res.1)) } /// Deserialize a `Fd` pod. pub fn deserialize_fd( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let res = self.deserialize_fixed_sized_pod()?; Ok((visitor.visit_fd(res.0)?, res.1)) } /// Deserialize a `Struct` pod. pub fn deserialize_struct( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let mut struct_deserializer = self.new_struct_deserializer()?; let res = visitor.visit_struct(&mut struct_deserializer)?; let success = struct_deserializer.end()?; Ok((res, success)) } fn deserialize_array_vec( self, ) -> Result<(Vec, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where T: CanonicalFixedSizedPod + FixedSizedPod + std::marker::Copy, { let mut array_deserializer: ArrayPodDeserializer<'de, T> = self.new_array_deserializer()?.0; let mut elements = Vec::with_capacity(array_deserializer.length as usize); for _ in 0..array_deserializer.length { elements.push(array_deserializer.deserialize_element()?); } let success = array_deserializer.end()?; Ok((elements, success)) } /// Deserialize an `array` pod containing elements of type `T`. pub fn deserialize_array( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de, ArrayElem = T>, T: CanonicalFixedSizedPod + FixedSizedPod + std::marker::Copy, { let (elements, success) = self.deserialize_array_vec::()?; let res = visitor.visit_array(elements)?; Ok((res, success)) } /// Deserialize an `Object` pod. pub fn deserialize_object( self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let mut obj_deserializer = self.new_object_deserializer()?; let res = visitor.visit_object(&mut obj_deserializer)?; let success = obj_deserializer.end()?; Ok((res, success)) } fn deserialize_choice_values( self, num_values: u32, ) -> Result<(Vec, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where E: CanonicalFixedSizedPod + FixedSizedPod, { // re-use the array deserializer as choice values are serialized the same way let mut array_deserializer = ArrayPodDeserializer { deserializer: self, length: num_values, deserialized: 0, _phantom: PhantomData, }; // C implementation documents that there might be more elements than required by the choice type, // which should be ignored, so deserialize all the values. let mut elements = Vec::new(); for _ in 0..num_values { elements.push(array_deserializer.deserialize_element()?); } let success = array_deserializer.end()?; Ok((elements, success)) } /// Deserialize a `Choice` pod. pub fn deserialize_choice( mut self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let len = self.parse(Self::header(spa_sys::SPA_TYPE_Choice))?; let (choice_type, flags) = self.parse(pair(u32(Endianness::Native), u32(Endianness::Native)))?; let (child_size, child_type) = self.parse(pair(u32(Endianness::Native), u32(Endianness::Native)))?; let num_values = (len - 16) / child_size; fn create_choice<'de, E>( choice_type: u32, values: Vec, flags: u32, ) -> Result, DeserializeError<&'de [u8]>> where E: CanonicalFixedSizedPod + FixedSizedPod + Copy, { let flags = ChoiceFlags::from_bits_retain(flags); match choice_type { spa_sys::SPA_CHOICE_None => { if values.is_empty() { Err(DeserializeError::MissingChoiceValues) } else { Ok(Choice(ChoiceFlags::empty(), ChoiceEnum::None(values[0]))) } } spa_sys::SPA_CHOICE_Range => { if values.len() < 3 { Err(DeserializeError::MissingChoiceValues) } else { Ok(Choice( flags, ChoiceEnum::Range { default: values[0], min: values[1], max: values[2], }, )) } } spa_sys::SPA_CHOICE_Step => { if values.len() < 4 { Err(DeserializeError::MissingChoiceValues) } else { Ok(Choice( flags, ChoiceEnum::Step { default: values[0], min: values[1], max: values[2], step: values[3], }, )) } } spa_sys::SPA_CHOICE_Enum => { if values.is_empty() { Err(DeserializeError::MissingChoiceValues) } else { Ok(Choice( flags, ChoiceEnum::Enum { default: values[0], alternatives: values[1..].to_vec(), }, )) } } spa_sys::SPA_CHOICE_Flags => { if values.is_empty() { Err(DeserializeError::MissingChoiceValues) } else { Ok(Choice( flags, ChoiceEnum::Flags { default: values[0], flags: values[1..].to_vec(), }, )) } } _ => Err(DeserializeError::InvalidChoiceType), } } match child_type { spa_sys::SPA_TYPE_Bool => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_bool(choice)?, success)) } spa_sys::SPA_TYPE_Int => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_i32(choice)?, success)) } spa_sys::SPA_TYPE_Long => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_i64(choice)?, success)) } spa_sys::SPA_TYPE_Float => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_f32(choice)?, success)) } spa_sys::SPA_TYPE_Double => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_f64(choice)?, success)) } spa_sys::SPA_TYPE_Id => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_id(choice)?, success)) } spa_sys::SPA_TYPE_Rectangle => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_rectangle(choice)?, success)) } spa_sys::SPA_TYPE_Fraction => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_fraction(choice)?, success)) } spa_sys::SPA_TYPE_Fd => { let (values, success) = self.deserialize_choice_values::(num_values)?; let choice = create_choice(choice_type, values, flags)?; Ok((visitor.visit_choice_fd(choice)?, success)) } _ => Err(DeserializeError::InvalidType), } } /// Deserialize a pointer pod. pub fn deserialize_pointer( mut self, visitor: V, ) -> Result<(V::Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where V: Visitor<'de>, { let len = self.parse(Self::header(spa_sys::SPA_TYPE_Pointer))?; let (type_, _padding) = self.parse(pair(u32(Endianness::Native), u32(Endianness::Native)))?; let ptr_size = len - 8; let res = match ptr_size { 8 => { let ptr = self.parse(u64(Endianness::Native))?; visitor.visit_pointer(type_, ptr as *const c_void)? } 4 => { let ptr = self.parse(u32(Endianness::Native))?; visitor.visit_pointer(type_, ptr as *const c_void)? } _ => panic!("unsupported pointer size {}", ptr_size), }; Ok((res, DeserializeSuccess(self))) } /// Deserialize any kind of pod using a visitor producing [`Value`]. pub fn deserialize_any( self, ) -> Result<(Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> { let type_ = self.peek(Self::type_())?; match type_ { spa_sys::SPA_TYPE_None => self.deserialize_none(ValueVisitor), spa_sys::SPA_TYPE_Bool => self.deserialize_bool(ValueVisitor), spa_sys::SPA_TYPE_Id => self.deserialize_id(ValueVisitor), spa_sys::SPA_TYPE_Int => self.deserialize_int(ValueVisitor), spa_sys::SPA_TYPE_Long => self.deserialize_long(ValueVisitor), spa_sys::SPA_TYPE_Float => self.deserialize_float(ValueVisitor), spa_sys::SPA_TYPE_Double => self.deserialize_double(ValueVisitor), spa_sys::SPA_TYPE_String => self.deserialize_str(ValueVisitor), spa_sys::SPA_TYPE_Bytes => self.deserialize_bytes(ValueVisitor), spa_sys::SPA_TYPE_Rectangle => self.deserialize_rectangle(ValueVisitor), spa_sys::SPA_TYPE_Fraction => self.deserialize_fraction(ValueVisitor), spa_sys::SPA_TYPE_Fd => self.deserialize_fd(ValueVisitor), spa_sys::SPA_TYPE_Struct => self.deserialize_struct(ValueVisitor), spa_sys::SPA_TYPE_Array => self.deserialize_array_any(), spa_sys::SPA_TYPE_Object => self.deserialize_object(ValueVisitor), spa_sys::SPA_TYPE_Choice => self.deserialize_choice(ValueVisitor), spa_sys::SPA_TYPE_Pointer => self.deserialize_pointer(ValueVisitor), _ => Err(DeserializeError::InvalidType), } } fn deserialize_array_any( self, ) -> Result<(Value, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> { let child_type = self.peek(preceded(Self::type_(), Self::type_()))?; let (array, success) = match child_type { spa_sys::SPA_TYPE_None => { let (elements, success) = self.deserialize_array_vec::<()>()?; let array = ValueArrayNoneVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Bool => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayBoolVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Id => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayIdVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Int => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayIntVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Long => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayLongVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Float => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayFloatVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Double => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayDoubleVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Rectangle => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayRectangleVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Fraction => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayFractionVisitor.visit_array(elements)?; (array, success) } spa_sys::SPA_TYPE_Fd => { let (elements, success) = self.deserialize_array_vec::()?; let array = ValueArrayFdVisitor.visit_array(elements)?; (array, success) } _ => return Err(DeserializeError::InvalidType), }; Ok((Value::ValueArray(array), success)) } /// Variant of [`Self::deserialize_from`] returning the parsed value as a [`Value`]. pub fn deserialize_any_from( input: &'de [u8], ) -> Result<(&'de [u8], Value), DeserializeError<&'de [u8]>> { Self::deserialize_from(input) } } /// This struct handles deserializing arrays. /// /// It can be obtained by calling [`PodDeserializer::deserialize_array`]. /// /// The exact number of elements that was returned from that call must be deserialized /// using the [`deserialize_element`](`Self::deserialize_element`) function, /// followed by calling its [`end`](`Self::end`) function to finish deserialization of the array. pub struct ArrayPodDeserializer<'de, E: FixedSizedPod> { deserializer: PodDeserializer<'de>, // The total number of elements that must be deserialized from this array. length: u32, // The number of elements that have been deserialized so far. deserialized: u32, /// The struct has the type parameter E to ensure all deserialized elements are the same type, /// but doesn't actually own any E, so we need the `PhantomData` instead. _phantom: PhantomData, } impl<'de, E: FixedSizedPod> ArrayPodDeserializer<'de, E> { /// Deserialize a single element. /// /// # Panics /// Panics if there are no elements left to deserialize. pub fn deserialize_element(&mut self) -> Result> { if !self.deserialized < self.length { panic!("No elements left in the pod to deserialize"); } let result = self .deserializer .parse(E::CanonicalType::deserialize_body) .map(|res| E::from_canonical_type(&res)) .map_err(|err| err.into()); self.deserialized += 1; result } /// Finish deserializing the array. /// /// # Panics /// Panics if not all elements of the array were deserialized. pub fn end(mut self) -> Result, DeserializeError<&'de [u8]>> { assert!( self.length == self.deserialized, "Not all fields were deserialized from the array pod" ); // Deserialize remaining padding bytes. let bytes_read = self.deserialized * E::CanonicalType::SIZE; let padding = if bytes_read % 8 == 0 { 0 } else { 8 - (bytes_read as usize % 8) }; self.deserializer.parse(take(padding))?; Ok(DeserializeSuccess(self.deserializer)) } } /// This struct handles deserializing structs. /// /// It can be obtained by calling [`PodDeserializer::deserialize_struct`]. /// /// Fields of the struct must be deserialized using its [`deserialize_field`](`Self::deserialize_field`) /// until it returns `None`. /// followed by calling its [`end`](`Self::end`) function to finish deserialization of the struct. pub struct StructPodDeserializer<'de> { /// The deserializer is saved in an option, but can be expected to always be a `Some` /// when `deserialize_field()` or `end()` is called. /// /// `deserialize_field()` `take()`s the deserializer, uses it to deserialize the field, /// and then puts the deserializer back inside. deserializer: Option>, /// Remaining struct pod body length in bytes remaining: u32, } impl<'de> StructPodDeserializer<'de> { /// Deserialize a single field of the struct /// /// Returns `Some` when a field was successfully deserialized and `None` when all fields have been read. pub fn deserialize_field>( &mut self, ) -> Result, DeserializeError<&'de [u8]>> { if self.remaining == 0 { Ok(None) } else { let deserializer = self .deserializer .take() .expect("StructPodDeserializer does not contain a deserializer"); // The amount of input bytes remaining before deserializing the element. let remaining_input_len = deserializer.input.len(); let (res, success) = P::deserialize(deserializer)?; // The amount of bytes deserialized is the length of the remaining input // minus the length of the remaining input now. self.remaining -= remaining_input_len as u32 - success.0.input.len() as u32; self.deserializer = Some(success.0); Ok(Some(res)) } } /// Finish deserialization of the pod. /// /// # Panics /// Panics if not all fields of the pod have been deserialized. pub fn end(self) -> Result, DeserializeError<&'de [u8]>> { assert!( self.remaining == 0, "Not all fields have been deserialized from the struct" ); // No padding parsing needed: Last field will already end aligned. Ok(DeserializeSuccess(self.deserializer.expect( "StructPodDeserializer does not contain a deserializer", ))) } } /// This struct handles deserializing objects. /// /// It can be obtained by calling [`PodDeserializer::deserialize_object`]. /// /// Properties of the object must be deserialized using its [`deserialize_property`](`Self::deserialize_property`) /// until it returns `None`. /// followed by calling its [`end`](`Self::end`) function to finish deserialization of the object. pub struct ObjectPodDeserializer<'de> { /// The deserializer is saved in an option, but can be expected to always be a `Some` /// when `deserialize_property()` or `end()` is called. /// /// `deserialize_property()` `take()`s the deserializer, uses it to deserialize the property, /// and then puts the deserializer back inside. deserializer: Option>, /// Remaining object pod body length in bytes remaining: u32, /// type of the object object_type: u32, /// id of the object object_id: u32, } impl<'de> ObjectPodDeserializer<'de> { /// Deserialize a single property of the object. /// /// Returns `Some` when a property was successfully deserialized and `None` when all properties have been read. #[allow(clippy::type_complexity)] pub fn deserialize_property>( &mut self, ) -> Result, DeserializeError<&'de [u8]>> { if self.remaining == 0 { Ok(None) } else { let mut deserializer = self .deserializer .take() .expect("ObjectPodDeserializer does not contain a deserializer"); // The amount of input bytes remaining before deserializing the element. let remaining_input_len = deserializer.input.len(); let key = deserializer.parse(u32(Endianness::Native))?; let flags = deserializer.parse(u32(Endianness::Native))?; let flags = PropertyFlags::from_bits_retain(flags); let (res, success) = P::deserialize(deserializer)?; // The amount of bytes deserialized is the length of the remaining input // minus the length of the remaining input now. self.remaining -= remaining_input_len as u32 - success.0.input.len() as u32; self.deserializer = Some(success.0); Ok(Some((res, key, flags))) } } /// Variant of [`Self::deserialize_property`] ensuring the property has a given key. /// /// Returns [`DeserializeError::PropertyMissing`] if the property is missing /// and [`DeserializeError::PropertyWrongKey`] if the property does not have the /// expected key. pub fn deserialize_property_key>( &mut self, key: u32, ) -> Result<(P, PropertyFlags), DeserializeError<&'de [u8]>> { let (prop, k, flags) = self .deserialize_property()? .ok_or(DeserializeError::PropertyMissing)?; if k != key { Err(DeserializeError::PropertyWrongKey(k)) } else { Ok((prop, flags)) } } /// Finish deserialization of the pod. /// /// # Panics /// Panics if not all properties of the pod have been deserialized. pub fn end(self) -> Result, DeserializeError<&'de [u8]>> { assert!( self.remaining == 0, "Not all properties have been deserialized from the object" ); // No padding parsing needed: Last field will already end aligned. Ok(DeserializeSuccess(self.deserializer.expect( "ObjectPodDeserializer does not contain a deserializer", ))) } } #[derive(Debug, PartialEq)] /// Represent an error raised when deserializing a pod pub enum DeserializeError { /// Parsing error Nom(nom::Err>), /// The visitor does not support the type UnsupportedType, /// The type is either invalid or not yet supported InvalidType, /// The property is missing from the object PropertyMissing, /// The property does not have the expected key PropertyWrongKey(u32), /// Invalid choice type InvalidChoiceType, /// Values are missing in the choice pod MissingChoiceValues, } impl From>> for DeserializeError { fn from(err: nom::Err>) -> Self { DeserializeError::Nom(err) } } /// This trait represents a visitor is "driven" by the deserializer to construct an instance of your type. pub trait Visitor<'de>: Sized { /// The value produced by this visitor type Value; /// The element type [`Visitor::visit_array`] is expecting as input. /// Only used for visitors implementing this method, /// [`std::convert::Infallible`] can be used as a default. type ArrayElem; /// The input contains a `none`. fn visit_none(&self) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a `bool`. fn visit_bool(&self, _v: bool) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an `i32`. fn visit_int(&self, _v: i32) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an `i64`. fn visit_long(&self, _v: i64) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an `f32`. fn visit_float(&self, _v: f32) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an `f64`. fn visit_double(&self, _v: f64) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a string. fn visit_string(&self, _v: &'de str) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a bytes array. fn visit_bytes(&self, _v: &'de [u8]) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`Rectangle`]. fn visit_rectangle(&self, _v: Rectangle) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`Fraction`]. fn visit_fraction(&self, _v: Fraction) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an [`Id`]. fn visit_id(&self, _v: Id) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an [`Fd`]. fn visit_fd(&self, _v: Fd) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a structure. fn visit_struct( &self, _struct_deserializer: &mut StructPodDeserializer<'de>, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an array. fn visit_array( &self, _elements: Vec, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an object. fn visit_object( &self, _object_deserializer: &mut ObjectPodDeserializer<'de>, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an [`i32`] choice. fn visit_choice_bool( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an [`i32`] choice. fn visit_choice_i32( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains an [`i64`] choice. fn visit_choice_i64( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`f32`] choice. fn visit_choice_f32( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`f64`] choice. fn visit_choice_f64( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`Id`] choice. fn visit_choice_id( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`Rectangle`] choice. fn visit_choice_rectangle( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`Fraction`] choice. fn visit_choice_fraction( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a [`Fd`] choice. fn visit_choice_fd( &self, _choice: Choice, ) -> Result> { Err(DeserializeError::UnsupportedType) } /// The input contains a pointer. fn visit_pointer( &self, _type: u32, _pointer: *const c_void, ) -> Result> { Err(DeserializeError::UnsupportedType) } } /// A visitor producing `()` for none values. pub struct NoneVisitor; impl<'de> Visitor<'de> for NoneVisitor { type Value = (); type ArrayElem = Infallible; fn visit_none(&self) -> Result> { Ok(()) } } /// A visitor producing [`bool`] for boolean values. pub struct BoolVisitor; impl<'de> Visitor<'de> for BoolVisitor { type Value = bool; type ArrayElem = Infallible; fn visit_bool(&self, v: bool) -> Result> { Ok(v) } } /// A visitor producing [`i32`] for integer values. pub struct IntVisitor; impl<'de> Visitor<'de> for IntVisitor { type Value = i32; type ArrayElem = Infallible; fn visit_int(&self, v: i32) -> Result> { Ok(v) } } /// A visitor producing [`i64`] for long values. pub struct LongVisitor; impl<'de> Visitor<'de> for LongVisitor { type Value = i64; type ArrayElem = Infallible; fn visit_long(&self, v: i64) -> Result> { Ok(v) } } /// A visitor producing [`f32`] for float values. pub struct FloatVisitor; impl<'de> Visitor<'de> for FloatVisitor { type Value = f32; type ArrayElem = Infallible; fn visit_float(&self, v: f32) -> Result> { Ok(v) } } /// A visitor producing [`f64`] for double values. pub struct DoubleVisitor; impl<'de> Visitor<'de> for DoubleVisitor { type Value = f64; type ArrayElem = Infallible; fn visit_double(&self, v: f64) -> Result> { Ok(v) } } /// A visitor producing [`&str`] for string values. pub struct StringVisitor; impl<'de> Visitor<'de> for StringVisitor { type Value = &'de str; type ArrayElem = Infallible; fn visit_string(&self, v: &'de str) -> Result> { Ok(v) } } /// A visitor producing [`&[u8]`] for bytes values. pub struct BytesVisitor; impl<'de> Visitor<'de> for BytesVisitor { type Value = &'de [u8]; type ArrayElem = Infallible; fn visit_bytes(&self, v: &'de [u8]) -> Result> { Ok(v) } } /// A visitor producing [`Rectangle`] for rectangle values. pub struct RectangleVisitor; impl<'de> Visitor<'de> for RectangleVisitor { type Value = Rectangle; type ArrayElem = Infallible; fn visit_rectangle(&self, v: Rectangle) -> Result> { Ok(v) } } /// A visitor producing [`Fraction`] for fraction values. pub struct FractionVisitor; impl<'de> Visitor<'de> for FractionVisitor { type Value = Fraction; type ArrayElem = Infallible; fn visit_fraction(&self, v: Fraction) -> Result> { Ok(v) } } /// A visitor producing [`Id`] for ID values. pub struct IdVisitor; impl<'de> Visitor<'de> for IdVisitor { type Value = Id; type ArrayElem = Infallible; fn visit_id(&self, v: Id) -> Result> { Ok(v) } } /// A visitor producing [`Fd`] for file descriptor values. pub struct FdVisitor; impl<'de> Visitor<'de> for FdVisitor { type Value = Fd; type ArrayElem = Infallible; fn visit_fd(&self, v: Fd) -> Result> { Ok(v) } } /// A visitor producing [`Vec`] for array values. pub struct VecVisitor { _phantom: PhantomData, } impl Default for VecVisitor { fn default() -> Self { Self { _phantom: PhantomData, } } } impl<'de, E: CanonicalFixedSizedPod + std::marker::Copy> Visitor<'de> for VecVisitor { type Value = Vec; type ArrayElem = E; fn visit_array(&self, elements: Vec) -> Result> { Ok(elements) } } /// A visitor producing [`Value`] for all type of values. pub struct ValueVisitor; impl<'de> Visitor<'de> for ValueVisitor { type Value = Value; type ArrayElem = std::convert::Infallible; fn visit_none(&self) -> Result> { Ok(Value::None) } fn visit_bool(&self, v: bool) -> Result> { Ok(Value::Bool(v)) } fn visit_int(&self, v: i32) -> Result> { Ok(Value::Int(v)) } fn visit_long(&self, v: i64) -> Result> { Ok(Value::Long(v)) } fn visit_float(&self, v: f32) -> Result> { Ok(Value::Float(v)) } fn visit_double(&self, v: f64) -> Result> { Ok(Value::Double(v)) } fn visit_string(&self, v: &'de str) -> Result> { Ok(Value::String(v.to_string())) } fn visit_bytes(&self, v: &'de [u8]) -> Result> { Ok(Value::Bytes(v.to_vec())) } fn visit_rectangle(&self, v: Rectangle) -> Result> { Ok(Value::Rectangle(v)) } fn visit_fraction(&self, v: Fraction) -> Result> { Ok(Value::Fraction(v)) } fn visit_id(&self, v: Id) -> Result> { Ok(Value::Id(v)) } fn visit_fd(&self, v: Fd) -> Result> { Ok(Value::Fd(v)) } fn visit_struct( &self, struct_deserializer: &mut StructPodDeserializer<'de>, ) -> Result> { let mut res = Vec::new(); while let Some(value) = struct_deserializer.deserialize_field()? { res.push(value); } Ok(Value::Struct(res)) } fn visit_object( &self, object_deserializer: &mut ObjectPodDeserializer<'de>, ) -> Result> { let mut properties = Vec::new(); while let Some((value, key, flags)) = object_deserializer.deserialize_property()? { let prop = Property { key, flags, value }; properties.push(prop); } let object = Object { type_: object_deserializer.object_type, id: object_deserializer.object_id, properties, }; Ok(Value::Object(object)) } fn visit_choice_bool( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Bool(choice))) } fn visit_choice_i32( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Int(choice))) } fn visit_choice_i64( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Long(choice))) } fn visit_choice_f32( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Float(choice))) } fn visit_choice_f64( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Double(choice))) } fn visit_choice_id( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Id(choice))) } fn visit_choice_rectangle( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Rectangle(choice))) } fn visit_choice_fraction( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Fraction(choice))) } fn visit_choice_fd( &self, choice: Choice, ) -> Result> { Ok(Value::Choice(ChoiceValue::Fd(choice))) } fn visit_pointer( &self, type_: u32, pointer: *const c_void, ) -> Result> { Ok(Value::Pointer(type_, pointer)) } } struct ValueArrayNoneVisitor; impl<'de> Visitor<'de> for ValueArrayNoneVisitor { type Value = ValueArray; type ArrayElem = (); fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::None(elements)) } } struct ValueArrayBoolVisitor; impl<'de> Visitor<'de> for ValueArrayBoolVisitor { type Value = ValueArray; type ArrayElem = bool; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Bool(elements)) } } struct ValueArrayIdVisitor; impl<'de> Visitor<'de> for ValueArrayIdVisitor { type Value = ValueArray; type ArrayElem = Id; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Id(elements)) } } struct ValueArrayIntVisitor; impl<'de> Visitor<'de> for ValueArrayIntVisitor { type Value = ValueArray; type ArrayElem = i32; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Int(elements)) } } struct ValueArrayLongVisitor; impl<'de> Visitor<'de> for ValueArrayLongVisitor { type Value = ValueArray; type ArrayElem = i64; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Long(elements)) } } struct ValueArrayFloatVisitor; impl<'de> Visitor<'de> for ValueArrayFloatVisitor { type Value = ValueArray; type ArrayElem = f32; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Float(elements)) } } struct ValueArrayDoubleVisitor; impl<'de> Visitor<'de> for ValueArrayDoubleVisitor { type Value = ValueArray; type ArrayElem = f64; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Double(elements)) } } struct ValueArrayRectangleVisitor; impl<'de> Visitor<'de> for ValueArrayRectangleVisitor { type Value = ValueArray; type ArrayElem = Rectangle; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Rectangle(elements)) } } struct ValueArrayFractionVisitor; impl<'de> Visitor<'de> for ValueArrayFractionVisitor { type Value = ValueArray; type ArrayElem = Fraction; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Fraction(elements)) } } struct ValueArrayFdVisitor; impl<'de> Visitor<'de> for ValueArrayFdVisitor { type Value = ValueArray; type ArrayElem = Fd; fn visit_array( &self, elements: Vec, ) -> Result> { Ok(ValueArray::Fd(elements)) } } /// A visitor producing [`Choice`] for boolean choice values. pub struct ChoiceBoolVisitor; impl<'de> Visitor<'de> for ChoiceBoolVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_bool( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for integer choice values. pub struct ChoiceIntVisitor; impl<'de> Visitor<'de> for ChoiceIntVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_i32( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for long integer choice values. pub struct ChoiceLongVisitor; impl<'de> Visitor<'de> for ChoiceLongVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_i64( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for floating choice values. pub struct ChoiceFloatVisitor; impl<'de> Visitor<'de> for ChoiceFloatVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_f32( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for double floating choice values. pub struct ChoiceDoubleVisitor; impl<'de> Visitor<'de> for ChoiceDoubleVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_f64( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for id choice values. pub struct ChoiceIdVisitor; impl<'de> Visitor<'de> for ChoiceIdVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_id( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for rectangle choice values. pub struct ChoiceRectangleVisitor; impl<'de> Visitor<'de> for ChoiceRectangleVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_rectangle( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for fraction choice values. pub struct ChoiceFractionVisitor; impl<'de> Visitor<'de> for ChoiceFractionVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_fraction( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing [`Choice`] for fd choice values. pub struct ChoiceFdVisitor; impl<'de> Visitor<'de> for ChoiceFdVisitor { type Value = Choice; type ArrayElem = Infallible; fn visit_choice_fd( &self, choice: Choice, ) -> Result> { Ok(choice) } } /// A visitor producing pointers for fd pointer values. pub struct PointerVisitor { _phantom: PhantomData, } impl Default for PointerVisitor { fn default() -> Self { Self { _phantom: PhantomData, } } } impl<'de, T> Visitor<'de> for PointerVisitor { type Value = (u32, *const T); type ArrayElem = Infallible; fn visit_pointer( &self, type_: u32, pointer: *const c_void, ) -> Result> { Ok((type_, pointer as *const T)) } } libspa-0.8.0/src/pod/mod.rs000064400000000000000000001130611046102023000136030ustar 00000000000000//! This module deals with SPA pods, providing ways to represent pods using idiomatic types //! and serialize them into their raw representation, and the other way around. //! //! Everything concerning serializing raw pods from rust types is in the [`serialize`] submodule. //! and everything about deserializing rust types from raw pods is in the [`deserialize`] submodule. //! //! The entire serialization and deserialization approach is inspired by and similar to the excellent `serde` crate, //! but is much more specialized to fit the SPA pod format. pub mod builder; pub mod deserialize; pub mod parser; pub mod serialize; use std::{ ffi::c_void, io::{Seek, Write}, mem::MaybeUninit, os::fd::RawFd, }; use bitflags::bitflags; use cookie_factory::{ bytes::{ne_f32, ne_f64, ne_i32, ne_i64, ne_u32}, gen_simple, sequence::pair, GenError, }; use nix::errno::Errno; use nom::{ combinator::map, number::{ complete::{f32, f64, i32, i64, u32}, Endianness, }, IResult, }; use deserialize::{BoolVisitor, NoneVisitor, PodDeserialize, PodDeserializer}; use serialize::{PodSerialize, PodSerializer}; use crate::utils::{Choice, Fd, Fraction, Id, Rectangle, SpaTypes}; use self::deserialize::{ ChoiceBoolVisitor, ChoiceDoubleVisitor, ChoiceFdVisitor, ChoiceFloatVisitor, ChoiceFractionVisitor, ChoiceIdVisitor, ChoiceIntVisitor, ChoiceLongVisitor, ChoiceRectangleVisitor, DoubleVisitor, FdVisitor, FloatVisitor, FractionVisitor, IdVisitor, IntVisitor, LongVisitor, PointerVisitor, RectangleVisitor, }; /// A transparent wrapper around a `spa_sys::spa_pod`. #[repr(transparent)] pub struct Pod(spa_sys::spa_pod); impl Pod { /// # Safety /// /// The provided pointer must point to a valid, well-aligned pod. /// /// The pods allocation must fit the entire size of the pod as indicated /// by the pods header, including header size, body size and any padding. /// /// The provided pod must not be mutated, moved, freed or similar while /// the borrow returned from this function is in use. /// This also means that other nonmutable borrows may be created to this pod, /// but no mutable borrows to this pod may be created until all borrows are dropped. /// /// The returned type has `'static` lifetime. /// It is suggested to shorten the lifetime to whatever is applicable afterwards. pub unsafe fn from_raw(pod: *const spa_sys::spa_pod) -> &'static Self { pod.cast::().as_ref().unwrap() } /// # Safety /// /// The provided pointer must point to a valid, well-aligned pod. /// /// The pods allocation must fit the entire size of the pod as indicated /// by the pods header, including header size, body size and any padding. /// /// The provided pod must not be mutated, moved, freed or similar while /// the borrow returned from this function is in use. /// This also means that no other borrow to this pod may be created until the borrow is dropped. /// /// The returned type has `'static` lifetime. /// It is suggested to shorten the lifetime to whatever is applicable afterwards. pub unsafe fn from_raw_mut(pod: *mut spa_sys::spa_pod) -> &'static mut Self { pod.cast::().as_mut().unwrap() } pub fn as_raw_ptr(&self) -> *mut spa_sys::spa_pod { std::ptr::addr_of!(self.0).cast_mut() } /// Construct a pod from raw bytes. /// /// The provided slice must be big enough to fit the entire pod including padding. pub fn from_bytes(bytes: &[u8]) -> Option<&Self> { // Ensure bytes contains at least a readable pod header // that we can read the pods size from const HEADER_SIZE: usize = std::mem::size_of::(); if bytes.len() < HEADER_SIZE { return None; } let pod: *const spa_sys::spa_pod = bytes.as_ptr().cast(); // `pod` now points to a valid pod header that we can read let size: usize = unsafe { *pod }.size.try_into().unwrap(); let padding = (8 - (size % 8)) % 8; // Now, ensure that `bytes` is big enough to fit the entire pod if HEADER_SIZE + size + padding <= bytes.len() { // Bytes is big enough to fit the entire header, body and padding. // We can safely convert this to a &Pod Some(unsafe { Self::from_raw(pod) }) } else { None } } pub fn as_bytes(&self) -> &[u8] { let ptr: *const u8 = self.as_raw_ptr().cast(); let size: usize = self.size().try_into().unwrap(); let size = size + std::mem::size_of::(); unsafe { std::slice::from_raw_parts(ptr, size) } } pub fn type_(&self) -> SpaTypes { SpaTypes::from_raw(self.0.type_) } pub fn size(&self) -> u32 { self.0.size } // TODO: Other methods from iter.h that are still missing pub fn is_none(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_none(self.as_raw_ptr()) }; res != 0 } pub fn is_bool(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_bool(self.as_raw_ptr()) }; res != 0 } pub fn get_bool(&self) -> Result { unsafe { let mut b: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_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 is_id(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_id(self.as_raw_ptr()) }; res != 0 } pub fn get_id(&self) -> Result { unsafe { let mut id: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_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 is_int(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_int(self.as_raw_ptr()) }; res != 0 } pub fn get_int(&self) -> Result { unsafe { let mut int: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_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 is_long(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_long(self.as_raw_ptr()) }; res != 0 } pub fn get_long(&self) -> Result { unsafe { let mut long: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_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 is_float(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_float(self.as_raw_ptr()) }; res != 0 } pub fn get_float(&self) -> Result { unsafe { let mut float: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_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 is_double(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_double(self.as_raw_ptr()) }; res != 0 } pub fn get_double(&self) -> Result { unsafe { let mut double: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_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 is_string(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_string(self.as_raw_ptr()) }; res != 0 } // TODO: to_string pub fn is_bytes(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_bytes(self.as_raw_ptr()) }; res != 0 } pub fn get_bytes(&self) -> Result<&[u8], Errno> { unsafe { let mut bytes: MaybeUninit<*const c_void> = MaybeUninit::uninit(); let mut len: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_get_bytes(self.as_raw_ptr(), bytes.as_mut_ptr(), 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.cast(), len.try_into().unwrap()); Ok(bytes) } else { Err(Errno::from_i32(-res)) } } } pub fn is_pointer(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_pointer(self.as_raw_ptr()) }; res != 0 } pub fn get_pointer(&self) -> Result<(*const c_void, Id), Errno> { unsafe { let mut _type: MaybeUninit = MaybeUninit::uninit(); let mut pointer: MaybeUninit<*const c_void> = MaybeUninit::uninit(); let res = spa_sys::spa_pod_get_pointer( self.as_raw_ptr(), _type.as_mut_ptr(), pointer.as_mut_ptr(), ); if res >= 0 { let _type = Id(_type.assume_init()); let pointer = pointer.assume_init(); Ok((pointer, _type)) } else { Err(Errno::from_i32(-res)) } } } pub fn is_fd(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_fd(self.as_raw_ptr()) }; res != 0 } pub fn get_fd(&self) -> Result { unsafe { let mut fd: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_get_fd(self.as_raw_ptr(), fd.as_mut_ptr()); if res >= 0 { let fd = fd.assume_init(); let fd: RawFd = fd.try_into().unwrap(); Ok(fd) } else { Err(Errno::from_i32(-res)) } } } pub fn is_rectangle(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_rectangle(self.as_raw_ptr()) }; res != 0 } pub fn get_rectangle(&self) -> Result { unsafe { let mut rectangle: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_get_rectangle(self.as_raw_ptr(), rectangle.as_mut_ptr()); if res >= 0 { Ok(rectangle.assume_init()) } else { Err(Errno::from_i32(-res)) } } } pub fn is_fraction(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_fraction(self.as_raw_ptr()) }; res != 0 } pub fn get_fraction(&self) -> Result { unsafe { let mut fraction: MaybeUninit = MaybeUninit::uninit(); let res = spa_sys::spa_pod_get_fraction(self.as_raw_ptr(), fraction.as_mut_ptr()); if res >= 0 { Ok(fraction.assume_init()) } else { Err(Errno::from_i32(-res)) } } } pub fn is_bitmap(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_bitmap(self.as_raw_ptr()) }; res != 0 } pub fn is_array(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_array(self.as_raw_ptr()) }; res != 0 } pub fn is_choice(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_choice(self.as_raw_ptr()) }; res != 0 } pub fn is_struct(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_struct(self.as_raw_ptr()) }; res != 0 } pub fn is_object(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_object(self.as_raw_ptr()) }; res != 0 } pub fn is_sequence(&self) -> bool { let res = unsafe { spa_sys::spa_pod_is_sequence(self.as_raw_ptr()) }; res != 0 } } /// Implementors of this trait are the canonical representation of a specific type of fixed sized SPA pod. /// /// They can be used as an output type for [`FixedSizedPod`] implementors /// and take care of the actual serialization/deserialization from/to the type of raw SPA pod they represent. /// /// The trait is sealed, so it can't be implemented outside of this crate. /// This is to ensure that no invalid pod can be serialized. /// /// If you want to have your type convert from and to a fixed sized pod, implement [`FixedSizedPod`] instead and choose /// a fitting implementor of this trait as the `CanonicalType` instead. pub trait CanonicalFixedSizedPod: private::CanonicalFixedSizedPodSeal { /// The raw type this serializes into. #[doc(hidden)] const TYPE: u32; /// The size of the pods body. #[doc(hidden)] const SIZE: u32; #[doc(hidden)] fn serialize_body(&self, out: O) -> Result; #[doc(hidden)] fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized; } mod private { /// This trait makes [`super::CanonicalFixedSizedPod`] a "sealed trait", which makes it impossible to implement /// outside of this crate. pub trait CanonicalFixedSizedPodSeal {} impl CanonicalFixedSizedPodSeal for () {} impl CanonicalFixedSizedPodSeal for bool {} impl CanonicalFixedSizedPodSeal for i32 {} impl CanonicalFixedSizedPodSeal for i64 {} impl CanonicalFixedSizedPodSeal for f32 {} impl CanonicalFixedSizedPodSeal for f64 {} impl CanonicalFixedSizedPodSeal for super::Rectangle {} impl CanonicalFixedSizedPodSeal for super::Fraction {} impl CanonicalFixedSizedPodSeal for super::Id {} impl CanonicalFixedSizedPodSeal for super::Fd {} } impl FixedSizedPod for T { type CanonicalType = Self; fn as_canonical_type(&self) -> Self::CanonicalType { *self } fn from_canonical_type(canonical: &Self::CanonicalType) -> Self { *canonical } } /// Serialize into a `None` type pod. impl CanonicalFixedSizedPod for () { const TYPE: u32 = spa_sys::SPA_TYPE_None; const SIZE: u32 = 0; fn serialize_body(&self, out: O) -> Result { Ok(out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { Ok((input, ())) } } /// Serialize into a `Bool` type pod. impl CanonicalFixedSizedPod for bool { const TYPE: u32 = spa_sys::SPA_TYPE_Bool; const SIZE: u32 = 4; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_u32(u32::from(*self)), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { map(u32(Endianness::Native), |b| b != 0)(input) } } /// Serialize into a `Int` type pod. impl CanonicalFixedSizedPod for i32 { const TYPE: u32 = spa_sys::SPA_TYPE_Int; const SIZE: u32 = 4; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_i32(*self), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { i32(Endianness::Native)(input) } } /// Serialize into a `Long` type pod. impl CanonicalFixedSizedPod for i64 { const TYPE: u32 = spa_sys::SPA_TYPE_Long; const SIZE: u32 = 8; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_i64(*self), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { i64(Endianness::Native)(input) } } /// Serialize into a `Float` type pod. impl CanonicalFixedSizedPod for f32 { const TYPE: u32 = spa_sys::SPA_TYPE_Float; const SIZE: u32 = 4; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_f32(*self), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { f32(Endianness::Native)(input) } } /// Serialize into a `Double` type pod. impl CanonicalFixedSizedPod for f64 { const TYPE: u32 = spa_sys::SPA_TYPE_Double; const SIZE: u32 = 8; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_f64(*self), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { f64(Endianness::Native)(input) } } /// Serialize into a `Rectangle` type pod. impl CanonicalFixedSizedPod for Rectangle { const TYPE: u32 = spa_sys::SPA_TYPE_Rectangle; const SIZE: u32 = 8; fn serialize_body(&self, out: O) -> Result { gen_simple(pair(ne_u32(self.width), ne_u32(self.height)), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { map( nom::sequence::pair(u32(Endianness::Native), u32(Endianness::Native)), |(width, height)| Rectangle { width, height }, )(input) } } /// Serialize into a `Fraction` type pod. impl CanonicalFixedSizedPod for Fraction { const TYPE: u32 = spa_sys::SPA_TYPE_Fraction; const SIZE: u32 = 8; fn serialize_body(&self, out: O) -> Result { gen_simple(pair(ne_u32(self.num), ne_u32(self.denom)), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { map( nom::sequence::pair(u32(Endianness::Native), u32(Endianness::Native)), |(num, denom)| Fraction { num, denom }, )(input) } } impl CanonicalFixedSizedPod for Id { const TYPE: u32 = spa_sys::SPA_TYPE_Id; const SIZE: u32 = 4; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_u32(self.0), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { map(u32(Endianness::Native), Id)(input) } } impl CanonicalFixedSizedPod for Fd { const TYPE: u32 = spa_sys::SPA_TYPE_Fd; const SIZE: u32 = 8; fn serialize_body(&self, out: O) -> Result { gen_simple(ne_i64(self.0), out) } fn deserialize_body(input: &[u8]) -> IResult<&[u8], Self> where Self: Sized, { map(i64(Endianness::Native), Fd)(input) } } /// Implementors of this trait can be serialized into pods that always have the same size. /// This lets them be used as elements in `Array` type SPA Pods. /// /// Implementors of this automatically implement [`PodSerialize`]. /// /// Serialization is accomplished by having the type convert itself into/from the canonical representation of this pod, /// e.g. `i32` for a `Int` type pod. /// /// That type then takes care of the actual serialization. /// /// See the [`CanonicalFixedSizedPod`] trait for a list of possible target types. /// /// Which type to convert in is specified with the traits [`FixedSizedPod::CanonicalType`] type, /// while the traits [`as_canonical_type`](`FixedSizedPod::as_canonical_type`) /// and [`from_canonical_type`](`FixedSizedPod::from_canonical_type`) methods are responsible for the actual conversion. /// /// # Examples /// Implementing the trait on a `i32` newtype wrapper: /// ```rust /// use libspa::pod::FixedSizedPod; /// /// struct Newtype(i32); /// /// impl FixedSizedPod for Newtype { /// // The pod we want to serialize into is a `Int` type pod, which has `i32` as it's canonical representation. /// type CanonicalType = i32; /// /// fn as_canonical_type(&self) -> Self::CanonicalType { /// // Convert self to the canonical type. /// self.0 /// } /// /// fn from_canonical_type(canonical: &Self::CanonicalType) -> Self { /// // Create a new Self instance from the canonical type. /// Newtype(*canonical) /// } /// } /// ``` pub trait FixedSizedPod { /// The canonical representation of the type of pod that should be serialized to/deserialized from. type CanonicalType: CanonicalFixedSizedPod; /// Convert `self` to the canonical type. fn as_canonical_type(&self) -> Self::CanonicalType; /// Convert the canonical type to `Self`. fn from_canonical_type(_: &Self::CanonicalType) -> Self; } impl PodSerialize for T { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { serializer.serialized_fixed_sized_pod(self) } } impl<'de> PodDeserialize<'de> for () { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_none(NoneVisitor) } } impl<'de> PodDeserialize<'de> for bool { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_bool(BoolVisitor) } } impl<'de> PodDeserialize<'de> for i32 { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_int(IntVisitor) } } impl<'de> PodDeserialize<'de> for i64 { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_long(LongVisitor) } } impl<'de> PodDeserialize<'de> for f32 { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_float(FloatVisitor) } } impl<'de> PodDeserialize<'de> for f64 { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_double(DoubleVisitor) } } impl<'de> PodDeserialize<'de> for Rectangle { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_rectangle(RectangleVisitor) } } impl<'de> PodDeserialize<'de> for Fraction { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_fraction(FractionVisitor) } } impl<'de> PodDeserialize<'de> for Id { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_id(IdVisitor) } } impl<'de> PodDeserialize<'de> for Fd { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_fd(FdVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceBoolVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceIntVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceLongVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceFloatVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceDoubleVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceIdVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceRectangleVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceFractionVisitor) } } impl<'de> PodDeserialize<'de> for Choice { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_choice(ChoiceFdVisitor) } } impl<'de, T> PodDeserialize<'de> for (u32, *const T) { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_pointer(PointerVisitor::::default()) } } impl<'de> PodDeserialize<'de> for Value { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result< (Self, deserialize::DeserializeSuccess<'de>), deserialize::DeserializeError<&'de [u8]>, > where Self: Sized, { deserializer.deserialize_any() } } /// A typed pod value. #[derive(Debug, Clone, PartialEq)] pub enum Value { /// no value or a NULL pointer. None, /// a boolean value. Bool(bool), /// an enumerated value. Id(Id), /// a 32 bits integer. Int(i32), /// a 64 bits integer. Long(i64), /// a 32 bits floating. Float(f32), /// a 64 bits floating. Double(f64), /// a string. String(String), /// a byte array. Bytes(Vec), /// a rectangle with width and height. Rectangle(Rectangle), /// a fraction with numerator and denominator. Fraction(Fraction), /// a file descriptor. Fd(Fd), /// an array of same type objects. ValueArray(ValueArray), /// a collection of types and objects. Struct(Vec), /// an object. Object(Object), /// a choice. Choice(ChoiceValue), /// a pointer. Pointer(u32, *const c_void), } /// an array of same type objects. #[derive(Debug, Clone, PartialEq)] pub enum ValueArray { /// an array of none. None(Vec<()>), /// an array of booleans. Bool(Vec), /// an array of Id. Id(Vec), /// an array of 32 bits integer. Int(Vec), /// an array of 64 bits integer. Long(Vec), /// an array of 32 bits floating. Float(Vec), /// an array of 64 bits floating. Double(Vec), /// an array of Rectangle. Rectangle(Vec), /// an array of Fraction. Fraction(Vec), /// an array of Fd. Fd(Vec), } /// A typed choice. #[derive(Debug, Clone, PartialEq)] pub enum ChoiceValue { /// Choice on boolean values. Bool(Choice), /// Choice on 32 bits integer values. Int(Choice), /// Choice on 64 bits integer values. Long(Choice), /// Choice on 32 bits floating values. Float(Choice), /// Choice on 64 bits floating values. Double(Choice), /// Choice on id values. Id(Choice), /// Choice on rectangle values. Rectangle(Choice), /// Choice on fraction values. Fraction(Choice), /// Choice on fd values. Fd(Choice), } /// An object from a pod. #[derive(Debug, Clone, PartialEq)] pub struct Object { /// the object type. pub type_: u32, /// the object id. pub id: u32, /// the object properties. pub properties: Vec, } /// A macro for creating a new [`Object`] with properties. /// /// The macro accepts the object type, id and a list of properties, separated by commas. /// /// # Examples: /// Create an `Object`. /// ```rust /// use libspa::pod::{object, property}; /// /// let pod_object = object!{ /// libspa::utils::SpaTypes::ObjectParamFormat, /// libspa::param::ParamType::EnumFormat, /// property!( /// libspa::param::format::FormatProperties::MediaType, /// Id, /// libspa::param::format::MediaType::Video /// ), /// property!( /// libspa::param::format::FormatProperties::MediaSubtype, /// Id, /// libspa::param::format::MediaSubtype::Raw /// ), /// }; /// ``` #[doc(hidden)] #[macro_export] macro_rules! __object__ { ($type_:expr, $id:expr, $($properties:expr),* $(,)?) => { pipewire::spa::pod::Object { type_: $type_.as_raw(), id: $id.as_raw(), properties: [ $( $properties, )* ].to_vec(), } }; } #[doc(inline)] pub use __object__ as object; /// An object property. #[derive(Debug, Clone, PartialEq)] pub struct Property { /// key of the property, list of valid keys depends on the object type. pub key: u32, /// flags for the property. pub flags: PropertyFlags, /// value of the property. pub value: Value, } impl Property { pub fn new(key: u32, value: Value) -> Self { Self { key, value, flags: PropertyFlags::empty(), } } } bitflags! { /// Property flags #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct PropertyFlags: u32 { // These flags are redefinitions from // https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/spa/include/spa/pod/pod.h /// Property is read-only. const READONLY = spa_sys::SPA_POD_PROP_FLAG_READONLY; /// Property is some sort of hardware parameter. const HARDWARE = spa_sys::SPA_POD_PROP_FLAG_HARDWARE; /// Property contains a dictionary struct. const HINT_DICT = spa_sys::SPA_POD_PROP_FLAG_HINT_DICT; /// Property is mandatory. const MANDATORY = spa_sys::SPA_POD_PROP_FLAG_MANDATORY; /// Property choices need no fixation. #[cfg(feature = "v0_3_33")] const DONT_FIXATE = spa_sys::SPA_POD_PROP_FLAG_DONT_FIXATE; } } /// A macro for creating a new Object [`Property`]. /// /// The macro accepts the following: /// - properties!(libspa::format::FormatProperties::``, Id, ``) /// - properties!(libspa::format::FormatProperties::``, ``, libspa::utils::``(``)) /// - properties!(libspa::format::FormatProperties::``, Choice, Enum, Id, ``, ``, ...) /// - properties!(libspa::format::FormatProperties::``, Choice, Enum, ``, /// libspa::utils::``(``), /// libspa::utils::``(``), ...) /// - properties!(libspa::format::FormatProperties::``, Choice, Flags, ``, /// libspa::utils::``(``), /// libspa::utils::``(``), ...) /// - properties!(libspa::format::FormatProperties::``, Choice, Step, ``, /// libspa::utils::``(default), /// libspa::utils::``(min), /// libspa::utils::``(max), /// libspa::utils::``(step)) /// - properties!(libspa::format::FormatProperties::``, Choice, Range, ``, /// libspa::utils::``(default), /// libspa::utils::``(min), /// libspa::utils::``(max)) #[doc(hidden)] #[macro_export] macro_rules! __property__ { ($key:expr, $value:expr) => { pipewire::spa::pod::Property { key: $key.as_raw(), flags: pipewire::spa::pod::PropertyFlags::empty(), value: $value, } }; ($key:expr, Id, $value:expr) => { pipewire::spa::pod::property!($key, pipewire::spa::pod::Value::Id(pipewire::spa::utils::Id($value.as_raw()))) }; ($key:expr, $type_:ident, $value:expr) => { pipewire::spa::pod::property!($key, pipewire::spa::pod::Value::$type_($value)) }; ($key:expr, Choice, Enum, Id, $default:expr, $($alternative:expr),+ $(,)?) => { pipewire::spa::pod::property!( $key, pipewire::spa::pod::Value::Choice(pipewire::spa::pod::ChoiceValue::Id( pipewire::spa::utils::Choice::( pipewire::spa::utils::ChoiceFlags::empty(), pipewire::spa::utils::ChoiceEnum::::Enum { default: pipewire::spa::utils::Id($default.as_raw()), alternatives: [ $( pipewire::spa::utils::Id($alternative.as_raw()), )+ ].to_vec() } ) )) ) }; ($key:expr, Choice, Enum, $type_:ident, $default:expr, $($alternative:expr),+ $(,)?) => { pipewire::spa::pod::property!( $key, pipewire::spa::pod::Value::Choice(pipewire::spa::pod::ChoiceValue::$type_( pipewire::spa::utils::Choice::( pipewire::spa::utils::ChoiceFlags::empty(), pipewire::spa::utils::ChoiceEnum::::Enum { default: $default, alternatives: [ $( $alternative, )+ ].to_vec() } ) )) ) }; ($key:expr, Choice, Flags, $type_:ident, $default:expr, $($alternative:expr),+ $(,)?) => { pipewire::spa::pod::property!( $key, pipewire::spa::pod::Value::Choice(pipewire::spa::pod::ChoiceValue::$type_( pipewire::spa::utils::Choice::( pipewire::spa::utils::ChoiceFlags::empty(), pipewire::spa::utils::ChoiceEnum::::Flags { default: $default, flags: [ $( $alternative, )+ ].to_vec() } ) )) ) }; ($key:expr, Choice, Step, $type_:ident, $default:expr, $min:expr, $max:expr, $step:expr) => { pipewire::spa::pod::property!( $key, pipewire::spa::pod::Value::Choice(pipewire::spa::pod::ChoiceValue::$type_( pipewire::spa::utils::Choice::( pipewire::spa::utils::ChoiceFlags::empty(), pipewire::spa::utils::ChoiceEnum::::Step { default: $default, min: $min, max: $max, step: $step, } ) )) ) }; ($key:expr, Choice, Range, $type_:ident, $default:expr, $min:expr, $max:expr) => { pipewire::spa::pod::property!( $key, pipewire::spa::pod::Value::Choice(pipewire::spa::pod::ChoiceValue::$type_( pipewire::spa::utils::Choice::( pipewire::spa::utils::ChoiceFlags::empty(), pipewire::spa::utils::ChoiceEnum::::Range { default: $default, min: $min, max: $max, } ) )) ) }; } #[doc(inline)] pub use __property__ as property; libspa-0.8.0/src/pod/parser.rs000064400000000000000000000553031046102023000143240ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT use std::{ ffi::{c_char, c_double, c_float, c_void, CStr}, marker::PhantomData, mem::MaybeUninit, }; use nix::errno::Errno; use crate::utils::{Fraction, Id, Rectangle}; /// Low-level wrapper around `spa_pod_parser`. /// /// Using this may require using `unsafe` and/or working with C types, but /// is still more safe and rusty than the raw functions and types. #[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 = 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 = 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 } /// # Safety /// /// The parser state may only be used as long as all frames that were pushed /// to the parser at the time of this call are alive and not moved pub unsafe fn state(&self) -> spa_sys::spa_pod_parser_state { let mut state: MaybeUninit = MaybeUninit::uninit(); spa_sys::spa_pod_parser_get_state(self.as_raw_ptr(), state.as_mut_ptr()); state.assume_init() } /// # Safety /// /// TODO: Constraints unknown, use at own risk 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) } /// # Safety /// /// TODO: Constraints unknown, use at own risk 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) } /// # Safety /// /// TODO: Constraints unknown, use at own risk 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) } /// # Safety /// /// TODO: Constraints unknown, use at own risk 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()) } } /// # Safety /// /// Pod pointed to must we valid, well aligned, and contained in the current frame /// /// TODO: Any other constraints? Use at own risk pub unsafe fn advance(&mut self, pod: *const spa_sys::spa_pod) { spa_sys::spa_pod_parser_advance(self.as_raw_ptr(), pod) } /// # Safety /// /// TODO: Constraints unknown, use at own risk pub unsafe fn next(&mut self) -> *mut spa_sys::spa_pod { spa_sys::spa_pod_parser_next(self.as_raw_ptr()) } /// # Safety /// /// Only the last added frame may be popped 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 { unsafe { let mut b: MaybeUninit = 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 { unsafe { let mut id: MaybeUninit = 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 { unsafe { let mut int: MaybeUninit = 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 { unsafe { let mut long: MaybeUninit = 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 { unsafe { let mut float: MaybeUninit = 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 { unsafe { let mut double: MaybeUninit = 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(); // FIXME: Do we need to check string for null? 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 = 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(); // TODO: Do we need to check bytes for null? 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 = 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 { unsafe { let mut fd: MaybeUninit = 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 { unsafe { let mut rect: MaybeUninit = 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 { unsafe { let mut frac: MaybeUninit = 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 { // Safety: // spa_pod_parser_get_pod() guarantees that if res >= 0, then // the returned pod is valid and fits in the parsed memory slice. let pod = crate::pod::Pod::from_raw(pod.assume_init()); Ok(pod) } else { Err(Errno::from_i32(-res)) } } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_struct( &mut self, frame: &mut MaybeUninit, ) -> 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)) } } /// # Safety /// The provided frame must not be moved or destroyed before it is popped again. /// /// The frame may only be assumed as initialized if this method returns `Ok`. pub unsafe fn push_object( &mut self, frame: &mut MaybeUninit, _type: u32, ) -> Result { let mut id: MaybeUninit = 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)) } } } /// Convenience macro to parse values from a spa pod using a spa pod parser. /// /// For arguments, the macro accepts the parser, and then the structure of the desired pods: /// /// ```ignore /// parser_get!(<&mut libspa::pod::parser::Parser>, Bool(<&mut bool>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Id(<&mut libspa::utils::Id>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Int(<&mut i32>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Long(<&mut i64>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Float(<&mut f32>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Double(<&mut f64>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Bytes(<&mut &[u8]>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Pointer(<&mut *const c_void>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Fd(<&mut i64>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Rectangle(<&mut libspa::utils::Rectangle>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Fraction(<&mut libspa::utils::Fraction>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, Pod(<&mut &libspa::pod::Pod>)); /// parser_get!(<&mut libspa::pod::parser::Parser>, /// Struct { /// // 0 to n fields, e.g.: /// Struct { /// Int(<&mut i32>), /// Float(<&mut f32>), /// }, /// Bytes(<&mut &[u8]), /// } /// ); /// ``` /// /// # Returns /// /// The macro returns a `Result<(), Errno>`. /// If parsing succeeds, an `Ok(())` is returned. /// Otherwise, the `Err(Errno)` from the point where parsing failed is returned, and the rest of the values are not parsed. #[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(|_| {}) } }; // TODO: String ($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() { // Discard Ok variant value so we can assign to Result<(), Errno> break 'outer res.map(|_| {}); } )* unsafe { $crate::pod::parser::Parser::pop($parser, frame.assume_init_mut()) } } }; // TODO: Object // TODO: ($parser:expr, Option( $type_:tt $val:tt )) or similar for optional values } pub use __parser_get__ as parser_get; #[cfg(test)] mod tests { use super::{parser_get, Parser}; // FIXME: The way we construct raw pods here is rather crude and error-prone. // Maybe replace it with the pod builder in the future, and share the tests with it. #[test] #[cfg_attr(miri, ignore)] fn parse_bool() { let pod: Vec = [ &4u32.to_ne_bytes(), // bool body size &2u32.to_ne_bytes(), // bool type &1u32.to_ne_bytes(), // bool "true" &[0, 0, 0, 0], // padding ] .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 = [ &0u32.to_ne_bytes(), // body size: 0 children => 0 bytes &14u32.to_ne_bytes(), // struct type ] .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(), // body size: (1 child * 104 bytes) + (4 children * 16 bytes per child) = 168 bytes &14u32.to_ne_bytes(), // struct type // begin inner struct &96u32.to_ne_bytes(), // body size: (6 children * 16 bytes per child) = 96 bytes &14u32.to_ne_bytes(), // struct type &4u32.to_ne_bytes(), // bool body size &2u32.to_ne_bytes(), // bool type &1u32.to_ne_bytes(), // bool "true" &[0, 0, 0, 0], // padding &4u32.to_ne_bytes(), // id body size &3u32.to_ne_bytes(), // id type &313u32.to_ne_bytes(), // id 313 &[0, 0, 0, 0], // padding &4u32.to_ne_bytes(), // int body size &4u32.to_ne_bytes(), // int type &313i32.to_ne_bytes(), // int 313 &[0, 0, 0, 0], // padding &8u32.to_ne_bytes(), // long body size &5u32.to_ne_bytes(), // long type &313i64.to_ne_bytes(), // long 313 &4u32.to_ne_bytes(), // float body size &6u32.to_ne_bytes(), // float type &31.3f32.to_ne_bytes(), // float 31.3 &[0, 0, 0, 0], // padding &8u32.to_ne_bytes(), // double body size &7u32.to_ne_bytes(), // double type &31.3f64.to_ne_bytes(), // double 31.3 // end inner struct &3u32.to_ne_bytes(), // bytes body size &9u32.to_ne_bytes(), // bytes type &[3, 1, 3], // bytes [3u8, 1u8, 3u8] &[0, 0, 0, 0, 0], // padding &8u32.to_ne_bytes(), // fd body size &18u32.to_ne_bytes(), // fd type &313i64.to_ne_bytes(), // fd 313 &8u32.to_ne_bytes(), // rectangle body size &10u32.to_ne_bytes(), // rectangle type &313u32.to_ne_bytes(), // rectangle width 313 &131u32.to_ne_bytes(), // rectangle height 131 &8u32.to_ne_bytes(), // fraction body size &11u32.to_ne_bytes(), // fraction type &313u32.to_ne_bytes(), // fraction num 313 &131u32.to_ne_bytes(), // fraction denom 131 ]; let pod: Vec = 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 } ); } } libspa-0.8.0/src/pod/serialize.rs000064400000000000000000000637001046102023000150170ustar 00000000000000//! This module deals with serializing rust types into raw SPA pods. //! //! A raw pod can be serialized by passing a implementor of the [`PodSerialize`] trait //! to [`PodSerializer::serialize`]. //! //! The crate provides a number of implementors of this trait either directly, //! or through [`FixedSizedPod`](`super::FixedSizedPod`). //! //! You can also implement the [`PodSerialize`] trait on another type yourself. See the traits documentation for more //! information on how to do that. use std::{ convert::TryInto, ffi::CString, io::{Seek, SeekFrom, Write}, marker::PhantomData, }; pub use cookie_factory::GenError; use cookie_factory::{ bytes::{ne_u32, ne_u64, ne_u8}, combinator::slice, gen, multi::all, sequence::{pair, tuple}, SerializeFn, }; use crate::{ pod::ChoiceValue, utils::{Choice, ChoiceEnum}, }; use super::{CanonicalFixedSizedPod, FixedSizedPod, PropertyFlags, Value, ValueArray}; /// Implementors of this trait are able to serialize themselves into a SPA pod by using a [`PodSerializer`]. /// /// Their [`serialize`](`PodSerialize::serialize`) method should invoke exactly one of the `serialize_*()` methods /// of the provided [`PodSerializer`] that fits the type that will be serialized. /// /// If you want to serialize into a pod that always has the same size, implement [`FixedSizedPod`] instead /// and this trait will be implemented for you automatically. /// /// # Examples /// Make a type serialize into a `String` pod. /// ```rust /// use std::io; /// use libspa::pod::serialize::{GenError, PodSerialize, PodSerializer, SerializeSuccess}; /// /// struct StringNewtype(String); /// /// impl PodSerialize for StringNewtype { /// fn serialize( /// &self, /// serializer: PodSerializer, /// ) -> Result, GenError> { /// serializer.serialize_string(self.0.as_str()) /// } /// } /// ``` /// `Bytes` pods are created in the same way, but with the `serialize_bytes` method. /// /// Make a type serialize into a `Array` pod with `Int` pod elements: /// ```rust /// use std::io; /// use libspa::pod::serialize::{GenError, PodSerialize, PodSerializer, SerializeSuccess}; /// /// struct Numbers(Vec); /// /// impl PodSerialize for Numbers { /// fn serialize( /// &self, /// serializer: PodSerializer, /// ) -> Result, GenError> { /// let mut array_serializer = serializer.serialize_array(self.0.len() as u32)?; /// for element in self.0.iter() { /// array_serializer.serialize_element(element)?; /// } /// array_serializer.end() /// } /// } /// ``` /// /// Make a struct serialize into a `Struct` pod: /// ```rust /// use std::io; /// use libspa::pod::serialize::{GenError, PodSerialize, PodSerializer, SerializeSuccess}; /// /// struct Animal { /// name: String, /// feet: u8, /// can_fly: bool, /// } /// /// impl PodSerialize for Animal { /// fn serialize( /// &self, /// serializer: PodSerializer, /// ) -> Result, GenError> { /// let mut struct_serializer = serializer.serialize_struct()?; /// struct_serializer.serialize_field(self.name.as_str())?; /// // No pod exists for u8, we need to use an `Int` type pod by casting to `i32`. /// struct_serializer.serialize_field(&(self.feet as i32))?; /// struct_serializer.serialize_field(&self.can_fly)?; /// struct_serializer.end() /// } /// } /// ``` pub trait PodSerialize { /// Serialize the type by using the provided [`PodSerializer`] fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError>; } // Serialize into a `String` pod. impl PodSerialize for str { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { serializer.serialize_string(self) } } // Serialize into a `Bytes` pod. impl PodSerialize for [u8] { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { serializer.serialize_bytes(self) } } // Serialize into the kind of pod represented by the value. impl PodSerialize for Value { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { /// Helper function that fully serializes an array containing FixedSizedPod elements. fn serialize_array( array: &[E], serializer: PodSerializer, ) -> Result, GenError> { let mut arr_serializer = serializer.serialize_array(array.len() as u32)?; for e in array.iter() { arr_serializer.serialize_element(e)?; } arr_serializer.end() } match self { Value::None => serializer.serialized_fixed_sized_pod(&()), Value::Bool(b) => serializer.serialized_fixed_sized_pod(b), Value::Id(id) => serializer.serialized_fixed_sized_pod(id), Value::Int(i) => serializer.serialized_fixed_sized_pod(i), Value::Long(l) => serializer.serialized_fixed_sized_pod(l), Value::Float(f) => serializer.serialized_fixed_sized_pod(f), Value::Double(d) => serializer.serialized_fixed_sized_pod(d), Value::String(s) => serializer.serialize_string(s.as_str()), Value::Bytes(b) => serializer.serialize_bytes(b.as_slice()), Value::Rectangle(rect) => serializer.serialized_fixed_sized_pod(rect), Value::Fraction(frac) => serializer.serialized_fixed_sized_pod(frac), Value::Fd(fd) => serializer.serialized_fixed_sized_pod(fd), Value::ValueArray(array) => match array { ValueArray::None(arr) => serialize_array(arr, serializer), ValueArray::Bool(arr) => serialize_array(arr, serializer), ValueArray::Id(arr) => serialize_array(arr, serializer), ValueArray::Int(arr) => serialize_array(arr, serializer), ValueArray::Long(arr) => serialize_array(arr, serializer), ValueArray::Float(arr) => serialize_array(arr, serializer), ValueArray::Double(arr) => serialize_array(arr, serializer), ValueArray::Rectangle(arr) => serialize_array(arr, serializer), ValueArray::Fraction(arr) => serialize_array(arr, serializer), ValueArray::Fd(arr) => serialize_array(arr, serializer), }, Value::Struct(array) => { let mut struct_serializer = serializer.serialize_struct()?; for elem in array.iter() { struct_serializer.serialize_field(elem)?; } struct_serializer.end() } Value::Object(object) => { let mut object_serializer = serializer.serialize_object(object.type_, object.id)?; for prop in object.properties.iter() { object_serializer.serialize_property(prop.key, &prop.value, prop.flags)?; } object_serializer.end() } Value::Choice(choice) => match choice { ChoiceValue::Bool(choice) => serializer.serialize_choice(choice), ChoiceValue::Int(choice) => serializer.serialize_choice(choice), ChoiceValue::Long(choice) => serializer.serialize_choice(choice), ChoiceValue::Float(choice) => serializer.serialize_choice(choice), ChoiceValue::Double(choice) => serializer.serialize_choice(choice), ChoiceValue::Id(choice) => serializer.serialize_choice(choice), ChoiceValue::Rectangle(choice) => serializer.serialize_choice(choice), ChoiceValue::Fraction(choice) => serializer.serialize_choice(choice), ChoiceValue::Fd(choice) => serializer.serialize_choice(choice), }, Value::Pointer(type_, pointer) => serializer.serialize_pointer(*type_, *pointer), } } } impl PodSerialize for [P] { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { let mut arr_serializer = serializer.serialize_array( self.len() .try_into() .expect("Array length does not fit in a u32"), )?; for element in self.iter() { arr_serializer.serialize_element(element)?; } arr_serializer.end() } } impl PodSerialize for (u32, *const T) { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { serializer.serialize_pointer(self.0, self.1) } } /// This struct is returned by [`PodSerialize`] implementors on serialization success. /// /// Because this can only be constructed by the [`PodSerializer`], [`PodSerialize`] implementors are forced /// to finish serialization of their pod instead of stopping after serializing only part of a pod. pub struct SerializeSuccess { /// Because [`PodSerialize`] implementors get ownership of the serializer, /// it is returned back to the caller in this struct. serializer: PodSerializer, /// The number of bytes written by the serialization operation that returns this struct. len: u64, } /// This struct is responsible for serializing a [`PodSerialize`] implementor into the raw POD format. pub struct PodSerializer { /// The writer is saved in an option, but can be expected to always be a `Some` when a `serialize_*` function /// is called. /// The function should then `take()` the writer, use it to serialize the item, /// and must then put the writer back inside. /// The [`Self::gen`] function can be used to do this. out: Option, } impl PodSerializer { /// Serialize the provided POD into the raw pod format, writing it into `out`. /// /// When serializing into an in-memory-buffer such as [`Vec`], you might have to wrap it into a [`std::io::Cursor`] /// to provide the [`Seek`] trait. /// /// The function returns back the `out` writer and the number of bytes written, /// or a generation error if serialization failed. pub fn serialize

(out: O, pod: &P) -> Result<(O, u64), GenError> where P: PodSerialize + ?Sized, { let serializer = Self { out: Some(out) }; pod.serialize(serializer).map(|success| { ( success .serializer .out .expect("Serializer does not contain a writer"), success.len, ) }) } /// Helper serialization method for serializing the Pod header. /// /// # Parameters /// - size: The size of the pod body /// - type: The type of the pod, e.g. `spa_sys::SPA_TYPE_Int` for a `spa_pod_int` fn header(size: usize, ty: u32) -> impl SerializeFn { pair(ne_u32(size as u32), ne_u32(ty)) } /// Helper serialization function for adding padding to a pod.. /// /// Pad output with 0x00 bytes so that it is aligned to 8 bytes. fn padding(len: usize) -> impl SerializeFn { let zeroes = std::iter::repeat(0u8); all(zeroes.take(len).map(ne_u8)) } /// Use the provided serialization function to write into the writer contained in self. fn gen(&mut self, f: impl SerializeFn) -> Result { gen( f, self.out .take() .expect("PodSerializer does not contain a writer"), ) .map(|(writer, len)| { self.out = Some(writer); len }) } /// Write out a full pod (header, body, padding), with the body computed using the provided serialization function. fn write_pod( mut self, size: usize, type_: u32, f: impl SerializeFn, ) -> Result, GenError> { let padding = if size % 8 == 0 { 0 } else { 8 - (size % 8) }; let written = self.gen(tuple(( Self::header(size, type_), f, Self::padding(padding), )))?; Ok(SerializeSuccess { serializer: self, len: written, }) } // Implementation note: // Each `serialize_*` function must serialize the _full_ pod, meaning header, body, and padding. // The `write_pod` method may be used to help with this. /// Serialize any fixed size pod. /// /// The type of the serialized pod will depend on the [`FixedSizedPod::CanonicalType`] that the passed type has. pub fn serialized_fixed_sized_pod

(self, pod: &P) -> Result, GenError> where P: FixedSizedPod + ?Sized, { self.write_pod( P::CanonicalType::SIZE as usize, P::CanonicalType::TYPE, |out| pod.as_canonical_type().serialize_body(out), ) } /// Serialize a `String` pod. pub fn serialize_string(self, string: &str) -> Result, GenError> { let cstr = CString::new(string) .expect("Pod::String contains string with '\0' byte") .into_bytes_with_nul(); self.write_pod(cstr.len(), spa_sys::SPA_TYPE_String, slice(cstr)) } /// Serialize a `Bytes` pod. pub fn serialize_bytes(self, bytes: &[u8]) -> Result, GenError> { self.write_pod(bytes.len(), spa_sys::SPA_TYPE_Bytes, slice(bytes)) } /// Begin serializing an `Array` pod with exactly `length` elements. pub fn serialize_array( mut self, length: u32, ) -> Result, GenError> { self.gen(pair( Self::header( (8 + length * P::CanonicalType::SIZE) as usize, spa_sys::SPA_TYPE_Array, ), Self::header(P::CanonicalType::SIZE as usize, P::CanonicalType::TYPE), ))?; Ok(ArrayPodSerializer { serializer: self, length, written: 0, _phantom: PhantomData, }) } /// Begin serializing a `Struct` pod. pub fn serialize_struct(mut self) -> Result, GenError> { let header_position = self .out .as_mut() .expect("PodSerializer does not contain a writer") .stream_position() .expect("Could not get current position in writer"); // Write a size of 0 for now, this will be updated when calling `StructPodSerializer.end()`. self.gen(Self::header(0, spa_sys::SPA_TYPE_Struct))?; Ok(StructPodSerializer { serializer: Some(self), header_position, written: 0, }) } /// Begin serializing an `Object` pod. pub fn serialize_object( mut self, object_type: u32, object_id: u32, ) -> Result, GenError> { let header_position = self .out .as_mut() .expect("PodSerializer does not contain a writer") .stream_position() .expect("Could not get current position in writer"); // Write a size of 0 for now, this will be updated when calling `ObjectPodSerializer.end()`. self.gen(Self::header(0, spa_sys::SPA_TYPE_Object))?; self.gen(pair(ne_u32(object_type), ne_u32(object_id)))?; Ok(ObjectPodSerializer { serializer: Some(self), header_position, written: 0, }) } /// Serialize a `Choice` pod. pub fn serialize_choice( mut self, choice: &Choice, ) -> Result, GenError> { let flags = choice.0; let (choice_type, values) = match &choice.1 { ChoiceEnum::None(value) => (spa_sys::SPA_CHOICE_None, vec![value]), ChoiceEnum::Range { default, min, max } => { (spa_sys::SPA_CHOICE_Range, vec![default, min, max]) } ChoiceEnum::Step { default, min, max, step, } => (spa_sys::SPA_CHOICE_Step, vec![default, min, max, step]), ChoiceEnum::Enum { default, alternatives, } => { let mut values = vec![default]; values.extend(alternatives); (spa_sys::SPA_CHOICE_Enum, values) } ChoiceEnum::Flags { default, flags } => { let mut values = vec![default]; values.extend(flags); (spa_sys::SPA_CHOICE_Flags, values) } }; let len: usize = 2 * 8 + values.len() * (T::SIZE as usize); self.gen(Self::header(len, spa_sys::SPA_TYPE_Choice))?; self.gen(pair(ne_u32(choice_type), ne_u32(flags.bits())))?; self.gen(pair(ne_u32(T::SIZE), ne_u32(T::TYPE)))?; for v in values { self.gen(|out| v.serialize_body(out))?; } let padding = if len % 8 == 0 { 0 } else { 8 - (len % 8) }; // Add padding to the pod. let pad_bytes = self.gen(PodSerializer::padding(padding))?; Ok(SerializeSuccess { serializer: self, // pod header + choice body + padding len: 8 + len as u64 + pad_bytes, }) } /// Serialize a pointer pod. pub fn serialize_pointer( mut self, type_: u32, ptr: *const T, ) -> Result, GenError> { let ptr_size = std::mem::size_of::(); let len = 8 + ptr_size; let mut written = self.gen(Self::header(len, spa_sys::SPA_TYPE_Pointer))?; written += self.gen(pair(ne_u32(type_), ne_u32(0)))?; written += match ptr_size { 4 => self.gen(ne_u32(ptr as u32))?, 8 => self.gen(ne_u64(ptr as u64))?, _ => panic!("unsupported pointer size {}", ptr_size), }; Ok(SerializeSuccess { serializer: self, len: written, }) } } /// This struct handles serializing arrays. /// /// It can be obtained by calling [`PodSerializer::serialize_array`]. /// /// The exact number of elements that was specified during that call must be written into it /// using its [`serialize_element`](`Self::serialize_element`) function, /// followed by calling its [`end`](`Self::end`) function to finish serialization of the array. pub struct ArrayPodSerializer { serializer: PodSerializer, /// The total length the array should have length: u32, /// The number of elements that have been written already written: u32, /// The struct has the type parameter P to ensure all serialized elements are the same type, /// but doesn't actually own any P, so we need the `PhantomData

` instead. _phantom: PhantomData

, } impl ArrayPodSerializer { /// Serialize a single element. /// /// Returns the amount of bytes written for this field. pub fn serialize_element(&mut self, elem: &P) -> Result { if !self.written < self.length { panic!("More elements than specified were serialized into the array POD"); } let result = self .serializer .gen(|out| elem.as_canonical_type().serialize_body(out)); self.written += 1; result } /// Finish serializing the array. pub fn end(mut self) -> Result, GenError> { assert_eq!( self.length, self.written, "Array POD was not serialized with the specified amount of elements" ); let bytes_written = self.written * P::CanonicalType::SIZE; let padding = if bytes_written % 8 == 0 { 0 } else { 8 - (bytes_written as usize % 8) }; // Add padding to the pod. let pad_bytes = self.serializer.gen(PodSerializer::padding(padding))?; Ok(SerializeSuccess { serializer: self.serializer, // Number of bytes written for the pod is two headers + body length + padding len: 16 + u64::from(self.written * P::CanonicalType::SIZE) + pad_bytes, }) } } /// This struct handles serializing structs. /// /// It can be obtained by calling [`PodSerializer::serialize_struct`]. /// /// Its [`serialize_field`](`Self::serialize_field`) method can be repeatedly called to serialize one field each. /// To finalize the struct, its [`end`](`Self::end`) method must be called. pub struct StructPodSerializer { /// The serializer is saved in an option, but can be expected to always be a `Some` /// when `serialize_field()` or `end()` is called. /// /// `serialize_field()` `take()`s the serializer, uses it to serialize the field, /// and then puts the serializer back inside. serializer: Option>, /// The position to seek to when modifying header. header_position: u64, written: usize, } impl StructPodSerializer { /// Serialize a single field of the struct. /// /// Returns the amount of bytes written for this field. pub fn serialize_field

(&mut self, field: &P) -> Result where P: PodSerialize + ?Sized, { let success = field.serialize( self.serializer .take() .expect("StructSerializer does not contain a serializer"), )?; self.written += success.len as usize; self.serializer = Some(success.serializer); Ok(success.len) } /// Finish serialization of the pod. pub fn end(self) -> Result, GenError> { let mut serializer = self .serializer .expect("StructSerializer does not contain a serializer"); // Seek to header position, write header with updates size, seek back. serializer .out .as_mut() .expect("Serializer does not contain a writer") .seek(SeekFrom::Start(self.header_position)) .expect("Failed to seek to header position"); serializer.gen(PodSerializer::header( self.written, spa_sys::SPA_TYPE_Struct, ))?; serializer .out .as_mut() .expect("Serializer does not contain a writer") .seek(SeekFrom::End(0)) .expect("Failed to seek to end"); // No padding needed: Last field will already end aligned. // Return full length of written pod. Ok(SerializeSuccess { serializer, len: self.written as u64 + 8, }) } } /// This struct handles serializing objects. /// /// It can be obtained by calling [`PodSerializer::serialize_object`]. /// /// Its [`serialize_property`](`Self::serialize_property`) method can be repeatedly called to serialize each property. /// To finalize the object, its [`end`](`Self::end`) method must be called. pub struct ObjectPodSerializer { /// The serializer is saved in an option, but can be expected to always be a `Some` /// when `serialize_field()` or `end()` is called. /// /// `serialize_property()` `take()`s the serializer, uses it to serialize the property, /// and then puts the serializer back inside. serializer: Option>, /// The position to seek to when modifying header. header_position: u64, written: usize, } impl ObjectPodSerializer { /// Serialize a single property of the object. /// /// Returns the amount of bytes written for this field. pub fn serialize_property

( &mut self, key: u32, value: &P, flags: PropertyFlags, ) -> Result where P: PodSerialize + ?Sized, { let mut serializer = self .serializer .take() .expect("ObjectPodSerializer does not contain a serializer"); serializer.gen(pair(ne_u32(key), ne_u32(flags.bits())))?; let mut success = value.serialize(serializer)?; success.len += 8; // add the key and flags len self.written += success.len as usize; self.serializer = Some(success.serializer); Ok(success.len) } /// Finish serialization of the pod. pub fn end(self) -> Result, GenError> { let mut serializer = self .serializer .expect("ObjectSerializer does not contain a serializer"); // Seek to header position, write header with updates size, seek back. serializer .out .as_mut() .expect("Serializer does not contain a writer") .seek(SeekFrom::Start(self.header_position)) .expect("Failed to seek to header position"); // size of properties + object type + object id let written = self.written + 8; serializer.gen(PodSerializer::header(written, spa_sys::SPA_TYPE_Object))?; serializer .out .as_mut() .expect("Serializer does not contain a writer") .seek(SeekFrom::End(0)) .expect("Failed to seek to end"); // No padding needed: Last field will already end aligned. // Return full length of written pod. Ok(SerializeSuccess { serializer, // pod header + object body len: 8 + written as u64, }) } } impl PodSerialize for Choice { fn serialize( &self, serializer: PodSerializer, ) -> Result, GenError> { serializer.serialize_choice(self) } } libspa-0.8.0/src/support/mod.rs000064400000000000000000000001341046102023000145310ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT pub mod system; libspa-0.8.0/src/support/system.rs000064400000000000000000000007301046102023000153000ustar 00000000000000use bitflags::bitflags; bitflags! { /// Flags used to specify different IO events. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct IoFlags: u32 { /// There is data to read const IN = spa_sys::SPA_IO_IN; /// Writing is possible const OUT = spa_sys::SPA_IO_OUT; /// An error has occurred const ERR = spa_sys::SPA_IO_ERR; /// IO channel has hung up const HUP = spa_sys::SPA_IO_HUP; } } libspa-0.8.0/src/utils/dict.rs000064400000000000000000000442671046102023000143400ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! Dictionary types and traits. use bitflags::bitflags; // re-exported as used in the static_dict! macro implementation pub use spa_sys::spa_dict_item; use std::{convert::TryInto, ffi::CStr, fmt, marker::PhantomData, ptr}; #[repr(transparent)] pub struct DictRef(spa_sys::spa_dict); impl DictRef { /// Returns a reference to the raw [`spa_sys::spa_dict`] this struct represents. pub fn as_raw(&self) -> &spa_sys::spa_dict { &self.0 } /// Returns the pointer to the raw [`spa_sys::spa_dict`] this struct represents. /// /// # Safety /// The returned pointer must not be used after the [`DictRef`] reference this method is called on becomes invalid. pub fn as_raw_ptr(&self) -> *mut spa_sys::spa_dict { self.as_raw() as *const _ as *mut _ } /// An iterator over all key-value pairs as `(&CStr, &CStr)` tuples. /// /// Use [`iter`](Self::iter) to iterate over all valid utf-8 pairs as (&str, &str) tuples instead. pub fn iter_cstr(&self) -> CIter { let items = if self.0.items.is_null() { &[] } else { unsafe { std::slice::from_raw_parts(self.0.items, self.len()) } }; CIter { items, _phantom: PhantomData, } } /// An iterator over all key-value pairs that are valid utf-8. /// The iterator element type is `(&str, &str)`. pub fn iter(&self) -> Iter { Iter { inner: self.iter_cstr(), } } /// An iterator over all keys that are valid utf-8. /// The iterator element type is &str. pub fn keys(&self) -> Keys { Keys { inner: self.iter_cstr(), } } /// An iterator over all values that are valid utf-8. /// The iterator element type is &str. pub fn values(&self) -> Values { Values { inner: self.iter_cstr(), } } /// Returns the number of key-value-pairs in the dict. /// This is the number of all pairs, not only pairs that are valid-utf8. pub fn len(&self) -> usize { self.0.n_items.try_into().unwrap() } /// Returns `true` if the dict is empty, `false` if it is not. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the bitflags that are set for the dict. pub fn flags(&self) -> Flags { Flags::from_bits_retain(self.0.flags) } /// Get the value associated with the provided key. /// /// If the dict does not contain the key or the value is non-utf8, `None` is returned. /// Use [`iter_cstr`] if you need a non-utf8 key or value. /// /// [`iter_cstr`]: #method.iter_cstr pub fn get(&self, key: &str) -> Option<&str> { self.iter().find(|(k, _)| *k == key).map(|(_, v)| v) } /// Get the value associated with the provided key and convert it to a given type. /// /// If the dict does not contain the key or the value is non-utf8, `None` is returned. /// /// If the value associated with the key cannot be parsed to the requested type, /// `Some(Err(ParseValueError))` is returned. /// /// See [`ParsableValue#foreign-impls`] for all the types which can be produced by this method. /// /// # Examples /// ``` /// use libspa::prelude::*; /// use libspa::{utils::dict::StaticDict, static_dict}; /// /// static DICT: StaticDict = static_dict! { /// "true" => "true", /// "ten" => "10", /// "pi" => "3.14159265359", /// "pointer" => "pointer:0xdeadbeef" /// }; /// /// assert_eq!(DICT.parse("true"), Some(Ok(true))); /// assert_eq!(DICT.parse("ten"), Some(Ok(10))); /// assert_eq!(DICT.parse("ten"), Some(Ok(10.0))); /// assert_eq!(DICT.parse("pi"), Some(Ok(3.14159265359))); /// /// let ptr = DICT.parse::<*const i32>("pointer").unwrap().unwrap(); /// assert!(!ptr.is_null()); /// ``` pub fn parse(&self, key: &str) -> Option> { self.iter() .find(|(k, _)| *k == key) .map(|(_, v)| match T::parse_value(v) { Some(v) => Ok(v), None => Err(ParseValueError { value: v.to_string(), type_name: std::any::type_name::(), }), }) } } impl AsRef for DictRef { fn as_ref(&self) -> &Self { self } } impl std::fmt::Debug for DictRef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { struct Entries<'a>(CIter<'a>); impl<'a> fmt::Debug for Entries<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map() .entries(self.0.clone().map(|(k, v)| (k, v))) .finish() } } f.debug_struct("DictRef") .field("flags", &self.flags()) .field("entries", &Entries(self.iter_cstr())) .finish() } } /// An error raised by [`DictRef::parse`] if the value cannot be converted to the requested type. #[derive(Debug, Eq, PartialEq)] pub struct ParseValueError { value: String, type_name: &'static str, } impl std::error::Error for ParseValueError {} impl fmt::Display for ParseValueError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "'{}' cannot be parsed to {}", self.value, self.type_name) } } /// Trait implemented on types which can be returned by [`DictRef::parse`]. pub trait ParsableValue: Copy { /// Try parsing `value` to convert it to the requested type. fn parse_value(value: &str) -> Option; } impl ParsableValue for bool { fn parse_value(value: &str) -> Option { // Same logic as pw_properties_parse_bool() if value == "true" { Some(true) } else { match value.parse::() { Ok(1) => Some(true), _ => Some(false), } } } } macro_rules! impl_parsable_value_numeric { ($type_:ty) => { impl ParsableValue for $type_ { fn parse_value(value: &str) -> Option { value.parse().ok() } } }; } impl_parsable_value_numeric!(i32); impl_parsable_value_numeric!(i64); impl_parsable_value_numeric!(u64); impl_parsable_value_numeric!(f32); impl_parsable_value_numeric!(f64); // not implemented in properties.h but good to have impl_parsable_value_numeric!(i8); impl_parsable_value_numeric!(u8); impl_parsable_value_numeric!(i16); impl_parsable_value_numeric!(u16); impl_parsable_value_numeric!(u32); impl_parsable_value_numeric!(i128); impl_parsable_value_numeric!(u128); impl_parsable_value_numeric!(isize); impl_parsable_value_numeric!(usize); const POINTER_PREFIX: &str = "pointer:0x"; impl ParsableValue for *const T { fn parse_value(value: &str) -> Option { match value .strip_prefix(POINTER_PREFIX) .map(|addr| usize::from_str_radix(addr, 16)) { Some(Ok(addr)) => Some(addr as *const T), _ => None, } } } bitflags! { /// Dictionary flags #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Flags: u32 { // These flags are redefinitions from // https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/spa/include/spa/utils/dict.h /// Dictionary has been sorted. const SORTED = spa_sys::SPA_DICT_FLAG_SORTED; } } /// Iterator on a dictionary's keys and values exposed as [`CStr`]. #[derive(Clone)] pub struct CIter<'a> { items: &'a [spa_sys::spa_dict_item], _phantom: PhantomData<&'a str>, } impl<'a> Iterator for CIter<'a> { type Item = (&'a CStr, &'a CStr); fn next(&mut self) -> Option { self.items.split_first().map(|(item, rest)| { self.items = rest; let k = unsafe { CStr::from_ptr(item.key) }; let v = unsafe { CStr::from_ptr(item.value) }; (k, v) }) } fn size_hint(&self) -> (usize, Option) { let bound = self.items.len(); // We know the exact value, so lower bound and upper bound are the same. (bound, Some(bound)) } } /// Iterator on a dictionary's keys and values exposed as [`str`]. pub struct Iter<'a> { inner: CIter<'a>, } impl<'a> Iterator for Iter<'a> { type Item = (&'a str, &'a str); fn next(&mut self) -> Option { self.inner .find_map(|(k, v)| k.to_str().ok().zip(v.to_str().ok())) } fn size_hint(&self) -> (usize, Option) { // Lower bound is 0, as all keys left might not be valid UTF-8. (0, self.inner.size_hint().1) } } /// Iterator on a dictionary's keys. pub struct Keys<'a> { inner: CIter<'a>, } impl<'a> Iterator for Keys<'a> { type Item = &'a str; fn next(&mut self) -> Option { self.inner.find_map(|(k, _)| k.to_str().ok()) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// Iterator on a dictionary's values. pub struct Values<'a> { inner: CIter<'a>, } impl<'a> Iterator for Values<'a> { type Item = &'a str; fn next(&mut self) -> Option { self.inner.find_map(|(_, v)| v.to_str().ok()) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A collection of static key/value pairs. /// /// # Examples /// Create a `StaticDict` and access the stored values by key: /// ```rust /// use libspa::{utils::dict::StaticDict, static_dict}; /// /// static DICT: StaticDict = static_dict!{ /// "Key" => "Value", /// "OtherKey" => "OtherValue" /// }; /// /// assert_eq!(Some("Value"), DICT.get("Key")); /// assert_eq!(Some("OtherValue"), DICT.get("OtherKey")); /// ``` pub struct StaticDict { ptr: ptr::NonNull, } impl StaticDict { /// Create a [`StaticDict`] from an existing raw `spa_dict` pointer. /// /// # Safety /// - The provided pointer must point to a valid, well-aligned `spa_dict` struct. /// - The struct and its content need to stay alive during the whole lifetime of the `StaticDict`. /// - The keys and values stored in this dict have to be static strings. pub const unsafe fn from_ptr(ptr: ptr::NonNull) -> Self { Self { ptr } } } /// A macro for creating a new [`StaticDict`] with predefined key-value pairs. /// /// The macro accepts a list of static `Key => Value` pairs, separated by commas. /// /// # Examples: /// Create a `StaticDict`. /// ```rust /// use libspa::{utils::dict::StaticDict, static_dict}; /// /// static PROPS: StaticDict = static_dict!{ /// "Key1" => "Value1", /// "Key2" => "Value2", /// }; /// ``` #[macro_export] macro_rules! static_dict { {$($k:expr => $v:expr),+ $(,)?} => {{ use $crate::utils::dict::{spa_dict_item, StaticDict, Flags}; use std::ptr; static mut ITEMS: &[spa_dict_item] = &[ $( spa_dict_item { key: concat!($k, "\0").as_ptr() as *const std::os::raw::c_char, value: concat!($v, "\0").as_ptr() as *const std::os::raw::c_char }, )+ ]; static mut RAW: spa_sys::spa_dict = unsafe { spa_sys::spa_dict { flags: Flags::empty().bits(), n_items: ITEMS.len() as u32, items: ITEMS.as_ptr(), } }; unsafe { let ptr = &RAW as *const _ as *mut _; StaticDict::from_ptr(ptr::NonNull::new_unchecked(ptr)) } }}; } impl std::ops::Deref for StaticDict { type Target = DictRef; fn deref(&self) -> &Self::Target { unsafe { self.ptr.cast::().as_ref() } } } impl fmt::Debug for StaticDict { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let dict: &DictRef = self.as_ref(); // FIXME: Debug-print dict keys and values directly f.debug_tuple("StaticDict").field(dict).finish() } } unsafe impl Send for StaticDict {} unsafe impl Sync for StaticDict {} #[cfg(test)] mod tests { use super::{DictRef, Flags, StaticDict}; use spa_sys::spa_dict; use std::{ffi::CString, ptr}; #[test] fn test_empty_dict() { let raw = spa_dict { flags: Flags::empty().bits(), n_items: 0, items: ptr::null(), }; let dict = DictRef(raw); let iter = dict.iter_cstr(); assert_eq!(0, dict.len()); iter.for_each(|_| panic!("Iterated over non-existing item")); } #[test] fn test_iter_cstr() { let dict = static_dict! { "K0" => "V0", "K1" => "V1" }; let mut iter = dict.iter_cstr(); assert_eq!( ( CString::new("K0").unwrap().as_c_str(), CString::new("V0").unwrap().as_c_str() ), iter.next().unwrap() ); assert_eq!( ( CString::new("K1").unwrap().as_c_str(), CString::new("V1").unwrap().as_c_str() ), iter.next().unwrap() ); assert_eq!(None, iter.next()); } #[test] fn test_iterators() { let dict = static_dict! { "K0" => "V0", "K1" => "V1" }; let mut iter = dict.iter(); assert_eq!(("K0", "V0"), iter.next().unwrap()); assert_eq!(("K1", "V1"), iter.next().unwrap()); assert_eq!(None, iter.next()); let mut key_iter = dict.keys(); assert_eq!("K0", key_iter.next().unwrap()); assert_eq!("K1", key_iter.next().unwrap()); assert_eq!(None, key_iter.next()); let mut val_iter = dict.values(); assert_eq!("V0", val_iter.next().unwrap()); assert_eq!("V1", val_iter.next().unwrap()); assert_eq!(None, val_iter.next()); } #[test] fn test_get() { let dict = static_dict! { "K0" => "V0" }; assert_eq!(Some("V0"), dict.get("K0")); } #[test] fn test_debug() { let dict = static_dict! { "K0" => "V0" }; assert_eq!( r#"StaticDict(DictRef { flags: Flags(0x0), entries: {"K0": "V0"} })"#, &format!("{:?}", dict) ); let raw = spa_dict { flags: Flags::SORTED.bits(), n_items: 0, items: ptr::null(), }; let dict = DictRef(raw); assert_eq!( r#"DictRef { flags: Flags(SORTED), entries: {} }"#, &format!("{:?}", dict) ); } #[test] fn static_dict() { static DICT: StaticDict = static_dict! { "K0" => "V0", "K1" => "V1" }; assert_eq!(DICT.len(), 2); assert_eq!(DICT.get("K0"), Some("V0")); assert_eq!(DICT.get("K1"), Some("V1")); } #[test] fn parse() { use super::ParseValueError; static DICT: StaticDict = static_dict! { "true" => "true", "false" => "false", "1" => "1", "10" => "10", "-10" => "-10", "i64-max" => "9223372036854775807", "1.5" => "1.5", "-1.5" => "-1.5", "pointer" => "pointer:0xdeadbeef", "badger" => "badger" }; macro_rules! parse_error { ($key:literal, $type_:ty) => { assert!(matches!( DICT.parse::<$type_>($key), Some(Err(ParseValueError { .. })) )); }; } assert_eq!(DICT.parse::("missing"), None); assert_eq!(DICT.parse("true"), Some(Ok(true))); assert_eq!(DICT.parse("1"), Some(Ok(true))); assert_eq!(DICT.parse("false"), Some(Ok(false))); assert_eq!(DICT.parse("10"), Some(Ok(false))); assert_eq!(DICT.parse("badger"), Some(Ok(false))); /* integer types */ assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10))); parse_error!("badger", i32); parse_error!("i64-max", i32); assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10))); assert_eq!(DICT.parse::("i64-max"), Some(Ok(i64::MAX))); parse_error!("badger", i64); assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("i64-max"), Some(Ok(i64::MAX as u64))); parse_error!("-10", u64); parse_error!("badger", u64); assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10))); assert_eq!(DICT.parse::("1"), Some(Ok(1))); parse_error!("-10", u8); assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10))); assert_eq!(DICT.parse::("1"), Some(Ok(1))); parse_error!("-10", u16); assert_eq!(DICT.parse::("1"), Some(Ok(1))); parse_error!("-10", u32); assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10))); assert_eq!(DICT.parse::("1"), Some(Ok(1))); parse_error!("-10", u128); assert_eq!(DICT.parse::("1"), Some(Ok(1))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10))); assert_eq!(DICT.parse::("1"), Some(Ok(1))); parse_error!("-10", usize); /* floating-point types */ assert_eq!(DICT.parse::("1"), Some(Ok(1.0))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10.0))); assert_eq!(DICT.parse::("1.5"), Some(Ok(1.5))); assert_eq!(DICT.parse::("-1.5"), Some(Ok(-1.5))); parse_error!("badger", f32); assert_eq!(DICT.parse::("1"), Some(Ok(1.0))); assert_eq!(DICT.parse::("-10"), Some(Ok(-10.0))); assert_eq!(DICT.parse::("1.5"), Some(Ok(1.5))); assert_eq!(DICT.parse::("-1.5"), Some(Ok(-1.5))); parse_error!("badger", f64); /* pointer */ let ptr = DICT.parse::<*const i32>("pointer").unwrap().unwrap(); assert!(!ptr.is_null()); parse_error!("badger", *const i32); } } libspa-0.8.0/src/utils/direction.rs000064400000000000000000000036201046102023000153610ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! SPA direction. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Direction(spa_sys::spa_direction); #[allow(non_upper_case_globals)] impl Direction { pub const Input: Self = Self(spa_sys::SPA_DIRECTION_INPUT); pub const Output: Self = Self(spa_sys::SPA_DIRECTION_OUTPUT); pub fn from_raw(raw: spa_sys::spa_direction) -> Self { Self(raw) } pub fn as_raw(&self) -> spa_sys::spa_direction { self.0 } /// Return a new [`Direction`] in the opposite direction, turning Input to Output, and Output to Input. /// /// An unknown/invalid direction is unchanged. pub fn reverse(&self) -> Self { match *self { Self::Input => Self::Output, Self::Output => Self::Input, _ => *self, } } } impl std::fmt::Debug for Direction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = format!( "Direction::{}", match *self { Self::Input => "Input", Self::Output => "Output", _ => "Unknown", } ); f.write_str(&name) } } #[cfg(test)] mod tests { use super::*; #[test] fn as_raw() { assert_eq!(Direction::Input.as_raw(), spa_sys::SPA_DIRECTION_INPUT); assert_eq!(Direction::Output.as_raw(), spa_sys::SPA_DIRECTION_OUTPUT); } #[test] fn from_raw() { assert_eq!( Direction::Input, Direction::from_raw(spa_sys::SPA_DIRECTION_INPUT) ); assert_eq!( Direction::Output, Direction::from_raw(spa_sys::SPA_DIRECTION_OUTPUT) ); } #[test] fn reverse() { assert_eq!(Direction::Output.reverse(), Direction::Input); assert_eq!(Direction::Input.reverse(), Direction::Output); } } libspa-0.8.0/src/utils/hook.rs000064400000000000000000000033341046102023000143430ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! SPA hook use crate::utils::list; /// Remove a hook pub fn remove(mut hook: spa_sys::spa_hook) { list::remove(&hook.link); if let Some(removed) = hook.removed { unsafe { removed(&mut hook as *mut _); } } } /// Call a method on a spa_interface. /// /// This needs to be called from within an `unsafe` block. /// /// The macro always takes at least three arguments: /// 1. A pointer to a C struct that can be casted to a spa_interface. /// 2. The type of the interfaces methods struct. /// 3. The name of the method that should be called. /// /// All additional arguments are added as arguments to the call in the order they are provided. /// /// The macro returns whatever the called method returns, for example an `i32`, or `()` if the method returns nothing. /// /// # Examples /// Here we call the sync method on a `pipewire_sys::pw_core` object. /// ``` /// use pipewire_sys as pw_sys; /// use libspa as spa; /// /// struct Core { /// ptr: *mut pw_sys::pw_core /// } /// /// impl Core { /// fn sync(&self, seq: i32) -> i32 { /// unsafe { /// spa::spa_interface_call_method!( /// &self.ptr, pw_sys::pw_core_methods, sync, pipewire::core::PW_ID_CORE, seq /// ) /// } /// } /// } /// ``` #[macro_export] macro_rules! spa_interface_call_method { ($interface_ptr:expr, $methods_struct:ty, $method:ident, $( $arg:expr ),*) => {{ let iface: *mut spa_sys::spa_interface = $interface_ptr.cast(); let funcs: *const $methods_struct = (*iface).cb.funcs.cast(); let f = (*funcs).$method.unwrap(); f((*iface).cb.data, $($arg),*) }}; } libspa-0.8.0/src/utils/list.rs000064400000000000000000000004131046102023000143510ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! SPA list /// Remove an element from its list pub fn remove(elem: &spa_sys::spa_list) { unsafe { (*elem.prev).next = elem.next; (*elem.next).prev = elem.prev; } } libspa-0.8.0/src/utils/mod.rs000064400000000000000000000170301046102023000141600ustar 00000000000000//! Miscellaneous and utility items. pub mod dict; mod direction; pub use direction::*; pub mod hook; pub mod list; pub mod result; use bitflags::bitflags; use convert_case::{Case, Casing}; use std::{ffi::CStr, fmt::Debug, os::raw::c_uint}; pub use spa_sys::spa_fraction as Fraction; pub use spa_sys::spa_rectangle as Rectangle; use crate::pod::CanonicalFixedSizedPod; /// An enumerated value in a pod #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Id(pub u32); /// A file descriptor in a pod #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(transparent)] pub struct Fd(pub i64); #[derive(Debug, Eq, PartialEq, Clone)] /// the flags and choice of a choice pod. pub struct Choice(pub ChoiceFlags, pub ChoiceEnum); bitflags! { /// [`Choice`] flags #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct ChoiceFlags: u32 { // no flags defined yet but we need at least one to keep bitflags! happy #[doc(hidden)] const _FAKE = 1; } } #[derive(Debug, PartialEq, Eq, Clone)] /// a choice in a pod. pub enum ChoiceEnum { /// no choice. None(T), /// range. Range { /// default value. default: T, /// minimum value. min: T, /// maximum value. max: T, }, /// range with step. Step { /// default value. default: T, /// minimum value. min: T, /// maximum value. max: T, /// step. step: T, }, /// list. Enum { /// default value. default: T, /// alternative values. alternatives: Vec, }, /// flags. Flags { /// default value. default: T, /// possible flags. flags: Vec, }, } #[derive(Copy, Clone, PartialEq, Eq)] pub struct SpaTypes(pub c_uint); #[allow(non_upper_case_globals)] impl SpaTypes { /* Basic types */ pub const None: Self = Self(spa_sys::SPA_TYPE_None); pub const Bool: Self = Self(spa_sys::SPA_TYPE_Bool); pub const Id: Self = Self(spa_sys::SPA_TYPE_Id); pub const Int: Self = Self(spa_sys::SPA_TYPE_Int); pub const Long: Self = Self(spa_sys::SPA_TYPE_Long); pub const Float: Self = Self(spa_sys::SPA_TYPE_Float); pub const Double: Self = Self(spa_sys::SPA_TYPE_Double); pub const String: Self = Self(spa_sys::SPA_TYPE_String); pub const Bytes: Self = Self(spa_sys::SPA_TYPE_Bytes); pub const Rectangle: Self = Self(spa_sys::SPA_TYPE_Rectangle); pub const Fraction: Self = Self(spa_sys::SPA_TYPE_Fraction); pub const Bitmap: Self = Self(spa_sys::SPA_TYPE_Bitmap); pub const Array: Self = Self(spa_sys::SPA_TYPE_Array); pub const Struct: Self = Self(spa_sys::SPA_TYPE_Struct); pub const Object: Self = Self(spa_sys::SPA_TYPE_Object); pub const Sequence: Self = Self(spa_sys::SPA_TYPE_Sequence); pub const Pointer: Self = Self(spa_sys::SPA_TYPE_Pointer); pub const Fd: Self = Self(spa_sys::SPA_TYPE_Fd); pub const Choice: Self = Self(spa_sys::SPA_TYPE_Choice); pub const Pod: Self = Self(spa_sys::SPA_TYPE_Pod); /* Pointers */ pub const PointerBuffer: Self = Self(spa_sys::SPA_TYPE_POINTER_Buffer); pub const PointerMeta: Self = Self(spa_sys::SPA_TYPE_POINTER_Meta); pub const PointerDict: Self = Self(spa_sys::SPA_TYPE_POINTER_Dict); /* Events */ pub const EventDevice: Self = Self(spa_sys::SPA_TYPE_EVENT_Device); pub const EventNode: Self = Self(spa_sys::SPA_TYPE_EVENT_Node); /* Commands */ pub const CommandDevice: Self = Self(spa_sys::SPA_TYPE_COMMAND_Device); pub const CommandNode: Self = Self(spa_sys::SPA_TYPE_COMMAND_Node); /* Objects */ pub const ObjectParamPropInfo: Self = Self(spa_sys::SPA_TYPE_OBJECT_PropInfo); pub const ObjectParamProps: Self = Self(spa_sys::SPA_TYPE_OBJECT_Props); pub const ObjectParamFormat: Self = Self(spa_sys::SPA_TYPE_OBJECT_Format); pub const ObjectParamBuffers: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamBuffers); pub const ObjectParamMeta: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamMeta); pub const ObjectParamIO: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamIO); pub const ObjectParamProfile: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamProfile); pub const ObjectParamPortConfig: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamPortConfig); pub const ObjectParamRoute: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamRoute); pub const ObjectProfiler: Self = Self(spa_sys::SPA_TYPE_OBJECT_Profiler); pub const ObjectParamLatency: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamLatency); pub const ObjectParamProcessLatency: Self = Self(spa_sys::SPA_TYPE_OBJECT_ParamProcessLatency); /* vendor extensions */ pub const VendorPipeWire: Self = Self(spa_sys::SPA_TYPE_VENDOR_PipeWire); pub const VendorOther: Self = Self(spa_sys::SPA_TYPE_VENDOR_Other); /// Obtain a [`SpaTypes`] from a raw `c_uint` variant. pub fn from_raw(raw: c_uint) -> Self { Self(raw) } /// Get the raw [`c_uint`](std::os::raw::c_uint) representing this `SpaTypes`. pub fn as_raw(&self) -> c_uint { self.0 } } impl Debug for SpaTypes { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { SpaTypes::VendorPipeWire => f.write_str("SpaTypes::VendorPipeWire"), SpaTypes::VendorOther => f.write_str("SpaTypes::VendorOther"), _ => { let c_str = unsafe { let c_buf = spa_sys::spa_debug_type_find_name(spa_sys::spa_types, self.as_raw()); if c_buf.is_null() { return f.write_str("Unknown"); } CStr::from_ptr(c_buf) }; let name = format!( "SpaTypes::{}", c_str .to_string_lossy() .replace("Spa:Pointer", "Pointer") .replace("Spa:Pod:Object:Event", "Event") .replace("Spa:Pod:Object:Command", "Command") .replace("Spa:Pod:Object", "Object") .replace("Spa:Pod:", "") .replace("Spa:", "") .replace(':', " ") .to_case(Case::Pascal) ); f.write_str(&name) } } } } #[cfg(test)] mod tests { use super::*; #[test] #[cfg_attr(miri, ignore)] fn debug_format() { assert_eq!("SpaTypes::None", format!("{:?}", SpaTypes::None)); assert_eq!( "SpaTypes::PointerBuffer", format!("{:?}", SpaTypes::PointerBuffer) ); assert_eq!( "SpaTypes::EventDevice", format!("{:?}", SpaTypes::EventDevice) ); assert_eq!( "SpaTypes::CommandDevice", format!("{:?}", SpaTypes::CommandDevice) ); assert_eq!( "SpaTypes::ObjectParamPropInfo", format!("{:?}", SpaTypes::ObjectParamPropInfo) ); assert_eq!( "SpaTypes::ObjectProfiler", format!("{:?}", SpaTypes::ObjectProfiler) ); assert_eq!( "SpaTypes::ObjectParamProcessLatency", format!("{:?}", SpaTypes::ObjectParamProcessLatency) ); assert_eq!( "SpaTypes::VendorPipeWire", format!("{:?}", SpaTypes::VendorPipeWire) ); assert_eq!( "SpaTypes::VendorOther", format!("{:?}", SpaTypes::VendorOther) ); } } libspa-0.8.0/src/utils/result.rs000064400000000000000000000127111046102023000147200ustar 00000000000000// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! SPA results and errors. use std::{convert::TryInto, fmt}; use nix::errno::Errno; /// A result returned by a SPA method, usually to be converted to /// a Rust result using [`SpaResult::into_result`] or [`SpaResult::into_async_result`]. #[derive(Debug, Eq, PartialEq)] pub struct SpaResult(i32); /// An asynchronous sequence number returned by a SPA component. /// /// Use [`AsyncSeq::seq`] to retrieve the actual sequence number. #[derive(PartialEq, Eq, Copy, Clone)] pub struct AsyncSeq(i32); /// A successful result from a SPA method. #[derive(Debug, Eq, PartialEq)] pub enum SpaSuccess { /// Synchronous success Sync(i32), /// Asynchronous success Async(AsyncSeq), } fn async_seq(res: i32) -> i32 { let mask: i32 = spa_sys::SPA_ASYNC_SEQ_MASK.try_into().unwrap(); res & mask } fn is_async(val: i32) -> bool { let bit: i32 = spa_sys::SPA_ASYNC_BIT.try_into().unwrap(); (val & spa_sys::SPA_ASYNC_MASK) == bit } impl AsyncSeq { /// The sequence number pub fn seq(&self) -> i32 { async_seq(self.0) } /// The raw value, this is the sequence number with the `SPA_ASYNC_BIT` bit set pub fn raw(&self) -> i32 { self.0 } /// Create a new [`AsyncSeq`] from a sequence number pub fn from_seq(seq: i32) -> Self { let bit: i32 = spa_sys::SPA_ASYNC_BIT.try_into().unwrap(); let res = bit | async_seq(seq); Self(res) } /// Create a new [`AsyncSeq`] from a raw value having the `SPA_ASYNC_BIT` bit set pub fn from_raw(val: i32) -> Self { debug_assert!(is_async(val)); Self(val) } } impl fmt::Debug for AsyncSeq { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AsyncSeq seq: {} raw: {}", &self.seq(), &self.raw()) } } impl SpaResult { /// Create a new [`SpaResult`] from an `i32` returned by C SPA method. pub fn from_c(res: i32) -> Self { Self(res) } /// Pending return for async operation identified with sequence number `seq`. pub fn new_return_async(seq: i32) -> Self { let seq = AsyncSeq::from_seq(seq); Self::from_c(seq.raw()) } fn is_async(&self) -> bool { is_async(self.0) } /// Convert a [`SpaResult`] into a [`Result`] pub fn into_result(self) -> Result { if self.0 < 0 { Err(Error::new(-self.0)) } else if self.is_async() { Ok(SpaSuccess::Async(AsyncSeq::from_raw(self.0))) } else { Ok(SpaSuccess::Sync(self.0)) } } /// Convert a [`SpaResult`] into either an [`AsyncSeq`] or an [`Error`]. /// /// # Panics /// /// This method will panic if the result is a synchronous success. pub fn into_async_result(self) -> Result { let res = self.into_result()?; match res { SpaSuccess::Async(res) => Ok(res), SpaSuccess::Sync(_) => panic!("result is synchronous success"), } } /// Convert a [`SpaResult`] into either a synchronous success or an [`Error`]. /// /// # Panics /// /// This method will panic if the result is an asynchronous success. pub fn into_sync_result(self) -> Result { let res = self.into_result()?; match res { SpaSuccess::Sync(res) => Ok(res), SpaSuccess::Async(_) => panic!("result is an asynchronous success"), } } } /// Error returned from a SPA method. #[derive(Debug, Eq, PartialEq)] pub struct Error(Errno); impl Error { fn new(e: i32) -> Self { assert!(e > 0); Self(Errno::from_i32(e)) } } impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[cfg(test)] mod tests { use super::*; #[test] #[cfg_attr(miri, ignore)] /* the errno crate is calling foreign function __xpg_strerror_r which is not supported by miri */ fn spa_result() { assert!(!SpaResult::from_c(0).is_async()); assert!(SpaResult::new_return_async(0).is_async()); assert_eq!( SpaResult::new_return_async(0).into_async_result(), Ok(AsyncSeq::from_seq(0)) ); assert_eq!(SpaResult::from_c(0).into_result(), Ok(SpaSuccess::Sync(0))); assert_eq!(SpaResult::from_c(1).into_result(), Ok(SpaSuccess::Sync(1))); assert_eq!(SpaResult::from_c(0).into_sync_result(), Ok(0)); assert_eq!( SpaResult::new_return_async(1).into_result(), Ok(SpaSuccess::Async(AsyncSeq::from_seq(1))) ); let err = SpaResult::from_c(-libc::EBUSY).into_result().unwrap_err(); assert_eq!(format!("{}", err), "EBUSY: Device or resource busy",); let res = SpaResult::from_c(-1).into_sync_result(); assert!(res.is_err()); } #[test] fn async_seq() { assert_eq!(AsyncSeq::from_seq(0).seq(), 0); assert_eq!(AsyncSeq::from_seq(1).seq(), 1); } #[should_panic] #[test] fn async_seq_panic() { // raw value does not have the SPA_ASYNC_BIT set AsyncSeq::from_raw(1); } #[should_panic] #[test] fn spa_async_result_panic() { let _ = SpaResult::from_c(0).into_async_result(); } #[should_panic] #[test] fn spa_sync_result_panic() { let _ = SpaResult::new_return_async(10).into_sync_result(); } } libspa-0.8.0/tests/pod.c000064400000000000000000000165331046102023000132030ustar 00000000000000#include #include #include #include #include int build_none(uint8_t *buffer, size_t len) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_none(&b); } int build_bool(uint8_t *buffer, size_t len, bool boolean) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_bool(&b, boolean); } int build_id(uint8_t *buffer, size_t len, uint32_t id) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_id(&b, id); } int build_int(uint8_t *buffer, size_t len, int32_t integer) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_int(&b, integer); } int build_long(uint8_t *buffer, size_t len, int64_t integer) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_long(&b, integer); } int build_float(uint8_t *buffer, size_t len, float f) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_float(&b, f); } int build_double(uint8_t *buffer, size_t len, double d) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_double(&b, d); } int build_string(uint8_t *buffer, size_t len, const char *string) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_string(&b, string); } int build_bytes(uint8_t *buffer, size_t len, const void *bytes, size_t bytes_len) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_bytes(&b, bytes, bytes_len); } int build_rectangle(uint8_t *buffer, size_t len, uint32_t width, uint32_t height) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_rectangle(&b, width, height); } int build_fraction(uint8_t *buffer, size_t len, uint32_t num, uint32_t denom) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_fraction(&b, num, denom); } int build_array(uint8_t *buffer, size_t len, uint32_t child_size, uint32_t child_type, uint32_t n_elems, const void *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_array(&b, child_size, child_type, n_elems, elems); } int build_fd(uint8_t *buffer, size_t len, int64_t fd) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_fd(&b, fd); } struct spa_pod *build_test_struct( uint8_t *buffer, size_t len, int32_t num, const char *string, uint32_t rect_width, uint32_t rect_height) { struct spa_pod_frame outer, inner; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); spa_pod_builder_push_struct(&b, &outer); spa_pod_builder_int(&b, num); spa_pod_builder_string(&b, string); spa_pod_builder_push_struct(&b, &inner); spa_pod_builder_rectangle(&b, rect_width, rect_height); spa_pod_builder_pop(&b, &inner); return spa_pod_builder_pop(&b, &outer); } struct spa_pod *build_test_object(uint8_t *buffer, size_t len) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, SPA_PROP_device, SPA_POD_String("hw:0"), SPA_PROP_frequency, SPA_POD_Float(440.0f)); } struct spa_pod *build_choice_i32(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, uint32_t *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i++) { spa_pod_builder_int(&b, elems[i]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_i64(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, uint64_t *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i++) { spa_pod_builder_long(&b, elems[i]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_f32(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, float *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i++) { spa_pod_builder_float(&b, elems[i]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_f64(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, double *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i++) { spa_pod_builder_double(&b, elems[i]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_id(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, uint32_t *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i++) { spa_pod_builder_id(&b, elems[i]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_rectangle(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, uint32_t *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; assert(n_elems % 2 == 0); // elements are actually (width, height) pairs spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i += 2) { spa_pod_builder_rectangle(&b, elems[i], elems[i + 1]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_fraction(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, uint32_t *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; assert(n_elems % 2 == 0); // elements are actually (num, denom) pairs spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i += 2) { spa_pod_builder_fraction(&b, elems[i], elems[i + 1]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_choice_fd(uint8_t *buffer, size_t len, uint32_t choice_type, uint32_t flags, uint32_t n_elems, int64_t *elems) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); struct spa_pod_frame f; uint32_t i; spa_pod_builder_push_choice(&b, &f, choice_type, flags); for (i = 0; i < n_elems; i++) { spa_pod_builder_fd(&b, elems[i]); } return spa_pod_builder_pop(&b, &f); } struct spa_pod *build_audio_info_raw(uint8_t *buffer, size_t len, uint32_t id, struct spa_audio_info_raw *info) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_format_audio_raw_build(&b, id, info); } int parse_audio_info_raw(const struct spa_pod *format) { struct spa_audio_info_raw info; int res = spa_format_audio_raw_parse(format, &info); return res; } int build_pointer(uint8_t *buffer, size_t len, uint32_t type, const void *val) { struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, len); return spa_pod_builder_pointer(&b, type, val); } void print_pod(const struct spa_pod *pod) { spa_debug_pod(0, NULL, pod); } libspa-0.8.0/tests/pod.rs000064400000000000000000001665551046102023000134170ustar 00000000000000use libspa::{ pod::deserialize::PodDeserializer, pod::{ deserialize::{ DeserializeError, DeserializeSuccess, ObjectPodDeserializer, PodDeserialize, StructPodDeserializer, Visitor, }, serialize::{PodSerialize, PodSerializer, SerializeSuccess}, CanonicalFixedSizedPod, ChoiceValue, Object, Property, PropertyFlags, Value, ValueArray, }, utils::{Choice, ChoiceEnum, ChoiceFlags, Fd, Fraction, Id, Rectangle}, }; use std::{ ffi::{c_void, CString}, io::Cursor, ptr, }; pub mod c { #[allow(non_camel_case_types)] type spa_pod = u8; #[link(name = "pod")] extern "C" { pub fn build_none(buffer: *mut u8, len: usize) -> i32; pub fn build_bool(buffer: *mut u8, len: usize, boolean: i32) -> i32; pub fn build_id(buffer: *mut u8, len: usize, id: u32) -> i32; pub fn build_int(buffer: *mut u8, len: usize, int: i32) -> i32; pub fn build_long(buffer: *mut u8, len: usize, long: i64) -> i32; pub fn build_float(buffer: *mut u8, len: usize, float: f32) -> i32; pub fn build_double(buffer: *mut u8, len: usize, float: f64) -> i32; pub fn build_string(buffer: *mut u8, len: usize, string: *const u8) -> i32; pub fn build_bytes(buffer: *mut u8, len: usize, bytes: *const u8, len: usize) -> i32; pub fn build_rectangle(buffer: *mut u8, len: usize, width: u32, height: u32) -> i32; pub fn build_fraction(buffer: *mut u8, len: usize, num: u32, denom: u32) -> i32; pub fn build_array( buffer: *mut u8, len: usize, child_size: u32, child_type: u32, n_elems: u32, elems: *const u8, ) -> i32; pub fn build_test_struct( buffer: *mut u8, len: usize, int: i32, string: *const u8, rect_width: u32, rect_height: u32, ) -> *const spa_pod; pub fn build_fd(buffer: *mut u8, len: usize, fd: i64) -> i32; pub fn build_test_object(buffer: *mut u8, len: usize) -> *const spa_pod; pub fn build_choice_i32( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const i32, ) -> *const spa_pod; pub fn build_choice_i64( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const i64, ) -> *const spa_pod; pub fn build_choice_f32( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const f32, ) -> *const spa_pod; pub fn build_choice_f64( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const f64, ) -> *const spa_pod; pub fn build_choice_id( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const u32, ) -> *const spa_pod; pub fn build_choice_rectangle( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const u32, ) -> *const spa_pod; pub fn build_choice_fraction( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const u32, ) -> *const spa_pod; pub fn build_choice_fd( buffer: *mut u8, len: usize, choice_type: u32, flags: u32, n_elems: u32, elems: *const i64, ) -> *const spa_pod; pub fn build_audio_info_raw( buffer: *mut u8, len: usize, id: u32, audio_raw: *const spa_sys::spa_audio_info_raw, ) -> *const spa_pod; pub fn parse_audio_info_raw(val: *const spa_pod) -> i32; pub fn build_pointer( buffer: *mut u8, len: usize, type_: u32, val: *const std::ffi::c_void, ) -> i32; pub fn print_pod(pod: *const spa_pod); } } #[test] #[cfg_attr(miri, ignore)] fn none() { let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::None) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 8]; assert_eq!(unsafe { c::build_none(vec_c.as_mut_ptr(), vec_c.len()) }, 0); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], ())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::None)) ); } #[test] #[cfg_attr(miri, ignore)] fn bool_true() { let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &true) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Bool(true)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_bool(vec_c.as_mut_ptr(), vec_c.len(), 1) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], true)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Bool(true))) ); } #[test] #[cfg_attr(miri, ignore)] fn bool_false() { let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &false) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Bool(false)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_bool(vec_c.as_mut_ptr(), vec_c.len(), 0) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], false)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Bool(false))) ); } #[test] #[cfg_attr(miri, ignore)] fn int() { let int: i32 = 765; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &int) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Int(int)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_int(vec_c.as_mut_ptr(), vec_c.len(), int) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], int)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Int(int))) ); } #[test] #[cfg_attr(miri, ignore)] fn long() { let long: i64 = 0x1234_5678_9876_5432; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &long) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Long(long)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_long(vec_c.as_mut_ptr(), vec_c.len(), long) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], long)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Long(long))) ); } #[test] #[cfg_attr(miri, ignore)] fn float() { let float: f32 = std::f32::consts::PI; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &float) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Float(float)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_float(vec_c.as_mut_ptr(), vec_c.len(), float) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], float)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Float(float))) ); } #[test] #[cfg_attr(miri, ignore)] fn double() { let double: f64 = std::f64::consts::PI; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &double) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Double(double)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_double(vec_c.as_mut_ptr(), vec_c.len(), double) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], double)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Double(double))) ); } #[test] #[cfg_attr(miri, ignore)] fn string() { let string = "123456789"; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), string) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::String(string.to_owned())) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 24]; let c_string = CString::new(string).unwrap(); assert_eq!( unsafe { c::build_string( vec_c.as_mut_ptr(), vec_c.len(), c_string.as_bytes_with_nul().as_ptr(), ) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); // Zero-copy deserializing. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], string)) ); // Deserializing by copying. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], String::from(string))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::String(string.to_string()))) ); } #[test] #[cfg_attr(miri, ignore)] fn string_no_padding() { // The pod resulting from this string should have no padding bytes. let string = "1234567"; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), string) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::String(string.to_owned())) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; let c_string = CString::new(string).unwrap(); assert_eq!( unsafe { c::build_string( vec_c.as_mut_ptr(), vec_c.len(), c_string.as_bytes_with_nul().as_ptr(), ) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); // Zero-copy deserializing. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], string)) ); // Deserializing by copying. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], String::from(string))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::String(string.to_string()))) ); } #[test] #[cfg_attr(miri, ignore)] fn string_empty() { let string = ""; let mut vec_c: Vec = vec![0; 16]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), string) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::String(string.to_owned())) .unwrap() .0 .into_inner(); let c_string = CString::new(string).unwrap(); assert_eq!( unsafe { c::build_string( vec_c.as_mut_ptr(), vec_c.len(), c_string.as_bytes_with_nul().as_ptr(), ) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); // Zero-copy deserializing. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], string)) ); // Deserializing by copying. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], String::from(string))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::String(string.to_string()))) ); } #[test] #[cfg_attr(miri, ignore)] fn bytes() { let bytes = b"123456789"; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), bytes as &[u8]) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Bytes(bytes.to_vec())) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 24]; assert_eq!( unsafe { c::build_bytes(vec_c.as_mut_ptr(), vec_c.len(), bytes.as_ptr(), bytes.len()) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); // Zero-copy deserializing. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], bytes as &[u8])) ); // Deserializing by copying. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], Vec::from(bytes as &[u8]))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Bytes(Vec::from(bytes as &[u8])))) ); } #[test] #[cfg_attr(miri, ignore)] fn bytes_no_padding() { // The pod resulting from this byte array should have no padding bytes. let bytes = b"12345678"; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), bytes as &[u8]) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Bytes(bytes.to_vec())) .unwrap() .0 .into_inner(); let mut vec_c = vec![0; 16]; assert_eq!( unsafe { c::build_bytes(vec_c.as_mut_ptr(), vec_c.len(), bytes.as_ptr(), bytes.len()) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); // Zero-copy deserializing. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], bytes as &[u8])) ); // Deserializing by copying. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], Vec::from(bytes as &[u8]))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Bytes(Vec::from(bytes as &[u8])))) ); } #[test] #[cfg_attr(miri, ignore)] fn bytes_empty() { let bytes = b""; let mut vec_c: Vec = vec![0; 8]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), bytes as &[u8]) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Bytes(bytes.to_vec())) .unwrap() .0 .into_inner(); assert_eq!( unsafe { c::build_bytes(vec_c.as_mut_ptr(), vec_c.len(), bytes.as_ptr(), bytes.len()) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); // Zero-copy deserializing. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], bytes as &[u8])) ); // Deserializing by copying. assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], Vec::from(bytes as &[u8]))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Bytes(Vec::from(bytes as &[u8])))) ); } #[test] #[cfg_attr(miri, ignore)] fn rectangle() { let rect = Rectangle { width: 640, height: 480, }; let vec_rs = PodSerializer::serialize(Cursor::new(Vec::new()), &rect) .unwrap() .0 .into_inner(); let vec_rs_val = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Rectangle(rect)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; unsafe { c::build_rectangle(vec_c.as_mut_ptr(), vec_c.len(), rect.width, rect.height) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], rect)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Rectangle(rect))) ); } #[test] #[cfg_attr(miri, ignore)] fn fraction() { let fraction = Fraction { num: 16, denom: 9 }; let vec_rs = PodSerializer::serialize(Cursor::new(Vec::new()), &fraction) .unwrap() .0 .into_inner(); let vec_rs_val = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Fraction(fraction)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; unsafe { c::build_fraction( vec_c.as_mut_ptr(), vec_c.len(), fraction.num, fraction.denom, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], fraction)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Fraction(fraction))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_i32() { // 3 elements, so the resulting array has 4 bytes padding. let array: Vec = vec![10, 15, 19]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Int(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 32]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Int, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Int(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_bool() { let array = vec![false, true]; // encode the bools on 4 bytes let array_u32: Vec = array.iter().map(|b| u32::from(*b)).collect(); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Bool(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 24]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Bool, array_u32.len() as u32, array_u32.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Bool(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_empty() { let array: Vec<()> = Vec::new(); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::None(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), 0, spa_sys::SPA_TYPE_None, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::None(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_id() { let array = vec![Id(1), Id(2)]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Id(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 24]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Id, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Id(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_long() { let array: Vec = vec![1, 2, 3]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Long(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Long, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Long(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_float() { let array: Vec = vec![1.0, 2.2]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Float(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 24]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Float, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Float(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_double() { let array = vec![1.0, 2.2]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Double(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 32]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Double, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Double(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_rectangle() { let array = vec![ Rectangle { width: 800, height: 600, }, Rectangle { width: 1920, height: 1080, }, ]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Rectangle(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 32]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Rectangle, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok(( &[] as &[u8], Value::ValueArray(ValueArray::Rectangle(array)) )) ); } #[test] #[cfg_attr(miri, ignore)] fn array_fraction() { let array = vec![Fraction { num: 1, denom: 2 }, Fraction { num: 2, denom: 3 }]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Fraction(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 32]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Fraction, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Fraction(array)))) ); } #[test] #[cfg_attr(miri, ignore)] fn array_fd() { let array = vec![Fd(10), Fd(20)]; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), array.as_slice()) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::ValueArray(ValueArray::Fd(array.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 32]; unsafe { c::build_array( vec_c.as_mut_ptr(), vec_c.len(), ::SIZE, spa_sys::SPA_TYPE_Fd, array.len() as u32, array.as_ptr() as *const u8, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], array.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::ValueArray(ValueArray::Fd(array)))) ); } #[derive(PartialEq, Eq, Debug, Clone)] struct TestStruct<'s> { // Fixed sized pod with padding. int: i32, // Dynamically sized pod. string: &'s str, // Another nested struct. nested: NestedStruct, } #[derive(PartialEq, Eq, Debug, Clone)] struct NestedStruct { rect: Rectangle, } impl<'s> PodSerialize for TestStruct<'s> { fn serialize( &self, serializer: PodSerializer, ) -> Result, cookie_factory::GenError> { let mut struct_serializer = serializer.serialize_struct()?; struct_serializer.serialize_field(&self.int)?; struct_serializer.serialize_field(self.string)?; struct_serializer.serialize_field(&self.nested)?; struct_serializer.end() } } impl PodSerialize for NestedStruct { fn serialize( &self, serializer: PodSerializer, ) -> Result, cookie_factory::GenError> { let mut struct_serializer = serializer.serialize_struct()?; struct_serializer.serialize_field(&self.rect)?; struct_serializer.end() } } impl<'de> PodDeserialize<'de> for TestStruct<'de> { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { struct TestVisitor; impl<'de> Visitor<'de> for TestVisitor { type Value = TestStruct<'de>; type ArrayElem = std::convert::Infallible; fn visit_struct( &self, struct_deserializer: &mut StructPodDeserializer<'de>, ) -> Result> { Ok(TestStruct { int: struct_deserializer .deserialize_field()? .expect("Input has too few fields"), string: struct_deserializer .deserialize_field()? .expect("Input has too few fields"), nested: struct_deserializer .deserialize_field()? .expect("Input has too few fields"), }) } } deserializer.deserialize_struct(TestVisitor) } } impl<'de> PodDeserialize<'de> for NestedStruct { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { struct NestedVisitor; impl<'de> Visitor<'de> for NestedVisitor { type Value = NestedStruct; type ArrayElem = std::convert::Infallible; fn visit_struct( &self, struct_deserializer: &mut StructPodDeserializer<'de>, ) -> Result> { Ok(NestedStruct { rect: struct_deserializer .deserialize_field()? .expect("Input has too few fields"), }) } } deserializer.deserialize_struct(NestedVisitor) } } #[test] #[cfg_attr(miri, ignore)] fn struct_() { const INT: i32 = 313; const STR: &str = "foo"; const RECT: Rectangle = Rectangle { width: 31, height: 14, }; let struct_ = TestStruct { int: INT, string: STR, nested: NestedStruct { rect: RECT }, }; let struct_val = Value::Struct(vec![ Value::Int(INT), Value::String(STR.to_owned()), Value::Struct(vec![Value::Rectangle(RECT)]), ]); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &struct_) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &struct_val) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 64]; let c_string = CString::new(struct_.string).unwrap(); let ptr = unsafe { c::build_test_struct( vec_c.as_mut_ptr(), vec_c.len(), struct_.int, c_string.as_bytes_with_nul().as_ptr(), struct_.nested.rect.width, struct_.nested.rect.height, ) }; assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], struct_.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], struct_val)) ); assert_eq!( unsafe { PodDeserializer::deserialize_ptr(ptr::NonNull::new(ptr as *mut _).unwrap()) }, Ok(struct_) ); } #[test] #[cfg_attr(miri, ignore)] fn id() { const ID: Id = Id(7); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &ID) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Id(ID)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_id(vec_c.as_mut_ptr(), vec_c.len(), ID.0) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], ID)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Id(ID))) ); } #[test] #[cfg_attr(miri, ignore)] fn fd() { const FD: Fd = Fd(7); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &FD) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &Value::Fd(FD)) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 16]; assert_eq!( unsafe { c::build_fd(vec_c.as_mut_ptr(), vec_c.len(), FD.0) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], FD)) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok((&[] as &[u8], Value::Fd(FD))) ); } #[test] #[cfg_attr(miri, ignore)] fn object() { let mut vec_c: Vec = vec![0; 64]; let ptr = unsafe { c::build_test_object(vec_c.as_mut_ptr(), vec_c.len()) }; #[derive(Debug, PartialEq)] struct MyProps { device: String, frequency: f32, } impl<'de> PodDeserialize<'de> for MyProps { fn deserialize( deserializer: PodDeserializer<'de>, ) -> Result<(Self, DeserializeSuccess<'de>), DeserializeError<&'de [u8]>> where Self: Sized, { struct PropsVisitor; impl<'de> Visitor<'de> for PropsVisitor { type Value = MyProps; type ArrayElem = std::convert::Infallible; fn visit_object( &self, object_deserializer: &mut ObjectPodDeserializer<'de>, ) -> Result> { let (device, _flags) = object_deserializer .deserialize_property_key::(spa_sys::SPA_PROP_device)?; let (frequency, _flags) = object_deserializer .deserialize_property_key::(spa_sys::SPA_PROP_frequency)?; Ok(MyProps { device, frequency }) } } deserializer.deserialize_object(PropsVisitor) } } let (_, props) = PodDeserializer::deserialize_from::(&vec_c).unwrap(); assert_eq!(props.device, "hw:0"); // clippy does not like comparing f32, see https://rust-lang.github.io/rust-clippy/master/#float_cmp assert!((props.frequency - 440.0_f32).abs() < f32::EPSILON); let props2 = unsafe { PodDeserializer::deserialize_ptr::(ptr::NonNull::new(ptr as *mut _).unwrap()) .unwrap() }; assert_eq!(props, props2); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok(( &[] as &[u8], Value::Object(Object { type_: spa_sys::SPA_TYPE_OBJECT_Props, id: spa_sys::SPA_PARAM_Props, properties: vec![ Property { key: spa_sys::SPA_PROP_device, flags: PropertyFlags::empty(), value: Value::String("hw:0".into()), }, Property { key: spa_sys::SPA_PROP_frequency, flags: PropertyFlags::empty(), value: Value::Float(440.0) } ] }) )) ); // serialization impl PodSerialize for MyProps { fn serialize( &self, serializer: PodSerializer, ) -> Result, cookie_factory::GenError> { let mut obj_serializer = serializer .serialize_object(spa_sys::SPA_TYPE_OBJECT_Props, spa_sys::SPA_PARAM_Props)?; obj_serializer.serialize_property( spa_sys::SPA_PROP_device, "hw:0", PropertyFlags::empty(), )?; obj_serializer.serialize_property( spa_sys::SPA_PROP_frequency, &440.0_f32, PropertyFlags::empty(), )?; obj_serializer.end() } } let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &props) .unwrap() .0 .into_inner(); assert_eq!(vec_rs, vec_c); } #[test] #[cfg_attr(miri, ignore)] fn choice_range_f32() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Range { default: 440.0, min: 110.0, max: 880.0, }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Float(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { assert_ne!( c::build_choice_f32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Range, 0, 3, &[440.0_f32, 110.0, 880.0] as *const f32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Float(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_range_i32() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Range { default: 5, min: 2, max: 10, }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Int(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { assert_ne!( c::build_choice_i32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Range, 0, 3, &[5, 2, 10] as *const i32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Int(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_none_i32() { let choice = Choice(ChoiceFlags::empty(), ChoiceEnum::None(5)); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Int(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 32]; unsafe { assert_ne!( c::build_choice_i32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_None, 0, 1, &[5] as *const i32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Int(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_step_i32() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Step { default: 5, min: 2, max: 10, step: 1, }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Int(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { assert_ne!( c::build_choice_i32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Step, 0, 4, &[5, 2, 10, 1] as *const i32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Int(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_enum_i32() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Enum { default: 5, alternatives: vec![2, 10, 1], }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Int(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { assert_ne!( c::build_choice_i32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Enum, 0, 4, &[5, 2, 10, 1] as *const i32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Int(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_flags_i32() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Flags { default: 5, flags: vec![2, 10, 1], }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Int(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { assert_ne!( c::build_choice_i32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Flags, 0, 4, &[5, 2, 10, 1] as *const i32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Int(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_range_i64() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Range { default: 440_i64, min: 110, max: 880, }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Long(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 48]; unsafe { assert_ne!( c::build_choice_i64( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Range, 0, 3, &[440_i64, 110, 880] as *const i64, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Long(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_range_f64() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Range { default: 440.0_f64, min: 110.0, max: 880.0, }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Double(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 48]; unsafe { assert_ne!( c::build_choice_f64( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Range, 0, 3, &[440.0_f64, 110.0, 880.0] as *const f64, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Double(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_enum_id() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Enum { default: Id(5), alternatives: vec![Id(2), Id(10), Id(1)], }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Id(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 40]; unsafe { assert_ne!( c::build_choice_id( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Enum, 0, 4, &[5_u32, 2, 10, 1] as *const u32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Id(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_enum_rectangle() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Enum { default: Rectangle { width: 800, height: 600, }, alternatives: vec![ Rectangle { width: 1920, height: 1080, }, Rectangle { width: 300, height: 200, }, ], }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Rectangle(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 48]; unsafe { assert_ne!( c::build_choice_rectangle( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Enum, 0, 6, &[800_u32, 600, 1920, 1080, 300, 200] as *const u32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Rectangle(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_enum_fraction() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Enum { default: Fraction { num: 1, denom: 2 }, alternatives: vec![Fraction { num: 2, denom: 3 }, Fraction { num: 1, denom: 3 }], }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Fraction(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 48]; unsafe { assert_ne!( c::build_choice_fraction( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Enum, 0, 6, &[1_u32, 2, 2, 3, 1, 3] as *const u32, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Fraction(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_enum_fd() { let choice = Choice( ChoiceFlags::empty(), ChoiceEnum::Enum { default: Fd(5), alternatives: vec![Fd(2), Fd(10), Fd(1)], }, ); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &choice) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Choice(ChoiceValue::Fd(choice.clone())), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 56]; unsafe { assert_ne!( c::build_choice_fd( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_Enum, 0, 4, &[5_i64, 2, 10, 1] as *const i64, ), std::ptr::null() ); } assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs, vec_rs_val); assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice.clone())) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_c), Ok((&[] as &[u8], Value::Choice(ChoiceValue::Fd(choice)))) ); } #[test] #[cfg_attr(miri, ignore)] fn choice_extra_values() { // deserialize a none choice having more than one values, which are ignored. let choice = Choice(ChoiceFlags::empty(), ChoiceEnum::None(5)); let mut vec_c: Vec = vec![0; 32]; unsafe { assert_ne!( c::build_choice_i32( vec_c.as_mut_ptr(), vec_c.len(), spa_sys::SPA_CHOICE_None, 0, 1, &[5, 6, 7, 8] as *const i32, ), std::ptr::null() ); } assert_eq!( PodDeserializer::deserialize_from(&vec_c), Ok((&[] as &[u8], choice)) ); } #[test] #[cfg_attr(miri, ignore)] fn pointer() { let val = 7; let ptr = &val as *const i32; const POINTER_TYPE: u32 = 10; let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &(POINTER_TYPE, ptr)) .unwrap() .0 .into_inner(); let vec_rs_val: Vec = PodSerializer::serialize( Cursor::new(Vec::new()), &Value::Pointer(POINTER_TYPE, ptr as *const c_void), ) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; 24]; assert_eq!( unsafe { c::build_pointer( vec_c.as_mut_ptr(), vec_c.len(), 10, ptr as *const std::ffi::c_void, ) }, 0 ); assert_eq!(vec_rs, vec_c); assert_eq!(vec_rs_val, vec_c); assert_eq!( PodDeserializer::deserialize_from(&vec_rs), Ok((&[] as &[u8], (POINTER_TYPE, ptr))) ); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs), Ok(( &[] as &[u8], Value::Pointer(POINTER_TYPE, ptr as *const c_void) )) ); } use libspa::param::audio::{self, AudioFormat, AudioInfoRaw}; #[test] #[cfg_attr(miri, ignore)] fn composite_values() { let all_type_values = [ Value::None, Value::Bool(false), Value::Id(Id(0)), Value::Int(0), Value::Long(0), Value::Float(0.0), Value::Double(0.0), Value::String(String::new()), Value::Bytes(vec![]), Value::Rectangle(Rectangle { width: 1, height: 1, }), Value::Fraction(Fraction { num: 0, denom: 1 }), Value::Fd(Fd(-1)), Value::ValueArray(ValueArray::None(vec![])), Value::Struct(vec![]), Value::Object(Object { type_: 0, id: 0, properties: vec![], }), Value::Choice(ChoiceValue::Int(Choice( ChoiceFlags::empty(), ChoiceEnum::None(0), ))), Value::Pointer(0, ptr::null_mut()), ]; for value in &all_type_values { let (cursor, len) = PodSerializer::serialize(Cursor::new(Vec::new()), value).unwrap(); let vec_rs_val = cursor.into_inner(); assert_eq!(len, vec_rs_val.len() as u64); } let struct_val = Value::Struct(all_type_values.to_vec()); let (cursor, len) = PodSerializer::serialize(Cursor::new(Vec::new()), &struct_val).unwrap(); let vec_rs_val = cursor.into_inner(); assert_eq!(len, vec_rs_val.len() as u64); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs_val), Ok((&[] as &[u8], struct_val)) ); let object_val = Value::Object(Object { type_: 0, id: 0, properties: all_type_values .iter() .map(|value| Property { flags: PropertyFlags::empty(), key: 0, value: value.clone(), }) .collect(), }); let (cursor, len) = PodSerializer::serialize(Cursor::new(Vec::new()), &object_val).unwrap(); let vec_rs_val = cursor.into_inner(); assert_eq!(len, vec_rs_val.len() as u64); assert_eq!( PodDeserializer::deserialize_any_from(&vec_rs_val), Ok((&[] as &[u8], object_val)) ); } #[test] #[cfg_attr(miri, ignore)] fn audio_info_raw() { let id = 1; let position = [0; audio::MAX_CHANNELS]; let mut info = AudioInfoRaw::new(); info.set_channels(1); info.set_rate(44100); info.set_format(AudioFormat::S8); info.set_position(position); let obj_rs = Value::Object(Object { type_: spa_sys::SPA_TYPE_OBJECT_Format, id, properties: info.into(), }); let vec_rs: Vec = PodSerializer::serialize(Cursor::new(Vec::new()), &obj_rs) .unwrap() .0 .into_inner(); let mut vec_c: Vec = vec![0; vec_rs.len()]; let obj_c: spa_sys::spa_audio_info_raw = info.as_raw(); assert_ne!( unsafe { c::build_audio_info_raw(vec_c.as_mut_ptr(), vec_c.len(), id, &obj_c) }, std::ptr::null() ); assert_eq!(vec_rs, vec_c); assert!(unsafe { c::parse_audio_info_raw(vec_c.as_mut_ptr()) } > 0); }