use std::{
convert::TryInto,
ffi::CString,
io::{Seek, SeekFrom, Write},
marker::PhantomData,
};
pub use cookie_factory::GenError;
use cookie_factory::{
bytes::{ne_u32, ne_u8},
combinator::slice,
gen,
multi::all,
sequence::{pair, tuple},
SerializeFn,
};
use super::{CanonicalFixedSizedPod, FixedSizedPod};
pub trait PodSerialize {
fn serialize<O: Write + Seek>(
&self,
serializer: PodSerializer<O>,
) -> Result<SerializeSuccess<O>, GenError>;
}
impl PodSerialize for str {
fn serialize<O: Write + Seek>(
&self,
serializer: PodSerializer<O>,
) -> Result<SerializeSuccess<O>, GenError> {
serializer.serialize_string(self)
}
}
impl PodSerialize for [u8] {
fn serialize<O: Write + Seek>(
&self,
serializer: PodSerializer<O>,
) -> Result<SerializeSuccess<O>, GenError> {
serializer.serialize_bytes(self)
}
}
impl<P: FixedSizedPod> PodSerialize for [P] {
fn serialize<O: Write + Seek>(
&self,
serializer: PodSerializer<O>,
) -> Result<SerializeSuccess<O>, 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()
}
}
pub struct SerializeSuccess<O: Write + Seek> {
serializer: PodSerializer<O>,
len: u64,
}
pub struct PodSerializer<O: Write + Seek> {
out: Option<O>,
}
impl<O: Write + Seek> PodSerializer<O> {
pub fn serialize<P>(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,
)
})
}
fn header(size: usize, ty: u32) -> impl SerializeFn<O> {
pair(ne_u32(size as u32), ne_u32(ty))
}
fn padding(len: usize) -> impl SerializeFn<O> {
let zeroes = std::iter::repeat(0u8);
all(zeroes.take(len).map(ne_u8))
}
fn gen(&mut self, f: impl SerializeFn<O>) -> Result<u64, GenError> {
gen(
f,
self.out
.take()
.expect("PodSerializer does not contain a writer"),
)
.map(|(writer, len)| {
self.out = Some(writer);
len
})
}
fn write_pod(
mut self,
size: usize,
type_: u32,
f: impl SerializeFn<O>,
) -> Result<SerializeSuccess<O>, 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,
})
}
pub fn serialized_fixed_sized_pod<P>(self, pod: &P) -> Result<SerializeSuccess<O>, GenError>
where
P: FixedSizedPod + ?Sized,
{
self.write_pod(
P::CanonicalType::SIZE as usize,
P::CanonicalType::TYPE,
|out| pod.as_canonical_type().serialize_body(out),
)
}
pub fn serialize_string(self, string: &str) -> Result<SerializeSuccess<O>, 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))
}
pub fn serialize_bytes(self, bytes: &[u8]) -> Result<SerializeSuccess<O>, GenError> {
self.write_pod(bytes.len(), spa_sys::SPA_TYPE_Bytes, slice(bytes))
}
pub fn serialize_array<P: FixedSizedPod>(
mut self,
length: u32,
) -> Result<ArrayPodSerializer<O, P>, 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,
})
}
pub fn serialize_struct(mut self) -> Result<StructPodSerializer<O>, GenError> {
let header_position = self
.out
.as_mut()
.expect("PodSerializer does not contain a writer")
.seek(SeekFrom::Current(0))
.expect("Could not get current position in writer");
self.gen(Self::header(0, spa_sys::SPA_TYPE_Struct))?;
Ok(StructPodSerializer {
serializer: Some(self),
header_position,
written: 0,
})
}
}
pub struct ArrayPodSerializer<O: Write + Seek, P: FixedSizedPod> {
serializer: PodSerializer<O>,
length: u32,
written: u32,
_phantom: PhantomData<P>,
}
impl<O: Write + Seek, P: FixedSizedPod> ArrayPodSerializer<O, P> {
pub fn serialize_element(&mut self, elem: &P) -> Result<u64, GenError> {
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
}
pub fn end(mut self) -> Result<SerializeSuccess<O>, 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)
};
let pad_bytes = self.serializer.gen(PodSerializer::padding(padding))?;
Ok(SerializeSuccess {
serializer: self.serializer,
len: 16 + u64::from(self.written * P::CanonicalType::SIZE) + pad_bytes,
})
}
}
pub struct StructPodSerializer<O: Write + Seek> {
serializer: Option<PodSerializer<O>>,
header_position: u64,
written: usize,
}
impl<O: Write + Seek> StructPodSerializer<O> {
pub fn serialize_field<P>(&mut self, field: &P) -> Result<u64, GenError>
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)
}
pub fn end(self) -> Result<SerializeSuccess<O>, GenError> {
let mut serializer = self
.serializer
.expect("StructSerializer does not contain a serializer");
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");
Ok(SerializeSuccess {
serializer,
len: self.written as u64 + 8,
})
}
}