use crate::Atom;
use std::cell::Cell;
use std::marker::Unpin;
use std::mem::{size_of, size_of_val};
use urid::URID;
#[derive(Clone, Copy)]
pub struct Space<'a> {
data: Option<&'a [u8]>,
}
impl<'a> Space<'a> {
#[allow(clippy::trivially_copy_pass_by_ref)]
pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> Self {
let size = atom.size as usize;
let data = std::slice::from_raw_parts(
atom as *const sys::LV2_Atom as *const u8,
size + size_of::<sys::LV2_Atom>(),
);
Self::from_slice(data)
}
pub fn from_slice(data: &'a [u8]) -> Self {
Space { data: Some(data) }
}
pub fn split_raw(self, size: usize) -> Option<(&'a [u8], Self)> {
let data = self.data?;
if size > data.len() {
return None;
}
let (lower_space, upper_space) = data.split_at(size);
let padding = if size % 8 == 0 { 0 } else { 8 - size % 8 };
let upper_space = if padding <= upper_space.len() {
let upper_space = upper_space.split_at(padding).1;
Some(upper_space)
} else {
None
};
let upper_space = Self { data: upper_space };
Some((lower_space, upper_space))
}
pub fn split_space(self, size: usize) -> Option<(Self, Self)> {
self.split_raw(size)
.map(|(data, rhs)| (Self::from_slice(data), rhs))
}
pub fn split_type<T>(self) -> Option<(&'a T, Self)>
where
T: Unpin + Copy + Send + Sync + Sized + 'static,
{
self.split_raw(size_of::<T>())
.map(|(data, rhs)| (unsafe { &*(data.as_ptr() as *const T) }, rhs))
}
pub fn split_atom(self) -> Option<(Self, Self)> {
let (header, _) = self.split_type::<sys::LV2_Atom>()?;
self.split_space(size_of::<sys::LV2_Atom>() + header.size as usize)
}
pub fn split_atom_body<T: ?Sized>(self, urid: URID<T>) -> Option<(Self, Self)> {
let (header, space) = self.split_type::<sys::LV2_Atom>()?;
if header.type_ != urid.get() {
return None;
}
space.split_space(header.size as usize)
}
pub fn from_reference<T: ?Sized>(instance: &'a T) -> Self {
let data = unsafe {
std::slice::from_raw_parts(instance as *const T as *const u8, size_of_val(instance))
};
assert_eq!(data.as_ptr() as usize % 8, 0);
Space { data: Some(data) }
}
pub fn concat(lhs: Self, rhs: Self) -> Option<Self> {
let lhs_data = match lhs.data {
Some(data) => data,
None => return Some(rhs),
};
let rhs_data = match rhs.data {
Some(data) => data,
None => return Some(lhs),
};
if unsafe { lhs_data.as_ptr().add(lhs_data.len()) } == rhs_data.as_ptr() {
Some(Self::from_slice(unsafe {
std::slice::from_raw_parts(lhs_data.as_ptr(), lhs_data.len() + rhs_data.len())
}))
} else {
None
}
}
pub fn data(&self) -> Option<&'a [u8]> {
self.data
}
pub fn mut_data(&mut self) -> &mut Option<&'a [u8]> {
&mut self.data
}
}
pub trait MutSpace<'a> {
fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])>;
fn write_raw(&mut self, data: &[u8], apply_padding: bool) -> Option<&'a mut [u8]> {
self.allocate(data.len(), apply_padding).map(|(_, space)| {
space.copy_from_slice(data);
space
})
}
}
pub struct RootMutSpace<'a> {
space: Cell<Option<&'a mut [u8]>>,
allocated_bytes: usize,
}
impl<'a> RootMutSpace<'a> {
pub unsafe fn from_atom(atom: &mut sys::LV2_Atom) -> Self {
let space = std::slice::from_raw_parts_mut(
atom as *mut _ as *mut u8,
atom.size as usize + size_of::<sys::LV2_Atom>(),
);
Self::new(space)
}
pub fn new(space: &'a mut [u8]) -> Self {
RootMutSpace {
space: Cell::new(Some(space)),
allocated_bytes: 0,
}
}
}
impl<'a> MutSpace<'a> for RootMutSpace<'a> {
fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])> {
if self.space.get_mut().is_none() {
return None;
}
let mut space = self.space.replace(None).unwrap();
let padding = if apply_padding {
let alignment = self.allocated_bytes % 8;
let padding = if alignment == 0 { 0 } else { 8 - alignment };
if padding > space.len() {
return None;
}
space = space.split_at_mut(padding).1;
self.allocated_bytes += padding;
padding
} else {
0
};
if size > space.len() {
return None;
}
let (lower_slice, upper_slice) = space.split_at_mut(size);
self.allocated_bytes += size;
self.space.set(Some(upper_slice));
Some((padding, lower_slice))
}
}
pub struct SpaceElement {
next: Option<(Box<Self>, Box<[u8]>)>,
}
impl Default for SpaceElement {
fn default() -> Self {
Self { next: None }
}
}
impl SpaceElement {
pub fn allocate(&mut self, size: usize) -> Option<(&mut Self, &mut [u8])> {
if self.next.is_some() {
return None;
}
let new_data = vec![0u8; size].into_boxed_slice();
let new_element = Box::new(Self::default());
self.next = Some((new_element, new_data));
self.next
.as_mut()
.map(|(new_element, new_data): &mut (Box<Self>, Box<[u8]>)| {
(new_element.as_mut(), new_data.as_mut())
})
}
pub fn to_vec(&self) -> Vec<u8> {
self.iter()
.map(|slice| slice.iter())
.flatten()
.cloned()
.collect()
}
pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
std::iter::successors(self.next.as_ref(), |element| element.0.next.as_ref())
.map(|(_, data)| data.as_ref())
}
}
pub struct SpaceHead<'a> {
element: Option<&'a mut SpaceElement>,
allocated_space: usize,
}
impl<'a> SpaceHead<'a> {
pub fn new(element: &'a mut SpaceElement) -> Self {
Self {
element: Some(element),
allocated_space: 0,
}
}
fn internal_allocate(&mut self, size: usize) -> Option<&'a mut [u8]> {
let element = self.element.take()?;
let (new_element, new_space) = element.allocate(size)?;
self.element = Some(new_element);
self.allocated_space += size;
Some(new_space)
}
}
impl<'a> MutSpace<'a> for SpaceHead<'a> {
fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])> {
let padding: usize = if apply_padding {
(8 - self.allocated_space % 8) % 8
} else {
0
};
if padding != 0 {
self.internal_allocate(padding);
}
self.internal_allocate(size)
.map(|new_space| (padding, new_space))
}
}
pub struct FramedMutSpace<'a, 'b> {
atom: &'a mut sys::LV2_Atom,
parent: &'b mut dyn MutSpace<'a>,
}
impl<'a, 'b> FramedMutSpace<'a, 'b> {
pub fn new<A: ?Sized>(parent: &'b mut dyn MutSpace<'a>, urid: URID<A>) -> Option<Self> {
let atom = sys::LV2_Atom {
size: 0,
type_: urid.get(),
};
let atom: &'a mut sys::LV2_Atom = parent.write(&atom, true)?;
Some(Self { atom, parent })
}
}
impl<'a, 'b> MutSpace<'a> for FramedMutSpace<'a, 'b> {
fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])> {
self.parent
.allocate(size, apply_padding)
.map(|(padding, data)| {
self.atom.size += (size + padding) as u32;
(padding, data)
})
}
}
impl<'a, 'b> dyn MutSpace<'a> + 'b {
pub fn write<T>(&mut self, instance: &T, apply_padding: bool) -> Option<&'a mut T>
where
T: Unpin + Copy + Send + Sync + Sized + 'static,
{
let size = std::mem::size_of::<T>();
let input_data =
unsafe { std::slice::from_raw_parts(instance as *const T as *const u8, size) };
let output_data = self.write_raw(input_data, apply_padding)?;
assert_eq!(size, output_data.len());
Some(unsafe { &mut *(output_data.as_mut_ptr() as *mut T) })
}
pub fn init<'c, A: Atom<'a, 'c>>(
&'c mut self,
urid: URID<A>,
parameter: A::WriteParameter,
) -> Option<A::WriteHandle> {
let new_space = FramedMutSpace::new(self, urid)?;
A::init(new_space, parameter)
}
}
#[cfg(test)]
mod tests {
use crate::space::*;
use std::mem::{size_of, size_of_val};
use urid::*;
#[test]
fn test_space() {
let mut vector: Vec<u8> = vec![0; 256];
for i in 0..128 {
vector[i] = i as u8;
}
unsafe {
let ptr = vector.as_mut_slice().as_mut_ptr().add(128) as *mut u32;
*(ptr) = 0x42424242;
}
let space = Space::from_slice(vector.as_slice());
let (lower_space, space) = space.split_raw(128).unwrap();
for i in 0..128 {
assert_eq!(lower_space[i], i as u8);
}
let (integer, _) = space.split_type::<u32>().unwrap();
assert_eq!(*integer, 0x42424242);
}
#[test]
fn test_split_atom() {
let mut data: Box<[u64]> = Box::new([0; 256]);
let urid: URID = unsafe { URID::new_unchecked(17) };
unsafe {
*(data.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int {
atom: sys::LV2_Atom {
size: size_of::<i32>() as u32,
type_: urid.get(),
},
body: 42,
}
}
let space = Space::from_reference(data.as_ref());
let (atom, _) = space.split_atom().unwrap();
let (body, _) = atom.split_atom_body(urid).unwrap();
let body = body.data().unwrap();
assert_eq!(size_of::<i32>(), size_of_val(body));
assert_eq!(42, unsafe { *(body.as_ptr() as *const i32) });
}
#[test]
fn test_from_reference() {
let value: u64 = 0x42424242;
let space = Space::from_reference(&value);
assert_eq!(value, *space.split_type::<u64>().unwrap().0);
}
#[test]
fn test_concat() {
let data: Box<[u64]> = Box::new([0; 64]);
let space = Space::from_reference(data.as_ref());
let (lhs, rhs) = space.split_space(8).unwrap();
let concated_space = Space::concat(lhs, rhs).unwrap();
assert_eq!(
space.data().unwrap().as_ptr(),
concated_space.data().unwrap().as_ptr()
);
assert_eq!(
space.data().unwrap().len(),
concated_space.data().unwrap().len()
);
}
fn test_mut_space<'a, S: MutSpace<'a>>(mut space: S) {
let map = HashURIDMapper::new();
let urids = crate::AtomURIDCollection::from_map(&map).unwrap();
let mut test_data: Vec<u8> = vec![0; 24];
for i in 0..test_data.len() {
test_data[i] = i as u8;
}
match space.write_raw(test_data.as_slice(), true) {
Some(written_data) => assert_eq!(test_data.as_slice(), written_data),
None => panic!("Writing failed!"),
}
let test_atom = sys::LV2_Atom { size: 42, type_: 1 };
let written_atom = (&mut space as &mut dyn MutSpace)
.write(&test_atom, true)
.unwrap();
assert_eq!(written_atom.size, test_atom.size);
assert_eq!(written_atom.type_, test_atom.type_);
let created_space = unsafe { RootMutSpace::from_atom(written_atom) }
.space
.take()
.unwrap();
assert_eq!(
created_space.as_ptr() as usize,
written_atom as *mut _ as usize
);
assert_eq!(created_space.len(), size_of::<sys::LV2_Atom>() + 42);
let mut atom_frame =
FramedMutSpace::new(&mut space as &mut dyn MutSpace, urids.chunk).unwrap();
let mut test_data: Vec<u8> = vec![0; 24];
for i in 0..test_data.len() {
test_data[i] = i as u8;
}
let written_data = atom_frame.write_raw(test_data.as_slice(), true).unwrap();
assert_eq!(test_data.as_slice(), written_data);
assert_eq!(atom_frame.atom.size, test_data.len() as u32);
let test_atom = sys::LV2_Atom { size: 42, type_: 1 };
let borrowed_frame = &mut atom_frame as &mut dyn MutSpace;
let written_atom = borrowed_frame.write(&test_atom, true).unwrap();
assert_eq!(written_atom.size, test_atom.size);
assert_eq!(written_atom.type_, test_atom.type_);
assert_eq!(
atom_frame.atom.size as usize,
test_data.len() + size_of_val(&test_atom)
);
}
#[test]
fn test_root_mut_space() {
const MEMORY_SIZE: usize = 256;
let mut memory: [u64; MEMORY_SIZE] = [0; MEMORY_SIZE];
let frame: RootMutSpace = RootMutSpace::new(unsafe {
std::slice::from_raw_parts_mut(
(&mut memory).as_mut_ptr() as *mut u8,
MEMORY_SIZE * size_of::<u64>(),
)
});
test_mut_space(frame);
}
#[test]
fn test_space_head() {
let mut space = SpaceElement::default();
let head = SpaceHead::new(&mut space);
test_mut_space(head);
}
#[test]
fn test_padding_inside_frame() {
const MEMORY_SIZE: usize = 256;
let mut memory: [u64; MEMORY_SIZE] = [0; MEMORY_SIZE];
let raw_space: &mut [u8] = unsafe {
std::slice::from_raw_parts_mut(
(&mut memory).as_mut_ptr() as *mut u8,
MEMORY_SIZE * size_of::<u64>(),
)
};
{
let mut root: RootMutSpace = RootMutSpace::new(raw_space);
let mut frame =
FramedMutSpace::new(&mut root as &mut dyn MutSpace, URID::<()>::new(1).unwrap())
.unwrap();
{
let frame = &mut frame as &mut dyn MutSpace;
frame.write::<u32>(&42, true).unwrap();
frame.write::<u32>(&17, true).unwrap();
}
}
{
let (atom, space) = raw_space.split_at(size_of::<sys::LV2_Atom>());
let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) };
assert_eq!(atom.type_, 1);
assert_eq!(atom.size as usize, 12);
let (value, space) = space.split_at(size_of::<u32>());
let value = unsafe { *(value.as_ptr() as *const u32) };
assert_eq!(value, 42);
let (_, space) = space.split_at(4);
let (value, _) = space.split_at(size_of::<u32>());
let value = unsafe { *(value.as_ptr() as *const u32) };
assert_eq!(value, 17);
}
}
#[test]
fn unaligned_root_write() {
let mut raw_space = Box::new([0u8; 8]);
{
let mut root_space = RootMutSpace::new(&mut raw_space[3..]);
(&mut root_space as &mut dyn MutSpace)
.write(&42u8, true)
.unwrap();
}
assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref());
}
}