From f9864a2e1d14e9a625b76f280a82d4d620fd3c5e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Fri, 6 Aug 2021 09:01:08 +0200 Subject: [PATCH 01/54] wip --- atom/src/chunk.rs | 6 +- atom/src/lib.rs | 34 +++-- atom/src/object.rs | 28 ++-- atom/src/scalar.rs | 6 +- atom/src/sequence.rs | 12 +- atom/src/space.rs | 229 ++++++++++++++++++--------------- atom/src/string.rs | 18 +-- atom/src/tuple.rs | 6 +- atom/src/vector.rs | 8 +- atom/tests/atom_integration.rs | 2 +- midi/src/raw.rs | 2 +- midi/src/wmidi_binding.rs | 6 +- state/src/raw.rs | 10 +- 13 files changed, 200 insertions(+), 167 deletions(-) diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 86ff77e2..50d1cc8d 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -46,8 +46,8 @@ where type WriteParameter = (); type WriteHandle = FramedMutSpace<'a, 'b>; - fn read(space: Space<'a>, _: ()) -> Option<&'a [u8]> { - space.data() + unsafe fn read(space: Space<'a>, _: ()) -> Option<&'a [u8]> { + Some(space.as_bytes()) } fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { @@ -109,7 +109,7 @@ mod tests { // reading { - let space = Space::from_reference(raw_space.as_ref()); + let space = Space::from_ref(raw_space.as_ref()); let data = Chunk::read(space.split_atom_body(urids.chunk).unwrap().0, ()).unwrap(); assert_eq!(data.len(), SLICE_LENGTH); diff --git a/atom/src/lib.rs b/atom/src/lib.rs index a091671d..c8927480 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -150,17 +150,22 @@ where /// It may contain a reference to a `MutSpace` and therefore may not outlive it. type WriteHandle: 'b; - /// Read the body of the atom. + /// Reads the body of the atom. /// - /// The passed space exactly covers the body of the atom, excluding the header. You may assume that the body is actually of your atom type, since the URID of the atom was checked beforehand. + /// The passed space exactly covers the body of the atom, excluding the header. /// - /// If the atom is malformed, you may not panic and return `None` instead. - fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option; + /// If the atom is malformed, this method returns `None`. + /// + /// # Safety + /// + /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, + /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. + unsafe fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option; /// Initialize the body of the atom. /// /// In this method, the atom is prepared for the writing handle. Usually, the atom will not be - /// valid when initializied; Users have to use the write handle to make it valid. + /// valid when initialized; Users have to use the write handle to make it valid. /// /// The frame of the atom was already initialized, containing the URID. /// @@ -182,8 +187,11 @@ pub struct UnidentifiedAtom<'a> { impl<'a> UnidentifiedAtom<'a> { /// Construct a new unidentified atom. /// - /// The space actually has to contain an atom. If it doesn't, crazy (but not undefined) things can happen. - pub fn new(space: Space<'a>) -> Self { + /// # Safety + /// + /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body.x + #[inline] + pub unsafe fn new_unchecked(space: Space<'a>) -> Self { Self { space } } @@ -195,18 +203,18 @@ impl<'a> UnidentifiedAtom<'a> { urid: URID, parameter: A::ReadParameter, ) -> Option { - self.space - .split_atom_body(urid) - .map(|(body, _)| body) - .and_then(|body| A::read(body, parameter)) + // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. + let (body, _rest) = unsafe { self.space.split_atom_body(urid) }?; + // SAFETY: the fact that this contains a valid instance of A is checked by split_atom_body above. + unsafe { A::read(body, parameter) } } /// Retrieve the type URID of the atom. /// /// This can be used to identify atoms without actually reading them. pub fn type_urid(self) -> Option { - self.space - .split_type::() + // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. + unsafe { self.space.split_for_type::() } .and_then(|(header, _)| URID::new(header.type_)) } } diff --git a/atom/src/object.rs b/atom/src/object.rs index 1a94d0e1..74be1887 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -102,8 +102,8 @@ where type WriteParameter = ObjectHeader; type WriteHandle = ObjectWriter<'a, 'b>; - fn read(body: Space<'a>, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { - let (header, body) = body.split_type::()?; + unsafe fn read(body: Space<'a>, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { + let (header, body) = body.split_for_type::()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), otype: URID::try_from(header.otype).ok()?, @@ -153,7 +153,7 @@ where type WriteHandle = >::WriteHandle; #[allow(clippy::unit_arg)] - fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option { + unsafe fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option { Object::read(body, parameter) } @@ -176,9 +176,11 @@ impl<'a> Iterator for ObjectReader<'a> { type Item = (PropertyHeader, UnidentifiedAtom<'a>); fn next(&mut self) -> Option<(PropertyHeader, UnidentifiedAtom<'a>)> { - let (header, value, space) = Property::read_body(self.space)?; + // SAFETY: The fact that this contains a valid property is guaranteed by this type. + let (header, value, space) = unsafe { Property::read_body(self.space) }?; self.space = space; - Some((header, UnidentifiedAtom::new(value))) + // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. + Some((header, unsafe { UnidentifiedAtom::new_unchecked(value) })) } } @@ -243,8 +245,12 @@ pub struct PropertyHeader { impl Property { /// Read the body of a property atom from a space. /// - /// This method assumes that the space actually contains the body of a property atom, without the header. It returns the property header, containing the key and optional context of the property, the body of the actual atom, and the space behind the atom. - fn read_body(space: Space) -> Option<(PropertyHeader, Space, Space)> { + /// It returns the property header, containing the key and optional context of the property, the body of the actual atom, and the remaining space behind the atom. + /// + /// # Safety + /// + /// The caller must ensure that the given Space actually contains a valid property. + unsafe fn read_body(space: Space) -> Option<(PropertyHeader, Space, Space)> { #[repr(C)] #[derive(Clone, Copy)] /// A custom version of the property body that does not include the value atom header. @@ -255,7 +261,7 @@ impl Property { context: u32, } - let (header, space) = space.split_type::()?; + let (header, space) = space.split_for_type::()?; let header = PropertyHeader { key: URID::try_from(header.key).ok()?, @@ -377,10 +383,10 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urids.object).unwrap(); + let space = Space::from_bytes(raw_space.as_ref()); + let (body, _) = unsafe { space.split_atom_body(urids.object) }.unwrap(); - let (header, iter) = Object::read(body, ()).unwrap(); + let (header, iter) = unsafe { Object::read(body, ()) }.unwrap(); assert_eq!(header.otype, object_type); assert_eq!(header.id, None); diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index 902572cd..d88b2196 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -49,7 +49,7 @@ pub trait ScalarAtom: UriBound { /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. fn read_scalar(body: Space) -> Option { - body.split_type::() + body.split_for_type::() .map(|(value, _)| *value) } @@ -73,7 +73,7 @@ where type WriteParameter = A::InternalType; type WriteHandle = &'a mut A::InternalType; - fn read(body: Space<'a>, _: ()) -> Option { + unsafe fn read(body: Space<'a>, _: ()) -> Option { ::read_scalar(body) } @@ -204,7 +204,7 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); + let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urid).unwrap(); assert_eq!(A::read(body, ()).unwrap(), value); } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index 2acb2e17..aaa1ba02 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -89,8 +89,8 @@ where type WriteParameter = TimeStampURID; type WriteHandle = SequenceWriter<'a, 'b>; - fn read(body: Space, bpm_urid: URID) -> Option { - let (header, body) = body.split_type::()?; + unsafe fn read(body: Space, bpm_urid: URID) -> Option { + let (header, body) = body.split_for_type::()?; let unit = if header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute } else { @@ -184,14 +184,14 @@ impl<'a> Iterator for SequenceIterator<'a> { type Item = (TimeStamp, UnidentifiedAtom<'a>); fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> { - let (raw_stamp, space) = self.space.split_type::()?; + let (raw_stamp, space) = self.space.split_for_type::()?; let stamp = match self.unit { TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) }, }; let (atom, space) = space.split_atom()?; self.space = space; - Some((stamp, UnidentifiedAtom::new(atom))) + Some((stamp, UnidentifiedAtom::new_unchecked(atom))) } } @@ -255,7 +255,7 @@ impl<'a, 'b> SequenceWriter<'a, 'b> { /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { - let data = atom.space.data()?; + let data = atom.space.as_bytes(); self.write_time_stamp(stamp)?; self.frame.write_raw(data, true).map(|_| ()) } @@ -338,7 +338,7 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); + let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.atom.sequence).unwrap(); let mut reader = Sequence::read(body, urids.units.beat).unwrap(); diff --git a/atom/src/space.rs b/atom/src/space.rs index b804582b..c0268479 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -13,15 +13,45 @@ use std::marker::Unpin; use std::mem::{size_of, size_of_val}; use urid::URID; -/// Specialized smart pointer to retrieve struct instances from a slice of memory. +#[inline] +fn pad_slice(data: &[u8]) -> Option<&[u8]> { + let start = data.as_ptr() as usize; + let padding = if start % 8 == 0 { 0 } else { 8 - start % 8 }; + + data.get(padding..) +} + +#[inline] +fn as_bytes(value: &T) -> &[u8] { + // SAFETY: any type can safely be transmuted to a byte slice + unsafe { + std::slice::from_raw_parts(value as *const T as *const u8, size_of_val(value)) + } +} + +#[inline] +fn as_bytes_mut(value: &mut T) -> &mut [u8] { + // SAFETY: any type can safely be transmuted to a byte slice + unsafe { + std::slice::from_raw_parts_mut(value as *mut T as *mut u8, size_of_val(value)) + } +} + +/// A 64-bit aligned slice of bytes that is designed to contain Atoms. /// /// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. #[derive(Clone, Copy)] -pub struct Space<'a> { - data: Option<&'a [u8]>, +pub struct Space { + data: [u8], } -impl<'a> Space<'a> { +impl Space { + /// Creates an empty Space. + #[inline] + pub const fn empty() -> &'static Space { + &Space { data: *&[][..] } + } + /// Create a new space from an atom pointer. /// /// The method creates a space that contains the atom as well as it's body. @@ -30,62 +60,84 @@ impl<'a> Space<'a> { /// /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> Self { - let size = atom.size as usize; + pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Self { let data = std::slice::from_raw_parts( atom as *const sys::LV2_Atom as *const u8, - size + size_of::(), + atom.size as usize + size_of::(), ); - Self::from_slice(data) + Self::from_bytes(data) + } + + /// Creates a new space from a slice of bytes. + /// + /// # Panics + /// + /// This method panics if the given slice's offset is not 64-bit aligned + /// (i.e. if it's pointer's value is not a multiple of 8 bytes). + /// + /// For a non-panicking version, see [`Space::try_from_bytes`]. + #[inline] + pub fn from_bytes(data: &[u8]) -> &Self { + Space::try_from_bytes(data).unwrap() + } + + /// Creates a new space from a slice of bytes. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned + /// (i.e. if it's pointer's value is not a multiple of 8 bytes). + /// + /// This is the non-panicking version of [`Space::from_bytes`]. + #[inline] + pub fn try_from_bytes(data: &[u8]) -> Option<&Self> { + if data.as_ptr() as usize % 8 != 0 { + return None; + } + + Some(&Space { data: *data }) } - /// Create a new space from a slice. + /// Creates a new space from a slice of bytes. /// - /// Since everything regarding atoms is 64-bit-aligned, this method panics if the data slice is not 64-bit-aligned. - pub fn from_slice(data: &'a [u8]) -> Self { - Space { data: Some(data) } + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned + /// (i.e. if it's pointer's value is not a multiple of 8 bytes). + /// + /// This is the non-panicking version of [`Space::from_bytes`]. + #[inline] + pub fn try_from_bytes_padded(data: &[u8]) -> Option<&Self> { + pad_slice(data).map(|data| &Self { data: *data }) } /// Try to retrieve a slice of bytes. /// /// This method basically splits off the lower part of the internal bytes slice and creates a new atom space pointer of the upper part. Since atoms have to be 64-bit-aligned, there might be a padding space that's neither in the lower nor in the upper part. - pub fn split_raw(self, size: usize) -> Option<(&'a [u8], Self)> { - let data = self.data?; - - if size > data.len() { + pub fn split_bytes_at(&self, size: usize) -> Option<(&[u8], &Space)> { + if size > self.data.len() { return None; } - let (lower_space, upper_space) = data.split_at(size); - - // Apply padding. - 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 }; + let (lower_space, upper_space) = self.data.split_at(size); + let upper_space = Self::try_from_bytes_padded(upper_space).unwrap_or(Space::empty()); Some((lower_space, upper_space)) } /// Try to retrieve space. /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. - 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_at(&self, size: usize) -> Option<(&Space, &Space)> { + self.split_bytes_at(size) + .map(|(data, rhs)| (Self::from_bytes(data), rhs)) } /// Try to retrieve a reference to a sized type. /// /// This method retrieves a slice of memory using the [`split_raw`](#method.split_raw) method and interprets it as an instance of `T`. Since there is no way to check that the memory is actually a valid instance of `T`, this method is unsafe. The second return value is the space after the instance of `T`. - pub fn split_type(self) -> Option<(&'a T, Self)> - where - T: Unpin + Copy + Send + Sync + Sized + 'static, + pub unsafe fn split_for_type<'a, T>(&'a self) -> Option<(&'a T, &'a Space)> where T: 'a, { - self.split_raw(size_of::()) + self.split_bytes_at(size_of::()) .map(|(data, rhs)| (unsafe { &*(data.as_ptr() as *const T) }, rhs)) } @@ -94,9 +146,9 @@ impl<'a> Space<'a> { /// This method assumes that the space contains an atom and retrieves the space occupied by the atom, including the atom header. The second return value is the rest of the space behind the atom. /// /// The difference to [`split_atom_body`](#method.split_atom_body) is that the returned space contains the header of the atom and that the type of the atom is not checked. - pub fn split_atom(self) -> Option<(Self, Self)> { - let (header, _) = self.split_type::()?; - self.split_space(size_of::() + header.size as usize) + pub unsafe fn split_atom(&self) -> Option<(&Self, &Self)> { + let (header, _) = self.split_for_type::()?; + self.split_at(size_of::() + header.size as usize) } /// Try to retrieve the body of the atom. @@ -104,52 +156,27 @@ impl<'a> Space<'a> { /// This method retrieves the header of the atom. If the type URID in the header matches the given URID, it returns the body of the atom. If not, it returns `None`. The first space is the body of the atom, the second one is the space behind it. /// /// The difference to [`split_atom`](#method.split_atom) is that the returned space does not contain the header of the atom and that the type of the atom is checked. - pub fn split_atom_body(self, urid: URID) -> Option<(Self, Self)> { - let (header, space) = self.split_type::()?; + pub unsafe fn split_atom_body(&self, urid: URID) -> Option<(&Self, &Self)> { + let (header, space) = self.split_for_type::()?; if header.type_ != urid.get() { return None; } - space.split_space(header.size as usize) + space.split_at(header.size as usize) } /// Create a space from a reference. - pub fn from_reference(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) } - } - - /// Concatenate two spaces. /// - /// There are situations where a space is split too often and you might want to reunite these two adjacent spaces. This method checks if the given spaces are adjacent, which means that the left space has to end exactly where the right one begins. In this case, the concatenated space is returned. If this is not the case, this method returns `None`. - pub fn concat(lhs: Self, rhs: Self) -> Option { - 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 - } + /// # Panics + /// + /// This method panics if the given instance pointer isn't 64-bit aligned. + pub fn from_ref(instance: &T) -> &Self { + Space::try_from_bytes_padded(as_bytes(instance)).unwrap() } /// Return the internal slice of the space. - pub fn data(&self) -> Option<&'a [u8]> { - self.data - } - - /// Return a mutable reference to the internal slice of the space. - pub fn mut_data(&mut self) -> &mut Option<&'a [u8]> { - &mut self.data + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.data } } @@ -162,12 +189,20 @@ pub trait MutSpace<'a> { /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. - fn allocate(&mut self, size: usize, apply_padding: bool) -> Option<(usize, &'a mut [u8])>; + fn allocate(&mut self, size: usize) -> Option<(usize, &'a mut Space)>; + + fn allocate_unpadded(&mut self, size: usize) -> Option<&'a mut [u8]>; + + fn write_raw_unpadded(&mut self, data: &[u8]) -> Option<&'a mut [u8]> { + let space = self.allocate_unpadded(data.len())?; + space.copy_from_slice(data); + Some(space) + } /// Try to write data to the internal data slice. /// /// The method allocates a slice with the [`allocate`](#tymethod.allocate) method and copies the data to the slice. - fn write_raw(&mut self, data: &[u8], apply_padding: bool) -> Option<&'a mut [u8]> { + fn write_raw(&mut self, data: &[u8]) -> Option<&'a mut Space> { self.allocate(data.len(), apply_padding).map(|(_, space)| { space.copy_from_slice(data); space @@ -314,7 +349,7 @@ impl SpaceElement { /// /// // Retrieving a continuos vector with the written data and verifying it's contents. /// let written_data: Vec = element.to_vec(); -/// let atom = UnidentifiedAtom::new(Space::from_slice(written_data.as_ref())); +/// let atom = unsafe { UnidentifiedAtom::new_unchecked(Space::from_slice(written_data.as_ref())) }; /// assert_eq!(42, atom.read(urids.int, ()).unwrap()); /// ``` pub struct SpaceHead<'a> { @@ -432,13 +467,13 @@ mod tests { *(ptr) = 0x42424242; } - let space = Space::from_slice(vector.as_slice()); - let (lower_space, space) = space.split_raw(128).unwrap(); + let space = Space::from_bytes(vector.as_slice()); + let (lower_space, space) = space.split_bytes_at(128).unwrap(); for i in 0..128 { assert_eq!(lower_space[i], i as u8); } - let (integer, _) = space.split_type::().unwrap(); + let (integer, _) = unsafe { space.split_for_type::() }.unwrap(); assert_eq!(*integer, 0x42424242); } @@ -455,39 +490,23 @@ mod tests { 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(); + let space = Space::from_ref(data.as_ref()); + let (atom, _) = space.split_atom().unwrap(); + let (body, _) = atom.split_atom_body(urid).unwrap(); + let body = body.as_bytes(); - assert_eq!(size_of::(), size_of_val(body)); - assert_eq!(42, unsafe { *(body.as_ptr() as *const i32) }); + assert_eq!(size_of::(), 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::().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() - ); + let space = Space::from_ref(&value); + assert_eq!(value, *unsafe { space.split_for_type::() }.unwrap().0); } fn test_mut_space<'a, S: MutSpace<'a>>(mut space: S) { diff --git a/atom/src/string.rs b/atom/src/string.rs index 1c9b39c2..9114a7ee 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -56,8 +56,8 @@ where type WriteParameter = LiteralInfo; type WriteHandle = StringWriter<'a, 'b>; - fn read(body: Space<'a>, _: ()) -> Option<(LiteralInfo, &'a str)> { - let (header, body) = body.split_type::()?; + unsafe fn read(body: Space<'a>, _: ()) -> Option<(LiteralInfo, &'a str)> { + let (header, body) = body.split_for_type::()?; let info = if header.lang != 0 && header.datatype == 0 { LiteralInfo::Language(URID::new(header.lang)?) } else if header.lang == 0 && header.datatype != 0 { @@ -65,7 +65,7 @@ where } else { return None; }; - let data = body.data()?; + let data = body.as_bytes(); std::str::from_utf8(&data[0..data.len() - 1]) .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) .ok() @@ -108,10 +108,10 @@ where type WriteParameter = (); type WriteHandle = StringWriter<'a, 'b>; - fn read(body: Space<'a>, _: ()) -> Option<&'a str> { - body.data() - .and_then(|data| std::str::from_utf8(data).ok()) - .map(|string| &string[..string.len() - 1]) // removing the null-terminator + unsafe fn read(body: Space<'a>, _: ()) -> Option<&'a str> { + let data = body.as_bytes(); + let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator + Some(core::str::from_utf8(rust_str_bytes).ok()?) } fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { @@ -212,7 +212,7 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); + let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.atom.literal).unwrap(); let (info, text) = Literal::read(body, ()).unwrap(); @@ -252,7 +252,7 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); + let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.string).unwrap(); let string = String::read(body, ()).unwrap(); assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1); diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 001c0be8..100fec32 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -52,7 +52,7 @@ where type WriteParameter = (); type WriteHandle = TupleWriter<'a, 'b>; - fn read(body: Space<'a>, _: ()) -> Option> { + unsafe fn read(body: Space<'a>, _: ()) -> Option> { Some(TupleIterator { space: body }) } @@ -74,7 +74,7 @@ impl<'a> Iterator for TupleIterator<'a> { fn next(&mut self) -> Option> { let (atom, space) = self.space.split_atom()?; self.space = space; - Some(UnidentifiedAtom::new(atom)) + Some(UnidentifiedAtom::new_unchecked(atom)) } } @@ -160,7 +160,7 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); + let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.tuple).unwrap(); let items: Vec = Tuple::read(body, ()).unwrap().collect(); assert_eq!(items[0].read(urids.vector, urids.int).unwrap(), [17; 9]); diff --git a/atom/src/vector.rs b/atom/src/vector.rs index 10643fd2..d79d85fc 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -58,8 +58,8 @@ where type WriteParameter = URID; type WriteHandle = VectorWriter<'a, 'b, C>; - fn read(body: Space<'a>, child_urid: URID) -> Option<&'a [C::InternalType]> { - let (header, body) = body.split_type::()?; + unsafe fn read(body: Space<'a>, child_urid: URID) -> Option<&'a [C::InternalType]> { + let (header, body) = body.split_for_type::()?; if header.child_type != child_urid || header.child_size as usize != size_of::() @@ -67,7 +67,7 @@ where return None; } - let data = body.data()?; + let data = body.as_bytes(); assert_eq!(data.len() % size_of::(), 0); let children_count = data.len() / size_of::(); @@ -186,7 +186,7 @@ mod tests { // reading { - let space = Space::from_slice(raw_space.as_ref()); + let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.vector).unwrap(); let children: &[i32] = Vector::::read(body, urids.int).unwrap(); diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 1b47fbb0..1522ee48 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -167,7 +167,7 @@ fn main() { } // Asserting the result - let (sequence, _) = Space::from_slice(output_atom_space.as_ref()) + let (sequence, _) = Space::from_bytes(output_atom_space.as_ref()) .split_atom_body(urids.atom.sequence) .unwrap(); for (stamp, atom) in Sequence::read(sequence, urids.units.beat).unwrap() { diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 5307c4d7..fe4c4312 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -25,7 +25,7 @@ where type WriteHandle = FramedMutSpace<'a, 'b>; fn read(body: Space<'a>, _: ()) -> Option<&'a [u8]> { - body.data() + body.as_bytes() } fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index c88dd2b4..3aa0c7c6 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -31,7 +31,7 @@ where fn read(space: Space<'a>, _: ()) -> Option> { space - .data() + .as_bytes() .and_then(|bytes| wmidi::MidiMessage::try_from(bytes).ok()) } @@ -145,7 +145,7 @@ mod tests { // reading { - let space = Space::from_reference(raw_space.as_ref()); + let space = Space::from_ref(raw_space.as_ref()); let message = WMidiEvent::read(space.split_atom_body(urid).unwrap().0, ()).unwrap(); assert_eq!(message, reference_message); @@ -179,7 +179,7 @@ mod tests { // reading { - let space = Space::from_reference(raw_space.as_ref()); + let space = Space::from_ref(raw_space.as_ref()); let message = WMidiEvent::read(space.split_atom_body(urid).unwrap().0, ()).unwrap(); assert_eq!( diff --git a/state/src/raw.rs b/state/src/raw.rs index ea0abc56..bacf74ef 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -54,12 +54,12 @@ impl<'a> StoreHandle<'a> { ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); - let space = Space::from_slice(space.as_ref()); + let space = Space::from_bytes(space.as_ref()); let (header, data) = space - .split_type::() + .split_for_type::() .ok_or(StateErr::BadData)?; let data = data - .split_raw(header.size as usize) + .split_bytes_at(header.size as usize) .map(|(data, _)| data) .ok_or(StateErr::BadData)?; @@ -182,7 +182,7 @@ impl<'a> RetrieveHandle<'a> { return Err(StateErr::NoProperty); }; - Ok(StatePropertyReader::new(type_, Space::from_slice(space))) + Ok(StatePropertyReader::new(type_, Space::from_bytes(space))) } } @@ -316,7 +316,7 @@ mod tests { } 3 => { assert_eq!(urids.vector::(), *type_); - let space = Space::from_slice(value.as_slice()); + let space = Space::from_bytes(value.as_slice()); let data = Vector::read(space, urids.int).unwrap(); assert_eq!([1, 2, 3, 4], data); } From a464d4d66adc150df71f398e363e0b2c860d491b Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sat, 7 Aug 2021 02:14:01 +0200 Subject: [PATCH 02/54] Even more wip --- atom/src/chunk.rs | 35 +-- atom/src/lib.rs | 10 +- atom/src/object.rs | 50 ++-- atom/src/port.rs | 16 +- atom/src/scalar.rs | 14 +- atom/src/sequence.rs | 36 ++- atom/src/space.rs | 496 ++------------------------------- atom/src/space/allocatable.rs | 72 +++++ atom/src/space/atom.rs | 43 +++ atom/src/space/list.rs | 110 ++++++++ atom/src/space/space.rs | 284 +++++++++++++++++++ atom/src/string.rs | 42 +-- atom/src/tuple.rs | 22 +- atom/src/vector.rs | 25 +- atom/tests/atom_integration.rs | 8 +- midi/src/raw.rs | 6 +- midi/src/wmidi_binding.rs | 22 +- state/src/raw.rs | 12 +- 18 files changed, 674 insertions(+), 629 deletions(-) create mode 100644 atom/src/space/allocatable.rs create mode 100644 atom/src/space/atom.rs create mode 100644 atom/src/space/list.rs create mode 100644 atom/src/space/space.rs diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 50d1cc8d..7ed963ce 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -15,7 +15,7 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let in_chunk: &[u8] = ports.input.read(urids.chunk, ()).unwrap(); -//! let mut out_chunk: FramedMutSpace = ports.output.init(urids.chunk, ()).unwrap(); +//! let mut out_chunk: AtomSpace = ports.output.init(urids.chunk, ()).unwrap(); //! //! out_chunk.write_raw(in_chunk, false).unwrap(); //! } @@ -44,13 +44,13 @@ where type ReadParameter = (); type ReadHandle = &'a [u8]; type WriteParameter = (); - type WriteHandle = FramedMutSpace<'a, 'b>; + type WriteHandle = AtomSpace<'a>; - unsafe fn read(space: Space<'a>, _: ()) -> Option<&'a [u8]> { + unsafe fn read(space: &'a Space, _: ()) -> Option<&'a [u8]> { Some(space.as_bytes()) } - fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpace<'a>, _: ()) -> Option> { Some(frame) } } @@ -73,28 +73,19 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) - .init(urids.chunk, ()) - .unwrap(); - - for (i, value) in writer - .allocate(SLICE_LENGTH - 1, false) - .map(|(_, data)| data) - .unwrap() - .into_iter() - .enumerate() - { + let mut space = raw_space.as_mut(); + let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); + let data = writer.allocate_unaligned(SLICE_LENGTH - 1).unwrap(); + + for (i, value) in data.into_iter().enumerate() { *value = i as u8; } - (&mut writer as &mut dyn MutSpace) - .write(&41u8, false) - .unwrap(); + + space::write_value(&mut space, 41u8).unwrap(); } // verifying { - let raw_space = raw_space.as_ref(); let (atom, data) = raw_space.split_at(size_of::()); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; @@ -109,9 +100,9 @@ mod tests { // reading { - let space = Space::from_ref(raw_space.as_ref()); + let space = Space::from_bytes(&raw_space); - let data = Chunk::read(space.split_atom_body(urids.chunk).unwrap().0, ()).unwrap(); + let data = unsafe { Chunk::read(space.split_atom_body(urids.chunk).unwrap().0, ()) }.unwrap(); assert_eq!(data.len(), SLICE_LENGTH); for (i, value) in data.iter().enumerate() { diff --git a/atom/src/lib.rs b/atom/src/lib.rs index c8927480..6e7e8109 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -86,7 +86,7 @@ pub mod prelude { pub use port::AtomPort; pub use scalar::{AtomURID, Bool, Double, Float, Int, Long}; pub use sequence::{Sequence, TimeStamp, TimeStampURID}; - pub use space::{FramedMutSpace, MutSpace, Space}; + pub use space::{AtomSpace, AllocateSpace, Space}; pub use string::{Literal, LiteralInfo, String}; pub use tuple::Tuple; pub use vector::Vector; @@ -160,7 +160,7 @@ where /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option; + unsafe fn read(body: &'a Space, parameter: Self::ReadParameter) -> Option; /// Initialize the body of the atom. /// @@ -171,7 +171,7 @@ where /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. fn init( - frame: FramedMutSpace<'a, 'b>, + frame: AtomSpace<'a>, parameter: Self::WriteParameter, ) -> Option; } @@ -181,7 +181,7 @@ where /// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. #[derive(Clone, Copy)] pub struct UnidentifiedAtom<'a> { - space: Space<'a>, + space: &'a Space, } impl<'a> UnidentifiedAtom<'a> { @@ -191,7 +191,7 @@ impl<'a> UnidentifiedAtom<'a> { /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body.x #[inline] - pub unsafe fn new_unchecked(space: Space<'a>) -> Self { + pub unsafe fn new_unchecked(space: &'a Space) -> Self { Self { space } } diff --git a/atom/src/object.rs b/atom/src/object.rs index 74be1887..abfee15c 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -100,9 +100,9 @@ where type ReadParameter = (); type ReadHandle = (ObjectHeader, ObjectReader<'a>); type WriteParameter = ObjectHeader; - type WriteHandle = ObjectWriter<'a, 'b>; + type WriteHandle = ObjectWriter<'a>; - unsafe fn read(body: Space<'a>, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { + unsafe fn read(body: &'a Space, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { let (header, body) = body.split_for_type::()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), @@ -115,18 +115,14 @@ where } fn init( - mut frame: FramedMutSpace<'a, 'b>, + mut frame: AtomSpace<'a>, header: ObjectHeader, - ) -> Option> { + ) -> Option> { { - let frame = &mut frame as &mut dyn MutSpace; - frame.write( - &sys::LV2_Atom_Object_Body { - id: header.id.map(|urid| urid.get()).unwrap_or(0), - otype: header.otype.get(), - }, - true, - ); + space::write_value(&mut frame, sys::LV2_Atom_Object_Body { + id: header.id.map(URID::get).unwrap_or(0), + otype: header.otype.get(), + }); } Some(ObjectWriter { frame }) } @@ -153,12 +149,12 @@ where type WriteHandle = >::WriteHandle; #[allow(clippy::unit_arg)] - unsafe fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option { + unsafe fn read(body: &'a Space, parameter: Self::ReadParameter) -> Option { Object::read(body, parameter) } fn init( - frame: FramedMutSpace<'a, 'b>, + frame: AtomSpace<'a>, parameter: Self::WriteParameter, ) -> Option { Object::init(frame, parameter) @@ -169,7 +165,7 @@ where /// /// Each iteration item is the header of the property, as well as the space occupied by the value atom. You can use normal `read` methods on the returned space. pub struct ObjectReader<'a> { - space: Space<'a>, + space: &'a Space, } impl<'a> Iterator for ObjectReader<'a> { @@ -187,11 +183,11 @@ impl<'a> Iterator for ObjectReader<'a> { /// Writing handle for object properties. /// /// This handle is a safeguard to assure that a object is always a series of properties. -pub struct ObjectWriter<'a, 'b> { - frame: FramedMutSpace<'a, 'b>, +pub struct ObjectWriter<'a> { + frame: AtomSpace<'a>, } -impl<'a, 'b> ObjectWriter<'a, 'b> { +impl<'a> ObjectWriter<'a> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. @@ -203,7 +199,7 @@ impl<'a, 'b> ObjectWriter<'a, 'b> { parameter: A::WriteParameter, ) -> Option { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; - (&mut self.frame as &mut dyn MutSpace).init(child_urid, parameter) + (&mut self.frame as &mut dyn AllocateSpace).init(child_urid, parameter) } /// Initialize a new property. @@ -218,7 +214,7 @@ impl<'a, 'b> ObjectWriter<'a, 'b> { parameter: A::WriteParameter, ) -> Option { Property::write_header::(&mut self.frame, key, None)?; - (&mut self.frame as &mut dyn MutSpace).init(child_urid, parameter) + (&mut self.frame as &mut dyn AllocateSpace).init(child_urid, parameter) } } @@ -250,7 +246,7 @@ impl Property { /// # Safety /// /// The caller must ensure that the given Space actually contains a valid property. - unsafe fn read_body(space: Space) -> Option<(PropertyHeader, Space, Space)> { + unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, &Space, &Space)> { #[repr(C)] #[derive(Clone, Copy)] /// A custom version of the property body that does not include the value atom header. @@ -275,13 +271,13 @@ impl Property { /// Write out the header of a property atom. /// /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. - fn write_header( - space: &mut dyn MutSpace, + fn write_header<'a, K: ?Sized, C: ?Sized>( + space: &mut impl AllocateSpace<'a>, key: URID, context: Option>, ) -> Option<()> { - space.write(&key.get(), true)?; - space.write(&context.map(|urid| urid.get()).unwrap_or(0), false)?; + space::write_value(space, key.get())?; + space::write_value(space, context.map(URID::get).unwrap_or(0))?; Some(()) } } @@ -316,8 +312,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let frame = FramedMutSpace::new(&mut space as &mut dyn MutSpace, urids.object).unwrap(); + let mut space = raw_space.as_mut(); + let frame = AtomSpace::write_new(&mut space as &mut dyn AllocateSpace, urids.object).unwrap(); let mut writer = Object::init( frame, ObjectHeader { diff --git a/atom/src/port.rs b/atom/src/port.rs index d8f3dd67..b7a82857 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -33,12 +33,12 @@ use urid::URID; /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to read atoms. pub struct PortReader<'a> { - space: Space<'a>, + space: &'a Space, } impl<'a> PortReader<'a> { /// Create a new port reader. - fn new(space: Space<'a>) -> Self { + fn new(space: &'a Space) -> Self { Self { space } } @@ -60,13 +60,13 @@ impl<'a> PortReader<'a> { /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. pub struct PortWriter<'a> { - space: RootMutSpace<'a>, + space: &'a mut Space, has_been_written: bool, } impl<'a> PortWriter<'a> { /// Create a new port writer. - fn new(space: RootMutSpace<'a>) -> Self { + fn new(space: &'a mut Space) -> Self { Self { space, has_been_written: false, @@ -87,7 +87,7 @@ impl<'a> PortWriter<'a> { ) -> Option { if !self.has_been_written { self.has_been_written = true; - (&mut self.space as &mut dyn MutSpace).init(urid, parameter) + crate::space::init_atom(self.space, urid, parameter) } else { None } @@ -113,7 +113,7 @@ impl PortType for AtomPort { #[inline] unsafe fn output_from_raw(pointer: NonNull, _sample_count: u32) -> PortWriter<'static> { - let space = RootMutSpace::from_atom(pointer.cast().as_mut()); + let space = Space::from_atom_mut(pointer.cast().as_mut()); PortWriter::new(space) } } @@ -136,8 +136,8 @@ mod tests { // writing a chunk to indicate the size of the space. { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) + let mut space = raw_space.as_mut(); + let mut writer = (&mut space as &mut dyn AllocateSpace) .init(urids.chunk, ()) .unwrap(); writer diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index d88b2196..4e5d830a 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -57,10 +57,10 @@ pub trait ScalarAtom: UriBound { /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. fn write_scalar<'a, 'b>( - mut frame: FramedMutSpace<'a, 'b>, + mut frame: AtomSpace<'a>, value: Self::InternalType, ) -> Option<&'a mut Self::InternalType> { - (&mut frame as &mut dyn MutSpace).write(&value, true) + (&mut frame as &mut dyn AllocateSpace).write(&value, true) } } @@ -73,12 +73,12 @@ where type WriteParameter = A::InternalType; type WriteHandle = &'a mut A::InternalType; - unsafe fn read(body: Space<'a>, _: ()) -> Option { + unsafe fn read(body: &'a Space, _: ()) -> Option { ::read_scalar(body) } fn init( - frame: FramedMutSpace<'a, 'b>, + frame: AtomSpace<'a>, value: A::InternalType, ) -> Option<&'a mut A::InternalType> { ::write_scalar(frame, value) @@ -181,14 +181,14 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - (&mut space as &mut dyn MutSpace).init(urid, value).unwrap(); + let mut space = raw_space.as_mut(); + crate::space::init_atom(&mut space, urid, value).unwrap(); } // verifying { /// Generic version of the scalar atom structs. - #[repr(C)] + #[repr(C, align(8))] struct Scalar { atom: sys::LV2_Atom, body: B, diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index aaa1ba02..d630dfdd 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -87,9 +87,9 @@ where type ReadParameter = URID; type ReadHandle = SequenceIterator<'a>; type WriteParameter = TimeStampURID; - type WriteHandle = SequenceWriter<'a, 'b>; + type WriteHandle = SequenceWriter<'a>; - unsafe fn read(body: Space, bpm_urid: URID) -> Option { + unsafe fn read(body: &Space, bpm_urid: URID) -> Option { let (header, body) = body.split_for_type::()?; let unit = if header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute @@ -100,11 +100,11 @@ where } fn init( - mut frame: FramedMutSpace<'a, 'b>, + mut frame: AtomSpace<'a>, unit: TimeStampURID, - ) -> Option> { + ) -> Option> { { - let frame = &mut frame as &mut dyn MutSpace; + let frame = &mut frame as &mut dyn AllocateSpace; let header = sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), @@ -170,7 +170,7 @@ impl TimeStamp { /// An iterator over all events in a sequence. pub struct SequenceIterator<'a> { - space: Space<'a>, + space: &'a Space, unit: TimeStampUnit, } @@ -196,13 +196,13 @@ impl<'a> Iterator for SequenceIterator<'a> { } /// The writing handle for sequences. -pub struct SequenceWriter<'a, 'b> { - frame: FramedMutSpace<'a, 'b>, +pub struct SequenceWriter<'a> { + frame: AtomSpace<'a>, unit: TimeStampUnit, last_stamp: Option, } -impl<'a, 'b> SequenceWriter<'a, 'b> { +impl<'a, 'b> SequenceWriter<'a> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: @@ -231,9 +231,9 @@ impl<'a, 'b> SequenceWriter<'a, 'b> { } }; self.last_stamp = Some(stamp); - (&mut self.frame as &mut dyn MutSpace) - .write(&raw_stamp, true) - .map(|_| ()) + space::write_value(&mut self.frame, raw_stamp)?; + + Some(()) } /// Initialize an event. @@ -246,7 +246,7 @@ impl<'a, 'b> SequenceWriter<'a, 'b> { parameter: A::WriteParameter, ) -> Option { self.write_time_stamp(stamp)?; - (&mut self.frame as &mut dyn MutSpace).init(urid, parameter) + (&mut self.frame as &mut dyn AllocateSpace).init(urid, parameter) } /// Forward an unidentified atom to the sequence. @@ -283,13 +283,9 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) - .init( - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame), - ) - .unwrap(); + let mut space = raw_space.as_mut(); + let mut writer = space::init_atom(space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); + writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) .unwrap(); diff --git a/atom/src/space.rs b/atom/src/space.rs index c0268479..cb1526e5 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -1,454 +1,14 @@ //! Smart pointers with safe atom reading and writing methods. -//! -//! # Safety -//! -//! The only unsafe things that happen in this module is when either space is created from a reference to a `sys::LV2_Atom` and when space is re-interpreted as typed data. -//! -//! In the first case, we have to trust that the space behind the atom header is accessible since we have no way to check whether it is or not. Therefore, we have to assume that it is sound. -//! -//! The second case is sound since a) the data is contained in a slice and therefore is accessible, b) generic type parameter bounds assure that the type is plain-old-data and c) 64-bit padding is assured. -use crate::Atom; -use std::cell::Cell; -use std::marker::Unpin; -use std::mem::{size_of, size_of_val}; -use urid::URID; -#[inline] -fn pad_slice(data: &[u8]) -> Option<&[u8]> { - let start = data.as_ptr() as usize; - let padding = if start % 8 == 0 { 0 } else { 8 - start % 8 }; +mod list; +mod space; +mod allocatable; +mod atom; - data.get(padding..) -} - -#[inline] -fn as_bytes(value: &T) -> &[u8] { - // SAFETY: any type can safely be transmuted to a byte slice - unsafe { - std::slice::from_raw_parts(value as *const T as *const u8, size_of_val(value)) - } -} - -#[inline] -fn as_bytes_mut(value: &mut T) -> &mut [u8] { - // SAFETY: any type can safely be transmuted to a byte slice - unsafe { - std::slice::from_raw_parts_mut(value as *mut T as *mut u8, size_of_val(value)) - } -} - -/// A 64-bit aligned slice of bytes that is designed to contain Atoms. -/// -/// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. -#[derive(Clone, Copy)] -pub struct Space { - data: [u8], -} - -impl Space { - /// Creates an empty Space. - #[inline] - pub const fn empty() -> &'static Space { - &Space { data: *&[][..] } - } - - /// Create a new space from an atom pointer. - /// - /// The method creates a space that contains the atom as well as it's body. - /// - /// # Safety - /// - /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. - #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Self { - let data = std::slice::from_raw_parts( - atom as *const sys::LV2_Atom as *const u8, - atom.size as usize + size_of::(), - ); - Self::from_bytes(data) - } - - /// Creates a new space from a slice of bytes. - /// - /// # Panics - /// - /// This method panics if the given slice's offset is not 64-bit aligned - /// (i.e. if it's pointer's value is not a multiple of 8 bytes). - /// - /// For a non-panicking version, see [`Space::try_from_bytes`]. - #[inline] - pub fn from_bytes(data: &[u8]) -> &Self { - Space::try_from_bytes(data).unwrap() - } - - /// Creates a new space from a slice of bytes. - /// - /// # Errors - /// - /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned - /// (i.e. if it's pointer's value is not a multiple of 8 bytes). - /// - /// This is the non-panicking version of [`Space::from_bytes`]. - #[inline] - pub fn try_from_bytes(data: &[u8]) -> Option<&Self> { - if data.as_ptr() as usize % 8 != 0 { - return None; - } - - Some(&Space { data: *data }) - } - - /// Creates a new space from a slice of bytes. - /// - /// # Errors - /// - /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned - /// (i.e. if it's pointer's value is not a multiple of 8 bytes). - /// - /// This is the non-panicking version of [`Space::from_bytes`]. - #[inline] - pub fn try_from_bytes_padded(data: &[u8]) -> Option<&Self> { - pad_slice(data).map(|data| &Self { data: *data }) - } - - /// Try to retrieve a slice of bytes. - /// - /// This method basically splits off the lower part of the internal bytes slice and creates a new atom space pointer of the upper part. Since atoms have to be 64-bit-aligned, there might be a padding space that's neither in the lower nor in the upper part. - pub fn split_bytes_at(&self, size: usize) -> Option<(&[u8], &Space)> { - if size > self.data.len() { - return None; - } - - let (lower_space, upper_space) = self.data.split_at(size); - let upper_space = Self::try_from_bytes_padded(upper_space).unwrap_or(Space::empty()); - Some((lower_space, upper_space)) - } - - /// Try to retrieve space. - /// - /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. - pub fn split_at(&self, size: usize) -> Option<(&Space, &Space)> { - self.split_bytes_at(size) - .map(|(data, rhs)| (Self::from_bytes(data), rhs)) - } - - /// Try to retrieve a reference to a sized type. - /// - /// This method retrieves a slice of memory using the [`split_raw`](#method.split_raw) method and interprets it as an instance of `T`. Since there is no way to check that the memory is actually a valid instance of `T`, this method is unsafe. The second return value is the space after the instance of `T`. - pub unsafe fn split_for_type<'a, T>(&'a self) -> Option<(&'a T, &'a Space)> where T: 'a, - { - self.split_bytes_at(size_of::()) - .map(|(data, rhs)| (unsafe { &*(data.as_ptr() as *const T) }, rhs)) - } - - /// Try to retrieve the space occupied by an atom. - /// - /// This method assumes that the space contains an atom and retrieves the space occupied by the atom, including the atom header. The second return value is the rest of the space behind the atom. - /// - /// The difference to [`split_atom_body`](#method.split_atom_body) is that the returned space contains the header of the atom and that the type of the atom is not checked. - pub unsafe fn split_atom(&self) -> Option<(&Self, &Self)> { - let (header, _) = self.split_for_type::()?; - self.split_at(size_of::() + header.size as usize) - } - - /// Try to retrieve the body of the atom. - /// - /// This method retrieves the header of the atom. If the type URID in the header matches the given URID, it returns the body of the atom. If not, it returns `None`. The first space is the body of the atom, the second one is the space behind it. - /// - /// The difference to [`split_atom`](#method.split_atom) is that the returned space does not contain the header of the atom and that the type of the atom is checked. - pub unsafe fn split_atom_body(&self, urid: URID) -> Option<(&Self, &Self)> { - let (header, space) = self.split_for_type::()?; - if header.type_ != urid.get() { - return None; - } - space.split_at(header.size as usize) - } - - /// Create a space from a reference. - /// - /// # Panics - /// - /// This method panics if the given instance pointer isn't 64-bit aligned. - pub fn from_ref(instance: &T) -> &Self { - Space::try_from_bytes_padded(as_bytes(instance)).unwrap() - } - - /// Return the internal slice of the space. - #[inline] - pub fn as_bytes(&self) -> &[u8] { - &self.data - } -} - -/// A smart pointer that writes atom data to an internal slice. -/// -/// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. -pub trait MutSpace<'a> { - /// Try to allocate memory on the internal data slice. - /// - /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. - /// - /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. - fn allocate(&mut self, size: usize) -> Option<(usize, &'a mut Space)>; - - fn allocate_unpadded(&mut self, size: usize) -> Option<&'a mut [u8]>; - - fn write_raw_unpadded(&mut self, data: &[u8]) -> Option<&'a mut [u8]> { - let space = self.allocate_unpadded(data.len())?; - space.copy_from_slice(data); - Some(space) - } - - /// Try to write data to the internal data slice. - /// - /// The method allocates a slice with the [`allocate`](#tymethod.allocate) method and copies the data to the slice. - fn write_raw(&mut self, data: &[u8]) -> Option<&'a mut Space> { - self.allocate(data.len(), apply_padding).map(|(_, space)| { - space.copy_from_slice(data); - space - }) - } -} - -/// A `MutSpace` that directly manages it's own internal data slice. -pub struct RootMutSpace<'a> { - space: Cell>, - allocated_bytes: usize, -} - -impl<'a> RootMutSpace<'a> { - /// Create new space from an atom. - /// - /// The method creates a space that contains the atom as well as it's body. - /// - /// # Safety - /// - /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe. - 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::(), - ); - Self::new(space) - } - - /// Create a new instance. - /// - /// This method takes the space reserved for the value and interprets it as a slice of bytes (`&mut [u8]`). - 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)) - } -} - -/// Linked list element for dynamic atom writing. -/// -/// This struct works in conjunction with [`SpaceHead`](struct.SpaceHead.html) to provide a way to write atoms to dynamically allocated memory. -pub struct SpaceElement { - next: Option<(Box, Box<[u8]>)>, -} - -impl Default for SpaceElement { - fn default() -> Self { - Self { next: None } - } -} - -impl SpaceElement { - /// Append an element to the list. - /// - /// If this is the last element of the list, allocate a slice of the required length and append a new element to the list. If not, do nothing and return `None`. - 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, Box<[u8]>)| { - (new_element.as_mut(), new_data.as_mut()) - }) - } - - /// Create a vector containing the data from all elements following this one. - pub fn to_vec(&self) -> Vec { - self.iter() - .map(|slice| slice.iter()) - .flatten() - .cloned() - .collect() - } - - /// Return an iterator over the chunks of all elements following this one. - pub fn iter(&self) -> impl Iterator { - std::iter::successors(self.next.as_ref(), |element| element.0.next.as_ref()) - .map(|(_, data)| data.as_ref()) - } -} - -/// A mutable space that dynamically allocates memory. -/// -/// This space uses a linked list of [`SpaceElement`s](struct.SpaceElement.html) to allocate memory. Every time `allocate` is called, a new element is appended to the list and a new byte slice is created. -/// -/// In order to use this space and retrieve the written data once it was written, you create a `SpaceElement` and create a new head with it. Then, you use the head like any other `MutSpace` and when you're done, you retrieve the written data by either calling [`to_vec`](struct.SpaceElement.html#method.to_vec) or [`iter`](struct.SpaceElement.html#iter). -/// -/// # Usage example -/// -/// ``` -/// # use lv2_core::prelude::*; -/// # use lv2_atom::prelude::*; -/// # use lv2_atom::space::*; -/// # use urid::*; -/// # use std::pin::Pin; -/// # let map = HashURIDMapper::new(); -/// // URID cache creation is omitted. -/// let urids: AtomURIDCollection = map.populate_collection().unwrap(); -/// -/// // Creating the first element in the list and the writing head. -/// let mut element = SpaceElement::default(); -/// let mut head = SpaceHead::new(&mut element); -/// -/// // Writing an integer. -/// (&mut head as &mut dyn MutSpace).init(urids.int, 42).unwrap(); -/// -/// // Retrieving a continuos vector with the written data and verifying it's contents. -/// let written_data: Vec = element.to_vec(); -/// let atom = unsafe { UnidentifiedAtom::new_unchecked(Space::from_slice(written_data.as_ref())) }; -/// assert_eq!(42, atom.read(urids.int, ()).unwrap()); -/// ``` -pub struct SpaceHead<'a> { - element: Option<&'a mut SpaceElement>, - allocated_space: usize, -} - -impl<'a> SpaceHead<'a> { - /// Create a new head that references the given element. - 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)) - } -} - -/// A `MutSpace` that notes the amount of allocated space in an atom header. -pub struct FramedMutSpace<'a, 'b> { - atom: &'a mut sys::LV2_Atom, - parent: &'b mut dyn MutSpace<'a>, -} - -impl<'a, 'b> FramedMutSpace<'a, 'b> { - /// Create a new framed space with the given parent and type URID. - pub fn new(parent: &'b mut dyn MutSpace<'a>, urid: URID) -> Option { - 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 { - /// Write a sized object to the space. - /// - /// If `apply_padding` is `true`, the method will assure that the written instance is 64-bit-aligned. - pub fn write(&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::(); - 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) }) - } - - /// Initialize a new atom in the space. - pub fn init<'c, A: Atom<'a, 'c>>( - &'c mut self, - urid: URID, - parameter: A::WriteParameter, - ) -> Option { - let new_space = FramedMutSpace::new(self, urid)?; - A::init(new_space, parameter) - } -} +pub use space::Space; +pub use list::{SpaceList, SpaceHead}; +pub use allocatable::*; +pub use atom::AtomSpace; #[cfg(test)] mod tests { @@ -509,7 +69,7 @@ mod tests { assert_eq!(value, *unsafe { space.split_for_type::() }.unwrap().0); } - fn test_mut_space<'a, S: MutSpace<'a>>(mut space: S) { + fn test_mut_space<'a, S: AllocateSpace<'a>>(mut space: S) { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); @@ -524,13 +84,11 @@ mod tests { } let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; - let written_atom = (&mut space as &mut dyn MutSpace) - .write(&test_atom, true) - .unwrap(); + let written_atom = crate::space::write_value(&mut space, test_atom).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) } + let created_space = unsafe { Space::from_atom_mut(written_atom) } .space .take() .unwrap(); @@ -541,7 +99,7 @@ mod tests { assert_eq!(created_space.len(), size_of::() + 42); let mut atom_frame = - FramedMutSpace::new(&mut space as &mut dyn MutSpace, urids.chunk).unwrap(); + AtomSpace::write_new(&mut space as &mut dyn AllocateSpace, urids.chunk).unwrap(); let mut test_data: Vec = vec![0; 24]; for i in 0..test_data.len() { @@ -550,15 +108,14 @@ mod tests { 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); + 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(); + let written_atom = crate::space::write_value(&mut atom_frame, test_atom).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, + atom_frame.atom().size as usize, test_data.len() + size_of_val(&test_atom) ); } @@ -567,19 +124,19 @@ mod tests { 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 { + let frame = unsafe { std::slice::from_raw_parts_mut( (&mut memory).as_mut_ptr() as *mut u8, MEMORY_SIZE * size_of::(), ) - }); + }; test_mut_space(frame); } #[test] fn test_space_head() { - let mut space = SpaceElement::default(); + let mut space = SpaceList::default(); let head = SpaceHead::new(&mut space); test_mut_space(head); } @@ -597,15 +154,12 @@ mod tests { // writing { - let mut root: RootMutSpace = RootMutSpace::new(raw_space); + let mut root = raw_space; let mut frame = - FramedMutSpace::new(&mut root as &mut dyn MutSpace, URID::<()>::new(1).unwrap()) + AtomSpace::write_new(&mut root, URID::<()>::new(1).unwrap()) .unwrap(); - { - let frame = &mut frame as &mut dyn MutSpace; - frame.write::(&42, true).unwrap(); - frame.write::(&17, true).unwrap(); - } + crate::space::write_value(&mut frame, 42u32).unwrap(); + crate::space::write_value(&mut frame, 17u32).unwrap(); } // checking @@ -631,10 +185,8 @@ mod tests { 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(); + let mut root_space = &mut raw_space[3..]; + crate::space::write_value(&mut root_space, 42u8).unwrap(); } assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref()); diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs new file mode 100644 index 00000000..94e6dae9 --- /dev/null +++ b/atom/src/space/allocatable.rs @@ -0,0 +1,72 @@ +use crate::Atom; +use urid::URID; +use crate::space::{AtomSpace, Space}; + +use core::mem::size_of_val; +use std::mem::MaybeUninit; + +/// A smart pointer that writes atom data to an internal slice. +/// +/// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. +pub trait AllocateSpace<'a> { + /// Try to allocate memory on the internal data slice. + /// + /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. + /// + /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. + fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]>; + + fn as_bytes(&self) -> &[u8]; +} + +impl<'a> AllocateSpace<'a> for &'a mut [u8] { + #[inline] + fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { + if size > self.space.len() { + return None + } + + let (allocated, remaining) = self.space.split_at_mut(size); + self.space = remaining; + Some(allocated) + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + self + } +} + +#[inline] +pub fn allocate<'a, T>(space: &mut impl AllocateSpace<'a>, size: usize) -> Option<&'a mut Space> { + let required_padding = crate::space::space::required_padding_for_type::(space.as_bytes()); + let raw = space.allocate_unaligned(size + required_padding)?; + + Space::try_align_from_bytes_mut(raw) +} + +#[inline] +pub fn init_atom<'a, 'b, A: Atom<'a, 'b>>(space: &mut impl AllocateSpace<'b>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { + let space = AtomSpace::write_new(space, atom_type)?; + A::init(space, write_parameter) +} + +#[inline] +pub fn write_bytes<'a>(space: &mut impl AllocateSpace<'a>, bytes: &[u8]) -> Option<&'a mut [u8]> { + let (_, space) = space.allocate_unaligned(bytes.len())?; + space.as_bytes_mut().copy_from_slice(bytes); + Some(space) +} + +#[inline] +pub fn write_value<'a, T>(space: &mut impl AllocateSpace<'a>, value: T) -> Option<&'a mut T> + where T: Copy + Sized + 'static +{ + let space = allocate(space, size_of_val(&value))?; + // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. + let space = unsafe { space.as_uninit_mut_unchecked() }; + *space = MaybeUninit::new(value); + + // SAFETY: the MaybeUninit has now been properly initialized. + Some (unsafe { &mut *(space.as_mut_ptr()) }) +} diff --git a/atom/src/space/atom.rs b/atom/src/space/atom.rs new file mode 100644 index 00000000..ef1dc959 --- /dev/null +++ b/atom/src/space/atom.rs @@ -0,0 +1,43 @@ +use crate::space::AllocateSpace; +use urid::URID; + +/// A `MutSpace` that notes the amount of allocated space in an atom header. +pub struct AtomSpace<'a> { + atom: &'a mut sys::LV2_Atom, + parent: &'a mut dyn AllocateSpace<'a>, +} + +impl<'a> AtomSpace<'a> { + #[inline] + pub fn atom(&self) -> &'a mut sys::LV2_Atom { + self.atom + } + + /// Create a new framed space with the given parent and type URID. + pub fn write_new(parent: &mut dyn AllocateSpace<'a>, urid: URID) -> Option { + let atom = sys::LV2_Atom { + size: 0, + type_: urid.get(), + }; + + let atom: &'a mut sys::LV2_Atom = crate::space::write_value(parent, atom)?; + Some(Self { atom, parent }) + } +} + +impl<'a, 'b> AllocateSpace<'a> for AtomSpace<'a> { + #[inline] + fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { + let result = self.parent.allocate_unaligned(size); + if result.is_some() { + self.atom.size += size as u32; + } + + result + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + self.parent.as_bytes() + } +} \ No newline at end of file diff --git a/atom/src/space/list.rs b/atom/src/space/list.rs new file mode 100644 index 00000000..41a45dcd --- /dev/null +++ b/atom/src/space/list.rs @@ -0,0 +1,110 @@ +use crate::space::AllocateSpace; + +/// Linked list element for dynamic atom writing. +/// +/// This struct works in conjunction with [`SpaceHead`](struct.SpaceHead.html) to provide a way to write atoms to dynamically allocated memory. +pub struct SpaceList { + next: Option<(Box, Box<[u8]>)>, +} + +impl Default for SpaceList { + fn default() -> Self { + Self { next: None } + } +} + +impl SpaceList { + /// Append an element to the list. + /// + /// If this is the last element of the list, allocate a slice of the required length and append a new element to the list. If not, do nothing and return `None`. + 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, Box<[u8]>)| { + (new_element.as_mut(), new_data.as_mut()) + }) + } + + /// Create a vector containing the data from all elements following this one. + pub fn to_vec(&self) -> Vec { + self.iter() + .map(|slice| slice.iter()) + .flatten() + .copied() + .collect() + } + + /// Return an iterator over the chunks of all elements following this one. + pub fn iter(&self) -> impl Iterator { + std::iter::successors(self.next.as_ref(), |element| element.0.next.as_ref()) + .map(|(_, data)| data.as_ref()) + } +} + +/// A mutable space that dynamically allocates memory. +/// +/// This space uses a linked list of [`SpaceElement`s](struct.SpaceElement.html) to allocate memory. Every time `allocate` is called, a new element is appended to the list and a new byte slice is created. +/// +/// In order to use this space and retrieve the written data once it was written, you create a `SpaceElement` and create a new head with it. Then, you use the head like any other `MutSpace` and when you're done, you retrieve the written data by either calling [`to_vec`](struct.SpaceElement.html#method.to_vec) or [`iter`](struct.SpaceElement.html#iter). +/// +/// # Usage example +/// +/// ``` +/// # use lv2_core::prelude::*; +/// # use lv2_atom::prelude::*; +/// # use lv2_atom::space::*; +/// # use urid::*; +/// # use std::pin::Pin; +/// # let map = HashURIDMapper::new(); +/// // URID cache creation is omitted. +/// let urids: AtomURIDCollection = map.populate_collection().unwrap(); +/// +/// // Creating the first element in the list and the writing head. +/// let mut element = SpaceList::default(); +/// let mut head = SpaceHead::new(&mut element); +/// +/// // Writing an integer. +/// (&mut head as &mut dyn MutSpace).init(urids.int, 42).unwrap(); +/// +/// // Retrieving a continuos vector with the written data and verifying it's contents. +/// let written_data: Vec = element.to_vec(); +/// let atom = unsafe { UnidentifiedAtom::new_unchecked(Space::from_slice(written_data.as_ref())) }; +/// assert_eq!(42, atom.read(urids.int, ()).unwrap()); +/// ``` +pub struct SpaceHead<'a> { + element: Option<&'a mut SpaceList>, + allocated_space: usize, +} + +impl<'a> SpaceHead<'a> { + /// Create a new head that references the given element. + pub fn new(element: &'a mut SpaceList) -> Self { + Self { + element: Some(element), + allocated_space: 0, + } + } +} + +impl<'a> AllocateSpace<'a> for SpaceHead<'a> { + #[inline] + fn allocate_unaligned(&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) + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + &[] + } +} diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs new file mode 100644 index 00000000..8ea6abf0 --- /dev/null +++ b/atom/src/space/space.rs @@ -0,0 +1,284 @@ +use core::mem::{align_of, size_of, size_of_val}; +use std::mem::MaybeUninit; +use std::marker::PhantomData; + +#[inline] +pub(crate) fn required_padding_for_alignment(data: &[u8], alignment: usize) -> usize { + let start = data.as_ptr() as usize; + if start % alignment == 0 { 0 } else { alignment - start % alignment } +} + +#[inline] +pub(crate) fn required_padding_for_type(data: &[u8]) -> usize { + required_padding_for_alignment(data, align_of::()) +} + +#[inline] +pub(crate) fn as_bytes(value: &T) -> &[u8] { + // SAFETY: any type can safely be transmuted to a byte slice + unsafe { + std::slice::from_raw_parts(value as *const T as *const u8, size_of_val(value)) + } +} + +/// An aligned slice of bytes that is designed to contain a given type `T` (by default, Atoms). +/// +/// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. +pub struct Space { + _type: PhantomData, + data: [u8] +} + +impl Space { + /// Creates an empty Space. + #[inline] + pub const fn empty() -> &'static Space { + &Space { _type: PhantomData, data: *&[][..] } + } + + /// Creates an empty mutable Space. + #[inline] + pub const fn empty_mut() -> &'static mut Space { + &mut Space { _type: PhantomData, data: *&mut [][..] } + } + + /// Creates a new space from a slice of bytes, without checking for padding correctness. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. + /// Otherwise, atom reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. + /// + /// For a safe, checked version, see [`Space::from_bytes`]. + // NOTE: This method will always be used internally instead of the constructor, to make sure that + // the unsafety is explicit and accounted for. + #[inline(always)] + pub unsafe fn from_bytes_unchecked(data: &[u8]) -> &Space { + &Space { _type: PhantomData, data: *data } + } + + /// Creates a new mutable space from a slice of bytes, without checking for padding correctness. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. + /// Otherwise, atom reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. + /// + /// For a safe, checked version, see [`Space::from_bytes`]. + // NOTE: This method will always be used internally instead of the constructor, to make sure that + // the unsafety is explicit and accounted for. + #[inline(always)] + pub unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut Space { + &mut Space { _type: PhantomData, data: *data } + } + + /// Create a new space from an atom pointer. + /// + /// The method creates a space that contains the atom as well as its body. + /// + /// # Safety + /// + /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Space { + let data = std::slice::from_raw_parts( + atom as *const sys::LV2_Atom as *const u8, + atom.size as usize + size_of::(), + ); + Self::from_bytes(data) + } + + /// Create a new mutable space from an atom pointer. + /// + /// The method creates a space that contains the atom as well as its body. + /// + /// # Safety + /// + /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub unsafe fn from_atom_mut(atom: &mut sys::LV2_Atom) -> &mut Space { + let data = std::slice::from_raw_parts_mut( + atom as *mut sys::LV2_Atom as *mut u8, + atom.size as usize + size_of::(), + ); + Self::from_bytes_mut(data) + } + + /// Creates a new space from a slice of bytes. + /// + /// # Panics + /// + /// This method panics if the given slice's offset is not 64-bit aligned + /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). + /// + /// For a non-panicking version, see [`Space::try_from_bytes`]. + #[inline] + pub fn from_bytes(data: &[u8]) -> &Self { + Space::try_from_bytes(data).unwrap() + } + + /// Creates a new space from a slice of bytes. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned + /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). + /// + /// This is the non-panicking version of [`Space::from_bytes`]. + #[inline] + pub fn try_from_bytes(data: &[u8]) -> Option<&Self> { + if data.as_ptr() as usize % align_of::() != 0 { + return None; + } + + // SAFETY: We just checked above that the pointer is correctly aligned + Some(unsafe { Space::from_bytes_unchecked(data) }) + } + + /// Creates a new space from a slice of bytes, aligning it if necessary. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's is too small to contain + /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + #[inline] + pub fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { + // SAFETY: The slice was just aligned by align_slice + data.get(required_padding_for_type::(data)..).map(|data| unsafe { Space::from_bytes_unchecked(data) }) + } + + /// Creates a new space from a slice of bytes, aligning it if necessary. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's is too small to contain + /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + #[inline] + pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + // SAFETY: The slice was just aligned by align_slice_mut + data.get_mut(required_padding_for_type::(data)..).map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) + } + + #[inline] + pub fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { + if mid > self.data.len() { + return None; + } + + let (start, end) = self.data.split_at(mid); + // SAFETY: Because this data was the start of an existing Space, it was aligned already. + let start = unsafe { Self::from_bytes_unchecked(start) }; + + Some((start, end)) + } + + #[inline] + pub fn split_bytes_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut [u8])> { + if mid > self.data.len() { + return None; + } + + let (start, end) = self.data.split_at_mut(mid); + // SAFETY: Because this data was the start of an existing Space, it was aligned already. + let start = unsafe { Self::from_bytes_unchecked_mut(start) }; + + Some((start, end)) + } + + /// Try to retrieve space. + /// + /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. + #[inline] + pub fn split_at(&self, mid: usize) -> Option<(&Self, &Self)> { + let (start, end) = self.split_bytes_at(mid)?; + let end = Self::try_align_from_bytes(end).unwrap_or(Space::empty()); + + Some((start, end)) + } + + #[inline] + pub fn split_for_value(&self) -> Option<(&MaybeUninit, &Self)> { + let (value, rest) = self.split_at(size_of::())?; + let value = value.as_uninit()?; + + Some((value, rest)) + } + + #[inline] + pub fn split_at_mut(&mut self, mid: usize) -> Option<(&mut Space, &mut Space)> { + let (start, end) = self.split_bytes_at_mut(mid)?; + let end = Self::try_align_from_bytes_mut(end).unwrap_or(Space::empty_mut()); + + Some((start, end)) + } + + /// Create a space from a reference. + /// + /// # Panics + /// + /// This method panics if the given instance pointer isn't 64-bit aligned. + #[inline] + pub fn from_ref(instance: &T) -> &Self { + // SAFETY: references are always aligned. + unsafe { Space::from_bytes_unchecked(as_bytes(instance)) } + } + + #[inline] + pub fn realigned(&self) -> Option<&Space> { + Space::::try_from_bytes(self.as_bytes()) + } + + /// Return the internal slice of the space. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.data + } + + /// Return the internal slice of the space. + #[inline] + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.data + } + + #[inline] + pub unsafe fn read(&self) -> Option<&T> { + // SAFETY: The caller has to ensure this slice actually points to initialized memory. + Some(unsafe { &*(self.as_uninit()?) }) + } + + #[inline] + pub unsafe fn read_as(&self) -> Option { + self.realigned()?.read() + } + + /// Gets a `T`-aligned pointer to the contents. + /// + /// This methods returns [`None`](Option::None) if the space is not large enough for a value of type `T`. + #[inline] + pub fn as_uninit(&self) -> Option<&MaybeUninit> { + if self.data.len() < size_of::() { + return None; + } + + // SAFETY: We just checked that the space was actually big enough. + Some(unsafe { self.as_uninit_unchecked() }) + } + + /// Gets a `T`-aligned pointer to the contents, but without checking that there actually is enough space to hold `T`. + #[inline] + pub unsafe fn as_uninit_unchecked(&self) -> &MaybeUninit { + // SAFETY: The caller has to ensure that the space is actually big enough. + unsafe { &*(self.data.as_ptr() as *const MaybeUninit) } + } + + /// Gets a `T`-aligned mutable pointer to the contents, but without checking that there actually is enough space to hold `T`. + #[inline] + pub unsafe fn as_uninit_mut_unchecked(&mut self) -> &mut MaybeUninit { + // SAFETY: The caller has to ensure that the space is actually big enough. + unsafe { &mut *(self.data.as_ptr() as *mut MaybeUninit) } + } +} + +impl Space { + +} \ No newline at end of file diff --git a/atom/src/string.rs b/atom/src/string.rs index 9114a7ee..cfa90823 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -54,9 +54,9 @@ where type ReadParameter = (); type ReadHandle = (LiteralInfo, &'a str); type WriteParameter = LiteralInfo; - type WriteHandle = StringWriter<'a, 'b>; + type WriteHandle = StringWriter<'a>; - unsafe fn read(body: Space<'a>, _: ()) -> Option<(LiteralInfo, &'a str)> { + unsafe fn read(body: &'a Space, _: ()) -> Option<(LiteralInfo, &'a str)> { let (header, body) = body.split_for_type::()?; let info = if header.lang != 0 && header.datatype == 0 { LiteralInfo::Language(URID::new(header.lang)?) @@ -72,9 +72,9 @@ where .map(|string| (info, string)) } - fn init(mut frame: FramedMutSpace<'a, 'b>, info: LiteralInfo) -> Option> { - (&mut frame as &mut dyn MutSpace).write( - &match info { + fn init(mut frame: AtomSpace<'a>, info: LiteralInfo) -> Option> { + crate::space::write_value(&mut frame, + match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), datatype: 0, @@ -83,8 +83,7 @@ where lang: 0, datatype: datatype.get(), }, - }, - true, + } )?; Some(StringWriter { frame }) } @@ -106,25 +105,25 @@ where type ReadParameter = (); type ReadHandle = &'a str; type WriteParameter = (); - type WriteHandle = StringWriter<'a, 'b>; + type WriteHandle = StringWriter<'a>; - unsafe fn read(body: Space<'a>, _: ()) -> Option<&'a str> { + unsafe fn read(body: &'a Space, _: ()) -> Option<&'a str> { let data = body.as_bytes(); let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator Some(core::str::from_utf8(rust_str_bytes).ok()?) } - fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpace<'a>, _: ()) -> Option> { Some(StringWriter { frame }) } } /// Handle to append strings to a string or literal. -pub struct StringWriter<'a, 'b> { - frame: FramedMutSpace<'a, 'b>, +pub struct StringWriter<'a> { + frame: AtomSpace<'a>, } -impl<'a, 'b> StringWriter<'a, 'b> { +impl<'a, 'b> StringWriter<'a> { /// Append a string. /// /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. @@ -137,10 +136,11 @@ impl<'a, 'b> StringWriter<'a, 'b> { } } -impl<'a, 'b> Drop for StringWriter<'a, 'b> { +impl<'a, 'b> Drop for StringWriter<'a> { fn drop(&mut self) { // Null terminator. - (&mut self.frame as &mut dyn MutSpace).write(&0u8, false); + // FIXME: this seems unsafe if the value could not be written for some reason. + let _ = self::write_value(&mut self.frame, 0u8); } } @@ -175,8 +175,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) + let mut space = raw_space.as_mut(); + let mut writer = (&mut space as &mut dyn AllocateSpace) .init( urids.atom.literal, LiteralInfo::Language(urids.german.into_general()), @@ -214,7 +214,7 @@ mod tests { { let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.atom.literal).unwrap(); - let (info, text) = Literal::read(body, ()).unwrap(); + let (info, text) = unsafe { Literal::read(body, ()) }.unwrap(); assert_eq!(info, LiteralInfo::Language(urids.german.into_general())); assert_eq!(text, SAMPLE0.to_owned() + SAMPLE1); @@ -230,8 +230,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) + let mut space = raw_space.as_mut(); + let mut writer = (&mut space as &mut dyn AllocateSpace) .init(urids.string, ()) .unwrap(); writer.append(SAMPLE0).unwrap(); @@ -254,7 +254,7 @@ mod tests { { let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.string).unwrap(); - let string = String::read(body, ()).unwrap(); + let string = unsafe { String::read(body, ()) }.unwrap(); assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1); } } diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 100fec32..673b8789 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -50,13 +50,13 @@ where type ReadParameter = (); type ReadHandle = TupleIterator<'a>; type WriteParameter = (); - type WriteHandle = TupleWriter<'a, 'b>; + type WriteHandle = TupleWriter<'a>; - unsafe fn read(body: Space<'a>, _: ()) -> Option> { + unsafe fn read(body: &'a Space, _: ()) -> Option> { Some(TupleIterator { space: body }) } - fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpace<'a>, _: ()) -> Option> { Some(TupleWriter { frame }) } } @@ -65,7 +65,7 @@ where /// /// The item of this iterator is simply the space a single atom occupies. pub struct TupleIterator<'a> { - space: Space<'a>, + space: &'a Space, } impl<'a> Iterator for TupleIterator<'a> { @@ -74,23 +74,23 @@ impl<'a> Iterator for TupleIterator<'a> { fn next(&mut self) -> Option> { let (atom, space) = self.space.split_atom()?; self.space = space; - Some(UnidentifiedAtom::new_unchecked(atom)) + Some(unsafe { UnidentifiedAtom::new_unchecked(atom) }) } } /// The writing handle to add atoms to a tuple. -pub struct TupleWriter<'a, 'b> { - frame: FramedMutSpace<'a, 'b>, +pub struct TupleWriter<'a> { + frame: AtomSpace<'a>, } -impl<'a, 'b> TupleWriter<'a, 'b> { +impl<'a, 'b> TupleWriter<'a> { /// Initialize a new tuple element. pub fn init<'c, A: Atom<'a, 'c>>( &'c mut self, child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { - (&mut self.frame as &mut dyn MutSpace).init(child_urid, child_parameter) + (&mut self.frame as &mut dyn AllocateSpace).init(child_urid, child_parameter) } } @@ -110,8 +110,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) + let mut space =raw_space.as_mut(); + let mut writer = (&mut space as &mut dyn AllocateSpace) .init(urids.tuple, ()) .unwrap(); { diff --git a/atom/src/vector.rs b/atom/src/vector.rs index d79d85fc..ffd7a981 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -56,9 +56,9 @@ where type ReadParameter = URID; type ReadHandle = &'a [C::InternalType]; type WriteParameter = URID; - type WriteHandle = VectorWriter<'a, 'b, C>; + type WriteHandle = VectorWriter<'a, C>; - unsafe fn read(body: Space<'a>, child_urid: URID) -> Option<&'a [C::InternalType]> { + unsafe fn read(body: &'a Space, child_urid: URID) -> Option<&'a [C::InternalType]> { let (header, body) = body.split_for_type::()?; if header.child_type != child_urid @@ -79,14 +79,14 @@ where } fn init( - mut frame: FramedMutSpace<'a, 'b>, + mut frame: AtomSpace<'a>, child_urid: URID, - ) -> Option> { + ) -> Option> { let body = sys::LV2_Atom_Vector_Body { child_type: child_urid.get(), child_size: size_of::() as u32, }; - (&mut frame as &mut dyn MutSpace).write(&body, false)?; + space::write_value(&mut frame, body)?; Some(VectorWriter { frame, @@ -98,15 +98,16 @@ where /// Handle to append elements to a vector. /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. -pub struct VectorWriter<'a, 'b, A: ScalarAtom> { - frame: FramedMutSpace<'a, 'b>, +pub struct VectorWriter<'a, A: ScalarAtom> { + frame: AtomSpace<'a>, type_: PhantomData, } -impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, 'b, A> { +impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, A> { /// Push a single value to the vector. + #[inline] pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { - (&mut self.frame as &mut dyn MutSpace).write(&child, false) + space::write_value(&mut self.frame, child) } /// Append a slice of undefined memory to the vector. @@ -155,8 +156,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) + let mut space = raw_space.as_mut(); + let mut writer = (&mut space as &mut dyn AllocateSpace) .init(urids.vector(), urids.int) .unwrap(); writer.append(&[42; CHILD_COUNT - 1]); @@ -188,7 +189,7 @@ mod tests { { let space = Space::from_bytes(raw_space.as_ref()); let (body, _) = space.split_atom_body(urids.vector).unwrap(); - let children: &[i32] = Vector::::read(body, urids.int).unwrap(); + let children: &[i32] = unsafe { Vector::::read(body, urids.int) }.unwrap(); assert_eq!(children.len(), CHILD_COUNT); for i in 0..children.len() - 1 { diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 1522ee48..95c96830 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -100,8 +100,8 @@ fn main() { // Preparing the input atom. let mut input_atom_space: Box<[u8]> = Box::new([0; 256]); { - let mut space = RootMutSpace::new(input_atom_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace) + let mut space = SpaceWriter::new(input_atom_space.as_mut()); + let mut writer = (&mut space as &mut dyn AllocateSpace) .init( urids.atom.sequence, TimeStampURID::Frames(urids.units.frame), @@ -121,8 +121,8 @@ fn main() { // preparing the output atom. let mut output_atom_space: Box<[u8]> = Box::new([0; 256]); { - let mut space = RootMutSpace::new(output_atom_space.as_mut()); - (&mut space as &mut dyn MutSpace) + let mut space = SpaceWriter::new(output_atom_space.as_mut()); + (&mut space as &mut dyn AllocateSpace) .init(urids.atom.chunk, ()) .unwrap() .allocate(256 - size_of::(), false) diff --git a/midi/src/raw.rs b/midi/src/raw.rs index fe4c4312..65727473 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -22,13 +22,13 @@ where type ReadParameter = (); type ReadHandle = &'a [u8]; type WriteParameter = (); - type WriteHandle = FramedMutSpace<'a, 'b>; + type WriteHandle = AtomSpace<'a, 'b>; - fn read(body: Space<'a>, _: ()) -> Option<&'a [u8]> { + fn read(body: &'a Space, _: ()) -> Option<&'a [u8]> { body.as_bytes() } - fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpace<'a, 'b>, _: ()) -> Option> { Some(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 3aa0c7c6..b45989ab 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -29,13 +29,13 @@ where type WriteParameter = wmidi::MidiMessage<'static>; type WriteHandle = (); - fn read(space: Space<'a>, _: ()) -> Option> { + fn read(space: &'a Space, _: ()) -> Option> { space .as_bytes() .and_then(|bytes| wmidi::MidiMessage::try_from(bytes).ok()) } - fn init(mut frame: FramedMutSpace<'a, 'b>, message: wmidi::MidiMessage<'b>) -> Option<()> { + fn init(mut frame: AtomSpace<'a, 'b>, message: wmidi::MidiMessage<'b>) -> Option<()> { frame .allocate(message.bytes_size(), false) .and_then(|(_, space)| message.copy_to_slice(space).ok()) @@ -63,11 +63,11 @@ where type WriteParameter = (); type WriteHandle = Writer<'a, 'b>; - fn read(space: Space<'a>, _: ()) -> Option> { + fn read(space: &'a Space, _: ()) -> Option> { WMidiEvent::read(space, ()) } - fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpace<'a, 'b>, _: ()) -> Option> { let mut writer = Writer { frame }; writer.write::(&0xf0); Some(writer) @@ -80,7 +80,7 @@ where /// /// The "start of system exclusive" status byte is written by [`SystemExclusiveWMidiEvent::init`](struct.SystemExclusiveWMidiEvent.html#method.init) method and the "end of system exclusive" status byte is written when the writer is dropped. pub struct Writer<'a, 'b> { - frame: FramedMutSpace<'a, 'b>, + frame: AtomSpace<'a, 'b>, } impl<'a, 'b> Writer<'a, 'b> { @@ -96,7 +96,7 @@ impl<'a, 'b> Writer<'a, 'b> { where T: Unpin + Copy + Send + Sync + Sized + 'static, { - (&mut self.frame as &mut dyn MutSpace).write(instance, false) + (&mut self.frame as &mut dyn AllocateSpace).write(instance, false) } } @@ -109,7 +109,7 @@ impl<'a, 'b> Drop for Writer<'a, 'b> { #[cfg(test)] mod tests { use crate::wmidi_binding::*; - use atom::space::RootMutSpace; + use atom::space::SpaceWriter; use std::convert::TryFrom; use std::mem::size_of; use wmidi::*; @@ -125,8 +125,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - (&mut space as &mut dyn MutSpace) + let mut space = SpaceWriter::new(raw_space.as_mut()); + (&mut space as &mut dyn AllocateSpace) .init(urid, reference_message.clone()) .unwrap(); } @@ -161,8 +161,8 @@ mod tests { // writing { - let mut space = RootMutSpace::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn MutSpace).init(urid, ()).unwrap(); + let mut space = SpaceWriter::new(raw_space.as_mut()); + let mut writer = (&mut space as &mut dyn AllocateSpace).init(urid, ()).unwrap(); writer.write_raw(&[1, 2, 3, 4]); } diff --git a/state/src/raw.rs b/state/src/raw.rs index bacf74ef..72fdaf96 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -12,7 +12,7 @@ use urid::*; /// /// The written properties a buffered and flushed when requested. Create new properties by calling [`draft`](#method.draft) and write them like any other atom. Once you are done, you can commit your properties by calling [`commit_all`](#method.commit_all) or [`commit`](#method.commit). You have to commit manually: Uncommitted properties will be discarded when the handle is dropped. pub struct StoreHandle<'a> { - properties: HashMap, + properties: HashMap, store_fn: sys::LV2_State_Store_Function, handle: sys::LV2_State_Handle, lifetime: PhantomData<&'a mut c_void>, @@ -37,7 +37,7 @@ impl<'a> StoreHandle<'a> { pub fn draft(&mut self, property_key: URID) -> StatePropertyWriter { let property_key = property_key.into_general(); self.properties - .insert(property_key.into_general(), SpaceElement::default()); + .insert(property_key.into_general(), SpaceList::default()); StatePropertyWriter::new(SpaceHead::new( self.properties .get_mut(&property_key.into_general()) @@ -50,7 +50,7 @@ impl<'a> StoreHandle<'a> { store_fn: sys::LV2_State_Store_Function, handle: sys::LV2_State_Handle, key: URID, - space: SpaceElement, + space: SpaceList, ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); @@ -130,7 +130,7 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result { if !self.initialized { self.initialized = true; - (&mut self.head as &mut dyn MutSpace) + (&mut self.head as &mut dyn AllocateSpace) .init(urid, parameter) .ok_or(StateErr::Unknown) } else { @@ -191,12 +191,12 @@ impl<'a> RetrieveHandle<'a> { /// This handle contains the type and the data of a property retrieved from the [`RetrieveHandle`](struct.RetrieveHandle.html). pub struct StatePropertyReader<'a> { type_: URID, - body: Space<'a>, + body: &'a Space, } impl<'a> StatePropertyReader<'a> { /// Create a new reading handle with the given type and data. - pub fn new(type_: URID, body: Space<'a>) -> Self { + pub fn new(type_: URID, body: &'a Space) -> Self { Self { type_: type_.into_general(), body, From b8bad34c51ba3f7aab553729f6cdec0e19f34c93 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sat, 7 Aug 2021 23:01:22 +0200 Subject: [PATCH 03/54] Even more wip again --- atom/src/chunk.rs | 6 +- atom/src/lib.rs | 59 ++-- atom/src/object.rs | 36 +-- atom/src/port.rs | 19 +- atom/src/scalar.rs | 20 +- atom/src/sequence.rs | 88 +++--- atom/src/space.rs | 186 +------------ atom/src/space/allocatable.rs | 67 ++++- atom/src/space/atom.rs | 19 +- atom/src/space/list.rs | 5 + atom/src/space/space.rs | 475 ++++++++++++++++++++++++++------- atom/src/string.rs | 67 ++--- atom/src/tuple.rs | 55 ++-- atom/src/vector.rs | 57 ++-- atom/tests/atom_integration.rs | 40 ++- midi/src/raw.rs | 8 +- midi/src/wmidi_binding.rs | 87 +++--- state/src/raw.rs | 23 +- 18 files changed, 702 insertions(+), 615 deletions(-) diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 7ed963ce..997553e6 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -15,7 +15,7 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let in_chunk: &[u8] = ports.input.read(urids.chunk, ()).unwrap(); -//! let mut out_chunk: AtomSpace = ports.output.init(urids.chunk, ()).unwrap(); +//! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk, ()).unwrap(); //! //! out_chunk.write_raw(in_chunk, false).unwrap(); //! } @@ -44,13 +44,13 @@ where type ReadParameter = (); type ReadHandle = &'a [u8]; type WriteParameter = (); - type WriteHandle = AtomSpace<'a>; + type WriteHandle = AtomSpaceWriter<'b>; unsafe fn read(space: &'a Space, _: ()) -> Option<&'a [u8]> { Some(space.as_bytes()) } - fn init(frame: AtomSpace<'a>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { Some(frame) } } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 6e7e8109..cb37a715 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -86,7 +86,7 @@ pub mod prelude { pub use port::AtomPort; pub use scalar::{AtomURID, Bool, Double, Float, Int, Long}; pub use sequence::{Sequence, TimeStamp, TimeStampURID}; - pub use space::{AtomSpace, AllocateSpace, Space}; + pub use space::{AtomSpaceWriter, AllocateSpace, AtomSpace, Space}; pub use string::{Literal, LiteralInfo, String}; pub use tuple::Tuple; pub use vector::Vector; @@ -126,9 +126,7 @@ impl AtomURIDCollection { /// This is the foundation of this crate: Types that implement `Atom` define the reading and writing functions for an atom type. However, these types will never be constructed; They are only names to be used for generic type arguments. /// /// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom. -pub trait Atom<'a, 'b>: UriBound -where - 'a: 'b, +pub trait Atom<'read, 'write>: UriBound { /// The atom-specific parameter of the `read` function. /// @@ -138,7 +136,7 @@ where /// The return value of the `read` function. /// /// It may contain a reference to the atom and therefore may not outlive it. - type ReadHandle: 'a; + type ReadHandle: 'read; /// The atom-specific parameter of the `write` function. /// @@ -148,7 +146,7 @@ where /// The return value of the `write` function. /// /// It may contain a reference to a `MutSpace` and therefore may not outlive it. - type WriteHandle: 'b; + type WriteHandle: 'write; /// Reads the body of the atom. /// @@ -160,7 +158,7 @@ where /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: &'a Space, parameter: Self::ReadParameter) -> Option; + unsafe fn read(body: &'read Space, parameter: Self::ReadParameter) -> Option; /// Initialize the body of the atom. /// @@ -171,7 +169,7 @@ where /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. fn init( - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'write>, parameter: Self::WriteParameter, ) -> Option; } @@ -180,8 +178,9 @@ where /// /// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. #[derive(Clone, Copy)] +// TODO: refactor this so it directly contains the atom and is not a fat pointer to the space that has to be re-checked every time pub struct UnidentifiedAtom<'a> { - space: &'a Space, + space: &'a AtomSpace, } impl<'a> UnidentifiedAtom<'a> { @@ -191,7 +190,7 @@ impl<'a> UnidentifiedAtom<'a> { /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body.x #[inline] - pub unsafe fn new_unchecked(space: &'a Space) -> Self { + pub unsafe fn new_unchecked(space: &'a AtomSpace) -> Self { Self { space } } @@ -199,22 +198,42 @@ impl<'a> UnidentifiedAtom<'a> { /// /// To identify the atom, it's URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. pub fn read<'b, A: Atom<'a, 'b>>( - self, + &'a self, urid: URID, parameter: A::ReadParameter, ) -> Option { - // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - let (body, _rest) = unsafe { self.space.split_atom_body(urid) }?; - // SAFETY: the fact that this contains a valid instance of A is checked by split_atom_body above. + let (header, body) = self.header_and_body()?; + + if header.type_ != urid { + return None; + } + + // SAFETY: the fact that this contains a valid instance of A is checked above. unsafe { A::read(body, parameter) } } - /// Retrieve the type URID of the atom. - /// - /// This can be used to identify atoms without actually reading them. - pub fn type_urid(self) -> Option { + #[inline] + pub fn as_space(&self) -> &'a AtomSpace { + self.space + } + + #[inline] + pub fn header_and_body(&self) -> Option<(&'a sys::LV2_Atom, &'a Space)> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - unsafe { self.space.split_for_type::() } - .and_then(|(header, _)| URID::new(header.type_)) + let (header, body) = unsafe { self.space.split_for_value_unchecked() }?; + let body = body.slice(header.size as usize)?; + + Some((header, body)) + } + + #[inline] + pub fn header(&self) -> Option<&'a sys::LV2_Atom> { + // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. + unsafe { self.space.read_unchecked() } + } + + #[inline] + pub fn body(&self) -> Option<&'a Space> { + self.header_and_body().map(|(_header, body)| body) } } diff --git a/atom/src/object.rs b/atom/src/object.rs index abfee15c..075e3113 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -100,10 +100,10 @@ where type ReadParameter = (); type ReadHandle = (ObjectHeader, ObjectReader<'a>); type WriteParameter = ObjectHeader; - type WriteHandle = ObjectWriter<'a>; + type WriteHandle = ObjectWriter<'b>; unsafe fn read(body: &'a Space, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { - let (header, body) = body.split_for_type::()?; + let (header, body) = body.split_for_value_as_unchecked::()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), otype: URID::try_from(header.otype).ok()?, @@ -115,9 +115,9 @@ where } fn init( - mut frame: AtomSpace<'a>, + mut frame: AtomSpaceWriter<'b>, header: ObjectHeader, - ) -> Option> { + ) -> Option> { { space::write_value(&mut frame, sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), @@ -154,7 +154,7 @@ where } fn init( - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'b>, parameter: Self::WriteParameter, ) -> Option { Object::init(frame, parameter) @@ -173,10 +173,10 @@ impl<'a> Iterator for ObjectReader<'a> { fn next(&mut self) -> Option<(PropertyHeader, UnidentifiedAtom<'a>)> { // SAFETY: The fact that this contains a valid property is guaranteed by this type. - let (header, value, space) = unsafe { Property::read_body(self.space) }?; + let (header, atom, space) = unsafe { Property::read_body(self.space) }?; self.space = space; // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - Some((header, unsafe { UnidentifiedAtom::new_unchecked(value) })) + Some((header, atom)) } } @@ -184,22 +184,22 @@ impl<'a> Iterator for ObjectReader<'a> { /// /// This handle is a safeguard to assure that a object is always a series of properties. pub struct ObjectWriter<'a> { - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'a>, } impl<'a> ObjectWriter<'a> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. - pub fn init_with_context<'c, K: ?Sized, T: ?Sized, A: Atom<'a, 'c>>( - &'c mut self, + pub fn init_with_context<'c, K: ?Sized, T: ?Sized, A: Atom<'c, 'a>>( + &'a mut self, key: URID, context: URID, child_urid: URID, parameter: A::WriteParameter, ) -> Option { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; - (&mut self.frame as &mut dyn AllocateSpace).init(child_urid, parameter) + space::init_atom(&mut self.frame, child_urid, parameter) } /// Initialize a new property. @@ -207,14 +207,14 @@ impl<'a> ObjectWriter<'a> { /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init<'c, K: ?Sized, A: Atom<'a, 'c>>( - &'c mut self, + pub fn init<'c, K: ?Sized, A: Atom<'c, 'a>>( + &'a mut self, key: URID, child_urid: URID, parameter: A::WriteParameter, ) -> Option { - Property::write_header::(&mut self.frame, key, None)?; - (&mut self.frame as &mut dyn AllocateSpace).init(child_urid, parameter) + Property::write_header(&mut self.frame, key, None::>)?; + space::init_atom(&mut self.frame, child_urid, parameter) } } @@ -246,7 +246,7 @@ impl Property { /// # Safety /// /// The caller must ensure that the given Space actually contains a valid property. - unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, &Space, &Space)> { + unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, UnidentifiedAtom, &Space)> { #[repr(C)] #[derive(Clone, Copy)] /// A custom version of the property body that does not include the value atom header. @@ -257,7 +257,7 @@ impl Property { context: u32, } - let (header, space) = space.split_for_type::()?; + let (header, space) = space.split_for_value_as_unchecked::()?; let header = PropertyHeader { key: URID::try_from(header.key).ok()?, @@ -313,7 +313,7 @@ mod tests { // writing { let mut space = raw_space.as_mut(); - let frame = AtomSpace::write_new(&mut space as &mut dyn AllocateSpace, urids.object).unwrap(); + let frame = AtomSpaceWriter::write_new(&mut space as &mut dyn AllocateSpace, urids.object).unwrap(); let mut writer = Object::init( frame, ObjectHeader { diff --git a/atom/src/port.rs b/atom/src/port.rs index b7a82857..6d4e0b56 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -52,7 +52,8 @@ impl<'a> PortReader<'a> { urid: URID, parameter: A::ReadParameter, ) -> Option { - A::read(self.space.split_atom_body(urid)?.0, parameter) + // SAFETY: The port's space has been initialized by the host + unsafe { A::read(self.space.split_atom_body(urid)?.0, parameter) } } } @@ -60,7 +61,7 @@ impl<'a> PortReader<'a> { /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. pub struct PortWriter<'a> { - space: &'a mut Space, + space: &'a mut [u8], has_been_written: bool, } @@ -68,7 +69,7 @@ impl<'a> PortWriter<'a> { /// Create a new port writer. fn new(space: &'a mut Space) -> Self { Self { - space, + space: space.as_bytes_mut(), has_been_written: false, } } @@ -80,14 +81,16 @@ impl<'a> PortWriter<'a> { /// Please note that you can call this method once only, because any atoms written behind the first one will not be identified. /// /// This method returns `None` if the space of the port isn't big enough or if the method was called multiple times. - pub fn init<'b, A: crate::Atom<'a, 'b>>( - &'b mut self, + pub fn init<'b, 'read, 'write, A: crate::Atom<'read, 'write>>( + &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, parameter: A::WriteParameter, ) -> Option { if !self.has_been_written { self.has_been_written = true; - crate::space::init_atom(self.space, urid, parameter) + // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. + let space: &'write mut &'write mut [u8] = unsafe { ::core::mem::transmute::<_, &'write mut &'write mut [u8]>(&mut self.space) }; + crate::space::init_atom(space, urid, parameter) } else { None } @@ -107,13 +110,13 @@ impl PortType for AtomPort { #[inline] unsafe fn input_from_raw(pointer: NonNull, _sample_count: u32) -> PortReader<'static> { - let space = Space::from_atom(pointer.cast().as_ref()); + let space = Space::from_atom(*pointer.cast().as_ref()); PortReader::new(space) } #[inline] unsafe fn output_from_raw(pointer: NonNull, _sample_count: u32) -> PortWriter<'static> { - let space = Space::from_atom_mut(pointer.cast().as_mut()); + let space = Space::from_atom_mut(&mut *pointer.cast().as_ptr()); PortWriter::new(space) } } diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index 4e5d830a..289dcc0f 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -48,19 +48,16 @@ pub trait ScalarAtom: UriBound { /// Try to read the atom from a space. /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. - fn read_scalar(body: Space) -> Option { - body.split_for_type::() - .map(|(value, _)| *value) + #[inline] + unsafe fn read_scalar(body: &Space) -> Option { + body.read_as_unchecked().copied() } /// Try to write the atom into a space. /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. - fn write_scalar<'a, 'b>( - mut frame: AtomSpace<'a>, - value: Self::InternalType, - ) -> Option<&'a mut Self::InternalType> { - (&mut frame as &mut dyn AllocateSpace).write(&value, true) + fn write_scalar(mut frame: AtomSpaceWriter, value: Self::InternalType) -> Option<&mut Self::InternalType> { + space::write_value(&mut frame, value) } } @@ -71,16 +68,13 @@ where type ReadParameter = (); type ReadHandle = A::InternalType; type WriteParameter = A::InternalType; - type WriteHandle = &'a mut A::InternalType; + type WriteHandle = &'b mut A::InternalType; unsafe fn read(body: &'a Space, _: ()) -> Option { ::read_scalar(body) } - fn init( - frame: AtomSpace<'a>, - value: A::InternalType, - ) -> Option<&'a mut A::InternalType> { + fn init(frame: AtomSpaceWriter, value: A::InternalType) -> Option<&mut A::InternalType> { ::write_scalar(frame, value) } } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index d630dfdd..3dedf98d 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -80,17 +80,14 @@ unsafe impl UriBound for Sequence { const URI: &'static [u8] = sys::LV2_ATOM__Sequence; } -impl<'a, 'b> Atom<'a, 'b> for Sequence -where - 'a: 'b, -{ +impl<'a, 'b> Atom<'a, 'b> for Sequence { type ReadParameter = URID; type ReadHandle = SequenceIterator<'a>; type WriteParameter = TimeStampURID; - type WriteHandle = SequenceWriter<'a>; + type WriteHandle = SequenceWriter<'b>; unsafe fn read(body: &Space, bpm_urid: URID) -> Option { - let (header, body) = body.split_for_type::()?; + let (header, body) = body.split_for_value_as_unchecked::()?; let unit = if header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute } else { @@ -99,21 +96,16 @@ where Some(SequenceIterator { space: body, unit }) } - fn init( - mut frame: AtomSpace<'a>, - unit: TimeStampURID, - ) -> Option> { - { - let frame = &mut frame as &mut dyn AllocateSpace; - let header = sys::LV2_Atom_Sequence_Body { - unit: match unit { - TimeStampURID::BeatsPerMinute(urid) => urid.get(), - TimeStampURID::Frames(urid) => urid.get(), - }, - pad: 0, - }; - frame.write(&header, true)?; - } + fn init(mut frame: AtomSpaceWriter<'b>, unit: TimeStampURID) -> Option> { + let header = sys::LV2_Atom_Sequence_Body { + unit: match unit { + TimeStampURID::BeatsPerMinute(urid) => urid.get(), + TimeStampURID::Frames(urid) => urid.get(), + }, + pad: 0, + }; + space::write_value(&mut frame, header)?; + Some(SequenceWriter { frame, unit: unit.into(), @@ -184,25 +176,28 @@ impl<'a> Iterator for SequenceIterator<'a> { type Item = (TimeStamp, UnidentifiedAtom<'a>); fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> { - let (raw_stamp, space) = self.space.split_for_type::()?; + // SAFETY: The validity of the space's contents is guaranteed by this type. + let (raw_stamp, space) = unsafe { self.space.split_for_value_as_unchecked::() }?; let stamp = match self.unit { TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) }, }; - let (atom, space) = space.split_atom()?; + + // SAFETY: The validity of the space's contents is guaranteed by this type. + let (atom, space) = unsafe { space.split_atom() }?; self.space = space; - Some((stamp, UnidentifiedAtom::new_unchecked(atom))) + Some((stamp, atom)) } } /// The writing handle for sequences. pub struct SequenceWriter<'a> { - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'a>, unit: TimeStampUnit, last_stamp: Option, } -impl<'a, 'b> SequenceWriter<'a> { +impl<'a> SequenceWriter<'a> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: @@ -239,14 +234,14 @@ impl<'a, 'b> SequenceWriter<'a> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init<'c, A: Atom<'a, 'c>>( - &'c mut self, + pub fn init<'c, A: Atom<'c, 'a>>( + &'a mut self, stamp: TimeStamp, urid: URID, parameter: A::WriteParameter, ) -> Option { self.write_time_stamp(stamp)?; - (&mut self.frame as &mut dyn AllocateSpace).init(urid, parameter) + space::init_atom(&mut self.frame, urid, parameter) } /// Forward an unidentified atom to the sequence. @@ -257,7 +252,9 @@ impl<'a, 'b> SequenceWriter<'a> { pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { let data = atom.space.as_bytes(); self.write_time_stamp(stamp)?; - self.frame.write_raw(data, true).map(|_| ()) + space::write_bytes(&mut self.frame, data)?; + + Some(()) } } @@ -279,12 +276,12 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDCollection::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = Space::boxed(256); // writing { - let mut space = raw_space.as_mut(); - let mut writer = space::init_atom(space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); + let mut space = raw_space.as_bytes_mut(); + let mut writer = space::init_atom(&mut space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) @@ -296,8 +293,7 @@ mod tests { // verifying { - let (sequence, space) = raw_space.split_at(size_of::()); - let sequence = unsafe { &*(sequence.as_ptr() as *const sys::LV2_Atom_Sequence) }; + let (sequence, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(sequence.atom.type_, urids.atom.sequence); assert_eq!( sequence.atom.size as usize, @@ -310,23 +306,18 @@ mod tests { ); assert_eq!(sequence.body.unit, urids.units.frame); - let (stamp, space) = space.split_at(size_of::()); - let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) }; - assert_eq!(unsafe { stamp.frames }, 0); + let (stamp, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + assert_eq!(stamp.frames, 0); - let (int, space) = space.split_at(size_of::()); - let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Int) }; + let (int, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); - let (_, space) = space.split_at(4); - let (stamp, space) = space.split_at(size_of::()); - let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) }; - assert_eq!(unsafe { stamp.frames }, 1); + let (stamp, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + assert_eq!(stamp.frames, 1); - let (int, _) = space.split_at(size_of::()); - let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Long) }; + let (int, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.long); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 17); @@ -334,9 +325,8 @@ mod tests { // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urids.atom.sequence).unwrap(); - let mut reader = Sequence::read(body, urids.units.beat).unwrap(); + let body = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.body().unwrap(); + let mut reader = unsafe { Sequence::read(body, urids.units.beat) }.unwrap(); assert_eq!(reader.unit(), TimeStampUnit::Frames); diff --git a/atom/src/space.rs b/atom/src/space.rs index cb1526e5..44fb2960 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -5,190 +5,8 @@ mod space; mod allocatable; mod atom; -pub use space::Space; +pub use space::{AtomSpace, Space}; pub use list::{SpaceList, SpaceHead}; pub use allocatable::*; -pub use atom::AtomSpace; +pub use atom::AtomSpaceWriter; -#[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 = 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_bytes(vector.as_slice()); - let (lower_space, space) = space.split_bytes_at(128).unwrap(); - for i in 0..128 { - assert_eq!(lower_space[i], i as u8); - } - - let (integer, _) = unsafe { space.split_for_type::() }.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) }; - - // Writing an integer atom. - unsafe { - *(data.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { - atom: sys::LV2_Atom { - size: size_of::() as u32, - type_: urid.get(), - }, - body: 42, - }; - - let space = Space::from_ref(data.as_ref()); - let (atom, _) = space.split_atom().unwrap(); - let (body, _) = atom.split_atom_body(urid).unwrap(); - let body = body.as_bytes(); - - assert_eq!(size_of::(), 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_ref(&value); - assert_eq!(value, *unsafe { space.split_for_type::() }.unwrap().0); - } - - fn test_mut_space<'a, S: AllocateSpace<'a>>(mut space: S) { - let map = HashURIDMapper::new(); - let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - - let mut test_data: Vec = 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 = crate::space::write_value(&mut space, test_atom).unwrap(); - assert_eq!(written_atom.size, test_atom.size); - assert_eq!(written_atom.type_, test_atom.type_); - - let created_space = unsafe { Space::from_atom_mut(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::() + 42); - - let mut atom_frame = - AtomSpace::write_new(&mut space as &mut dyn AllocateSpace, urids.chunk).unwrap(); - - let mut test_data: Vec = 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 written_atom = crate::space::write_value(&mut atom_frame, test_atom).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 = unsafe { - std::slice::from_raw_parts_mut( - (&mut memory).as_mut_ptr() as *mut u8, - MEMORY_SIZE * size_of::(), - ) - }; - - test_mut_space(frame); - } - - #[test] - fn test_space_head() { - let mut space = SpaceList::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::(), - ) - }; - - // writing - { - let mut root = raw_space; - let mut frame = - AtomSpace::write_new(&mut root, URID::<()>::new(1).unwrap()) - .unwrap(); - crate::space::write_value(&mut frame, 42u32).unwrap(); - crate::space::write_value(&mut frame, 17u32).unwrap(); - } - - // checking - { - let (atom, space) = raw_space.split_at(size_of::()); - 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::()); - 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::()); - 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 = &mut raw_space[3..]; - crate::space::write_value(&mut root_space, 42u8).unwrap(); - } - - assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref()); - } -} diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 94e6dae9..09792f5f 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -1,6 +1,6 @@ use crate::Atom; use urid::URID; -use crate::space::{AtomSpace, Space}; +use crate::space::{AtomSpaceWriter, Space}; use core::mem::size_of_val; use std::mem::MaybeUninit; @@ -8,7 +8,7 @@ use std::mem::MaybeUninit; /// A smart pointer that writes atom data to an internal slice. /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. -pub trait AllocateSpace<'a> { +pub trait AllocateSpace<'a>: 'a { /// Try to allocate memory on the internal data slice. /// /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. @@ -17,17 +17,19 @@ pub trait AllocateSpace<'a> { fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]>; fn as_bytes(&self) -> &[u8]; + fn as_bytes_mut(&mut self) -> &mut [u8]; } impl<'a> AllocateSpace<'a> for &'a mut [u8] { #[inline] fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { - if size > self.space.len() { + if size > self.len() { return None } - let (allocated, remaining) = self.space.split_at_mut(size); - self.space = remaining; + let slice: &'a mut [u8] = ::core::mem::replace(self, &mut []); // Lifetime workaround + let (allocated, remaining) = slice.split_at_mut(size); + *self = remaining; Some(allocated) } @@ -35,33 +37,59 @@ impl<'a> AllocateSpace<'a> for &'a mut [u8] { fn as_bytes(&self) -> &[u8] { self } + + #[inline] + fn as_bytes_mut(&mut self) -> &mut [u8] { + self + } +} + +impl<'a> AllocateSpace<'a> for &'a mut dyn AllocateSpace<'a> { + #[inline] + fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { + (*self).allocate_unaligned(size) + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + let s = Clone::clone(&self); + s.as_bytes() + } + + #[inline] + fn as_bytes_mut(&mut self) -> &mut [u8] { (*self).as_bytes_mut() } } #[inline] -pub fn allocate<'a, T>(space: &mut impl AllocateSpace<'a>, size: usize) -> Option<&'a mut Space> { - let required_padding = crate::space::space::required_padding_for_type::(space.as_bytes()); +pub fn allocate<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, size: usize) -> Option<&'a mut Space> { + let required_padding = Space::::padding_for(space.as_bytes()); let raw = space.allocate_unaligned(size + required_padding)?; Space::try_align_from_bytes_mut(raw) } #[inline] -pub fn init_atom<'a, 'b, A: Atom<'a, 'b>>(space: &mut impl AllocateSpace<'b>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { - let space = AtomSpace::write_new(space, atom_type)?; +pub fn allocate_values<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, count: usize) -> Option<&'a mut [MaybeUninit]> { + let space = allocate(space, count * ::core::mem::size_of::())?; + Some(space.as_uninit_slice_mut()) +} + +#[inline] +pub fn init_atom<'a, 'b, A: Atom<'a, 'b>>(space: &'b mut impl AllocateSpace<'b>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { + let space = AtomSpaceWriter::write_new(space, atom_type)?; A::init(space, write_parameter) } #[inline] pub fn write_bytes<'a>(space: &mut impl AllocateSpace<'a>, bytes: &[u8]) -> Option<&'a mut [u8]> { - let (_, space) = space.allocate_unaligned(bytes.len())?; - space.as_bytes_mut().copy_from_slice(bytes); + let space = space.allocate_unaligned(bytes.len())?; + space.copy_from_slice(bytes); Some(space) } #[inline] pub fn write_value<'a, T>(space: &mut impl AllocateSpace<'a>, value: T) -> Option<&'a mut T> - where T: Copy + Sized + 'static -{ + where T: Copy + Sized + 'static { let space = allocate(space, size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. let space = unsafe { space.as_uninit_mut_unchecked() }; @@ -70,3 +98,16 @@ pub fn write_value<'a, T>(space: &mut impl AllocateSpace<'a>, value: T) -> Optio // SAFETY: the MaybeUninit has now been properly initialized. Some (unsafe { &mut *(space.as_mut_ptr()) }) } + +pub fn write_values<'a, T>(space: &mut impl AllocateSpace<'a>, values: &[T]) -> Option<&'a mut [T]> + where T: Copy + Sized + 'static { + let space: &mut Space = allocate(space, size_of_val(values))?; + let space = space.as_uninit_slice_mut(); + + for (dst, src) in space.iter_mut().zip(values.iter()) { + *dst = MaybeUninit::new(*src) + } + + // SAFETY: Assume init: we just initialized the memory above + Some(unsafe { &mut *(space as *mut [_] as *mut [T]) }) +} diff --git a/atom/src/space/atom.rs b/atom/src/space/atom.rs index ef1dc959..fe09b89c 100644 --- a/atom/src/space/atom.rs +++ b/atom/src/space/atom.rs @@ -1,31 +1,31 @@ use crate::space::AllocateSpace; use urid::URID; -/// A `MutSpace` that notes the amount of allocated space in an atom header. -pub struct AtomSpace<'a> { +/// A `MutSpace` that tracks the amount of allocated space in an atom header. +pub struct AtomSpaceWriter<'a> { atom: &'a mut sys::LV2_Atom, parent: &'a mut dyn AllocateSpace<'a>, } -impl<'a> AtomSpace<'a> { +impl<'a> AtomSpaceWriter<'a> { #[inline] - pub fn atom(&self) -> &'a mut sys::LV2_Atom { + pub fn atom(&'a self) -> &'a sys::LV2_Atom { self.atom } /// Create a new framed space with the given parent and type URID. - pub fn write_new(parent: &mut dyn AllocateSpace<'a>, urid: URID) -> Option { + pub fn write_new(mut parent: &'a mut dyn AllocateSpace<'a>, urid: URID) -> Option { let atom = sys::LV2_Atom { size: 0, type_: urid.get(), }; - let atom: &'a mut sys::LV2_Atom = crate::space::write_value(parent, atom)?; + let atom = crate::space::write_value(&mut parent, atom)?; Some(Self { atom, parent }) } } -impl<'a, 'b> AllocateSpace<'a> for AtomSpace<'a> { +impl<'a> AllocateSpace<'a> for AtomSpaceWriter<'a> { #[inline] fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { let result = self.parent.allocate_unaligned(size); @@ -40,4 +40,9 @@ impl<'a, 'b> AllocateSpace<'a> for AtomSpace<'a> { fn as_bytes(&self) -> &[u8] { self.parent.as_bytes() } + + #[inline] + fn as_bytes_mut(&mut self) -> &mut [u8] { + self.parent.as_bytes_mut() + } } \ No newline at end of file diff --git a/atom/src/space/list.rs b/atom/src/space/list.rs index 41a45dcd..64bcf83b 100644 --- a/atom/src/space/list.rs +++ b/atom/src/space/list.rs @@ -107,4 +107,9 @@ impl<'a> AllocateSpace<'a> for SpaceHead<'a> { fn as_bytes(&self) -> &[u8] { &[] } + + #[inline] + fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut [] + } } diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 8ea6abf0..641248c4 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -1,45 +1,58 @@ -use core::mem::{align_of, size_of, size_of_val}; +use core::mem::{align_of, size_of}; use std::mem::MaybeUninit; use std::marker::PhantomData; - -#[inline] -pub(crate) fn required_padding_for_alignment(data: &[u8], alignment: usize) -> usize { - let start = data.as_ptr() as usize; - if start % alignment == 0 { 0 } else { alignment - start % alignment } -} - -#[inline] -pub(crate) fn required_padding_for_type(data: &[u8]) -> usize { - required_padding_for_alignment(data, align_of::()) -} - -#[inline] -pub(crate) fn as_bytes(value: &T) -> &[u8] { - // SAFETY: any type can safely be transmuted to a byte slice - unsafe { - std::slice::from_raw_parts(value as *const T as *const u8, size_of_val(value)) - } -} +use urid::URID; +use crate::UnidentifiedAtom; /// An aligned slice of bytes that is designed to contain a given type `T` (by default, Atoms). /// /// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. +#[repr(transparent)] pub struct Space { _type: PhantomData, + // Note: this could be [MaybeUninit] for alignment, but Spaces can have extra unaligned bytes at the end. + // TODO: replace this with [MaybeUninit] data: [u8] } -impl Space { +pub type AtomSpace = Space; + +impl Space { /// Creates an empty Space. #[inline] - pub const fn empty() -> &'static Space { - &Space { _type: PhantomData, data: *&[][..] } + fn empty() -> &'static Space { + // SAFETY: empty slices are always aligned + unsafe { Self::from_bytes_unchecked(&[]) } } /// Creates an empty mutable Space. #[inline] - pub const fn empty_mut() -> &'static mut Space { - &mut Space { _type: PhantomData, data: *&mut [][..] } + fn empty_mut() -> &'static mut Space { + // SAFETY: empty slices are always aligned + unsafe { Self::from_bytes_mut_unchecked(&mut []) } + } + + #[inline] + pub(crate) fn padding_for(data: &[u8]) -> usize { + let alignment = align_of::(); + let start = data.as_ptr() as usize; + if start % alignment == 0 { 0 } else { alignment - start % alignment } + } + + pub fn boxed(size: usize) -> Box where T: Copy { + let type_size = size_of::(); + let size = if type_size == 0 { + 0 + } else { + size / type_size + if size % type_size > 0 { 1 } else { 0 } + }; + + let boxed = vec![MaybeUninit::::zeroed(); size].into_boxed_slice(); + + // SAFETY: The slice is properly aligned as we allocated it as an array of T. + // SAFETY: Casting from zeroed memory to [u8] is safe. + // SAFETY: Casting from [u8] to Space is safe because the Space struct is repr(transparent). + unsafe { Box::from_raw(Box::into_raw(boxed) as *mut Self) } } /// Creates a new space from a slice of bytes, without checking for padding correctness. @@ -53,8 +66,10 @@ impl Space { // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] - pub unsafe fn from_bytes_unchecked(data: &[u8]) -> &Space { - &Space { _type: PhantomData, data: *data } + unsafe fn from_bytes_unchecked(data: &[u8]) -> &Space { + // SAFETY: It is safe to transmute, since our type has repr(transparent) with [u8]. + // SAFETY: The caller is responsible to check for slice alignment. + &*(data as *const _ as *const Self) } /// Creates a new mutable space from a slice of bytes, without checking for padding correctness. @@ -68,40 +83,10 @@ impl Space { // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] - pub unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut Space { - &mut Space { _type: PhantomData, data: *data } - } - - /// Create a new space from an atom pointer. - /// - /// The method creates a space that contains the atom as well as its body. - /// - /// # Safety - /// - /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. - #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Space { - let data = std::slice::from_raw_parts( - atom as *const sys::LV2_Atom as *const u8, - atom.size as usize + size_of::(), - ); - Self::from_bytes(data) - } - - /// Create a new mutable space from an atom pointer. - /// - /// The method creates a space that contains the atom as well as its body. - /// - /// # Safety - /// - /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. - #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom_mut(atom: &mut sys::LV2_Atom) -> &mut Space { - let data = std::slice::from_raw_parts_mut( - atom as *mut sys::LV2_Atom as *mut u8, - atom.size as usize + size_of::(), - ); - Self::from_bytes_mut(data) + unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut Space { + // SAFETY: It is safe to transmute, since our type has repr(transparent) with [u8]. + // SAFETY: The caller is responsible to check for slice alignment. + &mut *(data as *mut _ as *mut Self) } /// Creates a new space from a slice of bytes. @@ -135,6 +120,24 @@ impl Space { Some(unsafe { Space::from_bytes_unchecked(data) }) } + /// Creates a new mutable space from a mutable slice of bytes. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned + /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). + /// + /// This is the non-panicking version of [`Space::from_bytes`]. + #[inline] + pub fn try_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + if data.as_ptr() as usize % align_of::() != 0 { + return None; + } + + // SAFETY: We just checked above that the pointer is correctly aligned + Some(unsafe { Space::from_bytes_mut_unchecked(data) }) + } + /// Creates a new space from a slice of bytes, aligning it if necessary. /// /// # Errors @@ -142,9 +145,9 @@ impl Space { /// This method returns [`None`](Option::None) if the given slice's is too small to contain /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). #[inline] - pub fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { - // SAFETY: The slice was just aligned by align_slice - data.get(required_padding_for_type::(data)..).map(|data| unsafe { Space::from_bytes_unchecked(data) }) + fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { + // SAFETY: We just aligned the slice start + data.get(Self::padding_for(data)..).map(|data| unsafe { Space::from_bytes_unchecked(data) }) } /// Creates a new space from a slice of bytes, aligning it if necessary. @@ -154,13 +157,13 @@ impl Space { /// This method returns [`None`](Option::None) if the given slice's is too small to contain /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). #[inline] - pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { - // SAFETY: The slice was just aligned by align_slice_mut - data.get_mut(required_padding_for_type::(data)..).map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) + pub(crate) fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + // SAFETY: We just aligned the slice's start + data.get_mut(Self::padding_for(data)..).map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) } #[inline] - pub fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { + fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { if mid > self.data.len() { return None; } @@ -173,14 +176,20 @@ impl Space { } #[inline] - pub fn split_bytes_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut [u8])> { + pub fn slice(&self, length: usize) -> Option<&Self> { + // SAFETY: The data is part of the original slice which was aligned already. + Some(unsafe { Self::from_bytes_unchecked(self.data.get(..length)?) }) + } + + #[inline] + fn split_bytes_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut [u8])> { if mid > self.data.len() { return None; } let (start, end) = self.data.split_at_mut(mid); // SAFETY: Because this data was the start of an existing Space, it was aligned already. - let start = unsafe { Self::from_bytes_unchecked_mut(start) }; + let start = unsafe { Self::from_bytes_mut_unchecked(start) }; Some((start, end)) } @@ -197,7 +206,7 @@ impl Space { } #[inline] - pub fn split_for_value(&self) -> Option<(&MaybeUninit, &Self)> { + fn split_for_value(&self) -> Option<(&MaybeUninit, &Self)> { let (value, rest) = self.split_at(size_of::())?; let value = value.as_uninit()?; @@ -205,29 +214,42 @@ impl Space { } #[inline] - pub fn split_at_mut(&mut self, mid: usize) -> Option<(&mut Space, &mut Space)> { + pub unsafe fn split_for_value_unchecked(&self) -> Option<(&T, &Self)> { + let (value, rest) = self.split_for_value()?; + + Some((&*(value.as_ptr() as *const T), rest)) + } + + #[inline] + pub unsafe fn split_for_value_as_unchecked(&self) -> Option<(&U, &Self)> { + let (value, rest) = self.realign()?.split_for_value_unchecked()?; + + Some((value, rest.realign()?)) + } + + #[inline] + fn split_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut Self)> { let (start, end) = self.split_bytes_at_mut(mid)?; let end = Self::try_align_from_bytes_mut(end).unwrap_or(Space::empty_mut()); Some((start, end)) } - /// Create a space from a reference. - /// - /// # Panics - /// - /// This method panics if the given instance pointer isn't 64-bit aligned. #[inline] - pub fn from_ref(instance: &T) -> &Self { - // SAFETY: references are always aligned. - unsafe { Space::from_bytes_unchecked(as_bytes(instance)) } + pub fn realign(&self) -> Option<&Space> { + Space::::try_align_from_bytes(self.as_bytes()) } #[inline] - pub fn realigned(&self) -> Option<&Space> { + pub fn aligned(&self) -> Option<&Space> { Space::::try_from_bytes(self.as_bytes()) } + #[inline] + pub fn aligned_mut(&mut self) -> Option<&mut Space> { + Space::::try_from_bytes_mut(self.as_bytes_mut()) + } + /// Return the internal slice of the space. #[inline] pub fn as_bytes(&self) -> &[u8] { @@ -241,21 +263,21 @@ impl Space { } #[inline] - pub unsafe fn read(&self) -> Option<&T> { + pub(crate) unsafe fn read_unchecked(&self) -> Option<&T> { // SAFETY: The caller has to ensure this slice actually points to initialized memory. - Some(unsafe { &*(self.as_uninit()?) }) + Some(&*(self.as_uninit()?.as_ptr())) } #[inline] - pub unsafe fn read_as(&self) -> Option { - self.realigned()?.read() + pub unsafe fn read_as_unchecked(&self) -> Option<&U> { + self.aligned()?.read_unchecked() } /// Gets a `T`-aligned pointer to the contents. - /// + ///split_for_type /// This methods returns [`None`](Option::None) if the space is not large enough for a value of type `T`. #[inline] - pub fn as_uninit(&self) -> Option<&MaybeUninit> { + fn as_uninit(&self) -> Option<&MaybeUninit> { if self.data.len() < size_of::() { return None; } @@ -266,19 +288,288 @@ impl Space { /// Gets a `T`-aligned pointer to the contents, but without checking that there actually is enough space to hold `T`. #[inline] - pub unsafe fn as_uninit_unchecked(&self) -> &MaybeUninit { + unsafe fn as_uninit_unchecked(&self) -> &MaybeUninit { // SAFETY: The caller has to ensure that the space is actually big enough. - unsafe { &*(self.data.as_ptr() as *const MaybeUninit) } + &*(self.data.as_ptr() as *const MaybeUninit) } /// Gets a `T`-aligned mutable pointer to the contents, but without checking that there actually is enough space to hold `T`. #[inline] - pub unsafe fn as_uninit_mut_unchecked(&mut self) -> &mut MaybeUninit { + pub(crate) unsafe fn as_uninit_mut_unchecked(&mut self) -> &mut MaybeUninit { // SAFETY: The caller has to ensure that the space is actually big enough. - unsafe { &mut *(self.data.as_ptr() as *mut MaybeUninit) } + &mut *(self.data.as_ptr() as *mut MaybeUninit) + } + + /// Gets the contents as a slice of potentially uninitialized `T`s. + /// + /// The resulting slice contains as many values as can fit in the original space. + /// This means there might be less bytes in this slice than in this space, or zero if the space is too small for a single value. + #[inline] + pub(crate) fn as_uninit_slice(&self) -> &[MaybeUninit] { + // SAFETY: This type ensures alignment, so casting aligned bytes to uninitialized memory is safe. + unsafe { ::core::slice::from_raw_parts(self.data.as_ptr() as *const MaybeUninit, self.data.len() / size_of::()) } + } + + /// Gets the contents as a slice of potentially uninitialized `T`s. + /// + /// The resulting slice contains as many values as can fit in the original space. + /// This means there might be less bytes in this slice than in this space, or zero if the space is too small for a single value. + #[inline] + pub(crate) fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + // SAFETY: This type ensures alignment, so casting aligned bytes to uninitialized memory is safe. + unsafe { ::core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut MaybeUninit, self.data.len() / size_of::()) } + } +} + +impl AtomSpace { + /// Create a new space from an atom pointer. + /// + /// The method creates a space that contains the atom as well as its body. + /// + /// # Safety + /// + /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Space { + let data = std::slice::from_raw_parts( + atom as *const sys::LV2_Atom as *const u8, + atom.size as usize + size_of::(), + ); + Self::from_bytes(data) + } + + /// Create a new mutable space from an atom pointer. + /// + /// The method creates a space that contains the atom as well as its body. + /// + /// # Safety + /// + /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub unsafe fn from_atom_mut(atom: &mut sys::LV2_Atom) -> &mut Space { + let data = std::slice::from_raw_parts_mut( + atom as *mut sys::LV2_Atom as *mut u8, + atom.size as usize + size_of::(), + ); + + Self::from_bytes_mut_unchecked(data) + } + + #[inline] + pub unsafe fn to_atom_unchecked(&self) -> UnidentifiedAtom { + UnidentifiedAtom::new_unchecked(self) + } + + #[inline] + pub unsafe fn to_atom(&self) -> Option { + let header = self.read_unchecked()?; // Try to read to ensure there is enough room + // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents + Some(UnidentifiedAtom::new_unchecked(self.slice(header.size as usize)?)) + } + + #[inline] + pub unsafe fn split_atom(&self) -> Option<(UnidentifiedAtom, &Self)> { + let header = self.read_unchecked()?; + let (atom, rest) = self.split_at(header.size as usize)?; + let atom = UnidentifiedAtom::new_unchecked(atom); + + Some((atom, rest)) + } + + #[inline] + pub unsafe fn split_atom_body(&self, urid: URID) -> Option<(&Space, &Space)> { + let (header, body) = self.split_for_value()?; + // SAFETY: The caller is responsible for ensuring there is a valid atom header in there. + let header = &*header.as_ptr(); + + if header.type_ != urid { + return None + } + + body.split_at(header.size as usize) } } -impl Space { +impl Space { + /*pub fn from_bytes() { + + }*/ +} + +#[cfg(test)] +mod tests { + use crate::space::*; + use std::mem::{size_of, size_of_val}; + use urid::*; + + fn aligned_buf() -> Box<[u8]> { + unsafe { Box::from_raw(Box::into_raw(vec![0u64; 64].into_boxed_slice()) as *mut [u8]) } + } + + #[test] + fn test_space() { + let mut vector = aligned_buf(); + for i in 0..128 { + vector[i] = i as u8; + } + + unsafe { + let ptr = vector.as_mut_ptr().add(128) as *mut u32; + *(ptr) = 0x42424242; + } + + let space = Space::::from_bytes(&vector); + let (lower_space, space) = space.split_at(128).unwrap(); + let lower_space = lower_space.as_bytes(); + + for i in 0..128 { + assert_eq!(lower_space[i], i as u8); + } + + let integer = unsafe { space.read_unchecked() }.unwrap(); + assert_eq!(*integer, 0x42424242); + } + + #[test] + fn test_split_atom() { + let mut data = aligned_buf(); + let urid: URID = unsafe { URID::new_unchecked(17) }; + + // Writing an integer atom. + unsafe { + *(data.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { + atom: sys::LV2_Atom { + size: size_of::() as u32, + type_: urid.get(), + }, + body: 42, + }; + + let space = Space::from_bytes(&data); + let (atom, _) = space.split_atom().unwrap(); + let body = atom.body().unwrap().as_bytes(); + + assert_eq!(size_of::(), size_of_val(body)); + assert_eq!(42, unsafe { *(body.as_ptr() as *const i32) }); + } + } + + fn test_mut_space<'a, S: AllocateSpace<'a>>(mut space: S) { + let map = HashURIDMapper::new(); + let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); + + let mut test_data: Vec = vec![0; 128]; + for i in 0..test_data.len() { + test_data[i] = i as u8; + } + + let written_data = crate::space::write_bytes(&mut space, test_data.as_slice()).unwrap(); + assert_eq!(test_data.as_slice(), written_data); + + let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; + let written_atom = crate::space::write_value(&mut space, test_atom).unwrap(); + assert_eq!(written_atom.size, test_atom.size); + assert_eq!(written_atom.type_, test_atom.type_); + let created_space = unsafe { Space::from_atom_mut(written_atom) }; + + assert_eq!( + created_space.as_bytes().as_ptr() as usize, + written_atom as *mut _ as usize + ); + assert_eq!(created_space.as_bytes().len(), size_of::() + 42); + + let mut atom_frame = + AtomSpaceWriter::write_new(&mut space as &mut dyn AllocateSpace, urids.chunk).unwrap(); + + let mut test_data: Vec = vec![0; 24]; + for i in 0..test_data.len() { + test_data[i] = i as u8; + } + + let written_data = crate::space::write_bytes(&mut atom_frame, &test_data).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 written_atom = crate::space::write_value(&mut atom_frame, test_atom).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 = unsafe { + std::slice::from_raw_parts_mut( + (&mut memory).as_mut_ptr() as *mut u8, + MEMORY_SIZE * size_of::(), + ) + }; + + test_mut_space(frame); + } + + #[test] + fn test_space_head() { + let mut space = SpaceList::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::(), + ) + }; + + // writing + { + let mut root = raw_space; + let mut frame = + AtomSpaceWriter::write_new(&mut root, URID::<()>::new(1).unwrap()) + .unwrap(); + crate::space::write_value(&mut frame, 42u32).unwrap(); + crate::space::write_value(&mut frame, 17u32).unwrap(); + } + + // checking + { + let (atom, space) = raw_space.split_at(size_of::()); + 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::()); + 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::()); + 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 = &mut raw_space[3..]; + crate::space::write_value(&mut root_space, 42u8).unwrap(); + } + + assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref()); + } } \ No newline at end of file diff --git a/atom/src/string.rs b/atom/src/string.rs index cfa90823..c2a7891e 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -47,17 +47,14 @@ pub enum LiteralInfo { Datatype(URID), } -impl<'a, 'b> Atom<'a, 'b> for Literal -where - 'a: 'b, -{ +impl<'a, 'b> Atom<'a, 'b> for Literal { type ReadParameter = (); type ReadHandle = (LiteralInfo, &'a str); type WriteParameter = LiteralInfo; - type WriteHandle = StringWriter<'a>; + type WriteHandle = StringWriter<'b>; unsafe fn read(body: &'a Space, _: ()) -> Option<(LiteralInfo, &'a str)> { - let (header, body) = body.split_for_type::()?; + let (header, body) = body.split_for_value_as_unchecked::()?; let info = if header.lang != 0 && header.datatype == 0 { LiteralInfo::Language(URID::new(header.lang)?) } else if header.lang == 0 && header.datatype != 0 { @@ -72,7 +69,7 @@ where .map(|string| (info, string)) } - fn init(mut frame: AtomSpace<'a>, info: LiteralInfo) -> Option> { + fn init(mut frame: AtomSpaceWriter<'b>, info: LiteralInfo) -> Option> { crate::space::write_value(&mut frame, match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { @@ -98,14 +95,11 @@ unsafe impl UriBound for String { const URI: &'static [u8] = sys::LV2_ATOM__String; } -impl<'a, 'b> Atom<'a, 'b> for String -where - 'a: 'b, -{ +impl<'a, 'b> Atom<'a, 'b> for String { type ReadParameter = (); type ReadHandle = &'a str; type WriteParameter = (); - type WriteHandle = StringWriter<'a>; + type WriteHandle = StringWriter<'b>; unsafe fn read(body: &'a Space, _: ()) -> Option<&'a str> { let data = body.as_bytes(); @@ -113,14 +107,14 @@ where Some(core::str::from_utf8(rust_str_bytes).ok()?) } - fn init(frame: AtomSpace<'a>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { Some(StringWriter { frame }) } } /// Handle to append strings to a string or literal. pub struct StringWriter<'a> { - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'a>, } impl<'a, 'b> StringWriter<'a> { @@ -129,18 +123,18 @@ impl<'a, 'b> StringWriter<'a> { /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. - pub fn append(&mut self, string: &str) -> Option<&mut str> { + pub fn append(&'a mut self, string: &str) -> Option<&'a mut str> { let data = string.as_bytes(); - let space = self.frame.write_raw(data, false)?; + let space = crate::space::write_bytes(&mut self.frame, data)?; unsafe { Some(std::str::from_utf8_unchecked_mut(space)) } } } -impl<'a, 'b> Drop for StringWriter<'a> { +impl<'a> Drop for StringWriter<'a> { fn drop(&mut self) { // Null terminator. // FIXME: this seems unsafe if the value could not be written for some reason. - let _ = self::write_value(&mut self.frame, 0u8); + let _ = crate::space::write_value(&mut self.frame, 0u8); } } @@ -171,26 +165,20 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDs::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = Space::boxed(256); // writing { - let mut space = raw_space.as_mut(); - let mut writer = (&mut space as &mut dyn AllocateSpace) - .init( - urids.atom.literal, - LiteralInfo::Language(urids.german.into_general()), - ) - .unwrap(); + let mut space = raw_space.as_bytes_mut(); + let mut writer = crate::space::init_atom(&mut space, urids.atom.literal, LiteralInfo::Language(urids.german.into_general())).unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } // verifying { - let (atom, space) = raw_space.split_at(size_of::()); + let (literal, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); - let literal = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom_Literal) }; assert_eq!(literal.atom.type_, urids.atom.literal.get()); assert_eq!( literal.atom.size as usize, @@ -203,7 +191,7 @@ mod tests { assert_eq!(literal.body.datatype, 0); let size = literal.atom.size as usize - size_of::(); - let string = CStr::from_bytes_with_nul(space.split_at(size).0) + let string = CStr::from_bytes_with_nul(space.split_at(size).unwrap().0.as_bytes()) .unwrap() .to_str() .unwrap(); @@ -212,8 +200,7 @@ mod tests { // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urids.atom.literal).unwrap(); + let (body, _) = unsafe { raw_space.split_atom_body(urids.atom.literal) }.unwrap(); let (info, text) = unsafe { Literal::read(body, ()) }.unwrap(); assert_eq!(info, LiteralInfo::Language(urids.german.into_general())); @@ -226,34 +213,30 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = Space::boxed(256); // writing { - let mut space = raw_space.as_mut(); - let mut writer = (&mut space as &mut dyn AllocateSpace) - .init(urids.string, ()) - .unwrap(); + let mut space = raw_space.as_bytes_mut(); + + let mut writer = crate::space::init_atom(&mut space, urids.string, ()).unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } // verifying { - let (string, space) = raw_space.split_at(size_of::()); - - let string = unsafe { &*(string.as_ptr() as *const sys::LV2_Atom_String) }; + let (string, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(string.atom.type_, urids.string); assert_eq!(string.atom.size as usize, SAMPLE0.len() + SAMPLE1.len() + 1); - let string = std::str::from_utf8(space.split_at(string.atom.size as usize).0).unwrap(); + let string = std::str::from_utf8(space.split_at(string.atom.size as usize).unwrap().0.as_bytes()).unwrap(); assert_eq!(string[..string.len() - 1], SAMPLE0.to_owned() + SAMPLE1); } // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urids.string).unwrap(); + let (body, _) = unsafe { raw_space.split_atom_body(urids.string) }.unwrap(); let string = unsafe { String::read(body, ()) }.unwrap(); assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1); } diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 673b8789..05f53635 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -43,20 +43,17 @@ unsafe impl UriBound for Tuple { const URI: &'static [u8] = sys::LV2_ATOM__Tuple; } -impl<'a, 'b> Atom<'a, 'b> for Tuple -where - 'a: 'b, -{ +impl<'a, 'b> Atom<'a, 'b> for Tuple { type ReadParameter = (); type ReadHandle = TupleIterator<'a>; type WriteParameter = (); - type WriteHandle = TupleWriter<'a>; + type WriteHandle = TupleWriter<'b>; unsafe fn read(body: &'a Space, _: ()) -> Option> { Some(TupleIterator { space: body }) } - fn init(frame: AtomSpace<'a>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { Some(TupleWriter { frame }) } } @@ -72,25 +69,26 @@ impl<'a> Iterator for TupleIterator<'a> { type Item = UnidentifiedAtom<'a>; fn next(&mut self) -> Option> { - let (atom, space) = self.space.split_atom()?; + // SAFETY: The validity of the space is guaranteed by this type. + let (atom, space) = unsafe { self.space.split_atom() }?; self.space = space; - Some(unsafe { UnidentifiedAtom::new_unchecked(atom) }) + Some(atom) } } /// The writing handle to add atoms to a tuple. pub struct TupleWriter<'a> { - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'a>, } impl<'a, 'b> TupleWriter<'a> { /// Initialize a new tuple element. - pub fn init<'c, A: Atom<'a, 'c>>( - &'c mut self, + pub fn init<'c, A: Atom<'c, 'a>>( + &'a mut self, child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { - (&mut self.frame as &mut dyn AllocateSpace).init(child_urid, child_parameter) + space::init_atom(&mut self.frame, child_urid, child_parameter) } } @@ -106,14 +104,12 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = Space::boxed(256); // writing { - let mut space =raw_space.as_mut(); - let mut writer = (&mut space as &mut dyn AllocateSpace) - .init(urids.tuple, ()) - .unwrap(); + let mut space = raw_space.as_bytes_mut(); + let mut writer = crate::space::init_atom(&mut space, urids.tuple, ()).unwrap(); { let mut vector_writer = writer.init::>(urids.vector, urids.int).unwrap(); @@ -124,19 +120,18 @@ mod tests { // verifying { - let (atom, space) = raw_space.split_at(size_of::()); - let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; - assert_eq!(atom.type_, urids.tuple); + let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); + let header = atom.header().unwrap(); + assert_eq!(header.type_, urids.tuple); assert_eq!( - atom.size as usize, + header.size as usize, size_of::() + size_of::() * 9 + 4 + size_of::() ); - let (vector, space) = space.split_at(size_of::()); - let vector = unsafe { &*(vector.as_ptr() as *const sys::LV2_Atom_Vector) }; + let (vector, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(vector.atom.type_, urids.vector); assert_eq!( vector.atom.size as usize, @@ -145,14 +140,11 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int); - let (vector_items, space) = space.split_at(size_of::() * 9); - let vector_items = - unsafe { std::slice::from_raw_parts(vector_items.as_ptr() as *const i32, 9) }; + let (vector_items, space) = space.split_at(size_of::() * 9).unwrap(); + let vector_items = unsafe { std::slice::from_raw_parts(vector_items.as_bytes().as_ptr() as *const i32, 9) }; assert_eq!(vector_items, &[17; 9]); - let (_, space) = space.split_at(4); - let (int, _) = space.split_at(size_of::()); - let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Int) }; + let (int, _) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); @@ -160,9 +152,8 @@ mod tests { // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urids.tuple).unwrap(); - let items: Vec = Tuple::read(body, ()).unwrap().collect(); + let (body, _) = unsafe { raw_space.split_atom_body(urids.tuple) }.unwrap(); + let items: Vec = unsafe { Tuple::read(body, ()) }.unwrap().collect(); assert_eq!(items[0].read(urids.vector, urids.int).unwrap(), [17; 9]); assert_eq!(items[1].read(urids.int, ()).unwrap(), 42); } diff --git a/atom/src/vector.rs b/atom/src/vector.rs index ffd7a981..a4e35060 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -34,7 +34,7 @@ use crate::scalar::ScalarAtom; use crate::space::*; use crate::*; use std::marker::PhantomData; -use std::mem::size_of; +use std::mem::{size_of, MaybeUninit}; use urid::*; /// An atom containg an array of scalar atom bodies. @@ -48,40 +48,26 @@ unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } -impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector -where - 'a: 'b, - C: 'b, -{ +impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector where C: 'b, { type ReadParameter = URID; type ReadHandle = &'a [C::InternalType]; type WriteParameter = URID; - type WriteHandle = VectorWriter<'a, C>; + type WriteHandle = VectorWriter<'b, C>; unsafe fn read(body: &'a Space, child_urid: URID) -> Option<&'a [C::InternalType]> { - let (header, body) = body.split_for_type::()?; + let (header, body) = body.split_for_value_as_unchecked::()?; - if header.child_type != child_urid - || header.child_size as usize != size_of::() - { + if header.child_type != child_urid || header.child_size as usize != size_of::() { return None; } - let data = body.as_bytes(); - - assert_eq!(data.len() % size_of::(), 0); - let children_count = data.len() / size_of::(); + let data = body.aligned::()?.as_uninit_slice(); - let children = unsafe { - std::slice::from_raw_parts(data.as_ptr() as *const C::InternalType, children_count) - }; - Some(children) + // SAFETY: Assume Init: We can assume this data was properly initialized by the host. + Some(&*(data as *const _ as *const [C::InternalType])) } - fn init( - mut frame: AtomSpace<'a>, - child_urid: URID, - ) -> Option> { + fn init(mut frame: AtomSpaceWriter<'b>, child_urid: URID) -> Option> { let body = sys::LV2_Atom_Vector_Body { child_type: child_urid.get(), child_size: size_of::() as u32, @@ -99,7 +85,7 @@ where /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. pub struct VectorWriter<'a, A: ScalarAtom> { - frame: AtomSpace<'a>, + frame: AtomSpaceWriter<'a>, type_: PhantomData, } @@ -113,28 +99,15 @@ impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, A> { /// Append a slice of undefined memory to the vector. /// /// Using this method, you don't need to have the elements in memory before you can write them. - pub fn allocate(&mut self, size: usize) -> Option<&mut [A::InternalType]> { - self.frame - .allocate(size_of::() * size, false) - .map(|(_, data)| unsafe { - std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut A::InternalType, size) - }) + #[inline] + pub fn allocate_uninit(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { + space::allocate_values(&mut self.frame, count) } /// Append multiple elements to the vector. + #[inline] pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { - let raw_data = unsafe { - std::slice::from_raw_parts(data.as_ptr() as *const u8, std::mem::size_of_val(data)) - }; - self.frame - .allocate(raw_data.len(), false) - .map(|(_, space)| unsafe { - space.copy_from_slice(raw_data); - std::slice::from_raw_parts_mut( - space.as_mut_ptr() as *mut A::InternalType, - data.len(), - ) - }) + space::write_values(&mut self.frame, data) } } diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 95c96830..b1c85e5b 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -47,6 +47,7 @@ impl Plugin for AtomPlugin { .input .read::(self.urids.atom.sequence, self.urids.units.beat) .unwrap(); + let mut sequence_writer = ports .output .init::( @@ -98,18 +99,15 @@ fn main() { let urids: URIDs = map.populate_collection().unwrap(); // Preparing the input atom. - let mut input_atom_space: Box<[u8]> = Box::new([0; 256]); + let mut input_atom_space = AtomSpace::boxed(256); { - let mut space = SpaceWriter::new(input_atom_space.as_mut()); - let mut writer = (&mut space as &mut dyn AllocateSpace) - .init( - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame), - ) - .unwrap(); - writer + let mut space = input_atom_space.as_bytes_mut(); + let mut writer = lv2_atom::space::init_atom(&mut space, + urids.atom.sequence, + TimeStampURID::Frames(urids.units.frame)).unwrap(); + {writer .init(TimeStamp::Frames(0), urids.atom.int, 42) - .unwrap(); + .unwrap();} writer .init(TimeStamp::Frames(1), urids.atom.long, 17) .unwrap(); @@ -119,13 +117,13 @@ fn main() { } // preparing the output atom. - let mut output_atom_space: Box<[u8]> = Box::new([0; 256]); + let mut output_atom_space = Space::boxed(256); { - let mut space = SpaceWriter::new(output_atom_space.as_mut()); - (&mut space as &mut dyn AllocateSpace) - .init(urids.atom.chunk, ()) - .unwrap() - .allocate(256 - size_of::(), false) + let mut space = output_atom_space.as_bytes_mut(); + lv2_atom::space::init_atom(&mut space, + urids.atom.chunk, + ()).unwrap() + .allocate_unaligned(256 - size_of::()) .unwrap(); } @@ -149,12 +147,12 @@ fn main() { (plugin_descriptor.connect_port.unwrap())( plugin, 0, - input_atom_space.as_mut_ptr() as *mut c_void, + input_atom_space.as_bytes().as_mut_ptr() as *mut c_void, ); (plugin_descriptor.connect_port.unwrap())( plugin, 1, - output_atom_space.as_mut_ptr() as *mut c_void, + output_atom_space.as_bytes().as_mut_ptr() as *mut c_void, ); // Activate, run, deactivate. @@ -167,10 +165,8 @@ fn main() { } // Asserting the result - let (sequence, _) = Space::from_bytes(output_atom_space.as_ref()) - .split_atom_body(urids.atom.sequence) - .unwrap(); - for (stamp, atom) in Sequence::read(sequence, urids.units.beat).unwrap() { + let (sequence, _) = unsafe { output_atom_space.split_atom_body(urids.atom.sequence)}.unwrap(); + for (stamp, atom) in unsafe { Sequence::read(sequence, urids.units.beat) }.unwrap() { let stamp = stamp.as_frames().unwrap(); match stamp { 0 => assert_eq!(atom.read(urids.atom.int, ()).unwrap(), 84), diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 65727473..c14f3d86 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -22,13 +22,13 @@ where type ReadParameter = (); type ReadHandle = &'a [u8]; type WriteParameter = (); - type WriteHandle = AtomSpace<'a, 'b>; + type WriteHandle = AtomSpaceWriter<'b>; - fn read(body: &'a Space, _: ()) -> Option<&'a [u8]> { - body.as_bytes() + unsafe fn read(body: &'a Space, _: ()) -> Option<&'a [u8]> { + Some(body.as_bytes()) } - fn init(frame: AtomSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { Some(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index b45989ab..5ebcd80c 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -26,20 +26,18 @@ where { type ReadParameter = (); type ReadHandle = wmidi::MidiMessage<'a>; - type WriteParameter = wmidi::MidiMessage<'static>; + type WriteParameter = wmidi::MidiMessage<'b>; type WriteHandle = (); - fn read(space: &'a Space, _: ()) -> Option> { - space - .as_bytes() - .and_then(|bytes| wmidi::MidiMessage::try_from(bytes).ok()) + unsafe fn read(space: &'a Space, _: ()) -> Option> { + wmidi::MidiMessage::try_from(space.as_bytes()).ok() } - fn init(mut frame: AtomSpace<'a, 'b>, message: wmidi::MidiMessage<'b>) -> Option<()> { - frame - .allocate(message.bytes_size(), false) - .and_then(|(_, space)| message.copy_to_slice(space).ok()) - .map(|_| ()) + fn init(mut frame: AtomSpaceWriter<'b>, message: wmidi::MidiMessage) -> Option<()> { + let space: &mut Space = lv2_atom::space::allocate(&mut frame, message.bytes_size())?; + message.copy_to_slice(space.as_bytes_mut()).ok()?; + + Some(()) } } @@ -61,15 +59,15 @@ where type ReadParameter = (); type ReadHandle = wmidi::MidiMessage<'a>; type WriteParameter = (); - type WriteHandle = Writer<'a, 'b>; + type WriteHandle = Writer<'b>; - fn read(space: &'a Space, _: ()) -> Option> { + unsafe fn read(space: &'a Space, _: ()) -> Option> { WMidiEvent::read(space, ()) } - fn init(frame: AtomSpace<'a, 'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { let mut writer = Writer { frame }; - writer.write::(&0xf0); + writer.write::(0xf0); Some(writer) } } @@ -79,37 +77,31 @@ where /// This writing handle is similar to a chunk's `ByteWriter`: You can allocate space, write raw bytes and generic values. /// /// The "start of system exclusive" status byte is written by [`SystemExclusiveWMidiEvent::init`](struct.SystemExclusiveWMidiEvent.html#method.init) method and the "end of system exclusive" status byte is written when the writer is dropped. -pub struct Writer<'a, 'b> { - frame: AtomSpace<'a, 'b>, +pub struct Writer<'a> { + frame: AtomSpaceWriter<'a>, } -impl<'a, 'b> Writer<'a, 'b> { - pub fn allocate(&mut self, size: usize) -> Option<&'a mut [u8]> { - self.frame.allocate(size, false).map(|(_, slice)| slice) - } - +impl<'a> Writer<'a> { + #[inline] pub fn write_raw(&mut self, data: &[u8]) -> Option<&'a mut [u8]> { - self.frame.write_raw(data, false) + lv2_atom::space::write_bytes(&mut self.frame, data) } - pub fn write(&mut self, instance: &T) -> Option<&'a mut T> - where - T: Unpin + Copy + Send + Sync + Sized + 'static, - { - (&mut self.frame as &mut dyn AllocateSpace).write(instance, false) + #[inline] + pub fn write(&mut self, instance: T) -> Option<&'a mut T> where T: Copy + Sized + 'static, { + lv2_atom::space::write_value(&mut self.frame, instance) } } -impl<'a, 'b> Drop for Writer<'a, 'b> { +impl<'a, 'b> Drop for Writer<'a> { fn drop(&mut self) { - self.write::(&0xf7); + self.write::(0xf7); } } #[cfg(test)] mod tests { use crate::wmidi_binding::*; - use atom::space::SpaceWriter; use std::convert::TryFrom; use std::mem::size_of; use wmidi::*; @@ -119,35 +111,32 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = Space::boxed(256); let reference_message = MidiMessage::NoteOn(Channel::Ch1, Note::A0, Velocity::try_from(125).unwrap()); // writing { - let mut space = SpaceWriter::new(raw_space.as_mut()); - (&mut space as &mut dyn AllocateSpace) - .init(urid, reference_message.clone()) - .unwrap(); + let mut space = raw_space.as_bytes_mut(); + lv2_atom::space::init_atom(&mut space, urid, reference_message.clone()).unwrap(); } // verifying { - let (header, raw_space) = raw_space.split_at(size_of::()); - let header = unsafe { &*(header.as_ptr() as *const sys::LV2_Atom) }; + let (header, space) = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.header_and_body().unwrap(); assert_eq!(header.type_, urid); assert_eq!(header.size as usize, 3); - let (message, _) = raw_space.split_at(3); + let message = space.slice(3).unwrap().as_bytes(); let message = MidiMessage::try_from(message).unwrap(); assert_eq!(message, reference_message); } // reading { - let space = Space::from_ref(raw_space.as_ref()); + let space = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.body().unwrap(); - let message = WMidiEvent::read(space.split_atom_body(urid).unwrap().0, ()).unwrap(); + let message = unsafe { WMidiEvent::read(space, ()) }.unwrap(); assert_eq!(message, reference_message); } } @@ -157,31 +146,27 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = AtomSpace::boxed(256); // writing { - let mut space = SpaceWriter::new(raw_space.as_mut()); - let mut writer = (&mut space as &mut dyn AllocateSpace).init(urid, ()).unwrap(); + let mut space = raw_space.as_bytes_mut(); + let mut writer = lv2_atom::space::init_atom(&mut space, urid, ()).unwrap(); writer.write_raw(&[1, 2, 3, 4]); } // verifying { - let (header, raw_space) = raw_space.split_at(size_of::()); - let header = unsafe { &*(header.as_ptr() as *const sys::LV2_Atom) }; + let (header, body) = unsafe { raw_space.to_atom() }.unwrap().header_and_body().unwrap(); assert_eq!(header.type_, urid); assert_eq!(header.size as usize, 6); - - let (message, _) = raw_space.split_at(6); - assert_eq!(message, &[0xf0, 1, 2, 3, 4, 0xf7]); + assert_eq!(&body.as_bytes()[..6], &[0xf0, 1, 2, 3, 4, 0xf7]); } // reading { - let space = Space::from_ref(raw_space.as_ref()); - - let message = WMidiEvent::read(space.split_atom_body(urid).unwrap().0, ()).unwrap(); + let atom = unsafe { raw_space.to_atom() }.unwrap(); + let message = atom.read(urid, ()).unwrap(); assert_eq!( message, MidiMessage::SysEx(unsafe { U7::from_bytes_unchecked(&[1, 2, 3, 4]) }) diff --git a/state/src/raw.rs b/state/src/raw.rs index 72fdaf96..0ba1a241 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -54,14 +54,8 @@ impl<'a> StoreHandle<'a> { ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); - let space = Space::from_bytes(space.as_ref()); - let (header, data) = space - .split_for_type::() - .ok_or(StateErr::BadData)?; - let data = data - .split_bytes_at(header.size as usize) - .map(|(data, _)| data) - .ok_or(StateErr::BadData)?; + let space = Space::::from_bytes(&space); + let (header, data) = unsafe { space.to_atom() }.and_then(|a| a.header_and_body()).ok_or(StateErr::BadData)?; let key = key.get(); let data_ptr = data as *const _ as *const c_void; @@ -123,15 +117,14 @@ impl<'a> StatePropertyWriter<'a> { /// Initialize the property. /// /// This works like any other atom writer: You have to provide the URID of the atom type you want to write, as well as the type-specific parameter. If the property hasn't been initialized before, it will be initialized and the writing handle is returned. Otherwise, `Err(StateErr::Unknown)` is returned. - pub fn init<'b, A: Atom<'a, 'b>>( - &'b mut self, + pub fn init<'read, A: Atom<'read, 'a>>( + &'a mut self, urid: URID, parameter: A::WriteParameter, ) -> Result { if !self.initialized { self.initialized = true; - (&mut self.head as &mut dyn AllocateSpace) - .init(urid, parameter) + lv2_atom::space::init_atom(&mut self.head, urid, parameter) .ok_or(StateErr::Unknown) } else { Err(StateErr::Unknown) @@ -209,7 +202,7 @@ impl<'a> StatePropertyReader<'a> { } /// Return the data of the property. - pub fn body(&self) -> Space { + pub fn body(&self) -> &Space { self.body } @@ -224,7 +217,7 @@ impl<'a> StatePropertyReader<'a> { parameter: A::ReadParameter, ) -> Result { if urid == self.type_ { - A::read(self.body, parameter).ok_or(StateErr::Unknown) + unsafe { A::read(self.body, parameter) }.ok_or(StateErr::Unknown) } else { Err(StateErr::BadType) } @@ -317,7 +310,7 @@ mod tests { 3 => { assert_eq!(urids.vector::(), *type_); let space = Space::from_bytes(value.as_slice()); - let data = Vector::read(space, urids.int).unwrap(); + let data = unsafe { Vector::read(space, urids.int) }.unwrap(); assert_eq!([1, 2, 3, 4], data); } _ => panic!("Invalid key!"), From 67a75ceb1aa03fe17092efdbab55a2f0da657e18 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 8 Aug 2021 03:53:26 +0200 Subject: [PATCH 04/54] Fix things, break even more --- atom/src/chunk.rs | 5 +- atom/src/header.rs | 29 +++++++ atom/src/lib.rs | 12 +-- atom/src/object.rs | 9 ++- atom/src/port.rs | 10 +-- atom/src/scalar.rs | 4 +- atom/src/sequence.rs | 19 +++-- atom/src/space.rs | 5 +- atom/src/space/allocatable.rs | 68 +++++++++++----- atom/src/space/atom.rs | 48 ----------- atom/src/space/atom_writer.rs | 103 ++++++++++++++++++++++++ atom/src/space/boxed.rs | 38 +++++++++ atom/src/space/space.rs | 142 +++++++++++++++------------------ atom/src/string.rs | 14 ++-- atom/src/tuple.rs | 9 ++- atom/src/vector.rs | 20 ++--- atom/tests/atom_integration.rs | 13 +-- midi/src/wmidi_binding.rs | 4 +- state/src/raw.rs | 6 +- urid/src/lib.rs | 2 +- 20 files changed, 351 insertions(+), 209 deletions(-) create mode 100644 atom/src/header.rs delete mode 100644 atom/src/space/atom.rs create mode 100644 atom/src/space/atom_writer.rs create mode 100644 atom/src/space/boxed.rs diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 997553e6..63543bec 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -73,7 +73,7 @@ mod tests { // writing { - let mut space = raw_space.as_mut(); + let mut space: &mut _ = raw_space.as_mut(); let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); let data = writer.allocate_unaligned(SLICE_LENGTH - 1).unwrap(); @@ -81,7 +81,8 @@ mod tests { *value = i as u8; } - space::write_value(&mut space, 41u8).unwrap(); + todo!() + // space::write_value(&mut space, 41u8).unwrap(); } // verifying diff --git a/atom/src/header.rs b/atom/src/header.rs new file mode 100644 index 00000000..231900f9 --- /dev/null +++ b/atom/src/header.rs @@ -0,0 +1,29 @@ +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub struct AtomHeader { + inner: lv2_sys::LV2_Atom +} + +impl AtomHeader { + #[inline] + pub fn from_raw(inner: lv2_sys::LV2_Atom) -> Self { + Self { inner } + } + + #[inline] + pub(crate) fn as_raw_mut(&mut self) -> &mut lv2_sys::LV2_Atom { + // SAFETY: AtomHeader is repr(C) and has LV2_Atom as its only field, so transmuting between the two is safe. + unsafe { &mut *(self as *mut Self as *mut _) } + } + + #[inline] + pub fn size(self) -> usize { + self.inner.size as usize + } + + #[inline] + pub fn urid(self) -> u32 { + self.inner.type_ + } +} + diff --git a/atom/src/lib.rs b/atom/src/lib.rs index cb37a715..f1a09a22 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -75,6 +75,7 @@ pub mod vector; #[cfg(feature = "lv2-core")] pub mod port; +mod header; /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { @@ -94,6 +95,7 @@ pub mod prelude { use space::*; use urid::*; +use crate::header::AtomHeader; #[derive(Clone, URIDCollection)] /// Collection with the URIDs of all `UriBound`s in this crate. @@ -204,7 +206,7 @@ impl<'a> UnidentifiedAtom<'a> { ) -> Option { let (header, body) = self.header_and_body()?; - if header.type_ != urid { + if header.urid() != urid { return None; } @@ -218,16 +220,16 @@ impl<'a> UnidentifiedAtom<'a> { } #[inline] - pub fn header_and_body(&self) -> Option<(&'a sys::LV2_Atom, &'a Space)> { + pub fn header_and_body(&self) -> Option<(&'a AtomHeader, &'a Space)> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. let (header, body) = unsafe { self.space.split_for_value_unchecked() }?; - let body = body.slice(header.size as usize)?; + let body = body.slice(header.size())?; - Some((header, body)) + Some((&header, body)) } #[inline] - pub fn header(&self) -> Option<&'a sys::LV2_Atom> { + pub fn header(&self) -> Option<&'a AtomHeader> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. unsafe { self.space.read_unchecked() } } diff --git a/atom/src/object.rs b/atom/src/object.rs index 075e3113..29dedce2 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -183,8 +183,8 @@ impl<'a> Iterator for ObjectReader<'a> { /// Writing handle for object properties. /// /// This handle is a safeguard to assure that a object is always a series of properties. -pub struct ObjectWriter<'a> { - frame: AtomSpaceWriter<'a>, +pub struct ObjectWriter<'space> { + frame: AtomSpaceWriter<'space>, } impl<'a> ObjectWriter<'a> { @@ -313,7 +313,7 @@ mod tests { // writing { let mut space = raw_space.as_mut(); - let frame = AtomSpaceWriter::write_new(&mut space as &mut dyn AllocateSpace, urids.object).unwrap(); + let frame = AtomSpaceWriter::write_new(&mut space, urids.object).unwrap(); let mut writer = Object::init( frame, ObjectHeader { @@ -326,7 +326,8 @@ mod tests { writer.init(first_key, urids.int, first_value).unwrap(); } { - writer.init(second_key, urids.float, second_value).unwrap(); + todo!() + // writer.init(second_key, urids.float, second_value).unwrap(); } } diff --git a/atom/src/port.rs b/atom/src/port.rs index 6d4e0b56..2d7b5ae3 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -135,16 +135,14 @@ mod tests { let map = HashURIDMapper::new(); let urids = AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = AtomSpace::boxed_broken(256); // writing a chunk to indicate the size of the space. { - let mut space = raw_space.as_mut(); - let mut writer = (&mut space as &mut dyn AllocateSpace) - .init(urids.chunk, ()) - .unwrap(); + let mut space = raw_space.as_bytes_mut(); + let mut writer = crate::space::init_atom(&mut space, urids.chunk, ()).unwrap(); writer - .allocate(256 - size_of::(), false) + .allocate_unaligned(256 - size_of::()) .unwrap(); } diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index 289dcc0f..00fd0e39 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -199,8 +199,8 @@ mod tests { // reading { let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urid).unwrap(); - assert_eq!(A::read(body, ()).unwrap(), value); + let (body, _) = unsafe { space.split_atom_body(urid) }.unwrap(); + unsafe { assert_eq!(A::read(body, ()).unwrap(), value); } } } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index 3dedf98d..b6cfc7b2 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -234,8 +234,8 @@ impl<'a> SequenceWriter<'a> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init<'c, A: Atom<'c, 'a>>( - &'a mut self, + pub fn init<'read, 'write: 'a, A: Atom<'read, 'write>>( + &'write mut self, stamp: TimeStamp, urid: URID, parameter: A::WriteParameter, @@ -262,7 +262,6 @@ impl<'a> SequenceWriter<'a> { mod tests { use crate::prelude::*; use crate::sequence::*; - use std::mem::size_of; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; #[derive(URIDCollection)] @@ -276,7 +275,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDCollection::from_map(&map).unwrap(); - let mut raw_space = Space::boxed(256); + let mut raw_space = AtomSpace::boxed_broken(256); // writing { @@ -286,13 +285,17 @@ mod tests { writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) .unwrap(); - writer + + todo!() + /*writer .init::(TimeStamp::Frames(1), urids.atom.long, 17) - .unwrap(); + .unwrap();*/ } // verifying - { + + todo!() + /*{ let (sequence, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(sequence.atom.type_, urids.atom.sequence); assert_eq!( @@ -345,6 +348,6 @@ mod tests { assert_eq!(atom.read::(urids.atom.long, ()).unwrap(), 17); assert!(reader.next().is_none()); - } + }*/ } } diff --git a/atom/src/space.rs b/atom/src/space.rs index 44fb2960..f97f0d21 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -3,10 +3,11 @@ mod list; mod space; mod allocatable; -mod atom; +mod atom_writer; +mod boxed; pub use space::{AtomSpace, Space}; pub use list::{SpaceList, SpaceHead}; pub use allocatable::*; -pub use atom::AtomSpaceWriter; +pub use atom_writer::AtomSpaceWriter; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 09792f5f..cc7b7ed8 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -44,22 +44,6 @@ impl<'a> AllocateSpace<'a> for &'a mut [u8] { } } -impl<'a> AllocateSpace<'a> for &'a mut dyn AllocateSpace<'a> { - #[inline] - fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { - (*self).allocate_unaligned(size) - } - - #[inline] - fn as_bytes(&self) -> &[u8] { - let s = Clone::clone(&self); - s.as_bytes() - } - - #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { (*self).as_bytes_mut() } -} - #[inline] pub fn allocate<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, size: usize) -> Option<&'a mut Space> { let required_padding = Space::::padding_for(space.as_bytes()); @@ -75,9 +59,9 @@ pub fn allocate_values<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, count } #[inline] -pub fn init_atom<'a, 'b, A: Atom<'a, 'b>>(space: &'b mut impl AllocateSpace<'b>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { - let space = AtomSpaceWriter::write_new(space, atom_type)?; - A::init(space, write_parameter) +pub fn init_atom<'a: 'write, 'read, 'write, A: Atom<'read, 'write>>(space: &'a mut impl AllocateSpace<'write>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { + let space: AtomSpaceWriter<'write> = AtomSpaceWriter::write_new(space, atom_type)?; + A::init(space as AtomSpaceWriter<'write>, write_parameter) } #[inline] @@ -111,3 +95,49 @@ pub fn write_values<'a, T>(space: &mut impl AllocateSpace<'a>, values: &[T]) -> // SAFETY: Assume init: we just initialized the memory above Some(unsafe { &mut *(space as *mut [_] as *mut [T]) }) } + +#[cfg(test)] +mod tests { + use crate::space::{AtomSpace, write_value, init_atom}; + use crate::prelude::{Int, AtomSpaceWriter}; + use urid::URID; + use crate::Atom; + + const INT_URID: URID = unsafe { URID::new_unchecked(5) }; + + #[test] + fn test_init_atom_lifetimes () { + assert_eq!(AtomSpace::alignment(), 8); + + let mut space = AtomSpace::boxed(32); + assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed + + let mut cursor: &mut _ = space.as_bytes_mut(); // The pointer that is going to be moved as we keep writing. + let new_value = write_value(&mut cursor, 42u8).unwrap(); + + assert_eq!(42, *new_value); + assert_eq!(31, cursor.len()); + + { + let int_atom: &mut _ = init_atom(&mut cursor, INT_URID, 69).unwrap(); + assert_eq!(69, *int_atom); + // assert_eq!(12, cursor.len()); TODO + } + // let new_value = write_value(&mut cursor, 42u8).unwrap(); + /*{ + // Remaining once aligned: 24, with 8 bytes for atom header: 16 + let writer = AtomSpaceWriter::write_new(&mut cursor, INT_URID).unwrap(); + let int_atom = Int::init(writer, 69).unwrap(); + assert_eq!(69, *int_atom); + assert_eq!(12, cursor.len()); + }*/ + + assert_eq!(space.as_bytes(), [ + 42, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 5, 0, 0, 0, + 69, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ]); + assert_eq!(32, space.as_bytes().len()); + } +} \ No newline at end of file diff --git a/atom/src/space/atom.rs b/atom/src/space/atom.rs deleted file mode 100644 index fe09b89c..00000000 --- a/atom/src/space/atom.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::space::AllocateSpace; -use urid::URID; - -/// A `MutSpace` that tracks the amount of allocated space in an atom header. -pub struct AtomSpaceWriter<'a> { - atom: &'a mut sys::LV2_Atom, - parent: &'a mut dyn AllocateSpace<'a>, -} - -impl<'a> AtomSpaceWriter<'a> { - #[inline] - pub fn atom(&'a self) -> &'a sys::LV2_Atom { - self.atom - } - - /// Create a new framed space with the given parent and type URID. - pub fn write_new(mut parent: &'a mut dyn AllocateSpace<'a>, urid: URID) -> Option { - let atom = sys::LV2_Atom { - size: 0, - type_: urid.get(), - }; - - let atom = crate::space::write_value(&mut parent, atom)?; - Some(Self { atom, parent }) - } -} - -impl<'a> AllocateSpace<'a> for AtomSpaceWriter<'a> { - #[inline] - fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { - let result = self.parent.allocate_unaligned(size); - if result.is_some() { - self.atom.size += size as u32; - } - - result - } - - #[inline] - fn as_bytes(&self) -> &[u8] { - self.parent.as_bytes() - } - - #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { - self.parent.as_bytes_mut() - } -} \ No newline at end of file diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs new file mode 100644 index 00000000..081ee5dd --- /dev/null +++ b/atom/src/space/atom_writer.rs @@ -0,0 +1,103 @@ +use crate::space::AllocateSpace; +use urid::URID; +use std::mem::transmute; +use crate::header::AtomHeader; + +/// A `MutSpace` that tracks the amount of allocated space in an atom header. +pub struct AtomSpaceWriter<'a> { + atom: &'a mut AtomHeader, + parent: &'a mut dyn AllocateSpace<'a>, +} + +impl<'a> AtomSpaceWriter<'a> { + #[inline] + pub fn atom(&'a self) -> &'a AtomHeader { + self.atom + } + + /// Create a new framed space with the given parent and type URID. + pub fn write_new<'space: 'a, A: ?Sized>(parent: &'a mut impl AllocateSpace<'space>, urid: URID) -> Option { + let atom = AtomHeader::from_raw(sys::LV2_Atom { + size: 0, + type_: urid.get(), + }); + + let atom = crate::space::write_value(parent, atom)?; + Some(Self::new(atom, parent)) + } + + #[inline] + fn new<'space: 'a>(atom: &'a mut AtomHeader, parent: &'a mut dyn AllocateSpace<'space>) -> Self { + // SAFETY: Here we reduce the lifetime 'space to 'a, which is safe because 'space: 'a, and also because this reference will never be changed from now on. + let parent: &'a mut dyn AllocateSpace<'a> = unsafe { transmute(parent) }; + Self { atom, parent } + } +} + +impl<'a> AllocateSpace<'a> for AtomSpaceWriter<'a> { + #[inline] + fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { + let result = self.parent.allocate_unaligned(size); + if result.is_some() { + self.atom.as_raw_mut().size += size as u32; + } + + result + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + self.parent.as_bytes() + } + + #[inline] + fn as_bytes_mut(&mut self) -> &mut [u8] { + self.parent.as_bytes_mut() + } +} + +#[cfg(test)] +mod tests { + use core::mem::size_of; + use crate::prelude::AtomSpaceWriter; + use urid::URID; + + #[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 { + core::slice::from_raw_parts_mut( + (&mut memory).as_mut_ptr() as *mut u8, + MEMORY_SIZE * size_of::(), + ) + }; + + // writing + { + let mut root: &mut _ = raw_space; + let mut frame = + AtomSpaceWriter::write_new(&mut root, URID::<()>::new(1).unwrap()) + .unwrap(); + crate::space::write_value(&mut frame, 42u32).unwrap(); + crate::space::write_value(&mut frame, 17u32).unwrap(); + } + + // checking + { + let (atom, space) = raw_space.split_at(size_of::()); + 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::()); + 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::()); + let value = unsafe { *(value.as_ptr() as *const u32) }; + assert_eq!(value, 17); + } + } +} \ No newline at end of file diff --git a/atom/src/space/boxed.rs b/atom/src/space/boxed.rs new file mode 100644 index 00000000..d3094cfc --- /dev/null +++ b/atom/src/space/boxed.rs @@ -0,0 +1,38 @@ +use std::mem::{size_of, MaybeUninit}; +use std::ops::{Deref, DerefMut}; +use crate::prelude::Space; + +fn size_to_bytes(size: usize) -> usize { + let type_size = size_of::(); + if type_size == 0 { + 0 + } else { + size / type_size + if size % type_size > 0 { 1 } else { 0 } + } +} + +pub(crate) struct BoxedSpace { + inner: Box<[MaybeUninit]> +} + +impl BoxedSpace { + pub fn new_zeroed(size: usize) -> Self { + Self { inner: vec![MaybeUninit::zeroed(); size_to_bytes::(size)].into_boxed_slice() } + } +} + +impl Deref for BoxedSpace { + type Target = Space; + + #[inline] + fn deref(&self) -> &Self::Target { + Space::::from_uninit_slice(&self.inner) + } +} + +impl DerefMut for BoxedSpace { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + Space::::from_uninit_slice_mut(&mut self.inner) + } +} \ No newline at end of file diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 641248c4..ae881f94 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -1,21 +1,24 @@ use core::mem::{align_of, size_of}; -use std::mem::MaybeUninit; +use std::mem::{MaybeUninit, size_of_val}; use std::marker::PhantomData; use urid::URID; use crate::UnidentifiedAtom; +use std::ops::{DerefMut, Deref}; +use std::slice::{from_raw_parts, from_raw_parts_mut}; +use crate::header::AtomHeader; /// An aligned slice of bytes that is designed to contain a given type `T` (by default, Atoms). /// /// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. #[repr(transparent)] -pub struct Space { +pub struct Space { _type: PhantomData, // Note: this could be [MaybeUninit] for alignment, but Spaces can have extra unaligned bytes at the end. // TODO: replace this with [MaybeUninit] data: [u8] } -pub type AtomSpace = Space; +pub type AtomSpace = Space; impl Space { /// Creates an empty Space. @@ -39,7 +42,17 @@ impl Space { if start % alignment == 0 { 0 } else { alignment - start % alignment } } - pub fn boxed(size: usize) -> Box where T: Copy { + #[inline] + pub fn alignment() -> usize { + align_of::() + } + + pub fn boxed(size: usize) -> impl DerefMut where T: Copy { + crate::space::boxed::BoxedSpace::new_zeroed(size) + } + + pub fn boxed_broken(size: usize) -> Box where T: Copy { + todo!(); let type_size = size_of::(); let size = if type_size == 0 { 0 @@ -102,6 +115,22 @@ impl Space { Space::try_from_bytes(data).unwrap() } + #[inline] + pub(crate) fn from_uninit_slice(slice: &[MaybeUninit]) -> &Self { + // SAFETY: reinterpreting as raw bytes is safe for any value + let bytes = unsafe { from_raw_parts(slice.as_ptr() as *const u8, size_of_val(slice)) }; + // SAFETY: The pointer is a slice of T, therefore it is already correctly aligned + unsafe { Self::from_bytes_unchecked(bytes) } + } + + #[inline] + pub(crate) fn from_uninit_slice_mut(slice: &mut [MaybeUninit]) -> &mut Self { + // SAFETY: reinterpreting as raw bytes is safe for any value + let bytes = unsafe { from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of_val(slice)) }; + // SAFETY: The pointer is a slice of T, therefore it is already correctly aligned + unsafe { Self::from_bytes_mut_unchecked(bytes) } + } + /// Creates a new space from a slice of bytes. /// /// # Errors @@ -330,7 +359,7 @@ impl AtomSpace { /// /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Space { + pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Self { let data = std::slice::from_raw_parts( atom as *const sys::LV2_Atom as *const u8, atom.size as usize + size_of::(), @@ -346,7 +375,7 @@ impl AtomSpace { /// /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom_mut(atom: &mut sys::LV2_Atom) -> &mut Space { + pub unsafe fn from_atom_mut(atom: &mut sys::LV2_Atom) -> &mut Self { let data = std::slice::from_raw_parts_mut( atom as *mut sys::LV2_Atom as *mut u8, atom.size as usize + size_of::(), @@ -364,13 +393,13 @@ impl AtomSpace { pub unsafe fn to_atom(&self) -> Option { let header = self.read_unchecked()?; // Try to read to ensure there is enough room // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents - Some(UnidentifiedAtom::new_unchecked(self.slice(header.size as usize)?)) + Some(UnidentifiedAtom::new_unchecked(self.slice(header.size())?)) } #[inline] pub unsafe fn split_atom(&self) -> Option<(UnidentifiedAtom, &Self)> { let header = self.read_unchecked()?; - let (atom, rest) = self.split_at(header.size as usize)?; + let (atom, rest) = self.split_at(header.size())?; let atom = UnidentifiedAtom::new_unchecked(atom); Some((atom, rest)) @@ -382,20 +411,14 @@ impl AtomSpace { // SAFETY: The caller is responsible for ensuring there is a valid atom header in there. let header = &*header.as_ptr(); - if header.type_ != urid { + if header.urid() != urid { return None } - body.split_at(header.size as usize) + body.split_at(header.size()) } } -impl Space { - /*pub fn from_bytes() { - - }*/ -} - #[cfg(test)] mod tests { use crate::space::*; @@ -454,7 +477,7 @@ mod tests { } } - fn test_mut_space<'a, S: AllocateSpace<'a>>(mut space: S) { + fn test_mut_space<'a>(mut space: impl AllocateSpace<'a>) { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); @@ -470,35 +493,36 @@ mod tests { let written_atom = crate::space::write_value(&mut space, test_atom).unwrap(); assert_eq!(written_atom.size, test_atom.size); assert_eq!(written_atom.type_, test_atom.type_); + let written_atom_addr = written_atom as *mut _ as *mut _; let created_space = unsafe { Space::from_atom_mut(written_atom) }; - assert_eq!( - created_space.as_bytes().as_ptr() as usize, - written_atom as *mut _ as usize - ); + assert!(::core::ptr::eq(written_atom_addr, created_space.as_bytes().as_ptr())); assert_eq!(created_space.as_bytes().len(), size_of::() + 42); - let mut atom_frame = - AtomSpaceWriter::write_new(&mut space as &mut dyn AllocateSpace, urids.chunk).unwrap(); - - let mut test_data: Vec = vec![0; 24]; - for i in 0..test_data.len() { - test_data[i] = i as u8; + { + todo!() + /*let space: &mut _ = &mut space; + let _ = AtomSpaceWriter::write_new(space, urids.chunk).unwrap();*/ + + /*let mut test_data: Vec = vec![0; 24]; + for i in 0..test_data.len() { + test_data[i] = i as u8; + }/**/ + + let written_data = crate::space::write_bytes(&mut atom_frame, &test_data).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 written_atom = crate::space::write_value(&mut atom_frame, test_atom).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) + );*/ } - - let written_data = crate::space::write_bytes(&mut atom_frame, &test_data).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 written_atom = crate::space::write_value(&mut atom_frame, test_atom).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] @@ -522,44 +546,6 @@ mod tests { 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::(), - ) - }; - - // writing - { - let mut root = raw_space; - let mut frame = - AtomSpaceWriter::write_new(&mut root, URID::<()>::new(1).unwrap()) - .unwrap(); - crate::space::write_value(&mut frame, 42u32).unwrap(); - crate::space::write_value(&mut frame, 17u32).unwrap(); - } - - // checking - { - let (atom, space) = raw_space.split_at(size_of::()); - 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::()); - 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::()); - let value = unsafe { *(value.as_ptr() as *const u32) }; - assert_eq!(value, 17); - } - } #[test] fn unaligned_root_write() { diff --git a/atom/src/string.rs b/atom/src/string.rs index c2a7891e..610e9ae3 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -165,14 +165,15 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDs::from_map(&map).unwrap(); - let mut raw_space = Space::boxed(256); + let mut raw_space = Space::boxed_broken(256); // writing { let mut space = raw_space.as_bytes_mut(); let mut writer = crate::space::init_atom(&mut space, urids.atom.literal, LiteralInfo::Language(urids.german.into_general())).unwrap(); - writer.append(SAMPLE0).unwrap(); - writer.append(SAMPLE1).unwrap(); + todo!() + /*writer.append(SAMPLE0).unwrap(); + writer.append(SAMPLE1).unwrap();*/ } // verifying @@ -213,15 +214,16 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = Space::boxed(256); + let mut raw_space = Space::boxed_broken(256); // writing { let mut space = raw_space.as_bytes_mut(); let mut writer = crate::space::init_atom(&mut space, urids.string, ()).unwrap(); - writer.append(SAMPLE0).unwrap(); - writer.append(SAMPLE1).unwrap(); + todo!() + /*writer.append(SAMPLE0).unwrap(); + writer.append(SAMPLE1).unwrap();*/ } // verifying diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 05f53635..59df5e1b 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -104,7 +104,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = Space::boxed(256); + let mut raw_space = Space::boxed_broken(256); // writing { @@ -115,16 +115,17 @@ mod tests { writer.init::>(urids.vector, urids.int).unwrap(); vector_writer.append(&[17; 9]).unwrap(); } - writer.init::(urids.int, 42).unwrap(); + todo!() + //writer.init::(urids.int, 42).unwrap(); } // verifying { let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); let header = atom.header().unwrap(); - assert_eq!(header.type_, urids.tuple); + assert_eq!(header.urid(), urids.tuple); assert_eq!( - header.size as usize, + header.size(), size_of::() + size_of::() * 9 + 4 diff --git a/atom/src/vector.rs b/atom/src/vector.rs index a4e35060..6ebf8864 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -113,7 +113,6 @@ impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, A> { #[cfg(test)] mod tests { - use crate::prelude::*; use crate::space::*; use std::mem::size_of; use urid::*; @@ -125,23 +124,19 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = AtomSpace::boxed_broken(256); // writing { - let mut space = raw_space.as_mut(); - let mut writer = (&mut space as &mut dyn AllocateSpace) - .init(urids.vector(), urids.int) - .unwrap(); + let mut space = raw_space.as_bytes_mut(); + let mut writer = crate::space::init_atom(&mut space, urids.vector(), urids.int).unwrap(); writer.append(&[42; CHILD_COUNT - 1]); writer.push(1); } // verifying { - let (vector, children) = raw_space.split_at(size_of::()); - - let vector = unsafe { &*(vector.as_ptr() as *const sys::LV2_Atom_Vector) }; + let (vector, children) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(vector.atom.type_, urids.vector.get()); assert_eq!( vector.atom.size as usize, @@ -151,7 +146,7 @@ mod tests { assert_eq!(vector.body.child_type, urids.int.get()); let children = - unsafe { std::slice::from_raw_parts(children.as_ptr() as *const i32, CHILD_COUNT) }; + unsafe { std::slice::from_raw_parts(children.as_bytes().as_ptr() as *const i32, CHILD_COUNT) }; for value in &children[0..children.len() - 1] { assert_eq!(*value, 42); } @@ -160,9 +155,8 @@ mod tests { // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = space.split_atom_body(urids.vector).unwrap(); - let children: &[i32] = unsafe { Vector::::read(body, urids.int) }.unwrap(); + let atom = unsafe { raw_space.to_atom() }.unwrap(); + let children: &[i32] = atom.read(urids.vector, urids.int).unwrap(); assert_eq!(children.len(), CHILD_COUNT); for i in 0..children.len() - 1 { diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index b1c85e5b..4ff397e7 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -99,15 +99,16 @@ fn main() { let urids: URIDs = map.populate_collection().unwrap(); // Preparing the input atom. - let mut input_atom_space = AtomSpace::boxed(256); + let mut input_atom_space = AtomSpace::boxed_broken(256); { let mut space = input_atom_space.as_bytes_mut(); let mut writer = lv2_atom::space::init_atom(&mut space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); - {writer + {let _ = writer .init(TimeStamp::Frames(0), urids.atom.int, 42) - .unwrap();} + .unwrap(); + } writer .init(TimeStamp::Frames(1), urids.atom.long, 17) .unwrap(); @@ -117,7 +118,7 @@ fn main() { } // preparing the output atom. - let mut output_atom_space = Space::boxed(256); + let mut output_atom_space = Space::boxed_broken(256); { let mut space = output_atom_space.as_bytes_mut(); lv2_atom::space::init_atom(&mut space, @@ -147,12 +148,12 @@ fn main() { (plugin_descriptor.connect_port.unwrap())( plugin, 0, - input_atom_space.as_bytes().as_mut_ptr() as *mut c_void, + input_atom_space.as_bytes_mut().as_mut_ptr() as *mut c_void, ); (plugin_descriptor.connect_port.unwrap())( plugin, 1, - output_atom_space.as_bytes().as_mut_ptr() as *mut c_void, + output_atom_space.as_bytes_mut().as_mut_ptr() as *mut c_void, ); // Activate, run, deactivate. diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 5ebcd80c..a55f5f9b 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -111,7 +111,7 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space = Space::boxed(256); + let mut raw_space = Space::boxed_broken(256); let reference_message = MidiMessage::NoteOn(Channel::Ch1, Note::A0, Velocity::try_from(125).unwrap()); @@ -146,7 +146,7 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = AtomSpace::boxed_broken(256); // writing { diff --git a/state/src/raw.rs b/state/src/raw.rs index 0ba1a241..945c071d 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -54,13 +54,13 @@ impl<'a> StoreHandle<'a> { ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); - let space = Space::::from_bytes(&space); + let space = AtomSpace::from_bytes(&space); let (header, data) = unsafe { space.to_atom() }.and_then(|a| a.header_and_body()).ok_or(StateErr::BadData)?; let key = key.get(); let data_ptr = data as *const _ as *const c_void; - let data_size = header.size as usize; - let data_type = header.type_; + let data_size = header.size(); + let data_type = header.urid(); let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD | sys::LV2_State_Flags::LV2_STATE_IS_PORTABLE) .into(); diff --git a/urid/src/lib.rs b/urid/src/lib.rs index 77eb7f5c..cf6c754e 100644 --- a/urid/src/lib.rs +++ b/urid/src/lib.rs @@ -182,7 +182,7 @@ impl URID { /// A URID may not be 0 since this value is reserved for the `None` value of `Option>`, which therefore has the same size as a `URID`. If `T` is also a URI bound, the URID may only be the one that is mapped to the bounded URI. /// /// Since these constraints aren't checked by this method, it is unsafe. Using this method is technically sound as long as `raw_urid` is not zero, but might still result in bad behaviour if its the wrong URID for the bound `T`. - pub unsafe fn new_unchecked(raw_urid: u32) -> Self { + pub const unsafe fn new_unchecked(raw_urid: u32) -> Self { Self(NonZeroU32::new_unchecked(raw_urid), PhantomData) } From cbc82c44587793d5b10f9866907c9c35c14b5510 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 8 Aug 2021 05:03:58 +0200 Subject: [PATCH 05/54] Fix more things --- atom/src/chunk.rs | 3 +-- atom/src/header.rs | 7 ++++- atom/src/lib.rs | 4 ++- atom/src/object.rs | 51 +++++++++++++++++------------------ atom/src/sequence.rs | 18 ++++++------- atom/src/space/allocatable.rs | 9 +++---- atom/src/space/space.rs | 12 +++++---- atom/src/tuple.rs | 2 +- midi/src/wmidi_binding.rs | 8 +++--- state/src/raw.rs | 2 +- 10 files changed, 60 insertions(+), 56 deletions(-) diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 63543bec..0d2837e2 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -81,8 +81,7 @@ mod tests { *value = i as u8; } - todo!() - // space::write_value(&mut space, 41u8).unwrap(); + space::write_value(&mut space, 41u8).unwrap(); } // verifying diff --git a/atom/src/header.rs b/atom/src/header.rs index 231900f9..a555fff9 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -17,10 +17,15 @@ impl AtomHeader { } #[inline] - pub fn size(self) -> usize { + pub fn size_of_body(self) -> usize { self.inner.size as usize } + #[inline] + pub fn size_of_atom(self) -> usize { + self.size_of_body() + ::core::mem::size_of::() + } + #[inline] pub fn urid(self) -> u32 { self.inner.type_ diff --git a/atom/src/lib.rs b/atom/src/lib.rs index f1a09a22..0417ab3a 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -222,8 +222,10 @@ impl<'a> UnidentifiedAtom<'a> { #[inline] pub fn header_and_body(&self) -> Option<(&'a AtomHeader, &'a Space)> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. + let sl = self.space.as_bytes(); let (header, body) = unsafe { self.space.split_for_value_unchecked() }?; - let body = body.slice(header.size())?; + let bo = self.space.as_bytes(); + let body = body.slice(header.size_of_body())?; Some((&header, body)) } diff --git a/atom/src/object.rs b/atom/src/object.rs index 29dedce2..39e66e67 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -207,8 +207,8 @@ impl<'a> ObjectWriter<'a> { /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init<'c, K: ?Sized, A: Atom<'c, 'a>>( - &'a mut self, + pub fn init<'read, 'write, K: ?Sized, A: Atom<'read, 'write>>( + &'write mut self, key: URID, child_urid: URID, parameter: A::WriteParameter, @@ -288,6 +288,7 @@ mod tests { use crate::space::*; use std::mem::size_of; use urid::*; + use std::ops::Deref; #[test] fn test_object() { @@ -308,12 +309,12 @@ mod tests { .unwrap(); let second_value: f32 = 42.0; - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = AtomSpace::boxed(256); // writing { - let mut space = raw_space.as_mut(); - let frame = AtomSpaceWriter::write_new(&mut space, urids.object).unwrap(); + let mut cursor = raw_space.as_bytes_mut(); + let frame = AtomSpaceWriter::write_new(&mut cursor, urids.object).unwrap(); let mut writer = Object::init( frame, ObjectHeader { @@ -326,19 +327,22 @@ mod tests { writer.init(first_key, urids.int, first_value).unwrap(); } { - todo!() - // writer.init(second_key, urids.float, second_value).unwrap(); + writer.init(second_key, urids.float, second_value).unwrap(); } } + println!("{:?}", raw_space.as_bytes()); + // verifying { // Header - let (atom, space) = raw_space.split_at(size_of::()); - let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; - assert_eq!(atom.type_, urids.object); + let s = raw_space.deref(); + let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); + let header = atom.header().unwrap(); + let x = atom.as_space().as_bytes().len(); + assert_eq!(header.urid(), urids.object); assert_eq!( - atom.size as usize, + header.size_of_body(), size_of::() + size_of::() + 2 * size_of::() @@ -347,41 +351,36 @@ mod tests { ); // Object. - let (object, space) = space.split_at(size_of::()); - let object = unsafe { &*(object.as_ptr() as *const sys::LV2_Atom_Object_Body) }; + let (object, space) = unsafe { atom.body().unwrap().split_for_value_as_unchecked::() }.unwrap(); assert_eq!(object.id, 0); assert_eq!(object.otype, object_type); // First property. - let (property, space) = space.split_at(size_of::()); - let property = unsafe { &*(property.as_ptr() as *const sys::LV2_Atom_Property_Body) }; + let (property, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(property.key, first_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.int); assert_eq!(property.value.size as usize, size_of::()); - let (value, space) = space.split_at(size_of::()); - let value = unsafe { *(value.as_ptr() as *const i32) }; - assert_eq!(value, first_value); - let (_, space) = space.split_at(size_of::()); + let (value, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + assert_eq!(*value, first_value); + let (_, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); // Second property. - let (property, space) = space.split_at(size_of::()); - let property = unsafe { &*(property.as_ptr() as *const sys::LV2_Atom_Property_Body) }; + let (property, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(property.key, second_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.float); assert_eq!(property.value.size as usize, size_of::()); - let (value, _) = space.split_at(size_of::()); - let value = unsafe { *(value.as_ptr() as *const f32) }; - assert_eq!(value, second_value); + let (value, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + assert_eq!(*value, second_value); + assert_eq!(space.as_bytes().len(), 0); } // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = unsafe { space.split_atom_body(urids.object) }.unwrap(); + let (body, _) = unsafe { raw_space.split_atom_body(urids.object) }.unwrap(); let (header, iter) = unsafe { Object::read(body, ()) }.unwrap(); assert_eq!(header.otype, object_type); diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index b6cfc7b2..e5d18aa7 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -234,7 +234,7 @@ impl<'a> SequenceWriter<'a> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init<'read, 'write: 'a, A: Atom<'read, 'write>>( + pub fn init<'read, 'write, A: Atom<'read, 'write>>( &'write mut self, stamp: TimeStamp, urid: URID, @@ -262,7 +262,7 @@ impl<'a> SequenceWriter<'a> { mod tests { use crate::prelude::*; use crate::sequence::*; - use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; + use std::mem::size_of; #[derive(URIDCollection)] struct TestURIDCollection { @@ -286,16 +286,14 @@ mod tests { .init::(TimeStamp::Frames(0), urids.atom.int, 42) .unwrap(); - todo!() - /*writer + writer .init::(TimeStamp::Frames(1), urids.atom.long, 17) - .unwrap();*/ + .unwrap(); } // verifying - todo!() - /*{ + { let (sequence, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(sequence.atom.type_, urids.atom.sequence); assert_eq!( @@ -310,7 +308,7 @@ mod tests { assert_eq!(sequence.body.unit, urids.units.frame); let (stamp, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); - assert_eq!(stamp.frames, 0); + assert_eq!(unsafe { stamp.frames }, 0); let (int, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.int); @@ -318,7 +316,7 @@ mod tests { assert_eq!(int.body, 42); let (stamp, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); - assert_eq!(stamp.frames, 1); + assert_eq!(unsafe { stamp.frames }, 1); let (int, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.long); @@ -348,6 +346,6 @@ mod tests { assert_eq!(atom.read::(urids.atom.long, ()).unwrap(), 17); assert!(reader.next().is_none()); - }*/ + } } } diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index cc7b7ed8..27d313cb 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -59,9 +59,9 @@ pub fn allocate_values<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, count } #[inline] -pub fn init_atom<'a: 'write, 'read, 'write, A: Atom<'read, 'write>>(space: &'a mut impl AllocateSpace<'write>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { +pub fn init_atom<'space: 'write, 'read, 'write, A: Atom<'read, 'write>>(space: &'write mut impl AllocateSpace<'space>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { let space: AtomSpaceWriter<'write> = AtomSpaceWriter::write_new(space, atom_type)?; - A::init(space as AtomSpaceWriter<'write>, write_parameter) + A::init(space, write_parameter) } #[inline] @@ -99,9 +99,8 @@ pub fn write_values<'a, T>(space: &mut impl AllocateSpace<'a>, values: &[T]) -> #[cfg(test)] mod tests { use crate::space::{AtomSpace, write_value, init_atom}; - use crate::prelude::{Int, AtomSpaceWriter}; + use crate::prelude::Int; use urid::URID; - use crate::Atom; const INT_URID: URID = unsafe { URID::new_unchecked(5) }; @@ -121,7 +120,7 @@ mod tests { { let int_atom: &mut _ = init_atom(&mut cursor, INT_URID, 69).unwrap(); assert_eq!(69, *int_atom); - // assert_eq!(12, cursor.len()); TODO + assert_eq!(12, cursor.len()); } // let new_value = write_value(&mut cursor, 42u8).unwrap(); /*{ diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index ae881f94..5c464b9f 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -47,7 +47,7 @@ impl Space { align_of::() } - pub fn boxed(size: usize) -> impl DerefMut where T: Copy { + pub fn boxed(size: usize) -> impl Deref + DerefMut where T: Copy { crate::space::boxed::BoxedSpace::new_zeroed(size) } @@ -207,7 +207,9 @@ impl Space { #[inline] pub fn slice(&self, length: usize) -> Option<&Self> { // SAFETY: The data is part of the original slice which was aligned already. - Some(unsafe { Self::from_bytes_unchecked(self.data.get(..length)?) }) + &self.data[..length-1]; + let d = self.data.get(..(length - 1)); + Some(unsafe { Self::from_bytes_unchecked(d?) }) } #[inline] @@ -393,13 +395,13 @@ impl AtomSpace { pub unsafe fn to_atom(&self) -> Option { let header = self.read_unchecked()?; // Try to read to ensure there is enough room // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents - Some(UnidentifiedAtom::new_unchecked(self.slice(header.size())?)) + Some(UnidentifiedAtom::new_unchecked(self.slice(header.size_of_atom())?)) } #[inline] pub unsafe fn split_atom(&self) -> Option<(UnidentifiedAtom, &Self)> { let header = self.read_unchecked()?; - let (atom, rest) = self.split_at(header.size())?; + let (atom, rest) = self.split_at(header.size_of_atom())?; let atom = UnidentifiedAtom::new_unchecked(atom); Some((atom, rest)) @@ -415,7 +417,7 @@ impl AtomSpace { return None } - body.split_at(header.size()) + body.split_at(header.size_of_body()) } } diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 59df5e1b..b6ec69bc 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -125,7 +125,7 @@ mod tests { let header = atom.header().unwrap(); assert_eq!(header.urid(), urids.tuple); assert_eq!( - header.size(), + header.size_of_body(), size_of::() + size_of::() * 9 + 4 diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index a55f5f9b..7b0ab9d2 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -124,8 +124,8 @@ mod tests { // verifying { let (header, space) = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.header_and_body().unwrap(); - assert_eq!(header.type_, urid); - assert_eq!(header.size as usize, 3); + assert_eq!(header.urid(), urid); + assert_eq!(header.size_of_body(), 3); let message = space.slice(3).unwrap().as_bytes(); let message = MidiMessage::try_from(message).unwrap(); @@ -158,8 +158,8 @@ mod tests { // verifying { let (header, body) = unsafe { raw_space.to_atom() }.unwrap().header_and_body().unwrap(); - assert_eq!(header.type_, urid); - assert_eq!(header.size as usize, 6); + assert_eq!(header.urid(), urid); + assert_eq!(header.size_of_body(), 6); assert_eq!(&body.as_bytes()[..6], &[0xf0, 1, 2, 3, 4, 0xf7]); } diff --git a/state/src/raw.rs b/state/src/raw.rs index 945c071d..9eeafc15 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -59,7 +59,7 @@ impl<'a> StoreHandle<'a> { let key = key.get(); let data_ptr = data as *const _ as *const c_void; - let data_size = header.size(); + let data_size = header.size_of_body(); let data_type = header.urid(); let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD | sys::LV2_State_Flags::LV2_STATE_IS_PORTABLE) From 3d462e00865c8c15aa8df1e39a33a0c6c2dede1c Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 8 Aug 2021 15:23:52 +0200 Subject: [PATCH 06/54] Fixed almost everything --- atom/src/object.rs | 44 ++++++++++++++++++++++------------ atom/src/port.rs | 6 ++--- atom/src/sequence.rs | 2 +- atom/src/space/allocatable.rs | 7 ++++++ atom/src/space/space.rs | 27 +++++---------------- atom/src/string.rs | 4 ++-- atom/src/tuple.rs | 2 +- atom/src/vector.rs | 2 +- atom/tests/atom_integration.rs | 4 ++-- midi/src/wmidi_binding.rs | 4 ++-- sys/src/linux/aarch64.rs | 2 +- sys/src/linux/arm.rs | 2 +- sys/src/linux/x86.rs | 2 +- sys/src/linux/x86_64.rs | 2 +- sys/src/windows.rs | 2 +- 15 files changed, 59 insertions(+), 53 deletions(-) diff --git a/atom/src/object.rs b/atom/src/object.rs index 39e66e67..f103ca80 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -238,6 +238,16 @@ pub struct PropertyHeader { pub context: Option, } +#[repr(C, align(8))] +#[derive(Clone, Copy)] +/// A custom version of the property body that does not include the value atom header. +/// +/// We will retrieve/store it separately. +struct StrippedPropertyHeader { + key: u32, + context: u32, +} + impl Property { /// Read the body of a property atom from a space. /// @@ -247,17 +257,7 @@ impl Property { /// /// The caller must ensure that the given Space actually contains a valid property. unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, UnidentifiedAtom, &Space)> { - #[repr(C)] - #[derive(Clone, Copy)] - /// A custom version of the property body that does not include the value atom header. - /// - /// We will retrieve it separately. - struct StrippedPropertyBody { - key: u32, - context: u32, - } - - let (header, space) = space.split_for_value_as_unchecked::()?; + let (header, space) = space.split_for_value_as_unchecked::()?; let header = PropertyHeader { key: URID::try_from(header.key).ok()?, @@ -271,13 +271,18 @@ impl Property { /// Write out the header of a property atom. /// /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. + #[inline] fn write_header<'a, K: ?Sized, C: ?Sized>( space: &mut impl AllocateSpace<'a>, key: URID, context: Option>, ) -> Option<()> { - space::write_value(space, key.get())?; - space::write_value(space, context.map(URID::get).unwrap_or(0))?; + let header = StrippedPropertyHeader { + key: key.get(), + context: context.map(URID::get).unwrap_or(0) + }; + + space::write_value(space, header)?; Some(()) } } @@ -331,6 +336,16 @@ mod tests { } } + // Atom header: size: u32, type: u32 + // Object header: id: u32 = None, otype: u32 = object_type + // Object prop header1: key: u32 = first_key, context: u32 = 0 + // Object prop body atom: size: u32 = 4 type: u32 = int + // Int atom value: i32 = 17, padding(4) + // Object prop header12 key: u32 = first_key, context: u32 = 0 + // Object prop body atom: size: u32 = 4 type: u32 = int + // Float atom value: i32 = 69, padding(4) + + println!("{:?}", raw_space.as_bytes()); // verifying @@ -339,7 +354,7 @@ mod tests { let s = raw_space.deref(); let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); let header = atom.header().unwrap(); - let x = atom.as_space().as_bytes().len(); + let x = atom.body().unwrap().as_bytes().len(); assert_eq!(header.urid(), urids.object); assert_eq!( header.size_of_body(), @@ -364,7 +379,6 @@ mod tests { let (value, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(*value, first_value); - let (_, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); // Second property. let (property, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); diff --git a/atom/src/port.rs b/atom/src/port.rs index 2d7b5ae3..1bcf9b9d 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -135,7 +135,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing a chunk to indicate the size of the space. { @@ -149,14 +149,14 @@ mod tests { // Getting a writer with the port. { let mut writer = - unsafe { AtomPort::output_from_raw(NonNull::from(raw_space.as_mut()).cast(), 0) }; + unsafe { AtomPort::output_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) }; writer.init::(urids.int, 42).unwrap(); } // Reading { let reader = - unsafe { AtomPort::input_from_raw(NonNull::from(raw_space.as_mut()).cast(), 0) }; + unsafe { AtomPort::input_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) }; assert_eq!(reader.read::(urids.int, ()).unwrap(), 42); } } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index e5d18aa7..84f37033 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -275,7 +275,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing { diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 27d313cb..e1f4cc76 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -44,6 +44,13 @@ impl<'a> AllocateSpace<'a> for &'a mut [u8] { } } +#[inline] +pub fn realign<'a, T: 'static, S: AllocateSpace<'a>>(space: &mut S) -> Option<()> { + let required_padding = Space::::padding_for(space.as_bytes()); + let _ = space.allocate_unaligned(required_padding)?; + Some(()) +} + #[inline] pub fn allocate<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, size: usize) -> Option<&'a mut Space> { let required_padding = Space::::padding_for(space.as_bytes()); diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 5c464b9f..bce634b8 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -51,23 +51,6 @@ impl Space { crate::space::boxed::BoxedSpace::new_zeroed(size) } - pub fn boxed_broken(size: usize) -> Box where T: Copy { - todo!(); - let type_size = size_of::(); - let size = if type_size == 0 { - 0 - } else { - size / type_size + if size % type_size > 0 { 1 } else { 0 } - }; - - let boxed = vec![MaybeUninit::::zeroed(); size].into_boxed_slice(); - - // SAFETY: The slice is properly aligned as we allocated it as an array of T. - // SAFETY: Casting from zeroed memory to [u8] is safe. - // SAFETY: Casting from [u8] to Space is safe because the Space struct is repr(transparent). - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut Self) } - } - /// Creates a new space from a slice of bytes, without checking for padding correctness. /// /// # Safety @@ -193,6 +176,7 @@ impl Space { #[inline] fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { + let l = self.data.len(); if mid > self.data.len() { return None; } @@ -207,8 +191,7 @@ impl Space { #[inline] pub fn slice(&self, length: usize) -> Option<&Self> { // SAFETY: The data is part of the original slice which was aligned already. - &self.data[..length-1]; - let d = self.data.get(..(length - 1)); + let d = self.data.get(..length); Some(unsafe { Self::from_bytes_unchecked(d?) }) } @@ -253,9 +236,11 @@ impl Space { #[inline] pub unsafe fn split_for_value_as_unchecked(&self) -> Option<(&U, &Self)> { - let (value, rest) = self.realign()?.split_for_value_unchecked()?; + let l = self.as_bytes().len(); + let (value, rest) = self.realign()? + .split_for_value_unchecked()?; - Some((value, rest.realign()?)) + Some((value, rest.realign().unwrap_or(Self::empty()))) } #[inline] diff --git a/atom/src/string.rs b/atom/src/string.rs index 610e9ae3..40f4173f 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -165,7 +165,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDs::from_map(&map).unwrap(); - let mut raw_space = Space::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing { @@ -214,7 +214,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = Space::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing { diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index b6ec69bc..d2753c4d 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -104,7 +104,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = Space::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing { diff --git a/atom/src/vector.rs b/atom/src/vector.rs index 6ebf8864..8f814b11 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -124,7 +124,7 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing { diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 4ff397e7..f053572f 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -99,7 +99,7 @@ fn main() { let urids: URIDs = map.populate_collection().unwrap(); // Preparing the input atom. - let mut input_atom_space = AtomSpace::boxed_broken(256); + let mut input_atom_space = AtomSpace::boxed(256); { let mut space = input_atom_space.as_bytes_mut(); let mut writer = lv2_atom::space::init_atom(&mut space, @@ -118,7 +118,7 @@ fn main() { } // preparing the output atom. - let mut output_atom_space = Space::boxed_broken(256); + let mut output_atom_space = AtomSpace::boxed(256); { let mut space = output_atom_space.as_bytes_mut(); lv2_atom::space::init_atom(&mut space, diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 7b0ab9d2..d90741f4 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -111,7 +111,7 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space = Space::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); let reference_message = MidiMessage::NoteOn(Channel::Ch1, Note::A0, Velocity::try_from(125).unwrap()); @@ -146,7 +146,7 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space = AtomSpace::boxed_broken(256); + let mut raw_space = AtomSpace::boxed(256); // writing { diff --git a/sys/src/linux/aarch64.rs b/sys/src/linux/aarch64.rs index 22581d71..7f395a95 100644 --- a/sys/src/linux/aarch64.rs +++ b/sys/src/linux/aarch64.rs @@ -685,7 +685,7 @@ pub type __int32_t = ::std::os::raw::c_int; pub type __uint32_t = ::std::os::raw::c_uint; pub type __int64_t = ::std::os::raw::c_long; #[doc = " The header of an atom:Atom."] -#[repr(C)] +#[repr(C, align(8))] #[derive(Debug, Copy, Clone)] pub struct LV2_Atom { #[doc = "< Size in bytes, not including type and size."] diff --git a/sys/src/linux/arm.rs b/sys/src/linux/arm.rs index 8d6d0966..b74ab391 100644 --- a/sys/src/linux/arm.rs +++ b/sys/src/linux/arm.rs @@ -685,7 +685,7 @@ pub type __int32_t = ::std::os::raw::c_int; pub type __uint32_t = ::std::os::raw::c_uint; pub type __int64_t = ::std::os::raw::c_longlong; #[doc = " The header of an atom:Atom."] -#[repr(C)] +#[repr(C, align(8))] #[derive(Debug, Copy, Clone)] pub struct LV2_Atom { #[doc = "< Size in bytes, not including type and size."] diff --git a/sys/src/linux/x86.rs b/sys/src/linux/x86.rs index ca89369b..328e7b06 100644 --- a/sys/src/linux/x86.rs +++ b/sys/src/linux/x86.rs @@ -685,7 +685,7 @@ pub type __int32_t = ::std::os::raw::c_int; pub type __uint32_t = ::std::os::raw::c_uint; pub type __int64_t = ::std::os::raw::c_longlong; #[doc = " The header of an atom:Atom."] -#[repr(C)] +#[repr(C, align(8))] #[derive(Debug, Copy, Clone)] pub struct LV2_Atom { #[doc = "< Size in bytes, not including type and size."] diff --git a/sys/src/linux/x86_64.rs b/sys/src/linux/x86_64.rs index 438ccff2..cc535aea 100644 --- a/sys/src/linux/x86_64.rs +++ b/sys/src/linux/x86_64.rs @@ -685,7 +685,7 @@ pub type __int32_t = ::std::os::raw::c_int; pub type __uint32_t = ::std::os::raw::c_uint; pub type __int64_t = ::std::os::raw::c_long; #[doc = " The header of an atom:Atom."] -#[repr(C)] +#[repr(C, align(8))] #[derive(Debug, Copy, Clone)] pub struct LV2_Atom { #[doc = "< Size in bytes, not including type and size."] diff --git a/sys/src/windows.rs b/sys/src/windows.rs index 0b76865a..27397a77 100644 --- a/sys/src/windows.rs +++ b/sys/src/windows.rs @@ -681,7 +681,7 @@ pub const LV2_WORKER__schedule: &'static [u8; 41usize] = b"http://lv2plug.in/ns/ext/worker#schedule\0"; pub type va_list = *mut ::std::os::raw::c_char; #[doc = " The header of an atom:Atom."] -#[repr(C)] +#[repr(C, align(8))] #[derive(Debug, Copy, Clone)] pub struct LV2_Atom { #[doc = "< Size in bytes, not including type and size."] From 44c81d3a9ae7a7eb3a1f42deb2a43051116348ea Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 8 Aug 2021 18:47:52 +0200 Subject: [PATCH 07/54] Add VecSpace --- atom/src/chunk.rs | 14 ++-- atom/src/lib.rs | 2 - atom/src/object.rs | 7 +- atom/src/scalar.rs | 9 ++- atom/src/space.rs | 2 + atom/src/space/allocatable.rs | 2 +- atom/src/space/atom_writer.rs | 4 +- atom/src/space/boxed.rs | 7 +- atom/src/space/list.rs | 6 +- atom/src/space/space.rs | 119 ++++++++++++---------------------- atom/src/space/vec.rs | 87 +++++++++++++++++++++++++ atom/src/string.rs | 8 +-- atom/src/tuple.rs | 4 +- atom/src/vector.rs | 6 +- state/src/raw.rs | 6 +- 15 files changed, 164 insertions(+), 119 deletions(-) create mode 100644 atom/src/space/vec.rs diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 0d2837e2..4949b27b 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -17,7 +17,7 @@ //! let in_chunk: &[u8] = ports.input.read(urids.chunk, ()).unwrap(); //! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk, ()).unwrap(); //! -//! out_chunk.write_raw(in_chunk, false).unwrap(); +//! lv2_atom::space::write_bytes(&mut out_chunk, in_chunk).unwrap(); //! } //! ``` //! @@ -69,11 +69,11 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = AtomSpace::boxed(256); // writing { - let mut space: &mut _ = raw_space.as_mut(); + let mut space = raw_space.as_bytes_mut(); let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); let data = writer.allocate_unaligned(SLICE_LENGTH - 1).unwrap(); @@ -86,13 +86,13 @@ mod tests { // verifying { - let (atom, data) = raw_space.split_at(size_of::()); + let (atom, data) = raw_space.split_at(size_of::()).unwrap(); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; assert_eq!(atom.size as usize, SLICE_LENGTH); assert_eq!(atom.type_, urids.chunk.get()); - let data = data.split_at(SLICE_LENGTH).0; + let data = data.split_at(SLICE_LENGTH).unwrap().0.as_bytes(); for i in 0..SLICE_LENGTH { assert_eq!(data[i] as usize, i); } @@ -100,9 +100,7 @@ mod tests { // reading { - let space = Space::from_bytes(&raw_space); - - let data = unsafe { Chunk::read(space.split_atom_body(urids.chunk).unwrap().0, ()) }.unwrap(); + let data = unsafe { Chunk::read(raw_space.split_atom_body(urids.chunk).unwrap().0, ()) }.unwrap(); assert_eq!(data.len(), SLICE_LENGTH); for (i, value) in data.iter().enumerate() { diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 0417ab3a..a0f2483a 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -222,9 +222,7 @@ impl<'a> UnidentifiedAtom<'a> { #[inline] pub fn header_and_body(&self) -> Option<(&'a AtomHeader, &'a Space)> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - let sl = self.space.as_bytes(); let (header, body) = unsafe { self.space.split_for_value_unchecked() }?; - let bo = self.space.as_bytes(); let body = body.slice(header.size_of_body())?; Some((&header, body)) diff --git a/atom/src/object.rs b/atom/src/object.rs index f103ca80..e20b9226 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -345,16 +345,13 @@ mod tests { // Object prop body atom: size: u32 = 4 type: u32 = int // Float atom value: i32 = 69, padding(4) - - println!("{:?}", raw_space.as_bytes()); - // verifying { // Header let s = raw_space.deref(); let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); let header = atom.header().unwrap(); - let x = atom.body().unwrap().as_bytes().len(); + let x = atom.body().unwrap().len(); assert_eq!(header.urid(), urids.object); assert_eq!( header.size_of_body(), @@ -389,7 +386,7 @@ mod tests { let (value, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(*value, second_value); - assert_eq!(space.as_bytes().len(), 0); + assert_eq!(space.len(), 0); } // reading diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index 00fd0e39..e1397f34 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -171,11 +171,11 @@ mod tests { let map = HashURIDMapper::new(); let urid: URID = map.map_type().unwrap(); - let mut raw_space: Box<[u8]> = Box::new([0; 256]); + let mut raw_space = AtomSpace::boxed(256); // writing { - let mut space = raw_space.as_mut(); + let mut space = raw_space.as_bytes_mut(); crate::space::init_atom(&mut space, urid, value).unwrap(); } @@ -188,7 +188,7 @@ mod tests { body: B, } - let (scalar, _) = raw_space.split_at(size_of::()); + let (scalar, _) = raw_space.split_at(size_of::()).unwrap(); let scalar = unsafe { &*(scalar.as_ptr() as *const Scalar) }; assert_eq!(scalar.atom.type_, urid); @@ -198,8 +198,7 @@ mod tests { // reading { - let space = Space::from_bytes(raw_space.as_ref()); - let (body, _) = unsafe { space.split_atom_body(urid) }.unwrap(); + let (body, _) = unsafe { raw_space.split_atom_body(urid) }.unwrap(); unsafe { assert_eq!(A::read(body, ()).unwrap(), value); } } } diff --git a/atom/src/space.rs b/atom/src/space.rs index f97f0d21..4bb698a1 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -5,9 +5,11 @@ mod space; mod allocatable; mod atom_writer; mod boxed; +mod vec; pub use space::{AtomSpace, Space}; pub use list::{SpaceList, SpaceHead}; pub use allocatable::*; pub use atom_writer::AtomSpaceWriter; +pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index e1f4cc76..c6b51cf7 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -144,6 +144,6 @@ mod tests { 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); - assert_eq!(32, space.as_bytes().len()); + assert_eq!(32, space.len()); } } \ No newline at end of file diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 081ee5dd..a9f54cca 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -11,8 +11,8 @@ pub struct AtomSpaceWriter<'a> { impl<'a> AtomSpaceWriter<'a> { #[inline] - pub fn atom(&'a self) -> &'a AtomHeader { - self.atom + pub fn atom(&self) -> AtomHeader { + *self.atom } /// Create a new framed space with the given parent and type URID. diff --git a/atom/src/space/boxed.rs b/atom/src/space/boxed.rs index d3094cfc..ee594ef2 100644 --- a/atom/src/space/boxed.rs +++ b/atom/src/space/boxed.rs @@ -2,7 +2,7 @@ use std::mem::{size_of, MaybeUninit}; use std::ops::{Deref, DerefMut}; use crate::prelude::Space; -fn size_to_bytes(size: usize) -> usize { +pub(crate) fn byte_index_to_value_index(size: usize) -> usize { let type_size = size_of::(); if type_size == 0 { 0 @@ -12,12 +12,13 @@ fn size_to_bytes(size: usize) -> usize { } pub(crate) struct BoxedSpace { - inner: Box<[MaybeUninit]> + pub(crate) inner: Box<[MaybeUninit]> } impl BoxedSpace { + #[inline] pub fn new_zeroed(size: usize) -> Self { - Self { inner: vec![MaybeUninit::zeroed(); size_to_bytes::(size)].into_boxed_slice() } + Self { inner: vec![MaybeUninit::zeroed(); byte_index_to_value_index::(size)].into_boxed_slice() } } } diff --git a/atom/src/space/list.rs b/atom/src/space/list.rs index 64bcf83b..b9bddba7 100644 --- a/atom/src/space/list.rs +++ b/atom/src/space/list.rs @@ -71,11 +71,11 @@ impl SpaceList { /// let mut head = SpaceHead::new(&mut element); /// /// // Writing an integer. -/// (&mut head as &mut dyn MutSpace).init(urids.int, 42).unwrap(); +/// lv2_atom::space::init_atom(&mut head, urids.int, 42).unwrap(); /// -/// // Retrieving a continuos vector with the written data and verifying it's contents. +/// // Retrieving a continuos vector with the written data and verifying its contents. /// let written_data: Vec = element.to_vec(); -/// let atom = unsafe { UnidentifiedAtom::new_unchecked(Space::from_slice(written_data.as_ref())) }; +/// let atom = unsafe { AtomSpace::try_from_bytes(&written_data).unwrap().to_atom().unwrap() }; /// assert_eq!(42, atom.read(urids.int, ()).unwrap()); /// ``` pub struct SpaceHead<'a> { diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index bce634b8..27c3f1d4 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -28,13 +28,6 @@ impl Space { unsafe { Self::from_bytes_unchecked(&[]) } } - /// Creates an empty mutable Space. - #[inline] - fn empty_mut() -> &'static mut Space { - // SAFETY: empty slices are always aligned - unsafe { Self::from_bytes_mut_unchecked(&mut []) } - } - #[inline] pub(crate) fn padding_for(data: &[u8]) -> usize { let alignment = align_of::(); @@ -58,7 +51,7 @@ impl Space { /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. /// Otherwise, atom reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. /// - /// For a safe, checked version, see [`Space::from_bytes`]. + /// For a safe, checked version, see [`Space::try_from_bytes`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] @@ -75,7 +68,7 @@ impl Space { /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. /// Otherwise, atom reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. /// - /// For a safe, checked version, see [`Space::from_bytes`]. + /// For a safe, checked version, see [`Space::try_from_bytes_mut`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] @@ -85,19 +78,6 @@ impl Space { &mut *(data as *mut _ as *mut Self) } - /// Creates a new space from a slice of bytes. - /// - /// # Panics - /// - /// This method panics if the given slice's offset is not 64-bit aligned - /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). - /// - /// For a non-panicking version, see [`Space::try_from_bytes`]. - #[inline] - pub fn from_bytes(data: &[u8]) -> &Self { - Space::try_from_bytes(data).unwrap() - } - #[inline] pub(crate) fn from_uninit_slice(slice: &[MaybeUninit]) -> &Self { // SAFETY: reinterpreting as raw bytes is safe for any value @@ -176,7 +156,6 @@ impl Space { #[inline] fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { - let l = self.data.len(); if mid > self.data.len() { return None; } @@ -195,19 +174,6 @@ impl Space { Some(unsafe { Self::from_bytes_unchecked(d?) }) } - #[inline] - fn split_bytes_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut [u8])> { - if mid > self.data.len() { - return None; - } - - let (start, end) = self.data.split_at_mut(mid); - // SAFETY: Because this data was the start of an existing Space, it was aligned already. - let start = unsafe { Self::from_bytes_mut_unchecked(start) }; - - Some((start, end)) - } - /// Try to retrieve space. /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. @@ -236,21 +202,11 @@ impl Space { #[inline] pub unsafe fn split_for_value_as_unchecked(&self) -> Option<(&U, &Self)> { - let l = self.as_bytes().len(); - let (value, rest) = self.realign()? - .split_for_value_unchecked()?; + let (value, rest) = self.realign()?.split_for_value_unchecked()?; Some((value, rest.realign().unwrap_or(Self::empty()))) } - #[inline] - fn split_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut Self)> { - let (start, end) = self.split_bytes_at_mut(mid)?; - let end = Self::try_align_from_bytes_mut(end).unwrap_or(Space::empty_mut()); - - Some((start, end)) - } - #[inline] pub fn realign(&self) -> Option<&Space> { Space::::try_align_from_bytes(self.as_bytes()) @@ -272,6 +228,21 @@ impl Space { &self.data } + #[inline] + pub fn as_ptr(&self) -> *const u8 { + self.data.as_ptr() + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.data.as_mut_ptr() + } + + #[inline] + pub fn len(&self) -> usize { + self.data.len() + } + /// Return the internal slice of the space. #[inline] pub fn as_bytes_mut(&mut self) -> &mut [u8] { @@ -326,6 +297,12 @@ impl Space { unsafe { ::core::slice::from_raw_parts(self.data.as_ptr() as *const MaybeUninit, self.data.len() / size_of::()) } } + #[inline] + pub unsafe fn assume_init(&self) -> &[T] { + let data = self.as_uninit_slice(); + &*(data as *const _ as *const [T]) + } + /// Gets the contents as a slice of potentially uninitialized `T`s. /// /// The resulting slice contains as many values as can fit in the original space. @@ -351,7 +328,8 @@ impl AtomSpace { atom as *const sys::LV2_Atom as *const u8, atom.size as usize + size_of::(), ); - Self::from_bytes(data) + + Self::from_bytes_unchecked(data) } /// Create a new mutable space from an atom pointer. @@ -412,23 +390,20 @@ mod tests { use std::mem::{size_of, size_of_val}; use urid::*; - fn aligned_buf() -> Box<[u8]> { - unsafe { Box::from_raw(Box::into_raw(vec![0u64; 64].into_boxed_slice()) as *mut [u8]) } - } - #[test] fn test_space() { - let mut vector = aligned_buf(); + let mut space = Space::::boxed(256); + let bytes = space.as_bytes_mut(); + for i in 0..128 { - vector[i] = i as u8; + bytes[i] = i as u8; } unsafe { - let ptr = vector.as_mut_ptr().add(128) as *mut u32; + let ptr = space.as_mut_ptr().add(128) as *mut u32; *(ptr) = 0x42424242; } - let space = Space::::from_bytes(&vector); let (lower_space, space) = space.split_at(128).unwrap(); let lower_space = lower_space.as_bytes(); @@ -442,12 +417,12 @@ mod tests { #[test] fn test_split_atom() { - let mut data = aligned_buf(); + let mut space = AtomSpace::boxed(256); let urid: URID = unsafe { URID::new_unchecked(17) }; // Writing an integer atom. unsafe { - *(data.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { + *(space.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { atom: sys::LV2_Atom { size: size_of::() as u32, type_: urid.get(), @@ -455,12 +430,11 @@ mod tests { body: 42, }; - let space = Space::from_bytes(&data); let (atom, _) = space.split_atom().unwrap(); let body = atom.body().unwrap().as_bytes(); assert_eq!(size_of::(), size_of_val(body)); - assert_eq!(42, unsafe { *(body.as_ptr() as *const i32) }); + assert_eq!(42, *(body.as_ptr() as *const i32)); } } @@ -485,30 +459,29 @@ mod tests { let created_space = unsafe { Space::from_atom_mut(written_atom) }; assert!(::core::ptr::eq(written_atom_addr, created_space.as_bytes().as_ptr())); - assert_eq!(created_space.as_bytes().len(), size_of::() + 42); + assert_eq!(created_space.len(), size_of::() + 42); { - todo!() - /*let space: &mut _ = &mut space; - let _ = AtomSpaceWriter::write_new(space, urids.chunk).unwrap();*/ + let space: &mut _ = &mut space; + let mut atom_frame = AtomSpaceWriter::write_new(space, urids.chunk).unwrap(); - /*let mut test_data: Vec = vec![0; 24]; + let mut test_data: Vec = vec![0; 24]; for i in 0..test_data.len() { test_data[i] = i as u8; - }/**/ + } let written_data = crate::space::write_bytes(&mut atom_frame, &test_data).unwrap(); assert_eq!(test_data.as_slice(), written_data); - assert_eq!(atom_frame.atom().size, test_data.len() as u32); + assert_eq!(atom_frame.atom().size_of_body(), test_data.len()); let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; let written_atom = crate::space::write_value(&mut atom_frame, test_atom).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, + atom_frame.atom().size_of_body(), test_data.len() + size_of_val(&test_atom) - );*/ + ); } } @@ -526,14 +499,6 @@ mod tests { test_mut_space(frame); } - #[test] - fn test_space_head() { - let mut space = SpaceList::default(); - let head = SpaceHead::new(&mut space); - test_mut_space(head); - } - - #[test] fn unaligned_root_write() { let mut raw_space = Box::new([0u8; 8]); diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs new file mode 100644 index 00000000..035c861a --- /dev/null +++ b/atom/src/space/vec.rs @@ -0,0 +1,87 @@ +use std::mem::{MaybeUninit, transmute}; +use crate::space::{AllocateSpace, Space}; +use std::ops::Range; + +pub struct VecSpace { + inner: Vec> +} + +impl VecSpace { + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self { inner: vec![MaybeUninit::zeroed(); capacity] } + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + Space::::from_uninit_slice(&self.inner).as_bytes() + } + + #[inline] + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + Space::::from_uninit_slice_mut(&mut self.inner).as_bytes_mut() + } + + #[inline] + fn get_or_allocate_bytes_mut<'a>(&'a mut self, byte_range: Range) -> Option<&'a mut [u8]> { + let byte_len = self.inner.len() * ::core::mem::size_of::(); + let max = byte_range.start.max(byte_range.end); + + if max > byte_len { + let new_size = crate::space::boxed::byte_index_to_value_index::(max); + self.inner.resize(new_size, MaybeUninit::zeroed()); + } + + self.as_bytes_mut().get_mut(byte_range) + } + + #[inline] + pub fn cursor(&mut self) -> VecSpaceCursor { + VecSpaceCursor { vec: self, byte_index: 0 } + } +} + +pub struct VecSpaceCursor<'vec, T> { + vec: &'vec mut VecSpace, + byte_index: usize +} + +impl<'vec, T: Copy + 'static> AllocateSpace<'vec> for VecSpaceCursor<'vec, T> { + fn allocate_unaligned(&mut self, size: usize) -> Option<&'vec mut [u8]> { + let end = self.byte_index.checked_add(size)?; + // FIXME: this is really ugly + let vec: &'vec mut VecSpace = todo!(); + VecSpace::::get_or_allocate_bytes_mut(vec, self.byte_index..end) + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + self.vec.as_bytes() + } + + #[inline] + fn as_bytes_mut(&mut self) -> &mut [u8] { + self.vec.as_bytes_mut() + } +} + +#[cfg(test)] +mod tests { + use crate::space::{VecSpace, AllocateSpace}; + + #[test] + pub fn test_lifetimes () { + let mut buffer = VecSpace::::with_capacity(16); + let mut bytes = { + let mut cursor = buffer.cursor(); + let buf1 = cursor.allocate_unaligned(2).unwrap(); + let buf2 = cursor.allocate_unaligned(2).unwrap(); + buf1[0] = 5; + }; +/* + let other_cursor2 = buffer.cursor(); + let other_cursor = buffer.cursor();*/ + // We can't do this: + //bytes[0] = 5; + } +} \ No newline at end of file diff --git a/atom/src/string.rs b/atom/src/string.rs index 40f4173f..9e892ff9 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -123,9 +123,10 @@ impl<'a, 'b> StringWriter<'a> { /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. - pub fn append(&'a mut self, string: &str) -> Option<&'a mut str> { + pub fn append(&mut self, string: &str) -> Option<&'a mut str> { let data = string.as_bytes(); let space = crate::space::write_bytes(&mut self.frame, data)?; + // FIXME: make a "rewind" function to write the nul byte later unsafe { Some(std::str::from_utf8_unchecked_mut(space)) } } } @@ -171,9 +172,8 @@ mod tests { { let mut space = raw_space.as_bytes_mut(); let mut writer = crate::space::init_atom(&mut space, urids.atom.literal, LiteralInfo::Language(urids.german.into_general())).unwrap(); - todo!() - /*writer.append(SAMPLE0).unwrap(); - writer.append(SAMPLE1).unwrap();*/ + writer.append(SAMPLE0).unwrap(); + writer.append(SAMPLE1).unwrap(); } // verifying diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index d2753c4d..973872d5 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -83,8 +83,8 @@ pub struct TupleWriter<'a> { impl<'a, 'b> TupleWriter<'a> { /// Initialize a new tuple element. - pub fn init<'c, A: Atom<'c, 'a>>( - &'a mut self, + pub fn init<'read, 'write, A: Atom<'read, 'write>>( + &'write mut self, child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { diff --git a/atom/src/vector.rs b/atom/src/vector.rs index 8f814b11..28270e0c 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -61,10 +61,8 @@ impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector where C: 'b, { return None; } - let data = body.aligned::()?.as_uninit_slice(); - - // SAFETY: Assume Init: We can assume this data was properly initialized by the host. - Some(&*(data as *const _ as *const [C::InternalType])) + // SAFETY: We can assume this data was properly initialized by the host. + Some(body.aligned()?.assume_init()) } fn init(mut frame: AtomSpaceWriter<'b>, child_urid: URID) -> Option> { diff --git a/state/src/raw.rs b/state/src/raw.rs index 9eeafc15..6252a471 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -54,7 +54,7 @@ impl<'a> StoreHandle<'a> { ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); - let space = AtomSpace::from_bytes(&space); + let space = AtomSpace::try_from_bytes(&space).ok_or(StateErr::BadData)?; let (header, data) = unsafe { space.to_atom() }.and_then(|a| a.header_and_body()).ok_or(StateErr::BadData)?; let key = key.get(); @@ -175,7 +175,7 @@ impl<'a> RetrieveHandle<'a> { return Err(StateErr::NoProperty); }; - Ok(StatePropertyReader::new(type_, Space::from_bytes(space))) + Ok(StatePropertyReader::new(type_, Space::try_from_bytes(space).ok_or(StateErr::BadData)?)) } } @@ -309,7 +309,7 @@ mod tests { } 3 => { assert_eq!(urids.vector::(), *type_); - let space = Space::from_bytes(value.as_slice()); + let space = Space::try_from_bytes(value.as_slice()).unwrap(); let data = unsafe { Vector::read(space, urids.int) }.unwrap(); assert_eq!([1, 2, 3, 4], data); } From e2c099fbf35543245da8812b8811b4798053744c Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sun, 8 Aug 2021 21:50:21 +0200 Subject: [PATCH 08/54] Fix almost everything --- atom/src/chunk.rs | 13 ++++----- atom/src/lib.rs | 30 +++++++++---------- atom/src/object.rs | 55 ++++++++++++++++------------------- atom/src/scalar.rs | 13 ++++----- atom/src/sequence.rs | 22 +++++++------- atom/src/space/allocatable.rs | 18 ++++++------ atom/src/space/atom_writer.rs | 24 +++++---------- atom/src/space/vec.rs | 26 +++++++---------- atom/src/string.rs | 36 +++++++++++------------ atom/src/tuple.rs | 23 +++++++-------- atom/src/vector.rs | 22 +++++++------- 11 files changed, 129 insertions(+), 153 deletions(-) diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 4949b27b..6f23eb93 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -37,20 +37,17 @@ unsafe impl UriBound for Chunk { const URI: &'static [u8] = sys::LV2_ATOM__Chunk; } -impl<'a, 'b> Atom<'a, 'b> for Chunk -where - 'a: 'b, -{ +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { type ReadParameter = (); - type ReadHandle = &'a [u8]; + type ReadHandle = &'handle [u8]; type WriteParameter = (); - type WriteHandle = AtomSpaceWriter<'b>; + type WriteHandle = AtomSpaceWriter<'handle, 'space>; - unsafe fn read(space: &'a Space, _: ()) -> Option<&'a [u8]> { + unsafe fn read(space: &'handle Space, _: ()) -> Option<&'handle [u8]> { Some(space.as_bytes()) } - fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { Some(frame) } } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index a0f2483a..ea6adeac 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -128,7 +128,7 @@ impl AtomURIDCollection { /// This is the foundation of this crate: Types that implement `Atom` define the reading and writing functions for an atom type. However, these types will never be constructed; They are only names to be used for generic type arguments. /// /// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom. -pub trait Atom<'read, 'write>: UriBound +pub trait Atom<'handle, 'space: 'handle>: UriBound { /// The atom-specific parameter of the `read` function. /// @@ -138,7 +138,7 @@ pub trait Atom<'read, 'write>: UriBound /// The return value of the `read` function. /// /// It may contain a reference to the atom and therefore may not outlive it. - type ReadHandle: 'read; + type ReadHandle: 'handle; /// The atom-specific parameter of the `write` function. /// @@ -148,7 +148,7 @@ pub trait Atom<'read, 'write>: UriBound /// The return value of the `write` function. /// /// It may contain a reference to a `MutSpace` and therefore may not outlive it. - type WriteHandle: 'write; + type WriteHandle: 'handle; /// Reads the body of the atom. /// @@ -160,7 +160,7 @@ pub trait Atom<'read, 'write>: UriBound /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: &'read Space, parameter: Self::ReadParameter) -> Option; + unsafe fn read(body: &'space Space, parameter: Self::ReadParameter) -> Option; /// Initialize the body of the atom. /// @@ -171,7 +171,7 @@ pub trait Atom<'read, 'write>: UriBound /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. fn init( - frame: AtomSpaceWriter<'write>, + frame: AtomSpaceWriter<'handle, 'space>, parameter: Self::WriteParameter, ) -> Option; } @@ -181,26 +181,26 @@ pub trait Atom<'read, 'write>: UriBound /// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. #[derive(Clone, Copy)] // TODO: refactor this so it directly contains the atom and is not a fat pointer to the space that has to be re-checked every time -pub struct UnidentifiedAtom<'a> { - space: &'a AtomSpace, +pub struct UnidentifiedAtom<'space> { + space: &'space AtomSpace, } -impl<'a> UnidentifiedAtom<'a> { +impl<'space> UnidentifiedAtom<'space> { /// Construct a new unidentified atom. /// /// # Safety /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body.x #[inline] - pub unsafe fn new_unchecked(space: &'a AtomSpace) -> Self { + pub unsafe fn new_unchecked(space: &'space AtomSpace) -> Self { Self { space } } /// Try to read the atom. /// /// To identify the atom, it's URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read<'b, A: Atom<'a, 'b>>( - &'a self, + pub fn read<'handle, A: Atom<'handle, 'space>>( + &self, urid: URID, parameter: A::ReadParameter, ) -> Option { @@ -215,12 +215,12 @@ impl<'a> UnidentifiedAtom<'a> { } #[inline] - pub fn as_space(&self) -> &'a AtomSpace { + pub fn as_space(&self) -> &'space AtomSpace { self.space } #[inline] - pub fn header_and_body(&self) -> Option<(&'a AtomHeader, &'a Space)> { + pub fn header_and_body(&self) -> Option<(&'space AtomHeader, &'space Space)> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. let (header, body) = unsafe { self.space.split_for_value_unchecked() }?; let body = body.slice(header.size_of_body())?; @@ -229,13 +229,13 @@ impl<'a> UnidentifiedAtom<'a> { } #[inline] - pub fn header(&self) -> Option<&'a AtomHeader> { + pub fn header(&self) -> Option<&'space AtomHeader> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. unsafe { self.space.read_unchecked() } } #[inline] - pub fn body(&self) -> Option<&'a Space> { + pub fn body(&self) -> Option<&'space Space> { self.header_and_body().map(|(_header, body)| body) } } diff --git a/atom/src/object.rs b/atom/src/object.rs index e20b9226..346c554e 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -93,16 +93,13 @@ pub struct ObjectHeader { pub otype: URID, } -impl<'a, 'b> Atom<'a, 'b> for Object -where - 'a: 'b, -{ +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { type ReadParameter = (); - type ReadHandle = (ObjectHeader, ObjectReader<'a>); + type ReadHandle = (ObjectHeader, ObjectReader<'handle>); type WriteParameter = ObjectHeader; - type WriteHandle = ObjectWriter<'b>; + type WriteHandle = ObjectWriter<'handle, 'space>; - unsafe fn read(body: &'a Space, _: ()) -> Option<(ObjectHeader, ObjectReader<'a>)> { + unsafe fn read(body: &'handle Space, _: ()) -> Option<(ObjectHeader, ObjectReader<'handle>)> { let (header, body) = body.split_for_value_as_unchecked::()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), @@ -115,14 +112,14 @@ where } fn init( - mut frame: AtomSpaceWriter<'b>, + mut frame: AtomSpaceWriter<'handle, 'space>, header: ObjectHeader, - ) -> Option> { + ) -> Option> { { - space::write_value(&mut frame, sys::LV2_Atom_Object_Body { + let x = space::write_value(&mut frame, sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), otype: header.otype.get(), - }); + })?; } Some(ObjectWriter { frame }) } @@ -139,22 +136,20 @@ unsafe impl UriBound for Blank { const URI: &'static [u8] = sys::LV2_ATOM__Blank; } -impl<'a, 'b> Atom<'a, 'b> for Blank -where - 'a: 'b, -{ - type ReadParameter = >::ReadParameter; - type ReadHandle = >::ReadHandle; - type WriteParameter = >::WriteParameter; - type WriteHandle = >::WriteHandle; +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Blank { + type ReadParameter = >::ReadParameter; + type ReadHandle = >::ReadHandle; + type WriteParameter = >::WriteParameter; + type WriteHandle = >::WriteHandle; #[allow(clippy::unit_arg)] - unsafe fn read(body: &'a Space, parameter: Self::ReadParameter) -> Option { + #[inline] + unsafe fn read(body: &'handle Space, parameter: Self::ReadParameter) -> Option { Object::read(body, parameter) } fn init( - frame: AtomSpaceWriter<'b>, + frame: AtomSpaceWriter<'handle, 'space>, parameter: Self::WriteParameter, ) -> Option { Object::init(frame, parameter) @@ -183,16 +178,16 @@ impl<'a> Iterator for ObjectReader<'a> { /// Writing handle for object properties. /// /// This handle is a safeguard to assure that a object is always a series of properties. -pub struct ObjectWriter<'space> { - frame: AtomSpaceWriter<'space>, +pub struct ObjectWriter<'handle, 'space: 'handle> { + frame: AtomSpaceWriter<'handle, 'space>, } -impl<'a> ObjectWriter<'a> { +impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. - pub fn init_with_context<'c, K: ?Sized, T: ?Sized, A: Atom<'c, 'a>>( - &'a mut self, + pub fn init_with_context<'read, K: ?Sized, T: ?Sized, A: Atom<'read, 'space>>( + &'space mut self, key: URID, context: URID, child_urid: URID, @@ -207,8 +202,8 @@ impl<'a> ObjectWriter<'a> { /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init<'read, 'write, K: ?Sized, A: Atom<'read, 'write>>( - &'write mut self, + pub fn init<'read, 'write, K: ?Sized, A: Atom<'read, 'space>>( + &'space mut self, key: URID, child_urid: URID, parameter: A::WriteParameter, @@ -272,8 +267,8 @@ impl Property { /// /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. #[inline] - fn write_header<'a, K: ?Sized, C: ?Sized>( - space: &mut impl AllocateSpace<'a>, + fn write_header<'a, 'space, K: ?Sized, C: ?Sized>( + space: &'a mut impl AllocateSpace<'space>, key: URID, context: Option>, ) -> Option<()> { diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index e1397f34..f5be2447 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -56,25 +56,22 @@ pub trait ScalarAtom: UriBound { /// Try to write the atom into a space. /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. - fn write_scalar(mut frame: AtomSpaceWriter, value: Self::InternalType) -> Option<&mut Self::InternalType> { + fn write_scalar<'handle, 'space: 'handle>(mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType) -> Option<&'handle mut Self::InternalType> { space::write_value(&mut frame, value) } } -impl<'a, 'b, A: ScalarAtom> Atom<'a, 'b> for A -where - 'a: 'b, -{ +impl<'handle, 'space: 'handle, A: ScalarAtom> Atom<'handle, 'space> for A { type ReadParameter = (); type ReadHandle = A::InternalType; type WriteParameter = A::InternalType; - type WriteHandle = &'b mut A::InternalType; + type WriteHandle = &'handle mut A::InternalType; - unsafe fn read(body: &'a Space, _: ()) -> Option { + unsafe fn read(body: &'space Space, _: ()) -> Option { ::read_scalar(body) } - fn init(frame: AtomSpaceWriter, value: A::InternalType) -> Option<&mut A::InternalType> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, value: A::InternalType) -> Option<&'handle mut A::InternalType> { ::write_scalar(frame, value) } } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index 84f37033..afb4e303 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -80,11 +80,11 @@ unsafe impl UriBound for Sequence { const URI: &'static [u8] = sys::LV2_ATOM__Sequence; } -impl<'a, 'b> Atom<'a, 'b> for Sequence { +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { type ReadParameter = URID; - type ReadHandle = SequenceIterator<'a>; + type ReadHandle = SequenceIterator<'handle>; type WriteParameter = TimeStampURID; - type WriteHandle = SequenceWriter<'b>; + type WriteHandle = SequenceWriter<'handle, 'space>; unsafe fn read(body: &Space, bpm_urid: URID) -> Option { let (header, body) = body.split_for_value_as_unchecked::()?; @@ -96,7 +96,7 @@ impl<'a, 'b> Atom<'a, 'b> for Sequence { Some(SequenceIterator { space: body, unit }) } - fn init(mut frame: AtomSpaceWriter<'b>, unit: TimeStampURID) -> Option> { + fn init(mut frame: AtomSpaceWriter<'handle, 'space>, unit: TimeStampURID) -> Option> { let header = sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), @@ -191,20 +191,20 @@ impl<'a> Iterator for SequenceIterator<'a> { } /// The writing handle for sequences. -pub struct SequenceWriter<'a> { - frame: AtomSpaceWriter<'a>, +pub struct SequenceWriter<'handle, 'space> { + frame: AtomSpaceWriter<'handle, 'space>, unit: TimeStampUnit, last_stamp: Option, } -impl<'a> SequenceWriter<'a> { +impl<'handle, 'space> SequenceWriter<'handle, 'space> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: /// * The time stamp is not measured in our unit. /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. - fn write_time_stamp(&mut self, stamp: TimeStamp) -> Option<()> { + fn write_time_stamp(&'handle mut self, stamp: TimeStamp) -> Option<()> { let raw_stamp = match self.unit { TimeStampUnit::Frames => { let frames = stamp.as_frames()?; @@ -234,8 +234,8 @@ impl<'a> SequenceWriter<'a> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init<'read, 'write, A: Atom<'read, 'write>>( - &'write mut self, + pub fn init>( + &'handle mut self, stamp: TimeStamp, urid: URID, parameter: A::WriteParameter, @@ -249,7 +249,7 @@ impl<'a> SequenceWriter<'a> { /// If your cannot identify the type of the atom but have to write it, you can simply forward it. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { + pub fn forward(&'handle mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { let data = atom.space.as_bytes(); self.write_time_stamp(stamp)?; space::write_bytes(&mut self.frame, data)?; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index c6b51cf7..c54fb007 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -8,13 +8,13 @@ use std::mem::MaybeUninit; /// A smart pointer that writes atom data to an internal slice. /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. -pub trait AllocateSpace<'a>: 'a { +pub trait AllocateSpace<'a> { /// Try to allocate memory on the internal data slice. /// /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. - fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]>; + fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]>; fn as_bytes(&self) -> &[u8]; fn as_bytes_mut(&mut self) -> &mut [u8]; @@ -52,7 +52,7 @@ pub fn realign<'a, T: 'static, S: AllocateSpace<'a>>(space: &mut S) -> Option<() } #[inline] -pub fn allocate<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, size: usize) -> Option<&'a mut Space> { +pub fn allocate<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl AllocateSpace<'space>, size: usize) -> Option<&'handle mut Space> { let required_padding = Space::::padding_for(space.as_bytes()); let raw = space.allocate_unaligned(size + required_padding)?; @@ -60,26 +60,26 @@ pub fn allocate<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, size: usize) } #[inline] -pub fn allocate_values<'a, T: 'static>(space: &mut impl AllocateSpace<'a>, count: usize) -> Option<&'a mut [MaybeUninit]> { +pub fn allocate_values<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl AllocateSpace<'space>, count: usize) -> Option<&'handle mut [MaybeUninit]> { let space = allocate(space, count * ::core::mem::size_of::())?; Some(space.as_uninit_slice_mut()) } #[inline] -pub fn init_atom<'space: 'write, 'read, 'write, A: Atom<'read, 'write>>(space: &'write mut impl AllocateSpace<'space>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { - let space: AtomSpaceWriter<'write> = AtomSpaceWriter::write_new(space, atom_type)?; +pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>(space: &'handle mut impl AllocateSpace<'space>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { + let space: AtomSpaceWriter<'handle, 'space> = AtomSpaceWriter::write_new(space, atom_type)?; A::init(space, write_parameter) } #[inline] -pub fn write_bytes<'a>(space: &mut impl AllocateSpace<'a>, bytes: &[u8]) -> Option<&'a mut [u8]> { +pub fn write_bytes<'handle, 'space: 'handle>(space: &'handle mut impl AllocateSpace<'space>, bytes: &[u8]) -> Option<&'handle mut [u8]> { let space = space.allocate_unaligned(bytes.len())?; space.copy_from_slice(bytes); Some(space) } #[inline] -pub fn write_value<'a, T>(space: &mut impl AllocateSpace<'a>, value: T) -> Option<&'a mut T> +pub fn write_value<'handle, 'space: 'handle, T>(space: &'handle mut impl AllocateSpace<'space>, value: T) -> Option<&'handle mut T> where T: Copy + Sized + 'static { let space = allocate(space, size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. @@ -90,7 +90,7 @@ pub fn write_value<'a, T>(space: &mut impl AllocateSpace<'a>, value: T) -> Optio Some (unsafe { &mut *(space.as_mut_ptr()) }) } -pub fn write_values<'a, T>(space: &mut impl AllocateSpace<'a>, values: &[T]) -> Option<&'a mut [T]> +pub fn write_values<'handle, 'space: 'handle, T>(space: &'handle mut impl AllocateSpace<'space>, values: &[T]) -> Option<&'handle mut [T]> where T: Copy + Sized + 'static { let space: &mut Space = allocate(space, size_of_val(values))?; let space = space.as_uninit_slice_mut(); diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index a9f54cca..1d665d58 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,42 +1,34 @@ use crate::space::AllocateSpace; use urid::URID; -use std::mem::transmute; use crate::header::AtomHeader; /// A `MutSpace` that tracks the amount of allocated space in an atom header. -pub struct AtomSpaceWriter<'a> { - atom: &'a mut AtomHeader, - parent: &'a mut dyn AllocateSpace<'a>, +pub struct AtomSpaceWriter<'handle, 'space: 'handle> { + atom: &'space mut AtomHeader, + parent: &'handle mut dyn AllocateSpace<'space>, } -impl<'a> AtomSpaceWriter<'a> { +impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { #[inline] pub fn atom(&self) -> AtomHeader { *self.atom } /// Create a new framed space with the given parent and type URID. - pub fn write_new<'space: 'a, A: ?Sized>(parent: &'a mut impl AllocateSpace<'space>, urid: URID) -> Option { + pub fn write_new(parent: &'handle mut impl AllocateSpace<'space>, urid: URID) -> Option { let atom = AtomHeader::from_raw(sys::LV2_Atom { size: 0, type_: urid.get(), }); let atom = crate::space::write_value(parent, atom)?; - Some(Self::new(atom, parent)) - } - - #[inline] - fn new<'space: 'a>(atom: &'a mut AtomHeader, parent: &'a mut dyn AllocateSpace<'space>) -> Self { - // SAFETY: Here we reduce the lifetime 'space to 'a, which is safe because 'space: 'a, and also because this reference will never be changed from now on. - let parent: &'a mut dyn AllocateSpace<'a> = unsafe { transmute(parent) }; - Self { atom, parent } + Some(Self { atom, parent }) } } -impl<'a> AllocateSpace<'a> for AtomSpaceWriter<'a> { +impl<'handle, 'space: 'handle> AllocateSpace<'space> for AtomSpaceWriter<'handle, 'space> { #[inline] - fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { + fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { let result = self.parent.allocate_unaligned(size); if result.is_some() { self.atom.as_raw_mut().size += size as u32; diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 035c861a..318a050b 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,4 +1,4 @@ -use std::mem::{MaybeUninit, transmute}; +use std::mem::MaybeUninit; use crate::space::{AllocateSpace, Space}; use std::ops::Range; @@ -23,7 +23,7 @@ impl VecSpace { } #[inline] - fn get_or_allocate_bytes_mut<'a>(&'a mut self, byte_range: Range) -> Option<&'a mut [u8]> { + fn get_or_allocate_bytes_mut(&mut self, byte_range: Range) -> Option<&mut [u8]> { let byte_len = self.inner.len() * ::core::mem::size_of::(); let max = byte_range.start.max(byte_range.end); @@ -47,11 +47,9 @@ pub struct VecSpaceCursor<'vec, T> { } impl<'vec, T: Copy + 'static> AllocateSpace<'vec> for VecSpaceCursor<'vec, T> { - fn allocate_unaligned(&mut self, size: usize) -> Option<&'vec mut [u8]> { + fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { let end = self.byte_index.checked_add(size)?; - // FIXME: this is really ugly - let vec: &'vec mut VecSpace = todo!(); - VecSpace::::get_or_allocate_bytes_mut(vec, self.byte_index..end) + VecSpace::::get_or_allocate_bytes_mut(self.vec, self.byte_index..end) } #[inline] @@ -72,16 +70,14 @@ mod tests { #[test] pub fn test_lifetimes () { let mut buffer = VecSpace::::with_capacity(16); - let mut bytes = { + + { let mut cursor = buffer.cursor(); let buf1 = cursor.allocate_unaligned(2).unwrap(); - let buf2 = cursor.allocate_unaligned(2).unwrap(); - buf1[0] = 5; - }; -/* - let other_cursor2 = buffer.cursor(); - let other_cursor = buffer.cursor();*/ - // We can't do this: - //bytes[0] = 5; + buf1[0] = 5 + } + + let _other_cursor = buffer.cursor(); + let _other_cursor2 = buffer.cursor(); } } \ No newline at end of file diff --git a/atom/src/string.rs b/atom/src/string.rs index 9e892ff9..c4044547 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -47,13 +47,13 @@ pub enum LiteralInfo { Datatype(URID), } -impl<'a, 'b> Atom<'a, 'b> for Literal { +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { type ReadParameter = (); - type ReadHandle = (LiteralInfo, &'a str); + type ReadHandle = (LiteralInfo, &'handle str); type WriteParameter = LiteralInfo; - type WriteHandle = StringWriter<'b>; + type WriteHandle = StringWriter<'handle, 'space>; - unsafe fn read(body: &'a Space, _: ()) -> Option<(LiteralInfo, &'a str)> { + unsafe fn read(body: &'handle Space, _: ()) -> Option<(LiteralInfo, &'handle str)> { let (header, body) = body.split_for_value_as_unchecked::()?; let info = if header.lang != 0 && header.datatype == 0 { LiteralInfo::Language(URID::new(header.lang)?) @@ -69,7 +69,7 @@ impl<'a, 'b> Atom<'a, 'b> for Literal { .map(|string| (info, string)) } - fn init(mut frame: AtomSpaceWriter<'b>, info: LiteralInfo) -> Option> { + fn init(mut frame: AtomSpaceWriter<'handle, 'space>, info: LiteralInfo) -> Option> { crate::space::write_value(&mut frame, match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { @@ -95,35 +95,35 @@ unsafe impl UriBound for String { const URI: &'static [u8] = sys::LV2_ATOM__String; } -impl<'a, 'b> Atom<'a, 'b> for String { +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for String { type ReadParameter = (); - type ReadHandle = &'a str; + type ReadHandle = &'handle str; type WriteParameter = (); - type WriteHandle = StringWriter<'b>; + type WriteHandle = StringWriter<'handle, 'space>; - unsafe fn read(body: &'a Space, _: ()) -> Option<&'a str> { + unsafe fn read(body: &'space Space, _: ()) -> Option<&'handle str> { let data = body.as_bytes(); let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator Some(core::str::from_utf8(rust_str_bytes).ok()?) } - fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { Some(StringWriter { frame }) } } /// Handle to append strings to a string or literal. -pub struct StringWriter<'a> { - frame: AtomSpaceWriter<'a>, +pub struct StringWriter<'a, 'space> { + frame: AtomSpaceWriter<'a, 'space>, } -impl<'a, 'b> StringWriter<'a> { +impl<'a, 'space> StringWriter<'a, 'space> { /// Append a string. /// /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. - pub fn append(&mut self, string: &str) -> Option<&'a mut str> { + pub fn append(&'a mut self, string: &str) -> Option<&'a mut str> { let data = string.as_bytes(); let space = crate::space::write_bytes(&mut self.frame, data)?; // FIXME: make a "rewind" function to write the nul byte later @@ -131,10 +131,11 @@ impl<'a, 'b> StringWriter<'a> { } } -impl<'a> Drop for StringWriter<'a> { +impl<'a, 'space> Drop for StringWriter<'a, 'space> { fn drop(&mut self) { // Null terminator. // FIXME: this seems unsafe if the value could not be written for some reason. + // todo!() let _ = crate::space::write_value(&mut self.frame, 0u8); } } @@ -221,9 +222,8 @@ mod tests { let mut space = raw_space.as_bytes_mut(); let mut writer = crate::space::init_atom(&mut space, urids.string, ()).unwrap(); - todo!() - /*writer.append(SAMPLE0).unwrap(); - writer.append(SAMPLE1).unwrap();*/ + writer.append(SAMPLE0).unwrap(); + writer.append(SAMPLE1).unwrap(); } // verifying diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 973872d5..07ba0bc6 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -43,17 +43,17 @@ unsafe impl UriBound for Tuple { const URI: &'static [u8] = sys::LV2_ATOM__Tuple; } -impl<'a, 'b> Atom<'a, 'b> for Tuple { +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { type ReadParameter = (); - type ReadHandle = TupleIterator<'a>; + type ReadHandle = TupleIterator<'handle>; type WriteParameter = (); - type WriteHandle = TupleWriter<'b>; + type WriteHandle = TupleWriter<'handle, 'space>; - unsafe fn read(body: &'a Space, _: ()) -> Option> { + unsafe fn read(body: &'space Space, _: ()) -> Option> { Some(TupleIterator { space: body }) } - fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { Some(TupleWriter { frame }) } } @@ -77,14 +77,14 @@ impl<'a> Iterator for TupleIterator<'a> { } /// The writing handle to add atoms to a tuple. -pub struct TupleWriter<'a> { - frame: AtomSpaceWriter<'a>, +pub struct TupleWriter<'handle, 'space> { + frame: AtomSpaceWriter<'handle, 'space>, } -impl<'a, 'b> TupleWriter<'a> { +impl<'handle, 'space> TupleWriter<'handle, 'space> { /// Initialize a new tuple element. - pub fn init<'read, 'write, A: Atom<'read, 'write>>( - &'write mut self, + pub fn init>( + &'handle mut self, child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { @@ -115,8 +115,7 @@ mod tests { writer.init::>(urids.vector, urids.int).unwrap(); vector_writer.append(&[17; 9]).unwrap(); } - todo!() - //writer.init::(urids.int, 42).unwrap(); + writer.init::(urids.int, 42).unwrap(); } // verifying diff --git a/atom/src/vector.rs b/atom/src/vector.rs index 28270e0c..0aafb9da 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -48,13 +48,13 @@ unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } -impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector where C: 'b, { +impl<'handle, 'space: 'handle, C: ScalarAtom> Atom<'handle, 'space> for Vector where C: 'space, { type ReadParameter = URID; - type ReadHandle = &'a [C::InternalType]; + type ReadHandle = &'space [C::InternalType]; type WriteParameter = URID; - type WriteHandle = VectorWriter<'b, C>; + type WriteHandle = VectorWriter<'handle, 'space, C>; - unsafe fn read(body: &'a Space, child_urid: URID) -> Option<&'a [C::InternalType]> { + unsafe fn read(body: &'space Space, child_urid: URID) -> Option<&'space [C::InternalType]> { let (header, body) = body.split_for_value_as_unchecked::()?; if header.child_type != child_urid || header.child_size as usize != size_of::() { @@ -65,7 +65,7 @@ impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector where C: 'b, { Some(body.aligned()?.assume_init()) } - fn init(mut frame: AtomSpaceWriter<'b>, child_urid: URID) -> Option> { + fn init(mut frame: AtomSpaceWriter<'handle, 'space>, child_urid: URID) -> Option> { let body = sys::LV2_Atom_Vector_Body { child_type: child_urid.get(), child_size: size_of::() as u32, @@ -82,15 +82,15 @@ impl<'a, 'b, C: ScalarAtom> Atom<'a, 'b> for Vector where C: 'b, { /// Handle to append elements to a vector. /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. -pub struct VectorWriter<'a, A: ScalarAtom> { - frame: AtomSpaceWriter<'a>, +pub struct VectorWriter<'handle, 'space, A: ScalarAtom> { + frame: AtomSpaceWriter<'handle, 'space>, type_: PhantomData, } -impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, A> { +impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { /// Push a single value to the vector. #[inline] - pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { + pub fn push(&'handle mut self, child: A::InternalType) -> Option<&'handle mut A::InternalType> { space::write_value(&mut self.frame, child) } @@ -98,13 +98,13 @@ impl<'a, 'b, A: ScalarAtom> VectorWriter<'a, A> { /// /// Using this method, you don't need to have the elements in memory before you can write them. #[inline] - pub fn allocate_uninit(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { + pub fn allocate_uninit(&'handle mut self, count: usize) -> Option<&'handle mut [MaybeUninit]> { space::allocate_values(&mut self.frame, count) } /// Append multiple elements to the vector. #[inline] - pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { + pub fn append(&'handle mut self, data: &[A::InternalType]) -> Option<&'handle mut [A::InternalType]> { space::write_values(&mut self.frame, data) } } From c0913a747278dc924fd686d69dbeb0b4e676b425 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 17 Aug 2021 23:36:29 +0200 Subject: [PATCH 09/54] It builds! --- atom/src/chunk.rs | 2 +- atom/src/lib.rs | 6 ++-- atom/src/object.rs | 14 ++++---- atom/src/port.rs | 20 ++++++------ atom/src/scalar.rs | 14 ++++---- atom/src/sequence.rs | 10 +++--- atom/src/space.rs | 2 ++ atom/src/space/allocatable.rs | 60 ++++++++++++++++++++-------------- atom/src/space/atom_writer.rs | 46 ++++++++++++++++++-------- atom/src/space/cursor.rs | 40 +++++++++++++++++++++++ atom/src/space/list.rs | 20 +++++++++--- atom/src/space/space.rs | 20 +++++------- atom/src/space/vec.rs | 22 ++++++++++--- atom/src/string.rs | 12 +++---- atom/src/tuple.rs | 6 ++-- atom/src/vector.rs | 8 ++--- atom/tests/atom_integration.rs | 4 +-- midi/src/raw.rs | 13 +++----- midi/src/wmidi_binding.rs | 44 +++++++++++-------------- 19 files changed, 222 insertions(+), 141 deletions(-) create mode 100644 atom/src/space/cursor.rs diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 6f23eb93..7c821910 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -70,7 +70,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); let data = writer.allocate_unaligned(SLICE_LENGTH - 1).unwrap(); diff --git a/atom/src/lib.rs b/atom/src/lib.rs index ea6adeac..7e04d605 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -87,7 +87,7 @@ pub mod prelude { pub use port::AtomPort; pub use scalar::{AtomURID, Bool, Double, Float, Int, Long}; pub use sequence::{Sequence, TimeStamp, TimeStampURID}; - pub use space::{AtomSpaceWriter, AllocateSpace, AtomSpace, Space}; + pub use space::{AtomSpaceWriter, SpaceAllocator, AtomSpace, Space}; pub use string::{Literal, LiteralInfo, String}; pub use tuple::Tuple; pub use vector::Vector; @@ -199,8 +199,8 @@ impl<'space> UnidentifiedAtom<'space> { /// Try to read the atom. /// /// To identify the atom, it's URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read<'handle, A: Atom<'handle, 'space>>( - &self, + pub fn read<'a, A: Atom<'a, 'space>>( + &'a self, urid: URID, parameter: A::ReadParameter, ) -> Option { diff --git a/atom/src/object.rs b/atom/src/object.rs index 346c554e..7842f408 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -116,7 +116,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { header: ObjectHeader, ) -> Option> { { - let x = space::write_value(&mut frame, sys::LV2_Atom_Object_Body { + space::write_value(&mut frame, sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), otype: header.otype.get(), })?; @@ -202,8 +202,8 @@ impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init<'read, 'write, K: ?Sized, A: Atom<'read, 'space>>( - &'space mut self, + pub fn init<'a, K: ?Sized, A: Atom<'a, 'space>>( + &'a mut self, key: URID, child_urid: URID, parameter: A::WriteParameter, @@ -268,7 +268,7 @@ impl Property { /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. #[inline] fn write_header<'a, 'space, K: ?Sized, C: ?Sized>( - space: &'a mut impl AllocateSpace<'space>, + space: &'a mut impl SpaceAllocator<'space>, key: URID, context: Option>, ) -> Option<()> { @@ -313,7 +313,7 @@ mod tests { // writing { - let mut cursor = raw_space.as_bytes_mut(); + let mut cursor = SpaceCursor::new(raw_space.as_bytes_mut()); let frame = AtomSpaceWriter::write_new(&mut cursor, urids.object).unwrap(); let mut writer = Object::init( frame, @@ -343,10 +343,8 @@ mod tests { // verifying { // Header - let s = raw_space.deref(); - let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); + let (atom, _space) = unsafe { raw_space.split_atom() }.unwrap(); let header = atom.header().unwrap(); - let x = atom.body().unwrap().len(); assert_eq!(header.urid(), urids.object); assert_eq!( header.size_of_body(), diff --git a/atom/src/port.rs b/atom/src/port.rs index 1bcf9b9d..4a0a9f87 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -32,13 +32,13 @@ use urid::URID; /// A handle to read atoms from a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to read atoms. -pub struct PortReader<'a> { - space: &'a Space, +pub struct PortReader<'space> { + space: &'space Space, } -impl<'a> PortReader<'a> { +impl<'space> PortReader<'space> { /// Create a new port reader. - fn new(space: &'a Space) -> Self { + fn new(space: &'space Space) -> Self { Self { space } } @@ -47,8 +47,8 @@ impl<'a> PortReader<'a> { /// In order to identify the atom, the reader needs to know it's URID. Also, some atoms require a parameter. However, you can simply pass `()` in most cases. /// /// This method returns `None` if the atom is malformed or simply isn't of the specified type. - pub fn read<'b, A: crate::Atom<'a, 'b>>( - &'b self, + pub fn read<'handle, A: crate::Atom<'handle, 'space>>( + &self, urid: URID, parameter: A::ReadParameter, ) -> Option { @@ -61,7 +61,7 @@ impl<'a> PortReader<'a> { /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. pub struct PortWriter<'a> { - space: &'a mut [u8], + space: SpaceCursor<'a>, has_been_written: bool, } @@ -69,7 +69,7 @@ impl<'a> PortWriter<'a> { /// Create a new port writer. fn new(space: &'a mut Space) -> Self { Self { - space: space.as_bytes_mut(), + space: SpaceCursor::new(space.as_bytes_mut()), has_been_written: false, } } @@ -89,7 +89,7 @@ impl<'a> PortWriter<'a> { if !self.has_been_written { self.has_been_written = true; // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. - let space: &'write mut &'write mut [u8] = unsafe { ::core::mem::transmute::<_, &'write mut &'write mut [u8]>(&mut self.space) }; + let space: &'write mut SpaceCursor<'write> = unsafe { ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) }; crate::space::init_atom(space, urid, parameter) } else { None @@ -139,7 +139,7 @@ mod tests { // writing a chunk to indicate the size of the space. { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = crate::space::init_atom(&mut space, urids.chunk, ()).unwrap(); writer .allocate_unaligned(256 - size_of::()) diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index f5be2447..4b92c6cf 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -22,8 +22,7 @@ //! let read_value: f32 = ports.input.read(urids.float, ()).unwrap(); //! //! // Writing is done with the value of the atom. -//! // You can modify it afterwards. -//! let written_value: &mut f32 = ports.output.init(urids.float, 17.0).unwrap(); +//! ports.output.init(urids.float, 17.0).unwrap(); //! } //! ``` //! @@ -56,8 +55,9 @@ pub trait ScalarAtom: UriBound { /// Try to write the atom into a space. /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. - fn write_scalar<'handle, 'space: 'handle>(mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType) -> Option<&'handle mut Self::InternalType> { - space::write_value(&mut frame, value) + fn write_scalar<'handle, 'space: 'handle>(mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType) -> Option<()> { + space::write_value(&mut frame, value)?; + Some(()) } } @@ -65,13 +65,13 @@ impl<'handle, 'space: 'handle, A: ScalarAtom> Atom<'handle, 'space> for A { type ReadParameter = (); type ReadHandle = A::InternalType; type WriteParameter = A::InternalType; - type WriteHandle = &'handle mut A::InternalType; + type WriteHandle = (); unsafe fn read(body: &'space Space, _: ()) -> Option { ::read_scalar(body) } - fn init(frame: AtomSpaceWriter<'handle, 'space>, value: A::InternalType) -> Option<&'handle mut A::InternalType> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, value: A::InternalType) -> Option<()> { ::write_scalar(frame, value) } } @@ -172,7 +172,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); crate::space::init_atom(&mut space, urid, value).unwrap(); } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index afb4e303..76a1890c 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -204,7 +204,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { /// * The time stamp is not measured in our unit. /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. - fn write_time_stamp(&'handle mut self, stamp: TimeStamp) -> Option<()> { + fn write_time_stamp(&mut self, stamp: TimeStamp) -> Option<()> { let raw_stamp = match self.unit { TimeStampUnit::Frames => { let frames = stamp.as_frames()?; @@ -234,8 +234,8 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init>( - &'handle mut self, + pub fn init<'a, A: Atom<'a, 'space>>( + &'a mut self, stamp: TimeStamp, urid: URID, parameter: A::WriteParameter, @@ -249,7 +249,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { /// If your cannot identify the type of the atom but have to write it, you can simply forward it. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn forward(&'handle mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { + pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { let data = atom.space.as_bytes(); self.write_time_stamp(stamp)?; space::write_bytes(&mut self.frame, data)?; @@ -279,7 +279,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space::init_atom(&mut space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); writer diff --git a/atom/src/space.rs b/atom/src/space.rs index 4bb698a1..ebdc2cf2 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -6,10 +6,12 @@ mod allocatable; mod atom_writer; mod boxed; mod vec; +mod cursor; pub use space::{AtomSpace, Space}; pub use list::{SpaceList, SpaceHead}; pub use allocatable::*; pub use atom_writer::AtomSpaceWriter; pub use vec::{VecSpace, VecSpaceCursor}; +pub use cursor::SpaceCursor; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index c54fb007..99f639b4 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -8,19 +8,22 @@ use std::mem::MaybeUninit; /// A smart pointer that writes atom data to an internal slice. /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. -pub trait AllocateSpace<'a> { +pub trait SpaceAllocator<'a> { /// Try to allocate memory on the internal data slice. /// - /// If `apply_padding` is `true`, the method will assure that the allocated memory is 64-bit-aligned. The first return value is the number of padding bytes that has been used and the second return value is a mutable slice referencing the allocated data. - /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]>; - fn as_bytes(&self) -> &[u8]; - fn as_bytes_mut(&mut self) -> &mut [u8]; -} + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])>; + + fn allocated_bytes(&self) -> &[u8]; + fn allocated_bytes_mut(&mut self) -> &mut [u8]; -impl<'a> AllocateSpace<'a> for &'a mut [u8] { + fn remaining_bytes(&self) -> &[u8]; + fn remaining_bytes_mut(&mut self) -> &mut [u8]; +} +/* +impl<'a> SpaceAllocator<'a> for &'a mut [u8] { #[inline] fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { if size > self.len() { @@ -34,52 +37,61 @@ impl<'a> AllocateSpace<'a> for &'a mut [u8] { } #[inline] - fn as_bytes(&self) -> &[u8] { + fn remaining_bytes(&self) -> &[u8] { self } #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { + fn remaining_bytes_mut(&mut self) -> &mut [u8] { self } -} +}*/ +/* #[inline] pub fn realign<'a, T: 'static, S: AllocateSpace<'a>>(space: &mut S) -> Option<()> { let required_padding = Space::::padding_for(space.as_bytes()); let _ = space.allocate_unaligned(required_padding)?; Some(()) +}*/ + +unsafe fn assume_init_mut(s: &mut MaybeUninit) -> &mut T { + // SAFETY: the caller must guarantee that `self` is initialized. + // This also means that `self` must be a `value` variant. + unsafe { + &mut *s.as_mut_ptr() + } } #[inline] -pub fn allocate<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl AllocateSpace<'space>, size: usize) -> Option<&'handle mut Space> { - let required_padding = Space::::padding_for(space.as_bytes()); +pub fn allocate<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl SpaceAllocator<'space>, size: usize) -> Option<&'handle mut Space> { + let required_padding = Space::::padding_for(space.remaining_bytes()); let raw = space.allocate_unaligned(size + required_padding)?; Space::try_align_from_bytes_mut(raw) } #[inline] -pub fn allocate_values<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl AllocateSpace<'space>, count: usize) -> Option<&'handle mut [MaybeUninit]> { +pub fn allocate_values<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl SpaceAllocator<'space>, count: usize) -> Option<&'handle mut [MaybeUninit]> { let space = allocate(space, count * ::core::mem::size_of::())?; Some(space.as_uninit_slice_mut()) } #[inline] -pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>(space: &'handle mut impl AllocateSpace<'space>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { +pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>(space: &'handle mut impl SpaceAllocator<'space>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { let space: AtomSpaceWriter<'handle, 'space> = AtomSpaceWriter::write_new(space, atom_type)?; A::init(space, write_parameter) } #[inline] -pub fn write_bytes<'handle, 'space: 'handle>(space: &'handle mut impl AllocateSpace<'space>, bytes: &[u8]) -> Option<&'handle mut [u8]> { +pub fn write_bytes<'handle, 'space: 'handle>(space: &'handle mut impl SpaceAllocator<'space>, bytes: &[u8]) -> Option<&'handle mut [u8]> { let space = space.allocate_unaligned(bytes.len())?; space.copy_from_slice(bytes); Some(space) } #[inline] -pub fn write_value<'handle, 'space: 'handle, T>(space: &'handle mut impl AllocateSpace<'space>, value: T) -> Option<&'handle mut T> +pub fn write_value<'handle, 'space: 'handle, T>(space: &'handle mut impl SpaceAllocator<'space>, value: T) -> Option<&'handle mut T> where T: Copy + Sized + 'static { let space = allocate(space, size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. @@ -87,10 +99,10 @@ pub fn write_value<'handle, 'space: 'handle, T>(space: &'handle mut impl Allocat *space = MaybeUninit::new(value); // SAFETY: the MaybeUninit has now been properly initialized. - Some (unsafe { &mut *(space.as_mut_ptr()) }) + Some (unsafe { assume_init_mut(space) }) } -pub fn write_values<'handle, 'space: 'handle, T>(space: &'handle mut impl AllocateSpace<'space>, values: &[T]) -> Option<&'handle mut [T]> +pub fn write_values<'handle, 'space: 'handle, T>(space: &'handle mut impl SpaceAllocator<'space>, values: &[T]) -> Option<&'handle mut [T]> where T: Copy + Sized + 'static { let space: &mut Space = allocate(space, size_of_val(values))?; let space = space.as_uninit_slice_mut(); @@ -106,8 +118,9 @@ pub fn write_values<'handle, 'space: 'handle, T>(space: &'handle mut impl Alloca #[cfg(test)] mod tests { use crate::space::{AtomSpace, write_value, init_atom}; - use crate::prelude::Int; + use crate::prelude::{Int, SpaceAllocator}; use urid::URID; + use crate::space::cursor::SpaceCursor; const INT_URID: URID = unsafe { URID::new_unchecked(5) }; @@ -118,16 +131,15 @@ mod tests { let mut space = AtomSpace::boxed(32); assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed - let mut cursor: &mut _ = space.as_bytes_mut(); // The pointer that is going to be moved as we keep writing. + let mut cursor= SpaceCursor::new(space.as_bytes_mut()); // The pointer that is going to be moved as we keep writing. let new_value = write_value(&mut cursor, 42u8).unwrap(); assert_eq!(42, *new_value); - assert_eq!(31, cursor.len()); + assert_eq!(31, cursor.remaining_bytes().len()); { - let int_atom: &mut _ = init_atom(&mut cursor, INT_URID, 69).unwrap(); - assert_eq!(69, *int_atom); - assert_eq!(12, cursor.len()); + init_atom(&mut cursor, INT_URID, 69).unwrap(); + assert_eq!(12, cursor.remaining_bytes().len()); } // let new_value = write_value(&mut cursor, 42u8).unwrap(); /*{ diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 1d665d58..a93fd0ec 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,50 +1,67 @@ -use crate::space::AllocateSpace; +use crate::space::SpaceAllocator; use urid::URID; use crate::header::AtomHeader; /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'handle, 'space: 'handle> { - atom: &'space mut AtomHeader, - parent: &'handle mut dyn AllocateSpace<'space>, + atom_header_index: usize, + parent: &'handle mut dyn SpaceAllocator<'space>, } impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { #[inline] - pub fn atom(&self) -> AtomHeader { - *self.atom + pub fn atom_header(&self) -> AtomHeader { + todo!() } + #[inline] + fn atom_header_mut(&self) -> &mut AtomHeader { todo!() } + /// Create a new framed space with the given parent and type URID. - pub fn write_new(parent: &'handle mut impl AllocateSpace<'space>, urid: URID) -> Option { + pub fn write_new(parent: &'handle mut impl SpaceAllocator<'space>, urid: URID) -> Option { + let atom_header_index = parent.allocated_bytes().len(); let atom = AtomHeader::from_raw(sys::LV2_Atom { size: 0, type_: urid.get(), }); let atom = crate::space::write_value(parent, atom)?; - Some(Self { atom, parent }) + Some(Self { atom_header_index, parent }) } } -impl<'handle, 'space: 'handle> AllocateSpace<'space> for AtomSpaceWriter<'handle, 'space> { +impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handle, 'space> { #[inline] fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { let result = self.parent.allocate_unaligned(size); if result.is_some() { - self.atom.as_raw_mut().size += size as u32; + // TODO + // self.atom_header_mut().as_raw_mut().size += size as u32; } result } + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + todo!() + } + + fn allocated_bytes(&self) -> &[u8] { + todo!() + } + + fn allocated_bytes_mut(&mut self) -> &mut [u8] { + todo!() + } + #[inline] - fn as_bytes(&self) -> &[u8] { - self.parent.as_bytes() + fn remaining_bytes(&self) -> &[u8] { + self.parent.remaining_bytes() } #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { - self.parent.as_bytes_mut() + fn remaining_bytes_mut(&mut self) -> &mut [u8] { + self.parent.remaining_bytes_mut() } } @@ -53,6 +70,7 @@ mod tests { use core::mem::size_of; use crate::prelude::AtomSpaceWriter; use urid::URID; + use crate::space::cursor::SpaceCursor; #[test] fn test_padding_inside_frame() { @@ -67,7 +85,7 @@ mod tests { // writing { - let mut root: &mut _ = raw_space; + let mut root = SpaceCursor::new(raw_space); let mut frame = AtomSpaceWriter::write_new(&mut root, URID::<()>::new(1).unwrap()) .unwrap(); diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs new file mode 100644 index 00000000..a45dcc77 --- /dev/null +++ b/atom/src/space/cursor.rs @@ -0,0 +1,40 @@ +use crate::space::SpaceAllocator; + +pub struct SpaceCursor<'a> { + data: &'a mut [u8], + index: usize +} + +impl<'a> SpaceCursor<'a> { + pub fn new(data: &'a mut [u8]) -> Self { + Self { data, index: 0 } + } +} + +impl<'a> SpaceAllocator<'a> for SpaceCursor<'a> { + fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { + todo!() + } + + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + todo!() + } + + fn allocated_bytes(&self) -> &[u8] { + todo!() + } + + fn allocated_bytes_mut(&mut self) -> &mut [u8] { + todo!() + } + + #[inline] + fn remaining_bytes(&self) -> &[u8] { + self.data + } + + #[inline] + fn remaining_bytes_mut(&mut self) -> &mut [u8] { + self.data + } +} \ No newline at end of file diff --git a/atom/src/space/list.rs b/atom/src/space/list.rs index b9bddba7..89ce2541 100644 --- a/atom/src/space/list.rs +++ b/atom/src/space/list.rs @@ -1,4 +1,4 @@ -use crate::space::AllocateSpace; +use crate::space::SpaceAllocator; /// Linked list element for dynamic atom writing. /// @@ -93,7 +93,7 @@ impl<'a> SpaceHead<'a> { } } -impl<'a> AllocateSpace<'a> for SpaceHead<'a> { +impl<'a> SpaceAllocator<'a> for SpaceHead<'a> { #[inline] fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { let element = self.element.take()?; @@ -103,13 +103,25 @@ impl<'a> AllocateSpace<'a> for SpaceHead<'a> { Some(new_space) } + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + todo!() + } + + fn allocated_bytes(&self) -> &[u8] { + todo!() + } + + fn allocated_bytes_mut(&mut self) -> &mut [u8] { + todo!() + } + #[inline] - fn as_bytes(&self) -> &[u8] { + fn remaining_bytes(&self) -> &[u8] { &[] } #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { + fn remaining_bytes_mut(&mut self) -> &mut [u8] { &mut [] } } diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 27c3f1d4..78799e9c 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -201,6 +201,7 @@ impl Space { } #[inline] + // FIXME: rename this pub unsafe fn split_for_value_as_unchecked(&self) -> Option<(&U, &Self)> { let (value, rest) = self.realign()?.split_for_value_unchecked()?; @@ -438,7 +439,7 @@ mod tests { } } - fn test_mut_space<'a>(mut space: impl AllocateSpace<'a>) { + fn test_mut_space<'a>(mut space: impl SpaceAllocator<'a>) { let map = HashURIDMapper::new(); let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); @@ -472,14 +473,14 @@ mod tests { let written_data = crate::space::write_bytes(&mut atom_frame, &test_data).unwrap(); assert_eq!(test_data.as_slice(), written_data); - assert_eq!(atom_frame.atom().size_of_body(), test_data.len()); + assert_eq!(atom_frame.atom_header().size_of_body(), test_data.len()); let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; let written_atom = crate::space::write_value(&mut atom_frame, test_atom).unwrap(); assert_eq!(written_atom.size, test_atom.size); assert_eq!(written_atom.type_, test_atom.type_); assert_eq!( - atom_frame.atom().size_of_body(), + atom_frame.atom_header().size_of_body(), test_data.len() + size_of_val(&test_atom) ); } @@ -488,15 +489,10 @@ mod tests { #[test] fn test_root_mut_space() { const MEMORY_SIZE: usize = 256; - let mut memory: [u64; MEMORY_SIZE] = [0; MEMORY_SIZE]; - let frame = unsafe { - std::slice::from_raw_parts_mut( - (&mut memory).as_mut_ptr() as *mut u8, - MEMORY_SIZE * size_of::(), - ) - }; + let mut memory = [0; MEMORY_SIZE]; + let cursor = SpaceCursor::new(&mut memory[..]); - test_mut_space(frame); + test_mut_space(cursor); } #[test] @@ -504,7 +500,7 @@ mod tests { let mut raw_space = Box::new([0u8; 8]); { - let mut root_space = &mut raw_space[3..]; + let mut root_space = SpaceCursor::new(&mut raw_space[3..]); crate::space::write_value(&mut root_space, 42u8).unwrap(); } diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 318a050b..c8b7d713 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,5 +1,5 @@ use std::mem::MaybeUninit; -use crate::space::{AllocateSpace, Space}; +use crate::space::{SpaceAllocator, Space}; use std::ops::Range; pub struct VecSpace { @@ -46,26 +46,38 @@ pub struct VecSpaceCursor<'vec, T> { byte_index: usize } -impl<'vec, T: Copy + 'static> AllocateSpace<'vec> for VecSpaceCursor<'vec, T> { +impl<'vec, T: Copy + 'static> SpaceAllocator<'vec> for VecSpaceCursor<'vec, T> { fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { let end = self.byte_index.checked_add(size)?; VecSpace::::get_or_allocate_bytes_mut(self.vec, self.byte_index..end) } + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + todo!() + } + + fn allocated_bytes(&self) -> &[u8] { + todo!() + } + + fn allocated_bytes_mut(&mut self) -> &mut [u8] { + todo!() + } + #[inline] - fn as_bytes(&self) -> &[u8] { + fn remaining_bytes(&self) -> &[u8] { self.vec.as_bytes() } #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { + fn remaining_bytes_mut(&mut self) -> &mut [u8] { self.vec.as_bytes_mut() } } #[cfg(test)] mod tests { - use crate::space::{VecSpace, AllocateSpace}; + use crate::space::{VecSpace, SpaceAllocator}; #[test] pub fn test_lifetimes () { diff --git a/atom/src/string.rs b/atom/src/string.rs index c4044547..1a604fed 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -113,17 +113,17 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for String { } /// Handle to append strings to a string or literal. -pub struct StringWriter<'a, 'space> { - frame: AtomSpaceWriter<'a, 'space>, +pub struct StringWriter<'handle, 'space> { + frame: AtomSpaceWriter<'handle, 'space>, } -impl<'a, 'space> StringWriter<'a, 'space> { +impl<'handle, 'space> StringWriter<'handle, 'space> { /// Append a string. /// /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. - pub fn append(&'a mut self, string: &str) -> Option<&'a mut str> { + pub fn append(&mut self, string: &str) -> Option<&mut str> { let data = string.as_bytes(); let space = crate::space::write_bytes(&mut self.frame, data)?; // FIXME: make a "rewind" function to write the nul byte later @@ -171,7 +171,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = crate::space::init_atom(&mut space, urids.atom.literal, LiteralInfo::Language(urids.german.into_general())).unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); @@ -219,7 +219,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = crate::space::init_atom(&mut space, urids.string, ()).unwrap(); writer.append(SAMPLE0).unwrap(); diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 07ba0bc6..1d67b13d 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -83,8 +83,8 @@ pub struct TupleWriter<'handle, 'space> { impl<'handle, 'space> TupleWriter<'handle, 'space> { /// Initialize a new tuple element. - pub fn init>( - &'handle mut self, + pub fn init<'a, A: Atom<'a, 'space>>( + &'a mut self, child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { @@ -108,7 +108,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = crate::space::init_atom(&mut space, urids.tuple, ()).unwrap(); { let mut vector_writer = diff --git a/atom/src/vector.rs b/atom/src/vector.rs index 0aafb9da..d5c3a746 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -90,7 +90,7 @@ pub struct VectorWriter<'handle, 'space, A: ScalarAtom> { impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { /// Push a single value to the vector. #[inline] - pub fn push(&'handle mut self, child: A::InternalType) -> Option<&'handle mut A::InternalType> { + pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { space::write_value(&mut self.frame, child) } @@ -98,13 +98,13 @@ impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { /// /// Using this method, you don't need to have the elements in memory before you can write them. #[inline] - pub fn allocate_uninit(&'handle mut self, count: usize) -> Option<&'handle mut [MaybeUninit]> { + pub fn allocate_uninit(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { space::allocate_values(&mut self.frame, count) } /// Append multiple elements to the vector. #[inline] - pub fn append(&'handle mut self, data: &[A::InternalType]) -> Option<&'handle mut [A::InternalType]> { + pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { space::write_values(&mut self.frame, data) } } @@ -126,7 +126,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = crate::space::init_atom(&mut space, urids.vector(), urids.int).unwrap(); writer.append(&[42; CHILD_COUNT - 1]); writer.push(1); diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index f053572f..cce054b9 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -101,7 +101,7 @@ fn main() { // Preparing the input atom. let mut input_atom_space = AtomSpace::boxed(256); { - let mut space = input_atom_space.as_bytes_mut(); + let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); let mut writer = lv2_atom::space::init_atom(&mut space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); @@ -120,7 +120,7 @@ fn main() { // preparing the output atom. let mut output_atom_space = AtomSpace::boxed(256); { - let mut space = output_atom_space.as_bytes_mut(); + let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); lv2_atom::space::init_atom(&mut space, urids.atom.chunk, ()).unwrap() diff --git a/midi/src/raw.rs b/midi/src/raw.rs index c14f3d86..cf557567 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -15,20 +15,17 @@ unsafe impl UriBound for MidiEvent { const URI: &'static [u8] = sys::LV2_MIDI__MidiEvent; } -impl<'a, 'b> Atom<'a, 'b> for MidiEvent -where - 'a: 'b, -{ +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for MidiEvent { type ReadParameter = (); - type ReadHandle = &'a [u8]; + type ReadHandle = &'handle [u8]; type WriteParameter = (); - type WriteHandle = AtomSpaceWriter<'b>; + type WriteHandle = AtomSpaceWriter<'handle, 'space>; - unsafe fn read(body: &'a Space, _: ()) -> Option<&'a [u8]> { + unsafe fn read(body: &'handle Space, _: ()) -> Option<&'handle [u8]> { Some(body.as_bytes()) } - fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { Some(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index d90741f4..66947a13 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -20,20 +20,17 @@ unsafe impl UriBound for WMidiEvent { const URI: &'static [u8] = sys::LV2_MIDI__MidiEvent; } -impl<'a, 'b> Atom<'a, 'b> for WMidiEvent -where - 'a: 'b, -{ +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for WMidiEvent { type ReadParameter = (); - type ReadHandle = wmidi::MidiMessage<'a>; - type WriteParameter = wmidi::MidiMessage<'b>; + type ReadHandle = wmidi::MidiMessage<'handle>; + type WriteParameter = wmidi::MidiMessage<'handle>; type WriteHandle = (); - unsafe fn read(space: &'a Space, _: ()) -> Option> { + unsafe fn read(space: &'handle Space, _: ()) -> Option> { wmidi::MidiMessage::try_from(space.as_bytes()).ok() } - fn init(mut frame: AtomSpaceWriter<'b>, message: wmidi::MidiMessage) -> Option<()> { + fn init(mut frame: AtomSpaceWriter<'handle, 'space>, message: wmidi::MidiMessage) -> Option<()> { let space: &mut Space = lv2_atom::space::allocate(&mut frame, message.bytes_size())?; message.copy_to_slice(space.as_bytes_mut()).ok()?; @@ -52,20 +49,17 @@ unsafe impl UriBound for SystemExclusiveWMidiEvent { const URI: &'static [u8] = sys::LV2_MIDI__MidiEvent; } -impl<'a, 'b> Atom<'a, 'b> for SystemExclusiveWMidiEvent -where - 'a: 'b, -{ +impl<'handle, 'space: 'handle> Atom<'handle, 'space> for SystemExclusiveWMidiEvent { type ReadParameter = (); - type ReadHandle = wmidi::MidiMessage<'a>; + type ReadHandle = wmidi::MidiMessage<'handle>; type WriteParameter = (); - type WriteHandle = Writer<'b>; + type WriteHandle = Writer<'handle, 'space>; - unsafe fn read(space: &'a Space, _: ()) -> Option> { + unsafe fn read(space: &'handle Space, _: ()) -> Option> { WMidiEvent::read(space, ()) } - fn init(frame: AtomSpaceWriter<'b>, _: ()) -> Option> { + fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { let mut writer = Writer { frame }; writer.write::(0xf0); Some(writer) @@ -77,23 +71,23 @@ where /// This writing handle is similar to a chunk's `ByteWriter`: You can allocate space, write raw bytes and generic values. /// /// The "start of system exclusive" status byte is written by [`SystemExclusiveWMidiEvent::init`](struct.SystemExclusiveWMidiEvent.html#method.init) method and the "end of system exclusive" status byte is written when the writer is dropped. -pub struct Writer<'a> { - frame: AtomSpaceWriter<'a>, +pub struct Writer<'handle, 'space> { + frame: AtomSpaceWriter<'handle, 'space>, } -impl<'a> Writer<'a> { +impl<'handle, 'space> Writer<'handle, 'space> { #[inline] - pub fn write_raw(&mut self, data: &[u8]) -> Option<&'a mut [u8]> { + pub fn write_raw(&mut self, data: &[u8]) -> Option<&mut [u8]> { lv2_atom::space::write_bytes(&mut self.frame, data) } #[inline] - pub fn write(&mut self, instance: T) -> Option<&'a mut T> where T: Copy + Sized + 'static, { + pub fn write(&mut self, instance: T) -> Option<&mut T> where T: Copy + Sized + 'static, { lv2_atom::space::write_value(&mut self.frame, instance) } } -impl<'a, 'b> Drop for Writer<'a> { +impl<'handle, 'space> Drop for Writer<'handle, 'space> { fn drop(&mut self) { self.write::(0xf7); } @@ -103,8 +97,8 @@ impl<'a, 'b> Drop for Writer<'a> { mod tests { use crate::wmidi_binding::*; use std::convert::TryFrom; - use std::mem::size_of; use wmidi::*; + use lv2_atom::space::SpaceCursor; #[test] fn test_midi_event() { @@ -117,7 +111,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); lv2_atom::space::init_atom(&mut space, urid, reference_message.clone()).unwrap(); } @@ -150,7 +144,7 @@ mod tests { // writing { - let mut space = raw_space.as_bytes_mut(); + let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = lv2_atom::space::init_atom(&mut space, urid, ()).unwrap(); writer.write_raw(&[1, 2, 3, 4]); } From eaf1d4b167002f0ed44d513e4b2912086f60170d Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 18 Aug 2021 00:58:38 +0200 Subject: [PATCH 10/54] Some refactorings --- atom/src/chunk.rs | 11 ++-- atom/src/header.rs | 14 +++-- atom/src/lib.rs | 14 ++--- atom/src/object.rs | 46 ++++++++++------ atom/src/port.rs | 18 ++++--- atom/src/scalar.rs | 9 +++- atom/src/sequence.rs | 35 ++++++++---- atom/src/space.rs | 13 +++-- atom/src/space/allocatable.rs | 91 ++++++++++++++++++++----------- atom/src/space/atom_writer.rs | 86 +++++++++++++++++++----------- atom/src/space/boxed.rs | 11 ++-- atom/src/space/cursor.rs | 39 ++++++++++---- atom/src/space/list.rs | 6 ++- atom/src/space/space.rs | 97 +++++++++++++++++++++++++--------- atom/src/space/vec.rs | 97 +++++++++++++++++++++++++--------- atom/src/string.rs | 78 +++++++++++++++++++-------- atom/src/tuple.rs | 15 ++++-- atom/src/vector.rs | 28 +++++++--- atom/tests/atom_integration.rs | 25 +++++---- midi/src/raw.rs | 5 +- midi/src/wmidi_binding.rs | 26 ++++++--- state/src/raw.rs | 12 +++-- 22 files changed, 537 insertions(+), 239 deletions(-) diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index 7c821910..b54807b4 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -47,7 +47,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { Some(space.as_bytes()) } - fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { + fn init( + frame: AtomSpaceWriter<'handle, 'space>, + _: (), + ) -> Option> { Some(frame) } } @@ -72,7 +75,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); - let data = writer.allocate_unaligned(SLICE_LENGTH - 1).unwrap(); + let data = writer.allocate(SLICE_LENGTH - 1).unwrap(); for (i, value) in data.into_iter().enumerate() { *value = i as u8; @@ -97,7 +100,9 @@ mod tests { // reading { - let data = unsafe { Chunk::read(raw_space.split_atom_body(urids.chunk).unwrap().0, ()) }.unwrap(); + let data = + unsafe { Chunk::read(raw_space.split_atom_body(urids.chunk).unwrap().0, ()) } + .unwrap(); assert_eq!(data.len(), SLICE_LENGTH); for (i, value) in data.iter().enumerate() { diff --git a/atom/src/header.rs b/atom/src/header.rs index a555fff9..5092670b 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -1,13 +1,20 @@ +use urid::URID; + #[repr(C, align(8))] #[derive(Copy, Clone)] pub struct AtomHeader { - inner: lv2_sys::LV2_Atom + inner: lv2_sys::LV2_Atom, } impl AtomHeader { #[inline] - pub fn from_raw(inner: lv2_sys::LV2_Atom) -> Self { - Self { inner } + pub(crate) fn new(atom_type: URID) -> Self { + Self { + inner: lv2_sys::LV2_Atom { + size: 0, + type_: atom_type.get(), + }, + } } #[inline] @@ -31,4 +38,3 @@ impl AtomHeader { self.inner.type_ } } - diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 7e04d605..349c9293 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -73,9 +73,9 @@ pub mod string; pub mod tuple; pub mod vector; +mod header; #[cfg(feature = "lv2-core")] pub mod port; -mod header; /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { @@ -87,15 +87,15 @@ pub mod prelude { pub use port::AtomPort; pub use scalar::{AtomURID, Bool, Double, Float, Int, Long}; pub use sequence::{Sequence, TimeStamp, TimeStampURID}; - pub use space::{AtomSpaceWriter, SpaceAllocator, AtomSpace, Space}; + pub use space::{AtomSpace, AtomSpaceWriter, Space, SpaceAllocator}; pub use string::{Literal, LiteralInfo, String}; pub use tuple::Tuple; pub use vector::Vector; } +use crate::header::AtomHeader; use space::*; use urid::*; -use crate::header::AtomHeader; #[derive(Clone, URIDCollection)] /// Collection with the URIDs of all `UriBound`s in this crate. @@ -128,8 +128,7 @@ impl AtomURIDCollection { /// This is the foundation of this crate: Types that implement `Atom` define the reading and writing functions for an atom type. However, these types will never be constructed; They are only names to be used for generic type arguments. /// /// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom. -pub trait Atom<'handle, 'space: 'handle>: UriBound -{ +pub trait Atom<'handle, 'space: 'handle>: UriBound { /// The atom-specific parameter of the `read` function. /// /// If your atom does not need a reading parameter, you may set it to `()`. @@ -160,7 +159,8 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: &'space Space, parameter: Self::ReadParameter) -> Option; + unsafe fn read(body: &'space Space, parameter: Self::ReadParameter) + -> Option; /// Initialize the body of the atom. /// @@ -231,7 +231,7 @@ impl<'space> UnidentifiedAtom<'space> { #[inline] pub fn header(&self) -> Option<&'space AtomHeader> { // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - unsafe { self.space.read_unchecked() } + unsafe { self.space.assume_init_value() } } #[inline] diff --git a/atom/src/object.rs b/atom/src/object.rs index 7842f408..7f9744a9 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -116,10 +116,13 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { header: ObjectHeader, ) -> Option> { { - space::write_value(&mut frame, sys::LV2_Atom_Object_Body { - id: header.id.map(URID::get).unwrap_or(0), - otype: header.otype.get(), - })?; + space::write_value( + &mut frame, + sys::LV2_Atom_Object_Body { + id: header.id.map(URID::get).unwrap_or(0), + otype: header.otype.get(), + }, + )?; } Some(ObjectWriter { frame }) } @@ -144,7 +147,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Blank { #[allow(clippy::unit_arg)] #[inline] - unsafe fn read(body: &'handle Space, parameter: Self::ReadParameter) -> Option { + unsafe fn read( + body: &'handle Space, + parameter: Self::ReadParameter, + ) -> Option { Object::read(body, parameter) } @@ -274,7 +280,7 @@ impl Property { ) -> Option<()> { let header = StrippedPropertyHeader { key: key.get(), - context: context.map(URID::get).unwrap_or(0) + context: context.map(URID::get).unwrap_or(0), }; space::write_value(space, header)?; @@ -288,7 +294,6 @@ mod tests { use crate::space::*; use std::mem::size_of; use urid::*; - use std::ops::Deref; #[test] fn test_object() { @@ -333,12 +338,12 @@ mod tests { // Atom header: size: u32, type: u32 // Object header: id: u32 = None, otype: u32 = object_type - // Object prop header1: key: u32 = first_key, context: u32 = 0 - // Object prop body atom: size: u32 = 4 type: u32 = int - // Int atom value: i32 = 17, padding(4) - // Object prop header12 key: u32 = first_key, context: u32 = 0 - // Object prop body atom: size: u32 = 4 type: u32 = int - // Float atom value: i32 = 69, padding(4) + // Object prop header1: key: u32 = first_key, context: u32 = 0 + // Object prop body atom: size: u32 = 4 type: u32 = int + // Int atom value: i32 = 17, padding(4) + // Object prop header12 key: u32 = first_key, context: u32 = 0 + // Object prop body atom: size: u32 = 4 type: u32 = int + // Float atom value: i32 = 69, padding(4) // verifying { @@ -356,12 +361,19 @@ mod tests { ); // Object. - let (object, space) = unsafe { atom.body().unwrap().split_for_value_as_unchecked::() }.unwrap(); + let (object, space) = unsafe { + atom.body() + .unwrap() + .split_for_value_as_unchecked::() + } + .unwrap(); assert_eq!(object.id, 0); assert_eq!(object.otype, object_type); // First property. - let (property, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (property, space) = + unsafe { space.split_for_value_as_unchecked::() } + .unwrap(); assert_eq!(property.key, first_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.int); @@ -371,7 +383,9 @@ mod tests { assert_eq!(*value, first_value); // Second property. - let (property, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (property, space) = + unsafe { space.split_for_value_as_unchecked::() } + .unwrap(); assert_eq!(property.key, second_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.float); diff --git a/atom/src/port.rs b/atom/src/port.rs index 4a0a9f87..e7f3bf99 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -89,7 +89,9 @@ impl<'a> PortWriter<'a> { if !self.has_been_written { self.has_been_written = true; // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. - let space: &'write mut SpaceCursor<'write> = unsafe { ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) }; + let space: &'write mut SpaceCursor<'write> = unsafe { + ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) + }; crate::space::init_atom(space, urid, parameter) } else { None @@ -141,22 +143,22 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = crate::space::init_atom(&mut space, urids.chunk, ()).unwrap(); - writer - .allocate_unaligned(256 - size_of::()) - .unwrap(); + writer.allocate(256 - size_of::()).unwrap(); } // Getting a writer with the port. { - let mut writer = - unsafe { AtomPort::output_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) }; + let mut writer = unsafe { + AtomPort::output_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) + }; writer.init::(urids.int, 42).unwrap(); } // Reading { - let reader = - unsafe { AtomPort::input_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) }; + let reader = unsafe { + AtomPort::input_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) + }; assert_eq!(reader.read::(urids.int, ()).unwrap(), 42); } } diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index 4b92c6cf..b4923555 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -55,7 +55,10 @@ pub trait ScalarAtom: UriBound { /// Try to write the atom into a space. /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. - fn write_scalar<'handle, 'space: 'handle>(mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType) -> Option<()> { + fn write_scalar<'handle, 'space: 'handle>( + mut frame: AtomSpaceWriter<'handle, 'space>, + value: Self::InternalType, + ) -> Option<()> { space::write_value(&mut frame, value)?; Some(()) } @@ -196,7 +199,9 @@ mod tests { // reading { let (body, _) = unsafe { raw_space.split_atom_body(urid) }.unwrap(); - unsafe { assert_eq!(A::read(body, ()).unwrap(), value); } + unsafe { + assert_eq!(A::read(body, ()).unwrap(), value); + } } } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index 76a1890c..0a769037 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -96,7 +96,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { Some(SequenceIterator { space: body, unit }) } - fn init(mut frame: AtomSpaceWriter<'handle, 'space>, unit: TimeStampURID) -> Option> { + fn init( + mut frame: AtomSpaceWriter<'handle, 'space>, + unit: TimeStampURID, + ) -> Option> { let header = sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), @@ -177,7 +180,8 @@ impl<'a> Iterator for SequenceIterator<'a> { fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> { // SAFETY: The validity of the space's contents is guaranteed by this type. - let (raw_stamp, space) = unsafe { self.space.split_for_value_as_unchecked::() }?; + let (raw_stamp, space) = + unsafe { self.space.split_for_value_as_unchecked::() }?; let stamp = match self.unit { TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) }, @@ -280,7 +284,12 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space::init_atom(&mut space, urids.atom.sequence, TimeStampURID::Frames(urids.units.frame)).unwrap(); + let mut writer = space::init_atom( + &mut space, + urids.atom.sequence, + TimeStampURID::Frames(urids.units.frame), + ) + .unwrap(); writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) @@ -294,7 +303,9 @@ mod tests { // verifying { - let (sequence, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); + let (sequence, space) = + unsafe { raw_space.split_for_value_as_unchecked::() } + .unwrap(); assert_eq!(sequence.atom.type_, urids.atom.sequence); assert_eq!( sequence.atom.size as usize, @@ -307,18 +318,22 @@ mod tests { ); assert_eq!(sequence.body.unit, urids.units.frame); - let (stamp, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (stamp, space) = + unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(unsafe { stamp.frames }, 0); - let (int, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (int, space) = + unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); - let (stamp, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (stamp, space) = + unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(unsafe { stamp.frames }, 1); - let (int, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (int, _space) = + unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.long); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 17); @@ -326,7 +341,9 @@ mod tests { // reading { - let body = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.body().unwrap(); + let body = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) } + .body() + .unwrap(); let mut reader = unsafe { Sequence::read(body, urids.units.beat) }.unwrap(); assert_eq!(reader.unit(), TimeStampUnit::Frames); diff --git a/atom/src/space.rs b/atom/src/space.rs index ebdc2cf2..2468b007 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -1,17 +1,16 @@ //! Smart pointers with safe atom reading and writing methods. -mod list; -mod space; mod allocatable; mod atom_writer; mod boxed; -mod vec; mod cursor; +mod list; +mod space; +mod vec; -pub use space::{AtomSpace, Space}; -pub use list::{SpaceList, SpaceHead}; pub use allocatable::*; pub use atom_writer::AtomSpaceWriter; -pub use vec::{VecSpace, VecSpaceCursor}; pub use cursor::SpaceCursor; - +pub use list::{SpaceHead, SpaceList}; +pub use space::{AtomSpace, Space}; +pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 99f639b4..b517d1da 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -1,6 +1,6 @@ +use crate::space::{AtomSpaceWriter, Space}; use crate::Atom; use urid::URID; -use crate::space::{AtomSpaceWriter, Space}; use core::mem::size_of_val; use std::mem::MaybeUninit; @@ -9,18 +9,24 @@ use std::mem::MaybeUninit; /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. pub trait SpaceAllocator<'a> { - /// Try to allocate memory on the internal data slice. - /// - /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. - fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]>; - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])>; + #[must_use] + unsafe fn rewind(&mut self, byte_count: usize) -> bool; + fn allocated_bytes(&self) -> &[u8]; fn allocated_bytes_mut(&mut self) -> &mut [u8]; fn remaining_bytes(&self) -> &[u8]; fn remaining_bytes_mut(&mut self) -> &mut [u8]; + + /// Try to allocate memory on the internal data slice. + /// + /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. + #[inline] + fn allocate(&mut self, size: usize) -> Option<&mut [u8]> { + self.allocate_and_split(size).map(|(_, s)| s) + } } /* impl<'a> SpaceAllocator<'a> for &'a mut [u8] { @@ -55,55 +61,77 @@ pub fn realign<'a, T: 'static, S: AllocateSpace<'a>>(space: &mut S) -> Option<() Some(()) }*/ +// This function is separate to ensure proper lifetimes unsafe fn assume_init_mut(s: &mut MaybeUninit) -> &mut T { // SAFETY: the caller must guarantee that `self` is initialized. // This also means that `self` must be a `value` variant. - unsafe { - &mut *s.as_mut_ptr() - } + &mut *s.as_mut_ptr() } #[inline] -pub fn allocate<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl SpaceAllocator<'space>, size: usize) -> Option<&'handle mut Space> { +pub fn allocate<'handle, 'space: 'handle, T: 'static>( + space: &'handle mut impl SpaceAllocator<'space>, + size: usize, +) -> Option<&'handle mut Space> { let required_padding = Space::::padding_for(space.remaining_bytes()); - let raw = space.allocate_unaligned(size + required_padding)?; + let raw = space.allocate(size + required_padding)?; Space::try_align_from_bytes_mut(raw) } #[inline] -pub fn allocate_values<'handle, 'space: 'handle, T: 'static>(space: &'handle mut impl SpaceAllocator<'space>, count: usize) -> Option<&'handle mut [MaybeUninit]> { +pub fn allocate_values<'handle, 'space: 'handle, T: 'static>( + space: &'handle mut impl SpaceAllocator<'space>, + count: usize, +) -> Option<&'handle mut [MaybeUninit]> { let space = allocate(space, count * ::core::mem::size_of::())?; Some(space.as_uninit_slice_mut()) } #[inline] -pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>(space: &'handle mut impl SpaceAllocator<'space>, atom_type: URID, write_parameter: A::WriteParameter) -> Option { +pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>( + space: &'handle mut impl SpaceAllocator<'space>, + atom_type: URID, + write_parameter: A::WriteParameter, +) -> Option { let space: AtomSpaceWriter<'handle, 'space> = AtomSpaceWriter::write_new(space, atom_type)?; A::init(space, write_parameter) } #[inline] -pub fn write_bytes<'handle, 'space: 'handle>(space: &'handle mut impl SpaceAllocator<'space>, bytes: &[u8]) -> Option<&'handle mut [u8]> { - let space = space.allocate_unaligned(bytes.len())?; +pub fn write_bytes<'handle, 'space: 'handle>( + space: &'handle mut impl SpaceAllocator<'space>, + bytes: &[u8], +) -> Option<&'handle mut [u8]> { + let space = space.allocate(bytes.len())?; space.copy_from_slice(bytes); Some(space) } #[inline] -pub fn write_value<'handle, 'space: 'handle, T>(space: &'handle mut impl SpaceAllocator<'space>, value: T) -> Option<&'handle mut T> - where T: Copy + Sized + 'static { +pub fn write_value<'handle, 'space: 'handle, T>( + space: &'handle mut impl SpaceAllocator<'space>, + value: T, +) -> Option<&'handle mut T> +where + T: Copy + Sized + 'static, +{ let space = allocate(space, size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. let space = unsafe { space.as_uninit_mut_unchecked() }; *space = MaybeUninit::new(value); // SAFETY: the MaybeUninit has now been properly initialized. - Some (unsafe { assume_init_mut(space) }) + Some(unsafe { assume_init_mut(space) }) } -pub fn write_values<'handle, 'space: 'handle, T>(space: &'handle mut impl SpaceAllocator<'space>, values: &[T]) -> Option<&'handle mut [T]> - where T: Copy + Sized + 'static { +pub fn write_values<'handle, 'space: 'handle, T>( + space: &'handle mut impl SpaceAllocator<'space>, + values: &[T], +) -> Option<&'handle mut [T]> +where + T: Copy + Sized + 'static, +{ let space: &mut Space = allocate(space, size_of_val(values))?; let space = space.as_uninit_slice_mut(); @@ -117,21 +145,21 @@ pub fn write_values<'handle, 'space: 'handle, T>(space: &'handle mut impl SpaceA #[cfg(test)] mod tests { - use crate::space::{AtomSpace, write_value, init_atom}; use crate::prelude::{Int, SpaceAllocator}; - use urid::URID; use crate::space::cursor::SpaceCursor; + use crate::space::{init_atom, write_value, AtomSpace}; + use urid::URID; const INT_URID: URID = unsafe { URID::new_unchecked(5) }; #[test] - fn test_init_atom_lifetimes () { + fn test_init_atom_lifetimes() { assert_eq!(AtomSpace::alignment(), 8); let mut space = AtomSpace::boxed(32); assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed - let mut cursor= SpaceCursor::new(space.as_bytes_mut()); // The pointer that is going to be moved as we keep writing. + let mut cursor = SpaceCursor::new(space.as_bytes_mut()); // The pointer that is going to be moved as we keep writing. let new_value = write_value(&mut cursor, 42u8).unwrap(); assert_eq!(42, *new_value); @@ -150,12 +178,13 @@ mod tests { assert_eq!(12, cursor.len()); }*/ - assert_eq!(space.as_bytes(), [ - 42, 0, 0, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 5, 0, 0, 0, - 69, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ]); + assert_eq!( + space.as_bytes(), + [ + 42, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + ] + ); assert_eq!(32, space.len()); } -} \ No newline at end of file +} diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index a93fd0ec..90af4b1f 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,6 +1,6 @@ -use crate::space::SpaceAllocator; -use urid::URID; use crate::header::AtomHeader; +use crate::space::{Space, SpaceAllocator}; +use urid::URID; /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'handle, 'space: 'handle> { @@ -11,47 +11,75 @@ pub struct AtomSpaceWriter<'handle, 'space: 'handle> { impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { #[inline] pub fn atom_header(&self) -> AtomHeader { - todo!() + let previous = self + .parent + .allocated_bytes() + .get(self.atom_header_index..) + .unwrap(); + let space = Space::try_from_bytes(previous).unwrap(); + + unsafe { *space.assume_init_value().unwrap() } } - #[inline] - fn atom_header_mut(&self) -> &mut AtomHeader { todo!() } + fn atom_header_mut(&mut self) -> &mut AtomHeader { + let previous = self + .parent + .allocated_bytes_mut() + .get_mut(self.atom_header_index..) + .unwrap(); + let space = Space::::try_from_bytes_mut(previous).unwrap(); + + unsafe { space.assume_init_value_mut().unwrap() } + } /// Create a new framed space with the given parent and type URID. - pub fn write_new(parent: &'handle mut impl SpaceAllocator<'space>, urid: URID) -> Option { - let atom_header_index = parent.allocated_bytes().len(); - let atom = AtomHeader::from_raw(sys::LV2_Atom { - size: 0, - type_: urid.get(), - }); + pub fn write_new( + parent: &'handle mut impl SpaceAllocator<'space>, + urid: URID, + ) -> Option { + let atom = AtomHeader::new(urid); - let atom = crate::space::write_value(parent, atom)?; - Some(Self { atom_header_index, parent }) + let atom_header_index = parent.allocated_bytes().len(); + crate::space::write_value(parent, atom)?; + Some(Self { + atom_header_index, + parent, + }) } } impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handle, 'space> { #[inline] - fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { - let result = self.parent.allocate_unaligned(size); - if result.is_some() { - // TODO - // self.atom_header_mut().as_raw_mut().size += size as u32; - } + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + let (previous, current) = self.parent.allocate_and_split(size)?; - result + let space = + Space::::try_from_bytes_mut(previous.get_mut(self.atom_header_index..)?)?; + let header = unsafe { space.assume_init_value_mut() }?; + header.as_raw_mut().size += size as u32; + + Some((previous, current)) } - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { - todo!() + #[inline] + unsafe fn rewind(&mut self, byte_count: usize) -> bool { + let rewound = self.parent.rewind(byte_count); + + if rewound { + self.atom_header_mut().as_raw_mut().size -= byte_count as u32; + } + + rewound } + #[inline] fn allocated_bytes(&self) -> &[u8] { - todo!() + self.parent.allocated_bytes() } + #[inline] fn allocated_bytes_mut(&mut self) -> &mut [u8] { - todo!() + self.parent.allocated_bytes_mut() } #[inline] @@ -67,10 +95,10 @@ impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handl #[cfg(test)] mod tests { - use core::mem::size_of; use crate::prelude::AtomSpaceWriter; - use urid::URID; use crate::space::cursor::SpaceCursor; + use core::mem::size_of; + use urid::URID; #[test] fn test_padding_inside_frame() { @@ -86,9 +114,7 @@ mod tests { // writing { let mut root = SpaceCursor::new(raw_space); - let mut frame = - AtomSpaceWriter::write_new(&mut root, URID::<()>::new(1).unwrap()) - .unwrap(); + let mut frame = AtomSpaceWriter::write_new(&mut root, URID::new(1).unwrap()).unwrap(); crate::space::write_value(&mut frame, 42u32).unwrap(); crate::space::write_value(&mut frame, 17u32).unwrap(); } @@ -110,4 +136,4 @@ mod tests { assert_eq!(value, 17); } } -} \ No newline at end of file +} diff --git a/atom/src/space/boxed.rs b/atom/src/space/boxed.rs index ee594ef2..e661b8c5 100644 --- a/atom/src/space/boxed.rs +++ b/atom/src/space/boxed.rs @@ -1,6 +1,6 @@ +use crate::prelude::Space; use std::mem::{size_of, MaybeUninit}; use std::ops::{Deref, DerefMut}; -use crate::prelude::Space; pub(crate) fn byte_index_to_value_index(size: usize) -> usize { let type_size = size_of::(); @@ -12,13 +12,16 @@ pub(crate) fn byte_index_to_value_index(size: usize) -> usize { } pub(crate) struct BoxedSpace { - pub(crate) inner: Box<[MaybeUninit]> + pub(crate) inner: Box<[MaybeUninit]>, } impl BoxedSpace { #[inline] pub fn new_zeroed(size: usize) -> Self { - Self { inner: vec![MaybeUninit::zeroed(); byte_index_to_value_index::(size)].into_boxed_slice() } + Self { + inner: vec![MaybeUninit::zeroed(); byte_index_to_value_index::(size)] + .into_boxed_slice(), + } } } @@ -36,4 +39,4 @@ impl DerefMut for BoxedSpace { fn deref_mut(&mut self) -> &mut Self::Target { Space::::from_uninit_slice_mut(&mut self.inner) } -} \ No newline at end of file +} diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index a45dcc77..2adc11e0 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -2,39 +2,56 @@ use crate::space::SpaceAllocator; pub struct SpaceCursor<'a> { data: &'a mut [u8], - index: usize + allocated_length: usize, } impl<'a> SpaceCursor<'a> { pub fn new(data: &'a mut [u8]) -> Self { - Self { data, index: 0 } + Self { + data, + allocated_length: 0, + } } } impl<'a> SpaceAllocator<'a> for SpaceCursor<'a> { - fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { - todo!() + #[inline] + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + let (allocated, allocatable) = self.data.split_at_mut(self.allocated_length); + let new_allocation = allocatable.get_mut(..size)?; + self.allocated_length = self.allocated_length.checked_add(size)?; + + Some((allocated, new_allocation)) } - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { - todo!() + #[inline] + unsafe fn rewind(&mut self, byte_count: usize) -> bool { + if self.allocated_length < byte_count { + return false; + } + + self.allocated_length -= byte_count; + + true } + #[inline] fn allocated_bytes(&self) -> &[u8] { - todo!() + &self.data[..self.allocated_length] } + #[inline] fn allocated_bytes_mut(&mut self) -> &mut [u8] { - todo!() + &mut self.data[..self.allocated_length] } #[inline] fn remaining_bytes(&self) -> &[u8] { - self.data + &self.data[self.allocated_length..] } #[inline] fn remaining_bytes_mut(&mut self) -> &mut [u8] { - self.data + &mut self.data[self.allocated_length..] } -} \ No newline at end of file +} diff --git a/atom/src/space/list.rs b/atom/src/space/list.rs index 89ce2541..21bc2311 100644 --- a/atom/src/space/list.rs +++ b/atom/src/space/list.rs @@ -95,7 +95,7 @@ impl<'a> SpaceHead<'a> { impl<'a> SpaceAllocator<'a> for SpaceHead<'a> { #[inline] - fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { + fn 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); @@ -124,4 +124,8 @@ impl<'a> SpaceAllocator<'a> for SpaceHead<'a> { fn remaining_bytes_mut(&mut self) -> &mut [u8] { &mut [] } + + unsafe fn rewind(&mut self, byte_count: usize) -> bool { + todo!() + } } diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 78799e9c..954165f7 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -1,11 +1,11 @@ +use crate::header::AtomHeader; +use crate::UnidentifiedAtom; use core::mem::{align_of, size_of}; -use std::mem::{MaybeUninit, size_of_val}; use std::marker::PhantomData; -use urid::URID; -use crate::UnidentifiedAtom; -use std::ops::{DerefMut, Deref}; +use std::mem::{size_of_val, MaybeUninit}; +use std::ops::{Deref, DerefMut}; use std::slice::{from_raw_parts, from_raw_parts_mut}; -use crate::header::AtomHeader; +use urid::URID; /// An aligned slice of bytes that is designed to contain a given type `T` (by default, Atoms). /// @@ -15,7 +15,7 @@ pub struct Space { _type: PhantomData, // Note: this could be [MaybeUninit] for alignment, but Spaces can have extra unaligned bytes at the end. // TODO: replace this with [MaybeUninit] - data: [u8] + data: [u8], } pub type AtomSpace = Space; @@ -32,7 +32,11 @@ impl Space { pub(crate) fn padding_for(data: &[u8]) -> usize { let alignment = align_of::(); let start = data.as_ptr() as usize; - if start % alignment == 0 { 0 } else { alignment - start % alignment } + if start % alignment == 0 { + 0 + } else { + alignment - start % alignment + } } #[inline] @@ -40,7 +44,10 @@ impl Space { align_of::() } - pub fn boxed(size: usize) -> impl Deref + DerefMut where T: Copy { + pub fn boxed(size: usize) -> impl Deref + DerefMut + where + T: Copy, + { crate::space::boxed::BoxedSpace::new_zeroed(size) } @@ -89,7 +96,8 @@ impl Space { #[inline] pub(crate) fn from_uninit_slice_mut(slice: &mut [MaybeUninit]) -> &mut Self { // SAFETY: reinterpreting as raw bytes is safe for any value - let bytes = unsafe { from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of_val(slice)) }; + let bytes = + unsafe { from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of_val(slice)) }; // SAFETY: The pointer is a slice of T, therefore it is already correctly aligned unsafe { Self::from_bytes_mut_unchecked(bytes) } } @@ -139,7 +147,8 @@ impl Space { #[inline] fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { // SAFETY: We just aligned the slice start - data.get(Self::padding_for(data)..).map(|data| unsafe { Space::from_bytes_unchecked(data) }) + data.get(Self::padding_for(data)..) + .map(|data| unsafe { Space::from_bytes_unchecked(data) }) } /// Creates a new space from a slice of bytes, aligning it if necessary. @@ -151,7 +160,8 @@ impl Space { #[inline] pub(crate) fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { // SAFETY: We just aligned the slice's start - data.get_mut(Self::padding_for(data)..).map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) + data.get_mut(Self::padding_for(data)..) + .map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) } #[inline] @@ -168,7 +178,7 @@ impl Space { } #[inline] - pub fn slice(&self, length: usize) -> Option<&Self> { + pub fn slice(&self, length: usize) -> Option<&Self> { // SAFETY: The data is part of the original slice which was aligned already. let d = self.data.get(..length); Some(unsafe { Self::from_bytes_unchecked(d?) }) @@ -194,6 +204,7 @@ impl Space { } #[inline] + // FIXME: rename this pub unsafe fn split_for_value_unchecked(&self) -> Option<(&T, &Self)> { let (value, rest) = self.split_for_value()?; @@ -251,14 +262,20 @@ impl Space { } #[inline] - pub(crate) unsafe fn read_unchecked(&self) -> Option<&T> { + pub(crate) unsafe fn assume_init_value(&self) -> Option<&T> { // SAFETY: The caller has to ensure this slice actually points to initialized memory. Some(&*(self.as_uninit()?.as_ptr())) } + #[inline] + pub(crate) unsafe fn assume_init_value_mut(&mut self) -> Option<&mut T> { + // SAFETY: The caller has to ensure this slice actually points to initialized memory. + Some(&mut *(self.as_uninit_mut()?.as_mut_ptr())) + } + #[inline] pub unsafe fn read_as_unchecked(&self) -> Option<&U> { - self.aligned()?.read_unchecked() + self.aligned()?.assume_init_value() } /// Gets a `T`-aligned pointer to the contents. @@ -274,6 +291,19 @@ impl Space { Some(unsafe { self.as_uninit_unchecked() }) } + /// Gets a `T`-aligned pointer to the contents. + ///split_for_type + /// This methods returns [`None`](Option::None) if the space is not large enough for a value of type `T`. + #[inline] + fn as_uninit_mut(&mut self) -> Option<&mut MaybeUninit> { + if self.data.len() < size_of::() { + return None; + } + + // SAFETY: We just checked that the space was actually big enough. + Some(unsafe { self.as_uninit_mut_unchecked() }) + } + /// Gets a `T`-aligned pointer to the contents, but without checking that there actually is enough space to hold `T`. #[inline] unsafe fn as_uninit_unchecked(&self) -> &MaybeUninit { @@ -295,11 +325,16 @@ impl Space { #[inline] pub(crate) fn as_uninit_slice(&self) -> &[MaybeUninit] { // SAFETY: This type ensures alignment, so casting aligned bytes to uninitialized memory is safe. - unsafe { ::core::slice::from_raw_parts(self.data.as_ptr() as *const MaybeUninit, self.data.len() / size_of::()) } + unsafe { + ::core::slice::from_raw_parts( + self.data.as_ptr() as *const MaybeUninit, + self.data.len() / size_of::(), + ) + } } #[inline] - pub unsafe fn assume_init(&self) -> &[T] { + pub unsafe fn assume_init_slice(&self) -> &[T] { let data = self.as_uninit_slice(); &*(data as *const _ as *const [T]) } @@ -311,7 +346,12 @@ impl Space { #[inline] pub(crate) fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: This type ensures alignment, so casting aligned bytes to uninitialized memory is safe. - unsafe { ::core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut MaybeUninit, self.data.len() / size_of::()) } + unsafe { + ::core::slice::from_raw_parts_mut( + self.data.as_mut_ptr() as *mut MaybeUninit, + self.data.len() / size_of::(), + ) + } } } @@ -357,14 +397,16 @@ impl AtomSpace { #[inline] pub unsafe fn to_atom(&self) -> Option { - let header = self.read_unchecked()?; // Try to read to ensure there is enough room - // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents - Some(UnidentifiedAtom::new_unchecked(self.slice(header.size_of_atom())?)) + let header = self.assume_init_value()?; // Try to read to ensure there is enough room + // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents + Some(UnidentifiedAtom::new_unchecked( + self.slice(header.size_of_atom())?, + )) } #[inline] pub unsafe fn split_atom(&self) -> Option<(UnidentifiedAtom, &Self)> { - let header = self.read_unchecked()?; + let header = self.assume_init_value()?; let (atom, rest) = self.split_at(header.size_of_atom())?; let atom = UnidentifiedAtom::new_unchecked(atom); @@ -378,7 +420,7 @@ impl AtomSpace { let header = &*header.as_ptr(); if header.urid() != urid { - return None + return None; } body.split_at(header.size_of_body()) @@ -412,13 +454,13 @@ mod tests { assert_eq!(lower_space[i], i as u8); } - let integer = unsafe { space.read_unchecked() }.unwrap(); + let integer = unsafe { space.assume_init_value() }.unwrap(); assert_eq!(*integer, 0x42424242); } #[test] fn test_split_atom() { - let mut space = AtomSpace::boxed(256); + let mut space = AtomSpace::boxed(256); let urid: URID = unsafe { URID::new_unchecked(17) }; // Writing an integer atom. @@ -459,7 +501,10 @@ mod tests { let created_space = unsafe { Space::from_atom_mut(written_atom) }; - assert!(::core::ptr::eq(written_atom_addr, created_space.as_bytes().as_ptr())); + assert!(::core::ptr::eq( + written_atom_addr, + created_space.as_bytes().as_ptr() + )); assert_eq!(created_space.len(), size_of::() + 42); { @@ -506,4 +551,4 @@ mod tests { assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref()); } -} \ No newline at end of file +} diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index c8b7d713..328e1f6a 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,29 +1,46 @@ +#![deny(unsafe_code)] + +use crate::space::{Space, SpaceAllocator}; use std::mem::MaybeUninit; -use crate::space::{SpaceAllocator, Space}; use std::ops::Range; pub struct VecSpace { - inner: Vec> + inner: Vec>, } impl VecSpace { #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { inner: vec![MaybeUninit::zeroed(); capacity] } + pub fn new_with_capacity(capacity: usize) -> Self { + Self { + inner: vec![MaybeUninit::zeroed(); capacity], + } + } + + #[inline] + pub fn as_space(&self) -> &Space { + Space::from_uninit_slice(&self.inner) + } + + #[inline] + pub fn as_space_mut(&mut self) -> &mut Space { + Space::from_uninit_slice_mut(&mut self.inner) } #[inline] pub fn as_bytes(&self) -> &[u8] { - Space::::from_uninit_slice(&self.inner).as_bytes() + self.as_space().as_bytes() } #[inline] pub fn as_bytes_mut(&mut self) -> &mut [u8] { - Space::::from_uninit_slice_mut(&mut self.inner).as_bytes_mut() + self.as_space_mut().as_bytes_mut() } #[inline] - fn get_or_allocate_bytes_mut(&mut self, byte_range: Range) -> Option<&mut [u8]> { + fn get_or_allocate_bytes_mut( + &mut self, + byte_range: Range, + ) -> Option<(&mut [u8], &mut [u8])> { let byte_len = self.inner.len() * ::core::mem::size_of::(); let max = byte_range.start.max(byte_range.end); @@ -32,64 +49,96 @@ impl VecSpace { self.inner.resize(new_size, MaybeUninit::zeroed()); } - self.as_bytes_mut().get_mut(byte_range) + let bytes = self.as_bytes_mut(); + bytes.get(byte_range.clone())?; // To make sure everything is in range instead of panicking on split_at_mut + let (previous, allocatable) = bytes.split_at_mut(byte_range.start); + + return Some(( + previous, + allocatable.get_mut(..byte_range.end - byte_range.start)?, + )); } #[inline] pub fn cursor(&mut self) -> VecSpaceCursor { - VecSpaceCursor { vec: self, byte_index: 0 } + VecSpaceCursor { + vec: self, + allocated_length: 0, + } } } pub struct VecSpaceCursor<'vec, T> { vec: &'vec mut VecSpace, - byte_index: usize + allocated_length: usize, } impl<'vec, T: Copy + 'static> SpaceAllocator<'vec> for VecSpaceCursor<'vec, T> { - fn allocate_unaligned(&mut self, size: usize) -> Option<&mut [u8]> { - let end = self.byte_index.checked_add(size)?; - VecSpace::::get_or_allocate_bytes_mut(self.vec, self.byte_index..end) + fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + let end = self.allocated_length.checked_add(size)?; + let result = VecSpace::::get_or_allocate_bytes_mut(self.vec, self.allocated_length..end); + + if result.is_some() { + self.allocated_length = end; + } + + result } - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { - todo!() + #[inline] + #[allow(unsafe_code)] + unsafe fn rewind(&mut self, byte_count: usize) -> bool { + if self.allocated_length < byte_count { + return false; + } + + self.allocated_length -= byte_count; + + true } + #[inline] fn allocated_bytes(&self) -> &[u8] { - todo!() + &self.vec.as_bytes()[..self.allocated_length] } + #[inline] fn allocated_bytes_mut(&mut self) -> &mut [u8] { - todo!() + &mut self.vec.as_bytes_mut()[..self.allocated_length] } #[inline] fn remaining_bytes(&self) -> &[u8] { - self.vec.as_bytes() + self.vec + .as_bytes() + .get(self.allocated_length..) + .unwrap_or(&[]) } #[inline] fn remaining_bytes_mut(&mut self) -> &mut [u8] { - self.vec.as_bytes_mut() + self.vec + .as_bytes_mut() + .get_mut(self.allocated_length..) + .unwrap_or(&mut []) } } #[cfg(test)] mod tests { - use crate::space::{VecSpace, SpaceAllocator}; + use crate::space::{SpaceAllocator, VecSpace}; #[test] - pub fn test_lifetimes () { - let mut buffer = VecSpace::::with_capacity(16); + pub fn test_lifetimes() { + let mut buffer = VecSpace::::new_with_capacity(16); { let mut cursor = buffer.cursor(); - let buf1 = cursor.allocate_unaligned(2).unwrap(); + let buf1 = cursor.allocate(2).unwrap(); buf1[0] = 5 } let _other_cursor = buffer.cursor(); let _other_cursor2 = buffer.cursor(); } -} \ No newline at end of file +} diff --git a/atom/src/string.rs b/atom/src/string.rs index 1a604fed..5e87f530 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -69,8 +69,12 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { .map(|string| (info, string)) } - fn init(mut frame: AtomSpaceWriter<'handle, 'space>, info: LiteralInfo) -> Option> { - crate::space::write_value(&mut frame, + fn init( + mut frame: AtomSpaceWriter<'handle, 'space>, + info: LiteralInfo, + ) -> Option> { + crate::space::write_value( + &mut frame, match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), @@ -80,9 +84,12 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { lang: 0, datatype: datatype.get(), }, - } + }, )?; - Some(StringWriter { frame }) + Some(StringWriter { + frame, + has_nul_byte: false, + }) } } @@ -107,14 +114,21 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for String { Some(core::str::from_utf8(rust_str_bytes).ok()?) } - fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { - Some(StringWriter { frame }) + fn init( + frame: AtomSpaceWriter<'handle, 'space>, + _: (), + ) -> Option> { + Some(StringWriter { + frame, + has_nul_byte: false, + }) } } /// Handle to append strings to a string or literal. pub struct StringWriter<'handle, 'space> { frame: AtomSpaceWriter<'handle, 'space>, + has_nul_byte: bool, // If this writer already wrote a null byte before. } impl<'handle, 'space> StringWriter<'handle, 'space> { @@ -124,19 +138,23 @@ impl<'handle, 'space> StringWriter<'handle, 'space> { /// /// If the internal space for the atom is not big enough, this method returns `None`. pub fn append(&mut self, string: &str) -> Option<&mut str> { - let data = string.as_bytes(); - let space = crate::space::write_bytes(&mut self.frame, data)?; - // FIXME: make a "rewind" function to write the nul byte later - unsafe { Some(std::str::from_utf8_unchecked_mut(space)) } - } -} + // Rewind to overwrite previously written nul_byte before appending the string. + if self.has_nul_byte { + if unsafe { !self.frame.rewind(1) } { + return None; // Could not rewind + } + } + + // Manually write the bytes to make extra room for the nul byte + let bytes = string.as_bytes(); + let space = self.frame.allocate(bytes.len() + 1)?; + space.copy_from_slice(bytes); + // SAFETY: space is guaranteed to be at least 1 byte large + space[bytes.len()] = 0; -impl<'a, 'space> Drop for StringWriter<'a, 'space> { - fn drop(&mut self) { - // Null terminator. - // FIXME: this seems unsafe if the value could not be written for some reason. - // todo!() - let _ = crate::space::write_value(&mut self.frame, 0u8); + self.has_nul_byte = true; + // SAFETY: We just wrote that string, therefore it is guaranteed to be valid UTF-8 + unsafe { Some(std::str::from_utf8_unchecked_mut(&mut space[..bytes.len()])) } } } @@ -172,14 +190,21 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = crate::space::init_atom(&mut space, urids.atom.literal, LiteralInfo::Language(urids.german.into_general())).unwrap(); + let mut writer = crate::space::init_atom( + &mut space, + urids.atom.literal, + LiteralInfo::Language(urids.german.into_general()), + ) + .unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } // verifying { - let (literal, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); + let (literal, space) = + unsafe { raw_space.split_for_value_as_unchecked::() } + .unwrap(); assert_eq!(literal.atom.type_, urids.atom.literal.get()); assert_eq!( @@ -228,11 +253,20 @@ mod tests { // verifying { - let (string, space) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); + let (string, space) = + unsafe { raw_space.split_for_value_as_unchecked::() } + .unwrap(); assert_eq!(string.atom.type_, urids.string); assert_eq!(string.atom.size as usize, SAMPLE0.len() + SAMPLE1.len() + 1); - let string = std::str::from_utf8(space.split_at(string.atom.size as usize).unwrap().0.as_bytes()).unwrap(); + let string = std::str::from_utf8( + space + .split_at(string.atom.size as usize) + .unwrap() + .0 + .as_bytes(), + ) + .unwrap(); assert_eq!(string[..string.len() - 1], SAMPLE0.to_owned() + SAMPLE1); } diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 1d67b13d..7a92544c 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -53,7 +53,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { Some(TupleIterator { space: body }) } - fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { + fn init( + frame: AtomSpaceWriter<'handle, 'space>, + _: (), + ) -> Option> { Some(TupleWriter { frame }) } } @@ -131,7 +134,8 @@ mod tests { + size_of::() ); - let (vector, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (vector, space) = + unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(vector.atom.type_, urids.vector); assert_eq!( vector.atom.size as usize, @@ -141,10 +145,13 @@ mod tests { assert_eq!(vector.body.child_type, urids.int); let (vector_items, space) = space.split_at(size_of::() * 9).unwrap(); - let vector_items = unsafe { std::slice::from_raw_parts(vector_items.as_bytes().as_ptr() as *const i32, 9) }; + let vector_items = unsafe { + std::slice::from_raw_parts(vector_items.as_bytes().as_ptr() as *const i32, 9) + }; assert_eq!(vector_items, &[17; 9]); - let (int, _) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (int, _) = + unsafe { space.split_for_value_as_unchecked::() }.unwrap(); assert_eq!(int.atom.type_, urids.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); diff --git a/atom/src/vector.rs b/atom/src/vector.rs index d5c3a746..d53bd9fa 100644 --- a/atom/src/vector.rs +++ b/atom/src/vector.rs @@ -48,7 +48,10 @@ unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } -impl<'handle, 'space: 'handle, C: ScalarAtom> Atom<'handle, 'space> for Vector where C: 'space, { +impl<'handle, 'space: 'handle, C: ScalarAtom> Atom<'handle, 'space> for Vector +where + C: 'space, +{ type ReadParameter = URID; type ReadHandle = &'space [C::InternalType]; type WriteParameter = URID; @@ -57,15 +60,20 @@ impl<'handle, 'space: 'handle, C: ScalarAtom> Atom<'handle, 'space> for Vector) -> Option<&'space [C::InternalType]> { let (header, body) = body.split_for_value_as_unchecked::()?; - if header.child_type != child_urid || header.child_size as usize != size_of::() { + if header.child_type != child_urid + || header.child_size as usize != size_of::() + { return None; } // SAFETY: We can assume this data was properly initialized by the host. - Some(body.aligned()?.assume_init()) + Some(body.aligned()?.assume_init_slice()) } - fn init(mut frame: AtomSpaceWriter<'handle, 'space>, child_urid: URID) -> Option> { + fn init( + mut frame: AtomSpaceWriter<'handle, 'space>, + child_urid: URID, + ) -> Option> { let body = sys::LV2_Atom_Vector_Body { child_type: child_urid.get(), child_size: size_of::() as u32, @@ -127,14 +135,17 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = crate::space::init_atom(&mut space, urids.vector(), urids.int).unwrap(); + let mut writer = + crate::space::init_atom(&mut space, urids.vector(), urids.int).unwrap(); writer.append(&[42; CHILD_COUNT - 1]); writer.push(1); } // verifying { - let (vector, children) = unsafe { raw_space.split_for_value_as_unchecked::() }.unwrap(); + let (vector, children) = + unsafe { raw_space.split_for_value_as_unchecked::() } + .unwrap(); assert_eq!(vector.atom.type_, urids.vector.get()); assert_eq!( vector.atom.size as usize, @@ -143,8 +154,9 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int.get()); - let children = - unsafe { std::slice::from_raw_parts(children.as_bytes().as_ptr() as *const i32, CHILD_COUNT) }; + let children = unsafe { + std::slice::from_raw_parts(children.as_bytes().as_ptr() as *const i32, CHILD_COUNT) + }; for value in &children[0..children.len() - 1] { assert_eq!(*value, 42); } diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index cce054b9..b6f2ebcf 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -102,12 +102,16 @@ fn main() { let mut input_atom_space = AtomSpace::boxed(256); { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); - let mut writer = lv2_atom::space::init_atom(&mut space, - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame)).unwrap(); - {let _ = writer - .init(TimeStamp::Frames(0), urids.atom.int, 42) - .unwrap(); + let mut writer = lv2_atom::space::init_atom( + &mut space, + urids.atom.sequence, + TimeStampURID::Frames(urids.units.frame), + ) + .unwrap(); + { + let _ = writer + .init(TimeStamp::Frames(0), urids.atom.int, 42) + .unwrap(); } writer .init(TimeStamp::Frames(1), urids.atom.long, 17) @@ -121,10 +125,9 @@ fn main() { let mut output_atom_space = AtomSpace::boxed(256); { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); - lv2_atom::space::init_atom(&mut space, - urids.atom.chunk, - ()).unwrap() - .allocate_unaligned(256 - size_of::()) + lv2_atom::space::init_atom(&mut space, urids.atom.chunk, ()) + .unwrap() + .allocate(256 - size_of::()) .unwrap(); } @@ -166,7 +169,7 @@ fn main() { } // Asserting the result - let (sequence, _) = unsafe { output_atom_space.split_atom_body(urids.atom.sequence)}.unwrap(); + let (sequence, _) = unsafe { output_atom_space.split_atom_body(urids.atom.sequence) }.unwrap(); for (stamp, atom) in unsafe { Sequence::read(sequence, urids.units.beat) }.unwrap() { let stamp = stamp.as_frames().unwrap(); match stamp { diff --git a/midi/src/raw.rs b/midi/src/raw.rs index cf557567..2cefc2b8 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -25,7 +25,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for MidiEvent { Some(body.as_bytes()) } - fn init(frame: AtomSpaceWriter<'handle, 'space>, _: ()) -> Option> { + fn init( + frame: AtomSpaceWriter<'handle, 'space>, + _: (), + ) -> Option> { Some(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 66947a13..2196fff9 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -30,7 +30,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for WMidiEvent { wmidi::MidiMessage::try_from(space.as_bytes()).ok() } - fn init(mut frame: AtomSpaceWriter<'handle, 'space>, message: wmidi::MidiMessage) -> Option<()> { + fn init( + mut frame: AtomSpaceWriter<'handle, 'space>, + message: wmidi::MidiMessage, + ) -> Option<()> { let space: &mut Space = lv2_atom::space::allocate(&mut frame, message.bytes_size())?; message.copy_to_slice(space.as_bytes_mut()).ok()?; @@ -82,11 +85,15 @@ impl<'handle, 'space> Writer<'handle, 'space> { } #[inline] - pub fn write(&mut self, instance: T) -> Option<&mut T> where T: Copy + Sized + 'static, { + pub fn write(&mut self, instance: T) -> Option<&mut T> + where + T: Copy + Sized + 'static, + { lv2_atom::space::write_value(&mut self.frame, instance) } } +// TODO: use rewind instead of relying on a Drop impl<'handle, 'space> Drop for Writer<'handle, 'space> { fn drop(&mut self) { self.write::(0xf7); @@ -96,9 +103,9 @@ impl<'handle, 'space> Drop for Writer<'handle, 'space> { #[cfg(test)] mod tests { use crate::wmidi_binding::*; + use lv2_atom::space::SpaceCursor; use std::convert::TryFrom; use wmidi::*; - use lv2_atom::space::SpaceCursor; #[test] fn test_midi_event() { @@ -117,7 +124,9 @@ mod tests { // verifying { - let (header, space) = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.header_and_body().unwrap(); + let (header, space) = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) } + .header_and_body() + .unwrap(); assert_eq!(header.urid(), urid); assert_eq!(header.size_of_body(), 3); @@ -128,7 +137,9 @@ mod tests { // reading { - let space = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) }.body().unwrap(); + let space = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) } + .body() + .unwrap(); let message = unsafe { WMidiEvent::read(space, ()) }.unwrap(); assert_eq!(message, reference_message); @@ -151,7 +162,10 @@ mod tests { // verifying { - let (header, body) = unsafe { raw_space.to_atom() }.unwrap().header_and_body().unwrap(); + let (header, body) = unsafe { raw_space.to_atom() } + .unwrap() + .header_and_body() + .unwrap(); assert_eq!(header.urid(), urid); assert_eq!(header.size_of_body(), 6); assert_eq!(&body.as_bytes()[..6], &[0xf0, 1, 2, 3, 4, 0xf7]); diff --git a/state/src/raw.rs b/state/src/raw.rs index 6252a471..0cbefbe6 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -55,7 +55,9 @@ impl<'a> StoreHandle<'a> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); let space = AtomSpace::try_from_bytes(&space).ok_or(StateErr::BadData)?; - let (header, data) = unsafe { space.to_atom() }.and_then(|a| a.header_and_body()).ok_or(StateErr::BadData)?; + let (header, data) = unsafe { space.to_atom() } + .and_then(|a| a.header_and_body()) + .ok_or(StateErr::BadData)?; let key = key.get(); let data_ptr = data as *const _ as *const c_void; @@ -124,8 +126,7 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result { if !self.initialized { self.initialized = true; - lv2_atom::space::init_atom(&mut self.head, urid, parameter) - .ok_or(StateErr::Unknown) + lv2_atom::space::init_atom(&mut self.head, urid, parameter).ok_or(StateErr::Unknown) } else { Err(StateErr::Unknown) } @@ -175,7 +176,10 @@ impl<'a> RetrieveHandle<'a> { return Err(StateErr::NoProperty); }; - Ok(StatePropertyReader::new(type_, Space::try_from_bytes(space).ok_or(StateErr::BadData)?)) + Ok(StatePropertyReader::new( + type_, + Space::try_from_bytes(space).ok_or(StateErr::BadData)?, + )) } } From e462d0020de8b9639dc2ced305883809356ef88e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 18 Aug 2021 08:27:51 +0200 Subject: [PATCH 11/54] Some more refactorings --- atom/src/chunk.rs | 2 +- atom/src/header.rs | 15 +++++- atom/src/lib.rs | 91 +++++++++++++++++++++++++---------- atom/src/object.rs | 14 +++--- atom/src/port.rs | 16 +++--- atom/src/sequence.rs | 14 +++--- atom/src/space/allocatable.rs | 13 ++++- atom/src/space/atom_writer.rs | 8 ++- atom/src/space/space.rs | 25 +++++----- atom/src/tuple.rs | 8 +-- docs/metro/src/pipes.rs | 4 +- midi/src/wmidi_binding.rs | 26 ++++------ state/src/raw.rs | 10 ++-- 13 files changed, 151 insertions(+), 95 deletions(-) diff --git a/atom/src/chunk.rs b/atom/src/chunk.rs index b54807b4..1be1d1ab 100644 --- a/atom/src/chunk.rs +++ b/atom/src/chunk.rs @@ -75,7 +75,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); - let data = writer.allocate(SLICE_LENGTH - 1).unwrap(); + let data = writer.allocate(SLICE_LENGTH).unwrap(); for (i, value) in data.into_iter().enumerate() { *value = i as u8; diff --git a/atom/src/header.rs b/atom/src/header.rs index 5092670b..bd507645 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -18,9 +18,20 @@ impl AtomHeader { } #[inline] - pub(crate) fn as_raw_mut(&mut self) -> &mut lv2_sys::LV2_Atom { + pub(crate) fn from_raw(raw: &lv2_sys::LV2_Atom) -> &Self { // SAFETY: AtomHeader is repr(C) and has LV2_Atom as its only field, so transmuting between the two is safe. - unsafe { &mut *(self as *mut Self as *mut _) } + unsafe { &*(raw as *const lv2_sys::LV2_Atom as *const _) } + } + + #[inline] + pub(crate) fn from_raw_mut(raw: &mut lv2_sys::LV2_Atom) -> &mut Self { + // SAFETY: AtomHeader is repr(C) and has LV2_Atom as its only field, so transmuting between the two is safe. + unsafe { &mut *(raw as *mut lv2_sys::LV2_Atom as *mut _) } + } + + #[inline] + pub(crate) unsafe fn set_size_of_body(&mut self, size: usize) { + self.inner.size = size as u32; } #[inline] diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 349c9293..ca9c7733 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -180,62 +180,101 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { /// /// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. #[derive(Clone, Copy)] -// TODO: refactor this so it directly contains the atom and is not a fat pointer to the space that has to be re-checked every time -pub struct UnidentifiedAtom<'space> { - space: &'space AtomSpace, +#[repr(C)] +pub struct UnidentifiedAtom { + header: AtomHeader } -impl<'space> UnidentifiedAtom<'space> { +impl UnidentifiedAtom { /// Construct a new unidentified atom. /// /// # Safety /// - /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body.x + /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] - pub unsafe fn new_unchecked(space: &'space AtomSpace) -> Self { - Self { space } + pub unsafe fn from_space(space: &AtomSpace) -> Option<&Self> { + Some(Self::from_header(space.assume_init_value()?)) + } + + /// Construct a new unidentified atom. + /// + /// # Safety + /// + /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. + #[inline] + pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Option<&mut Self> { + Some(Self::from_header_mut(space.assume_init_value_mut()?)) + } + + /// Construct a new unidentified atom. + /// + /// # Safety + /// + /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. + #[inline] + pub unsafe fn from_space_unchecked(space: &AtomSpace) -> &Self { + Self::from_header(space.assume_init_value_unchecked()) + } + + #[inline] + pub unsafe fn from_header(header: &AtomHeader) -> &Self { + // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. + &*(header as *const _ as *const _) + } + + #[inline] + pub unsafe fn from_header_mut(header: &mut AtomHeader) -> &mut Self { + // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. + &mut *(header as *mut _ as *mut _) } /// Try to read the atom. /// - /// To identify the atom, it's URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read<'a, A: Atom<'a, 'space>>( - &'a self, + /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. + pub fn read<'handle, 'space, A: Atom<'handle, 'space>>( + &'space self, urid: URID, parameter: A::ReadParameter, ) -> Option { - let (header, body) = self.header_and_body()?; - - if header.urid() != urid { + if self.header.urid() != urid { return None; } // SAFETY: the fact that this contains a valid instance of A is checked above. - unsafe { A::read(body, parameter) } + unsafe { A::read(self.body(), parameter) } } #[inline] - pub fn as_space(&self) -> &'space AtomSpace { - self.space + pub fn header(&self) -> &AtomHeader { + &self.header } #[inline] - pub fn header_and_body(&self) -> Option<(&'space AtomHeader, &'space Space)> { - // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - let (header, body) = unsafe { self.space.split_for_value_unchecked() }?; - let body = body.slice(header.size_of_body())?; + fn body_bytes(&self) -> &[u8] { + if self.header.size_of_body() == 0 { + &[] + } else { + // SAFETY: This type's constructor ensures the atom's body is valid + // The edge case of an empty body is also checked above. + let ptr = unsafe { (self as *const UnidentifiedAtom).add(1) }; - Some((&header, body)) + // SAFETY: This type's constructor ensures the atom's body is valid + unsafe { ::core::slice::from_raw_parts(ptr.cast(), self.header.size_of_body()) } + } } #[inline] - pub fn header(&self) -> Option<&'space AtomHeader> { - // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - unsafe { self.space.assume_init_value() } + fn atom_space(&self) -> &AtomSpace { + let ptr = self as *const UnidentifiedAtom as *const u8; + let bytes = unsafe { ::core::slice::from_raw_parts(ptr, self.header.size_of_atom()) }; + + // SAFETY: the bytes are necessarily aligned, since they point to the aligned AtomHeader + unsafe { AtomSpace::from_bytes_unchecked(bytes) } } #[inline] - pub fn body(&self) -> Option<&'space Space> { - self.header_and_body().map(|(_header, body)| body) + pub fn body(&self) -> &AtomSpace { + // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader + unsafe { AtomSpace::from_bytes_unchecked(self.body_bytes()) } } } diff --git a/atom/src/object.rs b/atom/src/object.rs index 7f9744a9..9c04d96a 100644 --- a/atom/src/object.rs +++ b/atom/src/object.rs @@ -170,9 +170,9 @@ pub struct ObjectReader<'a> { } impl<'a> Iterator for ObjectReader<'a> { - type Item = (PropertyHeader, UnidentifiedAtom<'a>); + type Item = (PropertyHeader, &'a UnidentifiedAtom); - fn next(&mut self) -> Option<(PropertyHeader, UnidentifiedAtom<'a>)> { + fn next(&mut self) -> Option<(PropertyHeader, &'a UnidentifiedAtom)> { // SAFETY: The fact that this contains a valid property is guaranteed by this type. let (header, atom, space) = unsafe { Property::read_body(self.space) }?; self.space = space; @@ -257,7 +257,7 @@ impl Property { /// # Safety /// /// The caller must ensure that the given Space actually contains a valid property. - unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, UnidentifiedAtom, &Space)> { + unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, &UnidentifiedAtom, &Space)> { let (header, space) = space.split_for_value_as_unchecked::()?; let header = PropertyHeader { @@ -349,7 +349,7 @@ mod tests { { // Header let (atom, _space) = unsafe { raw_space.split_atom() }.unwrap(); - let header = atom.header().unwrap(); + let header = atom.header(); assert_eq!(header.urid(), urids.object); assert_eq!( header.size_of_body(), @@ -362,9 +362,7 @@ mod tests { // Object. let (object, space) = unsafe { - atom.body() - .unwrap() - .split_for_value_as_unchecked::() + atom.body().split_for_value_as_unchecked::() } .unwrap(); assert_eq!(object.id, 0); @@ -404,7 +402,7 @@ mod tests { assert_eq!(header.otype, object_type); assert_eq!(header.id, None); - let properties: Vec<(PropertyHeader, UnidentifiedAtom)> = iter.collect(); + let properties: Vec<(PropertyHeader, &UnidentifiedAtom)> = iter.collect(); let (header, atom) = properties[0]; assert_eq!(header.key, first_key); assert_eq!(atom.read::(urids.int, ()).unwrap(), first_value); diff --git a/atom/src/port.rs b/atom/src/port.rs index e7f3bf99..05d44d96 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -28,18 +28,20 @@ use lv2_core::port::PortType; use std::ffi::c_void; use std::ptr::NonNull; use urid::URID; +use crate::UnidentifiedAtom; +use crate::header::AtomHeader; /// A handle to read atoms from a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to read atoms. pub struct PortReader<'space> { - space: &'space Space, + atom: &'space UnidentifiedAtom, } impl<'space> PortReader<'space> { /// Create a new port reader. - fn new(space: &'space Space) -> Self { - Self { space } + fn new(atom: &'space UnidentifiedAtom) -> Self { + Self { atom } } /// Read an atom. @@ -47,13 +49,13 @@ impl<'space> PortReader<'space> { /// In order to identify the atom, the reader needs to know it's URID. Also, some atoms require a parameter. However, you can simply pass `()` in most cases. /// /// This method returns `None` if the atom is malformed or simply isn't of the specified type. + #[inline] pub fn read<'handle, A: crate::Atom<'handle, 'space>>( &self, urid: URID, parameter: A::ReadParameter, ) -> Option { - // SAFETY: The port's space has been initialized by the host - unsafe { A::read(self.space.split_atom_body(urid)?.0, parameter) } + self.atom.read(urid, parameter) } } @@ -112,8 +114,8 @@ impl PortType for AtomPort { #[inline] unsafe fn input_from_raw(pointer: NonNull, _sample_count: u32) -> PortReader<'static> { - let space = Space::from_atom(*pointer.cast().as_ref()); - PortReader::new(space) + let header: &'static AtomHeader = AtomHeader::from_raw(&*pointer.cast().as_ptr()); + PortReader::new(UnidentifiedAtom::from_header(header)) } #[inline] diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index 0a769037..6ca1d187 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -176,9 +176,9 @@ impl<'a> SequenceIterator<'a> { } impl<'a> Iterator for SequenceIterator<'a> { - type Item = (TimeStamp, UnidentifiedAtom<'a>); + type Item = (TimeStamp, &'a UnidentifiedAtom); - fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> { + fn next(&mut self) -> Option<(TimeStamp, &'a UnidentifiedAtom)> { // SAFETY: The validity of the space's contents is guaranteed by this type. let (raw_stamp, space) = unsafe { self.space.split_for_value_as_unchecked::() }?; @@ -253,10 +253,10 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { /// If your cannot identify the type of the atom but have to write it, you can simply forward it. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> { - let data = atom.space.as_bytes(); + pub fn forward(&mut self, stamp: TimeStamp, atom: &UnidentifiedAtom) -> Option<()> { self.write_time_stamp(stamp)?; - space::write_bytes(&mut self.frame, data)?; + + space::forward_atom(&mut self.frame, atom)?; Some(()) } @@ -341,9 +341,7 @@ mod tests { // reading { - let body = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) } - .body() - .unwrap(); + let body = unsafe { UnidentifiedAtom::from_space_unchecked(&raw_space) }.body(); let mut reader = unsafe { Sequence::read(body, urids.units.beat) }.unwrap(); assert_eq!(reader.unit(), TimeStampUnit::Frames); diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index b517d1da..6e080dde 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -1,5 +1,5 @@ use crate::space::{AtomSpaceWriter, Space}; -use crate::Atom; +use crate::{Atom, UnidentifiedAtom}; use urid::URID; use core::mem::size_of_val; @@ -98,6 +98,17 @@ pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>( A::init(space, write_parameter) } +#[inline] +pub fn forward_atom<'handle, 'space: 'handle>( + space: &'handle mut impl SpaceAllocator<'space>, + atom: &UnidentifiedAtom, +) -> Option<&'handle mut UnidentifiedAtom> { + let resulting_space = allocate(space, atom.atom_space().len())?; + resulting_space.as_bytes_mut().copy_from_slice(atom.atom_space().as_bytes()); + // SAFETY: We just wrote those bytes, we know for sure they're the same and aligned + unsafe { UnidentifiedAtom::from_space_mut(resulting_space) } +} + #[inline] pub fn write_bytes<'handle, 'space: 'handle>( space: &'handle mut impl SpaceAllocator<'space>, diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 90af4b1f..d9fcd817 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -56,7 +56,9 @@ impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handl let space = Space::::try_from_bytes_mut(previous.get_mut(self.atom_header_index..)?)?; let header = unsafe { space.assume_init_value_mut() }?; - header.as_raw_mut().size += size as u32; + + // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated + unsafe { header.set_size_of_body(header.size_of_body() + size) }; Some((previous, current)) } @@ -64,9 +66,11 @@ impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handl #[inline] unsafe fn rewind(&mut self, byte_count: usize) -> bool { let rewound = self.parent.rewind(byte_count); + let header = self.atom_header_mut(); if rewound { - self.atom_header_mut().as_raw_mut().size -= byte_count as u32; + // SAFETY: Reducing the size of the atom is fine + header.set_size_of_body(header.size_of_body() - byte_count); } rewound diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 954165f7..d4c1bdea 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -62,7 +62,7 @@ impl Space { // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] - unsafe fn from_bytes_unchecked(data: &[u8]) -> &Space { + pub unsafe fn from_bytes_unchecked(data: &[u8]) -> &Space { // SAFETY: It is safe to transmute, since our type has repr(transparent) with [u8]. // SAFETY: The caller is responsible to check for slice alignment. &*(data as *const _ as *const Self) @@ -79,7 +79,7 @@ impl Space { // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] - unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut Space { + pub unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut Space { // SAFETY: It is safe to transmute, since our type has repr(transparent) with [u8]. // SAFETY: The caller is responsible to check for slice alignment. &mut *(data as *mut _ as *mut Self) @@ -267,6 +267,12 @@ impl Space { Some(&*(self.as_uninit()?.as_ptr())) } + #[inline] + pub(crate) unsafe fn assume_init_value_unchecked(&self) -> &T { + // SAFETY: The caller has to ensure this slice actually points to initialized memory. + &*(self.as_uninit_unchecked().as_ptr()) + } + #[inline] pub(crate) unsafe fn assume_init_value_mut(&mut self) -> Option<&mut T> { // SAFETY: The caller has to ensure this slice actually points to initialized memory. @@ -391,24 +397,19 @@ impl AtomSpace { } #[inline] - pub unsafe fn to_atom_unchecked(&self) -> UnidentifiedAtom { - UnidentifiedAtom::new_unchecked(self) - } - - #[inline] - pub unsafe fn to_atom(&self) -> Option { + pub unsafe fn to_atom(&self) -> Option<&UnidentifiedAtom> { let header = self.assume_init_value()?; // Try to read to ensure there is enough room // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents - Some(UnidentifiedAtom::new_unchecked( + Some(UnidentifiedAtom::from_space_unchecked( self.slice(header.size_of_atom())?, )) } #[inline] - pub unsafe fn split_atom(&self) -> Option<(UnidentifiedAtom, &Self)> { + pub unsafe fn split_atom(&self) -> Option<(&UnidentifiedAtom, &Self)> { let header = self.assume_init_value()?; let (atom, rest) = self.split_at(header.size_of_atom())?; - let atom = UnidentifiedAtom::new_unchecked(atom); + let atom = UnidentifiedAtom::from_space_unchecked(atom); Some((atom, rest)) } @@ -474,7 +475,7 @@ mod tests { }; let (atom, _) = space.split_atom().unwrap(); - let body = atom.body().unwrap().as_bytes(); + let body = atom.body().as_bytes(); assert_eq!(size_of::(), size_of_val(body)); assert_eq!(42, *(body.as_ptr() as *const i32)); diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index 7a92544c..eed91578 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -69,9 +69,9 @@ pub struct TupleIterator<'a> { } impl<'a> Iterator for TupleIterator<'a> { - type Item = UnidentifiedAtom<'a>; + type Item = &'a UnidentifiedAtom; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option<&'a UnidentifiedAtom> { // SAFETY: The validity of the space is guaranteed by this type. let (atom, space) = unsafe { self.space.split_atom() }?; self.space = space; @@ -124,7 +124,7 @@ mod tests { // verifying { let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); - let header = atom.header().unwrap(); + let header = atom.header(); assert_eq!(header.urid(), urids.tuple); assert_eq!( header.size_of_body(), @@ -160,7 +160,7 @@ mod tests { // reading { let (body, _) = unsafe { raw_space.split_atom_body(urids.tuple) }.unwrap(); - let items: Vec = unsafe { Tuple::read(body, ()) }.unwrap().collect(); + let items: Vec<&UnidentifiedAtom> = unsafe { Tuple::read(body, ()) }.unwrap().collect(); assert_eq!(items[0].read(urids.vector, urids.int).unwrap(), [17; 9]); assert_eq!(items[1].read(urids.int, ()).unwrap(), 42); } diff --git a/docs/metro/src/pipes.rs b/docs/metro/src/pipes.rs index f9597b40..83db00fc 100644 --- a/docs/metro/src/pipes.rs +++ b/docs/metro/src/pipes.rs @@ -312,10 +312,10 @@ impl<'a> EventReader<'a> { } impl<'a> Pipe for EventReader<'a> { - type InputItem = Option>; + type InputItem = Option<&'a UnidentifiedAtom>; type OutputItem = PulseInput; - fn next(&mut self, atom: Option) -> PulseInput { + fn next(&mut self, atom: Option<&'a UnidentifiedAtom>) -> PulseInput { let mut updates = PulseInput { beat_update: None, bpm_update: None, diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 2196fff9..33ca2fb1 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -124,22 +124,18 @@ mod tests { // verifying { - let (header, space) = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) } - .header_and_body() - .unwrap(); - assert_eq!(header.urid(), urid); - assert_eq!(header.size_of_body(), 3); + let atom = unsafe { UnidentifiedAtom::from_space(&raw_space) }.unwrap(); + assert_eq!(atom.header().urid(), urid); + assert_eq!(atom.header().size_of_body(), 3); - let message = space.slice(3).unwrap().as_bytes(); + let message = atom.body().slice(3).unwrap().as_bytes(); let message = MidiMessage::try_from(message).unwrap(); assert_eq!(message, reference_message); } // reading { - let space = unsafe { UnidentifiedAtom::new_unchecked(&raw_space) } - .body() - .unwrap(); + let space = unsafe { UnidentifiedAtom::from_space(&raw_space) }.unwrap().body(); let message = unsafe { WMidiEvent::read(space, ()) }.unwrap(); assert_eq!(message, reference_message); @@ -162,13 +158,11 @@ mod tests { // verifying { - let (header, body) = unsafe { raw_space.to_atom() } - .unwrap() - .header_and_body() - .unwrap(); - assert_eq!(header.urid(), urid); - assert_eq!(header.size_of_body(), 6); - assert_eq!(&body.as_bytes()[..6], &[0xf0, 1, 2, 3, 4, 0xf7]); + let atom = unsafe { raw_space.to_atom() }.unwrap(); + + assert_eq!(atom.header().urid(), urid); + assert_eq!(atom.header().size_of_body(), 6); + assert_eq!(&atom.body().as_bytes()[..6], &[0xf0, 1, 2, 3, 4, 0xf7]); } // reading diff --git a/state/src/raw.rs b/state/src/raw.rs index 0cbefbe6..80644115 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -55,14 +55,12 @@ impl<'a> StoreHandle<'a> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space: Vec = space.to_vec(); let space = AtomSpace::try_from_bytes(&space).ok_or(StateErr::BadData)?; - let (header, data) = unsafe { space.to_atom() } - .and_then(|a| a.header_and_body()) - .ok_or(StateErr::BadData)?; + let atom = unsafe { space.to_atom() }.ok_or(StateErr::BadData)?; let key = key.get(); - let data_ptr = data as *const _ as *const c_void; - let data_size = header.size_of_body(); - let data_type = header.urid(); + let data_ptr = atom.body().as_ptr() as *const c_void; + let data_size = atom.header().size_of_body(); + let data_type = atom.header().urid(); let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD | sys::LV2_State_Flags::LV2_STATE_IS_PORTABLE) .into(); From be0ef554ce94bae2c98278416ebec67b73e4b6e0 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 18 Aug 2021 19:45:41 +0200 Subject: [PATCH 12/54] Wip fix --- atom/src/space/atom_writer.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index d9fcd817..6d292d9b 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -101,19 +101,15 @@ impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handl mod tests { use crate::prelude::AtomSpaceWriter; use crate::space::cursor::SpaceCursor; + use crate::space::AtomSpace; use core::mem::size_of; use urid::URID; #[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 { - core::slice::from_raw_parts_mut( - (&mut memory).as_mut_ptr() as *mut u8, - MEMORY_SIZE * size_of::(), - ) - }; + let mut space = AtomSpace::boxed(256); + let raw_space = space.as_bytes_mut(); // writing { @@ -128,12 +124,11 @@ mod tests { let (atom, space) = raw_space.split_at(size_of::()); let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; assert_eq!(atom.type_, 1); - assert_eq!(atom.size as usize, 12); + assert_eq!(atom.size as usize, 8); let (value, space) = space.split_at(size_of::()); 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::()); let value = unsafe { *(value.as_ptr() as *const u32) }; From 155b4e442549fd2f3c110b83b6eebf4255391569 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 19 Aug 2021 10:24:20 +0200 Subject: [PATCH 13/54] Some fixes --- atom/src/sequence.rs | 17 ++++++++++++----- atom/src/space/allocatable.rs | 8 -------- atom/src/space/atom_writer.rs | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index 6ca1d187..f6e66d41 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -71,6 +71,14 @@ use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; use urid::*; +#[repr(C, align(8))] +#[derive(Copy, Clone)] +struct SequenceBody(sys::LV2_Atom_Sequence_Body); + +#[repr(C, align(8))] +#[derive(Copy, Clone)] +struct TimestampBody(RawTimeStamp); + /// An atom containing a sequence of time-stamped events. /// /// [See also the module documentation.](index.html) @@ -100,13 +108,13 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { mut frame: AtomSpaceWriter<'handle, 'space>, unit: TimeStampURID, ) -> Option> { - let header = sys::LV2_Atom_Sequence_Body { + let header = SequenceBody(sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), TimeStampURID::Frames(urid) => urid.get(), }, pad: 0, - }; + }); space::write_value(&mut frame, header)?; Some(SequenceWriter { @@ -230,7 +238,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { } }; self.last_stamp = Some(stamp); - space::write_value(&mut self.frame, raw_stamp)?; + space::write_value(&mut self.frame, TimestampBody(raw_stamp))?; Some(()) } @@ -311,8 +319,7 @@ mod tests { sequence.atom.size as usize, size_of::() + size_of::() - + size_of::() - + 4 + + size_of::() // Int struct Includes padding + size_of::() + size_of::() ); diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 6e080dde..046094b7 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -180,14 +180,6 @@ mod tests { init_atom(&mut cursor, INT_URID, 69).unwrap(); assert_eq!(12, cursor.remaining_bytes().len()); } - // let new_value = write_value(&mut cursor, 42u8).unwrap(); - /*{ - // Remaining once aligned: 24, with 8 bytes for atom header: 16 - let writer = AtomSpaceWriter::write_new(&mut cursor, INT_URID).unwrap(); - let int_atom = Int::init(writer, 69).unwrap(); - assert_eq!(69, *int_atom); - assert_eq!(12, cursor.len()); - }*/ assert_eq!( space.as_bytes(), diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 6d292d9b..9aa81483 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -39,8 +39,9 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { ) -> Option { let atom = AtomHeader::new(urid); - let atom_header_index = parent.allocated_bytes().len(); crate::space::write_value(parent, atom)?; + let atom_header_index = parent.allocated_bytes().len() - ::core::mem::size_of::(); + Some(Self { atom_header_index, parent, @@ -107,7 +108,6 @@ mod tests { #[test] fn test_padding_inside_frame() { - const MEMORY_SIZE: usize = 256; let mut space = AtomSpace::boxed(256); let raw_space = space.as_bytes_mut(); From 69ed4e105503ab12ac32dbddf0d37fd26b88bb1d Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 4 Sep 2021 22:39:47 +0200 Subject: [PATCH 14/54] Fix all tests --- atom/src/lib.rs | 3 +- atom/src/scalar.rs | 1 + atom/src/sequence.rs | 14 +--- atom/src/space.rs | 2 - atom/src/space/allocatable.rs | 2 - atom/src/space/list.rs | 131 --------------------------------- atom/src/space/space.rs | 29 +++----- atom/src/space/vec.rs | 5 ++ atom/src/string.rs | 2 +- atom/src/tuple.rs | 19 ++--- atom/tests/atom_integration.rs | 2 +- midi/src/wmidi_binding.rs | 2 +- state/src/raw.rs | 30 ++++---- 13 files changed, 47 insertions(+), 195 deletions(-) delete mode 100644 atom/src/space/list.rs diff --git a/atom/src/lib.rs b/atom/src/lib.rs index ca9c7733..64730d8e 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -77,6 +77,8 @@ mod header; #[cfg(feature = "lv2-core")] pub mod port; +pub use header::AtomHeader; + /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { use crate::*; @@ -93,7 +95,6 @@ pub mod prelude { pub use vector::Vector; } -use crate::header::AtomHeader; use space::*; use urid::*; diff --git a/atom/src/scalar.rs b/atom/src/scalar.rs index b4923555..c9280b11 100644 --- a/atom/src/scalar.rs +++ b/atom/src/scalar.rs @@ -59,6 +59,7 @@ pub trait ScalarAtom: UriBound { mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType, ) -> Option<()> { + // TODO: decide if just the value has to be written, or if the whole atom type has to be written space::write_value(&mut frame, value)?; Some(()) } diff --git a/atom/src/sequence.rs b/atom/src/sequence.rs index f6e66d41..34035b73 100644 --- a/atom/src/sequence.rs +++ b/atom/src/sequence.rs @@ -49,7 +49,7 @@ //! // The sequence writer, however, assures that the written time stamps are monotonic. //! for event in input_sequence { //! // An event contains a timestamp and an atom. -//! let (timestamp, atom): (TimeStamp, UnidentifiedAtom) = event; +//! let (timestamp, atom): (TimeStamp, &UnidentifiedAtom) = event; //! // If the read atom is a 32-bit integer... //! if let Some(integer) = atom.read(urids.atom.int, ()) { //! // Multiply it by two and write it to the sequence. @@ -133,7 +133,7 @@ pub enum TimeStampUnit { } /// An event time stamp. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum TimeStamp { Frames(i64), BeatsPerMinute(f64), @@ -354,17 +354,11 @@ mod tests { assert_eq!(reader.unit(), TimeStampUnit::Frames); let (stamp, atom) = reader.next().unwrap(); - match stamp { - TimeStamp::Frames(frames) => assert_eq!(frames, 0), - _ => panic!("Invalid time stamp!"), - } + assert_eq!(stamp, TimeStamp::Frames(0)); assert_eq!(atom.read::(urids.atom.int, ()).unwrap(), 42); let (stamp, atom) = reader.next().unwrap(); - match stamp { - TimeStamp::Frames(frames) => assert_eq!(frames, 1), - _ => panic!("Invalid time stamp!"), - } + assert_eq!(stamp, TimeStamp::Frames(1)); assert_eq!(atom.read::(urids.atom.long, ()).unwrap(), 17); assert!(reader.next().is_none()); diff --git a/atom/src/space.rs b/atom/src/space.rs index 2468b007..36667f76 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -4,13 +4,11 @@ mod allocatable; mod atom_writer; mod boxed; mod cursor; -mod list; mod space; mod vec; pub use allocatable::*; pub use atom_writer::AtomSpaceWriter; pub use cursor::SpaceCursor; -pub use list::{SpaceHead, SpaceList}; pub use space::{AtomSpace, Space}; pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 046094b7..f8ebd847 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -165,8 +165,6 @@ mod tests { #[test] fn test_init_atom_lifetimes() { - assert_eq!(AtomSpace::alignment(), 8); - let mut space = AtomSpace::boxed(32); assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed diff --git a/atom/src/space/list.rs b/atom/src/space/list.rs deleted file mode 100644 index 21bc2311..00000000 --- a/atom/src/space/list.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::space::SpaceAllocator; - -/// Linked list element for dynamic atom writing. -/// -/// This struct works in conjunction with [`SpaceHead`](struct.SpaceHead.html) to provide a way to write atoms to dynamically allocated memory. -pub struct SpaceList { - next: Option<(Box, Box<[u8]>)>, -} - -impl Default for SpaceList { - fn default() -> Self { - Self { next: None } - } -} - -impl SpaceList { - /// Append an element to the list. - /// - /// If this is the last element of the list, allocate a slice of the required length and append a new element to the list. If not, do nothing and return `None`. - 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, Box<[u8]>)| { - (new_element.as_mut(), new_data.as_mut()) - }) - } - - /// Create a vector containing the data from all elements following this one. - pub fn to_vec(&self) -> Vec { - self.iter() - .map(|slice| slice.iter()) - .flatten() - .copied() - .collect() - } - - /// Return an iterator over the chunks of all elements following this one. - pub fn iter(&self) -> impl Iterator { - std::iter::successors(self.next.as_ref(), |element| element.0.next.as_ref()) - .map(|(_, data)| data.as_ref()) - } -} - -/// A mutable space that dynamically allocates memory. -/// -/// This space uses a linked list of [`SpaceElement`s](struct.SpaceElement.html) to allocate memory. Every time `allocate` is called, a new element is appended to the list and a new byte slice is created. -/// -/// In order to use this space and retrieve the written data once it was written, you create a `SpaceElement` and create a new head with it. Then, you use the head like any other `MutSpace` and when you're done, you retrieve the written data by either calling [`to_vec`](struct.SpaceElement.html#method.to_vec) or [`iter`](struct.SpaceElement.html#iter). -/// -/// # Usage example -/// -/// ``` -/// # use lv2_core::prelude::*; -/// # use lv2_atom::prelude::*; -/// # use lv2_atom::space::*; -/// # use urid::*; -/// # use std::pin::Pin; -/// # let map = HashURIDMapper::new(); -/// // URID cache creation is omitted. -/// let urids: AtomURIDCollection = map.populate_collection().unwrap(); -/// -/// // Creating the first element in the list and the writing head. -/// let mut element = SpaceList::default(); -/// let mut head = SpaceHead::new(&mut element); -/// -/// // Writing an integer. -/// lv2_atom::space::init_atom(&mut head, urids.int, 42).unwrap(); -/// -/// // Retrieving a continuos vector with the written data and verifying its contents. -/// let written_data: Vec = element.to_vec(); -/// let atom = unsafe { AtomSpace::try_from_bytes(&written_data).unwrap().to_atom().unwrap() }; -/// assert_eq!(42, atom.read(urids.int, ()).unwrap()); -/// ``` -pub struct SpaceHead<'a> { - element: Option<&'a mut SpaceList>, - allocated_space: usize, -} - -impl<'a> SpaceHead<'a> { - /// Create a new head that references the given element. - pub fn new(element: &'a mut SpaceList) -> Self { - Self { - element: Some(element), - allocated_space: 0, - } - } -} - -impl<'a> SpaceAllocator<'a> for SpaceHead<'a> { - #[inline] - fn 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) - } - - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { - todo!() - } - - fn allocated_bytes(&self) -> &[u8] { - todo!() - } - - fn allocated_bytes_mut(&mut self) -> &mut [u8] { - todo!() - } - - #[inline] - fn remaining_bytes(&self) -> &[u8] { - &[] - } - - #[inline] - fn remaining_bytes_mut(&mut self) -> &mut [u8] { - &mut [] - } - - unsafe fn rewind(&mut self, byte_count: usize) -> bool { - todo!() - } -} diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index d4c1bdea..d9d73824 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -39,11 +39,6 @@ impl Space { } } - #[inline] - pub fn alignment() -> usize { - align_of::() - } - pub fn boxed(size: usize) -> impl Deref + DerefMut where T: Copy, @@ -56,7 +51,7 @@ impl Space { /// # Safety /// /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. - /// Otherwise, atom reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. + /// Otherwise, reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. /// /// For a safe, checked version, see [`Space::try_from_bytes`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that @@ -73,7 +68,7 @@ impl Space { /// # Safety /// /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. - /// Otherwise, atom reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. + /// Otherwise, reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. /// /// For a safe, checked version, see [`Space::try_from_bytes_mut`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that @@ -177,13 +172,6 @@ impl Space { Some((start, end)) } - #[inline] - pub fn slice(&self, length: usize) -> Option<&Self> { - // SAFETY: The data is part of the original slice which was aligned already. - let d = self.data.get(..length); - Some(unsafe { Self::from_bytes_unchecked(d?) }) - } - /// Try to retrieve space. /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. @@ -359,6 +347,13 @@ impl Space { ) } } + + pub fn write(&mut self, value: T) -> Option<&mut T> { + let uninit = self.as_uninit_mut()?; + *uninit = MaybeUninit::new(value); + // SAFETY: We just initialized this value. + Some(unsafe { &mut *(uninit.as_mut_ptr()) }) + } } impl AtomSpace { @@ -398,11 +393,7 @@ impl AtomSpace { #[inline] pub unsafe fn to_atom(&self) -> Option<&UnidentifiedAtom> { - let header = self.assume_init_value()?; // Try to read to ensure there is enough room - // SAFETY: we just read and sliced to ensure this space is big enough for an atom header and its contents - Some(UnidentifiedAtom::from_space_unchecked( - self.slice(header.size_of_atom())?, - )) + UnidentifiedAtom::from_space(self) } #[inline] diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 328e1f6a..c39e005c 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -9,6 +9,11 @@ pub struct VecSpace { } impl VecSpace { + #[inline] + pub fn new() -> Self { + Self { inner: Vec::new() } + } + #[inline] pub fn new_with_capacity(capacity: usize) -> Self { Self { diff --git a/atom/src/string.rs b/atom/src/string.rs index 5e87f530..0def4dc7 100644 --- a/atom/src/string.rs +++ b/atom/src/string.rs @@ -148,7 +148,7 @@ impl<'handle, 'space> StringWriter<'handle, 'space> { // Manually write the bytes to make extra room for the nul byte let bytes = string.as_bytes(); let space = self.frame.allocate(bytes.len() + 1)?; - space.copy_from_slice(bytes); + space[..bytes.len()].copy_from_slice(bytes); // SAFETY: space is guaranteed to be at least 1 byte large space[bytes.len()] = 0; diff --git a/atom/src/tuple.rs b/atom/src/tuple.rs index eed91578..f5dce86b 100644 --- a/atom/src/tuple.rs +++ b/atom/src/tuple.rs @@ -65,7 +65,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { /// /// The item of this iterator is simply the space a single atom occupies. pub struct TupleIterator<'a> { - space: &'a Space, + space: &'a AtomSpace, } impl<'a> Iterator for TupleIterator<'a> { @@ -123,7 +123,7 @@ mod tests { // verifying { - let (atom, space) = unsafe { raw_space.split_atom() }.unwrap(); + let atom= unsafe { raw_space.to_atom() }.unwrap(); let header = atom.header(); assert_eq!(header.urid(), urids.tuple); assert_eq!( @@ -131,11 +131,11 @@ mod tests { size_of::() + size_of::() * 9 + 4 - + size_of::() + + size_of::() + size_of::() ); - let (vector, space) = - unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let (vector, remaining) = + unsafe { atom.body().split_for_value_as_unchecked::() }.unwrap(); assert_eq!(vector.atom.type_, urids.vector); assert_eq!( vector.atom.size as usize, @@ -144,14 +144,11 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int); - let (vector_items, space) = space.split_at(size_of::() * 9).unwrap(); - let vector_items = unsafe { - std::slice::from_raw_parts(vector_items.as_bytes().as_ptr() as *const i32, 9) - }; + let (vector_items, space) = remaining.split_at(size_of::() * 9).unwrap(); + let vector_items = unsafe { vector_items.aligned::().unwrap().assume_init_slice() }; assert_eq!(vector_items, &[17; 9]); - let (int, _) = - unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let int: &sys::LV2_Atom_Int = unsafe { space.aligned().unwrap().assume_init_value_unchecked() }; assert_eq!(int.atom.type_, urids.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index b6f2ebcf..b90565a0 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -124,7 +124,7 @@ fn main() { // preparing the output atom. let mut output_atom_space = AtomSpace::boxed(256); { - let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); + let mut space = SpaceCursor::new(output_atom_space.as_bytes_mut()); lv2_atom::space::init_atom(&mut space, urids.atom.chunk, ()) .unwrap() .allocate(256 - size_of::()) diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 33ca2fb1..bc5c739e 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -128,7 +128,7 @@ mod tests { assert_eq!(atom.header().urid(), urid); assert_eq!(atom.header().size_of_body(), 3); - let message = atom.body().slice(3).unwrap().as_bytes(); + let message = &atom.body().as_bytes()[..3]; let message = MidiMessage::try_from(message).unwrap(); assert_eq!(message, reference_message); } diff --git a/state/src/raw.rs b/state/src/raw.rs index 80644115..36785ead 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -1,6 +1,7 @@ use crate::StateErr; use atom::prelude::*; use atom::space::*; +use atom::AtomHeader; use std::collections::HashMap; use std::ffi::c_void; use std::marker::PhantomData; @@ -12,7 +13,7 @@ use urid::*; /// /// The written properties a buffered and flushed when requested. Create new properties by calling [`draft`](#method.draft) and write them like any other atom. Once you are done, you can commit your properties by calling [`commit_all`](#method.commit_all) or [`commit`](#method.commit). You have to commit manually: Uncommitted properties will be discarded when the handle is dropped. pub struct StoreHandle<'a> { - properties: HashMap, + properties: HashMap>, store_fn: sys::LV2_State_Store_Function, handle: sys::LV2_State_Handle, lifetime: PhantomData<&'a mut c_void>, @@ -36,25 +37,22 @@ impl<'a> StoreHandle<'a> { /// If you began to write a property and don't want the written things to be stored, you can discard it with [`discard`](#method.discard) or [`discard_all`](#method.discard_all). pub fn draft(&mut self, property_key: URID) -> StatePropertyWriter { let property_key = property_key.into_general(); - self.properties - .insert(property_key.into_general(), SpaceList::default()); - StatePropertyWriter::new(SpaceHead::new( - self.properties - .get_mut(&property_key.into_general()) - .unwrap(), - )) + let space = self.properties + .entry(property_key.into_general()) + .or_insert_with(VecSpace::new); + + StatePropertyWriter::new(space.cursor()) } /// Internal helper function to store a property. - pub fn commit_pair( + fn commit_pair( store_fn: sys::LV2_State_Store_Function, handle: sys::LV2_State_Handle, key: URID, - space: SpaceList, + space: VecSpace, ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; - let space: Vec = space.to_vec(); - let space = AtomSpace::try_from_bytes(&space).ok_or(StateErr::BadData)?; + let space = space.as_space(); let atom = unsafe { space.to_atom() }.ok_or(StateErr::BadData)?; let key = key.get(); @@ -101,15 +99,15 @@ impl<'a> StoreHandle<'a> { /// Writing handle for properties. pub struct StatePropertyWriter<'a> { - head: SpaceHead<'a>, + cursor: VecSpaceCursor<'a, AtomHeader>, initialized: bool, } impl<'a> StatePropertyWriter<'a> { /// Create a new property writer that uses the given space head. - pub fn new(head: SpaceHead<'a>) -> Self { + pub fn new(cursor: VecSpaceCursor<'a, AtomHeader>) -> Self { Self { - head, + cursor, initialized: false, } } @@ -124,7 +122,7 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result { if !self.initialized { self.initialized = true; - lv2_atom::space::init_atom(&mut self.head, urid, parameter).ok_or(StateErr::Unknown) + lv2_atom::space::init_atom(&mut self.cursor, urid, parameter).ok_or(StateErr::Unknown) } else { Err(StateErr::Unknown) } From 0187034e247bc84a79851c119d5d887a6793cac9 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 4 Sep 2021 23:26:32 +0200 Subject: [PATCH 15/54] Move atoms to separate atoms subdir --- atom/src/atoms.rs | 36 +++++++++++++++++++ atom/src/{ => atoms}/chunk.rs | 0 atom/src/{ => atoms}/object.rs | 1 - atom/src/{ => atoms}/scalar.rs | 1 - atom/src/{ => atoms}/sequence.rs | 4 +-- atom/src/{ => atoms}/string.rs | 0 atom/src/{ => atoms}/tuple.rs | 4 +-- atom/src/{ => atoms}/vector.rs | 6 ++-- atom/src/lib.rs | 62 ++++++++------------------------ atom/src/space/allocatable.rs | 2 +- atom/src/space/atom_writer.rs | 2 +- atom/src/space/vec.rs | 2 +- 12 files changed, 57 insertions(+), 63 deletions(-) create mode 100644 atom/src/atoms.rs rename atom/src/{ => atoms}/chunk.rs (100%) rename atom/src/{ => atoms}/object.rs (99%) rename atom/src/{ => atoms}/scalar.rs (99%) rename atom/src/{ => atoms}/sequence.rs (99%) rename atom/src/{ => atoms}/string.rs (100%) rename atom/src/{ => atoms}/tuple.rs (98%) rename atom/src/{ => atoms}/vector.rs (97%) diff --git a/atom/src/atoms.rs b/atom/src/atoms.rs new file mode 100644 index 00000000..a2fac992 --- /dev/null +++ b/atom/src/atoms.rs @@ -0,0 +1,36 @@ +pub mod chunk; +pub mod object; +pub mod scalar; +pub mod sequence; +pub mod string; +pub mod tuple; +pub mod vector; + +pub use crate::header::AtomHeader; +use urid::*; + +#[derive(Clone, URIDCollection)] +/// Collection with the URIDs of all `UriBound`s in this crate. +pub struct AtomURIDCollection { + pub blank: URID, + pub double: URID, + pub float: URID, + pub int: URID, + pub long: URID, + pub urid: URID, + pub bool: URID, + vector: URID>, + pub chunk: URID, + pub literal: URID, + pub object: URID, + pub property: URID, + pub string: URID, + pub tuple: URID, + pub sequence: URID, +} + +impl AtomURIDCollection { + pub fn vector(&self) -> URID> { + unsafe { URID::new_unchecked(self.vector.get()) } + } +} \ No newline at end of file diff --git a/atom/src/chunk.rs b/atom/src/atoms/chunk.rs similarity index 100% rename from atom/src/chunk.rs rename to atom/src/atoms/chunk.rs diff --git a/atom/src/object.rs b/atom/src/atoms/object.rs similarity index 99% rename from atom/src/object.rs rename to atom/src/atoms/object.rs index 9c04d96a..a9658494 100644 --- a/atom/src/object.rs +++ b/atom/src/atoms/object.rs @@ -67,7 +67,6 @@ //! //! # Specification //! [http://lv2plug.in/ns/ext/atom/atom.html#Object](http://lv2plug.in/ns/ext/atom/atom.html#Object). -use crate::space::*; use crate::*; use std::convert::TryFrom; use std::iter::Iterator; diff --git a/atom/src/scalar.rs b/atom/src/atoms/scalar.rs similarity index 99% rename from atom/src/scalar.rs rename to atom/src/atoms/scalar.rs index c9280b11..e85e3a4c 100644 --- a/atom/src/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -29,7 +29,6 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Number](http://lv2plug.in/ns/ext/atom/atom.html#Number) -use crate::space::*; use crate::*; use std::marker::Unpin; use urid::UriBound; diff --git a/atom/src/sequence.rs b/atom/src/atoms/sequence.rs similarity index 99% rename from atom/src/sequence.rs rename to atom/src/atoms/sequence.rs index 34035b73..1206b6ac 100644 --- a/atom/src/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -65,11 +65,9 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) -use crate::space::*; use crate::*; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; -use urid::*; #[repr(C, align(8))] #[derive(Copy, Clone)] @@ -273,7 +271,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { #[cfg(test)] mod tests { use crate::prelude::*; - use crate::sequence::*; + use crate::atoms::sequence::*; use std::mem::size_of; #[derive(URIDCollection)] diff --git a/atom/src/string.rs b/atom/src/atoms/string.rs similarity index 100% rename from atom/src/string.rs rename to atom/src/atoms/string.rs diff --git a/atom/src/tuple.rs b/atom/src/atoms/tuple.rs similarity index 98% rename from atom/src/tuple.rs rename to atom/src/atoms/tuple.rs index f5dce86b..31af9d89 100644 --- a/atom/src/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -30,9 +30,7 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Tuple](http://lv2plug.in/ns/ext/atom/atom.html#Tuple) -use crate::space::*; use crate::*; -use urid::*; /// An atom containing a series of other atoms. /// @@ -105,7 +103,7 @@ mod tests { #[test] fn test_tuple() { let map = HashURIDMapper::new(); - let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); + let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space = AtomSpace::boxed(256); diff --git a/atom/src/vector.rs b/atom/src/atoms/vector.rs similarity index 97% rename from atom/src/vector.rs rename to atom/src/atoms/vector.rs index d53bd9fa..b100a1d0 100644 --- a/atom/src/vector.rs +++ b/atom/src/atoms/vector.rs @@ -30,12 +30,10 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Vector](http://lv2plug.in/ns/ext/atom/atom.html#Vector) -use crate::scalar::ScalarAtom; -use crate::space::*; +use crate::atoms::scalar::ScalarAtom; use crate::*; use std::marker::PhantomData; use std::mem::{size_of, MaybeUninit}; -use urid::*; /// An atom containg an array of scalar atom bodies. /// @@ -128,7 +126,7 @@ mod tests { const CHILD_COUNT: usize = 17; let map = HashURIDMapper::new(); - let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); + let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space = AtomSpace::boxed(256); diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 64730d8e..fccfef6a 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -64,64 +64,30 @@ extern crate lv2_sys as sys; extern crate lv2_units as units; -pub mod chunk; -pub mod object; -pub mod scalar; -pub mod sequence; -pub mod space; -pub mod string; -pub mod tuple; -pub mod vector; +pub use header::AtomHeader; +use space::*; +use urid::*; mod header; #[cfg(feature = "lv2-core")] pub mod port; - -pub use header::AtomHeader; +pub mod atoms; +pub mod space; /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { - use crate::*; - - pub use crate::{Atom, AtomURIDCollection, UnidentifiedAtom}; - pub use chunk::Chunk; - pub use object::{Object, ObjectHeader, PropertyHeader}; + pub use atoms::chunk::Chunk; + pub use atoms::object::{Object, ObjectHeader, PropertyHeader}; pub use port::AtomPort; - pub use scalar::{AtomURID, Bool, Double, Float, Int, Long}; - pub use sequence::{Sequence, TimeStamp, TimeStampURID}; + pub use atoms::scalar::{AtomURID, Bool, Double, Float, Int, Long}; + pub use atoms::sequence::{Sequence, TimeStamp, TimeStampURID}; pub use space::{AtomSpace, AtomSpaceWriter, Space, SpaceAllocator}; - pub use string::{Literal, LiteralInfo, String}; - pub use tuple::Tuple; - pub use vector::Vector; -} - -use space::*; -use urid::*; + pub use atoms::string::{Literal, LiteralInfo, String}; + pub use atoms::tuple::Tuple; + pub use atoms::vector::Vector; -#[derive(Clone, URIDCollection)] -/// Collection with the URIDs of all `UriBound`s in this crate. -pub struct AtomURIDCollection { - pub blank: URID, - pub double: URID, - pub float: URID, - pub int: URID, - pub long: URID, - pub urid: URID, - pub bool: URID, - vector: URID>, - pub chunk: URID, - pub literal: URID, - pub object: URID, - pub property: URID, - pub string: URID, - pub tuple: URID, - pub sequence: URID, -} - -impl AtomURIDCollection { - pub fn vector(&self) -> URID> { - unsafe { URID::new_unchecked(self.vector.get()) } - } + use crate::*; + pub use crate::{Atom, atoms::AtomURIDCollection, UnidentifiedAtom}; } /// Atom type. diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index f8ebd847..ebe1412e 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -84,7 +84,7 @@ pub fn allocate_values<'handle, 'space: 'handle, T: 'static>( space: &'handle mut impl SpaceAllocator<'space>, count: usize, ) -> Option<&'handle mut [MaybeUninit]> { - let space = allocate(space, count * ::core::mem::size_of::())?; + let space = allocate(space, count * std::mem::size_of::())?; Some(space.as_uninit_slice_mut()) } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 9aa81483..f4d0f273 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -40,7 +40,7 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { let atom = AtomHeader::new(urid); crate::space::write_value(parent, atom)?; - let atom_header_index = parent.allocated_bytes().len() - ::core::mem::size_of::(); + let atom_header_index = parent.allocated_bytes().len() - std::mem::size_of::(); Some(Self { atom_header_index, diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index c39e005c..4748f1b6 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -46,7 +46,7 @@ impl VecSpace { &mut self, byte_range: Range, ) -> Option<(&mut [u8], &mut [u8])> { - let byte_len = self.inner.len() * ::core::mem::size_of::(); + let byte_len = self.inner.len() * std::mem::size_of::(); let max = byte_range.start.max(byte_range.end); if max > byte_len { From 89d9b97d7c1beb11082b11c89282ca763f428727 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 4 Sep 2021 23:29:58 +0200 Subject: [PATCH 16/54] Fix build --- atom/src/atoms/chunk.rs | 5 ++--- atom/src/atoms/scalar.rs | 2 +- atom/src/atoms/sequence.rs | 2 +- atom/src/atoms/string.rs | 4 ++-- atom/src/atoms/tuple.rs | 2 +- atom/src/atoms/vector.rs | 2 +- atom/src/header.rs | 6 ------ atom/src/space/space.rs | 2 +- 8 files changed, 9 insertions(+), 16 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 1be1d1ab..5b51e116 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -57,17 +57,16 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { #[cfg(test)] mod tests { - use crate::chunk::*; + use crate::atoms::chunk::*; use crate::*; use std::mem::size_of; - use urid::*; #[test] fn test_chunk_and_slice_writer() { const SLICE_LENGTH: usize = 42; let map = HashURIDMapper::new(); - let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); + let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space = AtomSpace::boxed(256); diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index e85e3a4c..e2f0aaed 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -157,7 +157,7 @@ make_scalar_atom!( #[cfg(test)] mod tests { use crate::prelude::*; - use crate::scalar::ScalarAtom; + use crate::atoms::scalar::ScalarAtom; use crate::space::*; use std::convert::TryFrom; use std::mem::size_of; diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 1206b6ac..b7cbe7c7 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -8,7 +8,7 @@ //! use lv2_core::prelude::*; //! use lv2_units::prelude::*; //! use lv2_atom::prelude::*; -//! use lv2_atom::sequence::*; +//! use lv2_atom::atoms::sequence::*; //! use urid::*; //! //! #[derive(PortCollection)] diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 0def4dc7..f049fe8a 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -8,7 +8,7 @@ //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; -//! use lv2_atom::string::StringWriter; +//! use lv2_atom::atoms::string::StringWriter; //! //! #[derive(PortCollection)] //! struct MyPorts { @@ -238,7 +238,7 @@ mod tests { #[test] fn test_string() { let map = HashURIDMapper::new(); - let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); + let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space = AtomSpace::boxed(256); diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 31af9d89..c39bd964 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -6,7 +6,7 @@ //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; -//! use lv2_atom::tuple::{TupleIterator, TupleWriter}; +//! use lv2_atom::atoms::tuple::{TupleIterator, TupleWriter}; //! //! #[derive(PortCollection)] //! struct MyPorts { diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index b100a1d0..c6228088 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -10,7 +10,7 @@ //! ``` //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; -//! use lv2_atom::vector::VectorWriter; +//! use lv2_atom::atoms::vector::VectorWriter; //! //! #[derive(PortCollection)] //! struct MyPorts { diff --git a/atom/src/header.rs b/atom/src/header.rs index bd507645..e21c4d87 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -23,12 +23,6 @@ impl AtomHeader { unsafe { &*(raw as *const lv2_sys::LV2_Atom as *const _) } } - #[inline] - pub(crate) fn from_raw_mut(raw: &mut lv2_sys::LV2_Atom) -> &mut Self { - // SAFETY: AtomHeader is repr(C) and has LV2_Atom as its only field, so transmuting between the two is safe. - unsafe { &mut *(raw as *mut lv2_sys::LV2_Atom as *mut _) } - } - #[inline] pub(crate) unsafe fn set_size_of_body(&mut self, size: usize) { self.inner.size = size as u32; diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index d9d73824..d07f2b91 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -475,7 +475,7 @@ mod tests { fn test_mut_space<'a>(mut space: impl SpaceAllocator<'a>) { let map = HashURIDMapper::new(); - let urids = crate::AtomURIDCollection::from_map(&map).unwrap(); + let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); let mut test_data: Vec = vec![0; 128]; for i in 0..test_data.len() { From 00edd9c011a8922f538ef248a0e2ea66551aef79 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 5 Sep 2021 14:12:32 +0200 Subject: [PATCH 17/54] Move extensions to allocator to own trait --- atom/src/atoms.rs | 2 +- atom/src/atoms/chunk.rs | 6 +- atom/src/atoms/object.rs | 12 +- atom/src/atoms/scalar.rs | 7 +- atom/src/atoms/sequence.rs | 17 +-- atom/src/atoms/string.rs | 9 +- atom/src/atoms/tuple.rs | 22 ++-- atom/src/atoms/vector.rs | 11 +- atom/src/lib.rs | 10 +- atom/src/port.rs | 8 +- atom/src/space/allocatable.rs | 209 +++++++++++++++------------------ atom/src/space/atom_writer.rs | 14 +-- atom/src/space/cursor.rs | 4 +- atom/src/space/space.rs | 15 ++- atom/src/space/vec.rs | 4 +- atom/tests/atom_integration.rs | 5 +- midi/src/wmidi_binding.rs | 16 +-- state/src/raw.rs | 5 +- 18 files changed, 182 insertions(+), 194 deletions(-) diff --git a/atom/src/atoms.rs b/atom/src/atoms.rs index a2fac992..62122d8c 100644 --- a/atom/src/atoms.rs +++ b/atom/src/atoms.rs @@ -33,4 +33,4 @@ impl AtomURIDCollection { pub fn vector(&self) -> URID> { unsafe { URID::new_unchecked(self.vector.get()) } } -} \ No newline at end of file +} diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 5b51e116..5efeedf6 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -17,7 +17,7 @@ //! let in_chunk: &[u8] = ports.input.read(urids.chunk, ()).unwrap(); //! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk, ()).unwrap(); //! -//! lv2_atom::space::write_bytes(&mut out_chunk, in_chunk).unwrap(); +//! out_chunk.write_bytes(in_chunk).unwrap(); //! } //! ``` //! @@ -73,14 +73,14 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space::init_atom(&mut space, urids.chunk, ()).unwrap(); + let mut writer = space.init_atom(urids.chunk, ()).unwrap(); let data = writer.allocate(SLICE_LENGTH).unwrap(); for (i, value) in data.into_iter().enumerate() { *value = i as u8; } - space::write_value(&mut space, 41u8).unwrap(); + space.write_value(41u8).unwrap(); } // verifying diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index a9658494..da8f8cb8 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -115,8 +115,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { header: ObjectHeader, ) -> Option> { { - space::write_value( - &mut frame, + frame.write_value( sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), otype: header.otype.get(), @@ -199,7 +198,7 @@ impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { parameter: A::WriteParameter, ) -> Option { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; - space::init_atom(&mut self.frame, child_urid, parameter) + self.frame.init_atom(child_urid, parameter) } /// Initialize a new property. @@ -214,7 +213,7 @@ impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { parameter: A::WriteParameter, ) -> Option { Property::write_header(&mut self.frame, key, None::>)?; - space::init_atom(&mut self.frame, child_urid, parameter) + self.frame.init_atom(child_urid, parameter) } } @@ -282,7 +281,7 @@ impl Property { context: context.map(URID::get).unwrap_or(0), }; - space::write_value(space, header)?; + space.write_value(header)?; Some(()) } } @@ -361,7 +360,8 @@ mod tests { // Object. let (object, space) = unsafe { - atom.body().split_for_value_as_unchecked::() + atom.body() + .split_for_value_as_unchecked::() } .unwrap(); assert_eq!(object.id, 0); diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index e2f0aaed..69c9069c 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -54,12 +54,13 @@ pub trait ScalarAtom: UriBound { /// Try to write the atom into a space. /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. + #[inline] fn write_scalar<'handle, 'space: 'handle>( mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType, ) -> Option<()> { // TODO: decide if just the value has to be written, or if the whole atom type has to be written - space::write_value(&mut frame, value)?; + frame.write_value(value)?; Some(()) } } @@ -156,8 +157,8 @@ make_scalar_atom!( #[cfg(test)] mod tests { - use crate::prelude::*; use crate::atoms::scalar::ScalarAtom; + use crate::prelude::*; use crate::space::*; use std::convert::TryFrom; use std::mem::size_of; @@ -176,7 +177,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - crate::space::init_atom(&mut space, urid, value).unwrap(); + space.init_atom(urid, value).unwrap(); } // verifying diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index b7cbe7c7..99ca5818 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -113,7 +113,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { }, pad: 0, }); - space::write_value(&mut frame, header)?; + frame.write_value(header)?; Some(SequenceWriter { frame, @@ -236,7 +236,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { } }; self.last_stamp = Some(stamp); - space::write_value(&mut self.frame, TimestampBody(raw_stamp))?; + self.frame.write_value(TimestampBody(raw_stamp))?; Some(()) } @@ -251,7 +251,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { parameter: A::WriteParameter, ) -> Option { self.write_time_stamp(stamp)?; - space::init_atom(&mut self.frame, urid, parameter) + self.frame.init_atom(urid, parameter) } /// Forward an unidentified atom to the sequence. @@ -262,7 +262,7 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { pub fn forward(&mut self, stamp: TimeStamp, atom: &UnidentifiedAtom) -> Option<()> { self.write_time_stamp(stamp)?; - space::forward_atom(&mut self.frame, atom)?; + self.frame.forward_atom(atom)?; Some(()) } @@ -270,8 +270,8 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { #[cfg(test)] mod tests { - use crate::prelude::*; use crate::atoms::sequence::*; + use crate::prelude::*; use std::mem::size_of; #[derive(URIDCollection)] @@ -290,12 +290,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space::init_atom( - &mut space, - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame), - ) - .unwrap(); + let mut writer = space.init_atom(urids.atom.sequence,TimeStampURID::Frames(urids.units.frame)).unwrap(); writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index f049fe8a..4ebd74d2 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -73,8 +73,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { mut frame: AtomSpaceWriter<'handle, 'space>, info: LiteralInfo, ) -> Option> { - crate::space::write_value( - &mut frame, + frame.write_value( match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), @@ -190,8 +189,8 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = crate::space::init_atom( - &mut space, + + let mut writer = space.init_atom( urids.atom.literal, LiteralInfo::Language(urids.german.into_general()), ) @@ -246,7 +245,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = crate::space::init_atom(&mut space, urids.string, ()).unwrap(); + let mut writer = space.init_atom(urids.string, ()).unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index c39bd964..eb91e312 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -89,7 +89,7 @@ impl<'handle, 'space> TupleWriter<'handle, 'space> { child_urid: URID, child_parameter: A::WriteParameter, ) -> Option { - space::init_atom(&mut self.frame, child_urid, child_parameter) + self.frame.init_atom(child_urid, child_parameter) } } @@ -110,7 +110,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = crate::space::init_atom(&mut space, urids.tuple, ()).unwrap(); + let mut writer = space.init_atom(urids.tuple, ()).unwrap(); { let mut vector_writer = writer.init::>(urids.vector, urids.int).unwrap(); @@ -121,7 +121,7 @@ mod tests { // verifying { - let atom= unsafe { raw_space.to_atom() }.unwrap(); + let atom = unsafe { raw_space.to_atom() }.unwrap(); let header = atom.header(); assert_eq!(header.urid(), urids.tuple); assert_eq!( @@ -129,11 +129,15 @@ mod tests { size_of::() + size_of::() * 9 + 4 - + size_of::() + size_of::() + + size_of::() + + size_of::() ); - let (vector, remaining) = - unsafe { atom.body().split_for_value_as_unchecked::() }.unwrap(); + let (vector, remaining) = unsafe { + atom.body() + .split_for_value_as_unchecked::() + } + .unwrap(); assert_eq!(vector.atom.type_, urids.vector); assert_eq!( vector.atom.size as usize, @@ -143,10 +147,12 @@ mod tests { assert_eq!(vector.body.child_type, urids.int); let (vector_items, space) = remaining.split_at(size_of::() * 9).unwrap(); - let vector_items = unsafe { vector_items.aligned::().unwrap().assume_init_slice() }; + let vector_items = + unsafe { vector_items.aligned::().unwrap().assume_init_slice() }; assert_eq!(vector_items, &[17; 9]); - let int: &sys::LV2_Atom_Int = unsafe { space.aligned().unwrap().assume_init_value_unchecked() }; + let int: &sys::LV2_Atom_Int = + unsafe { space.aligned().unwrap().assume_init_value_unchecked() }; assert_eq!(int.atom.type_, urids.int); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index c6228088..0adde576 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -76,7 +76,7 @@ where child_type: child_urid.get(), child_size: size_of::() as u32, }; - space::write_value(&mut frame, body)?; + frame.write_value(body)?; Some(VectorWriter { frame, @@ -97,7 +97,7 @@ impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { /// Push a single value to the vector. #[inline] pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { - space::write_value(&mut self.frame, child) + self.frame.write_value(child) } /// Append a slice of undefined memory to the vector. @@ -105,13 +105,13 @@ impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { /// Using this method, you don't need to have the elements in memory before you can write them. #[inline] pub fn allocate_uninit(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { - space::allocate_values(&mut self.frame, count) + self.frame.allocate_values(count) } /// Append multiple elements to the vector. #[inline] pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { - space::write_values(&mut self.frame, data) + self.frame.write_values(data) } } @@ -133,8 +133,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = - crate::space::init_atom(&mut space, urids.vector(), urids.int).unwrap(); + let mut writer = space.init_atom(urids.vector(), urids.int).unwrap(); writer.append(&[42; CHILD_COUNT - 1]); writer.push(1); } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index fccfef6a..cba1a0ef 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -68,26 +68,26 @@ pub use header::AtomHeader; use space::*; use urid::*; +pub mod atoms; mod header; #[cfg(feature = "lv2-core")] pub mod port; -pub mod atoms; pub mod space; /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { pub use atoms::chunk::Chunk; pub use atoms::object::{Object, ObjectHeader, PropertyHeader}; - pub use port::AtomPort; pub use atoms::scalar::{AtomURID, Bool, Double, Float, Int, Long}; pub use atoms::sequence::{Sequence, TimeStamp, TimeStampURID}; - pub use space::{AtomSpace, AtomSpaceWriter, Space, SpaceAllocator}; pub use atoms::string::{Literal, LiteralInfo, String}; pub use atoms::tuple::Tuple; pub use atoms::vector::Vector; + pub use port::AtomPort; + pub use space::{AtomSpace, AtomSpaceWriter, Space, SpaceAllocator}; use crate::*; - pub use crate::{Atom, atoms::AtomURIDCollection, UnidentifiedAtom}; + pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; } /// Atom type. @@ -149,7 +149,7 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { #[derive(Clone, Copy)] #[repr(C)] pub struct UnidentifiedAtom { - header: AtomHeader + header: AtomHeader, } impl UnidentifiedAtom { diff --git a/atom/src/port.rs b/atom/src/port.rs index 05d44d96..977477b6 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -23,13 +23,13 @@ //! ports.output.init(urids.int, 42).unwrap(); //! } //! ``` +use crate::header::AtomHeader; use crate::space::*; +use crate::UnidentifiedAtom; use lv2_core::port::PortType; use std::ffi::c_void; use std::ptr::NonNull; use urid::URID; -use crate::UnidentifiedAtom; -use crate::header::AtomHeader; /// A handle to read atoms from a port. /// @@ -94,7 +94,7 @@ impl<'a> PortWriter<'a> { let space: &'write mut SpaceCursor<'write> = unsafe { ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) }; - crate::space::init_atom(space, urid, parameter) + space.init_atom(urid, parameter) } else { None } @@ -144,7 +144,7 @@ mod tests { // writing a chunk to indicate the size of the space. { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = crate::space::init_atom(&mut space, urids.chunk, ()).unwrap(); + let mut writer = space.init_atom(urids.chunk, ()).unwrap(); writer.allocate(256 - size_of::()).unwrap(); } diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index ebe1412e..0f0aeac1 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -5,10 +5,18 @@ use urid::URID; use core::mem::size_of_val; use std::mem::MaybeUninit; +// This function is separate to ensure proper lifetimes +unsafe fn assume_init_mut(s: &mut MaybeUninit) -> &mut T { + // SAFETY: the caller must guarantee that `self` is initialized. + &mut *s.as_mut_ptr() +} + /// A smart pointer that writes atom data to an internal slice. /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. -pub trait SpaceAllocator<'a> { +/// +// TODO: Find proper name +pub trait SpaceAllocatorImpl<'space> { fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])>; #[must_use] @@ -19,7 +27,10 @@ pub trait SpaceAllocator<'a> { fn remaining_bytes(&self) -> &[u8]; fn remaining_bytes_mut(&mut self) -> &mut [u8]; +} +// TODO: Find proper name +pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { /// Try to allocate memory on the internal data slice. /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. @@ -27,138 +38,108 @@ pub trait SpaceAllocator<'a> { fn allocate(&mut self, size: usize) -> Option<&mut [u8]> { self.allocate_and_split(size).map(|(_, s)| s) } -} -/* -impl<'a> SpaceAllocator<'a> for &'a mut [u8] { - #[inline] - fn allocate_unaligned(&mut self, size: usize) -> Option<&'a mut [u8]> { - if size > self.len() { - return None - } - let slice: &'a mut [u8] = ::core::mem::replace(self, &mut []); // Lifetime workaround - let (allocated, remaining) = slice.split_at_mut(size); - *self = remaining; - Some(allocated) + #[inline] + fn allocate_aligned<'handle, T: 'static>(&'handle mut self, size: usize) -> Option<&'handle mut Space> + where + 'space: 'handle, + { + let required_padding = Space::::padding_for(self.remaining_bytes()); + let raw = self.allocate(size + required_padding)?; + + Space::try_align_from_bytes_mut(raw) } #[inline] - fn remaining_bytes(&self) -> &[u8] { - self + fn allocate_values<'handle, T: 'static>( + &'handle mut self, + count: usize, + ) -> Option<&'handle mut [MaybeUninit]> + where + 'space: 'handle, + { + let space = self.allocate_aligned(count * std::mem::size_of::())?; + Some(space.as_uninit_slice_mut()) } #[inline] - fn remaining_bytes_mut(&mut self) -> &mut [u8] { - self + fn init_atom<'handle, A: Atom<'handle, 'space>>( + &'handle mut self, + atom_type: URID, + write_parameter: A::WriteParameter, + ) -> Option + where + 'space: 'handle, + { + let space: AtomSpaceWriter<'handle, 'space> = AtomSpaceWriter::write_new(self, atom_type)?; + A::init(space, write_parameter) } -}*/ - -/* -#[inline] -pub fn realign<'a, T: 'static, S: AllocateSpace<'a>>(space: &mut S) -> Option<()> { - let required_padding = Space::::padding_for(space.as_bytes()); - let _ = space.allocate_unaligned(required_padding)?; - Some(()) -}*/ - -// This function is separate to ensure proper lifetimes -unsafe fn assume_init_mut(s: &mut MaybeUninit) -> &mut T { - // SAFETY: the caller must guarantee that `self` is initialized. - // This also means that `self` must be a `value` variant. - &mut *s.as_mut_ptr() -} - -#[inline] -pub fn allocate<'handle, 'space: 'handle, T: 'static>( - space: &'handle mut impl SpaceAllocator<'space>, - size: usize, -) -> Option<&'handle mut Space> { - let required_padding = Space::::padding_for(space.remaining_bytes()); - let raw = space.allocate(size + required_padding)?; - Space::try_align_from_bytes_mut(raw) -} - -#[inline] -pub fn allocate_values<'handle, 'space: 'handle, T: 'static>( - space: &'handle mut impl SpaceAllocator<'space>, - count: usize, -) -> Option<&'handle mut [MaybeUninit]> { - let space = allocate(space, count * std::mem::size_of::())?; - Some(space.as_uninit_slice_mut()) -} + #[inline] + fn forward_atom<'handle>( + &'handle mut self, + atom: &UnidentifiedAtom, + ) -> Option<&'handle mut UnidentifiedAtom> + where + 'space: 'handle, + { + let resulting_space = self.allocate_aligned(atom.atom_space().len())?; + resulting_space + .as_bytes_mut() + .copy_from_slice(atom.atom_space().as_bytes()); + // SAFETY: We just wrote those bytes, we know for sure they're the same and aligned + unsafe { UnidentifiedAtom::from_space_mut(resulting_space) } + } -#[inline] -pub fn init_atom<'handle, 'space: 'handle, A: Atom<'handle, 'space>>( - space: &'handle mut impl SpaceAllocator<'space>, - atom_type: URID, - write_parameter: A::WriteParameter, -) -> Option { - let space: AtomSpaceWriter<'handle, 'space> = AtomSpaceWriter::write_new(space, atom_type)?; - A::init(space, write_parameter) -} + #[inline] + fn write_bytes<'handle>(&'handle mut self, bytes: &[u8]) -> Option<&'handle mut [u8]> + where + 'space: 'handle, + { + let space = self.allocate(bytes.len())?; + space.copy_from_slice(bytes); + Some(space) + } -#[inline] -pub fn forward_atom<'handle, 'space: 'handle>( - space: &'handle mut impl SpaceAllocator<'space>, - atom: &UnidentifiedAtom, -) -> Option<&'handle mut UnidentifiedAtom> { - let resulting_space = allocate(space, atom.atom_space().len())?; - resulting_space.as_bytes_mut().copy_from_slice(atom.atom_space().as_bytes()); - // SAFETY: We just wrote those bytes, we know for sure they're the same and aligned - unsafe { UnidentifiedAtom::from_space_mut(resulting_space) } -} + #[inline] + fn write_value<'handle, T: 'static>(&'handle mut self, value: T) -> Option<&'handle mut T> + where + T: Copy + Sized + 'static, + 'space: 'handle, + { + let space = self.allocate_aligned(size_of_val(&value))?; + // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. + let space = unsafe { space.as_uninit_mut_unchecked() }; + *space = MaybeUninit::new(value); + + // SAFETY: the MaybeUninit has now been properly initialized. + Some(unsafe { assume_init_mut(space) }) + } -#[inline] -pub fn write_bytes<'handle, 'space: 'handle>( - space: &'handle mut impl SpaceAllocator<'space>, - bytes: &[u8], -) -> Option<&'handle mut [u8]> { - let space = space.allocate(bytes.len())?; - space.copy_from_slice(bytes); - Some(space) -} + fn write_values<'handle, T>(&'handle mut self, values: &[T]) -> Option<&'handle mut [T]> + where + T: Copy + Sized + 'static, + //'space: 'handle, + { + let space: &mut Space = self.allocate_aligned(size_of_val(values))?; + let space = space.as_uninit_slice_mut(); -#[inline] -pub fn write_value<'handle, 'space: 'handle, T>( - space: &'handle mut impl SpaceAllocator<'space>, - value: T, -) -> Option<&'handle mut T> -where - T: Copy + Sized + 'static, -{ - let space = allocate(space, size_of_val(&value))?; - // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. - let space = unsafe { space.as_uninit_mut_unchecked() }; - *space = MaybeUninit::new(value); - - // SAFETY: the MaybeUninit has now been properly initialized. - Some(unsafe { assume_init_mut(space) }) -} + for (dst, src) in space.iter_mut().zip(values.iter()) { + *dst = MaybeUninit::new(*src) + } -pub fn write_values<'handle, 'space: 'handle, T>( - space: &'handle mut impl SpaceAllocator<'space>, - values: &[T], -) -> Option<&'handle mut [T]> -where - T: Copy + Sized + 'static, -{ - let space: &mut Space = allocate(space, size_of_val(values))?; - let space = space.as_uninit_slice_mut(); - - for (dst, src) in space.iter_mut().zip(values.iter()) { - *dst = MaybeUninit::new(*src) + // SAFETY: Assume init: we just initialized the memory above + Some(unsafe { &mut *(space as *mut [_] as *mut [T]) }) } - - // SAFETY: Assume init: we just initialized the memory above - Some(unsafe { &mut *(space as *mut [_] as *mut [T]) }) } +impl<'space, H: SpaceAllocatorImpl<'space>> SpaceAllocator<'space> for H { } + #[cfg(test)] mod tests { use crate::prelude::{Int, SpaceAllocator}; use crate::space::cursor::SpaceCursor; - use crate::space::{init_atom, write_value, AtomSpace}; + use crate::space::{AtomSpace, SpaceAllocatorImpl}; use urid::URID; const INT_URID: URID = unsafe { URID::new_unchecked(5) }; @@ -169,13 +150,13 @@ mod tests { assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed let mut cursor = SpaceCursor::new(space.as_bytes_mut()); // The pointer that is going to be moved as we keep writing. - let new_value = write_value(&mut cursor, 42u8).unwrap(); + let new_value = cursor.write_value(42u8).unwrap(); assert_eq!(42, *new_value); assert_eq!(31, cursor.remaining_bytes().len()); { - init_atom(&mut cursor, INT_URID, 69).unwrap(); + cursor.init_atom(INT_URID, 69).unwrap(); assert_eq!(12, cursor.remaining_bytes().len()); } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index f4d0f273..c363cee7 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,11 +1,11 @@ use crate::header::AtomHeader; -use crate::space::{Space, SpaceAllocator}; +use crate::space::{Space, SpaceAllocator, SpaceAllocatorImpl}; use urid::URID; /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'handle, 'space: 'handle> { atom_header_index: usize, - parent: &'handle mut dyn SpaceAllocator<'space>, + parent: &'handle mut (dyn SpaceAllocatorImpl<'space>), } impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { @@ -39,7 +39,7 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { ) -> Option { let atom = AtomHeader::new(urid); - crate::space::write_value(parent, atom)?; + parent.write_value(atom)?; let atom_header_index = parent.allocated_bytes().len() - std::mem::size_of::(); Some(Self { @@ -49,7 +49,7 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { } } -impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handle, 'space> { +impl<'handle, 'space: 'handle> SpaceAllocatorImpl<'space> for AtomSpaceWriter<'handle, 'space> { #[inline] fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (previous, current) = self.parent.allocate_and_split(size)?; @@ -102,7 +102,7 @@ impl<'handle, 'space: 'handle> SpaceAllocator<'space> for AtomSpaceWriter<'handl mod tests { use crate::prelude::AtomSpaceWriter; use crate::space::cursor::SpaceCursor; - use crate::space::AtomSpace; + use crate::space::{AtomSpace, SpaceAllocator}; use core::mem::size_of; use urid::URID; @@ -115,8 +115,8 @@ mod tests { { let mut root = SpaceCursor::new(raw_space); let mut frame = AtomSpaceWriter::write_new(&mut root, URID::new(1).unwrap()).unwrap(); - crate::space::write_value(&mut frame, 42u32).unwrap(); - crate::space::write_value(&mut frame, 17u32).unwrap(); + frame.write_value(42u32).unwrap(); + frame.write_value(17u32).unwrap(); } // checking diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 2adc11e0..fd5135c6 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,4 +1,4 @@ -use crate::space::SpaceAllocator; +use crate::space::SpaceAllocatorImpl; pub struct SpaceCursor<'a> { data: &'a mut [u8], @@ -14,7 +14,7 @@ impl<'a> SpaceCursor<'a> { } } -impl<'a> SpaceAllocator<'a> for SpaceCursor<'a> { +impl<'a> SpaceAllocatorImpl<'a> for SpaceCursor<'a> { #[inline] fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (allocated, allocatable) = self.data.split_at_mut(self.allocated_length); diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index d07f2b91..20ce081f 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -212,6 +212,11 @@ impl Space { Space::::try_align_from_bytes(self.as_bytes()) } + #[inline] + pub fn realign_mut(&mut self) -> Option<&mut Space> { + Space::::try_align_from_bytes_mut(self.as_bytes_mut()) + } + #[inline] pub fn aligned(&self) -> Option<&Space> { Space::::try_from_bytes(self.as_bytes()) @@ -482,11 +487,11 @@ mod tests { test_data[i] = i as u8; } - let written_data = crate::space::write_bytes(&mut space, test_data.as_slice()).unwrap(); + let written_data = space.write_bytes(test_data.as_slice()).unwrap(); assert_eq!(test_data.as_slice(), written_data); let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; - let written_atom = crate::space::write_value(&mut space, test_atom).unwrap(); + let written_atom = space.write_value(test_atom).unwrap(); assert_eq!(written_atom.size, test_atom.size); assert_eq!(written_atom.type_, test_atom.type_); let written_atom_addr = written_atom as *mut _ as *mut _; @@ -508,12 +513,12 @@ mod tests { test_data[i] = i as u8; } - let written_data = crate::space::write_bytes(&mut atom_frame, &test_data).unwrap(); + let written_data = atom_frame.write_bytes(&test_data).unwrap(); assert_eq!(test_data.as_slice(), written_data); assert_eq!(atom_frame.atom_header().size_of_body(), test_data.len()); let test_atom = sys::LV2_Atom { size: 42, type_: 1 }; - let written_atom = crate::space::write_value(&mut atom_frame, test_atom).unwrap(); + let written_atom = atom_frame.write_value(test_atom).unwrap(); assert_eq!(written_atom.size, test_atom.size); assert_eq!(written_atom.type_, test_atom.type_); assert_eq!( @@ -538,7 +543,7 @@ mod tests { { let mut root_space = SpaceCursor::new(&mut raw_space[3..]); - crate::space::write_value(&mut root_space, 42u8).unwrap(); + root_space.write_value(42u8).unwrap(); } assert_eq!(&[0, 0, 0, 42, 0, 0, 0, 0], raw_space.as_ref()); diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 4748f1b6..b32a0e94 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] -use crate::space::{Space, SpaceAllocator}; +use crate::space::{Space, SpaceAllocatorImpl}; use std::mem::MaybeUninit; use std::ops::Range; @@ -78,7 +78,7 @@ pub struct VecSpaceCursor<'vec, T> { allocated_length: usize, } -impl<'vec, T: Copy + 'static> SpaceAllocator<'vec> for VecSpaceCursor<'vec, T> { +impl<'vec, T: Copy + 'static> SpaceAllocatorImpl<'vec> for VecSpaceCursor<'vec, T> { fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let end = self.allocated_length.checked_add(size)?; let result = VecSpace::::get_or_allocate_bytes_mut(self.vec, self.allocated_length..end); diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index b90565a0..28c384a7 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -102,8 +102,7 @@ fn main() { let mut input_atom_space = AtomSpace::boxed(256); { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); - let mut writer = lv2_atom::space::init_atom( - &mut space, + let mut writer = space.init_atom( urids.atom.sequence, TimeStampURID::Frames(urids.units.frame), ) @@ -125,7 +124,7 @@ fn main() { let mut output_atom_space = AtomSpace::boxed(256); { let mut space = SpaceCursor::new(output_atom_space.as_bytes_mut()); - lv2_atom::space::init_atom(&mut space, urids.atom.chunk, ()) + space.init_atom(urids.atom.chunk, ()) .unwrap() .allocate(256 - size_of::()) .unwrap(); diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index bc5c739e..437a3689 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -34,8 +34,8 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for WMidiEvent { mut frame: AtomSpaceWriter<'handle, 'space>, message: wmidi::MidiMessage, ) -> Option<()> { - let space: &mut Space = lv2_atom::space::allocate(&mut frame, message.bytes_size())?; - message.copy_to_slice(space.as_bytes_mut()).ok()?; + let space = frame.allocate(message.bytes_size())?; + message.copy_to_slice(space).ok()?; Some(()) } @@ -81,7 +81,7 @@ pub struct Writer<'handle, 'space> { impl<'handle, 'space> Writer<'handle, 'space> { #[inline] pub fn write_raw(&mut self, data: &[u8]) -> Option<&mut [u8]> { - lv2_atom::space::write_bytes(&mut self.frame, data) + self.frame.write_bytes(data) } #[inline] @@ -89,7 +89,7 @@ impl<'handle, 'space> Writer<'handle, 'space> { where T: Copy + Sized + 'static, { - lv2_atom::space::write_value(&mut self.frame, instance) + self.frame.write_value(instance) } } @@ -119,7 +119,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - lv2_atom::space::init_atom(&mut space, urid, reference_message.clone()).unwrap(); + space.init_atom(urid, reference_message.clone()).unwrap(); } // verifying @@ -135,7 +135,9 @@ mod tests { // reading { - let space = unsafe { UnidentifiedAtom::from_space(&raw_space) }.unwrap().body(); + let space = unsafe { UnidentifiedAtom::from_space(&raw_space) } + .unwrap() + .body(); let message = unsafe { WMidiEvent::read(space, ()) }.unwrap(); assert_eq!(message, reference_message); @@ -152,7 +154,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = lv2_atom::space::init_atom(&mut space, urid, ()).unwrap(); + let mut writer = space.init_atom(urid, ()).unwrap(); writer.write_raw(&[1, 2, 3, 4]); } diff --git a/state/src/raw.rs b/state/src/raw.rs index 36785ead..c6bcdcb9 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -37,7 +37,8 @@ impl<'a> StoreHandle<'a> { /// If you began to write a property and don't want the written things to be stored, you can discard it with [`discard`](#method.discard) or [`discard_all`](#method.discard_all). pub fn draft(&mut self, property_key: URID) -> StatePropertyWriter { let property_key = property_key.into_general(); - let space = self.properties + let space = self + .properties .entry(property_key.into_general()) .or_insert_with(VecSpace::new); @@ -122,7 +123,7 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result { if !self.initialized { self.initialized = true; - lv2_atom::space::init_atom(&mut self.cursor, urid, parameter).ok_or(StateErr::Unknown) + self.cursor.init_atom(urid, parameter).ok_or(StateErr::Unknown) } else { Err(StateErr::Unknown) } From 999df14bcd95baad307e6a60df171091a919fca9 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 5 Sep 2021 14:31:06 +0200 Subject: [PATCH 18/54] Remove "boxed" space helper --- atom/src/atoms/chunk.rs | 3 ++- atom/src/atoms/object.rs | 4 +++- atom/src/atoms/scalar.rs | 4 +++- atom/src/atoms/sequence.rs | 3 ++- atom/src/atoms/string.rs | 7 ++++-- atom/src/atoms/tuple.rs | 4 +++- atom/src/atoms/vector.rs | 4 +++- atom/src/port.rs | 4 +++- atom/src/space.rs | 1 - atom/src/space/allocatable.rs | 7 +++--- atom/src/space/atom_writer.rs | 5 ++-- atom/src/space/boxed.rs | 42 ---------------------------------- atom/src/space/space.rs | 18 +++++---------- atom/src/space/vec.rs | 11 ++++++++- atom/tests/atom_integration.rs | 7 ++++-- midi/src/wmidi_binding.rs | 9 +++++--- 16 files changed, 58 insertions(+), 75 deletions(-) delete mode 100644 atom/src/space/boxed.rs diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 5efeedf6..550afd6e 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -68,7 +68,8 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index da8f8cb8..893f1c90 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -292,6 +292,7 @@ mod tests { use crate::space::*; use std::mem::size_of; use urid::*; + use crate::AtomHeader; #[test] fn test_object() { @@ -312,7 +313,8 @@ mod tests { .unwrap(); let second_value: f32 = 42.0; - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 69c9069c..5c6f6048 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -163,6 +163,7 @@ mod tests { use std::convert::TryFrom; use std::mem::size_of; use urid::*; + use crate::AtomHeader; fn test_scalar(value: A::InternalType) where @@ -172,7 +173,8 @@ mod tests { let map = HashURIDMapper::new(); let urid: URID = map.map_type().unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 99ca5818..8a172140 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -285,7 +285,8 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 4ebd74d2..8cad351a 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -164,6 +164,7 @@ mod tests { use std::ffi::CStr; use std::mem::{size_of, size_of_val}; use urid::*; + use crate::AtomHeader; struct German; unsafe impl UriBound for German { @@ -184,7 +185,8 @@ mod tests { let map = HashURIDMapper::new(); let urids = TestURIDs::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { @@ -239,7 +241,8 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index eb91e312..fc3a4c3c 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -99,13 +99,15 @@ mod tests { use crate::space::*; use std::mem::size_of; use urid::*; + use crate::AtomHeader; #[test] fn test_tuple() { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 0adde576..12db461c 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -120,6 +120,7 @@ mod tests { use crate::space::*; use std::mem::size_of; use urid::*; + use crate::AtomHeader; #[test] fn test_vector() { @@ -128,7 +129,8 @@ mod tests { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { diff --git a/atom/src/port.rs b/atom/src/port.rs index 977477b6..6c8afb0f 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -133,13 +133,15 @@ mod tests { use std::mem::size_of; use std::ptr::NonNull; use urid::*; + use crate::AtomHeader; #[test] fn test_atom_port() { let map = HashURIDMapper::new(); let urids = AtomURIDCollection::from_map(&map).unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing a chunk to indicate the size of the space. { diff --git a/atom/src/space.rs b/atom/src/space.rs index 36667f76..bf656190 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -2,7 +2,6 @@ mod allocatable; mod atom_writer; -mod boxed; mod cursor; mod space; mod vec; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 0f0aeac1..8d1be5cc 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -139,14 +139,15 @@ impl<'space, H: SpaceAllocatorImpl<'space>> SpaceAllocator<'space> for H { } mod tests { use crate::prelude::{Int, SpaceAllocator}; use crate::space::cursor::SpaceCursor; - use crate::space::{AtomSpace, SpaceAllocatorImpl}; + use crate::space::{SpaceAllocatorImpl, VecSpace}; use urid::URID; + use crate::AtomHeader; const INT_URID: URID = unsafe { URID::new_unchecked(5) }; #[test] fn test_init_atom_lifetimes() { - let mut space = AtomSpace::boxed(32); + let mut space = VecSpace::::new_with_capacity(4); assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed let mut cursor = SpaceCursor::new(space.as_bytes_mut()); // The pointer that is going to be moved as we keep writing. @@ -167,6 +168,6 @@ mod tests { 0, 0, 0, 0, 0, ] ); - assert_eq!(32, space.len()); + assert_eq!(32, space.as_bytes().len()); } } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index c363cee7..3fe4a1a3 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -102,13 +102,14 @@ impl<'handle, 'space: 'handle> SpaceAllocatorImpl<'space> for AtomSpaceWriter<'h mod tests { use crate::prelude::AtomSpaceWriter; use crate::space::cursor::SpaceCursor; - use crate::space::{AtomSpace, SpaceAllocator}; + use crate::space::{SpaceAllocator, VecSpace}; use core::mem::size_of; use urid::URID; + use crate::AtomHeader; #[test] fn test_padding_inside_frame() { - let mut space = AtomSpace::boxed(256); + let mut space = VecSpace::::new_with_capacity(64); let raw_space = space.as_bytes_mut(); // writing diff --git a/atom/src/space/boxed.rs b/atom/src/space/boxed.rs deleted file mode 100644 index e661b8c5..00000000 --- a/atom/src/space/boxed.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::prelude::Space; -use std::mem::{size_of, MaybeUninit}; -use std::ops::{Deref, DerefMut}; - -pub(crate) fn byte_index_to_value_index(size: usize) -> usize { - let type_size = size_of::(); - if type_size == 0 { - 0 - } else { - size / type_size + if size % type_size > 0 { 1 } else { 0 } - } -} - -pub(crate) struct BoxedSpace { - pub(crate) inner: Box<[MaybeUninit]>, -} - -impl BoxedSpace { - #[inline] - pub fn new_zeroed(size: usize) -> Self { - Self { - inner: vec![MaybeUninit::zeroed(); byte_index_to_value_index::(size)] - .into_boxed_slice(), - } - } -} - -impl Deref for BoxedSpace { - type Target = Space; - - #[inline] - fn deref(&self) -> &Self::Target { - Space::::from_uninit_slice(&self.inner) - } -} - -impl DerefMut for BoxedSpace { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - Space::::from_uninit_slice_mut(&mut self.inner) - } -} diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 20ce081f..c82c6916 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -3,7 +3,6 @@ use crate::UnidentifiedAtom; use core::mem::{align_of, size_of}; use std::marker::PhantomData; use std::mem::{size_of_val, MaybeUninit}; -use std::ops::{Deref, DerefMut}; use std::slice::{from_raw_parts, from_raw_parts_mut}; use urid::URID; @@ -39,13 +38,6 @@ impl Space { } } - pub fn boxed(size: usize) -> impl Deref + DerefMut - where - T: Copy, - { - crate::space::boxed::BoxedSpace::new_zeroed(size) - } - /// Creates a new space from a slice of bytes, without checking for padding correctness. /// /// # Safety @@ -429,10 +421,11 @@ mod tests { use crate::space::*; use std::mem::{size_of, size_of_val}; use urid::*; + use crate::AtomHeader; #[test] fn test_space() { - let mut space = Space::::boxed(256); + let mut space = VecSpace::::new_with_capacity(64); let bytes = space.as_bytes_mut(); for i in 0..128 { @@ -440,11 +433,11 @@ mod tests { } unsafe { - let ptr = space.as_mut_ptr().add(128) as *mut u32; + let ptr = space.as_space_mut().as_mut_ptr().add(128) as *mut u32; *(ptr) = 0x42424242; } - let (lower_space, space) = space.split_at(128).unwrap(); + let (lower_space, space) = space.as_space_mut().split_at(128).unwrap(); let lower_space = lower_space.as_bytes(); for i in 0..128 { @@ -457,7 +450,8 @@ mod tests { #[test] fn test_split_atom() { - let mut space = AtomSpace::boxed(256); + let mut space = VecSpace::::new_with_capacity(64); + let space = space.as_space_mut(); let urid: URID = unsafe { URID::new_unchecked(17) }; // Writing an integer atom. diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index b32a0e94..d4651805 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -4,6 +4,15 @@ use crate::space::{Space, SpaceAllocatorImpl}; use std::mem::MaybeUninit; use std::ops::Range; +pub(crate) fn byte_index_to_value_index(size: usize) -> usize { + let type_size = ::core::mem::size_of::(); + if type_size == 0 { + 0 + } else { + size / type_size + if size % type_size > 0 { 1 } else { 0 } + } +} + pub struct VecSpace { inner: Vec>, } @@ -50,7 +59,7 @@ impl VecSpace { let max = byte_range.start.max(byte_range.end); if max > byte_len { - let new_size = crate::space::boxed::byte_index_to_value_index::(max); + let new_size = byte_index_to_value_index::(max); self.inner.resize(new_size, MaybeUninit::zeroed()); } diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 28c384a7..b8163785 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -8,6 +8,7 @@ use core::prelude::*; use lv2_urid::*; use units::prelude::*; use urid::*; +use atom::AtomHeader; #[derive(PortCollection)] struct Ports { @@ -99,7 +100,8 @@ fn main() { let urids: URIDs = map.populate_collection().unwrap(); // Preparing the input atom. - let mut input_atom_space = AtomSpace::boxed(256); + let mut input_atom_space = VecSpace::::new_with_capacity(64); + let input_atom_space = input_atom_space.as_space_mut(); { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); let mut writer = space.init_atom( @@ -121,7 +123,8 @@ fn main() { } // preparing the output atom. - let mut output_atom_space = AtomSpace::boxed(256); + let mut output_atom_space = VecSpace::::new_with_capacity(64); + let output_atom_space = output_atom_space.as_space_mut(); { let mut space = SpaceCursor::new(output_atom_space.as_bytes_mut()); space.init_atom(urids.atom.chunk, ()) diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 437a3689..e5fa67f6 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -103,7 +103,8 @@ impl<'handle, 'space> Drop for Writer<'handle, 'space> { #[cfg(test)] mod tests { use crate::wmidi_binding::*; - use lv2_atom::space::SpaceCursor; + use lv2_atom::space::{VecSpace, SpaceCursor}; + use lv2_atom::AtomHeader; use std::convert::TryFrom; use wmidi::*; @@ -112,7 +113,8 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); let reference_message = MidiMessage::NoteOn(Channel::Ch1, Note::A0, Velocity::try_from(125).unwrap()); @@ -149,7 +151,8 @@ mod tests { let map = HashURIDMapper::new(); let urid = map.map_type::().unwrap(); - let mut raw_space = AtomSpace::boxed(256); + let mut raw_space = VecSpace::::new_with_capacity(64); + let raw_space = raw_space.as_space_mut(); // writing { From aa46e6cdd61fe31b2ddaa4050bd21e7a57efae3b Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 6 Sep 2021 19:42:47 +0200 Subject: [PATCH 19/54] Introduce SpaceReader to cleanup clunky methods on Space struct --- atom/src/atoms/chunk.rs | 18 ++--- atom/src/atoms/object.rs | 89 +++++++++++---------- atom/src/atoms/scalar.rs | 24 +++--- atom/src/atoms/sequence.rs | 71 ++++++++++------- atom/src/atoms/string.rs | 79 ++++++++++--------- atom/src/atoms/tuple.rs | 48 +++++------- atom/src/atoms/vector.rs | 18 ++--- atom/src/header.rs | 21 ++++- atom/src/lib.rs | 47 ++++++++--- atom/src/port.rs | 10 +-- atom/src/space.rs | 1 + atom/src/space/allocatable.rs | 29 ++++--- atom/src/space/reader.rs | 104 ++++++++++++++++++++++++ atom/src/space/space.rs | 139 ++++++--------------------------- atom/src/space/vec.rs | 11 +-- atom/src/util.rs | 35 +++++++++ atom/tests/atom_integration.rs | 24 +++--- core/src/feature/mod.rs | 12 +-- midi/src/wmidi_binding.rs | 6 +- state/src/raw.rs | 8 +- 20 files changed, 448 insertions(+), 346 deletions(-) create mode 100644 atom/src/space/reader.rs create mode 100644 atom/src/util.rs diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 550afd6e..a351ca0a 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -59,7 +59,6 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { mod tests { use crate::atoms::chunk::*; use crate::*; - use std::mem::size_of; #[test] fn test_chunk_and_slice_writer() { @@ -86,23 +85,20 @@ mod tests { // verifying { - let (atom, data) = raw_space.split_at(size_of::()).unwrap(); + let atom = unsafe { raw_space.read().next_atom() }.unwrap(); + assert_eq!(atom.header().size_of_body(), SLICE_LENGTH); + assert_eq!(atom.header().urid(), urids.chunk.get()); - let atom = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom) }; - assert_eq!(atom.size as usize, SLICE_LENGTH); - assert_eq!(atom.type_, urids.chunk.get()); - - let data = data.split_at(SLICE_LENGTH).unwrap().0.as_bytes(); - for i in 0..SLICE_LENGTH { - assert_eq!(data[i] as usize, i); + let data = atom.body().as_bytes(); + for (i, value) in data.iter().enumerate() { + assert_eq!(*value as usize, i); } } // reading { let data = - unsafe { Chunk::read(raw_space.split_atom_body(urids.chunk).unwrap().0, ()) } - .unwrap(); + unsafe { Chunk::read(raw_space.read().next_atom().unwrap().body(), ()) }.unwrap(); assert_eq!(data.len(), SLICE_LENGTH); for (i, value) in data.iter().enumerate() { diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 893f1c90..711068df 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -67,6 +67,7 @@ //! //! # Specification //! [http://lv2plug.in/ns/ext/atom/atom.html#Object](http://lv2plug.in/ns/ext/atom/atom.html#Object). +use crate::space::reader::AtomSpaceReader; use crate::*; use std::convert::TryFrom; use std::iter::Iterator; @@ -83,6 +84,7 @@ unsafe impl UriBound for Object { } /// Information about an object atom. +#[derive(Copy, Clone)] pub struct ObjectHeader { /// The id of the object to distinguish different objects of the same type. /// @@ -99,13 +101,15 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { type WriteHandle = ObjectWriter<'handle, 'space>; unsafe fn read(body: &'handle Space, _: ()) -> Option<(ObjectHeader, ObjectReader<'handle>)> { - let (header, body) = body.split_for_value_as_unchecked::()?; + let mut reader = body.read(); + let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; + let header = ObjectHeader { id: URID::try_from(header.id).ok(), otype: URID::try_from(header.otype).ok()?, }; - let reader = ObjectReader { space: body }; + let reader = ObjectReader { reader }; Some((header, reader)) } @@ -115,12 +119,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { header: ObjectHeader, ) -> Option> { { - frame.write_value( - sys::LV2_Atom_Object_Body { - id: header.id.map(URID::get).unwrap_or(0), - otype: header.otype.get(), - }, - )?; + frame.write_value(sys::LV2_Atom_Object_Body { + id: header.id.map(URID::get).unwrap_or(0), + otype: header.otype.get(), + })?; } Some(ObjectWriter { frame }) } @@ -164,7 +166,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Blank { /// /// Each iteration item is the header of the property, as well as the space occupied by the value atom. You can use normal `read` methods on the returned space. pub struct ObjectReader<'a> { - space: &'a Space, + reader: AtomSpaceReader<'a>, } impl<'a> Iterator for ObjectReader<'a> { @@ -172,10 +174,8 @@ impl<'a> Iterator for ObjectReader<'a> { fn next(&mut self) -> Option<(PropertyHeader, &'a UnidentifiedAtom)> { // SAFETY: The fact that this contains a valid property is guaranteed by this type. - let (header, atom, space) = unsafe { Property::read_body(self.space) }?; - self.space = space; - // SAFETY: The fact that this contains a valid atom header is guaranteed by this type. - Some((header, atom)) + self.reader + .try_read(|reader| unsafe { Property::read_body(reader) }) } } @@ -255,16 +255,19 @@ impl Property { /// # Safety /// /// The caller must ensure that the given Space actually contains a valid property. - unsafe fn read_body(space: &Space) -> Option<(PropertyHeader, &UnidentifiedAtom, &Space)> { - let (header, space) = space.split_for_value_as_unchecked::()?; + unsafe fn read_body<'a>( + reader: &mut AtomSpaceReader<'a>, + ) -> Option<(PropertyHeader, &'a UnidentifiedAtom)> { + let header: &StrippedPropertyHeader = reader.next_value()?; let header = PropertyHeader { key: URID::try_from(header.key).ok()?, context: URID::try_from(header.context).ok(), }; - let (atom, space) = space.split_atom()?; - Some((header, atom, space)) + let atom = reader.next_atom()?; + + Some((header, atom)) } /// Write out the header of a property atom. @@ -290,11 +293,12 @@ impl Property { mod tests { use crate::prelude::*; use crate::space::*; + use crate::AtomHeader; use std::mem::size_of; use urid::*; - use crate::AtomHeader; #[test] + #[allow(clippy::float_cmp)] fn test_object() { let map = HashURIDMapper::new(); let urids = AtomURIDCollection::from_map(&map).unwrap(); @@ -348,68 +352,67 @@ mod tests { // verifying { // Header - let (atom, _space) = unsafe { raw_space.split_atom() }.unwrap(); - let header = atom.header(); - assert_eq!(header.urid(), urids.object); + let atom = unsafe { raw_space.read().next_atom() }.unwrap(); + + assert_eq!(atom.header().urid(), urids.object); assert_eq!( - header.size_of_body(), + atom.header().size_of_body(), size_of::() + size_of::() + 2 * size_of::() + size_of::() - + size_of::() + + 2 * size_of::() ); // Object. - let (object, space) = unsafe { - atom.body() - .split_for_value_as_unchecked::() - } - .unwrap(); + let mut object_reader = atom.body().read(); + let object: &sys::LV2_Atom_Object_Body = unsafe { object_reader.next_value() }.unwrap(); assert_eq!(object.id, 0); assert_eq!(object.otype, object_type); // First property. - let (property, space) = - unsafe { space.split_for_value_as_unchecked::() } - .unwrap(); + let property: &sys::LV2_Atom_Property_Body = + unsafe { object_reader.next_value() }.unwrap(); assert_eq!(property.key, first_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.int); - assert_eq!(property.value.size as usize, size_of::()); + assert_eq!(property.value.size as usize, 2 * size_of::()); - let (value, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let value: &i32 = unsafe { object_reader.next_value() }.unwrap(); assert_eq!(*value, first_value); // Second property. - let (property, space) = - unsafe { space.split_for_value_as_unchecked::() } - .unwrap(); + let property: &sys::LV2_Atom_Property_Body = + unsafe { object_reader.next_value() }.unwrap(); assert_eq!(property.key, second_key); assert_eq!(property.context, 0); assert_eq!(property.value.type_, urids.float); - assert_eq!(property.value.size as usize, size_of::()); + assert_eq!(property.value.size as usize, 2 * size_of::()); - let (value, space) = unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let value: &f32 = unsafe { object_reader.next_value() }.unwrap(); assert_eq!(*value, second_value); - assert_eq!(space.len(), 0); + assert!(object_reader.into_remaining().is_empty()); } // reading { - let (body, _) = unsafe { raw_space.split_atom_body(urids.object) }.unwrap(); + let (header, iter) = unsafe { raw_space.read().next_atom() } + .unwrap() + .read(urids.object, ()) + .unwrap(); - let (header, iter) = unsafe { Object::read(body, ()) }.unwrap(); assert_eq!(header.otype, object_type); assert_eq!(header.id, None); let properties: Vec<(PropertyHeader, &UnidentifiedAtom)> = iter.collect(); + let (header, atom) = properties[0]; assert_eq!(header.key, first_key); - assert_eq!(atom.read::(urids.int, ()).unwrap(), first_value); + assert_eq!(atom.read(urids.int, ()).unwrap(), first_value); + let (header, atom) = properties[1]; assert_eq!(header.key, second_key); - assert_eq!(atom.read::(urids.float, ()).unwrap(), second_value); + assert_eq!(atom.read(urids.float, ()).unwrap(), second_value); } } } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 5c6f6048..d1a00d8e 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -48,7 +48,7 @@ pub trait ScalarAtom: UriBound { /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. #[inline] unsafe fn read_scalar(body: &Space) -> Option { - body.read_as_unchecked().copied() + body.read().next_value().copied() } /// Try to write the atom into a space. @@ -59,8 +59,9 @@ pub trait ScalarAtom: UriBound { mut frame: AtomSpaceWriter<'handle, 'space>, value: Self::InternalType, ) -> Option<()> { - // TODO: decide if just the value has to be written, or if the whole atom type has to be written frame.write_value(value)?; + // Scalars have extra padding due to repr(C) + frame.allocate_padding_for::(); Some(()) } } @@ -160,10 +161,9 @@ mod tests { use crate::atoms::scalar::ScalarAtom; use crate::prelude::*; use crate::space::*; + use crate::AtomHeader; use std::convert::TryFrom; - use std::mem::size_of; use urid::*; - use crate::AtomHeader; fn test_scalar(value: A::InternalType) where @@ -191,20 +191,22 @@ mod tests { body: B, } - let (scalar, _) = raw_space.split_at(size_of::()).unwrap(); + let scalar: &Scalar = + unsafe { raw_space.read().next_value().unwrap() }; - let scalar = unsafe { &*(scalar.as_ptr() as *const Scalar) }; assert_eq!(scalar.atom.type_, urid); - assert_eq!(scalar.atom.size as usize, size_of::()); + assert_eq!(scalar.atom.size as usize, 8); // All are always aligned and padded assert_eq!(scalar.body, value); } // reading { - let (body, _) = unsafe { raw_space.split_atom_body(urid) }.unwrap(); - unsafe { - assert_eq!(A::read(body, ()).unwrap(), value); - } + let read_value = unsafe { raw_space.read().next_atom() } + .unwrap() + .read(urid, ()) + .unwrap(); + + assert_eq!(read_value, value); } } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 8a172140..3c84fda5 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -65,6 +65,7 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) +use crate::space::reader::AtomSpaceReader; use crate::*; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; @@ -93,13 +94,16 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { type WriteHandle = SequenceWriter<'handle, 'space>; unsafe fn read(body: &Space, bpm_urid: URID) -> Option { - let (header, body) = body.split_for_value_as_unchecked::()?; + let mut reader = body.read(); + let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; + let unit = if header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute } else { TimeStampUnit::Frames }; - Some(SequenceIterator { space: body, unit }) + + Some(SequenceIterator { reader, unit }) } fn init( @@ -171,7 +175,7 @@ impl TimeStamp { /// An iterator over all events in a sequence. pub struct SequenceIterator<'a> { - space: &'a Space, + reader: AtomSpaceReader<'a>, unit: TimeStampUnit, } @@ -185,18 +189,24 @@ impl<'a> Iterator for SequenceIterator<'a> { type Item = (TimeStamp, &'a UnidentifiedAtom); fn next(&mut self) -> Option<(TimeStamp, &'a UnidentifiedAtom)> { - // SAFETY: The validity of the space's contents is guaranteed by this type. - let (raw_stamp, space) = - unsafe { self.space.split_for_value_as_unchecked::() }?; - let stamp = match self.unit { - TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, - TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) }, - }; + let unit = self.unit; + + self.reader.try_read(|reader| { + // SAFETY: The validity of the space's contents is guaranteed by this type. + let raw_stamp: &RawTimeStamp = unsafe { reader.next_value()? }; + + let stamp = match unit { + TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, + TimeStampUnit::BeatsPerMinute => unsafe { + TimeStamp::BeatsPerMinute(raw_stamp.beats) + }, + }; - // SAFETY: The validity of the space's contents is guaranteed by this type. - let (atom, space) = unsafe { space.split_atom() }?; - self.space = space; - Some((stamp, atom)) + // SAFETY: The validity of the space's contents is guaranteed by this type. + let atom = unsafe { reader.next_atom()? }; + + Some((stamp, atom)) + }) } } @@ -291,7 +301,12 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.atom.sequence,TimeStampURID::Frames(urids.units.frame)).unwrap(); + let mut writer = space + .init_atom( + urids.atom.sequence, + TimeStampURID::Frames(urids.units.frame), + ) + .unwrap(); writer .init::(TimeStamp::Frames(0), urids.atom.int, 42) @@ -305,9 +320,8 @@ mod tests { // verifying { - let (sequence, space) = - unsafe { raw_space.split_for_value_as_unchecked::() } - .unwrap(); + let mut reader = raw_space.read(); + let sequence: &sys::LV2_Atom_Sequence = unsafe { reader.next_value() }.unwrap(); assert_eq!(sequence.atom.type_, urids.atom.sequence); assert_eq!( sequence.atom.size as usize, @@ -319,22 +333,18 @@ mod tests { ); assert_eq!(sequence.body.unit, urids.units.frame); - let (stamp, space) = - unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let stamp: &RawTimeStamp = unsafe { reader.next_value() }.unwrap(); assert_eq!(unsafe { stamp.frames }, 0); - let (int, space) = - unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let int: &sys::LV2_Atom_Int = unsafe { reader.next_value() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.int); - assert_eq!(int.atom.size as usize, size_of::()); + assert_eq!(int.atom.size as usize, 2 * size_of::()); assert_eq!(int.body, 42); - let (stamp, space) = - unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let stamp: &RawTimeStamp = unsafe { reader.next_value() }.unwrap(); assert_eq!(unsafe { stamp.frames }, 1); - let (int, _space) = - unsafe { space.split_for_value_as_unchecked::() }.unwrap(); + let int: &sys::LV2_Atom_Int = unsafe { reader.next_value() }.unwrap(); assert_eq!(int.atom.type_, urids.atom.long); assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 17); @@ -342,9 +352,10 @@ mod tests { // reading { - let body = unsafe { UnidentifiedAtom::from_space_unchecked(&raw_space) }.body(); - let mut reader = unsafe { Sequence::read(body, urids.units.beat) }.unwrap(); - + let mut reader = unsafe { raw_space.read().next_atom() } + .unwrap() + .read(urids.atom.sequence, urids.units.beat) + .unwrap(); assert_eq!(reader.unit(), TimeStampUnit::Frames); let (stamp, atom) = reader.next().unwrap(); diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 8cad351a..6dcdcf6b 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -54,7 +54,9 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { type WriteHandle = StringWriter<'handle, 'space>; unsafe fn read(body: &'handle Space, _: ()) -> Option<(LiteralInfo, &'handle str)> { - let (header, body) = body.split_for_value_as_unchecked::()?; + let mut reader = body.read(); + let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; + let info = if header.lang != 0 && header.datatype == 0 { LiteralInfo::Language(URID::new(header.lang)?) } else if header.lang == 0 && header.datatype != 0 { @@ -62,7 +64,9 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { } else { return None; }; - let data = body.as_bytes(); + + let data = reader.into_remaining().as_bytes(); + std::str::from_utf8(&data[0..data.len() - 1]) .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) .ok() @@ -73,18 +77,16 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { mut frame: AtomSpaceWriter<'handle, 'space>, info: LiteralInfo, ) -> Option> { - frame.write_value( - match info { - LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { - lang: lang.get(), - datatype: 0, - }, - LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body { - lang: 0, - datatype: datatype.get(), - }, + frame.write_value(match info { + LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { + lang: lang.get(), + datatype: 0, }, - )?; + LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body { + lang: 0, + datatype: datatype.get(), + }, + })?; Some(StringWriter { frame, has_nul_byte: false, @@ -161,10 +163,10 @@ impl<'handle, 'space> StringWriter<'handle, 'space> { mod tests { use crate::prelude::*; use crate::space::*; + use crate::AtomHeader; use std::ffi::CStr; use std::mem::{size_of, size_of_val}; use urid::*; - use crate::AtomHeader; struct German; unsafe impl UriBound for German { @@ -192,20 +194,20 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom( - urids.atom.literal, - LiteralInfo::Language(urids.german.into_general()), - ) - .unwrap(); + let mut writer = space + .init_atom( + urids.atom.literal, + LiteralInfo::Language(urids.german.into_general()), + ) + .unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } // verifying { - let (literal, space) = - unsafe { raw_space.split_for_value_as_unchecked::() } - .unwrap(); + let mut reader = raw_space.read(); + let literal: &sys::LV2_Atom_Literal = unsafe { reader.next_value() }.unwrap(); assert_eq!(literal.atom.type_, urids.atom.literal.get()); assert_eq!( @@ -219,7 +221,7 @@ mod tests { assert_eq!(literal.body.datatype, 0); let size = literal.atom.size as usize - size_of::(); - let string = CStr::from_bytes_with_nul(space.split_at(size).unwrap().0.as_bytes()) + let string = CStr::from_bytes_with_nul(reader.next_bytes(size).unwrap()) .unwrap() .to_str() .unwrap(); @@ -228,8 +230,14 @@ mod tests { // reading { - let (body, _) = unsafe { raw_space.split_atom_body(urids.atom.literal) }.unwrap(); - let (info, text) = unsafe { Literal::read(body, ()) }.unwrap(); + let (info, text) = unsafe { + raw_space + .read() + .next_atom() + .unwrap() + .read(urids.atom.literal, ()) + } + .unwrap(); assert_eq!(info, LiteralInfo::Language(urids.german.into_general())); assert_eq!(text, SAMPLE0.to_owned() + SAMPLE1); @@ -255,27 +263,22 @@ mod tests { // verifying { - let (string, space) = - unsafe { raw_space.split_for_value_as_unchecked::() } - .unwrap(); + let mut reader = raw_space.read(); + let string: &sys::LV2_Atom_String = unsafe { reader.next_value() }.unwrap(); assert_eq!(string.atom.type_, urids.string); assert_eq!(string.atom.size as usize, SAMPLE0.len() + SAMPLE1.len() + 1); - let string = std::str::from_utf8( - space - .split_at(string.atom.size as usize) - .unwrap() - .0 - .as_bytes(), - ) - .unwrap(); + let string = + std::str::from_utf8(reader.next_bytes(string.atom.size as usize).unwrap()).unwrap(); assert_eq!(string[..string.len() - 1], SAMPLE0.to_owned() + SAMPLE1); } // reading { - let (body, _) = unsafe { raw_space.split_atom_body(urids.string) }.unwrap(); - let string = unsafe { String::read(body, ()) }.unwrap(); + let string = unsafe { raw_space.read().next_atom() } + .unwrap() + .read(urids.string, ()) + .unwrap(); assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1); } } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index fc3a4c3c..18c00a82 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -30,6 +30,7 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Tuple](http://lv2plug.in/ns/ext/atom/atom.html#Tuple) +use crate::space::reader::AtomSpaceReader; use crate::*; /// An atom containing a series of other atoms. @@ -48,7 +49,9 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { type WriteHandle = TupleWriter<'handle, 'space>; unsafe fn read(body: &'space Space, _: ()) -> Option> { - Some(TupleIterator { space: body }) + Some(TupleIterator { + reader: AtomSpaceReader::new(body), + }) } fn init( @@ -63,17 +66,15 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { /// /// The item of this iterator is simply the space a single atom occupies. pub struct TupleIterator<'a> { - space: &'a AtomSpace, + reader: AtomSpaceReader<'a>, } impl<'a> Iterator for TupleIterator<'a> { type Item = &'a UnidentifiedAtom; fn next(&mut self) -> Option<&'a UnidentifiedAtom> { - // SAFETY: The validity of the space is guaranteed by this type. - let (atom, space) = unsafe { self.space.split_atom() }?; - self.space = space; - Some(atom) + // SAFETY: the validity of the given space is guaranteed by this type. + unsafe { self.reader.next_atom() } } } @@ -97,9 +98,9 @@ impl<'handle, 'space> TupleWriter<'handle, 'space> { mod tests { use crate::prelude::*; use crate::space::*; + use crate::AtomHeader; use std::mem::size_of; use urid::*; - use crate::AtomHeader; #[test] fn test_tuple() { @@ -111,19 +112,18 @@ mod tests { // writing { - let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.tuple, ()).unwrap(); + let mut cursor = raw_space.write(); + let mut writer = cursor.init_atom(urids.tuple, ()).unwrap(); { - let mut vector_writer = - writer.init::>(urids.vector, urids.int).unwrap(); + let mut vector_writer = writer.init(urids.vector, urids.int).unwrap(); vector_writer.append(&[17; 9]).unwrap(); } - writer.init::(urids.int, 42).unwrap(); + writer.init(urids.int, 42).unwrap(); } // verifying { - let atom = unsafe { raw_space.to_atom() }.unwrap(); + let atom = unsafe { raw_space.read().next_atom() }.unwrap(); let header = atom.header(); assert_eq!(header.urid(), urids.tuple); assert_eq!( @@ -131,15 +131,12 @@ mod tests { size_of::() + size_of::() * 9 + 4 - + size_of::() - + size_of::() + + size_of::() ); - let (vector, remaining) = unsafe { - atom.body() - .split_for_value_as_unchecked::() - } - .unwrap(); + let mut reader = atom.body().read(); + let vector: &sys::LV2_Atom_Vector = unsafe { reader.next_value().unwrap() }; + assert_eq!(vector.atom.type_, urids.vector); assert_eq!( vector.atom.size as usize, @@ -148,21 +145,18 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int); - let (vector_items, space) = remaining.split_at(size_of::() * 9).unwrap(); - let vector_items = - unsafe { vector_items.aligned::().unwrap().assume_init_slice() }; + let vector_items = unsafe { reader.next_slice::(9) }.unwrap(); assert_eq!(vector_items, &[17; 9]); - let int: &sys::LV2_Atom_Int = - unsafe { space.aligned().unwrap().assume_init_value_unchecked() }; + let int: &sys::LV2_Atom_Int = unsafe { reader.next_value() }.unwrap(); assert_eq!(int.atom.type_, urids.int); - assert_eq!(int.atom.size as usize, size_of::()); + assert_eq!(int.atom.size as usize, size_of::()); assert_eq!(int.body, 42); } // reading { - let (body, _) = unsafe { raw_space.split_atom_body(urids.tuple) }.unwrap(); + let body = unsafe { raw_space.read().next_atom().unwrap().body() }; let items: Vec<&UnidentifiedAtom> = unsafe { Tuple::read(body, ()) }.unwrap().collect(); assert_eq!(items[0].read(urids.vector, urids.int).unwrap(), [17; 9]); assert_eq!(items[1].read(urids.int, ()).unwrap(), 42); diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 12db461c..a8cbdfce 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -56,7 +56,8 @@ where type WriteHandle = VectorWriter<'handle, 'space, C>; unsafe fn read(body: &'space Space, child_urid: URID) -> Option<&'space [C::InternalType]> { - let (header, body) = body.split_for_value_as_unchecked::()?; + let mut reader = body.read(); + let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; if header.child_type != child_urid || header.child_size as usize != size_of::() @@ -65,7 +66,7 @@ where } // SAFETY: We can assume this data was properly initialized by the host. - Some(body.aligned()?.assume_init_slice()) + reader.as_slice() } fn init( @@ -118,9 +119,9 @@ impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { #[cfg(test)] mod tests { use crate::space::*; + use crate::AtomHeader; use std::mem::size_of; use urid::*; - use crate::AtomHeader; #[test] fn test_vector() { @@ -142,9 +143,8 @@ mod tests { // verifying { - let (vector, children) = - unsafe { raw_space.split_for_value_as_unchecked::() } - .unwrap(); + let mut reader = raw_space.read(); + let vector: &sys::LV2_Atom_Vector = unsafe { reader.next_value() }.unwrap(); assert_eq!(vector.atom.type_, urids.vector.get()); assert_eq!( vector.atom.size as usize, @@ -153,9 +153,7 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int.get()); - let children = unsafe { - std::slice::from_raw_parts(children.as_bytes().as_ptr() as *const i32, CHILD_COUNT) - }; + let children = unsafe { reader.next_slice::(CHILD_COUNT) }.unwrap(); for value in &children[0..children.len() - 1] { assert_eq!(*value, 42); } @@ -164,7 +162,7 @@ mod tests { // reading { - let atom = unsafe { raw_space.to_atom() }.unwrap(); + let atom = unsafe { raw_space.read().next_atom() }.unwrap(); let children: &[i32] = atom.read(urids.vector, urids.int).unwrap(); assert_eq!(children.len(), CHILD_COUNT); diff --git a/atom/src/header.rs b/atom/src/header.rs index e21c4d87..1df4b3d3 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -1,3 +1,4 @@ +use crate::UnidentifiedAtom; use urid::URID; #[repr(C, align(8))] @@ -23,6 +24,22 @@ impl AtomHeader { unsafe { &*(raw as *const lv2_sys::LV2_Atom as *const _) } } + #[inline] + pub(crate) fn from_raw_mut(raw: &mut lv2_sys::LV2_Atom) -> &mut Self { + // SAFETY: AtomHeader is repr(C) and has LV2_Atom as its only field, so transmuting between the two is safe. + unsafe { &mut *(raw as *mut lv2_sys::LV2_Atom as *mut _) } + } + + #[inline] + pub unsafe fn assume_full_atom(&self) -> &UnidentifiedAtom { + UnidentifiedAtom::from_header(self) + } + + #[inline] + pub unsafe fn assume_full_atom_mut(&mut self) -> &mut UnidentifiedAtom { + UnidentifiedAtom::from_header_mut(self) + } + #[inline] pub(crate) unsafe fn set_size_of_body(&mut self, size: usize) { self.inner.size = size as u32; @@ -39,7 +56,7 @@ impl AtomHeader { } #[inline] - pub fn urid(self) -> u32 { - self.inner.type_ + pub fn urid(self) -> URID { + URID::new(self.inner.type_).unwrap() } } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index cba1a0ef..851c5fcb 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -74,6 +74,8 @@ mod header; pub mod port; pub mod space; +pub(crate) mod util; + /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { pub use atoms::chunk::Chunk; @@ -173,24 +175,14 @@ impl UnidentifiedAtom { Some(Self::from_header_mut(space.assume_init_value_mut()?)) } - /// Construct a new unidentified atom. - /// - /// # Safety - /// - /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. - #[inline] - pub unsafe fn from_space_unchecked(space: &AtomSpace) -> &Self { - Self::from_header(space.assume_init_value_unchecked()) - } - #[inline] - pub unsafe fn from_header(header: &AtomHeader) -> &Self { + pub(crate) unsafe fn from_header(header: &AtomHeader) -> &Self { // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. &*(header as *const _ as *const _) } #[inline] - pub unsafe fn from_header_mut(header: &mut AtomHeader) -> &mut Self { + pub(crate) unsafe fn from_header_mut(header: &mut AtomHeader) -> &mut Self { // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. &mut *(header as *mut _ as *mut _) } @@ -231,7 +223,21 @@ impl UnidentifiedAtom { } #[inline] - fn atom_space(&self) -> &AtomSpace { + fn body_bytes_mut(&mut self) -> &mut [u8] { + if self.header.size_of_body() == 0 { + &mut [] + } else { + // SAFETY: This type's constructor ensures the atom's body is valid + // The edge case of an empty body is also checked above. + let ptr = unsafe { (self as *mut UnidentifiedAtom).add(1) }; + + // SAFETY: This type's constructor ensures the atom's body is valid + unsafe { ::core::slice::from_raw_parts_mut(ptr.cast(), self.header.size_of_body()) } + } + } + + #[inline] + pub fn atom_space(&self) -> &AtomSpace { let ptr = self as *const UnidentifiedAtom as *const u8; let bytes = unsafe { ::core::slice::from_raw_parts(ptr, self.header.size_of_atom()) }; @@ -239,9 +245,24 @@ impl UnidentifiedAtom { unsafe { AtomSpace::from_bytes_unchecked(bytes) } } + #[inline] + pub fn atom_space_mut(&mut self) -> &mut AtomSpace { + let ptr = self as *mut UnidentifiedAtom as *mut u8; + let bytes = unsafe { ::core::slice::from_raw_parts_mut(ptr, self.header.size_of_atom()) }; + + // SAFETY: the bytes are necessarily aligned, since they point to the aligned AtomHeader + unsafe { AtomSpace::from_bytes_mut_unchecked(bytes) } + } + #[inline] pub fn body(&self) -> &AtomSpace { // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader unsafe { AtomSpace::from_bytes_unchecked(self.body_bytes()) } } + + #[inline] + pub fn body_mut(&mut self) -> &mut AtomSpace { + // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader + unsafe { AtomSpace::from_bytes_mut_unchecked(self.body_bytes_mut()) } + } } diff --git a/atom/src/port.rs b/atom/src/port.rs index 6c8afb0f..681f7a1e 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -114,14 +114,14 @@ impl PortType for AtomPort { #[inline] unsafe fn input_from_raw(pointer: NonNull, _sample_count: u32) -> PortReader<'static> { - let header: &'static AtomHeader = AtomHeader::from_raw(&*pointer.cast().as_ptr()); - PortReader::new(UnidentifiedAtom::from_header(header)) + let header = AtomHeader::from_raw(pointer.cast().as_ref()); + PortReader::new(header.assume_full_atom()) } #[inline] unsafe fn output_from_raw(pointer: NonNull, _sample_count: u32) -> PortWriter<'static> { - let space = Space::from_atom_mut(&mut *pointer.cast().as_ptr()); - PortWriter::new(space) + let header = AtomHeader::from_raw_mut(pointer.cast().as_mut()); + PortWriter::new(header.assume_full_atom_mut().body_mut()) } } @@ -129,11 +129,11 @@ impl PortType for AtomPort { mod tests { use crate::prelude::*; use crate::space::*; + use crate::AtomHeader; use lv2_core::prelude::*; use std::mem::size_of; use std::ptr::NonNull; use urid::*; - use crate::AtomHeader; #[test] fn test_atom_port() { diff --git a/atom/src/space.rs b/atom/src/space.rs index bf656190..78326463 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -3,6 +3,7 @@ mod allocatable; mod atom_writer; mod cursor; +pub mod reader; mod space; mod vec; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 8d1be5cc..3c7f7a70 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -5,12 +5,6 @@ use urid::URID; use core::mem::size_of_val; use std::mem::MaybeUninit; -// This function is separate to ensure proper lifetimes -unsafe fn assume_init_mut(s: &mut MaybeUninit) -> &mut T { - // SAFETY: the caller must guarantee that `self` is initialized. - &mut *s.as_mut_ptr() -} - /// A smart pointer that writes atom data to an internal slice. /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. @@ -40,7 +34,18 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { } #[inline] - fn allocate_aligned<'handle, T: 'static>(&'handle mut self, size: usize) -> Option<&'handle mut Space> + fn allocate_padding_for(&mut self) -> Option<()> { + let required_padding = Space::::padding_for(self.remaining_bytes()); + self.allocate(required_padding)?; + + Some(()) + } + + #[inline] + fn allocate_aligned<'handle, T: 'static>( + &'handle mut self, + size: usize, + ) -> Option<&'handle mut Space> where 'space: 'handle, { @@ -113,7 +118,7 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { *space = MaybeUninit::new(value); // SAFETY: the MaybeUninit has now been properly initialized. - Some(unsafe { assume_init_mut(space) }) + Some(unsafe { crate::util::assume_init_mut(space) }) } fn write_values<'handle, T>(&'handle mut self, values: &[T]) -> Option<&'handle mut [T]> @@ -133,15 +138,15 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { } } -impl<'space, H: SpaceAllocatorImpl<'space>> SpaceAllocator<'space> for H { } +impl<'space, H: SpaceAllocatorImpl<'space>> SpaceAllocator<'space> for H {} #[cfg(test)] mod tests { use crate::prelude::{Int, SpaceAllocator}; use crate::space::cursor::SpaceCursor; use crate::space::{SpaceAllocatorImpl, VecSpace}; - use urid::URID; use crate::AtomHeader; + use urid::URID; const INT_URID: URID = unsafe { URID::new_unchecked(5) }; @@ -158,13 +163,13 @@ mod tests { { cursor.init_atom(INT_URID, 69).unwrap(); - assert_eq!(12, cursor.remaining_bytes().len()); + assert_eq!(8, cursor.remaining_bytes().len()); } assert_eq!( space.as_bytes(), [ - 42, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 42, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ] ); diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs new file mode 100644 index 00000000..8fe6688e --- /dev/null +++ b/atom/src/space/reader.rs @@ -0,0 +1,104 @@ +use crate::prelude::Space; +use crate::{AtomHeader, UnidentifiedAtom}; +use std::mem::MaybeUninit; + +pub struct SpaceReader<'a, T> { + space: &'a Space, +} + +impl<'a, T: 'static> SpaceReader<'a, T> { + #[inline] + pub fn new(space: &'a Space) -> Self { + SpaceReader { space } + } + + #[inline] + fn next_uninit_value(&mut self) -> Option<&'a MaybeUninit> { + let space = self.space.realign()?; + let value_size = ::core::mem::size_of::(); + let (value, remaining) = space.split_at(value_size)?; + + self.space = remaining.realign().unwrap_or_else(Space::empty); + + value.as_uninit() + } + + #[inline] + fn next_uninit_value_slice( + &mut self, + length: usize, + ) -> Option<&'a [MaybeUninit]> { + let space = self.space.realign()?; + let split_point = crate::util::value_index_to_byte_index::(length); + let (data, remaining) = space.split_at(split_point)?; + + self.space = remaining.realign().unwrap_or_else(Space::empty); + + Some(data.as_uninit_slice()) + } + + #[inline] + fn as_uninit_slice(&self) -> Option<&'a [MaybeUninit]> { + Some(self.space.realign()?.as_uninit_slice()) + } + + #[inline] + pub unsafe fn as_slice(&self) -> Option<&'a [U]> { + self.as_uninit_slice() + .map(|s| crate::util::assume_init_slice(s)) + } + + #[inline] + pub unsafe fn next_slice(&mut self, length: usize) -> Option<&'a [U]> { + self.next_uninit_value_slice(length) + .map(|s| crate::util::assume_init_slice(s)) + } + + #[inline] + pub fn next_bytes(&mut self, length: usize) -> Option<&'a [u8]> { + let split_point = crate::util::value_index_to_byte_index::(length); + + let (data, remaining) = self.space.split_at(split_point)?; + self.space = remaining.realign().unwrap_or_else(Space::empty); + + Some(data.as_bytes()) + } + + #[inline] + pub unsafe fn next_value(&mut self) -> Option<&'a U> { + self.next_uninit_value() + .map(|v| crate::util::assume_init_ref(v)) + } + + #[inline] + pub fn into_remaining(self) -> &'a Space { + self.space + } + + #[inline] + pub fn try_read(&mut self, read_handler: F) -> Option + where + F: FnOnce(&mut Self) -> Option, + { + let mut reader = self.space.read(); + let value = read_handler(&mut reader)?; + self.space = reader.into_remaining(); + + Some(value) + } +} + +pub type AtomSpaceReader<'a> = SpaceReader<'a, AtomHeader>; + +impl<'a> AtomSpaceReader<'a> { + #[inline] + pub unsafe fn next_atom(&mut self) -> Option<&'a UnidentifiedAtom> { + let header = self.space.assume_init_value()?; + let (_, rest) = self.space.split_at(header.size_of_atom())?; + + let atom = UnidentifiedAtom::from_header(header); + self.space = rest; + + Some(atom) + } +} diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index c82c6916..5b894b30 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -1,10 +1,10 @@ use crate::header::AtomHeader; -use crate::UnidentifiedAtom; +use crate::space::reader::SpaceReader; +use crate::space::SpaceCursor; use core::mem::{align_of, size_of}; use std::marker::PhantomData; use std::mem::{size_of_val, MaybeUninit}; use std::slice::{from_raw_parts, from_raw_parts_mut}; -use urid::URID; /// An aligned slice of bytes that is designed to contain a given type `T` (by default, Atoms). /// @@ -22,7 +22,7 @@ pub type AtomSpace = Space; impl Space { /// Creates an empty Space. #[inline] - fn empty() -> &'static Space { + pub fn empty<'a>() -> &'a Space { // SAFETY: empty slices are always aligned unsafe { Self::from_bytes_unchecked(&[]) } } @@ -170,35 +170,11 @@ impl Space { #[inline] pub fn split_at(&self, mid: usize) -> Option<(&Self, &Self)> { let (start, end) = self.split_bytes_at(mid)?; - let end = Self::try_align_from_bytes(end).unwrap_or(Space::empty()); + let end = Self::try_align_from_bytes(end).unwrap_or_else(Space::empty); Some((start, end)) } - #[inline] - fn split_for_value(&self) -> Option<(&MaybeUninit, &Self)> { - let (value, rest) = self.split_at(size_of::())?; - let value = value.as_uninit()?; - - Some((value, rest)) - } - - #[inline] - // FIXME: rename this - pub unsafe fn split_for_value_unchecked(&self) -> Option<(&T, &Self)> { - let (value, rest) = self.split_for_value()?; - - Some((&*(value.as_ptr() as *const T), rest)) - } - - #[inline] - // FIXME: rename this - pub unsafe fn split_for_value_as_unchecked(&self) -> Option<(&U, &Self)> { - let (value, rest) = self.realign()?.split_for_value_unchecked()?; - - Some((value, rest.realign().unwrap_or(Self::empty()))) - } - #[inline] pub fn realign(&self) -> Option<&Space> { Space::::try_align_from_bytes(self.as_bytes()) @@ -240,6 +216,11 @@ impl Space { self.data.len() } + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Return the internal slice of the space. #[inline] pub fn as_bytes_mut(&mut self) -> &mut [u8] { @@ -249,36 +230,25 @@ impl Space { #[inline] pub(crate) unsafe fn assume_init_value(&self) -> Option<&T> { // SAFETY: The caller has to ensure this slice actually points to initialized memory. - Some(&*(self.as_uninit()?.as_ptr())) - } - - #[inline] - pub(crate) unsafe fn assume_init_value_unchecked(&self) -> &T { - // SAFETY: The caller has to ensure this slice actually points to initialized memory. - &*(self.as_uninit_unchecked().as_ptr()) + Some(crate::util::assume_init_ref(self.as_uninit()?)) } #[inline] pub(crate) unsafe fn assume_init_value_mut(&mut self) -> Option<&mut T> { // SAFETY: The caller has to ensure this slice actually points to initialized memory. - Some(&mut *(self.as_uninit_mut()?.as_mut_ptr())) - } - - #[inline] - pub unsafe fn read_as_unchecked(&self) -> Option<&U> { - self.aligned()?.assume_init_value() + Some(crate::util::assume_init_mut(self.as_uninit_mut()?)) } /// Gets a `T`-aligned pointer to the contents. ///split_for_type /// This methods returns [`None`](Option::None) if the space is not large enough for a value of type `T`. #[inline] - fn as_uninit(&self) -> Option<&MaybeUninit> { + pub fn as_uninit(&self) -> Option<&MaybeUninit> { if self.data.len() < size_of::() { return None; } - // SAFETY: We just checked that the space was actually big enough. + // SAFETY: We just checked that the space was actually big enough, and the alignment is guaranteed by this type. Some(unsafe { self.as_uninit_unchecked() }) } @@ -291,7 +261,7 @@ impl Space { return None; } - // SAFETY: We just checked that the space was actually big enough. + // SAFETY: We just checked that the space was actually big enough, and the alignment is guaranteed by this type. Some(unsafe { self.as_uninit_mut_unchecked() }) } @@ -326,8 +296,7 @@ impl Space { #[inline] pub unsafe fn assume_init_slice(&self) -> &[T] { - let data = self.as_uninit_slice(); - &*(data as *const _ as *const [T]) + crate::util::assume_init_slice(self.as_uninit_slice()) } /// Gets the contents as a slice of potentially uninitialized `T`s. @@ -345,83 +314,23 @@ impl Space { } } - pub fn write(&mut self, value: T) -> Option<&mut T> { - let uninit = self.as_uninit_mut()?; - *uninit = MaybeUninit::new(value); - // SAFETY: We just initialized this value. - Some(unsafe { &mut *(uninit.as_mut_ptr()) }) - } -} - -impl AtomSpace { - /// Create a new space from an atom pointer. - /// - /// The method creates a space that contains the atom as well as its body. - /// - /// # Safety - /// - /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. - #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom(atom: &sys::LV2_Atom) -> &Self { - let data = std::slice::from_raw_parts( - atom as *const sys::LV2_Atom as *const u8, - atom.size as usize + size_of::(), - ); - - Self::from_bytes_unchecked(data) - } - - /// Create a new mutable space from an atom pointer. - /// - /// The method creates a space that contains the atom as well as its body. - /// - /// # Safety - /// - /// Since the body is not included in the atom reference, this method has to assume that it is valid memory and therefore is unsafe but sound. - #[allow(clippy::trivially_copy_pass_by_ref)] - pub unsafe fn from_atom_mut(atom: &mut sys::LV2_Atom) -> &mut Self { - let data = std::slice::from_raw_parts_mut( - atom as *mut sys::LV2_Atom as *mut u8, - atom.size as usize + size_of::(), - ); - - Self::from_bytes_mut_unchecked(data) - } - - #[inline] - pub unsafe fn to_atom(&self) -> Option<&UnidentifiedAtom> { - UnidentifiedAtom::from_space(self) - } - #[inline] - pub unsafe fn split_atom(&self) -> Option<(&UnidentifiedAtom, &Self)> { - let header = self.assume_init_value()?; - let (atom, rest) = self.split_at(header.size_of_atom())?; - let atom = UnidentifiedAtom::from_space_unchecked(atom); - - Some((atom, rest)) + pub fn read(&self) -> SpaceReader { + SpaceReader::new(self) } #[inline] - pub unsafe fn split_atom_body(&self, urid: URID) -> Option<(&Space, &Space)> { - let (header, body) = self.split_for_value()?; - // SAFETY: The caller is responsible for ensuring there is a valid atom header in there. - let header = &*header.as_ptr(); - - if header.urid() != urid { - return None; - } - - body.split_at(header.size_of_body()) + pub fn write(&mut self) -> SpaceCursor { + SpaceCursor::new(self.as_bytes_mut()) } } #[cfg(test)] mod tests { use crate::space::*; + use crate::AtomHeader; use std::mem::{size_of, size_of_val}; use urid::*; - use crate::AtomHeader; #[test] fn test_space() { @@ -464,7 +373,7 @@ mod tests { body: 42, }; - let (atom, _) = space.split_atom().unwrap(); + let atom = space.read().next_atom().unwrap(); let body = atom.body().as_bytes(); assert_eq!(size_of::(), size_of_val(body)); @@ -490,7 +399,11 @@ mod tests { assert_eq!(written_atom.type_, test_atom.type_); let written_atom_addr = written_atom as *mut _ as *mut _; - let created_space = unsafe { Space::from_atom_mut(written_atom) }; + let created_space = unsafe { + AtomHeader::from_raw(written_atom) + .assume_full_atom() + .atom_space() + }; assert!(::core::ptr::eq( written_atom_addr, diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index d4651805..6bd47775 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -4,15 +4,6 @@ use crate::space::{Space, SpaceAllocatorImpl}; use std::mem::MaybeUninit; use std::ops::Range; -pub(crate) fn byte_index_to_value_index(size: usize) -> usize { - let type_size = ::core::mem::size_of::(); - if type_size == 0 { - 0 - } else { - size / type_size + if size % type_size > 0 { 1 } else { 0 } - } -} - pub struct VecSpace { inner: Vec>, } @@ -59,7 +50,7 @@ impl VecSpace { let max = byte_range.start.max(byte_range.end); if max > byte_len { - let new_size = byte_index_to_value_index::(max); + let new_size = crate::util::byte_index_to_value_index::(max); self.inner.resize(new_size, MaybeUninit::zeroed()); } diff --git a/atom/src/util.rs b/atom/src/util.rs new file mode 100644 index 00000000..4908c25b --- /dev/null +++ b/atom/src/util.rs @@ -0,0 +1,35 @@ +use std::mem::MaybeUninit; + +// This function is separate to ensure proper lifetimes +#[inline] +pub(crate) unsafe fn assume_init_ref(s: &MaybeUninit) -> &T { + // SAFETY: the caller must guarantee that `self` is initialized. + &*s.as_ptr() +} + +// This function is separate to ensure proper lifetimes +#[inline] +pub(crate) unsafe fn assume_init_mut(s: &mut MaybeUninit) -> &mut T { + // SAFETY: the caller must guarantee that `self` is initialized. + &mut *s.as_mut_ptr() +} + +#[inline] +pub(crate) unsafe fn assume_init_slice(slice: &[MaybeUninit]) -> &[T] { + &*(slice as *const _ as *const [T]) +} + +#[inline] +pub(crate) fn value_index_to_byte_index(size: usize) -> usize { + size * ::core::mem::size_of::() +} + +#[inline] +pub(crate) fn byte_index_to_value_index(size: usize) -> usize { + let type_size = ::core::mem::size_of::(); + if type_size == 0 { + 0 + } else { + size / type_size + if size % type_size > 0 { 1 } else { 0 } + } +} diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index b8163785..14da3eba 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -4,11 +4,11 @@ extern crate lv2_sys as sys; extern crate lv2_units as units; use atom::prelude::*; +use atom::AtomHeader; use core::prelude::*; use lv2_urid::*; use units::prelude::*; use urid::*; -use atom::AtomHeader; #[derive(PortCollection)] struct Ports { @@ -104,11 +104,12 @@ fn main() { let input_atom_space = input_atom_space.as_space_mut(); { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); - let mut writer = space.init_atom( - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame), - ) - .unwrap(); + let mut writer = space + .init_atom( + urids.atom.sequence, + TimeStampURID::Frames(urids.units.frame), + ) + .unwrap(); { let _ = writer .init(TimeStamp::Frames(0), urids.atom.int, 42) @@ -127,7 +128,8 @@ fn main() { let output_atom_space = output_atom_space.as_space_mut(); { let mut space = SpaceCursor::new(output_atom_space.as_bytes_mut()); - space.init_atom(urids.atom.chunk, ()) + space + .init_atom(urids.atom.chunk, ()) .unwrap() .allocate(256 - size_of::()) .unwrap(); @@ -171,8 +173,12 @@ fn main() { } // Asserting the result - let (sequence, _) = unsafe { output_atom_space.split_atom_body(urids.atom.sequence) }.unwrap(); - for (stamp, atom) in unsafe { Sequence::read(sequence, urids.units.beat) }.unwrap() { + let sequence = unsafe { output_atom_space.read().next_atom() } + .unwrap() + .read(urids.atom.sequence, urids.units.beat) + .unwrap(); + + for (stamp, atom) in sequence { let stamp = stamp.as_frames().unwrap(); match stamp { 0 => assert_eq!(atom.read(urids.atom.int, ()).unwrap(), 84), diff --git a/core/src/feature/mod.rs b/core/src/feature/mod.rs index cbdcbc64..3871d1eb 100644 --- a/core/src/feature/mod.rs +++ b/core/src/feature/mod.rs @@ -152,10 +152,10 @@ mod tests { struct FeatureTestSetting<'a> { pub data_a: Pin>, - pub feature_a_sys: Pin>, + pub _feature_a_sys: Pin>, pub data_b: Pin>, - pub feature_b_sys: Pin>, - pub feature_c_sys: Pin>, + pub _feature_b_sys: Pin>, + pub _feature_c_sys: Pin>, pub features_cache: FeatureCache<'a>, } @@ -190,10 +190,10 @@ mod tests { Self { data_a, - feature_a_sys, + _feature_a_sys: feature_a_sys, data_b, - feature_b_sys, - feature_c_sys, + _feature_b_sys: feature_b_sys, + _feature_c_sys: feature_c_sys, features_cache, } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index e5fa67f6..42370904 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -103,7 +103,7 @@ impl<'handle, 'space> Drop for Writer<'handle, 'space> { #[cfg(test)] mod tests { use crate::wmidi_binding::*; - use lv2_atom::space::{VecSpace, SpaceCursor}; + use lv2_atom::space::{SpaceCursor, VecSpace}; use lv2_atom::AtomHeader; use std::convert::TryFrom; use wmidi::*; @@ -163,7 +163,7 @@ mod tests { // verifying { - let atom = unsafe { raw_space.to_atom() }.unwrap(); + let atom = unsafe { raw_space.read().next_atom() }.unwrap(); assert_eq!(atom.header().urid(), urid); assert_eq!(atom.header().size_of_body(), 6); @@ -172,7 +172,7 @@ mod tests { // reading { - let atom = unsafe { raw_space.to_atom() }.unwrap(); + let atom = unsafe { raw_space.read().next_atom() }.unwrap(); let message = atom.read(urid, ()).unwrap(); assert_eq!( message, diff --git a/state/src/raw.rs b/state/src/raw.rs index c6bcdcb9..801a6127 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -54,12 +54,12 @@ impl<'a> StoreHandle<'a> { ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space = space.as_space(); - let atom = unsafe { space.to_atom() }.ok_or(StateErr::BadData)?; + let atom = unsafe { space.read().next_atom() }.ok_or(StateErr::BadData)?; let key = key.get(); let data_ptr = atom.body().as_ptr() as *const c_void; let data_size = atom.header().size_of_body(); - let data_type = atom.header().urid(); + let data_type = atom.header().urid().get(); let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD | sys::LV2_State_Flags::LV2_STATE_IS_PORTABLE) .into(); @@ -123,7 +123,9 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result { if !self.initialized { self.initialized = true; - self.cursor.init_atom(urid, parameter).ok_or(StateErr::Unknown) + self.cursor + .init_atom(urid, parameter) + .ok_or(StateErr::Unknown) } else { Err(StateErr::Unknown) } From 95e7f175d8bdc1d88f86ccf926b0cb5d126a6aee Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 7 Sep 2021 11:09:20 +0200 Subject: [PATCH 20/54] Various fixes and improvements --- atom/src/atoms/chunk.rs | 14 ++++++++------ atom/src/port.rs | 8 +++++--- atom/src/space/space.rs | 1 - atom/tests/atom_integration.rs | 7 ++++++- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index a351ca0a..e109154b 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -14,10 +14,10 @@ //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { -//! let in_chunk: &[u8] = ports.input.read(urids.chunk, ()).unwrap(); +//! let in_chunk: &AtomSpace = ports.input.read(urids.chunk, ()).unwrap(); //! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk, ()).unwrap(); //! -//! out_chunk.write_bytes(in_chunk).unwrap(); +//! out_chunk.write_bytes(in_chunk.as_bytes()).unwrap(); //! } //! ``` //! @@ -39,14 +39,16 @@ unsafe impl UriBound for Chunk { impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { type ReadParameter = (); - type ReadHandle = &'handle [u8]; + type ReadHandle = &'handle AtomSpace; type WriteParameter = (); type WriteHandle = AtomSpaceWriter<'handle, 'space>; - unsafe fn read(space: &'handle Space, _: ()) -> Option<&'handle [u8]> { - Some(space.as_bytes()) + #[inline] + unsafe fn read(space: &'handle Space, _: ()) -> Option<&'handle AtomSpace> { + Some(space) } + #[inline] fn init( frame: AtomSpaceWriter<'handle, 'space>, _: (), @@ -101,7 +103,7 @@ mod tests { unsafe { Chunk::read(raw_space.read().next_atom().unwrap().body(), ()) }.unwrap(); assert_eq!(data.len(), SLICE_LENGTH); - for (i, value) in data.iter().enumerate() { + for (i, value) in data.as_bytes().iter().enumerate() { assert_eq!(*value as usize, i); } } diff --git a/atom/src/port.rs b/atom/src/port.rs index 681f7a1e..a4a7f771 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -160,9 +160,11 @@ mod tests { // Reading { - let reader = unsafe { - AtomPort::input_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) - }; + let chunk = unsafe { raw_space.read().next_atom() } + .unwrap() + .read(urids.chunk, ()) + .unwrap(); + let reader = unsafe { AtomPort::input_from_raw(NonNull::from(chunk).cast(), 0) }; assert_eq!(reader.read::(urids.int, ()).unwrap(), 42); } } diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 5b894b30..4db91068 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -13,7 +13,6 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; pub struct Space { _type: PhantomData, // Note: this could be [MaybeUninit] for alignment, but Spaces can have extra unaligned bytes at the end. - // TODO: replace this with [MaybeUninit] data: [u8], } diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 14da3eba..d113e8eb 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -172,8 +172,13 @@ fn main() { (plugin_descriptor.cleanup.unwrap())(plugin); } + let chunk = unsafe { output_atom_space.read().next_atom() } + .unwrap() + .read(urids.atom.chunk, ()) + .unwrap(); + // Asserting the result - let sequence = unsafe { output_atom_space.read().next_atom() } + let sequence = unsafe { chunk.read().next_atom() } .unwrap() .read(urids.atom.sequence, urids.units.beat) .unwrap(); From 355b898c8c84cc5442badd44c044ffef0156fdf2 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 7 Sep 2021 13:48:41 +0200 Subject: [PATCH 21/54] Remove type param on space reader --- atom/src/atoms/object.rs | 8 ++-- atom/src/atoms/sequence.rs | 4 +- atom/src/atoms/string.rs | 2 +- atom/src/atoms/tuple.rs | 6 +-- atom/src/space/reader.rs | 75 ++++++++++++++++++-------------------- atom/src/space/space.rs | 8 ++-- 6 files changed, 50 insertions(+), 53 deletions(-) diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 711068df..d886084a 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -67,7 +67,7 @@ //! //! # Specification //! [http://lv2plug.in/ns/ext/atom/atom.html#Object](http://lv2plug.in/ns/ext/atom/atom.html#Object). -use crate::space::reader::AtomSpaceReader; +use crate::space::reader::SpaceReader; use crate::*; use std::convert::TryFrom; use std::iter::Iterator; @@ -166,7 +166,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Blank { /// /// Each iteration item is the header of the property, as well as the space occupied by the value atom. You can use normal `read` methods on the returned space. pub struct ObjectReader<'a> { - reader: AtomSpaceReader<'a>, + reader: SpaceReader<'a>, } impl<'a> Iterator for ObjectReader<'a> { @@ -256,7 +256,7 @@ impl Property { /// /// The caller must ensure that the given Space actually contains a valid property. unsafe fn read_body<'a>( - reader: &mut AtomSpaceReader<'a>, + reader: &mut SpaceReader<'a>, ) -> Option<(PropertyHeader, &'a UnidentifiedAtom)> { let header: &StrippedPropertyHeader = reader.next_value()?; @@ -391,7 +391,7 @@ mod tests { let value: &f32 = unsafe { object_reader.next_value() }.unwrap(); assert_eq!(*value, second_value); - assert!(object_reader.into_remaining().is_empty()); + assert!(object_reader.remaining_bytes().is_empty()); } // reading diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 3c84fda5..196aa107 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -65,7 +65,7 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) -use crate::space::reader::AtomSpaceReader; +use crate::space::reader::SpaceReader; use crate::*; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; @@ -175,7 +175,7 @@ impl TimeStamp { /// An iterator over all events in a sequence. pub struct SequenceIterator<'a> { - reader: AtomSpaceReader<'a>, + reader: SpaceReader<'a>, unit: TimeStampUnit, } diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 6dcdcf6b..e710909c 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -65,7 +65,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { return None; }; - let data = reader.into_remaining().as_bytes(); + let data = reader.remaining_bytes(); std::str::from_utf8(&data[0..data.len() - 1]) .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 18c00a82..902fbb8d 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -30,7 +30,7 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Tuple](http://lv2plug.in/ns/ext/atom/atom.html#Tuple) -use crate::space::reader::AtomSpaceReader; +use crate::space::reader::SpaceReader; use crate::*; /// An atom containing a series of other atoms. @@ -50,7 +50,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { unsafe fn read(body: &'space Space, _: ()) -> Option> { Some(TupleIterator { - reader: AtomSpaceReader::new(body), + reader: body.read(), }) } @@ -66,7 +66,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { /// /// The item of this iterator is simply the space a single atom occupies. pub struct TupleIterator<'a> { - reader: AtomSpaceReader<'a>, + reader: SpaceReader<'a>, } impl<'a> Iterator for TupleIterator<'a> { diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index 8fe6688e..858e2d93 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -2,48 +2,50 @@ use crate::prelude::Space; use crate::{AtomHeader, UnidentifiedAtom}; use std::mem::MaybeUninit; -pub struct SpaceReader<'a, T> { - space: &'a Space, +pub struct SpaceReader<'a> { + space: &'a [u8], } -impl<'a, T: 'static> SpaceReader<'a, T> { +impl<'a> SpaceReader<'a> { #[inline] - pub fn new(space: &'a Space) -> Self { + pub fn new(space: &'a [u8]) -> Self { SpaceReader { space } } #[inline] - fn next_uninit_value(&mut self) -> Option<&'a MaybeUninit> { - let space = self.space.realign()?; - let value_size = ::core::mem::size_of::(); + fn next_uninit_value(&mut self) -> Option<&'a MaybeUninit> { + let space = Space::try_align_from_bytes(self.space)?; + let value_size = ::core::mem::size_of::(); let (value, remaining) = space.split_at(value_size)?; - self.space = remaining.realign().unwrap_or_else(Space::empty); + self.space = remaining.as_bytes(); value.as_uninit() } #[inline] - fn next_uninit_value_slice( + fn next_uninit_value_slice( &mut self, length: usize, - ) -> Option<&'a [MaybeUninit]> { - let space = self.space.realign()?; - let split_point = crate::util::value_index_to_byte_index::(length); + ) -> Option<&'a [MaybeUninit]> { + let space = Space::try_align_from_bytes(self.space)?; + + let split_point = crate::util::value_index_to_byte_index::(length); let (data, remaining) = space.split_at(split_point)?; - self.space = remaining.realign().unwrap_or_else(Space::empty); + self.space = remaining.as_bytes(); Some(data.as_uninit_slice()) } #[inline] - fn as_uninit_slice(&self) -> Option<&'a [MaybeUninit]> { - Some(self.space.realign()?.as_uninit_slice()) + fn as_uninit_slice(&self) -> Option<&'a [MaybeUninit]> { + let space = Space::try_align_from_bytes(self.space)?; + Some(space.as_uninit_slice()) } #[inline] - pub unsafe fn as_slice(&self) -> Option<&'a [U]> { + pub unsafe fn as_slice(&self) -> Option<&'a [T]> { self.as_uninit_slice() .map(|s| crate::util::assume_init_slice(s)) } @@ -56,12 +58,10 @@ impl<'a, T: 'static> SpaceReader<'a, T> { #[inline] pub fn next_bytes(&mut self, length: usize) -> Option<&'a [u8]> { - let split_point = crate::util::value_index_to_byte_index::(length); - - let (data, remaining) = self.space.split_at(split_point)?; - self.space = remaining.realign().unwrap_or_else(Space::empty); + let bytes = self.space.get(..length)?; + self.space = self.space.get(length..).unwrap_or(&[]); - Some(data.as_bytes()) + Some(bytes) } #[inline] @@ -71,7 +71,19 @@ impl<'a, T: 'static> SpaceReader<'a, T> { } #[inline] - pub fn into_remaining(self) -> &'a Space { + pub unsafe fn next_atom(&mut self) -> Option<&'a UnidentifiedAtom> { + let space = Space::::try_align_from_bytes(&self.space)?; + let header = space.assume_init_value()?; + let (_, rest) = space.split_at(header.size_of_atom())?; + + let atom = UnidentifiedAtom::from_header(header); + self.space = rest.as_bytes(); + + Some(atom) + } + + #[inline] + pub fn remaining_bytes(&self) -> &'a [u8] { self.space } @@ -80,25 +92,10 @@ impl<'a, T: 'static> SpaceReader<'a, T> { where F: FnOnce(&mut Self) -> Option, { - let mut reader = self.space.read(); + let mut reader = Self { space: self.space }; let value = read_handler(&mut reader)?; - self.space = reader.into_remaining(); + self.space = reader.remaining_bytes(); Some(value) } } - -pub type AtomSpaceReader<'a> = SpaceReader<'a, AtomHeader>; - -impl<'a> AtomSpaceReader<'a> { - #[inline] - pub unsafe fn next_atom(&mut self) -> Option<&'a UnidentifiedAtom> { - let header = self.space.assume_init_value()?; - let (_, rest) = self.space.split_at(header.size_of_atom())?; - - let atom = UnidentifiedAtom::from_header(header); - self.space = rest; - - Some(atom) - } -} diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 4db91068..ce72bb89 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -131,7 +131,7 @@ impl Space { /// This method returns [`None`](Option::None) if the given slice's is too small to contain /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). #[inline] - fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { + pub fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { // SAFETY: We just aligned the slice start data.get(Self::padding_for(data)..) .map(|data| unsafe { Space::from_bytes_unchecked(data) }) @@ -144,7 +144,7 @@ impl Space { /// This method returns [`None`](Option::None) if the given slice's is too small to contain /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). #[inline] - pub(crate) fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { // SAFETY: We just aligned the slice's start data.get_mut(Self::padding_for(data)..) .map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) @@ -314,8 +314,8 @@ impl Space { } #[inline] - pub fn read(&self) -> SpaceReader { - SpaceReader::new(self) + pub fn read(&self) -> SpaceReader { + SpaceReader::new(self.as_bytes()) } #[inline] From 5853d5fdb699a66bb3303f290241f121e7fd7aad Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 7 Sep 2021 15:38:23 +0200 Subject: [PATCH 22/54] Remove default type param on Space type --- atom/src/atoms/chunk.rs | 2 +- atom/src/atoms/object.rs | 9 ++++++--- atom/src/atoms/scalar.rs | 4 ++-- atom/src/atoms/sequence.rs | 2 +- atom/src/atoms/string.rs | 4 ++-- atom/src/atoms/tuple.rs | 2 +- atom/src/atoms/vector.rs | 5 ++++- atom/src/lib.rs | 6 ++++-- atom/src/port.rs | 2 +- atom/src/space/space.rs | 22 ++++++++++++++++++---- midi/src/raw.rs | 2 +- midi/src/wmidi_binding.rs | 4 ++-- state/src/raw.rs | 6 +++--- 13 files changed, 46 insertions(+), 24 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index e109154b..24d15032 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -44,7 +44,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { type WriteHandle = AtomSpaceWriter<'handle, 'space>; #[inline] - unsafe fn read(space: &'handle Space, _: ()) -> Option<&'handle AtomSpace> { + unsafe fn read(space: &'handle AtomSpace, _: ()) -> Option<&'handle AtomSpace> { Some(space) } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index d886084a..0eb495b2 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -100,7 +100,10 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { type WriteParameter = ObjectHeader; type WriteHandle = ObjectWriter<'handle, 'space>; - unsafe fn read(body: &'handle Space, _: ()) -> Option<(ObjectHeader, ObjectReader<'handle>)> { + unsafe fn read( + body: &'handle AtomSpace, + _: (), + ) -> Option<(ObjectHeader, ObjectReader<'handle>)> { let mut reader = body.read(); let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; @@ -148,7 +151,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Blank { #[allow(clippy::unit_arg)] #[inline] unsafe fn read( - body: &'handle Space, + body: &'handle AtomSpace, parameter: Self::ReadParameter, ) -> Option { Object::read(body, parameter) @@ -391,7 +394,7 @@ mod tests { let value: &f32 = unsafe { object_reader.next_value() }.unwrap(); assert_eq!(*value, second_value); - assert!(object_reader.remaining_bytes().is_empty()); + assert_eq!(object_reader.remaining_bytes().len(), 4); } // reading diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index d1a00d8e..39bd90fe 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -47,7 +47,7 @@ pub trait ScalarAtom: UriBound { /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. #[inline] - unsafe fn read_scalar(body: &Space) -> Option { + unsafe fn read_scalar(body: &AtomSpace) -> Option { body.read().next_value().copied() } @@ -72,7 +72,7 @@ impl<'handle, 'space: 'handle, A: ScalarAtom> Atom<'handle, 'space> for A { type WriteParameter = A::InternalType; type WriteHandle = (); - unsafe fn read(body: &'space Space, _: ()) -> Option { + unsafe fn read(body: &'space AtomSpace, _: ()) -> Option { ::read_scalar(body) } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 196aa107..661b05ca 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -93,7 +93,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { type WriteParameter = TimeStampURID; type WriteHandle = SequenceWriter<'handle, 'space>; - unsafe fn read(body: &Space, bpm_urid: URID) -> Option { + unsafe fn read(body: &AtomSpace, bpm_urid: URID) -> Option { let mut reader = body.read(); let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index e710909c..829f9d81 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -53,7 +53,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { type WriteParameter = LiteralInfo; type WriteHandle = StringWriter<'handle, 'space>; - unsafe fn read(body: &'handle Space, _: ()) -> Option<(LiteralInfo, &'handle str)> { + unsafe fn read(body: &'handle AtomSpace, _: ()) -> Option<(LiteralInfo, &'handle str)> { let mut reader = body.read(); let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; @@ -109,7 +109,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for String { type WriteParameter = (); type WriteHandle = StringWriter<'handle, 'space>; - unsafe fn read(body: &'space Space, _: ()) -> Option<&'handle str> { + unsafe fn read(body: &'space AtomSpace, _: ()) -> Option<&'handle str> { let data = body.as_bytes(); let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator Some(core::str::from_utf8(rust_str_bytes).ok()?) diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 902fbb8d..f1067409 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -48,7 +48,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { type WriteParameter = (); type WriteHandle = TupleWriter<'handle, 'space>; - unsafe fn read(body: &'space Space, _: ()) -> Option> { + unsafe fn read(body: &'space AtomSpace, _: ()) -> Option> { Some(TupleIterator { reader: body.read(), }) diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index a8cbdfce..be2e0a6b 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -55,7 +55,10 @@ where type WriteParameter = URID; type WriteHandle = VectorWriter<'handle, 'space, C>; - unsafe fn read(body: &'space Space, child_urid: URID) -> Option<&'space [C::InternalType]> { + unsafe fn read( + body: &'space AtomSpace, + child_urid: URID, + ) -> Option<&'space [C::InternalType]> { let mut reader = body.read(); let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 851c5fcb..dbb034bc 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -128,8 +128,10 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: &'space Space, parameter: Self::ReadParameter) - -> Option; + unsafe fn read( + body: &'space AtomSpace, + parameter: Self::ReadParameter, + ) -> Option; /// Initialize the body of the atom. /// diff --git a/atom/src/port.rs b/atom/src/port.rs index a4a7f771..bcef61e5 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -69,7 +69,7 @@ pub struct PortWriter<'a> { impl<'a> PortWriter<'a> { /// Create a new port writer. - fn new(space: &'a mut Space) -> Self { + fn new(space: &'a mut AtomSpace) -> Self { Self { space: SpaceCursor::new(space.as_bytes_mut()), has_been_written: false, diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index ce72bb89..00619de3 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -6,11 +6,16 @@ use std::marker::PhantomData; use std::mem::{size_of_val, MaybeUninit}; use std::slice::{from_raw_parts, from_raw_parts_mut}; -/// An aligned slice of bytes that is designed to contain a given type `T` (by default, Atoms). +/// An slice of bytes with the alignment of a type `T`. /// -/// The accessor methods of this struct all behave in a similar way: If the internal slice is big enough, they create a reference to the start of the slice with the desired type and create a new space object that contains the space after the references instance. +/// This type is a simple byte slice that guarantees its start is properly aligned for containing a type `T`. +/// This buffer can be split and realigned, effectively allowing to read a stream of arbitrary types +/// from a byte buffer, such as [`Atom`s](crate::atom::Atom). +/// +/// Note that only the start of the slice is aligned, not the end. This allows having a buffer that +/// is bigger than a multiple of `T`'s alignment. #[repr(transparent)] -pub struct Space { +pub struct Space { _type: PhantomData, // Note: this could be [MaybeUninit] for alignment, but Spaces can have extra unaligned bytes at the end. data: [u8], @@ -19,7 +24,16 @@ pub struct Space { pub type AtomSpace = Space; impl Space { - /// Creates an empty Space. + /// Creates a space from an empty slice. + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::prelude::Space; + /// + /// let space = Space::::empty(); + /// assert!(space.as_bytes().is_empty()); + /// ``` #[inline] pub fn empty<'a>() -> &'a Space { // SAFETY: empty slices are always aligned diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 2cefc2b8..59ef2f38 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -21,7 +21,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for MidiEvent { type WriteParameter = (); type WriteHandle = AtomSpaceWriter<'handle, 'space>; - unsafe fn read(body: &'handle Space, _: ()) -> Option<&'handle [u8]> { + unsafe fn read(body: &'handle AtomSpace, _: ()) -> Option<&'handle [u8]> { Some(body.as_bytes()) } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 42370904..f371a9e4 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -26,7 +26,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for WMidiEvent { type WriteParameter = wmidi::MidiMessage<'handle>; type WriteHandle = (); - unsafe fn read(space: &'handle Space, _: ()) -> Option> { + unsafe fn read(space: &'handle AtomSpace, _: ()) -> Option> { wmidi::MidiMessage::try_from(space.as_bytes()).ok() } @@ -58,7 +58,7 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for SystemExclusiveWMidiEve type WriteParameter = (); type WriteHandle = Writer<'handle, 'space>; - unsafe fn read(space: &'handle Space, _: ()) -> Option> { + unsafe fn read(space: &'handle AtomSpace, _: ()) -> Option> { WMidiEvent::read(space, ()) } diff --git a/state/src/raw.rs b/state/src/raw.rs index 801a6127..355b5005 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -187,12 +187,12 @@ impl<'a> RetrieveHandle<'a> { /// This handle contains the type and the data of a property retrieved from the [`RetrieveHandle`](struct.RetrieveHandle.html). pub struct StatePropertyReader<'a> { type_: URID, - body: &'a Space, + body: &'a AtomSpace, } impl<'a> StatePropertyReader<'a> { /// Create a new reading handle with the given type and data. - pub fn new(type_: URID, body: &'a Space) -> Self { + pub fn new(type_: URID, body: &'a AtomSpace) -> Self { Self { type_: type_.into_general(), body, @@ -205,7 +205,7 @@ impl<'a> StatePropertyReader<'a> { } /// Return the data of the property. - pub fn body(&self) -> &Space { + pub fn body(&self) -> &AtomSpace { self.body } From b486dc478e98451620c206d5e7f0072b44ce20e8 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 8 Sep 2021 15:01:04 +0200 Subject: [PATCH 23/54] Add lots of docs --- atom/src/lib.rs | 2 +- atom/src/space.rs | 2 +- atom/src/space/allocatable.rs | 12 +- atom/src/space/atom_writer.rs | 13 +- atom/src/space/reader.rs | 10 +- atom/src/space/space.rs | 302 ++++++++++++++++++++++------------ atom/src/space/vec.rs | 17 +- atom/src/util.rs | 10 ++ state/src/raw.rs | 8 +- 9 files changed, 242 insertions(+), 134 deletions(-) diff --git a/atom/src/lib.rs b/atom/src/lib.rs index dbb034bc..6808a4f8 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -86,7 +86,7 @@ pub mod prelude { pub use atoms::tuple::Tuple; pub use atoms::vector::Vector; pub use port::AtomPort; - pub use space::{AtomSpace, AtomSpaceWriter, Space, SpaceAllocator}; + pub use space::{AlignedSpace, AtomSpace, AtomSpaceWriter, SpaceAllocator}; use crate::*; pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; diff --git a/atom/src/space.rs b/atom/src/space.rs index 78326463..e01cd31c 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -10,5 +10,5 @@ mod vec; pub use allocatable::*; pub use atom_writer::AtomSpaceWriter; pub use cursor::SpaceCursor; -pub use space::{AtomSpace, Space}; +pub use space::{AlignedSpace, AtomSpace}; pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 3c7f7a70..9667f46a 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -1,4 +1,4 @@ -use crate::space::{AtomSpaceWriter, Space}; +use crate::space::{AlignedSpace, AtomSpaceWriter}; use crate::{Atom, UnidentifiedAtom}; use urid::URID; @@ -35,7 +35,7 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { #[inline] fn allocate_padding_for(&mut self) -> Option<()> { - let required_padding = Space::::padding_for(self.remaining_bytes()); + let required_padding = crate::util::padding_for::(self.remaining_bytes())?; self.allocate(required_padding)?; Some(()) @@ -45,14 +45,14 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { fn allocate_aligned<'handle, T: 'static>( &'handle mut self, size: usize, - ) -> Option<&'handle mut Space> + ) -> Option<&'handle mut AlignedSpace> where 'space: 'handle, { - let required_padding = Space::::padding_for(self.remaining_bytes()); + let required_padding = crate::util::padding_for::(self.remaining_bytes())?; let raw = self.allocate(size + required_padding)?; - Space::try_align_from_bytes_mut(raw) + AlignedSpace::align_from_bytes_mut(raw) } #[inline] @@ -126,7 +126,7 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { T: Copy + Sized + 'static, //'space: 'handle, { - let space: &mut Space = self.allocate_aligned(size_of_val(values))?; + let space: &mut AlignedSpace = self.allocate_aligned(size_of_val(values))?; let space = space.as_uninit_slice_mut(); for (dst, src) in space.iter_mut().zip(values.iter()) { diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 3fe4a1a3..70076dbf 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,5 +1,5 @@ use crate::header::AtomHeader; -use crate::space::{Space, SpaceAllocator, SpaceAllocatorImpl}; +use crate::space::{AlignedSpace, SpaceAllocator, SpaceAllocatorImpl}; use urid::URID; /// A `MutSpace` that tracks the amount of allocated space in an atom header. @@ -16,7 +16,7 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { .allocated_bytes() .get(self.atom_header_index..) .unwrap(); - let space = Space::try_from_bytes(previous).unwrap(); + let space = AlignedSpace::try_from_bytes(previous).unwrap(); unsafe { *space.assume_init_value().unwrap() } } @@ -27,7 +27,7 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { .allocated_bytes_mut() .get_mut(self.atom_header_index..) .unwrap(); - let space = Space::::try_from_bytes_mut(previous).unwrap(); + let space = AlignedSpace::::try_from_bytes_mut(previous).unwrap(); unsafe { space.assume_init_value_mut().unwrap() } } @@ -54,8 +54,9 @@ impl<'handle, 'space: 'handle> SpaceAllocatorImpl<'space> for AtomSpaceWriter<'h fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (previous, current) = self.parent.allocate_and_split(size)?; - let space = - Space::::try_from_bytes_mut(previous.get_mut(self.atom_header_index..)?)?; + let space = AlignedSpace::::try_from_bytes_mut( + previous.get_mut(self.atom_header_index..)?, + )?; let header = unsafe { space.assume_init_value_mut() }?; // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated @@ -103,9 +104,9 @@ mod tests { use crate::prelude::AtomSpaceWriter; use crate::space::cursor::SpaceCursor; use crate::space::{SpaceAllocator, VecSpace}; + use crate::AtomHeader; use core::mem::size_of; use urid::URID; - use crate::AtomHeader; #[test] fn test_padding_inside_frame() { diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index 858e2d93..84c59d7a 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -1,4 +1,4 @@ -use crate::prelude::Space; +use crate::prelude::AlignedSpace; use crate::{AtomHeader, UnidentifiedAtom}; use std::mem::MaybeUninit; @@ -14,7 +14,7 @@ impl<'a> SpaceReader<'a> { #[inline] fn next_uninit_value(&mut self) -> Option<&'a MaybeUninit> { - let space = Space::try_align_from_bytes(self.space)?; + let space = AlignedSpace::align_from_bytes(self.space)?; let value_size = ::core::mem::size_of::(); let (value, remaining) = space.split_at(value_size)?; @@ -28,7 +28,7 @@ impl<'a> SpaceReader<'a> { &mut self, length: usize, ) -> Option<&'a [MaybeUninit]> { - let space = Space::try_align_from_bytes(self.space)?; + let space = AlignedSpace::align_from_bytes(self.space)?; let split_point = crate::util::value_index_to_byte_index::(length); let (data, remaining) = space.split_at(split_point)?; @@ -40,7 +40,7 @@ impl<'a> SpaceReader<'a> { #[inline] fn as_uninit_slice(&self) -> Option<&'a [MaybeUninit]> { - let space = Space::try_align_from_bytes(self.space)?; + let space = AlignedSpace::align_from_bytes(self.space)?; Some(space.as_uninit_slice()) } @@ -72,7 +72,7 @@ impl<'a> SpaceReader<'a> { #[inline] pub unsafe fn next_atom(&mut self) -> Option<&'a UnidentifiedAtom> { - let space = Space::::try_align_from_bytes(&self.space)?; + let space = AlignedSpace::::align_from_bytes(&self.space)?; let header = space.assume_init_value()?; let (_, rest) = space.split_at(header.size_of_atom())?; diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 00619de3..7f79f877 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -10,45 +10,187 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// /// This type is a simple byte slice that guarantees its start is properly aligned for containing a type `T`. /// This buffer can be split and realigned, effectively allowing to read a stream of arbitrary types -/// from a byte buffer, such as [`Atom`s](crate::atom::Atom). +/// from a byte buffer, such as [`Atom`s](crate::Atom). +/// +/// Any operation that may lead to a misaligned `Space` is considered unsafe. /// /// Note that only the start of the slice is aligned, not the end. This allows having a buffer that /// is bigger than a multiple of `T`'s alignment. +/// +/// Although they are aligned, `Space`s do not consider their contents to be initialized. Therefore, +/// the only safe reading operations will return `MaybeUninit`. Unsafe helper methods that assume +/// the contents are initialized are also provided, for convenience. +/// +/// # Example +/// +/// The following example reads `u64`s from an aligned byte slice. +/// +/// ``` +/// # use lv2_atom::space::AlignedSpace; +/// let values = &[42u64, 69]; +/// // Transmuting to a slice of bytes +/// let bytes: &[u8] = unsafe { core::slice::from_ref(&value).align_to().1 }; +/// +/// // --- +/// +/// // Bytes are already aligned, the whole slice will be available +/// let space: &AlignedSpace = AlignedSpace::align_from_bytes(bytes).unwrap(); +/// // SAFETY: we know the slice was initialized with proper u64 values. +/// let read_values = unsafe { space.assume_init_slice() }; +/// assert_eq!(read_values, [42u64, 69]); +/// ``` +/// #[repr(transparent)] -pub struct Space { +pub struct AlignedSpace { _type: PhantomData, - // Note: this could be [MaybeUninit] for alignment, but Spaces can have extra unaligned bytes at the end. data: [u8], } -pub type AtomSpace = Space; +pub type AtomSpace = AlignedSpace; -impl Space { - /// Creates a space from an empty slice. +impl AlignedSpace { + /// Creates a new space from a slice of bytes. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's offset is not aligned + /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). /// /// # Example /// /// ``` - /// # use lv2_atom::prelude::Space; + /// # use lv2_atom::space::AlignedSpace; + /// let values = &[42u64, 69]; + /// // Transmuting to a slice of bytes + /// let bytes: &[u8] = unsafe { value.align_to().1 }; /// - /// let space = Space::::empty(); + /// assert!(AlignedSpace::::try_from_bytes(bytes).is_some()); + /// assert!(AlignedSpace::::try_from_bytes(&bytes[1..]).is_none()); + /// ``` + #[inline] + pub fn try_from_bytes(data: &[u8]) -> Option<&Self> { + if data.as_ptr() as usize % align_of::() != 0 { + return None; + } + + // SAFETY: We just checked above that the pointer is correctly aligned + Some(unsafe { AlignedSpace::from_bytes_unchecked(data) }) + } + + /// Creates a new mutable space from a mutable slice of bytes. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's offset is not aligned + /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::space::AlignedSpace; + /// let values = &mut [42u64, 69]; + /// // Transmuting to a slice of bytes + /// let bytes: &mut [u8] = unsafe { value.align_to_mut().1 }; + /// + /// assert!(AlignedSpace::::try_from_bytes_mut(bytes).is_some()); + /// assert!(AlignedSpace::::try_from_bytes_mut(&mut bytes[1..]).is_none()); + /// ``` + #[inline] + pub fn try_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + if data.as_ptr() as usize % align_of::() != 0 { + return None; + } + + // SAFETY: We just checked above that the pointer is correctly aligned + Some(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) + } + + /// Creates a new space from a slice of bytes, slicing some bytes off its start it if necessary. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's is too small to contain + /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::space::AlignedSpace; + /// let values = &[42u64, 69]; + /// // Transmuting to a slice of bytes + /// let bytes: &[u8] = unsafe { value.align_to().1 }; + /// + /// // The slice has space for both values + /// assert_eq!(AlignedSpace::::align_from_bytes(bytes).unwrap().values_len(), 2); + /// // The slice now only has space for a single value + /// assert_eq!(AlignedSpace::::align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); + /// // The slice doesn't have space for any value anymore + /// assert!(AlignedSpace::::align_from_bytes(&bytes[9..]).is_none()); + /// ``` + #[inline] + pub fn align_from_bytes(data: &[u8]) -> Option<&Self> { + // SAFETY: We just aligned the slice start + data.get(crate::util::padding_for::(data)?..) + .map(|data| unsafe { AlignedSpace::from_bytes_unchecked(data) }) + } + + /// Creates a new mutable space from a mutable slice of bytes, slicing some bytes off its start it if necessary. + /// + /// # Errors + /// + /// This method returns [`None`](Option::None) if the given slice's is too small to contain + /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::space::AlignedSpace; + /// let values = &mut [42u64, 69]; + /// // Transmuting to a slice of bytes + /// let bytes: &mut [u8] = unsafe { value.align_to_mut().1 }; + /// + /// // The slice has space for both values + /// assert_eq!(AlignedSpace::::align_from_bytes_mut(bytes).unwrap().values_len(), 2); + /// // The slice now only has space for a single value + /// assert_eq!(AlignedSpace::::align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); + /// // The slice doesn't have space for any value anymore + /// assert!(AlignedSpace::::align_from_bytes_mut(&mut bytes[9..]).is_none()); + /// ``` + #[inline] + pub fn align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + // SAFETY: We just aligned the slice's start + data.get_mut(crate::util::padding_for::(data)?..) + .map(|data| unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) + } + + /// Creates a space from an empty slice. + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::prelude::AlignedSpace; + /// let space = AlignedSpace::::empty(); /// assert!(space.as_bytes().is_empty()); /// ``` #[inline] - pub fn empty<'a>() -> &'a Space { + pub fn empty<'a>() -> &'a AlignedSpace { // SAFETY: empty slices are always aligned unsafe { Self::from_bytes_unchecked(&[]) } } + /// Creates an empty mutable space. + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::prelude::AlignedSpace; + /// let space = AlignedSpace::::empty_mut(); + /// assert!(space.as_bytes().is_empty()); + /// ``` #[inline] - pub(crate) fn padding_for(data: &[u8]) -> usize { - let alignment = align_of::(); - let start = data.as_ptr() as usize; - if start % alignment == 0 { - 0 - } else { - alignment - start % alignment - } + pub fn empty_mut<'a>() -> &'a mut AlignedSpace { + // SAFETY: empty slices are always aligned + unsafe { Self::from_bytes_mut_unchecked(&mut []) } } /// Creates a new space from a slice of bytes, without checking for padding correctness. @@ -56,13 +198,13 @@ impl Space { /// # Safety /// /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. - /// Otherwise, reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. + /// Calling this method with an unaligned slice will result from UB. /// /// For a safe, checked version, see [`Space::try_from_bytes`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] - pub unsafe fn from_bytes_unchecked(data: &[u8]) -> &Space { + pub unsafe fn from_bytes_unchecked(data: &[u8]) -> &AlignedSpace { // SAFETY: It is safe to transmute, since our type has repr(transparent) with [u8]. // SAFETY: The caller is responsible to check for slice alignment. &*(data as *const _ as *const Self) @@ -79,22 +221,24 @@ impl Space { // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] - pub unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut Space { + pub unsafe fn from_bytes_mut_unchecked(data: &mut [u8]) -> &mut AlignedSpace { // SAFETY: It is safe to transmute, since our type has repr(transparent) with [u8]. // SAFETY: The caller is responsible to check for slice alignment. &mut *(data as *mut _ as *mut Self) } + /// Creates a new space from an already aligned slice of T values. #[inline] - pub(crate) fn from_uninit_slice(slice: &[MaybeUninit]) -> &Self { + pub fn from_slice(slice: &[T]) -> &Self { // SAFETY: reinterpreting as raw bytes is safe for any value let bytes = unsafe { from_raw_parts(slice.as_ptr() as *const u8, size_of_val(slice)) }; // SAFETY: The pointer is a slice of T, therefore it is already correctly aligned unsafe { Self::from_bytes_unchecked(bytes) } } + /// Creates a new mutable space from an already aligned slice of T values. #[inline] - pub(crate) fn from_uninit_slice_mut(slice: &mut [MaybeUninit]) -> &mut Self { + pub fn from_slice_mut(slice: &mut [T]) -> &mut Self { // SAFETY: reinterpreting as raw bytes is safe for any value let bytes = unsafe { from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of_val(slice)) }; @@ -102,68 +246,26 @@ impl Space { unsafe { Self::from_bytes_mut_unchecked(bytes) } } - /// Creates a new space from a slice of bytes. - /// - /// # Errors - /// - /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned - /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). - /// - /// This is the non-panicking version of [`Space::from_bytes`]. - #[inline] - pub fn try_from_bytes(data: &[u8]) -> Option<&Self> { - if data.as_ptr() as usize % align_of::() != 0 { - return None; - } - - // SAFETY: We just checked above that the pointer is correctly aligned - Some(unsafe { Space::from_bytes_unchecked(data) }) - } - - /// Creates a new mutable space from a mutable slice of bytes. - /// - /// # Errors - /// - /// This method returns [`None`](Option::None) if the given slice's offset is not 64-bit aligned - /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). - /// - /// This is the non-panicking version of [`Space::from_bytes`]. - #[inline] - pub fn try_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { - if data.as_ptr() as usize % align_of::() != 0 { - return None; - } - - // SAFETY: We just checked above that the pointer is correctly aligned - Some(unsafe { Space::from_bytes_mut_unchecked(data) }) - } - - /// Creates a new space from a slice of bytes, aligning it if necessary. - /// - /// # Errors - /// - /// This method returns [`None`](Option::None) if the given slice's is too small to contain - /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + /// Creates a new space from an already aligned, potentially uninitialized slice of T. #[inline] - pub fn try_align_from_bytes(data: &[u8]) -> Option<&Self> { - // SAFETY: We just aligned the slice start - data.get(Self::padding_for(data)..) - .map(|data| unsafe { Space::from_bytes_unchecked(data) }) + pub(crate) fn from_uninit_slice(slice: &[MaybeUninit]) -> &Self { + // SAFETY: reinterpreting as raw bytes is safe for any value + let bytes = unsafe { from_raw_parts(slice.as_ptr() as *const u8, size_of_val(slice)) }; + // SAFETY: The pointer is a slice of T, therefore it is already correctly aligned + unsafe { Self::from_bytes_unchecked(bytes) } } - /// Creates a new space from a slice of bytes, aligning it if necessary. - /// - /// # Errors - /// - /// This method returns [`None`](Option::None) if the given slice's is too small to contain - /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + /// Creates a new space from an already aligned, potentially uninitialized slice of T. #[inline] - pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { - // SAFETY: We just aligned the slice's start - data.get_mut(Self::padding_for(data)..) - .map(|data| unsafe { Space::from_bytes_mut_unchecked(data) }) + pub(crate) fn from_uninit_slice_mut(slice: &mut [MaybeUninit]) -> &mut Self { + // SAFETY: reinterpreting as raw bytes is safe for any value + let bytes = + unsafe { from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of_val(slice)) }; + // SAFETY: The pointer is a slice of T, therefore it is already correctly aligned + unsafe { Self::from_bytes_mut_unchecked(bytes) } } + /// A checked version of slice::split_at, which returns the first part as an already-aligned slice. #[inline] fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { if mid > self.data.len() { @@ -183,29 +285,29 @@ impl Space { #[inline] pub fn split_at(&self, mid: usize) -> Option<(&Self, &Self)> { let (start, end) = self.split_bytes_at(mid)?; - let end = Self::try_align_from_bytes(end).unwrap_or_else(Space::empty); + let end = Self::align_from_bytes(end).unwrap_or_else(AlignedSpace::empty); Some((start, end)) } #[inline] - pub fn realign(&self) -> Option<&Space> { - Space::::try_align_from_bytes(self.as_bytes()) + pub fn realign(&self) -> Option<&AlignedSpace> { + AlignedSpace::::align_from_bytes(self.as_bytes()) } #[inline] - pub fn realign_mut(&mut self) -> Option<&mut Space> { - Space::::try_align_from_bytes_mut(self.as_bytes_mut()) + pub fn realign_mut(&mut self) -> Option<&mut AlignedSpace> { + AlignedSpace::::align_from_bytes_mut(self.as_bytes_mut()) } #[inline] - pub fn aligned(&self) -> Option<&Space> { - Space::::try_from_bytes(self.as_bytes()) + pub fn aligned(&self) -> Option<&AlignedSpace> { + AlignedSpace::::try_from_bytes(self.as_bytes()) } #[inline] - pub fn aligned_mut(&mut self) -> Option<&mut Space> { - Space::::try_from_bytes_mut(self.as_bytes_mut()) + pub fn aligned_mut(&mut self) -> Option<&mut AlignedSpace> { + AlignedSpace::::try_from_bytes_mut(self.as_bytes_mut()) } /// Return the internal slice of the space. @@ -214,14 +316,10 @@ impl Space { &self.data } + /// Returns the internal slice of the space. #[inline] - pub fn as_ptr(&self) -> *const u8 { - self.data.as_ptr() - } - - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.data.as_mut_ptr() + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.data } #[inline] @@ -230,14 +328,16 @@ impl Space { } #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 + pub fn values_len(&self) -> usize { + self.data + .len() + .checked_div(size_of::()) + .unwrap_or(usize::MAX) } - /// Return the internal slice of the space. #[inline] - pub fn as_bytes_mut(&mut self) -> &mut [u8] { - &mut self.data + pub fn is_empty(&self) -> bool { + self.data.is_empty() } #[inline] @@ -355,7 +455,7 @@ mod tests { } unsafe { - let ptr = space.as_space_mut().as_mut_ptr().add(128) as *mut u32; + let ptr = space.as_space_mut().as_bytes_mut().as_mut_ptr().add(128) as *mut u32; *(ptr) = 0x42424242; } @@ -378,7 +478,7 @@ mod tests { // Writing an integer atom. unsafe { - *(space.as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { + *(space.as_bytes_mut().as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { atom: sys::LV2_Atom { size: size_of::() as u32, type_: urid.get(), diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 6bd47775..c991c3d7 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] -use crate::space::{Space, SpaceAllocatorImpl}; +use crate::space::{AlignedSpace, SpaceAllocatorImpl}; use std::mem::MaybeUninit; use std::ops::Range; @@ -22,13 +22,13 @@ impl VecSpace { } #[inline] - pub fn as_space(&self) -> &Space { - Space::from_uninit_slice(&self.inner) + pub fn as_space(&self) -> &AlignedSpace { + AlignedSpace::from_uninit_slice(&self.inner) } #[inline] - pub fn as_space_mut(&mut self) -> &mut Space { - Space::from_uninit_slice_mut(&mut self.inner) + pub fn as_space_mut(&mut self) -> &mut AlignedSpace { + AlignedSpace::from_uninit_slice_mut(&mut self.inner) } #[inline] @@ -42,10 +42,7 @@ impl VecSpace { } #[inline] - fn get_or_allocate_bytes_mut( - &mut self, - byte_range: Range, - ) -> Option<(&mut [u8], &mut [u8])> { + fn reallocate_bytes_mut(&mut self, byte_range: Range) -> Option<(&mut [u8], &mut [u8])> { let byte_len = self.inner.len() * std::mem::size_of::(); let max = byte_range.start.max(byte_range.end); @@ -81,7 +78,7 @@ pub struct VecSpaceCursor<'vec, T> { impl<'vec, T: Copy + 'static> SpaceAllocatorImpl<'vec> for VecSpaceCursor<'vec, T> { fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let end = self.allocated_length.checked_add(size)?; - let result = VecSpace::::get_or_allocate_bytes_mut(self.vec, self.allocated_length..end); + let result = VecSpace::::reallocate_bytes_mut(self.vec, self.allocated_length..end); if result.is_some() { self.allocated_length = end; diff --git a/atom/src/util.rs b/atom/src/util.rs index 4908c25b..64c99646 100644 --- a/atom/src/util.rs +++ b/atom/src/util.rs @@ -33,3 +33,13 @@ pub(crate) fn byte_index_to_value_index(size: usize) -> usize { size / type_size + if size % type_size > 0 { 1 } else { 0 } } } + +#[inline] +pub(crate) fn padding_for(data: &[u8]) -> Option { + let value = data.as_ptr().align_offset(::core::mem::align_of::()); + if value == usize::MAX { + None + } else { + Some(value) + } +} diff --git a/state/src/raw.rs b/state/src/raw.rs index 355b5005..9461bd76 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -57,7 +57,7 @@ impl<'a> StoreHandle<'a> { let atom = unsafe { space.read().next_atom() }.ok_or(StateErr::BadData)?; let key = key.get(); - let data_ptr = atom.body().as_ptr() as *const c_void; + let data_ptr = atom.body().as_bytes().as_ptr() as *const c_void; let data_size = atom.header().size_of_body(); let data_type = atom.header().urid().get(); let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD @@ -177,7 +177,7 @@ impl<'a> RetrieveHandle<'a> { Ok(StatePropertyReader::new( type_, - Space::try_from_bytes(space).ok_or(StateErr::BadData)?, + AlignedSpace::try_from_bytes(space).ok_or(StateErr::BadData)?, )) } } @@ -231,7 +231,7 @@ impl<'a> StatePropertyReader<'a> { mod tests { use crate::raw::*; use crate::storage::Storage; - use atom::space::Space; + use atom::space::AlignedSpace; fn store(storage: &mut Storage, urids: &AtomURIDCollection) { let mut store_handle = storage.store_handle(); @@ -312,7 +312,7 @@ mod tests { } 3 => { assert_eq!(urids.vector::(), *type_); - let space = Space::try_from_bytes(value.as_slice()).unwrap(); + let space = AlignedSpace::try_from_bytes(value.as_slice()).unwrap(); let data = unsafe { Vector::read(space, urids.int) }.unwrap(); assert_eq!([1, 2, 3, 4], data); } From 0165d69e241f7ee1799f0f170eecb4761f1a0ced Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 8 Sep 2021 16:07:02 +0200 Subject: [PATCH 24/54] Some more tests and fixes --- atom/src/space/space.rs | 63 ++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 7f79f877..f0b04c2a 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -29,7 +29,7 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// # use lv2_atom::space::AlignedSpace; /// let values = &[42u64, 69]; /// // Transmuting to a slice of bytes -/// let bytes: &[u8] = unsafe { core::slice::from_ref(&value).align_to().1 }; +/// let bytes: &[u8] = unsafe { values.align_to().1 }; /// /// // --- /// @@ -62,7 +62,7 @@ impl AlignedSpace { /// # use lv2_atom::space::AlignedSpace; /// let values = &[42u64, 69]; /// // Transmuting to a slice of bytes - /// let bytes: &[u8] = unsafe { value.align_to().1 }; + /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// /// assert!(AlignedSpace::::try_from_bytes(bytes).is_some()); /// assert!(AlignedSpace::::try_from_bytes(&bytes[1..]).is_none()); @@ -90,7 +90,7 @@ impl AlignedSpace { /// # use lv2_atom::space::AlignedSpace; /// let values = &mut [42u64, 69]; /// // Transmuting to a slice of bytes - /// let bytes: &mut [u8] = unsafe { value.align_to_mut().1 }; + /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; /// /// assert!(AlignedSpace::::try_from_bytes_mut(bytes).is_some()); /// assert!(AlignedSpace::::try_from_bytes_mut(&mut bytes[1..]).is_none()); @@ -118,14 +118,14 @@ impl AlignedSpace { /// # use lv2_atom::space::AlignedSpace; /// let values = &[42u64, 69]; /// // Transmuting to a slice of bytes - /// let bytes: &[u8] = unsafe { value.align_to().1 }; + /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// /// // The slice has space for both values /// assert_eq!(AlignedSpace::::align_from_bytes(bytes).unwrap().values_len(), 2); /// // The slice now only has space for a single value /// assert_eq!(AlignedSpace::::align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert!(AlignedSpace::::align_from_bytes(&bytes[9..]).is_none()); + /// assert!(AlignedSpace::::align_from_bytes(&bytes[10..]).is_none()); /// ``` #[inline] pub fn align_from_bytes(data: &[u8]) -> Option<&Self> { @@ -147,14 +147,14 @@ impl AlignedSpace { /// # use lv2_atom::space::AlignedSpace; /// let values = &mut [42u64, 69]; /// // Transmuting to a slice of bytes - /// let bytes: &mut [u8] = unsafe { value.align_to_mut().1 }; + /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; /// /// // The slice has space for both values /// assert_eq!(AlignedSpace::::align_from_bytes_mut(bytes).unwrap().values_len(), 2); /// // The slice now only has space for a single value /// assert_eq!(AlignedSpace::::align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert!(AlignedSpace::::align_from_bytes_mut(&mut bytes[9..]).is_none()); + /// assert!(AlignedSpace::::align_from_bytes_mut(&mut bytes[10..]).is_none()); /// ``` #[inline] pub fn align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { @@ -446,28 +446,33 @@ mod tests { use urid::*; #[test] - fn test_space() { - let mut space = VecSpace::::new_with_capacity(64); - let bytes = space.as_bytes_mut(); - - for i in 0..128 { - bytes[i] = i as u8; - } - - unsafe { - let ptr = space.as_space_mut().as_bytes_mut().as_mut_ptr().add(128) as *mut u32; - *(ptr) = 0x42424242; - } - - let (lower_space, space) = space.as_space_mut().split_at(128).unwrap(); - let lower_space = lower_space.as_bytes(); - - for i in 0..128 { - assert_eq!(lower_space[i], i as u8); - } - - let integer = unsafe { space.assume_init_value() }.unwrap(); - assert_eq!(*integer, 0x42424242); + fn align_from_bytes() { + let values = &mut [42u64, 69]; + let bytes = unsafe { values.align_to_mut().1 }; + + assert_eq!( + AlignedSpace::::try_from_bytes(bytes).unwrap().len(), + ::core::mem::size_of::() * 2 + ); + assert_eq!( + AlignedSpace::::try_from_bytes_mut(bytes) + .unwrap() + .len(), + ::core::mem::size_of::() * 2 + ); + + assert_eq!( + unsafe { AlignedSpace::::from_bytes_unchecked(bytes) }.len(), + ::core::mem::size_of::() * 2 + ); + + assert_eq!( + unsafe { AlignedSpace::::from_bytes_mut_unchecked(bytes) }.len(), + ::core::mem::size_of::() * 2 + ); + + assert!(AlignedSpace::::try_from_bytes(&bytes[1..]).is_none()); + assert!(AlignedSpace::::try_from_bytes_mut(&mut bytes[1..]).is_none()); } #[test] From f572097edc2901f2f33b1bfb998e16a93d4bdfa1 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 9 Sep 2021 16:48:37 +0200 Subject: [PATCH 25/54] WIP --- atom/src/atoms/chunk.rs | 5 +++-- atom/src/atoms/object.rs | 18 ++++++++++++++---- atom/src/lib.rs | 22 +++++++++++++--------- atom/src/space.rs | 2 +- atom/src/space/atom_writer.rs | 7 +++++++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 24d15032..fc5cac92 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -26,6 +26,7 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Chunk](http://lv2plug.in/ns/ext/atom/atom.html#Chunk) use crate::space::*; use crate::Atom; +use crate::AtomSpaceWriter; use urid::UriBound; /// An atom containing memory of undefined type. @@ -37,11 +38,11 @@ unsafe impl UriBound for Chunk { const URI: &'static [u8] = sys::LV2_ATOM__Chunk; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Chunk { +impl<'handle, 'space: 'handle> Atom for Chunk { type ReadParameter = (); type ReadHandle = &'handle AtomSpace; type WriteParameter = (); - type WriteHandle = AtomSpaceWriter<'handle, 'space>; + type WriteHandle = AtomSpaceWriterHandle; #[inline] unsafe fn read(space: &'handle AtomSpace, _: ()) -> Option<&'handle AtomSpace> { diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 0eb495b2..8c41b52b 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -94,13 +94,23 @@ pub struct ObjectHeader { pub otype: URID, } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Object { +struct ObjectReaderHandle; +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { + type Handle = AtomSpaceWriter<'handle, 'space>; +} + +struct ObjectWriterHandle; +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { + type Handle = AtomSpaceWriter<'handle, 'space>; +} + +impl Atom for Object { type ReadParameter = (); - type ReadHandle = (ObjectHeader, ObjectReader<'handle>); + type ReadHandle = ObjectReaderHandle; type WriteParameter = ObjectHeader; - type WriteHandle = ObjectWriter<'handle, 'space>; + type WriteHandle = ObjectWriterHandle; - unsafe fn read( + unsafe fn read<'handle, 'space: 'handle>( body: &'handle AtomSpace, _: (), ) -> Option<(ObjectHeader, ObjectReader<'handle>)> { diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 6808a4f8..df01a11c 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -92,12 +92,16 @@ pub mod prelude { pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; } +pub trait AtomHandle<'handle, 'space: 'handle> { + type Handle: 'handle; +} + /// Atom type. /// /// This is the foundation of this crate: Types that implement `Atom` define the reading and writing functions for an atom type. However, these types will never be constructed; They are only names to be used for generic type arguments. /// /// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom. -pub trait Atom<'handle, 'space: 'handle>: UriBound { +pub trait Atom: UriBound { /// The atom-specific parameter of the `read` function. /// /// If your atom does not need a reading parameter, you may set it to `()`. @@ -106,7 +110,7 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { /// The return value of the `read` function. /// /// It may contain a reference to the atom and therefore may not outlive it. - type ReadHandle: 'handle; + type ReadHandle: for<'handle, 'space> AtomHandle<'handle, 'space>; /// The atom-specific parameter of the `write` function. /// @@ -116,7 +120,7 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { /// The return value of the `write` function. /// /// It may contain a reference to a `MutSpace` and therefore may not outlive it. - type WriteHandle: 'handle; + type WriteHandle: for<'handle, 'space> AtomHandle<'handle, 'space>; /// Reads the body of the atom. /// @@ -128,10 +132,10 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read( + unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, parameter: Self::ReadParameter, - ) -> Option; + ) -> Option<>::Handle>; /// Initialize the body of the atom. /// @@ -141,10 +145,10 @@ pub trait Atom<'handle, 'space: 'handle>: UriBound { /// The frame of the atom was already initialized, containing the URID. /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. - fn init( + fn init<'handle, 'space: 'handle>( frame: AtomSpaceWriter<'handle, 'space>, parameter: Self::WriteParameter, - ) -> Option; + ) -> Option<>::Handle>; } /// An atom of yet unknown type. @@ -192,11 +196,11 @@ impl UnidentifiedAtom { /// Try to read the atom. /// /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read<'handle, 'space, A: Atom<'handle, 'space>>( + pub fn read<'handle, 'space: 'handle, A: Atom>( &'space self, urid: URID, parameter: A::ReadParameter, - ) -> Option { + ) -> Option<>::Handle> { if self.header.urid() != urid { return None; } diff --git a/atom/src/space.rs b/atom/src/space.rs index e01cd31c..e0b41629 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -8,7 +8,7 @@ mod space; mod vec; pub use allocatable::*; -pub use atom_writer::AtomSpaceWriter; +pub use atom_writer::{AtomSpaceWriter, AtomSpaceWriterHandle}; pub use cursor::SpaceCursor; pub use space::{AlignedSpace, AtomSpace}; pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 70076dbf..76f486db 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,7 +1,14 @@ use crate::header::AtomHeader; use crate::space::{AlignedSpace, SpaceAllocator, SpaceAllocatorImpl}; +use crate::AtomHandle; use urid::URID; +pub struct AtomSpaceWriterHandle; + +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { + type Handle = AtomSpaceWriter<'handle, 'space>; +} + /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'handle, 'space: 'handle> { atom_header_index: usize, From aae230462596660bcc00614cda7d1b6c7a60e1c6 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 10 Sep 2021 01:20:10 +0200 Subject: [PATCH 26/54] WIP: replace lifetimes in Atom trait with GAT workaround --- atom/src/atoms/chunk.rs | 33 ++++++---- atom/src/atoms/object.rs | 108 +++++++++++++++---------------- atom/src/atoms/scalar.rs | 60 ++++++++++++----- atom/src/atoms/sequence.rs | 68 ++++++++++++++------ atom/src/atoms/string.rs | 118 ++++++++++++++++++++++------------ atom/src/atoms/tuple.rs | 13 ++-- atom/src/atoms/vector.rs | 19 ++---- atom/src/lib.rs | 31 +++------ atom/src/port.rs | 28 ++++---- atom/src/space/allocatable.rs | 54 ++++------------ atom/src/space/atom_writer.rs | 10 +-- midi/src/raw.rs | 2 +- midi/src/wmidi_binding.rs | 4 +- 13 files changed, 296 insertions(+), 252 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index fc5cac92..9ea53fab 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -14,8 +14,8 @@ //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { -//! let in_chunk: &AtomSpace = ports.input.read(urids.chunk, ()).unwrap(); -//! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk, ()).unwrap(); +//! let in_chunk: &AtomSpace = ports.input.read(urids.chunk).unwrap(); +//! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk).unwrap(); //! //! out_chunk.write_bytes(in_chunk.as_bytes()).unwrap(); //! } @@ -25,8 +25,8 @@ //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Chunk](http://lv2plug.in/ns/ext/atom/atom.html#Chunk) use crate::space::*; -use crate::Atom; use crate::AtomSpaceWriter; +use crate::{Atom, AtomHandle}; use urid::UriBound; /// An atom containing memory of undefined type. @@ -38,22 +38,31 @@ unsafe impl UriBound for Chunk { const URI: &'static [u8] = sys::LV2_ATOM__Chunk; } -impl<'handle, 'space: 'handle> Atom for Chunk { - type ReadParameter = (); - type ReadHandle = &'handle AtomSpace; - type WriteParameter = (); +struct ChunkReaderHandle; +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ChunkReaderHandle { + type Handle = &'handle AtomSpace; +} + +struct ChunkWriterHandle; +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ChunkWriterHandle { + type Handle = AtomSpaceWriter<'handle, 'space>; +} + +impl Atom for Chunk { + type ReadHandle = ChunkReaderHandle; type WriteHandle = AtomSpaceWriterHandle; #[inline] - unsafe fn read(space: &'handle AtomSpace, _: ()) -> Option<&'handle AtomSpace> { - Some(space) + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { + Some(body) } #[inline] - fn init( + fn init<'handle, 'space: 'handle>( frame: AtomSpaceWriter<'handle, 'space>, - _: (), - ) -> Option> { + ) -> Option<>::Handle> { Some(frame) } } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 8c41b52b..ba1c2031 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -30,12 +30,12 @@ //! fn run(ports: &mut MyPorts, urids: &MyURIDs) { //! // Create the reading handle. //! // We don't need the header now. -//! let (_header, object_reader) = ports.input.read(urids.atom.object, ()).unwrap(); +//! let (_header, object_reader) = ports.input.read(urids.atom.object).unwrap(); //! //! /// Iterate through all properties of the object. //! for (property_header, atom) in object_reader { //! // If the property is an integer... -//! if let Some(integer) = atom.read(urids.atom.int, ()) { +//! if let Some(integer) = atom.read(urids.atom.int) { //! // Print it! //! println!( //! "Property No. {} has integer value {}", @@ -52,16 +52,17 @@ //! } //! //! // Initialize the object. -//! let mut object_writer = ports.output.init( -//! urids.atom.object, -//! ObjectHeader { -//! id: None, -//! otype: urids.object_class.into_general(), -//! } +//! let mut object_writer = ports.output.init(urids.atom.object) +//! .unwrap() +//! .write_header( +//! ObjectHeader { +//! id: None, +//! otype: urids.object_class.into_general(), +//! } //! ).unwrap(); //! //! // Write a property to the object. -//! object_writer.init(urids.property_a, urids.atom.int, 42).unwrap(); +//! object_writer.init(urids.property_a, urids.atom.int).unwrap(); //! } //! ``` //! @@ -95,25 +96,37 @@ pub struct ObjectHeader { } struct ObjectReaderHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { - type Handle = AtomSpaceWriter<'handle, 'space>; +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ObjectReaderHandle { + type Handle = (ObjectHeader, ObjectReader<'handle>); } struct ObjectWriterHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { - type Handle = AtomSpaceWriter<'handle, 'space>; +impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ObjectWriterHandle { + type Handle = ObjectHeaderWriter<'handle, 'space>; +} + +pub struct ObjectHeaderWriter<'handle, 'space> { + frame: AtomSpaceWriter<'handle>, +} + +impl<'handle, 'space: 'handle> ObjectHeaderWriter<'handle, 'space> { + pub fn write_header(mut self, header: ObjectHeader) -> Option> { + self.frame.write_value(sys::LV2_Atom_Object_Body { + id: header.id.map(URID::get).unwrap_or(0), + otype: header.otype.get(), + })?; + + Some(ObjectWriter { frame: self.frame }) + } } impl Atom for Object { - type ReadParameter = (); type ReadHandle = ObjectReaderHandle; - type WriteParameter = ObjectHeader; type WriteHandle = ObjectWriterHandle; unsafe fn read<'handle, 'space: 'handle>( - body: &'handle AtomSpace, - _: (), - ) -> Option<(ObjectHeader, ObjectReader<'handle>)> { + body: &'space AtomSpace, + ) -> Option<>::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; @@ -127,17 +140,10 @@ impl Atom for Object { Some((header, reader)) } - fn init( + fn init<'handle, 'space: 'handle>( mut frame: AtomSpaceWriter<'handle, 'space>, - header: ObjectHeader, - ) -> Option> { - { - frame.write_value(sys::LV2_Atom_Object_Body { - id: header.id.map(URID::get).unwrap_or(0), - otype: header.otype.get(), - })?; - } - Some(ObjectWriter { frame }) + ) -> Option<>::Handle> { + Some(ObjectHeaderWriter { frame }) } } @@ -152,26 +158,22 @@ unsafe impl UriBound for Blank { const URI: &'static [u8] = sys::LV2_ATOM__Blank; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Blank { - type ReadParameter = >::ReadParameter; - type ReadHandle = >::ReadHandle; - type WriteParameter = >::WriteParameter; - type WriteHandle = >::WriteHandle; +impl Atom for Blank { + type ReadHandle = ::ReadHandle; + type WriteHandle = ::WriteHandle; - #[allow(clippy::unit_arg)] #[inline] - unsafe fn read( - body: &'handle AtomSpace, - parameter: Self::ReadParameter, - ) -> Option { - Object::read(body, parameter) + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { + Object::read(body) } - fn init( - frame: AtomSpaceWriter<'handle, 'space>, - parameter: Self::WriteParameter, - ) -> Option { - Object::init(frame, parameter) + #[inline] + fn init<'handle, 'space: 'handle>( + mut frame: AtomSpaceWriter<'handle, 'space>, + ) -> Option<>::Handle> { + Object::init(frame) } } @@ -203,15 +205,14 @@ impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. - pub fn init_with_context<'read, K: ?Sized, T: ?Sized, A: Atom<'read, 'space>>( + pub fn init_with_context<'read, K: ?Sized, T: ?Sized, A: Atom>( &'space mut self, key: URID, context: URID, child_urid: URID, - parameter: A::WriteParameter, ) -> Option { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; - self.frame.init_atom(child_urid, parameter) + self.frame.init_atom(child_urid) } /// Initialize a new property. @@ -219,14 +220,13 @@ impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init<'a, K: ?Sized, A: Atom<'a, 'space>>( + pub fn init<'a, K: ?Sized, A: Atom>( &'a mut self, key: URID, child_urid: URID, - parameter: A::WriteParameter, - ) -> Option { + ) -> Option<>::Handle> { Property::write_header(&mut self.frame, key, None::>)?; - self.frame.init_atom(child_urid, parameter) + self.frame.init_atom(child_urid) } } @@ -411,7 +411,7 @@ mod tests { { let (header, iter) = unsafe { raw_space.read().next_atom() } .unwrap() - .read(urids.object, ()) + .read(urids.object) .unwrap(); assert_eq!(header.otype, object_type); @@ -421,11 +421,11 @@ mod tests { let (header, atom) = properties[0]; assert_eq!(header.key, first_key); - assert_eq!(atom.read(urids.int, ()).unwrap(), first_value); + assert_eq!(atom.read(urids.int).unwrap(), first_value); let (header, atom) = properties[1]; assert_eq!(header.key, second_key); - assert_eq!(atom.read(urids.float, ()).unwrap(), second_value); + assert_eq!(atom.read(urids.float).unwrap(), second_value); } } } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 39bd90fe..1c9e71c5 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -19,10 +19,10 @@ //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! // Scalar atoms don't need a reading parameter. -//! let read_value: f32 = ports.input.read(urids.float, ()).unwrap(); +//! let read_value: &f32 = ports.input.read(urids.float).unwrap(); //! //! // Writing is done with the value of the atom. -//! ports.output.init(urids.float, 17.0).unwrap(); +//! ports.output.init(urids.float).unwrap().set(17.0); //! } //! ``` //! @@ -30,7 +30,8 @@ //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Number](http://lv2plug.in/ns/ext/atom/atom.html#Number) use crate::*; -use std::marker::Unpin; +use std::marker::{PhantomData, Unpin}; +use std::mem::MaybeUninit; use urid::UriBound; use urid::URID; @@ -56,28 +57,53 @@ pub trait ScalarAtom: UriBound { /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. #[inline] fn write_scalar<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'handle, 'space>, - value: Self::InternalType, - ) -> Option<()> { - frame.write_value(value)?; + mut frame: AtomSpaceWriter<'space>, + ) -> Option> { + let value = frame.write_value(MaybeUninit::::uninit())?; // Scalars have extra padding due to repr(C) frame.allocate_padding_for::(); - Some(()) + Some(ScalarWriter(value)) } } -impl<'handle, 'space: 'handle, A: ScalarAtom> Atom<'handle, 'space> for A { - type ReadParameter = (); - type ReadHandle = A::InternalType; - type WriteParameter = A::InternalType; - type WriteHandle = (); +pub struct ScalarWriter<'handle, T: Copy + 'static>(&'handle mut MaybeUninit); - unsafe fn read(body: &'space AtomSpace, _: ()) -> Option { +impl<'handle, T: Copy + 'static> ScalarWriter<'handle, T> { + #[inline] + pub fn set<'a>(&mut self, value: T) -> &mut T + where + 'a: 'handle, + { + *self.0 = MaybeUninit::new(value); + // SAFETY: we just wrote the value, therefore it is initialized now + unsafe { crate::util::assume_init_mut(&mut self.0) } + } +} + +struct ScalarReaderHandle; +impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarReaderHandle { + type Handle = &'handle T; +} + +struct ScalarWriterHandle; +impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarWriterHandle { + type Handle = ScalarWriter<'handle, T>; +} + +impl Atom for A { + type ReadHandle = ScalarReaderHandle; + type WriteHandle = ScalarWriterHandle; + + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { ::read_scalar(body) } - fn init(frame: AtomSpaceWriter<'handle, 'space>, value: A::InternalType) -> Option<()> { - ::write_scalar(frame, value) + fn init<'handle, 'space: 'handle>( + frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle> { + ::write_scalar(frame) } } @@ -203,7 +229,7 @@ mod tests { { let read_value = unsafe { raw_space.read().next_atom() } .unwrap() - .read(urid, ()) + .read(urid) .unwrap(); assert_eq!(read_value, value); diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 661b05ca..19b6f28e 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -87,29 +87,56 @@ unsafe impl UriBound for Sequence { const URI: &'static [u8] = sys::LV2_ATOM__Sequence; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Sequence { - type ReadParameter = URID; - type ReadHandle = SequenceIterator<'handle>; - type WriteParameter = TimeStampURID; - type WriteHandle = SequenceWriter<'handle, 'space>; +struct SequenceReadHandle; +impl<'handle> AtomHandle<'handle> for SequenceReadHandle { + type Handle = SequenceHeaderReader<'handle>; +} - unsafe fn read(body: &AtomSpace, bpm_urid: URID) -> Option { - let mut reader = body.read(); - let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; +struct SequenceWriteHandle; +impl<'handle> AtomHandle<'handle> for SequenceWriteHandle { + type Handle = SequenceWriter<'handle>; +} - let unit = if header.unit == bpm_urid { +pub struct SequenceHeaderReader<'handle> { + header: &'handle sys::LV2_Atom_Sequence_Body, + reader: SpaceReader<'handle>, +} + +impl<'handle> SequenceHeaderReader<'handle> { + pub fn read(self, bpm_urid: URID) -> SequenceIterator<'handle> { + let unit = if self.header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute } else { TimeStampUnit::Frames }; - Some(SequenceIterator { reader, unit }) + SequenceIterator { + reader: self.reader, + unit, + } + } +} + +pub struct SequenceHeaderWriter<'handle> { + frame: AtomSpaceWriter<'handle>, +} + +impl Atom for Sequence { + type ReadHandle = SequenceReadHandle; + type WriteHandle = SequenceWriteHandle; + + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { + let mut reader = body.read(); + let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; + + Some(SequenceHeaderReader { reader, header }) } - fn init( - mut frame: AtomSpaceWriter<'handle, 'space>, - unit: TimeStampURID, - ) -> Option> { + fn init<'handle, 'space: 'handle>( + frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle> { let header = SequenceBody(sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), @@ -211,13 +238,13 @@ impl<'a> Iterator for SequenceIterator<'a> { } /// The writing handle for sequences. -pub struct SequenceWriter<'handle, 'space> { - frame: AtomSpaceWriter<'handle, 'space>, +pub struct SequenceWriter<'handle> { + frame: AtomSpaceWriter<'handle>, unit: TimeStampUnit, last_stamp: Option, } -impl<'handle, 'space> SequenceWriter<'handle, 'space> { +impl<'handle> SequenceWriter<'handle> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: @@ -254,14 +281,13 @@ impl<'handle, 'space> SequenceWriter<'handle, 'space> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init<'a, A: Atom<'a, 'space>>( + pub fn init<'a, A: Atom>( &'a mut self, stamp: TimeStamp, urid: URID, - parameter: A::WriteParameter, ) -> Option { self.write_time_stamp(stamp)?; - self.frame.init_atom(urid, parameter) + self.frame.init_atom(urid) } /// Forward an unidentified atom to the sequence. @@ -354,7 +380,7 @@ mod tests { { let mut reader = unsafe { raw_space.read().next_atom() } .unwrap() - .read(urids.atom.sequence, urids.units.beat) + .read(urids.atom.sequence) .unwrap(); assert_eq!(reader.unit(), TimeStampUnit::Frames); diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 829f9d81..2693511d 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -17,8 +17,8 @@ //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { -//! let input: &str = ports.input.read(urids.string, ()).unwrap(); -//! let mut writer: StringWriter = ports.output.init(urids.string, ()).unwrap(); +//! let input: &str = ports.input.read(urids.string).unwrap(); +//! let mut writer: StringWriter = ports.output.init(urids.string).unwrap(); //! writer.append(input).unwrap(); //! } //! ``` @@ -29,6 +29,7 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Literal](http://lv2plug.in/ns/ext/atom/atom.html#Literal) use crate::prelude::*; use crate::space::*; +use crate::AtomHandle; use urid::*; /// An atom containing either a localized string or an RDF literal. @@ -47,13 +48,49 @@ pub enum LiteralInfo { Datatype(URID), } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { - type ReadParameter = (); - type ReadHandle = (LiteralInfo, &'handle str); - type WriteParameter = LiteralInfo; - type WriteHandle = StringWriter<'handle, 'space>; +pub struct LiteralInfoWriter<'a> { + frame: AtomSpaceWriter<'a>, +} + +impl<'a> LiteralInfoWriter<'a> { + pub fn write_info(mut self, info: LiteralInfo) -> Option> { + self.frame.write_value(match info { + LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { + lang: lang.get(), + datatype: 0, + }, + LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body { + lang: 0, + datatype: datatype.get(), + }, + })?; - unsafe fn read(body: &'handle AtomSpace, _: ()) -> Option<(LiteralInfo, &'handle str)> { + Some(StringWriter { + frame: self.frame, + has_nul_byte: false, + }) + } +} + +struct LiteralReadHandle; + +impl<'a> AtomHandle<'a> for LiteralReadHandle { + type Handle = (LiteralInfo, &'a str); +} + +struct LiteralWriteHandle; + +impl<'a> AtomHandle<'a> for LiteralWriteHandle { + type Handle = StringWriter<'a>; +} + +impl Atom for Literal { + type ReadHandle = LiteralReadHandle; + type WriteHandle = LiteralWriteHandle; + + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; @@ -73,27 +110,26 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Literal { .map(|string| (info, string)) } - fn init( - mut frame: AtomSpaceWriter<'handle, 'space>, - info: LiteralInfo, - ) -> Option> { - frame.write_value(match info { - LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { - lang: lang.get(), - datatype: 0, - }, - LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body { - lang: 0, - datatype: datatype.get(), - }, - })?; - Some(StringWriter { - frame, - has_nul_byte: false, - }) + #[inline] + fn init<'handle, 'space: 'handle>( + frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle> { + Some(LiteralInfoWriter { frame }) } } +struct StringReadHandle; + +impl<'a> AtomHandle<'a> for StringReadHandle { + type Handle = &'a str; +} + +struct StringWriteHandle; + +impl<'a> AtomHandle<'a> for StringWriteHandle { + type Handle = StringWriter<'a>; +} + /// An atom containing a UTF-8 encoded string. /// /// [See also the module documentation.](index.html) @@ -103,22 +139,21 @@ unsafe impl UriBound for String { const URI: &'static [u8] = sys::LV2_ATOM__String; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for String { - type ReadParameter = (); - type ReadHandle = &'handle str; - type WriteParameter = (); - type WriteHandle = StringWriter<'handle, 'space>; +impl Atom for String { + type ReadHandle = StringReadHandle; + type WriteHandle = StringWriteHandle; - unsafe fn read(body: &'space AtomSpace, _: ()) -> Option<&'handle str> { + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { let data = body.as_bytes(); let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator Some(core::str::from_utf8(rust_str_bytes).ok()?) } - fn init( - frame: AtomSpaceWriter<'handle, 'space>, - _: (), - ) -> Option> { + fn init<'handle, 'space: 'handle>( + frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle> { Some(StringWriter { frame, has_nul_byte: false, @@ -127,12 +162,12 @@ impl<'handle, 'space: 'handle> Atom<'handle, 'space> for String { } /// Handle to append strings to a string or literal. -pub struct StringWriter<'handle, 'space> { - frame: AtomSpaceWriter<'handle, 'space>, +pub struct StringWriter<'a> { + frame: AtomSpaceWriter<'a>, has_nul_byte: bool, // If this writer already wrote a null byte before. } -impl<'handle, 'space> StringWriter<'handle, 'space> { +impl<'a> StringWriter<'a> { /// Append a string. /// /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. @@ -141,6 +176,7 @@ impl<'handle, 'space> StringWriter<'handle, 'space> { pub fn append(&mut self, string: &str) -> Option<&mut str> { // Rewind to overwrite previously written nul_byte before appending the string. if self.has_nul_byte { + // SAFETY: it is safe to overwrite the nul byte if unsafe { !self.frame.rewind(1) } { return None; // Could not rewind } @@ -235,7 +271,7 @@ mod tests { .read() .next_atom() .unwrap() - .read(urids.atom.literal, ()) + .read(urids.atom.literal) } .unwrap(); @@ -277,7 +313,7 @@ mod tests { { let string = unsafe { raw_space.read().next_atom() } .unwrap() - .read(urids.string, ()) + .read(urids.string) .unwrap(); assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1); } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index f1067409..6c87c857 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -42,22 +42,21 @@ unsafe impl UriBound for Tuple { const URI: &'static [u8] = sys::LV2_ATOM__Tuple; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for Tuple { - type ReadParameter = (); +impl Atom for Tuple { type ReadHandle = TupleIterator<'handle>; - type WriteParameter = (); type WriteHandle = TupleWriter<'handle, 'space>; - unsafe fn read(body: &'space AtomSpace, _: ()) -> Option> { + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { Some(TupleIterator { reader: body.read(), }) } - fn init( + fn init<'handle, 'space: 'handle>( frame: AtomSpaceWriter<'handle, 'space>, - _: (), - ) -> Option> { + ) -> Option<>::Handle> { Some(TupleWriter { frame }) } } diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index be2e0a6b..403b9654 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -46,19 +46,13 @@ unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } -impl<'handle, 'space: 'handle, C: ScalarAtom> Atom<'handle, 'space> for Vector -where - C: 'space, -{ - type ReadParameter = URID; +impl Atom for Vector { type ReadHandle = &'space [C::InternalType]; - type WriteParameter = URID; type WriteHandle = VectorWriter<'handle, 'space, C>; - unsafe fn read( + unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, - child_urid: URID, - ) -> Option<&'space [C::InternalType]> { + ) -> Option<>::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; @@ -72,10 +66,9 @@ where reader.as_slice() } - fn init( - mut frame: AtomSpaceWriter<'handle, 'space>, - child_urid: URID, - ) -> Option> { + fn init<'handle, 'space: 'handle>( + frame: AtomSpaceWriter<'handle, 'space>, + ) -> Option<>::Handle> { let body = sys::LV2_Atom_Vector_Body { child_type: child_urid.get(), child_size: size_of::() as u32, diff --git a/atom/src/lib.rs b/atom/src/lib.rs index df01a11c..5f61c4ab 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -47,7 +47,7 @@ //! // An event contains a timestamp and an atom. //! let (timestamp, atom) = event; //! // If the read atom is a 32-bit integer... -//! if let Some(integer) = atom.read(urids.atom.int, ()) { +//! if let Some(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. //! output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap(); //! } else { @@ -92,7 +92,7 @@ pub mod prelude { pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; } -pub trait AtomHandle<'handle, 'space: 'handle> { +pub trait AtomHandle<'handle> { type Handle: 'handle; } @@ -102,25 +102,15 @@ pub trait AtomHandle<'handle, 'space: 'handle> { /// /// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom. pub trait Atom: UriBound { - /// The atom-specific parameter of the `read` function. - /// - /// If your atom does not need a reading parameter, you may set it to `()`. - type ReadParameter; - /// The return value of the `read` function. /// /// It may contain a reference to the atom and therefore may not outlive it. - type ReadHandle: for<'handle, 'space> AtomHandle<'handle, 'space>; - - /// The atom-specific parameter of the `write` function. - /// - /// If your atom does not need a writing parameter, you may set it to `()`. - type WriteParameter; + type ReadHandle: for<'handle> AtomHandle<'handle>; /// The return value of the `write` function. /// /// It may contain a reference to a `MutSpace` and therefore may not outlive it. - type WriteHandle: for<'handle, 'space> AtomHandle<'handle, 'space>; + type WriteHandle: for<'handle> AtomHandle<'handle>; /// Reads the body of the atom. /// @@ -134,8 +124,7 @@ pub trait Atom: UriBound { /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, - parameter: Self::ReadParameter, - ) -> Option<>::Handle>; + ) -> Option<>::Handle>; /// Initialize the body of the atom. /// @@ -146,9 +135,8 @@ pub trait Atom: UriBound { /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'handle, 'space>, - parameter: Self::WriteParameter, - ) -> Option<>::Handle>; + frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle>; } /// An atom of yet unknown type. @@ -199,14 +187,13 @@ impl UnidentifiedAtom { pub fn read<'handle, 'space: 'handle, A: Atom>( &'space self, urid: URID, - parameter: A::ReadParameter, - ) -> Option<>::Handle> { + ) -> Option<>::Handle> { if self.header.urid() != urid { return None; } // SAFETY: the fact that this contains a valid instance of A is checked above. - unsafe { A::read(self.body(), parameter) } + unsafe { A::read(self.body()) } } #[inline] diff --git a/atom/src/port.rs b/atom/src/port.rs index bcef61e5..c3425ae1 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -18,14 +18,14 @@ //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! // Read an integer from the port and print it. -//! println!("My input is: {}", ports.input.read(urids.int, ()).unwrap()); +//! println!("My input is: {}", ports.input.read(urids.int).unwrap()); //! // Write the integer `42` to the port. //! ports.output.init(urids.int, 42).unwrap(); //! } //! ``` use crate::header::AtomHeader; use crate::space::*; -use crate::UnidentifiedAtom; +use crate::{AtomHandle, UnidentifiedAtom}; use lv2_core::port::PortType; use std::ffi::c_void; use std::ptr::NonNull; @@ -50,26 +50,25 @@ impl<'space> PortReader<'space> { /// /// This method returns `None` if the atom is malformed or simply isn't of the specified type. #[inline] - pub fn read<'handle, A: crate::Atom<'handle, 'space>>( + pub fn read<'handle, A: crate::Atom>( &self, urid: URID, - parameter: A::ReadParameter, - ) -> Option { - self.atom.read(urid, parameter) + ) -> Option<>::Handle> { + self.atom.read(urid) } } /// A handle to write atoms into a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. -pub struct PortWriter<'a> { - space: SpaceCursor<'a>, +pub struct PortWriter<'space> { + space: SpaceCursor<'space>, has_been_written: bool, } -impl<'a> PortWriter<'a> { +impl<'space> PortWriter<'space> { /// Create a new port writer. - fn new(space: &'a mut AtomSpace) -> Self { + fn new(space: &'space mut AtomSpace) -> Self { Self { space: SpaceCursor::new(space.as_bytes_mut()), has_been_written: false, @@ -83,18 +82,17 @@ impl<'a> PortWriter<'a> { /// Please note that you can call this method once only, because any atoms written behind the first one will not be identified. /// /// This method returns `None` if the space of the port isn't big enough or if the method was called multiple times. - pub fn init<'b, 'read, 'write, A: crate::Atom<'read, 'write>>( + pub fn init<'b, 'write, A: crate::Atom>( &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, - parameter: A::WriteParameter, - ) -> Option { + ) -> Option<>::Handle> { if !self.has_been_written { self.has_been_written = true; // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. let space: &'write mut SpaceCursor<'write> = unsafe { ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) }; - space.init_atom(urid, parameter) + space.init_atom(urid) } else { None } @@ -162,7 +160,7 @@ mod tests { { let chunk = unsafe { raw_space.read().next_atom() } .unwrap() - .read(urids.chunk, ()) + .read(urids.chunk) .unwrap(); let reader = unsafe { AtomPort::input_from_raw(NonNull::from(chunk).cast(), 0) }; assert_eq!(reader.read::(urids.int, ()).unwrap(), 42); diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 9667f46a..311389f5 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -10,7 +10,7 @@ use std::mem::MaybeUninit; /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. /// // TODO: Find proper name -pub trait SpaceAllocatorImpl<'space> { +pub trait SpaceAllocatorImpl { fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])>; #[must_use] @@ -24,7 +24,7 @@ pub trait SpaceAllocatorImpl<'space> { } // TODO: Find proper name -pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { +pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { /// Try to allocate memory on the internal data slice. /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. @@ -42,13 +42,7 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { } #[inline] - fn allocate_aligned<'handle, T: 'static>( - &'handle mut self, - size: usize, - ) -> Option<&'handle mut AlignedSpace> - where - 'space: 'handle, - { + fn allocate_aligned(&mut self, size: usize) -> Option<&mut AlignedSpace> { let required_padding = crate::util::padding_for::(self.remaining_bytes())?; let raw = self.allocate(size + required_padding)?; @@ -56,38 +50,19 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { } #[inline] - fn allocate_values<'handle, T: 'static>( - &'handle mut self, - count: usize, - ) -> Option<&'handle mut [MaybeUninit]> - where - 'space: 'handle, - { + fn allocate_values(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { let space = self.allocate_aligned(count * std::mem::size_of::())?; Some(space.as_uninit_slice_mut()) } #[inline] - fn init_atom<'handle, A: Atom<'handle, 'space>>( - &'handle mut self, - atom_type: URID, - write_parameter: A::WriteParameter, - ) -> Option - where - 'space: 'handle, - { - let space: AtomSpaceWriter<'handle, 'space> = AtomSpaceWriter::write_new(self, atom_type)?; - A::init(space, write_parameter) + fn init_atom(&mut self, atom_type: URID) -> Option { + let space = AtomSpaceWriter::write_new(self, atom_type)?; + A::init(space) } #[inline] - fn forward_atom<'handle>( - &'handle mut self, - atom: &UnidentifiedAtom, - ) -> Option<&'handle mut UnidentifiedAtom> - where - 'space: 'handle, - { + fn forward_atom(&mut self, atom: &UnidentifiedAtom) -> Option<&mut UnidentifiedAtom> { let resulting_space = self.allocate_aligned(atom.atom_space().len())?; resulting_space .as_bytes_mut() @@ -97,20 +72,16 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { } #[inline] - fn write_bytes<'handle>(&'handle mut self, bytes: &[u8]) -> Option<&'handle mut [u8]> - where - 'space: 'handle, - { + fn write_bytes(&mut self, bytes: &[u8]) -> Option<&mut [u8]> { let space = self.allocate(bytes.len())?; space.copy_from_slice(bytes); Some(space) } #[inline] - fn write_value<'handle, T: 'static>(&'handle mut self, value: T) -> Option<&'handle mut T> + fn write_value(&mut self, value: T) -> Option<&mut T> where T: Copy + Sized + 'static, - 'space: 'handle, { let space = self.allocate_aligned(size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. @@ -121,10 +92,9 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { Some(unsafe { crate::util::assume_init_mut(space) }) } - fn write_values<'handle, T>(&'handle mut self, values: &[T]) -> Option<&'handle mut [T]> + fn write_values(&mut self, values: &[T]) -> Option<&mut [T]> where T: Copy + Sized + 'static, - //'space: 'handle, { let space: &mut AlignedSpace = self.allocate_aligned(size_of_val(values))?; let space = space.as_uninit_slice_mut(); @@ -138,7 +108,7 @@ pub trait SpaceAllocator<'space>: SpaceAllocatorImpl<'space> + Sized { } } -impl<'space, H: SpaceAllocatorImpl<'space>> SpaceAllocator<'space> for H {} +impl SpaceAllocator for H {} #[cfg(test)] mod tests { diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 76f486db..67af119b 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -6,16 +6,16 @@ use urid::URID; pub struct AtomSpaceWriterHandle; impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { - type Handle = AtomSpaceWriter<'handle, 'space>; + type Handle = AtomSpaceWriter<'handle>; } /// A `MutSpace` that tracks the amount of allocated space in an atom header. -pub struct AtomSpaceWriter<'handle, 'space: 'handle> { +pub struct AtomSpaceWriter<'handle> { atom_header_index: usize, - parent: &'handle mut (dyn SpaceAllocatorImpl<'space>), + parent: &'handle mut (dyn SpaceAllocatorImpl<'handle>), } -impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { +impl<'handle> AtomSpaceWriter<'handle> { #[inline] pub fn atom_header(&self) -> AtomHeader { let previous = self @@ -56,7 +56,7 @@ impl<'handle, 'space> AtomSpaceWriter<'handle, 'space> { } } -impl<'handle, 'space: 'handle> SpaceAllocatorImpl<'space> for AtomSpaceWriter<'handle, 'space> { +impl<'handle> SpaceAllocatorImpl<'handle> for AtomSpaceWriter<'handle> { #[inline] fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (previous, current) = self.parent.allocate_and_split(size)?; diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 59ef2f38..eac32b99 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -15,7 +15,7 @@ unsafe impl UriBound for MidiEvent { const URI: &'static [u8] = sys::LV2_MIDI__MidiEvent; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for MidiEvent { +impl Atom for MidiEvent { type ReadParameter = (); type ReadHandle = &'handle [u8]; type WriteParameter = (); diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index f371a9e4..fb9fb007 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -20,7 +20,7 @@ unsafe impl UriBound for WMidiEvent { const URI: &'static [u8] = sys::LV2_MIDI__MidiEvent; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for WMidiEvent { +impl Atom for WMidiEvent { type ReadParameter = (); type ReadHandle = wmidi::MidiMessage<'handle>; type WriteParameter = wmidi::MidiMessage<'handle>; @@ -52,7 +52,7 @@ unsafe impl UriBound for SystemExclusiveWMidiEvent { const URI: &'static [u8] = sys::LV2_MIDI__MidiEvent; } -impl<'handle, 'space: 'handle> Atom<'handle, 'space> for SystemExclusiveWMidiEvent { +impl Atom for SystemExclusiveWMidiEvent { type ReadParameter = (); type ReadHandle = wmidi::MidiMessage<'handle>; type WriteParameter = (); From c59416b676a0a2742e88256e3ba0dc4a899cac16 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 10 Sep 2021 10:48:14 +0200 Subject: [PATCH 27/54] More WIP --- atom/src/atoms/object.rs | 64 +++++++++++++++++++++----------------- atom/src/atoms/scalar.rs | 4 +-- atom/src/atoms/sequence.rs | 46 ++++++++++++++++----------- atom/src/port.rs | 2 +- atom/src/util.rs | 7 +++++ 5 files changed, 72 insertions(+), 51 deletions(-) diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index ba1c2031..501b5e38 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -72,6 +72,7 @@ use crate::space::reader::SpaceReader; use crate::*; use std::convert::TryFrom; use std::iter::Iterator; +use std::mem::MaybeUninit; use urid::UriBound; use urid::URID; @@ -96,27 +97,31 @@ pub struct ObjectHeader { } struct ObjectReaderHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ObjectReaderHandle { +impl<'handle> AtomHandle<'handle> for ObjectReaderHandle { type Handle = (ObjectHeader, ObjectReader<'handle>); } struct ObjectWriterHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ObjectWriterHandle { - type Handle = ObjectHeaderWriter<'handle, 'space>; +impl<'handle> AtomHandle<'handle> for ObjectWriterHandle { + type Handle = ObjectHeaderWriter<'handle>; } -pub struct ObjectHeaderWriter<'handle, 'space> { +pub struct ObjectHeaderWriter<'handle> { + header: &'handle mut MaybeUninit, frame: AtomSpaceWriter<'handle>, } -impl<'handle, 'space: 'handle> ObjectHeaderWriter<'handle, 'space> { - pub fn write_header(mut self, header: ObjectHeader) -> Option> { - self.frame.write_value(sys::LV2_Atom_Object_Body { - id: header.id.map(URID::get).unwrap_or(0), - otype: header.otype.get(), - })?; - - Some(ObjectWriter { frame: self.frame }) +impl<'handle> ObjectHeaderWriter<'handle> { + pub fn write_header(mut self, header: ObjectHeader) -> ObjectWriter<'handle> { + crate::util::write_uninit( + &mut self.header, + sys::LV2_Atom_Object_Body { + id: header.id.map(URID::get).unwrap_or(0), + otype: header.otype.get(), + }, + ); + + ObjectWriter { frame: self.frame } } } @@ -126,7 +131,7 @@ impl Atom for Object { unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, - ) -> Option<>::Handle> { + ) -> Option<>::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; @@ -141,9 +146,10 @@ impl Atom for Object { } fn init<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'handle, 'space>, - ) -> Option<>::Handle> { - Some(ObjectHeaderWriter { frame }) + mut frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle> { + let header = frame.write_value(MaybeUninit::uninit())?; + Some(ObjectHeaderWriter { header, frame }) } } @@ -165,14 +171,14 @@ impl Atom for Blank { #[inline] unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, - ) -> Option<>::Handle> { + ) -> Option<>::Handle> { Object::read(body) } #[inline] fn init<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'handle, 'space>, - ) -> Option<>::Handle> { + mut frame: AtomSpaceWriter<'space>, + ) -> Option<>::Handle> { Object::init(frame) } } @@ -197,20 +203,20 @@ impl<'a> Iterator for ObjectReader<'a> { /// Writing handle for object properties. /// /// This handle is a safeguard to assure that a object is always a series of properties. -pub struct ObjectWriter<'handle, 'space: 'handle> { - frame: AtomSpaceWriter<'handle, 'space>, +pub struct ObjectWriter<'a> { + frame: AtomSpaceWriter<'a>, } -impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { +impl<'a> ObjectWriter<'a> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. pub fn init_with_context<'read, K: ?Sized, T: ?Sized, A: Atom>( - &'space mut self, + &mut self, key: URID, context: URID, child_urid: URID, - ) -> Option { + ) -> Option<::Handle> { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; self.frame.init_atom(child_urid) } @@ -220,11 +226,11 @@ impl<'handle, 'space: 'handle> ObjectWriter<'handle, 'space> { /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init<'a, K: ?Sized, A: Atom>( - &'a mut self, + pub fn init( + &mut self, key: URID, child_urid: URID, - ) -> Option<>::Handle> { + ) -> Option<::Handle> { Property::write_header(&mut self.frame, key, None::>)?; self.frame.init_atom(child_urid) } @@ -287,8 +293,8 @@ impl Property { /// /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. #[inline] - fn write_header<'a, 'space, K: ?Sized, C: ?Sized>( - space: &'a mut impl SpaceAllocator<'space>, + fn write_header( + space: &mut impl SpaceAllocator, key: URID, context: Option>, ) -> Option<()> { diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 1c9e71c5..c2f7bdab 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -74,9 +74,7 @@ impl<'handle, T: Copy + 'static> ScalarWriter<'handle, T> { where 'a: 'handle, { - *self.0 = MaybeUninit::new(value); - // SAFETY: we just wrote the value, therefore it is initialized now - unsafe { crate::util::assume_init_mut(&mut self.0) } + crate::util::write_uninit(&mut self.0, value) } } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 19b6f28e..7bfad682 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -29,10 +29,9 @@ //! // The reading method needs the URID of the BPM unit to tell if the time stamp //! // is measured in beats or in frames. If the atom doesn't says that it's measured //! // in beats, it is assumed that it is measured in frames. -//! let input_sequence: SequenceIterator = ports.input.read( -//! urids.atom.sequence, -//! urids.units.beat -//! ).unwrap(); +//! let input_sequence: SequenceIterator = ports.input +//! .read(urids.atom.sequence) +//! .unwrap().read(urids.units.beat); //! //! // Get the write handle to the sequence. //! // You have to provide the unit of the time stamps. @@ -67,6 +66,7 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) use crate::space::reader::SpaceReader; use crate::*; +use std::mem::MaybeUninit; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; @@ -118,9 +118,30 @@ impl<'handle> SequenceHeaderReader<'handle> { } pub struct SequenceHeaderWriter<'handle> { + header: &'handle mut MaybeUninit, frame: AtomSpaceWriter<'handle>, } +impl<'a> SequenceHeaderWriter<'a> { + pub fn with_unit(mut self, unit: TimeStampURID) -> SequenceWriter<'a> { + let header = SequenceBody(sys::LV2_Atom_Sequence_Body { + unit: match unit { + TimeStampURID::BeatsPerMinute(urid) => urid.get(), + TimeStampURID::Frames(urid) => urid.get(), + }, + pad: 0, + }); + + crate::util::write_uninit(&mut self.header, header); + + SequenceWriter { + frame: self.frame, + unit: unit.into(), + last_stamp: None, + } + } +} + impl Atom for Sequence { type ReadHandle = SequenceReadHandle; type WriteHandle = SequenceWriteHandle; @@ -135,22 +156,11 @@ impl Atom for Sequence { } fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'space>, + mut frame: AtomSpaceWriter<'space>, ) -> Option<>::Handle> { - let header = SequenceBody(sys::LV2_Atom_Sequence_Body { - unit: match unit { - TimeStampURID::BeatsPerMinute(urid) => urid.get(), - TimeStampURID::Frames(urid) => urid.get(), - }, - pad: 0, - }); - frame.write_value(header)?; + let header = frame.write_value(MaybeUninit::uninit())?; - Some(SequenceWriter { - frame, - unit: unit.into(), - last_stamp: None, - }) + Some(SequenceHeaderWriter { header, frame }) } } diff --git a/atom/src/port.rs b/atom/src/port.rs index c3425ae1..8cd85f81 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -20,7 +20,7 @@ //! // Read an integer from the port and print it. //! println!("My input is: {}", ports.input.read(urids.int).unwrap()); //! // Write the integer `42` to the port. -//! ports.output.init(urids.int, 42).unwrap(); +//! ports.output.init(urids.int).unwrap(); //! } //! ``` use crate::header::AtomHeader; diff --git a/atom/src/util.rs b/atom/src/util.rs index 64c99646..5f6b0da1 100644 --- a/atom/src/util.rs +++ b/atom/src/util.rs @@ -19,6 +19,13 @@ pub(crate) unsafe fn assume_init_slice(slice: &[MaybeUninit]) -> &[T] { &*(slice as *const _ as *const [T]) } +#[inline] +pub(crate) fn write_uninit(uninit: &mut MaybeUninit, value: T) -> &mut T { + *uninit = MaybeUninit::new(value); + // SAFETY: we just wrote the value, therefore it is initialized now + unsafe { assume_init_mut(uninit) } +} + #[inline] pub(crate) fn value_index_to_byte_index(size: usize) -> usize { size * ::core::mem::size_of::() From 1df4e4c67d53dac4c76f1c16fe0c46ebedc6f51b Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 10 Sep 2021 19:04:01 +0200 Subject: [PATCH 28/54] More WIP 2 --- atom/src/atoms.rs | 8 +-- atom/src/atoms/chunk.rs | 16 +++--- atom/src/atoms/object.rs | 31 +++++------ atom/src/atoms/scalar.rs | 14 +++-- atom/src/atoms/sequence.rs | 45 ++++++++-------- atom/src/atoms/string.rs | 33 ++++++------ atom/src/atoms/tuple.rs | 43 ++++++++++------ atom/src/atoms/vector.rs | 97 ++++++++++++++++++++++++----------- atom/src/lib.rs | 4 +- atom/src/port.rs | 10 ++-- atom/src/space/allocatable.rs | 7 ++- atom/src/space/atom_writer.rs | 8 +-- atom/src/space/cursor.rs | 2 +- atom/src/space/space.rs | 2 +- atom/src/space/vec.rs | 2 +- rustfmt.toml | 4 ++ state/tests/integration.rs | 2 +- 17 files changed, 179 insertions(+), 149 deletions(-) create mode 100644 rustfmt.toml diff --git a/atom/src/atoms.rs b/atom/src/atoms.rs index 62122d8c..501a7ff7 100644 --- a/atom/src/atoms.rs +++ b/atom/src/atoms.rs @@ -19,7 +19,7 @@ pub struct AtomURIDCollection { pub long: URID, pub urid: URID, pub bool: URID, - vector: URID>, + pub vector: URID, pub chunk: URID, pub literal: URID, pub object: URID, @@ -28,9 +28,3 @@ pub struct AtomURIDCollection { pub tuple: URID, pub sequence: URID, } - -impl AtomURIDCollection { - pub fn vector(&self) -> URID> { - unsafe { URID::new_unchecked(self.vector.get()) } - } -} diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 9ea53fab..abdc765e 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -39,13 +39,13 @@ unsafe impl UriBound for Chunk { } struct ChunkReaderHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ChunkReaderHandle { +impl<'handle> AtomHandle<'handle> for ChunkReaderHandle { type Handle = &'handle AtomSpace; } struct ChunkWriterHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for ChunkWriterHandle { - type Handle = AtomSpaceWriter<'handle, 'space>; +impl<'handle> AtomHandle<'handle> for ChunkWriterHandle { + type Handle = AtomSpaceWriter<'handle>; } impl Atom for Chunk { @@ -55,14 +55,12 @@ impl Atom for Chunk { #[inline] unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, - ) -> Option<>::Handle> { + ) -> Option<>::Handle> { Some(body) } #[inline] - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'handle, 'space>, - ) -> Option<>::Handle> { + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { Some(frame) } } @@ -85,7 +83,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.chunk, ()).unwrap(); + let mut writer = space.init_atom(urids.chunk).unwrap(); let data = writer.allocate(SLICE_LENGTH).unwrap(); for (i, value) in data.into_iter().enumerate() { @@ -110,7 +108,7 @@ mod tests { // reading { let data = - unsafe { Chunk::read(raw_space.read().next_atom().unwrap().body(), ()) }.unwrap(); + unsafe { Chunk::read(raw_space.read().next_atom().unwrap().body()) }.unwrap(); assert_eq!(data.len(), SLICE_LENGTH); for (i, value) in data.as_bytes().iter().enumerate() { diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 501b5e38..ea0c9fa0 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -145,9 +145,7 @@ impl Atom for Object { Some((header, reader)) } - fn init<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle> { + fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { let header = frame.write_value(MaybeUninit::uninit())?; Some(ObjectHeaderWriter { header, frame }) } @@ -176,9 +174,7 @@ impl Atom for Blank { } #[inline] - fn init<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle> { + fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { Object::init(frame) } } @@ -343,19 +339,18 @@ mod tests { { let mut cursor = SpaceCursor::new(raw_space.as_bytes_mut()); let frame = AtomSpaceWriter::write_new(&mut cursor, urids.object).unwrap(); - let mut writer = Object::init( - frame, - ObjectHeader { - id: None, - otype: object_type, - }, - ) - .unwrap(); + let mut writer = Object::init(frame).unwrap().write_header(ObjectHeader { + id: None, + otype: object_type, + }); { - writer.init(first_key, urids.int, first_value).unwrap(); + writer.init(first_key, urids.int).unwrap().set(first_value); } { - writer.init(second_key, urids.float, second_value).unwrap(); + writer + .init(second_key, urids.float) + .unwrap() + .set(second_value); } } @@ -427,11 +422,11 @@ mod tests { let (header, atom) = properties[0]; assert_eq!(header.key, first_key); - assert_eq!(atom.read(urids.int).unwrap(), first_value); + assert_eq!(*atom.read(urids.int).unwrap(), first_value); let (header, atom) = properties[1]; assert_eq!(header.key, second_key); - assert_eq!(atom.read(urids.float).unwrap(), second_value); + assert_eq!(*atom.read(urids.float).unwrap(), second_value); } } } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index c2f7bdab..6fa80260 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -48,8 +48,8 @@ pub trait ScalarAtom: UriBound { /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. #[inline] - unsafe fn read_scalar(body: &AtomSpace) -> Option { - body.read().next_value().copied() + unsafe fn read_scalar(body: &AtomSpace) -> Option<&Self::InternalType> { + body.read().next_value() } /// Try to write the atom into a space. @@ -78,12 +78,12 @@ impl<'handle, T: Copy + 'static> ScalarWriter<'handle, T> { } } -struct ScalarReaderHandle; +struct ScalarReaderHandle(PhantomData); impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarReaderHandle { type Handle = &'handle T; } -struct ScalarWriterHandle; +struct ScalarWriterHandle(PhantomData); impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarWriterHandle { type Handle = ScalarWriter<'handle, T>; } @@ -98,9 +98,7 @@ impl Atom for A { ::read_scalar(body) } - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle> { + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { ::write_scalar(frame) } } @@ -203,7 +201,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - space.init_atom(urid, value).unwrap(); + space.init_atom(urid).unwrap().set(value); } // verifying diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 7bfad682..f4c1e6dd 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -37,8 +37,7 @@ //! // You have to provide the unit of the time stamps. //! let mut output_sequence: SequenceWriter = ports.output.init( //! urids.atom.sequence, -//! TimeStampURID::Frames(urids.units.frame) -//! ).unwrap(); +//! ).unwrap().with_unit(urids.units.beat); //! //! // Iterate through all events in the input sequence. //! // @@ -52,7 +51,7 @@ //! // If the read atom is a 32-bit integer... //! if let Some(integer) = atom.read(urids.atom.int, ()) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap(); +//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(integer * 2); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); @@ -94,7 +93,7 @@ impl<'handle> AtomHandle<'handle> for SequenceReadHandle { struct SequenceWriteHandle; impl<'handle> AtomHandle<'handle> for SequenceWriteHandle { - type Handle = SequenceWriter<'handle>; + type Handle = SequenceHeaderWriter<'handle>; } pub struct SequenceHeaderReader<'handle> { @@ -155,9 +154,7 @@ impl Atom for Sequence { Some(SequenceHeaderReader { reader, header }) } - fn init<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle> { + fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { let header = frame.write_value(MaybeUninit::uninit())?; Some(SequenceHeaderWriter { header, frame }) @@ -288,14 +285,14 @@ impl<'handle> SequenceWriter<'handle> { Some(()) } - /// Initialize an event. + /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init<'a, A: Atom>( - &'a mut self, + pub fn init( + &mut self, stamp: TimeStamp, urid: URID, - ) -> Option { + ) -> Option<::Handle> { self.write_time_stamp(stamp)?; self.frame.init_atom(urid) } @@ -338,19 +335,19 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space - .init_atom( - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame), - ) - .unwrap(); + .init_atom(urids.atom.sequence) + .unwrap() + .with_unit(TimeStampURID::Frames(urids.units.frame)); writer - .init::(TimeStamp::Frames(0), urids.atom.int, 42) - .unwrap(); + .init::(TimeStamp::Frames(0), urids.atom.int) + .unwrap() + .set(42); writer - .init::(TimeStamp::Frames(1), urids.atom.long, 17) - .unwrap(); + .init::(TimeStamp::Frames(1), urids.atom.long) + .unwrap() + .set(17); } // verifying @@ -391,16 +388,18 @@ mod tests { let mut reader = unsafe { raw_space.read().next_atom() } .unwrap() .read(urids.atom.sequence) - .unwrap(); + .unwrap() + .read(urids.units.beat); + assert_eq!(reader.unit(), TimeStampUnit::Frames); let (stamp, atom) = reader.next().unwrap(); assert_eq!(stamp, TimeStamp::Frames(0)); - assert_eq!(atom.read::(urids.atom.int, ()).unwrap(), 42); + assert_eq!(atom.read::(urids.atom.int).unwrap(), 42); let (stamp, atom) = reader.next().unwrap(); assert_eq!(stamp, TimeStamp::Frames(1)); - assert_eq!(atom.read::(urids.atom.long, ()).unwrap(), 17); + assert_eq!(atom.read::(urids.atom.long).unwrap(), 17); assert!(reader.next().is_none()); } diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 2693511d..82cd50a7 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -30,6 +30,7 @@ use crate::prelude::*; use crate::space::*; use crate::AtomHandle; +use std::mem::MaybeUninit; use urid::*; /// An atom containing either a localized string or an RDF literal. @@ -49,11 +50,12 @@ pub enum LiteralInfo { } pub struct LiteralInfoWriter<'a> { + body: &'a mut MaybeUninit, frame: AtomSpaceWriter<'a>, } impl<'a> LiteralInfoWriter<'a> { - pub fn write_info(mut self, info: LiteralInfo) -> Option> { + pub fn write_info(mut self, info: LiteralInfo) -> StringWriter<'a> { self.frame.write_value(match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), @@ -63,12 +65,12 @@ impl<'a> LiteralInfoWriter<'a> { lang: 0, datatype: datatype.get(), }, - })?; + }); - Some(StringWriter { + StringWriter { frame: self.frame, has_nul_byte: false, - }) + } } } @@ -81,7 +83,7 @@ impl<'a> AtomHandle<'a> for LiteralReadHandle { struct LiteralWriteHandle; impl<'a> AtomHandle<'a> for LiteralWriteHandle { - type Handle = StringWriter<'a>; + type Handle = LiteralInfoWriter<'a>; } impl Atom for Literal { @@ -111,10 +113,9 @@ impl Atom for Literal { } #[inline] - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle> { - Some(LiteralInfoWriter { frame }) + fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { + let body = frame.write_value(MaybeUninit::uninit())?; + Some(LiteralInfoWriter { body, frame }) } } @@ -151,9 +152,7 @@ impl Atom for String { Some(core::str::from_utf8(rust_str_bytes).ok()?) } - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle> { + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { Some(StringWriter { frame, has_nul_byte: false, @@ -231,11 +230,9 @@ mod tests { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space - .init_atom( - urids.atom.literal, - LiteralInfo::Language(urids.german.into_general()), - ) - .unwrap(); + .init_atom(urids.atom.literal) + .unwrap() + .write_info(LiteralInfo::Language(urids.german.into_general())); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } @@ -292,7 +289,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.string, ()).unwrap(); + let mut writer = space.init_atom(urids.string).unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 6c87c857..52a1ad56 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -42,21 +42,31 @@ unsafe impl UriBound for Tuple { const URI: &'static [u8] = sys::LV2_ATOM__Tuple; } +struct TupleReadHandle; + +impl<'a> AtomHandle<'a> for TupleReadHandle { + type Handle = TupleIterator<'a>; +} + +struct TupleWriteHandle; + +impl<'a> AtomHandle<'a> for TupleWriteHandle { + type Handle = TupleWriter<'a>; +} + impl Atom for Tuple { - type ReadHandle = TupleIterator<'handle>; - type WriteHandle = TupleWriter<'handle, 'space>; + type ReadHandle = TupleReadHandle; + type WriteHandle = TupleWriteHandle; unsafe fn read<'handle, 'space: 'handle>( body: &'space AtomSpace, - ) -> Option<>::Handle> { + ) -> Option<>::Handle> { Some(TupleIterator { reader: body.read(), }) } - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'handle, 'space>, - ) -> Option<>::Handle> { + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { Some(TupleWriter { frame }) } } @@ -78,18 +88,17 @@ impl<'a> Iterator for TupleIterator<'a> { } /// The writing handle to add atoms to a tuple. -pub struct TupleWriter<'handle, 'space> { - frame: AtomSpaceWriter<'handle, 'space>, +pub struct TupleWriter<'space> { + frame: AtomSpaceWriter<'space>, } -impl<'handle, 'space> TupleWriter<'handle, 'space> { +impl<'space> TupleWriter<'space> { /// Initialize a new tuple element. - pub fn init<'a, A: Atom<'a, 'space>>( - &'a mut self, + pub fn init( + &mut self, child_urid: URID, - child_parameter: A::WriteParameter, - ) -> Option { - self.frame.init_atom(child_urid, child_parameter) + ) -> Option<::Handle> { + self.frame.init_atom(child_urid) } } @@ -112,12 +121,12 @@ mod tests { // writing { let mut cursor = raw_space.write(); - let mut writer = cursor.init_atom(urids.tuple, ()).unwrap(); + let mut writer = cursor.init_atom(urids.tuple).unwrap(); { - let mut vector_writer = writer.init(urids.vector, urids.int).unwrap(); + let mut vector_writer = writer.init(urids.vector).unwrap().of_type(urids.int); vector_writer.append(&[17; 9]).unwrap(); } - writer.init(urids.int, 42).unwrap(); + writer.init(urids.int).unwrap().set(42); } // verifying diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 403b9654..214915a6 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -31,6 +31,7 @@ //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Vector](http://lv2plug.in/ns/ext/atom/atom.html#Vector) use crate::atoms::scalar::ScalarAtom; +use crate::space::reader::SpaceReader; use crate::*; use std::marker::PhantomData; use std::mem::{size_of, MaybeUninit}; @@ -38,63 +39,97 @@ use std::mem::{size_of, MaybeUninit}; /// An atom containg an array of scalar atom bodies. /// /// [See also the module documentation.](index.html) -pub struct Vector { - child: PhantomData, -} +pub struct Vector; -unsafe impl UriBound for Vector { +unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } -impl Atom for Vector { - type ReadHandle = &'space [C::InternalType]; - type WriteHandle = VectorWriter<'handle, 'space, C>; +struct VectorReadHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { - let mut reader = body.read(); - let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; +impl<'a> AtomHandle<'a> for VectorReadHandle { + type Handle = VectorReader<'a>; +} + +struct VectorWriteHandle; - if header.child_type != child_urid - || header.child_size as usize != size_of::() +impl<'a> AtomHandle<'a> for VectorWriteHandle { + type Handle = VectorTypeWriter<'a>; +} + +pub struct VectorReader<'a> { + reader: SpaceReader<'a>, + header: &'a sys::LV2_Atom_Vector_Body, +} + +impl<'a> VectorReader<'a> { + pub fn of_type(self, atom_type: URID) -> Option<&'a [C::InternalType]> { + if self.header.child_type != atom_type + || self.header.child_size as usize != size_of::() { return None; } - // SAFETY: We can assume this data was properly initialized by the host. - reader.as_slice() + // SAFETY: The data type has just been checked above, and we can assume this data was + // properly initialized by the host. + unsafe { self.reader.as_slice() } } +} + +pub struct VectorTypeWriter<'a> { + header: &'a mut MaybeUninit, + writer: AtomSpaceWriter<'a>, +} - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'handle, 'space>, - ) -> Option<>::Handle> { +impl<'a> VectorTypeWriter<'a> { + pub fn of_type(mut self, atom_type: URID) -> VectorWriter<'a, C> { let body = sys::LV2_Atom_Vector_Body { - child_type: child_urid.get(), + child_type: atom_type.get(), child_size: size_of::() as u32, }; - frame.write_value(body)?; - Some(VectorWriter { - frame, + crate::util::write_uninit(&mut self.header, body); + + VectorWriter { + writer: self.writer, type_: PhantomData, - }) + } + } +} + +impl Atom for Vector { + type ReadHandle = VectorReadHandle; + type WriteHandle = VectorWriteHandle; + + unsafe fn read<'handle, 'space: 'handle>( + body: &'space AtomSpace, + ) -> Option<>::Handle> { + let mut reader = body.read(); + let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; + + Some(VectorReader { reader, header }) + } + + fn init(mut writer: AtomSpaceWriter) -> Option<::Handle> { + let header = writer.write_value(MaybeUninit::uninit())?; + + Some(VectorTypeWriter { writer, header }) } } /// Handle to append elements to a vector. /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. -pub struct VectorWriter<'handle, 'space, A: ScalarAtom> { - frame: AtomSpaceWriter<'handle, 'space>, +pub struct VectorWriter<'handle, A: ScalarAtom> { + writer: AtomSpaceWriter<'handle>, type_: PhantomData, } -impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { +impl<'handle, A: ScalarAtom> VectorWriter<'handle, A> { /// Push a single value to the vector. #[inline] pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { - self.frame.write_value(child) + self.writer.write_value(child) } /// Append a slice of undefined memory to the vector. @@ -102,13 +137,13 @@ impl<'handle, 'space, A: ScalarAtom> VectorWriter<'handle, 'space, A> { /// Using this method, you don't need to have the elements in memory before you can write them. #[inline] pub fn allocate_uninit(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { - self.frame.allocate_values(count) + self.writer.allocate_values(count) } /// Append multiple elements to the vector. #[inline] pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { - self.frame.write_values(data) + self.writer.write_values(data) } } @@ -159,7 +194,7 @@ mod tests { // reading { let atom = unsafe { raw_space.read().next_atom() }.unwrap(); - let children: &[i32] = atom.read(urids.vector, urids.int).unwrap(); + let children: &[i32] = atom.read(urids.vector).unwrap(); assert_eq!(children.len(), CHILD_COUNT); for i in 0..children.len() - 1 { diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 5f61c4ab..c393b23e 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -134,9 +134,7 @@ pub trait Atom: UriBound { /// The frame of the atom was already initialized, containing the URID. /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. - fn init<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'space>, - ) -> Option<>::Handle>; + fn init(frame: AtomSpaceWriter) -> Option<::Handle>; } /// An atom of yet unknown type. diff --git a/atom/src/port.rs b/atom/src/port.rs index 8cd85f81..082cc775 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -50,10 +50,10 @@ impl<'space> PortReader<'space> { /// /// This method returns `None` if the atom is malformed or simply isn't of the specified type. #[inline] - pub fn read<'handle, A: crate::Atom>( + pub fn read( &self, urid: URID, - ) -> Option<>::Handle> { + ) -> Option<::Handle> { self.atom.read(urid) } } @@ -82,10 +82,10 @@ impl<'space> PortWriter<'space> { /// Please note that you can call this method once only, because any atoms written behind the first one will not be identified. /// /// This method returns `None` if the space of the port isn't big enough or if the method was called multiple times. - pub fn init<'b, 'write, A: crate::Atom>( - &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. + pub fn init<'a, 'write, A: crate::Atom>( + &'a mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, - ) -> Option<>::Handle> { + ) -> Option<>::Handle> { if !self.has_been_written { self.has_been_written = true; // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 311389f5..ebdd171e 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -1,5 +1,5 @@ use crate::space::{AlignedSpace, AtomSpaceWriter}; -use crate::{Atom, UnidentifiedAtom}; +use crate::{Atom, AtomHandle, UnidentifiedAtom}; use urid::URID; use core::mem::size_of_val; @@ -56,7 +56,10 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { } #[inline] - fn init_atom(&mut self, atom_type: URID) -> Option { + fn init_atom( + &mut self, + atom_type: URID, + ) -> Option<::Handle> { let space = AtomSpaceWriter::write_new(self, atom_type)?; A::init(space) } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 67af119b..7ee2cae2 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -5,14 +5,14 @@ use urid::URID; pub struct AtomSpaceWriterHandle; -impl<'handle, 'space: 'handle> AtomHandle<'handle, 'space> for AtomSpaceWriterHandle { +impl<'handle> AtomHandle<'handle> for AtomSpaceWriterHandle { type Handle = AtomSpaceWriter<'handle>; } /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'handle> { atom_header_index: usize, - parent: &'handle mut (dyn SpaceAllocatorImpl<'handle>), + parent: &'handle mut (dyn SpaceAllocatorImpl), } impl<'handle> AtomSpaceWriter<'handle> { @@ -41,7 +41,7 @@ impl<'handle> AtomSpaceWriter<'handle> { /// Create a new framed space with the given parent and type URID. pub fn write_new( - parent: &'handle mut impl SpaceAllocator<'space>, + parent: &'handle mut impl SpaceAllocator, urid: URID, ) -> Option { let atom = AtomHeader::new(urid); @@ -56,7 +56,7 @@ impl<'handle> AtomSpaceWriter<'handle> { } } -impl<'handle> SpaceAllocatorImpl<'handle> for AtomSpaceWriter<'handle> { +impl<'handle> SpaceAllocatorImpl for AtomSpaceWriter<'handle> { #[inline] fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (previous, current) = self.parent.allocate_and_split(size)?; diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index fd5135c6..42eeabdd 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -14,7 +14,7 @@ impl<'a> SpaceCursor<'a> { } } -impl<'a> SpaceAllocatorImpl<'a> for SpaceCursor<'a> { +impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { #[inline] fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (allocated, allocatable) = self.data.split_at_mut(self.allocated_length); diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index f0b04c2a..7a607c39 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -499,7 +499,7 @@ mod tests { } } - fn test_mut_space<'a>(mut space: impl SpaceAllocator<'a>) { + fn test_mut_space<'a>(mut space: impl SpaceAllocator) { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index c991c3d7..0f5ad96c 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -75,7 +75,7 @@ pub struct VecSpaceCursor<'vec, T> { allocated_length: usize, } -impl<'vec, T: Copy + 'static> SpaceAllocatorImpl<'vec> for VecSpaceCursor<'vec, T> { +impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let end = self.allocated_length.checked_add(size)?; let result = VecSpace::::reallocate_bytes_mut(self.vec, self.allocated_length..end); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..acc7a37e --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +format_code_in_doc_comments = true +reorder_impl_items = true +use_field_init_shorthand = true +wrap_comments = true diff --git a/state/tests/integration.rs b/state/tests/integration.rs index b34f11dc..1c7201c9 100644 --- a/state/tests/integration.rs +++ b/state/tests/integration.rs @@ -89,7 +89,7 @@ fn create_plugin(mapper: Pin<&mut HostMap>) -> Stateful { // Constructing the plugin. Stateful::new( &PluginInfo::new(Stateful::uri(), Path::new("./"), 44100.0), - &mut Features { map: map }, + &mut Features { map }, ) .unwrap() }; From 2e4f411829204fcee80ecff955c7f88793ec04f1 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Sep 2021 14:32:51 +0200 Subject: [PATCH 29/54] Fixed everything --- atom/src/atoms/chunk.rs | 4 +- atom/src/atoms/object.rs | 45 ++++++++--------- atom/src/atoms/scalar.rs | 31 +++++++----- atom/src/atoms/sequence.rs | 38 +++++++-------- atom/src/atoms/string.rs | 29 +++++------ atom/src/atoms/tuple.rs | 33 ++++++++----- atom/src/atoms/vector.rs | 31 ++++++------ atom/src/lib.rs | 10 ++-- atom/src/port.rs | 6 +-- atom/src/space/allocatable.rs | 34 ++++++++++--- atom/src/space/atom_writer.rs | 11 +++++ atom/src/space/space.rs | 18 +++++-- atom/tests/atom_integration.rs | 55 ++++++++++++--------- docs/fifths/src/lib.rs | 32 +++++------- docs/metro/src/lib.rs | 3 +- docs/metro/src/pipes.rs | 12 ++--- docs/midigate/src/lib.rs | 32 ++++++++---- midi/src/lib.rs | 14 +++--- midi/src/raw.rs | 26 ++++++---- midi/src/wmidi_binding.rs | 89 +++++++++++++++++++++++----------- state/src/lib.rs | 8 +-- state/src/path.rs | 4 +- state/src/raw.rs | 57 +++++++++++++--------- state/tests/integration.rs | 16 ++++-- 24 files changed, 375 insertions(+), 263 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index abdc765e..aee36b12 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -38,12 +38,12 @@ unsafe impl UriBound for Chunk { const URI: &'static [u8] = sys::LV2_ATOM__Chunk; } -struct ChunkReaderHandle; +pub struct ChunkReaderHandle; impl<'handle> AtomHandle<'handle> for ChunkReaderHandle { type Handle = &'handle AtomSpace; } -struct ChunkWriterHandle; +pub struct ChunkWriterHandle; impl<'handle> AtomHandle<'handle> for ChunkWriterHandle { type Handle = AtomSpaceWriter<'handle>; } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index ea0c9fa0..73a00380 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -70,9 +70,8 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Object](http://lv2plug.in/ns/ext/atom/atom.html#Object). use crate::space::reader::SpaceReader; use crate::*; -use std::convert::TryFrom; -use std::iter::Iterator; -use std::mem::MaybeUninit; +use core::convert::TryFrom; +use core::iter::Iterator; use urid::UriBound; use urid::URID; @@ -96,32 +95,28 @@ pub struct ObjectHeader { pub otype: URID, } -struct ObjectReaderHandle; +pub struct ObjectReaderHandle; impl<'handle> AtomHandle<'handle> for ObjectReaderHandle { type Handle = (ObjectHeader, ObjectReader<'handle>); } -struct ObjectWriterHandle; +pub struct ObjectWriterHandle; impl<'handle> AtomHandle<'handle> for ObjectWriterHandle { type Handle = ObjectHeaderWriter<'handle>; } pub struct ObjectHeaderWriter<'handle> { - header: &'handle mut MaybeUninit, frame: AtomSpaceWriter<'handle>, } impl<'handle> ObjectHeaderWriter<'handle> { - pub fn write_header(mut self, header: ObjectHeader) -> ObjectWriter<'handle> { - crate::util::write_uninit( - &mut self.header, - sys::LV2_Atom_Object_Body { - id: header.id.map(URID::get).unwrap_or(0), - otype: header.otype.get(), - }, - ); - - ObjectWriter { frame: self.frame } + pub fn write_header(mut self, header: ObjectHeader) -> Option> { + self.frame.write_value(sys::LV2_Atom_Object_Body { + id: header.id.map(URID::get).unwrap_or(0), + otype: header.otype.get(), + })?; + + Some(ObjectWriter { frame: self.frame }) } } @@ -145,9 +140,8 @@ impl Atom for Object { Some((header, reader)) } - fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { - let header = frame.write_value(MaybeUninit::uninit())?; - Some(ObjectHeaderWriter { header, frame }) + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { + Some(ObjectHeaderWriter { frame }) } } @@ -174,7 +168,7 @@ impl Atom for Blank { } #[inline] - fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { Object::init(frame) } } @@ -339,10 +333,13 @@ mod tests { { let mut cursor = SpaceCursor::new(raw_space.as_bytes_mut()); let frame = AtomSpaceWriter::write_new(&mut cursor, urids.object).unwrap(); - let mut writer = Object::init(frame).unwrap().write_header(ObjectHeader { - id: None, - otype: object_type, - }); + let mut writer = Object::init(frame) + .unwrap() + .write_header(ObjectHeader { + id: None, + otype: object_type, + }) + .unwrap(); { writer.init(first_key, urids.int).unwrap().set(first_value); } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 6fa80260..f9a83ef6 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -30,8 +30,7 @@ //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Number](http://lv2plug.in/ns/ext/atom/atom.html#Number) use crate::*; -use std::marker::{PhantomData, Unpin}; -use std::mem::MaybeUninit; +use core::marker::{PhantomData, Unpin}; use urid::UriBound; use urid::URID; @@ -57,33 +56,39 @@ pub trait ScalarAtom: UriBound { /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. #[inline] fn write_scalar<'handle, 'space: 'handle>( - mut frame: AtomSpaceWriter<'space>, + frame: AtomSpaceWriter<'space>, ) -> Option> { - let value = frame.write_value(MaybeUninit::::uninit())?; - // Scalars have extra padding due to repr(C) - frame.allocate_padding_for::(); - Some(ScalarWriter(value)) + Some(ScalarWriter(frame.re_borrow(), PhantomData)) } } -pub struct ScalarWriter<'handle, T: Copy + 'static>(&'handle mut MaybeUninit); +pub struct ScalarWriter<'handle, T: Copy + 'static>(AtomSpaceWriter<'handle>, PhantomData); impl<'handle, T: Copy + 'static> ScalarWriter<'handle, T> { #[inline] - pub fn set<'a>(&mut self, value: T) -> &mut T + pub fn set<'a>(&mut self, value: T) -> Option<&mut T> where 'a: 'handle, { - crate::util::write_uninit(&mut self.0, value) + #[repr(align(8))] + #[derive(Copy, Clone)] + struct Padder; + + #[derive(Copy, Clone)] + #[repr(C)] + struct ScalarValue(T, Padder); + + // Scalars have extra padding due to previous header in LV2_ATOM_Int and such + Some(&mut self.0.write_value(ScalarValue(value, Padder))?.0) } } -struct ScalarReaderHandle(PhantomData); +pub struct ScalarReaderHandle(PhantomData); impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarReaderHandle { type Handle = &'handle T; } -struct ScalarWriterHandle(PhantomData); +pub struct ScalarWriterHandle(PhantomData); impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarWriterHandle { type Handle = ScalarWriter<'handle, T>; } @@ -228,7 +233,7 @@ mod tests { .read(urid) .unwrap(); - assert_eq!(read_value, value); + assert_eq!(*read_value, value); } } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index f4c1e6dd..db251b5a 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -37,7 +37,7 @@ //! // You have to provide the unit of the time stamps. //! let mut output_sequence: SequenceWriter = ports.output.init( //! urids.atom.sequence, -//! ).unwrap().with_unit(urids.units.beat); +//! ).unwrap().with_unit(TimeStampURID::BeatsPerMinute(urids.units.beat)); //! //! // Iterate through all events in the input sequence. //! // @@ -49,9 +49,9 @@ //! // An event contains a timestamp and an atom. //! let (timestamp, atom): (TimeStamp, &UnidentifiedAtom) = event; //! // If the read atom is a 32-bit integer... -//! if let Some(integer) = atom.read(urids.atom.int, ()) { +//! if let Some(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(integer * 2); +//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); @@ -65,7 +65,6 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) use crate::space::reader::SpaceReader; use crate::*; -use std::mem::MaybeUninit; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; use units::prelude::*; @@ -86,12 +85,12 @@ unsafe impl UriBound for Sequence { const URI: &'static [u8] = sys::LV2_ATOM__Sequence; } -struct SequenceReadHandle; +pub struct SequenceReadHandle; impl<'handle> AtomHandle<'handle> for SequenceReadHandle { type Handle = SequenceHeaderReader<'handle>; } -struct SequenceWriteHandle; +pub struct SequenceWriteHandle; impl<'handle> AtomHandle<'handle> for SequenceWriteHandle { type Handle = SequenceHeaderWriter<'handle>; } @@ -117,8 +116,7 @@ impl<'handle> SequenceHeaderReader<'handle> { } pub struct SequenceHeaderWriter<'handle> { - header: &'handle mut MaybeUninit, - frame: AtomSpaceWriter<'handle>, + writer: AtomSpaceWriter<'handle>, } impl<'a> SequenceHeaderWriter<'a> { @@ -131,10 +129,10 @@ impl<'a> SequenceHeaderWriter<'a> { pad: 0, }); - crate::util::write_uninit(&mut self.header, header); + self.writer.write_value(header); SequenceWriter { - frame: self.frame, + writer: self.writer, unit: unit.into(), last_stamp: None, } @@ -154,10 +152,8 @@ impl Atom for Sequence { Some(SequenceHeaderReader { reader, header }) } - fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { - let header = frame.write_value(MaybeUninit::uninit())?; - - Some(SequenceHeaderWriter { header, frame }) + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { + Some(SequenceHeaderWriter { writer: frame }) } } @@ -246,7 +242,7 @@ impl<'a> Iterator for SequenceIterator<'a> { /// The writing handle for sequences. pub struct SequenceWriter<'handle> { - frame: AtomSpaceWriter<'handle>, + writer: AtomSpaceWriter<'handle>, unit: TimeStampUnit, last_stamp: Option, } @@ -280,7 +276,7 @@ impl<'handle> SequenceWriter<'handle> { } }; self.last_stamp = Some(stamp); - self.frame.write_value(TimestampBody(raw_stamp))?; + self.writer.write_value(TimestampBody(raw_stamp))?; Some(()) } @@ -294,7 +290,7 @@ impl<'handle> SequenceWriter<'handle> { urid: URID, ) -> Option<::Handle> { self.write_time_stamp(stamp)?; - self.frame.init_atom(urid) + self.writer.init_atom(urid) } /// Forward an unidentified atom to the sequence. @@ -305,7 +301,7 @@ impl<'handle> SequenceWriter<'handle> { pub fn forward(&mut self, stamp: TimeStamp, atom: &UnidentifiedAtom) -> Option<()> { self.write_time_stamp(stamp)?; - self.frame.forward_atom(atom)?; + self.writer.forward_atom(atom)?; Some(()) } @@ -326,7 +322,7 @@ mod tests { #[test] fn test_sequence() { let map = HashURIDMapper::new(); - let urids = TestURIDCollection::from_map(&map).unwrap(); + let urids: TestURIDCollection = TestURIDCollection::from_map(&map).unwrap(); let mut raw_space = VecSpace::::new_with_capacity(64); let raw_space = raw_space.as_space_mut(); @@ -395,11 +391,11 @@ mod tests { let (stamp, atom) = reader.next().unwrap(); assert_eq!(stamp, TimeStamp::Frames(0)); - assert_eq!(atom.read::(urids.atom.int).unwrap(), 42); + assert_eq!(*atom.read::(urids.atom.int).unwrap(), 42); let (stamp, atom) = reader.next().unwrap(); assert_eq!(stamp, TimeStamp::Frames(1)); - assert_eq!(atom.read::(urids.atom.long).unwrap(), 17); + assert_eq!(*atom.read::(urids.atom.long).unwrap(), 17); assert!(reader.next().is_none()); } diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 82cd50a7..ce690756 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -30,7 +30,6 @@ use crate::prelude::*; use crate::space::*; use crate::AtomHandle; -use std::mem::MaybeUninit; use urid::*; /// An atom containing either a localized string or an RDF literal. @@ -50,13 +49,12 @@ pub enum LiteralInfo { } pub struct LiteralInfoWriter<'a> { - body: &'a mut MaybeUninit, - frame: AtomSpaceWriter<'a>, + writer: AtomSpaceWriter<'a>, } impl<'a> LiteralInfoWriter<'a> { pub fn write_info(mut self, info: LiteralInfo) -> StringWriter<'a> { - self.frame.write_value(match info { + self.writer.write_value(match info { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), datatype: 0, @@ -68,19 +66,19 @@ impl<'a> LiteralInfoWriter<'a> { }); StringWriter { - frame: self.frame, + writer: self.writer, has_nul_byte: false, } } } -struct LiteralReadHandle; +pub struct LiteralReadHandle; impl<'a> AtomHandle<'a> for LiteralReadHandle { type Handle = (LiteralInfo, &'a str); } -struct LiteralWriteHandle; +pub struct LiteralWriteHandle; impl<'a> AtomHandle<'a> for LiteralWriteHandle { type Handle = LiteralInfoWriter<'a>; @@ -113,19 +111,18 @@ impl Atom for Literal { } #[inline] - fn init(mut frame: AtomSpaceWriter) -> Option<::Handle> { - let body = frame.write_value(MaybeUninit::uninit())?; - Some(LiteralInfoWriter { body, frame }) + fn init(frame: AtomSpaceWriter) -> Option<::Handle> { + Some(LiteralInfoWriter { writer: frame }) } } -struct StringReadHandle; +pub struct StringReadHandle; impl<'a> AtomHandle<'a> for StringReadHandle { type Handle = &'a str; } -struct StringWriteHandle; +pub struct StringWriteHandle; impl<'a> AtomHandle<'a> for StringWriteHandle { type Handle = StringWriter<'a>; @@ -154,7 +151,7 @@ impl Atom for String { fn init(frame: AtomSpaceWriter) -> Option<::Handle> { Some(StringWriter { - frame, + writer: frame, has_nul_byte: false, }) } @@ -162,7 +159,7 @@ impl Atom for String { /// Handle to append strings to a string or literal. pub struct StringWriter<'a> { - frame: AtomSpaceWriter<'a>, + writer: AtomSpaceWriter<'a>, has_nul_byte: bool, // If this writer already wrote a null byte before. } @@ -176,14 +173,14 @@ impl<'a> StringWriter<'a> { // Rewind to overwrite previously written nul_byte before appending the string. if self.has_nul_byte { // SAFETY: it is safe to overwrite the nul byte - if unsafe { !self.frame.rewind(1) } { + if unsafe { !self.writer.rewind(1) } { return None; // Could not rewind } } // Manually write the bytes to make extra room for the nul byte let bytes = string.as_bytes(); - let space = self.frame.allocate(bytes.len() + 1)?; + let space = self.writer.allocate(bytes.len() + 1)?; space[..bytes.len()].copy_from_slice(bytes); // SAFETY: space is guaranteed to be at least 1 byte large space[bytes.len()] = 0; diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 52a1ad56..bfbc4205 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -15,13 +15,13 @@ //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { -//! let input: TupleIterator = ports.input.read(urids.tuple, ()).unwrap(); -//! let mut output: TupleWriter = ports.output.init(urids.tuple, ()).unwrap(); +//! let input: TupleIterator = ports.input.read(urids.tuple).unwrap(); +//! let mut output: TupleWriter = ports.output.init(urids.tuple).unwrap(); //! for atom in input { -//! if let Some(integer) = atom.read(urids.int, ()) { -//! output.init(urids.int, integer * 2).unwrap(); +//! if let Some(integer) = atom.read(urids.int) { +//! output.init(urids.int).unwrap().set(*integer * 2).unwrap(); //! } else { -//! output.init(urids.int, -1).unwrap(); +//! output.init(urids.int).unwrap().set(-1).unwrap(); //! } //! } //! } @@ -42,13 +42,13 @@ unsafe impl UriBound for Tuple { const URI: &'static [u8] = sys::LV2_ATOM__Tuple; } -struct TupleReadHandle; +pub struct TupleReadHandle; impl<'a> AtomHandle<'a> for TupleReadHandle { type Handle = TupleIterator<'a>; } -struct TupleWriteHandle; +pub struct TupleWriteHandle; impl<'a> AtomHandle<'a> for TupleWriteHandle { type Handle = TupleWriter<'a>; @@ -123,7 +123,11 @@ mod tests { let mut cursor = raw_space.write(); let mut writer = cursor.init_atom(urids.tuple).unwrap(); { - let mut vector_writer = writer.init(urids.vector).unwrap().of_type(urids.int); + let mut vector_writer = writer + .init(urids.vector) + .unwrap() + .of_type(urids.int) + .unwrap(); vector_writer.append(&[17; 9]).unwrap(); } writer.init(urids.int).unwrap().set(42); @@ -165,9 +169,16 @@ mod tests { // reading { let body = unsafe { raw_space.read().next_atom().unwrap().body() }; - let items: Vec<&UnidentifiedAtom> = unsafe { Tuple::read(body, ()) }.unwrap().collect(); - assert_eq!(items[0].read(urids.vector, urids.int).unwrap(), [17; 9]); - assert_eq!(items[1].read(urids.int, ()).unwrap(), 42); + let items: Vec<&UnidentifiedAtom> = unsafe { Tuple::read(body) }.unwrap().collect(); + assert_eq!( + items[0] + .read(urids.vector) + .unwrap() + .of_type(urids.int) + .unwrap(), + [17; 9] + ); + assert_eq!(*items[1].read(urids.int).unwrap(), 42); } } } diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 214915a6..baa2f73e 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -19,8 +19,8 @@ //! } //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { -//! let input: &[i32] = ports.input.read(urids.vector(), urids.int).unwrap(); -//! let mut output: VectorWriter = ports.output.init(urids.vector(), urids.int).unwrap(); +//! let input: &[i32] = ports.input.read(urids.vector).unwrap().of_type(urids.int).unwrap(); +//! let mut output: VectorWriter = ports.output.init(urids.vector).unwrap().of_type(urids.int).unwrap(); //! output.append(input).unwrap(); //! } //! ``` @@ -45,13 +45,13 @@ unsafe impl UriBound for Vector { const URI: &'static [u8] = sys::LV2_ATOM__Vector; } -struct VectorReadHandle; +pub struct VectorReadHandle; impl<'a> AtomHandle<'a> for VectorReadHandle { type Handle = VectorReader<'a>; } -struct VectorWriteHandle; +pub struct VectorWriteHandle; impl<'a> AtomHandle<'a> for VectorWriteHandle { type Handle = VectorTypeWriter<'a>; @@ -77,23 +77,22 @@ impl<'a> VectorReader<'a> { } pub struct VectorTypeWriter<'a> { - header: &'a mut MaybeUninit, writer: AtomSpaceWriter<'a>, } impl<'a> VectorTypeWriter<'a> { - pub fn of_type(mut self, atom_type: URID) -> VectorWriter<'a, C> { + pub fn of_type(mut self, atom_type: URID) -> Option> { let body = sys::LV2_Atom_Vector_Body { child_type: atom_type.get(), child_size: size_of::() as u32, }; - crate::util::write_uninit(&mut self.header, body); + self.writer.write_value(body)?; - VectorWriter { + Some(VectorWriter { writer: self.writer, type_: PhantomData, - } + }) } } @@ -110,10 +109,8 @@ impl Atom for Vector { Some(VectorReader { reader, header }) } - fn init(mut writer: AtomSpaceWriter) -> Option<::Handle> { - let header = writer.write_value(MaybeUninit::uninit())?; - - Some(VectorTypeWriter { writer, header }) + fn init(writer: AtomSpaceWriter) -> Option<::Handle> { + Some(VectorTypeWriter { writer }) } } @@ -167,7 +164,11 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.vector(), urids.int).unwrap(); + let mut writer = space + .init_atom(urids.vector) + .unwrap() + .of_type(urids.int) + .unwrap(); writer.append(&[42; CHILD_COUNT - 1]); writer.push(1); } @@ -194,7 +195,7 @@ mod tests { // reading { let atom = unsafe { raw_space.read().next_atom() }.unwrap(); - let children: &[i32] = atom.read(urids.vector).unwrap(); + let children: &[i32] = atom.read(urids.vector).unwrap().of_type(urids.int).unwrap(); assert_eq!(children.len(), CHILD_COUNT); for i in 0..children.len() - 1 { diff --git a/atom/src/lib.rs b/atom/src/lib.rs index c393b23e..53145d27 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -33,14 +33,12 @@ //! // Get the read handle to the sequence. //! let input_sequence = ports.input.read( //! urids.atom.sequence, -//! urids.units.beat -//! ).unwrap(); +//! ).unwrap().read(urids.units.beat); //! //! // Get the write handle to the sequence. //! let mut output_sequence = ports.output.init( //! urids.atom.sequence, -//! TimeStampURID::Frames(urids.units.frame) -//! ).unwrap(); +//! ).unwrap().with_unit(TimeStampURID::Frames(urids.units.frame)); //! //! // Iterate through all events in the input sequence. //! for event in input_sequence { @@ -49,7 +47,7 @@ //! // If the read atom is a 32-bit integer... //! if let Some(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap(); +//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); @@ -134,7 +132,7 @@ pub trait Atom: UriBound { /// The frame of the atom was already initialized, containing the URID. /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. - fn init(frame: AtomSpaceWriter) -> Option<::Handle>; + fn init(writer: AtomSpaceWriter) -> Option<::Handle>; } /// An atom of yet unknown type. diff --git a/atom/src/port.rs b/atom/src/port.rs index 082cc775..8f3289b4 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -144,7 +144,7 @@ mod tests { // writing a chunk to indicate the size of the space. { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.chunk, ()).unwrap(); + let mut writer = space.init_atom(urids.chunk).unwrap(); writer.allocate(256 - size_of::()).unwrap(); } @@ -153,7 +153,7 @@ mod tests { let mut writer = unsafe { AtomPort::output_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) }; - writer.init::(urids.int, 42).unwrap(); + writer.init::(urids.int).unwrap().set(42).unwrap(); } // Reading @@ -163,7 +163,7 @@ mod tests { .read(urids.chunk) .unwrap(); let reader = unsafe { AtomPort::input_from_raw(NonNull::from(chunk).cast(), 0) }; - assert_eq!(reader.read::(urids.int, ()).unwrap(), 42); + assert_eq!(*reader.read::(urids.int).unwrap(), 42); } } } diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index ebdd171e..c85f07c9 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -2,8 +2,7 @@ use crate::space::{AlignedSpace, AtomSpaceWriter}; use crate::{Atom, AtomHandle, UnidentifiedAtom}; use urid::URID; -use core::mem::size_of_val; -use std::mem::MaybeUninit; +use core::mem::{size_of, size_of_val, MaybeUninit}; /// A smart pointer that writes atom data to an internal slice. /// @@ -49,6 +48,13 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { AlignedSpace::align_from_bytes_mut(raw) } + #[inline] + fn allocate_value(&mut self) -> Option<&mut MaybeUninit> { + let space = self.allocate_aligned(size_of::>())?; + // SAFETY: We used size_of, so we are sure that the allocated space is exactly big enough for T. + Some(unsafe { space.as_uninit_mut_unchecked() }) + } + #[inline] fn allocate_values(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { let space = self.allocate_aligned(count * std::mem::size_of::())?; @@ -89,10 +95,20 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { let space = self.allocate_aligned(size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. let space = unsafe { space.as_uninit_mut_unchecked() }; - *space = MaybeUninit::new(value); - // SAFETY: the MaybeUninit has now been properly initialized. - Some(unsafe { crate::util::assume_init_mut(space) }) + Some(crate::util::write_uninit(space, value)) + } + + #[inline] + fn write_value_padded(&mut self, value: T) -> Option<&mut T> { + let space = self + .allocate_aligned::(size_of::>())? + .realign_mut()?; + + // SAFETY: We used size_of, so we are sure that the allocated space is exactly big enough for T. + let space = unsafe { space.as_uninit_mut_unchecked() }; + + Some(crate::util::write_uninit(space, value)) } fn write_values(&mut self, values: &[T]) -> Option<&mut [T]> @@ -121,6 +137,7 @@ mod tests { use crate::AtomHeader; use urid::URID; + // SAFETY: this is just for testing, values aren't actually read using this URID. const INT_URID: URID = unsafe { URID::new_unchecked(5) }; #[test] @@ -135,17 +152,18 @@ mod tests { assert_eq!(31, cursor.remaining_bytes().len()); { - cursor.init_atom(INT_URID, 69).unwrap(); + cursor.init_atom(INT_URID).unwrap().set(69); assert_eq!(8, cursor.remaining_bytes().len()); } - assert_eq!( + /*assert_eq!( space.as_bytes(), [ + // FIXME: this test is endianness-sensitive 42, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ] - ); + );*/ assert_eq!(32, space.as_bytes().len()); } } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 7ee2cae2..5b817004 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -16,6 +16,17 @@ pub struct AtomSpaceWriter<'handle> { } impl<'handle> AtomSpaceWriter<'handle> { + #[inline] + pub fn re_borrow<'a>(self) -> AtomSpaceWriter<'a> + where + 'handle: 'a, + { + AtomSpaceWriter { + atom_header_index: self.atom_header_index, + parent: self.parent, + } + } + #[inline] pub fn atom_header(&self) -> AtomHeader { let previous = self diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 7a607c39..8dbfd50a 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -129,9 +129,13 @@ impl AlignedSpace { /// ``` #[inline] pub fn align_from_bytes(data: &[u8]) -> Option<&Self> { + let data = data.get(crate::util::padding_for::(data)?..)?; + if data.is_empty() { + return None; + } + // SAFETY: We just aligned the slice start - data.get(crate::util::padding_for::(data)?..) - .map(|data| unsafe { AlignedSpace::from_bytes_unchecked(data) }) + Some(unsafe { AlignedSpace::from_bytes_unchecked(data) }) } /// Creates a new mutable space from a mutable slice of bytes, slicing some bytes off its start it if necessary. @@ -158,9 +162,13 @@ impl AlignedSpace { /// ``` #[inline] pub fn align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { - // SAFETY: We just aligned the slice's start - data.get_mut(crate::util::padding_for::(data)?..) - .map(|data| unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) + let data = data.get_mut(crate::util::padding_for::(data)?..)?; + if data.is_empty() { + return None; + } + + // SAFETY: We just aligned the slice start + Some(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) } /// Creates a space from an empty slice. diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index d113e8eb..d9ae740e 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -46,22 +46,23 @@ impl Plugin for AtomPlugin { fn run(&mut self, ports: &mut Ports, _: &mut (), _: u32) { let sequence_reader = ports .input - .read::(self.urids.atom.sequence, self.urids.units.beat) - .unwrap(); + .read::(self.urids.atom.sequence) + .unwrap() + .read(self.urids.units.beat); let mut sequence_writer = ports .output - .init::( - self.urids.atom.sequence, - TimeStampURID::Frames(self.urids.units.frame), - ) - .unwrap(); + .init::(self.urids.atom.sequence) + .unwrap() + .with_unit(TimeStampURID::Frames(self.urids.units.frame)); for (time_stamp, atom) in sequence_reader { - match atom.read(self.urids.atom.int, ()) { + match atom.read(self.urids.atom.int) { Some(number) => { sequence_writer - .init::(time_stamp, self.urids.atom.int, number * 2) + .init(time_stamp, self.urids.atom.int) + .unwrap() + .set(number * 2) .unwrap(); } None => { @@ -105,21 +106,26 @@ fn main() { { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); let mut writer = space - .init_atom( - urids.atom.sequence, - TimeStampURID::Frames(urids.units.frame), - ) - .unwrap(); + .init_atom(urids.atom.sequence) + .unwrap() + .with_unit(TimeStampURID::Frames(urids.units.frame)); { let _ = writer - .init(TimeStamp::Frames(0), urids.atom.int, 42) + .init(TimeStamp::Frames(0), urids.atom.int) + .unwrap() + .set(42) .unwrap(); } writer - .init(TimeStamp::Frames(1), urids.atom.long, 17) + .init(TimeStamp::Frames(1), urids.atom.long) + .unwrap() + .set(17) .unwrap(); + writer - .init(TimeStamp::Frames(2), urids.atom.int, 3) + .init(TimeStamp::Frames(2), urids.atom.int) + .unwrap() + .set(3) .unwrap(); } @@ -129,7 +135,7 @@ fn main() { { let mut space = SpaceCursor::new(output_atom_space.as_bytes_mut()); space - .init_atom(urids.atom.chunk, ()) + .init_atom(urids.atom.chunk) .unwrap() .allocate(256 - size_of::()) .unwrap(); @@ -174,21 +180,22 @@ fn main() { let chunk = unsafe { output_atom_space.read().next_atom() } .unwrap() - .read(urids.atom.chunk, ()) + .read(urids.atom.chunk) .unwrap(); // Asserting the result let sequence = unsafe { chunk.read().next_atom() } .unwrap() - .read(urids.atom.sequence, urids.units.beat) - .unwrap(); + .read(urids.atom.sequence) + .unwrap() + .read(urids.units.beat); for (stamp, atom) in sequence { let stamp = stamp.as_frames().unwrap(); match stamp { - 0 => assert_eq!(atom.read(urids.atom.int, ()).unwrap(), 84), - 1 => assert_eq!(atom.read(urids.atom.long, ()).unwrap(), 17), - 2 => assert_eq!(atom.read(urids.atom.int, ()).unwrap(), 6), + 0 => assert_eq!(*atom.read(urids.atom.int).unwrap(), 84), + 1 => assert_eq!(*atom.read(urids.atom.long).unwrap(), 17), + 2 => assert_eq!(*atom.read(urids.atom.int).unwrap(), 6), _ => panic!("Invalid time stamp in sequence!"), } } diff --git a/docs/fifths/src/lib.rs b/docs/fifths/src/lib.rs index 7e963c5d..9652829a 100644 --- a/docs/fifths/src/lib.rs +++ b/docs/fifths/src/lib.rs @@ -42,24 +42,23 @@ impl Plugin for Fifths { // Get the reading handle of the input sequence. let input_sequence = ports .input - .read(self.urids.atom.sequence, self.urids.unit.beat) - .unwrap(); + .read(self.urids.atom.sequence) + .unwrap() + .read(self.urids.unit.beat); // Initialise the output sequence and get the writing handle. let mut output_sequence = ports .output - .init( - self.urids.atom.sequence, - TimeStampURID::Frames(self.urids.unit.frame), - ) - .unwrap(); + .init(self.urids.atom.sequence) + .unwrap() + .with_unit(TimeStampURID::Frames(self.urids.unit.frame)); for (timestamp, atom) in input_sequence { // Every message is forwarded, regardless of it's content. output_sequence.forward(timestamp, atom); // Retrieve the message. - let message = if let Some(message) = atom.read(self.urids.midi.wmidi, ()) { + let message = if let Some(message) = atom.read(self.urids.midi.wmidi) { message } else { continue; @@ -71,11 +70,9 @@ impl Plugin for Fifths { if let Ok(note) = note.step(7) { // Write the fifth. Writing is done after initialization. output_sequence - .init( - timestamp, - self.urids.midi.wmidi, - MidiMessage::NoteOn(channel, note, velocity), - ) + .init(timestamp, self.urids.midi.wmidi) + .unwrap() + .set(MidiMessage::NoteOn(channel, note, velocity)) .unwrap(); } } @@ -83,12 +80,9 @@ impl Plugin for Fifths { // Do the same thing for `NoteOff`. if let Ok(note) = note.step(7) { output_sequence - .init( - timestamp, - self.urids.midi.wmidi, - MidiMessage::NoteOff(channel, note, velocity), - ) - .unwrap(); + .init(timestamp, self.urids.midi.wmidi) + .unwrap() + .set(MidiMessage::NoteOff(channel, note, velocity)); } } _ => (), diff --git a/docs/metro/src/lib.rs b/docs/metro/src/lib.rs index 582bed0e..76af5bf8 100644 --- a/docs/metro/src/lib.rs +++ b/docs/metro/src/lib.rs @@ -77,7 +77,8 @@ impl Plugin for Metro { fn run(&mut self, ports: &mut Ports, _: &mut (), _: u32) { if let Some(control) = ports .control - .read(self.urids.atom.sequence, self.urids.unit.beat) + .read(self.urids.atom.sequence) + .map(|s| s.read(self.urids.unit.beat)) { // Here, the final assembly of the pipeline is done. First, the event iterator is pre-processed to only emit an index and an `UnidentifiedAtom`. Then, the event iterator is wrapped into an `EventAtomizer`, which is then connected to an `EventReader` and the envelope. The resulting pipe consumes a `()` and emits the next frame of the envelope; It's already a compact pipeline. // diff --git a/docs/metro/src/pipes.rs b/docs/metro/src/pipes.rs index 83db00fc..478f12b3 100644 --- a/docs/metro/src/pipes.rs +++ b/docs/metro/src/pipes.rs @@ -324,21 +324,21 @@ impl<'a> Pipe for EventReader<'a> { if let Some(atom) = atom { if let Some((object_header, object_reader)) = atom - .read(self.atom_urids.object, ()) - .or_else(|| atom.read(self.atom_urids.blank, ())) + .read(self.atom_urids.object) + .or_else(|| atom.read(self.atom_urids.blank)) { if object_header.otype == self.time_urids.position_class { for (property_header, property) in object_reader { if property_header.key == self.time_urids.bar_beat { updates.beat_update = property - .read(self.atom_urids.float, ()) - .map(|float| float as f64); + .read(self.atom_urids.float) + .map(|float| *float as f64); } if property_header.key == self.time_urids.beats_per_minute { - updates.bpm_update = property.read(self.atom_urids.float, ()); + updates.bpm_update = property.read(self.atom_urids.float).copied(); } if property_header.key == self.time_urids.speed { - updates.speed_update = property.read(self.atom_urids.float, ()); + updates.speed_update = property.read(self.atom_urids.float).copied(); } } } diff --git a/docs/midigate/src/lib.rs b/docs/midigate/src/lib.rs index feb33897..7736c184 100644 --- a/docs/midigate/src/lib.rs +++ b/docs/midigate/src/lib.rs @@ -30,9 +30,15 @@ pub struct Midigate { impl Midigate { // A function to write a chunk of output, to be called from `run()`. If the gate is high, then the input will be passed through for this chunk, otherwise silence is written. - fn write_output(&mut self, ports: &mut Ports, offset: usize, mut len: usize) { - if ports.input.len() < offset + len { - len = ports.input.len() - offset; + fn write_output( + &mut self, + input: &InputPort, - parameter: A::WriteParameter, - ) -> Result { + ) -> Result<>::Handle, StateErr> { if !self.initialized { self.initialized = true; - self.cursor - .init_atom(urid, parameter) - .ok_or(StateErr::Unknown) + self.cursor.init_atom(urid).ok_or(StateErr::Unknown) } else { Err(StateErr::Unknown) } @@ -214,13 +212,12 @@ impl<'a> StatePropertyReader<'a> { /// This works like any atom reader: You pass the URID of the atom type as well as the type-specific argument, and if the desired type is the actual type of the data, a read handle is returned. /// /// If the desired and actual data types don't match, `Err(StateErr::BadType)` is returned. - pub fn read>( + pub fn read( &self, urid: URID, - parameter: A::ReadParameter, - ) -> Result { + ) -> Result<>::Handle, StateErr> { if urid == self.type_ { - unsafe { A::read(self.body, parameter) }.ok_or(StateErr::Unknown) + unsafe { A::read(self.body) }.ok_or(StateErr::Unknown) } else { Err(StateErr::BadType) } @@ -238,25 +235,32 @@ mod tests { store_handle .draft(URID::new(1).unwrap()) - .init(urids.int, 17) - .unwrap(); + .init(urids.int) + .unwrap() + .set(17); store_handle .draft(URID::new(2).unwrap()) - .init(urids.float, 1.0) - .unwrap(); + .init(urids.float) + .unwrap() + .set(1.0); store_handle.commit(URID::new(1).unwrap()).unwrap().unwrap(); let mut vector_writer = store_handle.draft(URID::new(3).unwrap()); - let mut vector_writer = vector_writer.init(urids.vector(), urids.int).unwrap(); + let mut vector_writer = vector_writer + .init(urids.vector) + .unwrap() + .of_type(urids.int) + .unwrap(); vector_writer.append(&[1, 2, 3, 4]).unwrap(); store_handle.commit_all().unwrap(); store_handle .draft(URID::new(4).unwrap()) - .init(urids.int, 0) - .unwrap(); + .init(urids.int) + .unwrap() + .set(0); } fn retrieve(storage: &mut Storage, urids: &AtomURIDCollection) { @@ -264,18 +268,18 @@ mod tests { assert_eq!( 17, - retrieve_handle + *retrieve_handle .retrieve(URID::new(1).unwrap()) .unwrap() - .read(urids.int, ()) + .read(urids.int) .unwrap() ); assert_eq!( 1.0, - retrieve_handle + *retrieve_handle .retrieve(URID::new(2).unwrap()) .unwrap() - .read(urids.float, ()) + .read(urids.float) .unwrap() ); assert_eq!( @@ -283,7 +287,9 @@ mod tests { retrieve_handle .retrieve(URID::new(3).unwrap()) .unwrap() - .read(urids.vector(), urids.int) + .read(urids.vector) + .unwrap() + .of_type(urids.int) .unwrap() ); assert!(retrieve_handle.retrieve(URID::new(4).unwrap()).is_err()); @@ -311,9 +317,12 @@ mod tests { }); } 3 => { - assert_eq!(urids.vector::(), *type_); + assert_eq!(urids.vector, *type_); let space = AlignedSpace::try_from_bytes(value.as_slice()).unwrap(); - let data = unsafe { Vector::read(space, urids.int) }.unwrap(); + let data = unsafe { Vector::read(space) } + .unwrap() + .of_type(urids.int) + .unwrap(); assert_eq!([1, 2, 3, 4], data); } _ => panic!("Invalid key!"), diff --git a/state/tests/integration.rs b/state/tests/integration.rs index 1c7201c9..4a1b8094 100644 --- a/state/tests/integration.rs +++ b/state/tests/integration.rs @@ -52,23 +52,29 @@ impl State for Stateful { fn save(&self, mut store: StoreHandle, _: ()) -> Result<(), StateErr> { store .draft(URID::new(1000).unwrap()) - .init(self.urids.float, self.internal)?; + .init(self.urids.float)? + .set(self.internal) + .ok_or(StateErr::Unknown)?; store .draft(URID::new(1001).unwrap()) - .init(self.urids.vector(), self.urids.float)? + .init(self.urids.vector)? + .of_type(self.urids.float) + .ok_or(StateErr::Unknown)? .append(self.audio.as_ref()); store.commit_all() } fn restore(&mut self, store: RetrieveHandle, _: ()) -> Result<(), StateErr> { - self.internal = store + self.internal = *store .retrieve(URID::new(1000).unwrap())? - .read(self.urids.float, ())?; + .read(self.urids.float)?; self.audio = Vec::from( store .retrieve(URID::new(1001).unwrap())? - .read(self.urids.vector(), self.urids.float)?, + .read(self.urids.vector)? + .of_type(self.urids.float) + .ok_or(StateErr::BadData)?, ); Ok(()) } From 91b30755771b675e44ef866dde3aae23055e8c5c Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Sep 2021 22:25:01 +0200 Subject: [PATCH 30/54] Remove unnecessary Atom::read lifetimes --- atom/src/atoms/chunk.rs | 4 +--- atom/src/atoms/object.rs | 8 ++------ atom/src/atoms/scalar.rs | 4 +--- atom/src/atoms/sequence.rs | 4 +--- atom/src/atoms/string.rs | 8 ++------ atom/src/atoms/tuple.rs | 4 +--- atom/src/atoms/vector.rs | 4 +--- atom/src/lib.rs | 4 +--- atom/src/space/space.rs | 4 ++-- midi/src/raw.rs | 2 +- midi/src/wmidi_binding.rs | 8 ++------ 11 files changed, 15 insertions(+), 39 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index aee36b12..3d9b158d 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -53,9 +53,7 @@ impl Atom for Chunk { type WriteHandle = AtomSpaceWriterHandle; #[inline] - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { Some(body) } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 73a00380..d88956da 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -124,9 +124,7 @@ impl Atom for Object { type ReadHandle = ObjectReaderHandle; type WriteHandle = ObjectWriterHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; @@ -161,9 +159,7 @@ impl Atom for Blank { type WriteHandle = ::WriteHandle; #[inline] - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { Object::read(body) } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index f9a83ef6..fa93d68e 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -97,9 +97,7 @@ impl Atom for A { type ReadHandle = ScalarReaderHandle; type WriteHandle = ScalarWriterHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { ::read_scalar(body) } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index db251b5a..1c0c1e3b 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -143,9 +143,7 @@ impl Atom for Sequence { type ReadHandle = SequenceReadHandle; type WriteHandle = SequenceWriteHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index ce690756..e52ffcd0 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -88,9 +88,7 @@ impl Atom for Literal { type ReadHandle = LiteralReadHandle; type WriteHandle = LiteralWriteHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; @@ -141,9 +139,7 @@ impl Atom for String { type ReadHandle = StringReadHandle; type WriteHandle = StringWriteHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { let data = body.as_bytes(); let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator Some(core::str::from_utf8(rust_str_bytes).ok()?) diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index bfbc4205..f8b706e3 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -58,9 +58,7 @@ impl Atom for Tuple { type ReadHandle = TupleReadHandle; type WriteHandle = TupleWriteHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { Some(TupleIterator { reader: body.read(), }) diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index baa2f73e..92d64aa4 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -100,9 +100,7 @@ impl Atom for Vector { type ReadHandle = VectorReadHandle; type WriteHandle = VectorWriteHandle; - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle> { + unsafe fn read(body: &AtomSpace) -> Option<::Handle> { let mut reader = body.read(); let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 53145d27..9f644e36 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -120,9 +120,7 @@ pub trait Atom: UriBound { /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read<'handle, 'space: 'handle>( - body: &'space AtomSpace, - ) -> Option<>::Handle>; + unsafe fn read(body: &AtomSpace) -> Option<::Handle>; /// Initialize the body of the atom. /// diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 8dbfd50a..2d021645 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -12,12 +12,12 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// This buffer can be split and realigned, effectively allowing to read a stream of arbitrary types /// from a byte buffer, such as [`Atom`s](crate::Atom). /// -/// Any operation that may lead to a misaligned `Space` is considered unsafe. +/// Any operation that may lead to a misaligned `AlignedSpace` is considered unsafe. /// /// Note that only the start of the slice is aligned, not the end. This allows having a buffer that /// is bigger than a multiple of `T`'s alignment. /// -/// Although they are aligned, `Space`s do not consider their contents to be initialized. Therefore, +/// Although they are aligned, `AlignedSpace`s do not consider their contents to be initialized. Therefore, /// the only safe reading operations will return `MaybeUninit`. Unsafe helper methods that assume /// the contents are initialized are also provided, for convenience. /// diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 3fcb22b4..b7db9c42 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -32,7 +32,7 @@ impl Atom for MidiEvent { type ReadHandle = MidiEventReadHandle; type WriteHandle = MidiEventWriteHandle; - unsafe fn read<'handle, 'space: 'handle>(body: &'space AtomSpace) -> Option<&'handle [u8]> { + unsafe fn read(body: &AtomSpace) -> Option<&[u8]> { Some(body.as_bytes()) } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 3e880ef2..708bc618 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -51,9 +51,7 @@ impl Atom for WMidiEvent { type WriteHandle = WMidiEventWriteHandle; #[inline] - unsafe fn read<'handle, 'space: 'handle>( - space: &'space AtomSpace, - ) -> Option> { + unsafe fn read(space: &AtomSpace) -> Option { wmidi::MidiMessage::try_from(space.as_bytes()).ok() } @@ -85,9 +83,7 @@ impl Atom for SystemExclusiveWMidiEvent { type WriteHandle = SystemExclusiveWMidiEventWriteHandle; #[inline] - unsafe fn read<'handle, 'space: 'handle>( - space: &'space AtomSpace, - ) -> Option> { + unsafe fn read(space: &AtomSpace) -> Option { WMidiEvent::read(space) } From 5fc58bf6c8b609d68d843fca4896ee8584fb3fdf Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Sep 2021 22:31:46 +0200 Subject: [PATCH 31/54] Remove more unnecessary lifetimes --- atom/src/atoms/chunk.rs | 8 ++++---- atom/src/atoms/object.rs | 16 ++++++++-------- atom/src/atoms/scalar.rs | 21 ++++++++------------- atom/src/atoms/sequence.rs | 28 ++++++++++++++-------------- atom/src/atoms/tuple.rs | 6 +++--- atom/src/atoms/vector.rs | 6 +++--- atom/src/lib.rs | 13 +++++-------- atom/src/port.rs | 20 ++++++++++---------- atom/src/space/atom_writer.rs | 18 +++++++++--------- 9 files changed, 64 insertions(+), 72 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 3d9b158d..6c2d95c6 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -39,13 +39,13 @@ unsafe impl UriBound for Chunk { } pub struct ChunkReaderHandle; -impl<'handle> AtomHandle<'handle> for ChunkReaderHandle { - type Handle = &'handle AtomSpace; +impl<'a> AtomHandle<'a> for ChunkReaderHandle { + type Handle = &'a AtomSpace; } pub struct ChunkWriterHandle; -impl<'handle> AtomHandle<'handle> for ChunkWriterHandle { - type Handle = AtomSpaceWriter<'handle>; +impl<'a> AtomHandle<'a> for ChunkWriterHandle { + type Handle = AtomSpaceWriter<'a>; } impl Atom for Chunk { diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index d88956da..4ee6e622 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -96,21 +96,21 @@ pub struct ObjectHeader { } pub struct ObjectReaderHandle; -impl<'handle> AtomHandle<'handle> for ObjectReaderHandle { - type Handle = (ObjectHeader, ObjectReader<'handle>); +impl<'a> AtomHandle<'a> for ObjectReaderHandle { + type Handle = (ObjectHeader, ObjectReader<'a>); } pub struct ObjectWriterHandle; -impl<'handle> AtomHandle<'handle> for ObjectWriterHandle { - type Handle = ObjectHeaderWriter<'handle>; +impl<'a> AtomHandle<'a> for ObjectWriterHandle { + type Handle = ObjectHeaderWriter<'a>; } -pub struct ObjectHeaderWriter<'handle> { - frame: AtomSpaceWriter<'handle>, +pub struct ObjectHeaderWriter<'a> { + frame: AtomSpaceWriter<'a>, } -impl<'handle> ObjectHeaderWriter<'handle> { - pub fn write_header(mut self, header: ObjectHeader) -> Option> { +impl<'a> ObjectHeaderWriter<'a> { + pub fn write_header(mut self, header: ObjectHeader) -> Option> { self.frame.write_value(sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), otype: header.otype.get(), diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index fa93d68e..4ae3c5f0 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -55,21 +55,16 @@ pub trait ScalarAtom: UriBound { /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. #[inline] - fn write_scalar<'handle, 'space: 'handle>( - frame: AtomSpaceWriter<'space>, - ) -> Option> { + fn write_scalar(frame: AtomSpaceWriter) -> Option> { Some(ScalarWriter(frame.re_borrow(), PhantomData)) } } -pub struct ScalarWriter<'handle, T: Copy + 'static>(AtomSpaceWriter<'handle>, PhantomData); +pub struct ScalarWriter<'a, T: Copy + 'static>(AtomSpaceWriter<'a>, PhantomData); -impl<'handle, T: Copy + 'static> ScalarWriter<'handle, T> { +impl<'a, T: Copy + 'static> ScalarWriter<'a, T> { #[inline] - pub fn set<'a>(&mut self, value: T) -> Option<&mut T> - where - 'a: 'handle, - { + pub fn set(&mut self, value: T) -> Option<&mut T> { #[repr(align(8))] #[derive(Copy, Clone)] struct Padder; @@ -84,13 +79,13 @@ impl<'handle, T: Copy + 'static> ScalarWriter<'handle, T> { } pub struct ScalarReaderHandle(PhantomData); -impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarReaderHandle { - type Handle = &'handle T; +impl<'a, T: Copy + 'static> AtomHandle<'a> for ScalarReaderHandle { + type Handle = &'a T; } pub struct ScalarWriterHandle(PhantomData); -impl<'handle, T: Copy + 'static> AtomHandle<'handle> for ScalarWriterHandle { - type Handle = ScalarWriter<'handle, T>; +impl<'a, T: Copy + 'static> AtomHandle<'a> for ScalarWriterHandle { + type Handle = ScalarWriter<'a, T>; } impl Atom for A { diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 1c0c1e3b..0c4eb4f9 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -86,22 +86,22 @@ unsafe impl UriBound for Sequence { } pub struct SequenceReadHandle; -impl<'handle> AtomHandle<'handle> for SequenceReadHandle { - type Handle = SequenceHeaderReader<'handle>; +impl<'a> AtomHandle<'a> for SequenceReadHandle { + type Handle = SequenceHeaderReader<'a>; } pub struct SequenceWriteHandle; -impl<'handle> AtomHandle<'handle> for SequenceWriteHandle { - type Handle = SequenceHeaderWriter<'handle>; +impl<'a> AtomHandle<'a> for SequenceWriteHandle { + type Handle = SequenceHeaderWriter<'a>; } -pub struct SequenceHeaderReader<'handle> { - header: &'handle sys::LV2_Atom_Sequence_Body, - reader: SpaceReader<'handle>, +pub struct SequenceHeaderReader<'a> { + header: &'a sys::LV2_Atom_Sequence_Body, + reader: SpaceReader<'a>, } -impl<'handle> SequenceHeaderReader<'handle> { - pub fn read(self, bpm_urid: URID) -> SequenceIterator<'handle> { +impl<'a> SequenceHeaderReader<'a> { + pub fn read(self, bpm_urid: URID) -> SequenceIterator<'a> { let unit = if self.header.unit == bpm_urid { TimeStampUnit::BeatsPerMinute } else { @@ -115,8 +115,8 @@ impl<'handle> SequenceHeaderReader<'handle> { } } -pub struct SequenceHeaderWriter<'handle> { - writer: AtomSpaceWriter<'handle>, +pub struct SequenceHeaderWriter<'a> { + writer: AtomSpaceWriter<'a>, } impl<'a> SequenceHeaderWriter<'a> { @@ -239,13 +239,13 @@ impl<'a> Iterator for SequenceIterator<'a> { } /// The writing handle for sequences. -pub struct SequenceWriter<'handle> { - writer: AtomSpaceWriter<'handle>, +pub struct SequenceWriter<'a> { + writer: AtomSpaceWriter<'a>, unit: TimeStampUnit, last_stamp: Option, } -impl<'handle> SequenceWriter<'handle> { +impl<'a> SequenceWriter<'a> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index f8b706e3..5f33a098 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -86,11 +86,11 @@ impl<'a> Iterator for TupleIterator<'a> { } /// The writing handle to add atoms to a tuple. -pub struct TupleWriter<'space> { - frame: AtomSpaceWriter<'space>, +pub struct TupleWriter<'a> { + frame: AtomSpaceWriter<'a>, } -impl<'space> TupleWriter<'space> { +impl<'a> TupleWriter<'a> { /// Initialize a new tuple element. pub fn init( &mut self, diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 92d64aa4..df7d24bf 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -115,12 +115,12 @@ impl Atom for Vector { /// Handle to append elements to a vector. /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. -pub struct VectorWriter<'handle, A: ScalarAtom> { - writer: AtomSpaceWriter<'handle>, +pub struct VectorWriter<'a, A: ScalarAtom> { + writer: AtomSpaceWriter<'a>, type_: PhantomData, } -impl<'handle, A: ScalarAtom> VectorWriter<'handle, A> { +impl<'a, A: ScalarAtom> VectorWriter<'a, A> { /// Push a single value to the vector. #[inline] pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 9f644e36..647d268e 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -90,8 +90,8 @@ pub mod prelude { pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; } -pub trait AtomHandle<'handle> { - type Handle: 'handle; +pub trait AtomHandle<'a> { + type Handle: 'a; } /// Atom type. @@ -103,12 +103,12 @@ pub trait Atom: UriBound { /// The return value of the `read` function. /// /// It may contain a reference to the atom and therefore may not outlive it. - type ReadHandle: for<'handle> AtomHandle<'handle>; + type ReadHandle: for<'a> AtomHandle<'a>; /// The return value of the `write` function. /// /// It may contain a reference to a `MutSpace` and therefore may not outlive it. - type WriteHandle: for<'handle> AtomHandle<'handle>; + type WriteHandle: for<'a> AtomHandle<'a>; /// Reads the body of the atom. /// @@ -178,10 +178,7 @@ impl UnidentifiedAtom { /// Try to read the atom. /// /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read<'handle, 'space: 'handle, A: Atom>( - &'space self, - urid: URID, - ) -> Option<>::Handle> { + pub fn read(&self, urid: URID) -> Option<::Handle> { if self.header.urid() != urid { return None; } diff --git a/atom/src/port.rs b/atom/src/port.rs index 8f3289b4..fe9c4697 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -34,13 +34,13 @@ use urid::URID; /// A handle to read atoms from a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to read atoms. -pub struct PortReader<'space> { - atom: &'space UnidentifiedAtom, +pub struct PortReader<'a> { + atom: &'a UnidentifiedAtom, } -impl<'space> PortReader<'space> { +impl<'a> PortReader<'a> { /// Create a new port reader. - fn new(atom: &'space UnidentifiedAtom) -> Self { + fn new(atom: &'a UnidentifiedAtom) -> Self { Self { atom } } @@ -61,14 +61,14 @@ impl<'space> PortReader<'space> { /// A handle to write atoms into a port. /// /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. -pub struct PortWriter<'space> { - space: SpaceCursor<'space>, +pub struct PortWriter<'a> { + space: SpaceCursor<'a>, has_been_written: bool, } -impl<'space> PortWriter<'space> { +impl<'a> PortWriter<'a> { /// Create a new port writer. - fn new(space: &'space mut AtomSpace) -> Self { + fn new(space: &'a mut AtomSpace) -> Self { Self { space: SpaceCursor::new(space.as_bytes_mut()), has_been_written: false, @@ -82,8 +82,8 @@ impl<'space> PortWriter<'space> { /// Please note that you can call this method once only, because any atoms written behind the first one will not be identified. /// /// This method returns `None` if the space of the port isn't big enough or if the method was called multiple times. - pub fn init<'a, 'write, A: crate::Atom>( - &'a mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. + pub fn init<'b, 'write, A: crate::Atom>( + &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, ) -> Option<>::Handle> { if !self.has_been_written { diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 5b817004..f6c39a04 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -5,21 +5,21 @@ use urid::URID; pub struct AtomSpaceWriterHandle; -impl<'handle> AtomHandle<'handle> for AtomSpaceWriterHandle { - type Handle = AtomSpaceWriter<'handle>; +impl<'a> AtomHandle<'a> for AtomSpaceWriterHandle { + type Handle = AtomSpaceWriter<'a>; } /// A `MutSpace` that tracks the amount of allocated space in an atom header. -pub struct AtomSpaceWriter<'handle> { +pub struct AtomSpaceWriter<'a> { atom_header_index: usize, - parent: &'handle mut (dyn SpaceAllocatorImpl), + parent: &'a mut (dyn SpaceAllocatorImpl), } -impl<'handle> AtomSpaceWriter<'handle> { +impl<'a> AtomSpaceWriter<'a> { #[inline] - pub fn re_borrow<'a>(self) -> AtomSpaceWriter<'a> + pub fn re_borrow<'b>(self) -> AtomSpaceWriter<'b> where - 'handle: 'a, + 'a: 'b, { AtomSpaceWriter { atom_header_index: self.atom_header_index, @@ -52,7 +52,7 @@ impl<'handle> AtomSpaceWriter<'handle> { /// Create a new framed space with the given parent and type URID. pub fn write_new( - parent: &'handle mut impl SpaceAllocator, + parent: &'a mut impl SpaceAllocator, urid: URID, ) -> Option { let atom = AtomHeader::new(urid); @@ -67,7 +67,7 @@ impl<'handle> AtomSpaceWriter<'handle> { } } -impl<'handle> SpaceAllocatorImpl for AtomSpaceWriter<'handle> { +impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { #[inline] fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { let (previous, current) = self.parent.allocate_and_split(size)?; From 0011183959e2ecf03749619ecc7c481478e0b414 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Sep 2021 15:39:29 +0200 Subject: [PATCH 32/54] Replace Option with Result in many atom read/write operation --- atom/src/atoms/chunk.rs | 12 +++-- atom/src/atoms/object.rs | 45 +++++++++++------- atom/src/atoms/scalar.rs | 18 +++++--- atom/src/atoms/sequence.rs | 64 +++++++++++++++----------- atom/src/atoms/string.rs | 83 +++++++++++++++++++++++----------- atom/src/atoms/tuple.rs | 16 ++++--- atom/src/atoms/vector.rs | 56 +++++++++++++++++------ atom/src/header.rs | 16 ++++++- atom/src/lib.rs | 37 +++++++++++---- atom/src/port.rs | 6 +-- atom/src/space.rs | 2 + atom/src/space/allocatable.rs | 63 +++++++++++++------------- atom/src/space/atom_writer.rs | 27 +++++++---- atom/src/space/cursor.rs | 25 +++++++--- atom/src/space/error.rs | 43 ++++++++++++++++++ atom/src/space/reader.rs | 64 ++++++++++++++++---------- atom/src/space/space.rs | 65 ++++++++++++-------------- atom/src/space/vec.rs | 27 +++++++---- atom/src/util.rs | 6 +++ atom/tests/atom_integration.rs | 4 +- docs/fifths/src/lib.rs | 9 ++-- docs/metro/src/lib.rs | 2 +- docs/metro/src/pipes.rs | 12 +++-- docs/midigate/src/lib.rs | 2 +- midi/src/raw.rs | 9 ++-- midi/src/wmidi_binding.rs | 31 ++++++++----- state/src/raw.rs | 12 +++-- state/tests/integration.rs | 6 +-- 28 files changed, 496 insertions(+), 266 deletions(-) create mode 100644 atom/src/space/error.rs diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 6c2d95c6..81b01783 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -53,13 +53,17 @@ impl Atom for Chunk { type WriteHandle = AtomSpaceWriterHandle; #[inline] - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { - Some(body) + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { + Ok(body) } #[inline] - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { - Some(frame) + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(frame) } } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 4ee6e622..9cff4cf9 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -110,13 +110,13 @@ pub struct ObjectHeaderWriter<'a> { } impl<'a> ObjectHeaderWriter<'a> { - pub fn write_header(mut self, header: ObjectHeader) -> Option> { + pub fn write_header(mut self, header: ObjectHeader) -> Result, AtomError> { self.frame.write_value(sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), otype: header.otype.get(), })?; - Some(ObjectWriter { frame: self.frame }) + Ok(ObjectWriter { frame: self.frame }) } } @@ -124,22 +124,28 @@ impl Atom for Object { type ReadHandle = ObjectReaderHandle; type WriteHandle = ObjectWriterHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), - otype: URID::try_from(header.otype).ok()?, + otype: URID::try_from(header.otype).map_err(|_| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + })?, }; let reader = ObjectReader { reader }; - Some((header, reader)) + Ok((header, reader)) } - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { - Some(ObjectHeaderWriter { frame }) + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(ObjectHeaderWriter { frame }) } } @@ -159,12 +165,16 @@ impl Atom for Blank { type WriteHandle = ::WriteHandle; #[inline] - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { Object::read(body) } #[inline] - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { Object::init(frame) } } @@ -183,6 +193,7 @@ impl<'a> Iterator for ObjectReader<'a> { // SAFETY: The fact that this contains a valid property is guaranteed by this type. self.reader .try_read(|reader| unsafe { Property::read_body(reader) }) + .ok() } } @@ -202,7 +213,7 @@ impl<'a> ObjectWriter<'a> { key: URID, context: URID, child_urid: URID, - ) -> Option<::Handle> { + ) -> Result<::Handle, AtomError> { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; self.frame.init_atom(child_urid) } @@ -216,7 +227,7 @@ impl<'a> ObjectWriter<'a> { &mut self, key: URID, child_urid: URID, - ) -> Option<::Handle> { + ) -> Result<::Handle, AtomError> { Property::write_header(&mut self.frame, key, None::>)?; self.frame.init_atom(child_urid) } @@ -262,17 +273,19 @@ impl Property { /// The caller must ensure that the given Space actually contains a valid property. unsafe fn read_body<'a>( reader: &mut SpaceReader<'a>, - ) -> Option<(PropertyHeader, &'a UnidentifiedAtom)> { + ) -> Result<(PropertyHeader, &'a UnidentifiedAtom), AtomError> { let header: &StrippedPropertyHeader = reader.next_value()?; let header = PropertyHeader { - key: URID::try_from(header.key).ok()?, + key: URID::try_from(header.key).map_err(|_| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + })?, context: URID::try_from(header.context).ok(), }; let atom = reader.next_atom()?; - Some((header, atom)) + Ok((header, atom)) } /// Write out the header of a property atom. @@ -283,14 +296,14 @@ impl Property { space: &mut impl SpaceAllocator, key: URID, context: Option>, - ) -> Option<()> { + ) -> Result<(), AtomError> { let header = StrippedPropertyHeader { key: key.get(), context: context.map(URID::get).unwrap_or(0), }; space.write_value(header)?; - Some(()) + Ok(()) } } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 4ae3c5f0..5337e53c 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -47,7 +47,7 @@ pub trait ScalarAtom: UriBound { /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. #[inline] - unsafe fn read_scalar(body: &AtomSpace) -> Option<&Self::InternalType> { + unsafe fn read_scalar(body: &AtomSpace) -> Result<&Self::InternalType, AtomError> { body.read().next_value() } @@ -55,8 +55,8 @@ pub trait ScalarAtom: UriBound { /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. #[inline] - fn write_scalar(frame: AtomSpaceWriter) -> Option> { - Some(ScalarWriter(frame.re_borrow(), PhantomData)) + fn write_scalar(frame: AtomSpaceWriter) -> Result, AtomError> { + Ok(ScalarWriter(frame.re_borrow(), PhantomData)) } } @@ -64,7 +64,7 @@ pub struct ScalarWriter<'a, T: Copy + 'static>(AtomSpaceWriter<'a>, PhantomData< impl<'a, T: Copy + 'static> ScalarWriter<'a, T> { #[inline] - pub fn set(&mut self, value: T) -> Option<&mut T> { + pub fn set(&mut self, value: T) -> Result<&mut T, AtomError> { #[repr(align(8))] #[derive(Copy, Clone)] struct Padder; @@ -74,7 +74,7 @@ impl<'a, T: Copy + 'static> ScalarWriter<'a, T> { struct ScalarValue(T, Padder); // Scalars have extra padding due to previous header in LV2_ATOM_Int and such - Some(&mut self.0.write_value(ScalarValue(value, Padder))?.0) + Ok(&mut self.0.write_value(ScalarValue(value, Padder))?.0) } } @@ -92,11 +92,15 @@ impl Atom for A { type ReadHandle = ScalarReaderHandle; type WriteHandle = ScalarWriterHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { ::read_scalar(body) } - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { ::write_scalar(frame) } } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 0c4eb4f9..17963ded 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -129,7 +129,7 @@ impl<'a> SequenceHeaderWriter<'a> { pad: 0, }); - self.writer.write_value(header); + self.writer.write_value(header); // FIXME SequenceWriter { writer: self.writer, @@ -143,15 +143,19 @@ impl Atom for Sequence { type ReadHandle = SequenceReadHandle; type WriteHandle = SequenceWriteHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; - Some(SequenceHeaderReader { reader, header }) + Ok(SequenceHeaderReader { reader, header }) } - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { - Some(SequenceHeaderWriter { writer: frame }) + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(SequenceHeaderWriter { writer: frame }) } } @@ -219,22 +223,24 @@ impl<'a> Iterator for SequenceIterator<'a> { fn next(&mut self) -> Option<(TimeStamp, &'a UnidentifiedAtom)> { let unit = self.unit; - self.reader.try_read(|reader| { - // SAFETY: The validity of the space's contents is guaranteed by this type. - let raw_stamp: &RawTimeStamp = unsafe { reader.next_value()? }; + self.reader + .try_read(|reader| { + // SAFETY: The validity of the space's contents is guaranteed by this type. + let raw_stamp: &RawTimeStamp = unsafe { reader.next_value()? }; - let stamp = match unit { - TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, - TimeStampUnit::BeatsPerMinute => unsafe { - TimeStamp::BeatsPerMinute(raw_stamp.beats) - }, - }; + let stamp = match unit { + TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, + TimeStampUnit::BeatsPerMinute => unsafe { + TimeStamp::BeatsPerMinute(raw_stamp.beats) + }, + }; - // SAFETY: The validity of the space's contents is guaranteed by this type. - let atom = unsafe { reader.next_atom()? }; + // SAFETY: The validity of the space's contents is guaranteed by this type. + let atom = unsafe { reader.next_atom()? }; - Some((stamp, atom)) - }) + Ok((stamp, atom)) + }) + .ok() } } @@ -252,22 +258,26 @@ impl<'a> SequenceWriter<'a> { /// * The time stamp is not measured in our unit. /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. - fn write_time_stamp(&mut self, stamp: TimeStamp) -> Option<()> { + fn write_time_stamp(&mut self, stamp: TimeStamp) -> Result<(), AtomError> { let raw_stamp = match self.unit { TimeStampUnit::Frames => { - let frames = stamp.as_frames()?; + let frames = stamp.as_frames().unwrap(); // TODO if let Some(last_stamp) = self.last_stamp { if last_stamp.as_frames().unwrap() > frames { - return None; + return Err(AtomError::InvalidAtomValue { + reading_type_uri: Sequence::uri(), + }); } } RawTimeStamp { frames } } TimeStampUnit::BeatsPerMinute => { - let beats = stamp.as_bpm()?; + let beats = stamp.as_bpm().unwrap(); // TODO if let Some(last_stamp) = self.last_stamp { if last_stamp.as_bpm().unwrap() > beats { - return None; + return Err(AtomError::InvalidAtomValue { + reading_type_uri: Sequence::uri(), + }); } } RawTimeStamp { beats } @@ -276,7 +286,7 @@ impl<'a> SequenceWriter<'a> { self.last_stamp = Some(stamp); self.writer.write_value(TimestampBody(raw_stamp))?; - Some(()) + Ok(()) } /// Initialize an event. @@ -286,7 +296,7 @@ impl<'a> SequenceWriter<'a> { &mut self, stamp: TimeStamp, urid: URID, - ) -> Option<::Handle> { + ) -> Result<::Handle, AtomError> { self.write_time_stamp(stamp)?; self.writer.init_atom(urid) } @@ -296,12 +306,12 @@ impl<'a> SequenceWriter<'a> { /// If your cannot identify the type of the atom but have to write it, you can simply forward it. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn forward(&mut self, stamp: TimeStamp, atom: &UnidentifiedAtom) -> Option<()> { + pub fn forward(&mut self, stamp: TimeStamp, atom: &UnidentifiedAtom) -> Result<(), AtomError> { self.write_time_stamp(stamp)?; self.writer.forward_atom(atom)?; - Some(()) + Ok(()) } } diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index e52ffcd0..4472a416 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -48,13 +48,19 @@ pub enum LiteralInfo { Datatype(URID), } -pub struct LiteralInfoWriter<'a> { - writer: AtomSpaceWriter<'a>, -} +impl LiteralInfo { + fn try_from_raw(header: &sys::LV2_Atom_Literal_Body) -> Option { + if header.lang != 0 && header.datatype == 0 { + Some(LiteralInfo::Language(URID::new(header.lang)?)) + } else if header.lang == 0 && header.datatype != 0 { + Some(LiteralInfo::Datatype(URID::new(header.datatype)?)) + } else { + None + } + } -impl<'a> LiteralInfoWriter<'a> { - pub fn write_info(mut self, info: LiteralInfo) -> StringWriter<'a> { - self.writer.write_value(match info { + fn into_raw(self) -> sys::LV2_Atom_Literal_Body { + match self { LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body { lang: lang.get(), datatype: 0, @@ -63,7 +69,17 @@ impl<'a> LiteralInfoWriter<'a> { lang: 0, datatype: datatype.get(), }, - }); + } + } +} + +pub struct LiteralInfoWriter<'a> { + writer: AtomSpaceWriter<'a>, +} + +impl<'a> LiteralInfoWriter<'a> { + pub fn write_info(mut self, info: LiteralInfo) -> StringWriter<'a> { + self.writer.write_value(info.into_raw()); // FIXME StringWriter { writer: self.writer, @@ -88,29 +104,32 @@ impl Atom for Literal { type ReadHandle = LiteralReadHandle; type WriteHandle = LiteralWriteHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; - let info = if header.lang != 0 && header.datatype == 0 { - LiteralInfo::Language(URID::new(header.lang)?) - } else if header.lang == 0 && header.datatype != 0 { - LiteralInfo::Datatype(URID::new(header.datatype)?) - } else { - return None; - }; + let info = + LiteralInfo::try_from_raw(header).ok_or_else(|| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + })?; let data = reader.remaining_bytes(); std::str::from_utf8(&data[0..data.len() - 1]) .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) - .ok() + .map_err(|_| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + }) .map(|string| (info, string)) } #[inline] - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { - Some(LiteralInfoWriter { writer: frame }) + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(LiteralInfoWriter { writer: frame }) } } @@ -139,14 +158,26 @@ impl Atom for String { type ReadHandle = StringReadHandle; type WriteHandle = StringWriteHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { let data = body.as_bytes(); - let rust_str_bytes = data.get(..data.len() - 1)?; // removing the null-terminator - Some(core::str::from_utf8(rust_str_bytes).ok()?) + let rust_str_bytes = + data.get(..data.len() - 1) + .ok_or_else(|| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + })?; // removing the null-terminator + Ok( + core::str::from_utf8(rust_str_bytes).map_err(|_| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + })?, + ) } - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { - Some(StringWriter { + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(StringWriter { writer: frame, has_nul_byte: false, }) @@ -165,12 +196,12 @@ impl<'a> StringWriter<'a> { /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. - pub fn append(&mut self, string: &str) -> Option<&mut str> { + pub fn append(&mut self, string: &str) -> Result<&mut str, AtomError> { // Rewind to overwrite previously written nul_byte before appending the string. if self.has_nul_byte { // SAFETY: it is safe to overwrite the nul byte if unsafe { !self.writer.rewind(1) } { - return None; // Could not rewind + return Err(AtomError::Unknown); // Could not rewind } } @@ -183,7 +214,7 @@ impl<'a> StringWriter<'a> { self.has_nul_byte = true; // SAFETY: We just wrote that string, therefore it is guaranteed to be valid UTF-8 - unsafe { Some(std::str::from_utf8_unchecked_mut(&mut space[..bytes.len()])) } + unsafe { Ok(std::str::from_utf8_unchecked_mut(&mut space[..bytes.len()])) } } } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 5f33a098..ab1a0782 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -58,14 +58,18 @@ impl Atom for Tuple { type ReadHandle = TupleReadHandle; type WriteHandle = TupleWriteHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { - Some(TupleIterator { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { + Ok(TupleIterator { reader: body.read(), }) } - fn init(frame: AtomSpaceWriter) -> Option<::Handle> { - Some(TupleWriter { frame }) + fn init( + frame: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(TupleWriter { frame }) } } @@ -81,7 +85,7 @@ impl<'a> Iterator for TupleIterator<'a> { fn next(&mut self) -> Option<&'a UnidentifiedAtom> { // SAFETY: the validity of the given space is guaranteed by this type. - unsafe { self.reader.next_atom() } + unsafe { self.reader.next_atom() }.ok() } } @@ -95,7 +99,7 @@ impl<'a> TupleWriter<'a> { pub fn init( &mut self, child_urid: URID, - ) -> Option<::Handle> { + ) -> Result<::Handle, AtomError> { self.frame.init_atom(child_urid) } } diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index df7d24bf..bbd0458f 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -63,11 +63,26 @@ pub struct VectorReader<'a> { } impl<'a> VectorReader<'a> { - pub fn of_type(self, atom_type: URID) -> Option<&'a [C::InternalType]> { - if self.header.child_type != atom_type - || self.header.child_size as usize != size_of::() - { - return None; + pub fn of_type( + self, + atom_type: URID, + ) -> Result<&'a [C::InternalType], AtomError> { + if self.header.child_type != atom_type { + return Err(AtomError::InvalidAtomUrid { + found_urid: URID::new(self.header.child_size).ok_or_else(|| { + AtomError::InvalidAtomValue { + reading_type_uri: Vector::uri(), + } + })?, + expected_urid: atom_type.into_general(), + expected_uri: C::uri(), + }); + } + + if self.header.child_size as usize != size_of::() { + return Err(AtomError::InvalidAtomValue { + reading_type_uri: Vector::uri(), + }); } // SAFETY: The data type has just been checked above, and we can assume this data was @@ -81,7 +96,10 @@ pub struct VectorTypeWriter<'a> { } impl<'a> VectorTypeWriter<'a> { - pub fn of_type(mut self, atom_type: URID) -> Option> { + pub fn of_type( + mut self, + atom_type: URID, + ) -> Result, AtomError> { let body = sys::LV2_Atom_Vector_Body { child_type: atom_type.get(), child_size: size_of::() as u32, @@ -89,7 +107,7 @@ impl<'a> VectorTypeWriter<'a> { self.writer.write_value(body)?; - Some(VectorWriter { + Ok(VectorWriter { writer: self.writer, type_: PhantomData, }) @@ -100,15 +118,19 @@ impl Atom for Vector { type ReadHandle = VectorReadHandle; type WriteHandle = VectorWriteHandle; - unsafe fn read(body: &AtomSpace) -> Option<::Handle> { + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; - Some(VectorReader { reader, header }) + Ok(VectorReader { reader, header }) } - fn init(writer: AtomSpaceWriter) -> Option<::Handle> { - Some(VectorTypeWriter { writer }) + fn init( + writer: AtomSpaceWriter, + ) -> Result<::Handle, AtomError> { + Ok(VectorTypeWriter { writer }) } } @@ -123,7 +145,7 @@ pub struct VectorWriter<'a, A: ScalarAtom> { impl<'a, A: ScalarAtom> VectorWriter<'a, A> { /// Push a single value to the vector. #[inline] - pub fn push(&mut self, child: A::InternalType) -> Option<&mut A::InternalType> { + pub fn push(&mut self, child: A::InternalType) -> Result<&mut A::InternalType, AtomError> { self.writer.write_value(child) } @@ -131,13 +153,19 @@ impl<'a, A: ScalarAtom> VectorWriter<'a, A> { /// /// Using this method, you don't need to have the elements in memory before you can write them. #[inline] - pub fn allocate_uninit(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { + pub fn allocate_uninit( + &mut self, + count: usize, + ) -> Result<&mut [MaybeUninit], AtomError> { self.writer.allocate_values(count) } /// Append multiple elements to the vector. #[inline] - pub fn append(&mut self, data: &[A::InternalType]) -> Option<&mut [A::InternalType]> { + pub fn append( + &mut self, + data: &[A::InternalType], + ) -> Result<&mut [A::InternalType], AtomError> { self.writer.write_values(data) } } diff --git a/atom/src/header.rs b/atom/src/header.rs index 1df4b3d3..8e3327e0 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -1,4 +1,5 @@ -use crate::UnidentifiedAtom; +use crate::space::AtomError; +use crate::{Atom, UnidentifiedAtom}; use urid::URID; #[repr(C, align(8))] @@ -59,4 +60,17 @@ impl AtomHeader { pub fn urid(self) -> URID { URID::new(self.inner.type_).unwrap() } + + #[inline] + pub fn check_urid(self, other: URID) -> Result<(), AtomError> { + if other == self.urid() { + Ok(()) + } else { + Err(AtomError::InvalidAtomUrid { + expected_uri: A::uri(), + expected_urid: other.into_general(), + found_urid: self.urid(), + }) + } + } } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 647d268e..f3d050d0 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -120,7 +120,8 @@ pub trait Atom: UriBound { /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: &AtomSpace) -> Option<::Handle>; + unsafe fn read(body: &AtomSpace) + -> Result<::Handle, AtomError>; /// Initialize the body of the atom. /// @@ -130,7 +131,9 @@ pub trait Atom: UriBound { /// The frame of the atom was already initialized, containing the URID. /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. - fn init(writer: AtomSpaceWriter) -> Option<::Handle>; + fn init( + writer: AtomSpaceWriter, + ) -> Result<::Handle, AtomError>; } /// An atom of yet unknown type. @@ -149,8 +152,13 @@ impl UnidentifiedAtom { /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] - pub unsafe fn from_space(space: &AtomSpace) -> Option<&Self> { - Some(Self::from_header(space.assume_init_value()?)) + pub unsafe fn from_space(space: &AtomSpace) -> Result<&Self, AtomError> { + Ok(Self::from_header(space.assume_init_value().ok_or_else( + || AtomError::ReadingOutOfBounds { + capacity: space.len(), + requested: ::core::mem::size_of::(), + }, + )?)) } /// Construct a new unidentified atom. @@ -159,8 +167,16 @@ impl UnidentifiedAtom { /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] - pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Option<&mut Self> { - Some(Self::from_header_mut(space.assume_init_value_mut()?)) + pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomError> { + let len = space.len(); + Ok(Self::from_header_mut( + space + .assume_init_value_mut() + .ok_or_else(|| AtomError::ReadingOutOfBounds { + capacity: len, + requested: ::core::mem::size_of::(), + })?, + )) } #[inline] @@ -178,10 +194,11 @@ impl UnidentifiedAtom { /// Try to read the atom. /// /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read(&self, urid: URID) -> Option<::Handle> { - if self.header.urid() != urid { - return None; - } + pub fn read( + &self, + urid: URID, + ) -> Result<::Handle, AtomError> { + self.header.check_urid(urid)?; // SAFETY: the fact that this contains a valid instance of A is checked above. unsafe { A::read(self.body()) } diff --git a/atom/src/port.rs b/atom/src/port.rs index fe9c4697..10f3a21a 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -53,7 +53,7 @@ impl<'a> PortReader<'a> { pub fn read( &self, urid: URID, - ) -> Option<::Handle> { + ) -> Result<::Handle, AtomError> { self.atom.read(urid) } } @@ -85,7 +85,7 @@ impl<'a> PortWriter<'a> { pub fn init<'b, 'write, A: crate::Atom>( &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, - ) -> Option<>::Handle> { + ) -> Result<>::Handle, AtomError> { if !self.has_been_written { self.has_been_written = true; // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. @@ -94,7 +94,7 @@ impl<'a> PortWriter<'a> { }; space.init_atom(urid) } else { - None + Err(AtomError::AtomAlreadyWritten) } } } diff --git a/atom/src/space.rs b/atom/src/space.rs index e0b41629..607be995 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -3,6 +3,7 @@ mod allocatable; mod atom_writer; mod cursor; +mod error; pub mod reader; mod space; mod vec; @@ -10,5 +11,6 @@ mod vec; pub use allocatable::*; pub use atom_writer::{AtomSpaceWriter, AtomSpaceWriterHandle}; pub use cursor::SpaceCursor; +pub use error::AtomError; pub use space::{AlignedSpace, AtomSpace}; pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index c85f07c9..6cd68aa0 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -2,6 +2,7 @@ use crate::space::{AlignedSpace, AtomSpaceWriter}; use crate::{Atom, AtomHandle, UnidentifiedAtom}; use urid::URID; +use crate::space::error::AtomError; use core::mem::{size_of, size_of_val, MaybeUninit}; /// A smart pointer that writes atom data to an internal slice. @@ -10,7 +11,7 @@ use core::mem::{size_of, size_of_val, MaybeUninit}; /// // TODO: Find proper name pub trait SpaceAllocatorImpl { - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])>; + fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError>; #[must_use] unsafe fn rewind(&mut self, byte_count: usize) -> bool; @@ -28,67 +29,77 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. #[inline] - fn allocate(&mut self, size: usize) -> Option<&mut [u8]> { + fn allocate(&mut self, size: usize) -> Result<&mut [u8], AtomError> { self.allocate_and_split(size).map(|(_, s)| s) } #[inline] - fn allocate_padding_for(&mut self) -> Option<()> { - let required_padding = crate::util::padding_for::(self.remaining_bytes())?; + fn allocate_padding_for(&mut self) -> Result<(), AtomError> { + let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; self.allocate(required_padding)?; - Some(()) + Ok(()) } #[inline] - fn allocate_aligned(&mut self, size: usize) -> Option<&mut AlignedSpace> { - let required_padding = crate::util::padding_for::(self.remaining_bytes())?; + fn allocate_aligned( + &mut self, + size: usize, + ) -> Result<&mut AlignedSpace, AtomError> { + let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; let raw = self.allocate(size + required_padding)?; - AlignedSpace::align_from_bytes_mut(raw) + AlignedSpace::align_from_bytes_mut(raw).ok_or_else(|| AtomError::Unknown) } #[inline] - fn allocate_value(&mut self) -> Option<&mut MaybeUninit> { + fn allocate_value(&mut self) -> Result<&mut MaybeUninit, AtomError> { let space = self.allocate_aligned(size_of::>())?; // SAFETY: We used size_of, so we are sure that the allocated space is exactly big enough for T. - Some(unsafe { space.as_uninit_mut_unchecked() }) + Ok(unsafe { space.as_uninit_mut_unchecked() }) } #[inline] - fn allocate_values(&mut self, count: usize) -> Option<&mut [MaybeUninit]> { + fn allocate_values( + &mut self, + count: usize, + ) -> Result<&mut [MaybeUninit], AtomError> { let space = self.allocate_aligned(count * std::mem::size_of::())?; - Some(space.as_uninit_slice_mut()) + Ok(space.as_uninit_slice_mut()) } #[inline] fn init_atom( &mut self, atom_type: URID, - ) -> Option<::Handle> { + ) -> Result<::Handle, AtomError> { let space = AtomSpaceWriter::write_new(self, atom_type)?; A::init(space) } #[inline] - fn forward_atom(&mut self, atom: &UnidentifiedAtom) -> Option<&mut UnidentifiedAtom> { + fn forward_atom( + &mut self, + atom: &UnidentifiedAtom, + ) -> Result<&mut UnidentifiedAtom, AtomError> { let resulting_space = self.allocate_aligned(atom.atom_space().len())?; resulting_space .as_bytes_mut() .copy_from_slice(atom.atom_space().as_bytes()); + // SAFETY: We just wrote those bytes, we know for sure they're the same and aligned unsafe { UnidentifiedAtom::from_space_mut(resulting_space) } } #[inline] - fn write_bytes(&mut self, bytes: &[u8]) -> Option<&mut [u8]> { + fn write_bytes(&mut self, bytes: &[u8]) -> Result<&mut [u8], AtomError> { let space = self.allocate(bytes.len())?; space.copy_from_slice(bytes); - Some(space) + Ok(space) } #[inline] - fn write_value(&mut self, value: T) -> Option<&mut T> + fn write_value(&mut self, value: T) -> Result<&mut T, AtomError> where T: Copy + Sized + 'static, { @@ -96,22 +107,10 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. let space = unsafe { space.as_uninit_mut_unchecked() }; - Some(crate::util::write_uninit(space, value)) - } - - #[inline] - fn write_value_padded(&mut self, value: T) -> Option<&mut T> { - let space = self - .allocate_aligned::(size_of::>())? - .realign_mut()?; - - // SAFETY: We used size_of, so we are sure that the allocated space is exactly big enough for T. - let space = unsafe { space.as_uninit_mut_unchecked() }; - - Some(crate::util::write_uninit(space, value)) + Ok(crate::util::write_uninit(space, value)) } - fn write_values(&mut self, values: &[T]) -> Option<&mut [T]> + fn write_values(&mut self, values: &[T]) -> Result<&mut [T], AtomError> where T: Copy + Sized + 'static, { @@ -123,7 +122,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { } // SAFETY: Assume init: we just initialized the memory above - Some(unsafe { &mut *(space as *mut [_] as *mut [T]) }) + Ok(unsafe { &mut *(space as *mut [_] as *mut [T]) }) } } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index f6c39a04..88914640 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,5 +1,5 @@ use crate::header::AtomHeader; -use crate::space::{AlignedSpace, SpaceAllocator, SpaceAllocatorImpl}; +use crate::space::{AlignedSpace, AtomError, SpaceAllocator, SpaceAllocatorImpl}; use crate::AtomHandle; use urid::URID; @@ -50,17 +50,24 @@ impl<'a> AtomSpaceWriter<'a> { unsafe { space.assume_init_value_mut().unwrap() } } + pub fn allocate_and_unwrap Result>( + mut self, + operation: F, + ) -> Result { + operation(&mut self) + } + /// Create a new framed space with the given parent and type URID. pub fn write_new( parent: &'a mut impl SpaceAllocator, urid: URID, - ) -> Option { + ) -> Result { let atom = AtomHeader::new(urid); parent.write_value(atom)?; let atom_header_index = parent.allocated_bytes().len() - std::mem::size_of::(); - Some(Self { + Ok(Self { atom_header_index, parent, }) @@ -69,18 +76,22 @@ impl<'a> AtomSpaceWriter<'a> { impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { #[inline] - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { + fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { let (previous, current) = self.parent.allocate_and_split(size)?; let space = AlignedSpace::::try_from_bytes_mut( - previous.get_mut(self.atom_header_index..)?, - )?; - let header = unsafe { space.assume_init_value_mut() }?; + previous + .get_mut(self.atom_header_index..) + .ok_or(AtomError::CannotUpdateAtomHeader)?, + ) + .ok_or(AtomError::CannotUpdateAtomHeader)?; + let header = + unsafe { space.assume_init_value_mut() }.ok_or(AtomError::CannotUpdateAtomHeader)?; // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated unsafe { header.set_size_of_body(header.size_of_body() + size) }; - Some((previous, current)) + Ok((previous, current)) } #[inline] diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 42eeabdd..75c36db5 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,4 +1,4 @@ -use crate::space::SpaceAllocatorImpl; +use crate::space::{AtomError, SpaceAllocatorImpl}; pub struct SpaceCursor<'a> { data: &'a mut [u8], @@ -16,12 +16,25 @@ impl<'a> SpaceCursor<'a> { impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { #[inline] - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { - let (allocated, allocatable) = self.data.split_at_mut(self.allocated_length); - let new_allocation = allocatable.get_mut(..size)?; - self.allocated_length = self.allocated_length.checked_add(size)?; + fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + let allocated_length = self.allocated_length; + let data_len = self.data.len(); + let (allocated, allocatable) = self.data.split_at_mut(allocated_length); - Some((allocated, new_allocation)) + let new_allocation = allocatable + .get_mut(..size) + .ok_or_else(|| AtomError::OutOfSpace { + used: allocated_length, + capacity: data_len, + requested: size, + })?; + + self.allocated_length = self + .allocated_length + .checked_add(size) + .ok_or(AtomError::AllocatorOverflow)?; + + Ok((allocated, new_allocation)) } #[inline] diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs new file mode 100644 index 00000000..aef4d196 --- /dev/null +++ b/atom/src/space/error.rs @@ -0,0 +1,43 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; +use urid::{Uri, URID}; + +#[derive(Debug, Clone)] +pub enum AtomError { + OutOfSpace { + used: usize, + capacity: usize, + requested: usize, + }, + CannotComputeAlignment { + ptr: *const u8, + }, + AllocatorOverflow, + ResizeFailed, + CannotUpdateAtomHeader, + AtomAlreadyWritten, + + // Reading + InvalidAtomUrid { + expected_uri: &'static Uri, + expected_urid: URID, + found_urid: URID, + }, + ReadingOutOfBounds { + capacity: usize, + requested: usize, + }, + InvalidAtomValue { + reading_type_uri: &'static Uri, + }, + + Unknown, +} + +impl Display for AtomError { + fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl Error for AtomError {} diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index 84c59d7a..bc7bad29 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -1,4 +1,5 @@ use crate::prelude::AlignedSpace; +use crate::space::AtomError; use crate::{AtomHeader, UnidentifiedAtom}; use std::mem::MaybeUninit; @@ -13,73 +14,86 @@ impl<'a> SpaceReader<'a> { } #[inline] - fn next_uninit_value(&mut self) -> Option<&'a MaybeUninit> { - let space = AlignedSpace::align_from_bytes(self.space)?; + fn next_uninit_value(&mut self) -> Result<&'a MaybeUninit, AtomError> { + let space = AlignedSpace::try_align_from_bytes(self.space)?; let value_size = ::core::mem::size_of::(); - let (value, remaining) = space.split_at(value_size)?; + let (value, remaining) = space.try_split_at(value_size)?; self.space = remaining.as_bytes(); - value.as_uninit() + // This shouldn't be possible, but it doesn't hurt to check + value.as_uninit().ok_or(AtomError::Unknown) } #[inline] fn next_uninit_value_slice( &mut self, length: usize, - ) -> Option<&'a [MaybeUninit]> { - let space = AlignedSpace::align_from_bytes(self.space)?; + ) -> Result<&'a [MaybeUninit], AtomError> { + let space = AlignedSpace::try_align_from_bytes(self.space)?; let split_point = crate::util::value_index_to_byte_index::(length); - let (data, remaining) = space.split_at(split_point)?; + let (data, remaining) = space.try_split_at(split_point)?; self.space = remaining.as_bytes(); - Some(data.as_uninit_slice()) + Ok(data.as_uninit_slice()) } #[inline] - fn as_uninit_slice(&self) -> Option<&'a [MaybeUninit]> { - let space = AlignedSpace::align_from_bytes(self.space)?; - Some(space.as_uninit_slice()) + fn as_uninit_slice(&self) -> Result<&'a [MaybeUninit], AtomError> { + let space = AlignedSpace::try_align_from_bytes(self.space)?; + Ok(space.as_uninit_slice()) } #[inline] - pub unsafe fn as_slice(&self) -> Option<&'a [T]> { + pub unsafe fn as_slice(&self) -> Result<&'a [T], AtomError> { self.as_uninit_slice() .map(|s| crate::util::assume_init_slice(s)) } #[inline] - pub unsafe fn next_slice(&mut self, length: usize) -> Option<&'a [U]> { + pub unsafe fn next_slice(&mut self, length: usize) -> Result<&'a [U], AtomError> { self.next_uninit_value_slice(length) .map(|s| crate::util::assume_init_slice(s)) } #[inline] - pub fn next_bytes(&mut self, length: usize) -> Option<&'a [u8]> { - let bytes = self.space.get(..length)?; + pub fn next_bytes(&mut self, length: usize) -> Result<&'a [u8], AtomError> { + let bytes = self + .space + .get(..length) + .ok_or_else(|| AtomError::ReadingOutOfBounds { + requested: length, + capacity: self.space.len(), + })?; + self.space = self.space.get(length..).unwrap_or(&[]); - Some(bytes) + Ok(bytes) } #[inline] - pub unsafe fn next_value(&mut self) -> Option<&'a U> { + pub unsafe fn next_value(&mut self) -> Result<&'a U, AtomError> { self.next_uninit_value() .map(|v| crate::util::assume_init_ref(v)) } #[inline] - pub unsafe fn next_atom(&mut self) -> Option<&'a UnidentifiedAtom> { - let space = AlignedSpace::::align_from_bytes(&self.space)?; - let header = space.assume_init_value()?; - let (_, rest) = space.split_at(header.size_of_atom())?; + pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomError> { + let space = AlignedSpace::::try_align_from_bytes(&self.space)?; + let header = space + .assume_init_value() + .ok_or_else(|| AtomError::ReadingOutOfBounds { + capacity: space.len(), + requested: core::mem::size_of::(), + })?; + let (_, rest) = space.try_split_at(header.size_of_atom())?; let atom = UnidentifiedAtom::from_header(header); self.space = rest.as_bytes(); - Some(atom) + Ok(atom) } #[inline] @@ -88,14 +102,14 @@ impl<'a> SpaceReader<'a> { } #[inline] - pub fn try_read(&mut self, read_handler: F) -> Option + pub fn try_read(&mut self, read_handler: F) -> Result where - F: FnOnce(&mut Self) -> Option, + F: FnOnce(&mut Self) -> Result, { let mut reader = Self { space: self.space }; let value = read_handler(&mut reader)?; self.space = reader.remaining_bytes(); - Some(value) + Ok(value) } } diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index 2d021645..f03556d2 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -1,6 +1,6 @@ use crate::header::AtomHeader; use crate::space::reader::SpaceReader; -use crate::space::SpaceCursor; +use crate::space::{AtomError, SpaceCursor}; use core::mem::{align_of, size_of}; use std::marker::PhantomData; use std::mem::{size_of_val, MaybeUninit}; @@ -34,7 +34,7 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// // --- /// /// // Bytes are already aligned, the whole slice will be available -/// let space: &AlignedSpace = AlignedSpace::align_from_bytes(bytes).unwrap(); +/// let space: &AlignedSpace = AlignedSpace::try_align_from_bytes(bytes).unwrap(); /// // SAFETY: we know the slice was initialized with proper u64 values. /// let read_values = unsafe { space.assume_init_slice() }; /// assert_eq!(read_values, [42u64, 69]); @@ -115,27 +115,37 @@ impl AlignedSpace { /// # Example /// /// ``` - /// # use lv2_atom::space::AlignedSpace; + /// # use lv2_atom::space::{AlignedSpace, AtomError}; /// let values = &[42u64, 69]; /// // Transmuting to a slice of bytes /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// /// // The slice has space for both values - /// assert_eq!(AlignedSpace::::align_from_bytes(bytes).unwrap().values_len(), 2); + /// assert_eq!(AlignedSpace::::try_align_from_bytes(bytes).unwrap().values_len(), 2); /// // The slice now only has space for a single value - /// assert_eq!(AlignedSpace::::align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); + /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert!(AlignedSpace::::align_from_bytes(&bytes[10..]).is_none()); + /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[10..]), Err(AtomError::ReadingOutOfBounds { capacity: 7, requested: 9 })); /// ``` #[inline] - pub fn align_from_bytes(data: &[u8]) -> Option<&Self> { - let data = data.get(crate::util::padding_for::(data)?..)?; + pub fn try_align_from_bytes(data: &[u8]) -> Result<&Self, AtomError> { + let padding = crate::util::try_padding_for::(data)?; + let data = data + .get(padding..) + .ok_or_else(|| AtomError::ReadingOutOfBounds { + capacity: data.len(), + requested: padding + 1, + })?; + if data.is_empty() { - return None; + return Err(AtomError::ReadingOutOfBounds { + capacity: data.len(), + requested: padding + 1, + }); } // SAFETY: We just aligned the slice start - Some(unsafe { AlignedSpace::from_bytes_unchecked(data) }) + Ok(unsafe { AlignedSpace::from_bytes_unchecked(data) }) } /// Creates a new mutable space from a mutable slice of bytes, slicing some bytes off its start it if necessary. @@ -275,47 +285,30 @@ impl AlignedSpace { /// A checked version of slice::split_at, which returns the first part as an already-aligned slice. #[inline] - fn split_bytes_at(&self, mid: usize) -> Option<(&Self, &[u8])> { + fn split_bytes_at(&self, mid: usize) -> Result<(&Self, &[u8]), AtomError> { if mid > self.data.len() { - return None; + return Err(AtomError::ReadingOutOfBounds { + requested: mid, + capacity: self.data.len(), + }); } let (start, end) = self.data.split_at(mid); // SAFETY: Because this data was the start of an existing Space, it was aligned already. let start = unsafe { Self::from_bytes_unchecked(start) }; - Some((start, end)) + Ok((start, end)) } /// Try to retrieve space. /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. #[inline] - pub fn split_at(&self, mid: usize) -> Option<(&Self, &Self)> { + pub fn try_split_at(&self, mid: usize) -> Result<(&Self, &Self), AtomError> { let (start, end) = self.split_bytes_at(mid)?; - let end = Self::align_from_bytes(end).unwrap_or_else(AlignedSpace::empty); + let end = Self::try_align_from_bytes(end).unwrap_or_else(|_| AlignedSpace::empty()); - Some((start, end)) - } - - #[inline] - pub fn realign(&self) -> Option<&AlignedSpace> { - AlignedSpace::::align_from_bytes(self.as_bytes()) - } - - #[inline] - pub fn realign_mut(&mut self) -> Option<&mut AlignedSpace> { - AlignedSpace::::align_from_bytes_mut(self.as_bytes_mut()) - } - - #[inline] - pub fn aligned(&self) -> Option<&AlignedSpace> { - AlignedSpace::::try_from_bytes(self.as_bytes()) - } - - #[inline] - pub fn aligned_mut(&mut self) -> Option<&mut AlignedSpace> { - AlignedSpace::::try_from_bytes_mut(self.as_bytes_mut()) + Ok((start, end)) } /// Return the internal slice of the space. diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 0f5ad96c..2869d3d7 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] -use crate::space::{AlignedSpace, SpaceAllocatorImpl}; +use crate::space::{AlignedSpace, AtomError, SpaceAllocatorImpl}; use std::mem::MaybeUninit; use std::ops::Range; @@ -42,7 +42,10 @@ impl VecSpace { } #[inline] - fn reallocate_bytes_mut(&mut self, byte_range: Range) -> Option<(&mut [u8], &mut [u8])> { + fn reallocate_bytes_mut( + &mut self, + byte_range: Range, + ) -> Result<(&mut [u8], &mut [u8]), AtomError> { let byte_len = self.inner.len() * std::mem::size_of::(); let max = byte_range.start.max(byte_range.end); @@ -52,12 +55,16 @@ impl VecSpace { } let bytes = self.as_bytes_mut(); - bytes.get(byte_range.clone())?; // To make sure everything is in range instead of panicking on split_at_mut + bytes + .get(byte_range.clone()) + .ok_or(AtomError::ResizeFailed)?; // To make sure everything is in range instead of panicking on split_at_mut let (previous, allocatable) = bytes.split_at_mut(byte_range.start); - return Some(( + return Ok(( previous, - allocatable.get_mut(..byte_range.end - byte_range.start)?, + allocatable + .get_mut(..byte_range.end - byte_range.start) + .ok_or(AtomError::ResizeFailed)?, )); } @@ -76,11 +83,15 @@ pub struct VecSpaceCursor<'vec, T> { } impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { - fn allocate_and_split(&mut self, size: usize) -> Option<(&mut [u8], &mut [u8])> { - let end = self.allocated_length.checked_add(size)?; + fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + let end = self + .allocated_length + .checked_add(size) + .ok_or(AtomError::AllocatorOverflow)?; + let result = VecSpace::::reallocate_bytes_mut(self.vec, self.allocated_length..end); - if result.is_some() { + if result.is_ok() { self.allocated_length = end; } diff --git a/atom/src/util.rs b/atom/src/util.rs index 5f6b0da1..903ee5a8 100644 --- a/atom/src/util.rs +++ b/atom/src/util.rs @@ -1,3 +1,4 @@ +use crate::space::AtomError; use std::mem::MaybeUninit; // This function is separate to ensure proper lifetimes @@ -50,3 +51,8 @@ pub(crate) fn padding_for(data: &[u8]) -> Option { Some(value) } } + +#[inline] +pub(crate) fn try_padding_for(data: &[u8]) -> Result { + padding_for::(data).ok_or_else(|| AtomError::CannotComputeAlignment { ptr: data.as_ptr() }) +} diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index d9ae740e..e899409c 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -58,14 +58,14 @@ impl Plugin for AtomPlugin { for (time_stamp, atom) in sequence_reader { match atom.read(self.urids.atom.int) { - Some(number) => { + Ok(number) => { sequence_writer .init(time_stamp, self.urids.atom.int) .unwrap() .set(number * 2) .unwrap(); } - None => { + Err(_) => { sequence_writer.forward(time_stamp, atom).unwrap(); } } diff --git a/docs/fifths/src/lib.rs b/docs/fifths/src/lib.rs index 9652829a..78aef9fa 100644 --- a/docs/fifths/src/lib.rs +++ b/docs/fifths/src/lib.rs @@ -54,11 +54,11 @@ impl Plugin for Fifths { .with_unit(TimeStampURID::Frames(self.urids.unit.frame)); for (timestamp, atom) in input_sequence { - // Every message is forwarded, regardless of it's content. - output_sequence.forward(timestamp, atom); + // Every message is forwarded, regardless of its contents. + output_sequence.forward(timestamp, atom).unwrap(); // Retrieve the message. - let message = if let Some(message) = atom.read(self.urids.midi.wmidi) { + let message = if let Ok(message) = atom.read(self.urids.midi.wmidi) { message } else { continue; @@ -82,7 +82,8 @@ impl Plugin for Fifths { output_sequence .init(timestamp, self.urids.midi.wmidi) .unwrap() - .set(MidiMessage::NoteOff(channel, note, velocity)); + .set(MidiMessage::NoteOff(channel, note, velocity)) + .unwrap(); } } _ => (), diff --git a/docs/metro/src/lib.rs b/docs/metro/src/lib.rs index 76af5bf8..a847b7d2 100644 --- a/docs/metro/src/lib.rs +++ b/docs/metro/src/lib.rs @@ -75,7 +75,7 @@ impl Plugin for Metro { } fn run(&mut self, ports: &mut Ports, _: &mut (), _: u32) { - if let Some(control) = ports + if let Ok(control) = ports .control .read(self.urids.atom.sequence) .map(|s| s.read(self.urids.unit.beat)) diff --git a/docs/metro/src/pipes.rs b/docs/metro/src/pipes.rs index 478f12b3..b72553a5 100644 --- a/docs/metro/src/pipes.rs +++ b/docs/metro/src/pipes.rs @@ -323,22 +323,24 @@ impl<'a> Pipe for EventReader<'a> { }; if let Some(atom) = atom { - if let Some((object_header, object_reader)) = atom + if let Ok((object_header, object_reader)) = atom .read(self.atom_urids.object) - .or_else(|| atom.read(self.atom_urids.blank)) + .or_else(|_| atom.read(self.atom_urids.blank)) { if object_header.otype == self.time_urids.position_class { for (property_header, property) in object_reader { if property_header.key == self.time_urids.bar_beat { updates.beat_update = property .read(self.atom_urids.float) - .map(|float| *float as f64); + .map(|float| *float as f64) + .ok(); } if property_header.key == self.time_urids.beats_per_minute { - updates.bpm_update = property.read(self.atom_urids.float).copied(); + updates.bpm_update = property.read(self.atom_urids.float).ok().copied(); } if property_header.key == self.time_urids.speed { - updates.speed_update = property.read(self.atom_urids.float).copied(); + updates.speed_update = + property.read(self.atom_urids.float).ok().copied(); } } } diff --git a/docs/midigate/src/lib.rs b/docs/midigate/src/lib.rs index 7736c184..0499175e 100644 --- a/docs/midigate/src/lib.rs +++ b/docs/midigate/src/lib.rs @@ -99,7 +99,7 @@ impl Plugin for Midigate { continue; }; - let message = if let Some(message) = message.read(self.urids.midi.wmidi) { + let message = if let Ok(message) = message.read(self.urids.midi.wmidi) { message } else { continue; diff --git a/midi/src/raw.rs b/midi/src/raw.rs index b7db9c42..8d44628e 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -4,6 +4,7 @@ //! //! If you just want to use MIDI messages in your plugin, you should use the optional `wmidi` feature. use atom::prelude::*; +use atom::space::AtomError; use atom::AtomHandle; use urid::*; @@ -32,11 +33,11 @@ impl Atom for MidiEvent { type ReadHandle = MidiEventReadHandle; type WriteHandle = MidiEventWriteHandle; - unsafe fn read(body: &AtomSpace) -> Option<&[u8]> { - Some(body.as_bytes()) + unsafe fn read(body: &AtomSpace) -> Result<&[u8], AtomError> { + Ok(body.as_bytes()) } - fn init(frame: AtomSpaceWriter) -> Option { - Some(frame) + fn init(frame: AtomSpaceWriter) -> Result { + Ok(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 708bc618..7b9f2c0e 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -4,6 +4,7 @@ //! //! If you want to have raw, low-level access to the messages, you should use the [raw module](../raw/index.html). use atom::prelude::*; +use atom::space::AtomError; use atom::AtomHandle; use std::convert::TryFrom; use urid::*; @@ -39,10 +40,14 @@ pub struct WMidiEventWriter<'a> { impl<'a> WMidiEventWriter<'a> { #[inline] - pub fn set(mut self, message: wmidi::MidiMessage) -> Option<()> { + pub fn set(mut self, message: wmidi::MidiMessage) -> Result<(), AtomError> { let space = self.writer.allocate(message.bytes_size())?; - message.copy_to_slice(space).ok()?; - Some(()) + + // The error shouldn't be happening, as we allocated just as many bytes as needed + message + .copy_to_slice(space) + .map_err(|_| AtomError::Unknown)?; + Ok(()) } } @@ -51,13 +56,15 @@ impl Atom for WMidiEvent { type WriteHandle = WMidiEventWriteHandle; #[inline] - unsafe fn read(space: &AtomSpace) -> Option { - wmidi::MidiMessage::try_from(space.as_bytes()).ok() + unsafe fn read(space: &AtomSpace) -> Result { + wmidi::MidiMessage::try_from(space.as_bytes()).map_err(|_| AtomError::InvalidAtomValue { + reading_type_uri: Self::uri(), + }) } #[inline] - fn init(writer: AtomSpaceWriter) -> Option { - Some(WMidiEventWriter { writer }) + fn init(writer: AtomSpaceWriter) -> Result { + Ok(WMidiEventWriter { writer }) } } @@ -83,14 +90,14 @@ impl Atom for SystemExclusiveWMidiEvent { type WriteHandle = SystemExclusiveWMidiEventWriteHandle; #[inline] - unsafe fn read(space: &AtomSpace) -> Option { + unsafe fn read(space: &AtomSpace) -> Result { WMidiEvent::read(space) } - fn init(frame: AtomSpaceWriter) -> Option { + fn init(frame: AtomSpaceWriter) -> Result { let mut writer = Writer { frame }; writer.write::(0xf0); - Some(writer) + Ok(writer) } } @@ -105,12 +112,12 @@ pub struct Writer<'a> { impl<'a> Writer<'a> { #[inline] - pub fn write_raw(&mut self, data: &[u8]) -> Option<&mut [u8]> { + pub fn write_raw(&mut self, data: &[u8]) -> Result<&mut [u8], AtomError> { self.frame.write_bytes(data) } #[inline] - pub fn write(&mut self, instance: T) -> Option<&mut T> + pub fn write(&mut self, instance: T) -> Result<&mut T, AtomError> where T: Copy + Sized + 'static, { diff --git a/state/src/raw.rs b/state/src/raw.rs index 66c3d6f0..423b6f00 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -55,7 +55,7 @@ impl<'a> StoreHandle<'a> { ) -> Result<(), StateErr> { let store_fn = store_fn.ok_or(StateErr::BadCallback)?; let space = space.as_space(); - let atom = unsafe { space.read().next_atom() }.ok_or(StateErr::BadData)?; + let atom = unsafe { space.read().next_atom() }.map_err(|_| StateErr::BadData)?; let key = key.get(); let data_ptr = atom.body().as_bytes().as_ptr() as *const c_void; @@ -123,7 +123,7 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result<>::Handle, StateErr> { if !self.initialized { self.initialized = true; - self.cursor.init_atom(urid).ok_or(StateErr::Unknown) + self.cursor.init_atom(urid).map_err(|_| StateErr::Unknown) } else { Err(StateErr::Unknown) } @@ -217,7 +217,7 @@ impl<'a> StatePropertyReader<'a> { urid: URID, ) -> Result<>::Handle, StateErr> { if urid == self.type_ { - unsafe { A::read(self.body) }.ok_or(StateErr::Unknown) + unsafe { A::read(self.body) }.map_err(|_| StateErr::Unknown) } else { Err(StateErr::BadType) } @@ -237,12 +237,14 @@ mod tests { .draft(URID::new(1).unwrap()) .init(urids.int) .unwrap() - .set(17); + .set(17) + .unwrap(); store_handle .draft(URID::new(2).unwrap()) .init(urids.float) .unwrap() - .set(1.0); + .set(1.0) + .unwrap(); store_handle.commit(URID::new(1).unwrap()).unwrap().unwrap(); diff --git a/state/tests/integration.rs b/state/tests/integration.rs index 4a1b8094..9408e5e4 100644 --- a/state/tests/integration.rs +++ b/state/tests/integration.rs @@ -54,12 +54,12 @@ impl State for Stateful { .draft(URID::new(1000).unwrap()) .init(self.urids.float)? .set(self.internal) - .ok_or(StateErr::Unknown)?; + .map_err(|_| StateErr::Unknown)?; store .draft(URID::new(1001).unwrap()) .init(self.urids.vector)? .of_type(self.urids.float) - .ok_or(StateErr::Unknown)? + .map_err(|_| StateErr::Unknown)? .append(self.audio.as_ref()); store.commit_all() @@ -74,7 +74,7 @@ impl State for Stateful { .retrieve(URID::new(1001).unwrap())? .read(self.urids.vector)? .of_type(self.urids.float) - .ok_or(StateErr::BadData)?, + .map_err(|_| StateErr::BadData)?, ); Ok(()) } From 4b7dd6c2169781f40c79c71c8982efb121192048 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Sep 2021 17:43:04 +0200 Subject: [PATCH 33/54] Doc & test fixes --- atom/src/atoms/object.rs | 11 +++++-- atom/src/atoms/scalar.rs | 2 +- atom/src/atoms/sequence.rs | 28 ++++++++++------- atom/src/atoms/string.rs | 41 ++++++++---------------- atom/src/atoms/tuple.rs | 4 +-- atom/src/atoms/vector.rs | 8 +++-- atom/src/lib.rs | 9 +++--- atom/src/space.rs | 2 ++ atom/src/space/allocatable.rs | 13 +++++--- atom/src/space/atom_writer.rs | 12 +++---- atom/src/space/cursor.rs | 9 ++++-- atom/src/space/error.rs | 6 +++- atom/src/space/space.rs | 36 ++++++++++++++------- atom/src/space/terminated.rs | 57 ++++++++++++++++++++++++++++++++++ atom/src/space/vec.rs | 9 ++++-- atom/tests/atom_integration.rs | 6 ++-- docs/fifths/src/lib.rs | 3 +- midi/src/lib.rs | 5 +-- midi/src/wmidi_binding.rs | 23 ++++++-------- state/src/raw.rs | 3 +- state/tests/integration.rs | 3 +- 21 files changed, 187 insertions(+), 103 deletions(-) create mode 100644 atom/src/space/terminated.rs diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 9cff4cf9..ea89c97f 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -35,7 +35,7 @@ //! /// Iterate through all properties of the object. //! for (property_header, atom) in object_reader { //! // If the property is an integer... -//! if let Some(integer) = atom.read(urids.atom.int) { +//! if let Ok(integer) = atom.read(urids.atom.int) { //! // Print it! //! println!( //! "Property No. {} has integer value {}", @@ -350,13 +350,18 @@ mod tests { }) .unwrap(); { - writer.init(first_key, urids.int).unwrap().set(first_value); + writer + .init(first_key, urids.int) + .unwrap() + .set(first_value) + .unwrap(); } { writer .init(second_key, urids.float) .unwrap() - .set(second_value); + .set(second_value) + .unwrap(); } } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 5337e53c..f267bede 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -203,7 +203,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - space.init_atom(urid).unwrap().set(value); + space.init_atom(urid).unwrap().set(value).unwrap(); } // verifying diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 17963ded..1438de3b 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -35,9 +35,10 @@ //! //! // Get the write handle to the sequence. //! // You have to provide the unit of the time stamps. -//! let mut output_sequence: SequenceWriter = ports.output.init( -//! urids.atom.sequence, -//! ).unwrap().with_unit(TimeStampURID::BeatsPerMinute(urids.units.beat)); +//! let mut output_sequence: SequenceWriter = ports.output.init(urids.atom.sequence) +//! .unwrap() +//! .with_unit(TimeStampURID::BeatsPerMinute(urids.units.beat)) +//! .unwrap(); //! //! // Iterate through all events in the input sequence. //! // @@ -49,9 +50,9 @@ //! // An event contains a timestamp and an atom. //! let (timestamp, atom): (TimeStamp, &UnidentifiedAtom) = event; //! // If the read atom is a 32-bit integer... -//! if let Some(integer) = atom.read(urids.atom.int) { +//! if let Ok(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2); +//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); @@ -120,7 +121,7 @@ pub struct SequenceHeaderWriter<'a> { } impl<'a> SequenceHeaderWriter<'a> { - pub fn with_unit(mut self, unit: TimeStampURID) -> SequenceWriter<'a> { + pub fn with_unit(mut self, unit: TimeStampURID) -> Result, AtomError> { let header = SequenceBody(sys::LV2_Atom_Sequence_Body { unit: match unit { TimeStampURID::BeatsPerMinute(urid) => urid.get(), @@ -129,13 +130,13 @@ impl<'a> SequenceHeaderWriter<'a> { pad: 0, }); - self.writer.write_value(header); // FIXME + self.writer.write_value(header)?; - SequenceWriter { + Ok(SequenceWriter { writer: self.writer, unit: unit.into(), last_stamp: None, - } + }) } } @@ -341,17 +342,20 @@ mod tests { let mut writer = space .init_atom(urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(urids.units.frame)); + .with_unit(TimeStampURID::Frames(urids.units.frame)) + .unwrap(); writer .init::(TimeStamp::Frames(0), urids.atom.int) .unwrap() - .set(42); + .set(42) + .unwrap(); writer .init::(TimeStamp::Frames(1), urids.atom.long) .unwrap() - .set(17); + .set(17) + .unwrap(); } // verifying diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 4472a416..d7b7a58d 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -78,13 +78,12 @@ pub struct LiteralInfoWriter<'a> { } impl<'a> LiteralInfoWriter<'a> { - pub fn write_info(mut self, info: LiteralInfo) -> StringWriter<'a> { - self.writer.write_value(info.into_raw()); // FIXME + pub fn write_info(mut self, info: LiteralInfo) -> Result, AtomError> { + self.writer.write_value(info.into_raw())?; - StringWriter { - writer: self.writer, - has_nul_byte: false, - } + Ok(StringWriter { + writer: self.writer.terminated(0), + }) } } @@ -178,16 +177,14 @@ impl Atom for String { frame: AtomSpaceWriter, ) -> Result<::Handle, AtomError> { Ok(StringWriter { - writer: frame, - has_nul_byte: false, + writer: frame.terminated(0), }) } } /// Handle to append strings to a string or literal. pub struct StringWriter<'a> { - writer: AtomSpaceWriter<'a>, - has_nul_byte: bool, // If this writer already wrote a null byte before. + writer: Terminated>, } impl<'a> StringWriter<'a> { @@ -197,24 +194,10 @@ impl<'a> StringWriter<'a> { /// /// If the internal space for the atom is not big enough, this method returns `None`. pub fn append(&mut self, string: &str) -> Result<&mut str, AtomError> { - // Rewind to overwrite previously written nul_byte before appending the string. - if self.has_nul_byte { - // SAFETY: it is safe to overwrite the nul byte - if unsafe { !self.writer.rewind(1) } { - return Err(AtomError::Unknown); // Could not rewind - } - } - - // Manually write the bytes to make extra room for the nul byte - let bytes = string.as_bytes(); - let space = self.writer.allocate(bytes.len() + 1)?; - space[..bytes.len()].copy_from_slice(bytes); - // SAFETY: space is guaranteed to be at least 1 byte large - space[bytes.len()] = 0; + let bytes = self.writer.write_bytes(string.as_bytes())?; - self.has_nul_byte = true; // SAFETY: We just wrote that string, therefore it is guaranteed to be valid UTF-8 - unsafe { Ok(std::str::from_utf8_unchecked_mut(&mut space[..bytes.len()])) } + unsafe { Ok(std::str::from_utf8_unchecked_mut(bytes)) } } } @@ -244,7 +227,7 @@ mod tests { #[test] fn test_literal() { let map = HashURIDMapper::new(); - let urids = TestURIDs::from_map(&map).unwrap(); + let urids: TestURIDs = TestURIDs::from_map(&map).unwrap(); let mut raw_space = VecSpace::::new_with_capacity(64); let raw_space = raw_space.as_space_mut(); @@ -256,7 +239,9 @@ mod tests { let mut writer = space .init_atom(urids.atom.literal) .unwrap() - .write_info(LiteralInfo::Language(urids.german.into_general())); + .write_info(LiteralInfo::Language(urids.german.into_general())) + .unwrap(); + writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index ab1a0782..19f9d9fc 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -18,7 +18,7 @@ //! let input: TupleIterator = ports.input.read(urids.tuple).unwrap(); //! let mut output: TupleWriter = ports.output.init(urids.tuple).unwrap(); //! for atom in input { -//! if let Some(integer) = atom.read(urids.int) { +//! if let Ok(integer) = atom.read(urids.int) { //! output.init(urids.int).unwrap().set(*integer * 2).unwrap(); //! } else { //! output.init(urids.int).unwrap().set(-1).unwrap(); @@ -132,7 +132,7 @@ mod tests { .unwrap(); vector_writer.append(&[17; 9]).unwrap(); } - writer.init(urids.int).unwrap().set(42); + writer.init(urids.int).unwrap().set(42).unwrap(); } // verifying diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index bbd0458f..5e4d278e 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -172,6 +172,7 @@ impl<'a, A: ScalarAtom> VectorWriter<'a, A> { #[cfg(test)] mod tests { + use crate::atoms::AtomURIDCollection; use crate::space::*; use crate::AtomHeader; use std::mem::size_of; @@ -182,7 +183,7 @@ mod tests { const CHILD_COUNT: usize = 17; let map = HashURIDMapper::new(); - let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); + let urids: AtomURIDCollection = AtomURIDCollection::from_map(&map).unwrap(); let mut raw_space = VecSpace::::new_with_capacity(64); let raw_space = raw_space.as_space_mut(); @@ -195,8 +196,9 @@ mod tests { .unwrap() .of_type(urids.int) .unwrap(); - writer.append(&[42; CHILD_COUNT - 1]); - writer.push(1); + + writer.append(&[42; CHILD_COUNT - 1]).unwrap(); + writer.push(1).unwrap(); } // verifying diff --git a/atom/src/lib.rs b/atom/src/lib.rs index f3d050d0..aff5bd55 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -36,16 +36,17 @@ //! ).unwrap().read(urids.units.beat); //! //! // Get the write handle to the sequence. -//! let mut output_sequence = ports.output.init( -//! urids.atom.sequence, -//! ).unwrap().with_unit(TimeStampURID::Frames(urids.units.frame)); +//! let mut output_sequence = ports.output.init(urids.atom.sequence) +//! .unwrap() +//! .with_unit(TimeStampURID::Frames(urids.units.frame)) +//! .unwrap(); //! //! // Iterate through all events in the input sequence. //! for event in input_sequence { //! // An event contains a timestamp and an atom. //! let (timestamp, atom) = event; //! // If the read atom is a 32-bit integer... -//! if let Some(integer) = atom.read(urids.atom.int) { +//! if let Ok(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. //! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); //! } else { diff --git a/atom/src/space.rs b/atom/src/space.rs index 607be995..0d760a6c 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -6,6 +6,7 @@ mod cursor; mod error; pub mod reader; mod space; +mod terminated; mod vec; pub use allocatable::*; @@ -13,4 +14,5 @@ pub use atom_writer::{AtomSpaceWriter, AtomSpaceWriterHandle}; pub use cursor::SpaceCursor; pub use error::AtomError; pub use space::{AlignedSpace, AtomSpace}; +pub use terminated::Terminated; pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 6cd68aa0..9a18690f 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -3,6 +3,7 @@ use crate::{Atom, AtomHandle, UnidentifiedAtom}; use urid::URID; use crate::space::error::AtomError; +use crate::space::terminated::Terminated; use core::mem::{size_of, size_of_val, MaybeUninit}; /// A smart pointer that writes atom data to an internal slice. @@ -13,8 +14,7 @@ use core::mem::{size_of, size_of_val, MaybeUninit}; pub trait SpaceAllocatorImpl { fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError>; - #[must_use] - unsafe fn rewind(&mut self, byte_count: usize) -> bool; + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError>; fn allocated_bytes(&self) -> &[u8]; fn allocated_bytes_mut(&mut self) -> &mut [u8]; @@ -49,7 +49,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; let raw = self.allocate(size + required_padding)?; - AlignedSpace::align_from_bytes_mut(raw).ok_or_else(|| AtomError::Unknown) + AlignedSpace::try_align_from_bytes_mut(raw) } #[inline] @@ -124,6 +124,11 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { // SAFETY: Assume init: we just initialized the memory above Ok(unsafe { &mut *(space as *mut [_] as *mut [T]) }) } + + #[inline] + fn terminated(self, terminator: u8) -> Terminated { + Terminated::new(self, terminator) + } } impl SpaceAllocator for H {} @@ -151,7 +156,7 @@ mod tests { assert_eq!(31, cursor.remaining_bytes().len()); { - cursor.init_atom(INT_URID).unwrap().set(69); + cursor.init_atom(INT_URID).unwrap().set(69).unwrap(); assert_eq!(8, cursor.remaining_bytes().len()); } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 88914640..48f75dcb 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -95,16 +95,14 @@ impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { } #[inline] - unsafe fn rewind(&mut self, byte_count: usize) -> bool { - let rewound = self.parent.rewind(byte_count); + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { + self.parent.rewind(byte_count)?; let header = self.atom_header_mut(); - if rewound { - // SAFETY: Reducing the size of the atom is fine - header.set_size_of_body(header.size_of_body() - byte_count); - } + // SAFETY: Reducing the size of the atom is fine if rewind was successful + header.set_size_of_body(header.size_of_body() - byte_count); - rewound + Ok(()) } #[inline] diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 75c36db5..99cb2e31 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -38,14 +38,17 @@ impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { } #[inline] - unsafe fn rewind(&mut self, byte_count: usize) -> bool { + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { if self.allocated_length < byte_count { - return false; + return Err(AtomError::RewindError { + requested: byte_count, + capacity: self.allocated_length, + }); } self.allocated_length -= byte_count; - true + Ok(()) } #[inline] diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs index aef4d196..1d5da126 100644 --- a/atom/src/space/error.rs +++ b/atom/src/space/error.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use urid::{Uri, URID}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum AtomError { OutOfSpace { used: usize, @@ -16,6 +16,10 @@ pub enum AtomError { ResizeFailed, CannotUpdateAtomHeader, AtomAlreadyWritten, + RewindError { + requested: usize, + capacity: usize, + }, // Reading InvalidAtomUrid { diff --git a/atom/src/space/space.rs b/atom/src/space/space.rs index f03556d2..5c5023b4 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/space.rs @@ -125,21 +125,23 @@ impl AlignedSpace { /// // The slice now only has space for a single value /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[10..]), Err(AtomError::ReadingOutOfBounds { capacity: 7, requested: 9 })); + /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[9..]).err().unwrap(), AtomError::ReadingOutOfBounds { capacity: 7, requested: 8 }); /// ``` #[inline] pub fn try_align_from_bytes(data: &[u8]) -> Result<&Self, AtomError> { let padding = crate::util::try_padding_for::(data)?; + let data_len = data.len(); + let data = data .get(padding..) .ok_or_else(|| AtomError::ReadingOutOfBounds { - capacity: data.len(), + capacity: data_len, requested: padding + 1, })?; if data.is_empty() { return Err(AtomError::ReadingOutOfBounds { - capacity: data.len(), + capacity: data_len, requested: padding + 1, }); } @@ -158,27 +160,39 @@ impl AlignedSpace { /// # Example /// /// ``` - /// # use lv2_atom::space::AlignedSpace; + /// # use lv2_atom::space::{AlignedSpace, AtomError}; /// let values = &mut [42u64, 69]; /// // Transmuting to a slice of bytes /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; /// /// // The slice has space for both values - /// assert_eq!(AlignedSpace::::align_from_bytes_mut(bytes).unwrap().values_len(), 2); + /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(bytes).unwrap().values_len(), 2); /// // The slice now only has space for a single value - /// assert_eq!(AlignedSpace::::align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); + /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert!(AlignedSpace::::align_from_bytes_mut(&mut bytes[10..]).is_none()); + /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[9..]).err().unwrap(), AtomError::ReadingOutOfBounds { capacity: 7, requested: 8 }); /// ``` #[inline] - pub fn align_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { - let data = data.get_mut(crate::util::padding_for::(data)?..)?; + pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AtomError> { + let padding = crate::util::try_padding_for::(data)?; + let data_len = data.len(); + + let data = data + .get_mut(padding..) + .ok_or_else(|| AtomError::ReadingOutOfBounds { + capacity: data_len, + requested: padding + 1, + })?; + if data.is_empty() { - return None; + return Err(AtomError::ReadingOutOfBounds { + capacity: data_len, + requested: padding + 1, + }); } // SAFETY: We just aligned the slice start - Some(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) + Ok(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) } /// Creates a space from an empty slice. diff --git a/atom/src/space/terminated.rs b/atom/src/space/terminated.rs new file mode 100644 index 00000000..92813527 --- /dev/null +++ b/atom/src/space/terminated.rs @@ -0,0 +1,57 @@ +use crate::space::{AtomError, SpaceAllocatorImpl}; + +pub struct Terminated { + inner: W, + terminator: u8, + wrote_terminator_byte: bool, +} + +impl Terminated { + pub fn new(inner: W, terminator: u8) -> Self { + Self { + inner, + terminator, + wrote_terminator_byte: false, + } + } +} + +impl SpaceAllocatorImpl for Terminated { + fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + if self.wrote_terminator_byte { + // SAFETY: We checked we already wrote the terminator byte, and it is safe to be overwritten + unsafe { self.inner.rewind(1)? }; + } + + let (allocated, allocatable) = self.inner.allocate_and_split(size + 1)?; + allocatable[size] = self.terminator; + self.wrote_terminator_byte = true; + + Ok((allocated, &mut allocatable[..size])) + } + + #[inline] + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { + self.inner.rewind(byte_count) + } + + #[inline] + fn allocated_bytes(&self) -> &[u8] { + self.inner.allocated_bytes() + } + + #[inline] + fn allocated_bytes_mut(&mut self) -> &mut [u8] { + self.inner.allocated_bytes_mut() + } + + #[inline] + fn remaining_bytes(&self) -> &[u8] { + self.inner.remaining_bytes() + } + + #[inline] + fn remaining_bytes_mut(&mut self) -> &mut [u8] { + self.inner.remaining_bytes_mut() + } +} diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 2869d3d7..c583b8da 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -100,14 +100,17 @@ impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { #[inline] #[allow(unsafe_code)] - unsafe fn rewind(&mut self, byte_count: usize) -> bool { + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { if self.allocated_length < byte_count { - return false; + return Err(AtomError::RewindError { + requested: byte_count, + capacity: self.allocated_length, + }); } self.allocated_length -= byte_count; - true + Ok(()) } #[inline] diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index e899409c..3b5ec2f4 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -54,7 +54,8 @@ impl Plugin for AtomPlugin { .output .init::(self.urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(self.urids.units.frame)); + .with_unit(TimeStampURID::Frames(self.urids.units.frame)) + .unwrap(); for (time_stamp, atom) in sequence_reader { match atom.read(self.urids.atom.int) { @@ -108,7 +109,8 @@ fn main() { let mut writer = space .init_atom(urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(urids.units.frame)); + .with_unit(TimeStampURID::Frames(urids.units.frame)) + .unwrap(); { let _ = writer .init(TimeStamp::Frames(0), urids.atom.int) diff --git a/docs/fifths/src/lib.rs b/docs/fifths/src/lib.rs index 78aef9fa..6b2bb2c6 100644 --- a/docs/fifths/src/lib.rs +++ b/docs/fifths/src/lib.rs @@ -51,7 +51,8 @@ impl Plugin for Fifths { .output .init(self.urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(self.urids.unit.frame)); + .with_unit(TimeStampURID::Frames(self.urids.unit.frame)) + .unwrap(); for (timestamp, atom) in input_sequence { // Every message is forwarded, regardless of its contents. diff --git a/midi/src/lib.rs b/midi/src/lib.rs index 1a15e1f7..191ddd60 100644 --- a/midi/src/lib.rs +++ b/midi/src/lib.rs @@ -36,11 +36,12 @@ //! let mut output_sequence = ports.output //! .init(urids.atom.sequence) //! .unwrap() -//! .with_unit(TimeStampURID::Frames(urids.units.frame)); +//! .with_unit(TimeStampURID::Frames(urids.units.frame)) +//! .unwrap(); //! //! for (timestamp, atom) in input_sequence { //! // If the atom encodes a message... -//! if let Some(message) = atom.read(urids.midi.wmidi) { +//! if let Ok(message) = atom.read(urids.midi.wmidi) { //! // Calculate the message to send. //! let message_to_send = match message { //! MidiMessage::NoteOn(channel, note, velocity) => { diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 7b9f2c0e..cab7d281 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -4,7 +4,7 @@ //! //! If you want to have raw, low-level access to the messages, you should use the [raw module](../raw/index.html). use atom::prelude::*; -use atom::space::AtomError; +use atom::space::{AtomError, Terminated}; use atom::AtomHandle; use std::convert::TryFrom; use urid::*; @@ -94,10 +94,12 @@ impl Atom for SystemExclusiveWMidiEvent { WMidiEvent::read(space) } - fn init(frame: AtomSpaceWriter) -> Result { - let mut writer = Writer { frame }; - writer.write::(0xf0); - Ok(writer) + fn init(mut frame: AtomSpaceWriter) -> Result { + frame.write_value(0xf0u8)?; + + Ok(Writer { + frame: frame.terminated(0xf7), + }) } } @@ -107,7 +109,7 @@ impl Atom for SystemExclusiveWMidiEvent { /// /// The "start of system exclusive" status byte is written by [`SystemExclusiveWMidiEvent::init`](struct.SystemExclusiveWMidiEvent.html#method.init) method and the "end of system exclusive" status byte is written when the writer is dropped. pub struct Writer<'a> { - frame: AtomSpaceWriter<'a>, + frame: Terminated>, } impl<'a> Writer<'a> { @@ -125,13 +127,6 @@ impl<'a> Writer<'a> { } } -// TODO: use rewind instead of relying on a Drop -impl<'a> Drop for Writer<'a> { - fn drop(&mut self) { - self.write::(0xf7); - } -} - #[cfg(test)] mod tests { use crate::wmidi_binding::*; @@ -194,7 +189,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space.init_atom(urid).unwrap(); - writer.write_raw(&[1, 2, 3, 4]); + writer.write_raw(&[1, 2, 3, 4]).unwrap(); } // verifying diff --git a/state/src/raw.rs b/state/src/raw.rs index 423b6f00..f444ced6 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -262,7 +262,8 @@ mod tests { .draft(URID::new(4).unwrap()) .init(urids.int) .unwrap() - .set(0); + .set(0) + .unwrap(); } fn retrieve(storage: &mut Storage, urids: &AtomURIDCollection) { diff --git a/state/tests/integration.rs b/state/tests/integration.rs index 9408e5e4..2f4a83d6 100644 --- a/state/tests/integration.rs +++ b/state/tests/integration.rs @@ -60,7 +60,8 @@ impl State for Stateful { .init(self.urids.vector)? .of_type(self.urids.float) .map_err(|_| StateErr::Unknown)? - .append(self.audio.as_ref()); + .append(self.audio.as_ref()) + .unwrap(); store.commit_all() } From 8c2e516e88d8647546529ef388904ce43a5d0b4e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Sep 2021 21:46:41 +0200 Subject: [PATCH 34/54] Change Sequence to hold its unit in a generic type rather than having to check them using an enum --- atom/src/atoms/sequence.rs | 204 +++++++++++--------------------- atom/src/atoms/sequence/unit.rs | 64 ++++++++++ atom/src/lib.rs | 19 +-- atom/src/space/error.rs | 5 + atom/tests/atom_integration.rs | 27 ++--- docs/fifths/src/lib.rs | 9 +- docs/metro/src/lib.rs | 5 +- docs/midigate/src/lib.rs | 9 +- midi/src/lib.rs | 19 +-- 9 files changed, 182 insertions(+), 179 deletions(-) create mode 100644 atom/src/atoms/sequence/unit.rs diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 1438de3b..fdc12019 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -29,15 +29,16 @@ //! // The reading method needs the URID of the BPM unit to tell if the time stamp //! // is measured in beats or in frames. If the atom doesn't says that it's measured //! // in beats, it is assumed that it is measured in frames. -//! let input_sequence: SequenceIterator = ports.input +//! let input_sequence: SequenceIterator = ports.input //! .read(urids.atom.sequence) -//! .unwrap().read(urids.units.beat); +//! .unwrap() +//! .with_unit(urids.units.frame).unwrap(); //! //! // Get the write handle to the sequence. //! // You have to provide the unit of the time stamps. -//! let mut output_sequence: SequenceWriter = ports.output.init(urids.atom.sequence) +//! let mut output_sequence: SequenceWriter = ports.output.init(urids.atom.sequence) //! .unwrap() -//! .with_unit(TimeStampURID::BeatsPerMinute(urids.units.beat)) +//! .with_unit(urids.units.frame) //! .unwrap(); //! //! // Iterate through all events in the input sequence. @@ -48,11 +49,11 @@ //! // The sequence writer, however, assures that the written time stamps are monotonic. //! for event in input_sequence { //! // An event contains a timestamp and an atom. -//! let (timestamp, atom): (TimeStamp, &UnidentifiedAtom) = event; +//! let (timestamp, atom): (i64, &UnidentifiedAtom) = event; //! // If the read atom is a 32-bit integer... //! if let Ok(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); +//! output_sequence.new_event(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); @@ -64,19 +65,18 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) +mod unit; + use crate::space::reader::SpaceReader; use crate::*; +use std::marker::PhantomData; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; -use units::prelude::*; +pub use unit::*; #[repr(C, align(8))] #[derive(Copy, Clone)] struct SequenceBody(sys::LV2_Atom_Sequence_Body); -#[repr(C, align(8))] -#[derive(Copy, Clone)] -struct TimestampBody(RawTimeStamp); - /// An atom containing a sequence of time-stamped events. /// /// [See also the module documentation.](index.html) @@ -102,16 +102,23 @@ pub struct SequenceHeaderReader<'a> { } impl<'a> SequenceHeaderReader<'a> { - pub fn read(self, bpm_urid: URID) -> SequenceIterator<'a> { - let unit = if self.header.unit == bpm_urid { - TimeStampUnit::BeatsPerMinute + pub fn with_unit( + self, + unit_urid: URID, + ) -> Result, AtomError> { + if (self.header.unit == 0 && U::TYPE == SequenceUnitType::Frame) + || (self.header.unit == unit_urid) + { + Ok(SequenceIterator { + reader: self.reader, + unit_type: PhantomData, + }) } else { - TimeStampUnit::Frames - }; - - SequenceIterator { - reader: self.reader, - unit, + Err(AtomError::InvalidUrid { + expected_uri: U::uri(), + expected_urid: unit_urid.into_general(), + found_urid: self.header.unit, + }) } } } @@ -121,12 +128,12 @@ pub struct SequenceHeaderWriter<'a> { } impl<'a> SequenceHeaderWriter<'a> { - pub fn with_unit(mut self, unit: TimeStampURID) -> Result, AtomError> { + pub fn with_unit( + mut self, + unit_urid: URID, + ) -> Result, AtomError> { let header = SequenceBody(sys::LV2_Atom_Sequence_Body { - unit: match unit { - TimeStampURID::BeatsPerMinute(urid) => urid.get(), - TimeStampURID::Frames(urid) => urid.get(), - }, + unit: unit_urid.get(), pad: 0, }); @@ -134,7 +141,6 @@ impl<'a> SequenceHeaderWriter<'a> { Ok(SequenceWriter { writer: self.writer, - unit: unit.into(), last_stamp: None, }) } @@ -160,81 +166,23 @@ impl Atom for Sequence { } } -/// The measuring units of time stamps. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum TimeStampUnit { - Frames, - BeatsPerMinute, -} - -/// An event time stamp. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum TimeStamp { - Frames(i64), - BeatsPerMinute(f64), -} - -/// The measuring units of time stamps, with their URIDs. -#[derive(Clone, Copy)] -pub enum TimeStampURID { - Frames(URID), - BeatsPerMinute(URID), -} - -impl From for TimeStampUnit { - fn from(urid: TimeStampURID) -> TimeStampUnit { - match urid { - TimeStampURID::Frames(_) => TimeStampUnit::Frames, - TimeStampURID::BeatsPerMinute(_) => TimeStampUnit::BeatsPerMinute, - } - } -} - -impl TimeStamp { - pub fn as_frames(self) -> Option { - match self { - Self::Frames(frame) => Some(frame), - _ => None, - } - } - - pub fn as_bpm(self) -> Option { - match self { - Self::BeatsPerMinute(bpm) => Some(bpm), - _ => None, - } - } -} - /// An iterator over all events in a sequence. -pub struct SequenceIterator<'a> { +pub struct SequenceIterator<'a, U: SequenceUnit> { reader: SpaceReader<'a>, - unit: TimeStampUnit, -} - -impl<'a> SequenceIterator<'a> { - pub fn unit(&self) -> TimeStampUnit { - self.unit - } + unit_type: PhantomData, } -impl<'a> Iterator for SequenceIterator<'a> { - type Item = (TimeStamp, &'a UnidentifiedAtom); - - fn next(&mut self) -> Option<(TimeStamp, &'a UnidentifiedAtom)> { - let unit = self.unit; +impl<'a, U: SequenceUnit> Iterator for SequenceIterator<'a, U> { + type Item = (U::Value, &'a UnidentifiedAtom); + fn next(&mut self) -> Option<(U::Value, &'a UnidentifiedAtom)> { self.reader .try_read(|reader| { // SAFETY: The validity of the space's contents is guaranteed by this type. let raw_stamp: &RawTimeStamp = unsafe { reader.next_value()? }; - let stamp = match unit { - TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) }, - TimeStampUnit::BeatsPerMinute => unsafe { - TimeStamp::BeatsPerMinute(raw_stamp.beats) - }, - }; + // SAFETY: The validity of the unit type is guaranteed by this type. + let stamp = unsafe { U::convert_from_raw(*raw_stamp) }; // SAFETY: The validity of the space's contents is guaranteed by this type. let atom = unsafe { reader.next_atom()? }; @@ -246,46 +194,28 @@ impl<'a> Iterator for SequenceIterator<'a> { } /// The writing handle for sequences. -pub struct SequenceWriter<'a> { +pub struct SequenceWriter<'a, U: SequenceUnit> { writer: AtomSpaceWriter<'a>, - unit: TimeStampUnit, - last_stamp: Option, + last_stamp: Option, } -impl<'a> SequenceWriter<'a> { +impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { /// Write out the time stamp and update `last_stamp`. /// /// This method returns `Ǹone` if: - /// * The time stamp is not measured in our unit. /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. - fn write_time_stamp(&mut self, stamp: TimeStamp) -> Result<(), AtomError> { - let raw_stamp = match self.unit { - TimeStampUnit::Frames => { - let frames = stamp.as_frames().unwrap(); // TODO - if let Some(last_stamp) = self.last_stamp { - if last_stamp.as_frames().unwrap() > frames { - return Err(AtomError::InvalidAtomValue { - reading_type_uri: Sequence::uri(), - }); - } - } - RawTimeStamp { frames } + fn write_time_stamp(&mut self, time_stamp: U::Value) -> Result<(), AtomError> { + if let Some(last_stamp) = self.last_stamp { + if last_stamp > time_stamp { + return Err(AtomError::InvalidAtomValue { + reading_type_uri: Sequence::uri(), + }); } - TimeStampUnit::BeatsPerMinute => { - let beats = stamp.as_bpm().unwrap(); // TODO - if let Some(last_stamp) = self.last_stamp { - if last_stamp.as_bpm().unwrap() > beats { - return Err(AtomError::InvalidAtomValue { - reading_type_uri: Sequence::uri(), - }); - } - } - RawTimeStamp { beats } - } - }; - self.last_stamp = Some(stamp); - self.writer.write_value(TimestampBody(raw_stamp))?; + } + + self.last_stamp = Some(time_stamp); + self.writer.write_value(U::convert_into_raw(time_stamp))?; Ok(()) } @@ -293,12 +223,12 @@ impl<'a> SequenceWriter<'a> { /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn init( + pub fn new_event( &mut self, - stamp: TimeStamp, + time_stamp: U::Value, urid: URID, ) -> Result<::Handle, AtomError> { - self.write_time_stamp(stamp)?; + self.write_time_stamp(time_stamp)?; self.writer.init_atom(urid) } @@ -307,8 +237,12 @@ impl<'a> SequenceWriter<'a> { /// If your cannot identify the type of the atom but have to write it, you can simply forward it. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. - pub fn forward(&mut self, stamp: TimeStamp, atom: &UnidentifiedAtom) -> Result<(), AtomError> { - self.write_time_stamp(stamp)?; + pub fn forward( + &mut self, + time_stamp: U::Value, + atom: &UnidentifiedAtom, + ) -> Result<(), AtomError> { + self.write_time_stamp(time_stamp)?; self.writer.forward_atom(atom)?; @@ -321,6 +255,7 @@ mod tests { use crate::atoms::sequence::*; use crate::prelude::*; use std::mem::size_of; + use units::UnitURIDCollection; #[derive(URIDCollection)] struct TestURIDCollection { @@ -342,17 +277,17 @@ mod tests { let mut writer = space .init_atom(urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(urids.units.frame)) + .with_unit(urids.units.frame) .unwrap(); writer - .init::(TimeStamp::Frames(0), urids.atom.int) + .new_event(0, urids.atom.int) .unwrap() .set(42) .unwrap(); writer - .init::(TimeStamp::Frames(1), urids.atom.long) + .new_event(1, urids.atom.long) .unwrap() .set(17) .unwrap(); @@ -397,16 +332,15 @@ mod tests { .unwrap() .read(urids.atom.sequence) .unwrap() - .read(urids.units.beat); - - assert_eq!(reader.unit(), TimeStampUnit::Frames); + .with_unit(urids.units.frame) + .unwrap(); let (stamp, atom) = reader.next().unwrap(); - assert_eq!(stamp, TimeStamp::Frames(0)); + assert_eq!(stamp, 0); assert_eq!(*atom.read::(urids.atom.int).unwrap(), 42); let (stamp, atom) = reader.next().unwrap(); - assert_eq!(stamp, TimeStamp::Frames(1)); + assert_eq!(stamp, 1); assert_eq!(*atom.read::(urids.atom.long).unwrap(), 17); assert!(reader.next().is_none()); diff --git a/atom/src/atoms/sequence/unit.rs b/atom/src/atoms/sequence/unit.rs new file mode 100644 index 00000000..6df00d62 --- /dev/null +++ b/atom/src/atoms/sequence/unit.rs @@ -0,0 +1,64 @@ +use lv2_sys::LV2_Atom_Event__bindgen_ty_1; +use lv2_units::units::{Beat, Frame}; +use urid::UriBound; + +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub struct TimestampBody(LV2_Atom_Event__bindgen_ty_1); + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum SequenceUnitType { + Beat, + Frame, +} + +pub trait SequenceUnit: UriBound + private::Sealed { + type Value: Copy + PartialEq + PartialOrd + 'static; + + const TYPE: SequenceUnitType; + + #[doc(hidden)] + unsafe fn convert_from_raw(raw: sys::LV2_Atom_Event__bindgen_ty_1) -> Self::Value; + + #[doc(hidden)] + fn convert_into_raw(value: Self::Value) -> TimestampBody; +} + +impl SequenceUnit for Beat { + type Value = f64; + const TYPE: SequenceUnitType = SequenceUnitType::Beat; + + #[inline] + unsafe fn convert_from_raw(raw: LV2_Atom_Event__bindgen_ty_1) -> Self::Value { + raw.beats + } + + #[inline] + fn convert_into_raw(value: Self::Value) -> TimestampBody { + TimestampBody(LV2_Atom_Event__bindgen_ty_1 { beats: value }) + } +} + +impl SequenceUnit for Frame { + type Value = i64; + const TYPE: SequenceUnitType = SequenceUnitType::Frame; + + #[inline] + unsafe fn convert_from_raw(raw: LV2_Atom_Event__bindgen_ty_1) -> Self::Value { + raw.frames + } + + #[inline] + fn convert_into_raw(value: Self::Value) -> TimestampBody { + TimestampBody(LV2_Atom_Event__bindgen_ty_1 { frames: value }) + } +} + +mod private { + use super::*; + + pub trait Sealed {} + + impl Sealed for Beat {} + impl Sealed for Frame {} +} diff --git a/atom/src/lib.rs b/atom/src/lib.rs index aff5bd55..a0b46f6c 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -31,24 +31,25 @@ //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: &MyURIDs) { //! // Get the read handle to the sequence. -//! let input_sequence = ports.input.read( -//! urids.atom.sequence, -//! ).unwrap().read(urids.units.beat); +//! let input_sequence = ports.input +//! .read(urids.atom.sequence) +//! .unwrap() +//! .with_unit(urids.units.frame) +//! .unwrap(); //! //! // Get the write handle to the sequence. //! let mut output_sequence = ports.output.init(urids.atom.sequence) //! .unwrap() -//! .with_unit(TimeStampURID::Frames(urids.units.frame)) +//! .with_unit(urids.units.frame) //! .unwrap(); //! //! // Iterate through all events in the input sequence. -//! for event in input_sequence { -//! // An event contains a timestamp and an atom. -//! let (timestamp, atom) = event; +//! // An event contains a timestamp and an atom. +//! for (timestamp, atom) in input_sequence { //! // If the read atom is a 32-bit integer... //! if let Ok(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.init(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); +//! output_sequence.new_event(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); //! } else { //! // Forward the atom to the sequence without a change. //! output_sequence.forward(timestamp, atom).unwrap(); @@ -80,7 +81,7 @@ pub mod prelude { pub use atoms::chunk::Chunk; pub use atoms::object::{Object, ObjectHeader, PropertyHeader}; pub use atoms::scalar::{AtomURID, Bool, Double, Float, Int, Long}; - pub use atoms::sequence::{Sequence, TimeStamp, TimeStampURID}; + pub use atoms::sequence::Sequence; pub use atoms::string::{Literal, LiteralInfo, String}; pub use atoms::tuple::Tuple; pub use atoms::vector::Vector; diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs index 1d5da126..0e7a68db 100644 --- a/atom/src/space/error.rs +++ b/atom/src/space/error.rs @@ -27,6 +27,11 @@ pub enum AtomError { expected_urid: URID, found_urid: URID, }, + InvalidUrid { + expected_uri: &'static Uri, + expected_urid: URID, + found_urid: u32, + }, ReadingOutOfBounds { capacity: usize, requested: usize, diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 3b5ec2f4..a58a90a0 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -46,22 +46,23 @@ impl Plugin for AtomPlugin { fn run(&mut self, ports: &mut Ports, _: &mut (), _: u32) { let sequence_reader = ports .input - .read::(self.urids.atom.sequence) + .read(self.urids.atom.sequence) .unwrap() - .read(self.urids.units.beat); + .with_unit(self.urids.units.frame) + .unwrap(); let mut sequence_writer = ports .output - .init::(self.urids.atom.sequence) + .init(self.urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(self.urids.units.frame)) + .with_unit(self.urids.units.frame) .unwrap(); for (time_stamp, atom) in sequence_reader { match atom.read(self.urids.atom.int) { Ok(number) => { sequence_writer - .init(time_stamp, self.urids.atom.int) + .new_event(time_stamp, self.urids.atom.int) .unwrap() .set(number * 2) .unwrap(); @@ -109,26 +110,22 @@ fn main() { let mut writer = space .init_atom(urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(urids.units.frame)) + .with_unit(urids.units.frame) .unwrap(); { let _ = writer - .init(TimeStamp::Frames(0), urids.atom.int) + .new_event(0, urids.atom.int) .unwrap() .set(42) .unwrap(); } writer - .init(TimeStamp::Frames(1), urids.atom.long) + .new_event(1, urids.atom.long) .unwrap() .set(17) .unwrap(); - writer - .init(TimeStamp::Frames(2), urids.atom.int) - .unwrap() - .set(3) - .unwrap(); + writer.new_event(2, urids.atom.int).unwrap().set(3).unwrap(); } // preparing the output atom. @@ -190,10 +187,10 @@ fn main() { .unwrap() .read(urids.atom.sequence) .unwrap() - .read(urids.units.beat); + .with_unit(urids.units.frame) + .unwrap(); for (stamp, atom) in sequence { - let stamp = stamp.as_frames().unwrap(); match stamp { 0 => assert_eq!(*atom.read(urids.atom.int).unwrap(), 84), 1 => assert_eq!(*atom.read(urids.atom.long).unwrap(), 17), diff --git a/docs/fifths/src/lib.rs b/docs/fifths/src/lib.rs index 6b2bb2c6..7cf71cc1 100644 --- a/docs/fifths/src/lib.rs +++ b/docs/fifths/src/lib.rs @@ -44,14 +44,15 @@ impl Plugin for Fifths { .input .read(self.urids.atom.sequence) .unwrap() - .read(self.urids.unit.beat); + .with_unit(self.urids.unit.frame) + .unwrap(); // Initialise the output sequence and get the writing handle. let mut output_sequence = ports .output .init(self.urids.atom.sequence) .unwrap() - .with_unit(TimeStampURID::Frames(self.urids.unit.frame)) + .with_unit(self.urids.unit.frame) .unwrap(); for (timestamp, atom) in input_sequence { @@ -71,7 +72,7 @@ impl Plugin for Fifths { if let Ok(note) = note.step(7) { // Write the fifth. Writing is done after initialization. output_sequence - .init(timestamp, self.urids.midi.wmidi) + .new_event(timestamp, self.urids.midi.wmidi) .unwrap() .set(MidiMessage::NoteOn(channel, note, velocity)) .unwrap(); @@ -81,7 +82,7 @@ impl Plugin for Fifths { // Do the same thing for `NoteOff`. if let Ok(note) = note.step(7) { output_sequence - .init(timestamp, self.urids.midi.wmidi) + .new_event(timestamp, self.urids.midi.wmidi) .unwrap() .set(MidiMessage::NoteOff(channel, note, velocity)) .unwrap(); diff --git a/docs/metro/src/lib.rs b/docs/metro/src/lib.rs index a847b7d2..d781ec51 100644 --- a/docs/metro/src/lib.rs +++ b/docs/metro/src/lib.rs @@ -78,13 +78,12 @@ impl Plugin for Metro { if let Ok(control) = ports .control .read(self.urids.atom.sequence) - .map(|s| s.read(self.urids.unit.beat)) + .and_then(|s| s.with_unit(self.urids.unit.beat)) { // Here, the final assembly of the pipeline is done. First, the event iterator is pre-processed to only emit an index and an `UnidentifiedAtom`. Then, the event iterator is wrapped into an `EventAtomizer`, which is then connected to an `EventReader` and the envelope. The resulting pipe consumes a `()` and emits the next frame of the envelope; It's already a compact pipeline. // // Then, the final pipeline is constructed using some lazy pipes: The first one splits a `()` to a tuple of `()`, which is then connected to a tuple of the envelope and the pre-constructed sampler. A tuple of two pipes is also a pipe; The two pipes are processed in parallel. Then, the emitted envelope and sample frame are multiplied to one frame. - let control = - control.map(|(timestamp, event)| (timestamp.as_frames().unwrap() as usize, event)); + let control = control.map(|(timestamp, event)| (timestamp as usize, event)); let complete_envelope = EventAtomizer::new(control).compose() >> EventReader::new(&self.urids.atom, &self.urids.time) diff --git a/docs/midigate/src/lib.rs b/docs/midigate/src/lib.rs index 0499175e..13d8141a 100644 --- a/docs/midigate/src/lib.rs +++ b/docs/midigate/src/lib.rs @@ -90,14 +90,11 @@ impl Plugin for Midigate { .control .read(self.urids.atom.sequence) .unwrap() - .read(self.urids.unit.beat); + .with_unit(self.urids.unit.frame) + .unwrap(); for (timestamp, message) in control_sequence { - let timestamp: usize = if let Some(timestamp) = timestamp.as_frames() { - timestamp as usize - } else { - continue; - }; + let timestamp = timestamp as usize; let message = if let Ok(message) = message.read(self.urids.midi.wmidi) { message diff --git a/midi/src/lib.rs b/midi/src/lib.rs index 191ddd60..29fb7a42 100644 --- a/midi/src/lib.rs +++ b/midi/src/lib.rs @@ -32,12 +32,17 @@ //! /// Something like a plugin's run method. //! fn run(ports: &mut MyPorts, urids: MyURIDs) { //! // Retrieving the input and output sequence. -//! let input_sequence = ports.input.read(urids.atom.sequence).unwrap().read(urids.units.beat); +//! let input_sequence = ports.input +//! .read(urids.atom.sequence) +//! .unwrap() +//! .with_unit(urids.units.frame) +//! .unwrap(); +//! //! let mut output_sequence = ports.output -//! .init(urids.atom.sequence) -//! .unwrap() -//! .with_unit(TimeStampURID::Frames(urids.units.frame)) -//! .unwrap(); +//! .init(urids.atom.sequence) +//! .unwrap() +//! .with_unit(urids.units.frame) +//! .unwrap(); //! //! for (timestamp, atom) in input_sequence { //! // If the atom encodes a message... @@ -47,7 +52,7 @@ //! MidiMessage::NoteOn(channel, note, velocity) => { //! let note = note.step(5).unwrap(); // modulate up five half-steps. //! MidiMessage::NoteOn(channel, note, velocity) -//! }, +//! } //! MidiMessage::NoteOff(channel, note, velocity) => { //! let note = note.step(5).unwrap(); // modulate up five half-steps. //! MidiMessage::NoteOff(channel, note, velocity) @@ -55,7 +60,7 @@ //! _ => message, //! }; //! // Write the modulated message or forward it. -//! output_sequence.init(timestamp, urids.midi.wmidi).unwrap().set(message_to_send).unwrap(); +//! output_sequence.new_event(timestamp, urids.midi.wmidi).unwrap().set(message_to_send).unwrap(); //! } else { //! // Forward the other, uninterpreted message. //! output_sequence.forward(timestamp, atom); From c6aff298fab2f049b877e9ca559a023cd80f1b3f Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 21 Sep 2021 18:37:16 +0200 Subject: [PATCH 35/54] Split Atom error type --- atom/src/atoms/chunk.rs | 9 ++-- atom/src/atoms/object.rs | 27 +++++----- atom/src/atoms/scalar.rs | 12 +++-- atom/src/atoms/sequence.rs | 22 ++++---- atom/src/atoms/sequence/unit.rs | 18 +++---- atom/src/atoms/string.rs | 40 ++++++++------- atom/src/atoms/tuple.rs | 8 +-- atom/src/atoms/vector.rs | 31 ++++++------ atom/src/header.rs | 6 +-- atom/src/lib.rs | 35 ++++++------- atom/src/port.rs | 7 +-- atom/src/space.rs | 14 +++--- atom/src/space/{space.rs => aligned.rs} | 41 +++++++-------- atom/src/space/allocatable.rs | 27 +++++----- atom/src/space/atom_writer.rs | 30 +++++------ atom/src/space/cursor.rs | 18 ++++--- atom/src/space/error.rs | 67 ++++++++++++++++++++++--- atom/src/space/reader.rs | 37 +++++++------- atom/src/space/terminated.rs | 10 ++-- atom/src/space/vec.rs | 22 ++++---- atom/src/util.rs | 17 +++---- midi/src/raw.rs | 6 +-- midi/src/wmidi_binding.rs | 28 ++++++----- 23 files changed, 306 insertions(+), 226 deletions(-) rename atom/src/space/{space.rs => aligned.rs} (95%) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 81b01783..ac64c61d 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -24,12 +24,13 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Chunk](http://lv2plug.in/ns/ext/atom/atom.html#Chunk) +use crate::space::error::{AtomReadError, AtomWriteError}; use crate::space::*; use crate::AtomSpaceWriter; use crate::{Atom, AtomHandle}; use urid::UriBound; -/// An atom containing memory of undefined type. +/// An atom containing an arbitrary byte buffer. /// /// [See also the module documentation.](index.html) pub struct Chunk; @@ -50,19 +51,19 @@ impl<'a> AtomHandle<'a> for ChunkWriterHandle { impl Atom for Chunk { type ReadHandle = ChunkReaderHandle; - type WriteHandle = AtomSpaceWriterHandle; + type WriteHandle = ChunkWriterHandle; #[inline] unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { Ok(body) } #[inline] fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(frame) } } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index ea89c97f..767f12db 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -68,7 +68,7 @@ //! //! # Specification //! [http://lv2plug.in/ns/ext/atom/atom.html#Object](http://lv2plug.in/ns/ext/atom/atom.html#Object). -use crate::space::reader::SpaceReader; +use crate::space::SpaceReader; use crate::*; use core::convert::TryFrom; use core::iter::Iterator; @@ -110,7 +110,10 @@ pub struct ObjectHeaderWriter<'a> { } impl<'a> ObjectHeaderWriter<'a> { - pub fn write_header(mut self, header: ObjectHeader) -> Result, AtomError> { + pub fn write_header( + mut self, + header: ObjectHeader, + ) -> Result, AtomWriteError> { self.frame.write_value(sys::LV2_Atom_Object_Body { id: header.id.map(URID::get).unwrap_or(0), otype: header.otype.get(), @@ -126,13 +129,13 @@ impl Atom for Object { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Object_Body = reader.next_value()?; let header = ObjectHeader { id: URID::try_from(header.id).ok(), - otype: URID::try_from(header.otype).map_err(|_| AtomError::InvalidAtomValue { + otype: URID::try_from(header.otype).map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), })?, }; @@ -144,7 +147,7 @@ impl Atom for Object { fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(ObjectHeaderWriter { frame }) } } @@ -167,14 +170,14 @@ impl Atom for Blank { #[inline] unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { Object::read(body) } #[inline] fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Object::init(frame) } } @@ -213,7 +216,7 @@ impl<'a> ObjectWriter<'a> { key: URID, context: URID, child_urid: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; self.frame.init_atom(child_urid) } @@ -227,7 +230,7 @@ impl<'a> ObjectWriter<'a> { &mut self, key: URID, child_urid: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Property::write_header(&mut self.frame, key, None::>)?; self.frame.init_atom(child_urid) } @@ -273,11 +276,11 @@ impl Property { /// The caller must ensure that the given Space actually contains a valid property. unsafe fn read_body<'a>( reader: &mut SpaceReader<'a>, - ) -> Result<(PropertyHeader, &'a UnidentifiedAtom), AtomError> { + ) -> Result<(PropertyHeader, &'a UnidentifiedAtom), AtomReadError> { let header: &StrippedPropertyHeader = reader.next_value()?; let header = PropertyHeader { - key: URID::try_from(header.key).map_err(|_| AtomError::InvalidAtomValue { + key: URID::try_from(header.key).map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), })?, context: URID::try_from(header.context).ok(), @@ -296,7 +299,7 @@ impl Property { space: &mut impl SpaceAllocator, key: URID, context: Option>, - ) -> Result<(), AtomError> { + ) -> Result<(), AtomWriteError> { let header = StrippedPropertyHeader { key: key.get(), context: context.map(URID::get).unwrap_or(0), diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index f267bede..173ad4c5 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -47,7 +47,7 @@ pub trait ScalarAtom: UriBound { /// /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. #[inline] - unsafe fn read_scalar(body: &AtomSpace) -> Result<&Self::InternalType, AtomError> { + unsafe fn read_scalar(body: &AtomSpace) -> Result<&Self::InternalType, AtomReadError> { body.read().next_value() } @@ -55,7 +55,9 @@ pub trait ScalarAtom: UriBound { /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. #[inline] - fn write_scalar(frame: AtomSpaceWriter) -> Result, AtomError> { + fn write_scalar( + frame: AtomSpaceWriter, + ) -> Result, AtomWriteError> { Ok(ScalarWriter(frame.re_borrow(), PhantomData)) } } @@ -64,7 +66,7 @@ pub struct ScalarWriter<'a, T: Copy + 'static>(AtomSpaceWriter<'a>, PhantomData< impl<'a, T: Copy + 'static> ScalarWriter<'a, T> { #[inline] - pub fn set(&mut self, value: T) -> Result<&mut T, AtomError> { + pub fn set(&mut self, value: T) -> Result<&mut T, AtomWriteError> { #[repr(align(8))] #[derive(Copy, Clone)] struct Padder; @@ -94,13 +96,13 @@ impl Atom for A { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { ::read_scalar(body) } fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { ::write_scalar(frame) } } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index fdc12019..3725d8e2 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -67,7 +67,7 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence) mod unit; -use crate::space::reader::SpaceReader; +use crate::space::SpaceReader; use crate::*; use std::marker::PhantomData; use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp; @@ -105,7 +105,7 @@ impl<'a> SequenceHeaderReader<'a> { pub fn with_unit( self, unit_urid: URID, - ) -> Result, AtomError> { + ) -> Result, AtomReadError> { if (self.header.unit == 0 && U::TYPE == SequenceUnitType::Frame) || (self.header.unit == unit_urid) { @@ -114,7 +114,7 @@ impl<'a> SequenceHeaderReader<'a> { unit_type: PhantomData, }) } else { - Err(AtomError::InvalidUrid { + Err(AtomReadError::InvalidUrid { expected_uri: U::uri(), expected_urid: unit_urid.into_general(), found_urid: self.header.unit, @@ -131,7 +131,7 @@ impl<'a> SequenceHeaderWriter<'a> { pub fn with_unit( mut self, unit_urid: URID, - ) -> Result, AtomError> { + ) -> Result, AtomWriteError> { let header = SequenceBody(sys::LV2_Atom_Sequence_Body { unit: unit_urid.get(), pad: 0, @@ -152,7 +152,7 @@ impl Atom for Sequence { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Sequence_Body = reader.next_value()?; @@ -161,7 +161,7 @@ impl Atom for Sequence { fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(SequenceHeaderWriter { writer: frame }) } } @@ -205,11 +205,11 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { /// This method returns `Ǹone` if: /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. - fn write_time_stamp(&mut self, time_stamp: U::Value) -> Result<(), AtomError> { + fn write_time_stamp(&mut self, time_stamp: U::Value) -> Result<(), AtomWriteError> { if let Some(last_stamp) = self.last_stamp { if last_stamp > time_stamp { - return Err(AtomError::InvalidAtomValue { - reading_type_uri: Sequence::uri(), + return Err(AtomWriteError::WritingIllegalState { + writing_type_uri: Sequence::uri(), }); } } @@ -227,7 +227,7 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { &mut self, time_stamp: U::Value, urid: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { self.write_time_stamp(time_stamp)?; self.writer.init_atom(urid) } @@ -241,7 +241,7 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { &mut self, time_stamp: U::Value, atom: &UnidentifiedAtom, - ) -> Result<(), AtomError> { + ) -> Result<(), AtomWriteError> { self.write_time_stamp(time_stamp)?; self.writer.forward_atom(atom)?; diff --git a/atom/src/atoms/sequence/unit.rs b/atom/src/atoms/sequence/unit.rs index 6df00d62..ade27169 100644 --- a/atom/src/atoms/sequence/unit.rs +++ b/atom/src/atoms/sequence/unit.rs @@ -2,10 +2,6 @@ use lv2_sys::LV2_Atom_Event__bindgen_ty_1; use lv2_units::units::{Beat, Frame}; use urid::UriBound; -#[repr(C, align(8))] -#[derive(Copy, Clone)] -pub struct TimestampBody(LV2_Atom_Event__bindgen_ty_1); - #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum SequenceUnitType { Beat, @@ -21,7 +17,7 @@ pub trait SequenceUnit: UriBound + private::Sealed { unsafe fn convert_from_raw(raw: sys::LV2_Atom_Event__bindgen_ty_1) -> Self::Value; #[doc(hidden)] - fn convert_into_raw(value: Self::Value) -> TimestampBody; + fn convert_into_raw(value: Self::Value) -> private::TimestampBody; } impl SequenceUnit for Beat { @@ -34,8 +30,8 @@ impl SequenceUnit for Beat { } #[inline] - fn convert_into_raw(value: Self::Value) -> TimestampBody { - TimestampBody(LV2_Atom_Event__bindgen_ty_1 { beats: value }) + fn convert_into_raw(value: Self::Value) -> private::TimestampBody { + private::TimestampBody(LV2_Atom_Event__bindgen_ty_1 { beats: value }) } } @@ -49,14 +45,18 @@ impl SequenceUnit for Frame { } #[inline] - fn convert_into_raw(value: Self::Value) -> TimestampBody { - TimestampBody(LV2_Atom_Event__bindgen_ty_1 { frames: value }) + fn convert_into_raw(value: Self::Value) -> private::TimestampBody { + private::TimestampBody(LV2_Atom_Event__bindgen_ty_1 { frames: value }) } } mod private { use super::*; + #[repr(C, align(8))] + #[derive(Copy, Clone)] + pub struct TimestampBody(pub LV2_Atom_Event__bindgen_ty_1); + pub trait Sealed {} impl Sealed for Beat {} diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index d7b7a58d..8a90ecbc 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -28,8 +28,10 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#String](http://lv2plug.in/ns/ext/atom/atom.html#String) //! [http://lv2plug.in/ns/ext/atom/atom.html#Literal](http://lv2plug.in/ns/ext/atom/atom.html#Literal) use crate::prelude::*; +use crate::space::error::{AtomReadError, AtomWriteError}; use crate::space::*; use crate::AtomHandle; +use std::ffi::CStr; use urid::*; /// An atom containing either a localized string or an RDF literal. @@ -78,7 +80,7 @@ pub struct LiteralInfoWriter<'a> { } impl<'a> LiteralInfoWriter<'a> { - pub fn write_info(mut self, info: LiteralInfo) -> Result, AtomError> { + pub fn write_info(mut self, info: LiteralInfo) -> Result, AtomWriteError> { self.writer.write_value(info.into_raw())?; Ok(StringWriter { @@ -105,12 +107,12 @@ impl Atom for Literal { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; let info = - LiteralInfo::try_from_raw(header).ok_or_else(|| AtomError::InvalidAtomValue { + LiteralInfo::try_from_raw(header).ok_or_else(|| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), })?; @@ -118,7 +120,7 @@ impl Atom for Literal { std::str::from_utf8(&data[0..data.len() - 1]) .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) - .map_err(|_| AtomError::InvalidAtomValue { + .map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), }) .map(|string| (info, string)) @@ -127,7 +129,7 @@ impl Atom for Literal { #[inline] fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(LiteralInfoWriter { writer: frame }) } } @@ -159,23 +161,25 @@ impl Atom for String { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { - let data = body.as_bytes(); - let rust_str_bytes = - data.get(..data.len() - 1) - .ok_or_else(|| AtomError::InvalidAtomValue { - reading_type_uri: Self::uri(), - })?; // removing the null-terminator - Ok( - core::str::from_utf8(rust_str_bytes).map_err(|_| AtomError::InvalidAtomValue { + ) -> Result<::Handle, AtomReadError> { + let c_str = CStr::from_bytes_with_nul(body.as_bytes()).map_err(|_| { + AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), - })?, - ) + } + })?; + + let str = c_str + .to_str() + .map_err(|_| AtomReadError::InvalidAtomValue { + reading_type_uri: Self::uri(), + })?; + + Ok(str) } fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(StringWriter { writer: frame.terminated(0), }) @@ -193,7 +197,7 @@ impl<'a> StringWriter<'a> { /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. /// /// If the internal space for the atom is not big enough, this method returns `None`. - pub fn append(&mut self, string: &str) -> Result<&mut str, AtomError> { + pub fn append(&mut self, string: &str) -> Result<&mut str, AtomWriteError> { let bytes = self.writer.write_bytes(string.as_bytes())?; // SAFETY: We just wrote that string, therefore it is guaranteed to be valid UTF-8 diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 19f9d9fc..5441516b 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -30,7 +30,7 @@ //! # Specification //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Tuple](http://lv2plug.in/ns/ext/atom/atom.html#Tuple) -use crate::space::reader::SpaceReader; +use crate::space::SpaceReader; use crate::*; /// An atom containing a series of other atoms. @@ -60,7 +60,7 @@ impl Atom for Tuple { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { Ok(TupleIterator { reader: body.read(), }) @@ -68,7 +68,7 @@ impl Atom for Tuple { fn init( frame: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(TupleWriter { frame }) } } @@ -99,7 +99,7 @@ impl<'a> TupleWriter<'a> { pub fn init( &mut self, child_urid: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { self.frame.init_atom(child_urid) } } diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 5e4d278e..6000d638 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -31,7 +31,7 @@ //! //! [http://lv2plug.in/ns/ext/atom/atom.html#Vector](http://lv2plug.in/ns/ext/atom/atom.html#Vector) use crate::atoms::scalar::ScalarAtom; -use crate::space::reader::SpaceReader; +use crate::space::SpaceReader; use crate::*; use std::marker::PhantomData; use std::mem::{size_of, MaybeUninit}; @@ -66,21 +66,22 @@ impl<'a> VectorReader<'a> { pub fn of_type( self, atom_type: URID, - ) -> Result<&'a [C::InternalType], AtomError> { + ) -> Result<&'a [C::InternalType], AtomReadError> { if self.header.child_type != atom_type { - return Err(AtomError::InvalidAtomUrid { - found_urid: URID::new(self.header.child_size).ok_or_else(|| { - AtomError::InvalidAtomValue { - reading_type_uri: Vector::uri(), - } - })?, + let found_urid = + URID::new(self.header.child_size).ok_or(AtomReadError::InvalidAtomValue { + reading_type_uri: Vector::uri(), + })?; + + return Err(AtomReadError::InvalidAtomUrid { + found_urid, expected_urid: atom_type.into_general(), expected_uri: C::uri(), }); } if self.header.child_size as usize != size_of::() { - return Err(AtomError::InvalidAtomValue { + return Err(AtomReadError::InvalidAtomValue { reading_type_uri: Vector::uri(), }); } @@ -99,7 +100,7 @@ impl<'a> VectorTypeWriter<'a> { pub fn of_type( mut self, atom_type: URID, - ) -> Result, AtomError> { + ) -> Result, AtomWriteError> { let body = sys::LV2_Atom_Vector_Body { child_type: atom_type.get(), child_size: size_of::() as u32, @@ -120,7 +121,7 @@ impl Atom for Vector { unsafe fn read( body: &AtomSpace, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { let mut reader = body.read(); let header: &sys::LV2_Atom_Vector_Body = reader.next_value()?; @@ -129,7 +130,7 @@ impl Atom for Vector { fn init( writer: AtomSpaceWriter, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { Ok(VectorTypeWriter { writer }) } } @@ -145,7 +146,7 @@ pub struct VectorWriter<'a, A: ScalarAtom> { impl<'a, A: ScalarAtom> VectorWriter<'a, A> { /// Push a single value to the vector. #[inline] - pub fn push(&mut self, child: A::InternalType) -> Result<&mut A::InternalType, AtomError> { + pub fn push(&mut self, child: A::InternalType) -> Result<&mut A::InternalType, AtomWriteError> { self.writer.write_value(child) } @@ -156,7 +157,7 @@ impl<'a, A: ScalarAtom> VectorWriter<'a, A> { pub fn allocate_uninit( &mut self, count: usize, - ) -> Result<&mut [MaybeUninit], AtomError> { + ) -> Result<&mut [MaybeUninit], AtomWriteError> { self.writer.allocate_values(count) } @@ -165,7 +166,7 @@ impl<'a, A: ScalarAtom> VectorWriter<'a, A> { pub fn append( &mut self, data: &[A::InternalType], - ) -> Result<&mut [A::InternalType], AtomError> { + ) -> Result<&mut [A::InternalType], AtomWriteError> { self.writer.write_values(data) } } diff --git a/atom/src/header.rs b/atom/src/header.rs index 8e3327e0..bfd8151c 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -1,4 +1,4 @@ -use crate::space::AtomError; +use crate::space::error::AtomReadError; use crate::{Atom, UnidentifiedAtom}; use urid::URID; @@ -62,11 +62,11 @@ impl AtomHeader { } #[inline] - pub fn check_urid(self, other: URID) -> Result<(), AtomError> { + pub(crate) fn check_urid(self, other: URID) -> Result<(), AtomReadError> { if other == self.urid() { Ok(()) } else { - Err(AtomError::InvalidAtomUrid { + Err(AtomReadError::InvalidAtomUrid { expected_uri: A::uri(), expected_urid: other.into_general(), found_urid: self.urid(), diff --git a/atom/src/lib.rs b/atom/src/lib.rs index a0b46f6c..ddae4778 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -64,6 +64,7 @@ extern crate lv2_sys as sys; extern crate lv2_units as units; +use crate::space::error::{AtomReadError, AtomWriteError}; pub use header::AtomHeader; use space::*; use urid::*; @@ -122,8 +123,9 @@ pub trait Atom: UriBound { /// /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. - unsafe fn read(body: &AtomSpace) - -> Result<::Handle, AtomError>; + unsafe fn read( + body: &AtomSpace, + ) -> Result<::Handle, AtomReadError>; /// Initialize the body of the atom. /// @@ -135,7 +137,7 @@ pub trait Atom: UriBound { /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. fn init( writer: AtomSpaceWriter, - ) -> Result<::Handle, AtomError>; + ) -> Result<::Handle, AtomWriteError>; } /// An atom of yet unknown type. @@ -154,10 +156,10 @@ impl UnidentifiedAtom { /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] - pub unsafe fn from_space(space: &AtomSpace) -> Result<&Self, AtomError> { + pub unsafe fn from_space(space: &AtomSpace) -> Result<&Self, AtomReadError> { Ok(Self::from_header(space.assume_init_value().ok_or_else( - || AtomError::ReadingOutOfBounds { - capacity: space.len(), + || AtomReadError::ReadingOutOfBounds { + available: space.len(), requested: ::core::mem::size_of::(), }, )?)) @@ -169,16 +171,15 @@ impl UnidentifiedAtom { /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] - pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomError> { - let len = space.len(); - Ok(Self::from_header_mut( - space - .assume_init_value_mut() - .ok_or_else(|| AtomError::ReadingOutOfBounds { - capacity: len, - requested: ::core::mem::size_of::(), - })?, - )) + pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomWriteError> { + let available = space.len(); + + Ok(Self::from_header_mut(space.assume_init_value_mut().ok_or( + AtomWriteError::WritingOutOfBounds { + available, + requested: ::core::mem::size_of::(), + }, + )?)) } #[inline] @@ -199,7 +200,7 @@ impl UnidentifiedAtom { pub fn read( &self, urid: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { self.header.check_urid(urid)?; // SAFETY: the fact that this contains a valid instance of A is checked above. diff --git a/atom/src/port.rs b/atom/src/port.rs index 10f3a21a..3e78c708 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -24,6 +24,7 @@ //! } //! ``` use crate::header::AtomHeader; +use crate::space::error::{AtomReadError, AtomWriteError}; use crate::space::*; use crate::{AtomHandle, UnidentifiedAtom}; use lv2_core::port::PortType; @@ -53,7 +54,7 @@ impl<'a> PortReader<'a> { pub fn read( &self, urid: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomReadError> { self.atom.read(urid) } } @@ -85,7 +86,7 @@ impl<'a> PortWriter<'a> { pub fn init<'b, 'write, A: crate::Atom>( &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, - ) -> Result<>::Handle, AtomError> { + ) -> Result<>::Handle, AtomWriteError> { if !self.has_been_written { self.has_been_written = true; // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. @@ -94,7 +95,7 @@ impl<'a> PortWriter<'a> { }; space.init_atom(urid) } else { - Err(AtomError::AtomAlreadyWritten) + Err(AtomWriteError::AtomAlreadyWritten) } } } diff --git a/atom/src/space.rs b/atom/src/space.rs index 0d760a6c..aeb30716 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -1,18 +1,18 @@ -//! Smart pointers with safe atom reading and writing methods. +//! A collection of tools to assist reading and writing custom Atom types in Atom byte buffers (referred as **Spaces**). +mod aligned; mod allocatable; mod atom_writer; mod cursor; -mod error; -pub mod reader; -mod space; +pub mod error; +mod reader; mod terminated; mod vec; +pub use aligned::{AlignedSpace, AtomSpace}; pub use allocatable::*; -pub use atom_writer::{AtomSpaceWriter, AtomSpaceWriterHandle}; +pub use atom_writer::AtomSpaceWriter; pub use cursor::SpaceCursor; -pub use error::AtomError; -pub use space::{AlignedSpace, AtomSpace}; +pub use reader::SpaceReader; pub use terminated::Terminated; pub use vec::{VecSpace, VecSpaceCursor}; diff --git a/atom/src/space/space.rs b/atom/src/space/aligned.rs similarity index 95% rename from atom/src/space/space.rs rename to atom/src/space/aligned.rs index 5c5023b4..cfbce04b 100644 --- a/atom/src/space/space.rs +++ b/atom/src/space/aligned.rs @@ -1,6 +1,7 @@ use crate::header::AtomHeader; -use crate::space::reader::SpaceReader; -use crate::space::{AtomError, SpaceCursor}; +use crate::space::error::{AtomReadError, AtomWriteError}; +use crate::space::SpaceCursor; +use crate::space::SpaceReader; use core::mem::{align_of, size_of}; use std::marker::PhantomData; use std::mem::{size_of_val, MaybeUninit}; @@ -115,7 +116,7 @@ impl AlignedSpace { /// # Example /// /// ``` - /// # use lv2_atom::space::{AlignedSpace, AtomError}; + /// # use lv2_atom::space::{AlignedSpace, error::AtomReadError}; /// let values = &[42u64, 69]; /// // Transmuting to a slice of bytes /// let bytes: &[u8] = unsafe { values.align_to().1 }; @@ -125,23 +126,23 @@ impl AlignedSpace { /// // The slice now only has space for a single value /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[9..]).err().unwrap(), AtomError::ReadingOutOfBounds { capacity: 7, requested: 8 }); + /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[9..]).err().unwrap(), AtomReadError::ReadingOutOfBounds { available: 7, requested: 8 }); /// ``` #[inline] - pub fn try_align_from_bytes(data: &[u8]) -> Result<&Self, AtomError> { + pub fn try_align_from_bytes(data: &[u8]) -> Result<&Self, AtomReadError> { let padding = crate::util::try_padding_for::(data)?; let data_len = data.len(); let data = data .get(padding..) - .ok_or_else(|| AtomError::ReadingOutOfBounds { - capacity: data_len, + .ok_or_else(|| AtomReadError::ReadingOutOfBounds { + available: data_len, requested: padding + 1, })?; if data.is_empty() { - return Err(AtomError::ReadingOutOfBounds { - capacity: data_len, + return Err(AtomReadError::ReadingOutOfBounds { + available: data_len, requested: padding + 1, }); } @@ -160,7 +161,7 @@ impl AlignedSpace { /// # Example /// /// ``` - /// # use lv2_atom::space::{AlignedSpace, AtomError}; + /// # use lv2_atom::space::{AlignedSpace, error::AtomWriteError}; /// let values = &mut [42u64, 69]; /// // Transmuting to a slice of bytes /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; @@ -170,23 +171,23 @@ impl AlignedSpace { /// // The slice now only has space for a single value /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[9..]).err().unwrap(), AtomError::ReadingOutOfBounds { capacity: 7, requested: 8 }); + /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[9..]).err().unwrap(), AtomWriteError::WritingOutOfBounds { available: 7, requested: 8 }); /// ``` #[inline] - pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AtomError> { + pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AtomWriteError> { let padding = crate::util::try_padding_for::(data)?; let data_len = data.len(); let data = data .get_mut(padding..) - .ok_or_else(|| AtomError::ReadingOutOfBounds { - capacity: data_len, + .ok_or(AtomWriteError::WritingOutOfBounds { + available: data_len, requested: padding + 1, })?; if data.is_empty() { - return Err(AtomError::ReadingOutOfBounds { - capacity: data_len, + return Err(AtomWriteError::WritingOutOfBounds { + available: data_len, requested: padding + 1, }); } @@ -299,11 +300,11 @@ impl AlignedSpace { /// A checked version of slice::split_at, which returns the first part as an already-aligned slice. #[inline] - fn split_bytes_at(&self, mid: usize) -> Result<(&Self, &[u8]), AtomError> { + fn split_bytes_at(&self, mid: usize) -> Result<(&Self, &[u8]), AtomReadError> { if mid > self.data.len() { - return Err(AtomError::ReadingOutOfBounds { + return Err(AtomReadError::ReadingOutOfBounds { requested: mid, - capacity: self.data.len(), + available: self.data.len(), }); } @@ -318,7 +319,7 @@ impl AlignedSpace { /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. #[inline] - pub fn try_split_at(&self, mid: usize) -> Result<(&Self, &Self), AtomError> { + pub fn try_split_at(&self, mid: usize) -> Result<(&Self, &Self), AtomReadError> { let (start, end) = self.split_bytes_at(mid)?; let end = Self::try_align_from_bytes(end).unwrap_or_else(|_| AlignedSpace::empty()); diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 9a18690f..26857f2d 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -2,7 +2,7 @@ use crate::space::{AlignedSpace, AtomSpaceWriter}; use crate::{Atom, AtomHandle, UnidentifiedAtom}; use urid::URID; -use crate::space::error::AtomError; +use crate::space::error::AtomWriteError; use crate::space::terminated::Terminated; use core::mem::{size_of, size_of_val, MaybeUninit}; @@ -12,9 +12,10 @@ use core::mem::{size_of, size_of_val, MaybeUninit}; /// // TODO: Find proper name pub trait SpaceAllocatorImpl { - fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError>; + fn allocate_and_split(&mut self, size: usize) + -> Result<(&mut [u8], &mut [u8]), AtomWriteError>; - unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError>; + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError>; fn allocated_bytes(&self) -> &[u8]; fn allocated_bytes_mut(&mut self) -> &mut [u8]; @@ -29,12 +30,12 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. #[inline] - fn allocate(&mut self, size: usize) -> Result<&mut [u8], AtomError> { + fn allocate(&mut self, size: usize) -> Result<&mut [u8], AtomWriteError> { self.allocate_and_split(size).map(|(_, s)| s) } #[inline] - fn allocate_padding_for(&mut self) -> Result<(), AtomError> { + fn allocate_padding_for(&mut self) -> Result<(), AtomWriteError> { let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; self.allocate(required_padding)?; @@ -45,7 +46,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { fn allocate_aligned( &mut self, size: usize, - ) -> Result<&mut AlignedSpace, AtomError> { + ) -> Result<&mut AlignedSpace, AtomWriteError> { let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; let raw = self.allocate(size + required_padding)?; @@ -53,7 +54,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { } #[inline] - fn allocate_value(&mut self) -> Result<&mut MaybeUninit, AtomError> { + fn allocate_value(&mut self) -> Result<&mut MaybeUninit, AtomWriteError> { let space = self.allocate_aligned(size_of::>())?; // SAFETY: We used size_of, so we are sure that the allocated space is exactly big enough for T. Ok(unsafe { space.as_uninit_mut_unchecked() }) @@ -63,7 +64,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { fn allocate_values( &mut self, count: usize, - ) -> Result<&mut [MaybeUninit], AtomError> { + ) -> Result<&mut [MaybeUninit], AtomWriteError> { let space = self.allocate_aligned(count * std::mem::size_of::())?; Ok(space.as_uninit_slice_mut()) } @@ -72,7 +73,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { fn init_atom( &mut self, atom_type: URID, - ) -> Result<::Handle, AtomError> { + ) -> Result<::Handle, AtomWriteError> { let space = AtomSpaceWriter::write_new(self, atom_type)?; A::init(space) } @@ -81,7 +82,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { fn forward_atom( &mut self, atom: &UnidentifiedAtom, - ) -> Result<&mut UnidentifiedAtom, AtomError> { + ) -> Result<&mut UnidentifiedAtom, AtomWriteError> { let resulting_space = self.allocate_aligned(atom.atom_space().len())?; resulting_space .as_bytes_mut() @@ -92,14 +93,14 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { } #[inline] - fn write_bytes(&mut self, bytes: &[u8]) -> Result<&mut [u8], AtomError> { + fn write_bytes(&mut self, bytes: &[u8]) -> Result<&mut [u8], AtomWriteError> { let space = self.allocate(bytes.len())?; space.copy_from_slice(bytes); Ok(space) } #[inline] - fn write_value(&mut self, value: T) -> Result<&mut T, AtomError> + fn write_value(&mut self, value: T) -> Result<&mut T, AtomWriteError> where T: Copy + Sized + 'static, { @@ -110,7 +111,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { Ok(crate::util::write_uninit(space, value)) } - fn write_values(&mut self, values: &[T]) -> Result<&mut [T], AtomError> + fn write_values(&mut self, values: &[T]) -> Result<&mut [T], AtomWriteError> where T: Copy + Sized + 'static, { diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 48f75dcb..9411288d 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,14 +1,7 @@ use crate::header::AtomHeader; -use crate::space::{AlignedSpace, AtomError, SpaceAllocator, SpaceAllocatorImpl}; -use crate::AtomHandle; +use crate::space::{error::AtomWriteError, AlignedSpace, SpaceAllocator, SpaceAllocatorImpl}; use urid::URID; -pub struct AtomSpaceWriterHandle; - -impl<'a> AtomHandle<'a> for AtomSpaceWriterHandle { - type Handle = AtomSpaceWriter<'a>; -} - /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'a> { atom_header_index: usize, @@ -50,10 +43,10 @@ impl<'a> AtomSpaceWriter<'a> { unsafe { space.assume_init_value_mut().unwrap() } } - pub fn allocate_and_unwrap Result>( + pub fn allocate_and_unwrap Result>( mut self, operation: F, - ) -> Result { + ) -> Result { operation(&mut self) } @@ -61,7 +54,7 @@ impl<'a> AtomSpaceWriter<'a> { pub fn write_new( parent: &'a mut impl SpaceAllocator, urid: URID, - ) -> Result { + ) -> Result { let atom = AtomHeader::new(urid); parent.write_value(atom)?; @@ -76,17 +69,20 @@ impl<'a> AtomSpaceWriter<'a> { impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { #[inline] - fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + fn allocate_and_split( + &mut self, + size: usize, + ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { let (previous, current) = self.parent.allocate_and_split(size)?; let space = AlignedSpace::::try_from_bytes_mut( previous .get_mut(self.atom_header_index..) - .ok_or(AtomError::CannotUpdateAtomHeader)?, + .ok_or(AtomWriteError::CannotUpdateAtomHeader)?, ) - .ok_or(AtomError::CannotUpdateAtomHeader)?; - let header = - unsafe { space.assume_init_value_mut() }.ok_or(AtomError::CannotUpdateAtomHeader)?; + .ok_or(AtomWriteError::CannotUpdateAtomHeader)?; + let header = unsafe { space.assume_init_value_mut() } + .ok_or(AtomWriteError::CannotUpdateAtomHeader)?; // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated unsafe { header.set_size_of_body(header.size_of_body() + size) }; @@ -95,7 +91,7 @@ impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { } #[inline] - unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError> { self.parent.rewind(byte_count)?; let header = self.atom_header_mut(); diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 99cb2e31..168d660b 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,4 +1,5 @@ -use crate::space::{AtomError, SpaceAllocatorImpl}; +use crate::space::error::AtomWriteError; +use crate::space::SpaceAllocatorImpl; pub struct SpaceCursor<'a> { data: &'a mut [u8], @@ -16,14 +17,17 @@ impl<'a> SpaceCursor<'a> { impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { #[inline] - fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + fn allocate_and_split( + &mut self, + size: usize, + ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { let allocated_length = self.allocated_length; let data_len = self.data.len(); let (allocated, allocatable) = self.data.split_at_mut(allocated_length); let new_allocation = allocatable .get_mut(..size) - .ok_or_else(|| AtomError::OutOfSpace { + .ok_or(AtomWriteError::OutOfSpace { used: allocated_length, capacity: data_len, requested: size, @@ -32,17 +36,17 @@ impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { self.allocated_length = self .allocated_length .checked_add(size) - .ok_or(AtomError::AllocatorOverflow)?; + .ok_or(AtomWriteError::AllocatorOverflow)?; Ok((allocated, new_allocation)) } #[inline] - unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError> { if self.allocated_length < byte_count { - return Err(AtomError::RewindError { + return Err(AtomWriteError::RewindError { requested: byte_count, - capacity: self.allocated_length, + available: self.allocated_length, }); } diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs index 0e7a68db..cabfad3a 100644 --- a/atom/src/space/error.rs +++ b/atom/src/space/error.rs @@ -1,27 +1,59 @@ +use std::any::TypeId; use std::error::Error; use std::fmt::{Display, Formatter}; use urid::{Uri, URID}; #[derive(Debug, Clone, Eq, PartialEq)] -pub enum AtomError { +#[non_exhaustive] +pub enum AlignmentError { + CannotComputeAlignment { type_id: TypeId, ptr: *const u8 }, +} + +impl From for AtomWriteError { + #[inline] + fn from(error: AlignmentError) -> Self { + AtomWriteError::AlignmentError(error) + } +} + +impl From for AtomReadError { + #[inline] + fn from(error: AlignmentError) -> Self { + AtomReadError::AlignmentError(error) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum AtomWriteError { OutOfSpace { used: usize, capacity: usize, requested: usize, }, - CannotComputeAlignment { - ptr: *const u8, - }, AllocatorOverflow, ResizeFailed, CannotUpdateAtomHeader, AtomAlreadyWritten, RewindError { + available: usize, requested: usize, - capacity: usize, }, + WritingOutOfBounds { + available: usize, + requested: usize, + }, + WritingIllegalState { + writing_type_uri: &'static Uri, + }, + AlignmentError(AlignmentError), - // Reading + Unknown, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum AtomReadError { InvalidAtomUrid { expected_uri: &'static Uri, expected_urid: URID, @@ -33,16 +65,23 @@ pub enum AtomError { found_urid: u32, }, ReadingOutOfBounds { - capacity: usize, + available: usize, requested: usize, }, InvalidAtomValue { reading_type_uri: &'static Uri, }, + AlignmentError(AlignmentError), Unknown, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum AtomError { + ReadError(AtomReadError), + WriteError(AtomWriteError), +} + impl Display for AtomError { fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { todo!() @@ -50,3 +89,17 @@ impl Display for AtomError { } impl Error for AtomError {} + +impl From for AtomError { + #[inline] + fn from(error: AtomReadError) -> Self { + AtomError::ReadError(error) + } +} + +impl From for AtomError { + #[inline] + fn from(error: AtomWriteError) -> Self { + AtomError::WriteError(error) + } +} diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index bc7bad29..a261b6a3 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -1,5 +1,5 @@ use crate::prelude::AlignedSpace; -use crate::space::AtomError; +use crate::space::error::AtomReadError; use crate::{AtomHeader, UnidentifiedAtom}; use std::mem::MaybeUninit; @@ -14,7 +14,7 @@ impl<'a> SpaceReader<'a> { } #[inline] - fn next_uninit_value(&mut self) -> Result<&'a MaybeUninit, AtomError> { + fn next_uninit_value(&mut self) -> Result<&'a MaybeUninit, AtomReadError> { let space = AlignedSpace::try_align_from_bytes(self.space)?; let value_size = ::core::mem::size_of::(); let (value, remaining) = space.try_split_at(value_size)?; @@ -22,14 +22,14 @@ impl<'a> SpaceReader<'a> { self.space = remaining.as_bytes(); // This shouldn't be possible, but it doesn't hurt to check - value.as_uninit().ok_or(AtomError::Unknown) + value.as_uninit().ok_or(AtomReadError::Unknown) } #[inline] fn next_uninit_value_slice( &mut self, length: usize, - ) -> Result<&'a [MaybeUninit], AtomError> { + ) -> Result<&'a [MaybeUninit], AtomReadError> { let space = AlignedSpace::try_align_from_bytes(self.space)?; let split_point = crate::util::value_index_to_byte_index::(length); @@ -41,31 +41,34 @@ impl<'a> SpaceReader<'a> { } #[inline] - fn as_uninit_slice(&self) -> Result<&'a [MaybeUninit], AtomError> { + fn as_uninit_slice(&self) -> Result<&'a [MaybeUninit], AtomReadError> { let space = AlignedSpace::try_align_from_bytes(self.space)?; Ok(space.as_uninit_slice()) } #[inline] - pub unsafe fn as_slice(&self) -> Result<&'a [T], AtomError> { + pub unsafe fn as_slice(&self) -> Result<&'a [T], AtomReadError> { self.as_uninit_slice() .map(|s| crate::util::assume_init_slice(s)) } #[inline] - pub unsafe fn next_slice(&mut self, length: usize) -> Result<&'a [U], AtomError> { + pub unsafe fn next_slice( + &mut self, + length: usize, + ) -> Result<&'a [U], AtomReadError> { self.next_uninit_value_slice(length) .map(|s| crate::util::assume_init_slice(s)) } #[inline] - pub fn next_bytes(&mut self, length: usize) -> Result<&'a [u8], AtomError> { + pub fn next_bytes(&mut self, length: usize) -> Result<&'a [u8], AtomReadError> { let bytes = self .space .get(..length) - .ok_or_else(|| AtomError::ReadingOutOfBounds { + .ok_or_else(|| AtomReadError::ReadingOutOfBounds { requested: length, - capacity: self.space.len(), + available: self.space.len(), })?; self.space = self.space.get(length..).unwrap_or(&[]); @@ -74,19 +77,19 @@ impl<'a> SpaceReader<'a> { } #[inline] - pub unsafe fn next_value(&mut self) -> Result<&'a U, AtomError> { + pub unsafe fn next_value(&mut self) -> Result<&'a U, AtomReadError> { self.next_uninit_value() .map(|v| crate::util::assume_init_ref(v)) } #[inline] - pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomError> { + pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomReadError> { let space = AlignedSpace::::try_align_from_bytes(&self.space)?; let header = space .assume_init_value() - .ok_or_else(|| AtomError::ReadingOutOfBounds { - capacity: space.len(), - requested: core::mem::size_of::(), + .ok_or(AtomReadError::ReadingOutOfBounds { + available: space.len(), + requested: core::mem::size_of::(), })?; let (_, rest) = space.try_split_at(header.size_of_atom())?; @@ -102,9 +105,9 @@ impl<'a> SpaceReader<'a> { } #[inline] - pub fn try_read(&mut self, read_handler: F) -> Result + pub fn try_read(&mut self, read_handler: F) -> Result where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> Result, { let mut reader = Self { space: self.space }; let value = read_handler(&mut reader)?; diff --git a/atom/src/space/terminated.rs b/atom/src/space/terminated.rs index 92813527..989b3fff 100644 --- a/atom/src/space/terminated.rs +++ b/atom/src/space/terminated.rs @@ -1,4 +1,5 @@ -use crate::space::{AtomError, SpaceAllocatorImpl}; +use crate::space::error::AtomWriteError; +use crate::space::SpaceAllocatorImpl; pub struct Terminated { inner: W, @@ -17,7 +18,10 @@ impl Terminated { } impl SpaceAllocatorImpl for Terminated { - fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + fn allocate_and_split( + &mut self, + size: usize, + ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { if self.wrote_terminator_byte { // SAFETY: We checked we already wrote the terminator byte, and it is safe to be overwritten unsafe { self.inner.rewind(1)? }; @@ -31,7 +35,7 @@ impl SpaceAllocatorImpl for Terminated { } #[inline] - unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError> { self.inner.rewind(byte_count) } diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index c583b8da..0feef7a6 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,6 +1,7 @@ #![deny(unsafe_code)] -use crate::space::{AlignedSpace, AtomError, SpaceAllocatorImpl}; +use crate::space::error::AtomWriteError; +use crate::space::{AlignedSpace, SpaceAllocatorImpl}; use std::mem::MaybeUninit; use std::ops::Range; @@ -45,7 +46,7 @@ impl VecSpace { fn reallocate_bytes_mut( &mut self, byte_range: Range, - ) -> Result<(&mut [u8], &mut [u8]), AtomError> { + ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { let byte_len = self.inner.len() * std::mem::size_of::(); let max = byte_range.start.max(byte_range.end); @@ -57,14 +58,14 @@ impl VecSpace { let bytes = self.as_bytes_mut(); bytes .get(byte_range.clone()) - .ok_or(AtomError::ResizeFailed)?; // To make sure everything is in range instead of panicking on split_at_mut + .ok_or(AtomWriteError::ResizeFailed)?; // To make sure everything is in range instead of panicking on split_at_mut let (previous, allocatable) = bytes.split_at_mut(byte_range.start); return Ok(( previous, allocatable .get_mut(..byte_range.end - byte_range.start) - .ok_or(AtomError::ResizeFailed)?, + .ok_or(AtomWriteError::ResizeFailed)?, )); } @@ -83,11 +84,14 @@ pub struct VecSpaceCursor<'vec, T> { } impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { - fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomError> { + fn allocate_and_split( + &mut self, + size: usize, + ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { let end = self .allocated_length .checked_add(size) - .ok_or(AtomError::AllocatorOverflow)?; + .ok_or(AtomWriteError::AllocatorOverflow)?; let result = VecSpace::::reallocate_bytes_mut(self.vec, self.allocated_length..end); @@ -100,11 +104,11 @@ impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { #[inline] #[allow(unsafe_code)] - unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomError> { + unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError> { if self.allocated_length < byte_count { - return Err(AtomError::RewindError { + return Err(AtomWriteError::RewindError { requested: byte_count, - capacity: self.allocated_length, + available: self.allocated_length, }); } diff --git a/atom/src/util.rs b/atom/src/util.rs index 903ee5a8..ee5a562f 100644 --- a/atom/src/util.rs +++ b/atom/src/util.rs @@ -1,4 +1,5 @@ -use crate::space::AtomError; +use crate::space::error::AlignmentError; +use std::any::TypeId; use std::mem::MaybeUninit; // This function is separate to ensure proper lifetimes @@ -43,16 +44,14 @@ pub(crate) fn byte_index_to_value_index(size: usize) -> usize { } #[inline] -pub(crate) fn padding_for(data: &[u8]) -> Option { +pub(crate) fn try_padding_for(data: &[u8]) -> Result { let value = data.as_ptr().align_offset(::core::mem::align_of::()); if value == usize::MAX { - None + Err(AlignmentError::CannotComputeAlignment { + type_id: TypeId::of::(), + ptr: data.as_ptr(), + }) } else { - Some(value) + Ok(value) } } - -#[inline] -pub(crate) fn try_padding_for(data: &[u8]) -> Result { - padding_for::(data).ok_or_else(|| AtomError::CannotComputeAlignment { ptr: data.as_ptr() }) -} diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 8d44628e..0c155d13 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -4,7 +4,7 @@ //! //! If you just want to use MIDI messages in your plugin, you should use the optional `wmidi` feature. use atom::prelude::*; -use atom::space::AtomError; +use atom::space::error::*; use atom::AtomHandle; use urid::*; @@ -33,11 +33,11 @@ impl Atom for MidiEvent { type ReadHandle = MidiEventReadHandle; type WriteHandle = MidiEventWriteHandle; - unsafe fn read(body: &AtomSpace) -> Result<&[u8], AtomError> { + unsafe fn read(body: &AtomSpace) -> Result<&[u8], AtomReadError> { Ok(body.as_bytes()) } - fn init(frame: AtomSpaceWriter) -> Result { + fn init(frame: AtomSpaceWriter) -> Result { Ok(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index cab7d281..2e68bcd5 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -4,7 +4,7 @@ //! //! If you want to have raw, low-level access to the messages, you should use the [raw module](../raw/index.html). use atom::prelude::*; -use atom::space::{AtomError, Terminated}; +use atom::space::{error::*, Terminated}; use atom::AtomHandle; use std::convert::TryFrom; use urid::*; @@ -40,13 +40,13 @@ pub struct WMidiEventWriter<'a> { impl<'a> WMidiEventWriter<'a> { #[inline] - pub fn set(mut self, message: wmidi::MidiMessage) -> Result<(), AtomError> { + pub fn set(mut self, message: wmidi::MidiMessage) -> Result<(), AtomWriteError> { let space = self.writer.allocate(message.bytes_size())?; // The error shouldn't be happening, as we allocated just as many bytes as needed message .copy_to_slice(space) - .map_err(|_| AtomError::Unknown)?; + .map_err(|_| AtomWriteError::Unknown)?; Ok(()) } } @@ -56,14 +56,16 @@ impl Atom for WMidiEvent { type WriteHandle = WMidiEventWriteHandle; #[inline] - unsafe fn read(space: &AtomSpace) -> Result { - wmidi::MidiMessage::try_from(space.as_bytes()).map_err(|_| AtomError::InvalidAtomValue { - reading_type_uri: Self::uri(), + unsafe fn read(space: &AtomSpace) -> Result { + wmidi::MidiMessage::try_from(space.as_bytes()).map_err(|_| { + AtomReadError::InvalidAtomValue { + reading_type_uri: Self::uri(), + } }) } #[inline] - fn init(writer: AtomSpaceWriter) -> Result { + fn init(writer: AtomSpaceWriter) -> Result { Ok(WMidiEventWriter { writer }) } } @@ -90,11 +92,11 @@ impl Atom for SystemExclusiveWMidiEvent { type WriteHandle = SystemExclusiveWMidiEventWriteHandle; #[inline] - unsafe fn read(space: &AtomSpace) -> Result { + unsafe fn read(space: &AtomSpace) -> Result { WMidiEvent::read(space) } - fn init(mut frame: AtomSpaceWriter) -> Result { + fn init(mut frame: AtomSpaceWriter) -> Result { frame.write_value(0xf0u8)?; Ok(Writer { @@ -114,12 +116,12 @@ pub struct Writer<'a> { impl<'a> Writer<'a> { #[inline] - pub fn write_raw(&mut self, data: &[u8]) -> Result<&mut [u8], AtomError> { + pub fn write_raw(&mut self, data: &[u8]) -> Result<&mut [u8], AtomWriteError> { self.frame.write_bytes(data) } #[inline] - pub fn write(&mut self, instance: T) -> Result<&mut T, AtomError> + pub fn write(&mut self, instance: T) -> Result<&mut T, AtomWriteError> where T: Copy + Sized + 'static, { @@ -157,7 +159,7 @@ mod tests { // verifying { - let atom = unsafe { UnidentifiedAtom::from_space(&raw_space) }.unwrap(); + let atom = unsafe { UnidentifiedAtom::from_space(raw_space) }.unwrap(); assert_eq!(atom.header().urid(), urid); assert_eq!(atom.header().size_of_body(), 3); @@ -168,7 +170,7 @@ mod tests { // reading { - let space = unsafe { UnidentifiedAtom::from_space(&raw_space) } + let space = unsafe { UnidentifiedAtom::from_space(raw_space) } .unwrap() .body(); From d9abd29db49529a41ba6d2fcaa487d14537ae33b Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 23 Sep 2021 08:45:07 +0200 Subject: [PATCH 36/54] Properly split alignment errors --- atom/src/space/aligned.rs | 235 ++++++++++++++++++++++------------ atom/src/space/allocatable.rs | 10 +- atom/src/space/atom_writer.rs | 16 +-- atom/src/space/error.rs | 48 ++++++- atom/src/space/reader.rs | 49 +++++-- atom/src/util.rs | 13 +- state/src/raw.rs | 4 +- 7 files changed, 262 insertions(+), 113 deletions(-) diff --git a/atom/src/space/aligned.rs b/atom/src/space/aligned.rs index cfbce04b..e78e2a94 100644 --- a/atom/src/space/aligned.rs +++ b/atom/src/space/aligned.rs @@ -1,8 +1,9 @@ use crate::header::AtomHeader; -use crate::space::error::{AtomReadError, AtomWriteError}; +use crate::space::error::{AlignmentError, AlignmentErrorInner, TypeData}; use crate::space::SpaceCursor; use crate::space::SpaceReader; use core::mem::{align_of, size_of}; +use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::mem::{size_of_val, MaybeUninit}; use std::slice::{from_raw_parts, from_raw_parts_mut}; @@ -35,12 +36,13 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// // --- /// /// // Bytes are already aligned, the whole slice will be available -/// let space: &AlignedSpace = AlignedSpace::try_align_from_bytes(bytes).unwrap(); +/// let space: &AlignedSpace = AlignedSpace::align_from_bytes(bytes).unwrap(); /// // SAFETY: we know the slice was initialized with proper u64 values. /// let read_values = unsafe { space.assume_init_slice() }; /// assert_eq!(read_values, [42u64, 69]); /// ``` /// +#[derive(Eq, PartialEq)] #[repr(transparent)] pub struct AlignedSpace { _type: PhantomData, @@ -65,17 +67,20 @@ impl AlignedSpace { /// // Transmuting to a slice of bytes /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// - /// assert!(AlignedSpace::::try_from_bytes(bytes).is_some()); - /// assert!(AlignedSpace::::try_from_bytes(&bytes[1..]).is_none()); + /// assert!(AlignedSpace::::from_bytes(bytes).is_ok()); + /// assert!(AlignedSpace::::from_bytes(&bytes[1..]).is_err()); /// ``` #[inline] - pub fn try_from_bytes(data: &[u8]) -> Option<&Self> { + pub fn from_bytes(data: &[u8]) -> Result<&Self, AlignmentError> { if data.as_ptr() as usize % align_of::() != 0 { - return None; + return Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { + type_id: TypeData::of::(), + ptr: data.as_ptr(), + })); } // SAFETY: We just checked above that the pointer is correctly aligned - Some(unsafe { AlignedSpace::from_bytes_unchecked(data) }) + Ok(unsafe { AlignedSpace::from_bytes_unchecked(data) }) } /// Creates a new mutable space from a mutable slice of bytes. @@ -93,17 +98,20 @@ impl AlignedSpace { /// // Transmuting to a slice of bytes /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; /// - /// assert!(AlignedSpace::::try_from_bytes_mut(bytes).is_some()); - /// assert!(AlignedSpace::::try_from_bytes_mut(&mut bytes[1..]).is_none()); + /// assert!(AlignedSpace::::from_bytes_mut(bytes).is_ok()); + /// assert!(AlignedSpace::::from_bytes_mut(&mut bytes[1..]).is_err()); /// ``` #[inline] - pub fn try_from_bytes_mut(data: &mut [u8]) -> Option<&mut Self> { + pub fn from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AlignmentError> { if data.as_ptr() as usize % align_of::() != 0 { - return None; + return Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { + type_id: TypeData::of::(), + ptr: data.as_ptr(), + })); } // SAFETY: We just checked above that the pointer is correctly aligned - Some(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) + Ok(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) } /// Creates a new space from a slice of bytes, slicing some bytes off its start it if necessary. @@ -122,30 +130,27 @@ impl AlignedSpace { /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// /// // The slice has space for both values - /// assert_eq!(AlignedSpace::::try_align_from_bytes(bytes).unwrap().values_len(), 2); + /// assert_eq!(AlignedSpace::::align_from_bytes(bytes).unwrap().values_len(), 2); /// // The slice now only has space for a single value - /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); + /// assert_eq!(AlignedSpace::::align_from_bytes(&bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert_eq!(AlignedSpace::::try_align_from_bytes(&bytes[9..]).err().unwrap(), AtomReadError::ReadingOutOfBounds { available: 7, requested: 8 }); + /// assert_eq!(AlignedSpace::::align_from_bytes(&bytes[9..]).unwrap().values_len(), 0); + /// // The slice cannot be aligned + /// assert!(AlignedSpace::::align_from_bytes(&bytes[10..11]).is_err()); /// ``` #[inline] - pub fn try_align_from_bytes(data: &[u8]) -> Result<&Self, AtomReadError> { + pub fn align_from_bytes(data: &[u8]) -> Result<&Self, AlignmentError> { let padding = crate::util::try_padding_for::(data)?; let data_len = data.len(); - let data = data - .get(padding..) - .ok_or_else(|| AtomReadError::ReadingOutOfBounds { - available: data_len, - requested: padding + 1, - })?; - - if data.is_empty() { - return Err(AtomReadError::ReadingOutOfBounds { - available: data_len, - requested: padding + 1, - }); - } + let data = data.get(padding..).ok_or_else(|| { + AlignmentError(AlignmentErrorInner::NotEnoughSpaceToRealign { + ptr: data.as_ptr(), + available_size: data_len, + required_padding: padding + 1, + type_id: TypeData::of::(), + }) + })?; // SAFETY: We just aligned the slice start Ok(unsafe { AlignedSpace::from_bytes_unchecked(data) }) @@ -167,30 +172,28 @@ impl AlignedSpace { /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; /// /// // The slice has space for both values - /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(bytes).unwrap().values_len(), 2); + /// assert_eq!(AlignedSpace::::align_from_bytes_mut(bytes).unwrap().values_len(), 2); /// // The slice now only has space for a single value - /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); + /// assert_eq!(AlignedSpace::::align_from_bytes_mut(&mut bytes[1..]).unwrap().values_len(), 1); /// // The slice doesn't have space for any value anymore - /// assert_eq!(AlignedSpace::::try_align_from_bytes_mut(&mut bytes[9..]).err().unwrap(), AtomWriteError::WritingOutOfBounds { available: 7, requested: 8 }); + /// assert_eq!(AlignedSpace::::align_from_bytes_mut(&mut bytes[9..]).unwrap().values_len(), 0); + /// // The slice cannot be aligned + /// assert!(AlignedSpace::::align_from_bytes_mut(&mut bytes[10..11]).is_err()); /// ``` #[inline] - pub fn try_align_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AtomWriteError> { + pub fn align_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AlignmentError> { let padding = crate::util::try_padding_for::(data)?; let data_len = data.len(); + let data_ptr = data.as_ptr(); - let data = data - .get_mut(padding..) - .ok_or(AtomWriteError::WritingOutOfBounds { - available: data_len, - requested: padding + 1, - })?; - - if data.is_empty() { - return Err(AtomWriteError::WritingOutOfBounds { - available: data_len, - requested: padding + 1, - }); - } + let data = data.get_mut(padding..).ok_or_else(|| { + AlignmentError(AlignmentErrorInner::NotEnoughSpaceToRealign { + ptr: data_ptr, + available_size: data_len, + required_padding: padding + 1, + type_id: TypeData::of::(), + }) + })?; // SAFETY: We just aligned the slice start Ok(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) @@ -233,7 +236,7 @@ impl AlignedSpace { /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. /// Calling this method with an unaligned slice will result from UB. /// - /// For a safe, checked version, see [`Space::try_from_bytes`]. + /// For a safe, checked version, see [`Space::from_bytes`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] @@ -250,7 +253,7 @@ impl AlignedSpace { /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. /// Otherwise, reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. /// - /// For a safe, checked version, see [`Space::try_from_bytes_mut`]. + /// For a safe, checked version, see [`Space::from_bytes_mut`]. // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] @@ -300,12 +303,16 @@ impl AlignedSpace { /// A checked version of slice::split_at, which returns the first part as an already-aligned slice. #[inline] - fn split_bytes_at(&self, mid: usize) -> Result<(&Self, &[u8]), AtomReadError> { + fn split_bytes_at(&self, mid: usize) -> Result<(&Self, &[u8]), AlignmentError> { if mid > self.data.len() { - return Err(AtomReadError::ReadingOutOfBounds { - requested: mid, - available: self.data.len(), - }); + return Err(AlignmentError( + AlignmentErrorInner::NotEnoughSpaceToRealign { + ptr: self.data.as_ptr(), + available_size: self.data.len(), + required_padding: mid + 1, + type_id: TypeData::of::(), + }, + )); } let (start, end) = self.data.split_at(mid); @@ -319,9 +326,9 @@ impl AlignedSpace { /// /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. #[inline] - pub fn try_split_at(&self, mid: usize) -> Result<(&Self, &Self), AtomReadError> { + pub fn split_at(&self, mid: usize) -> Result<(&Self, &Self), AlignmentError> { let (start, end) = self.split_bytes_at(mid)?; - let end = Self::try_align_from_bytes(end).unwrap_or_else(|_| AlignedSpace::empty()); + let end = Self::align_from_bytes(end).unwrap_or_else(|_| AlignedSpace::empty()); Ok((start, end)) } @@ -454,26 +461,32 @@ impl AlignedSpace { } } +impl Debug for AlignedSpace { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.data, f) + } +} + #[cfg(test)] mod tests { + use crate::space::error::{AlignmentError, AlignmentErrorInner, TypeData}; use crate::space::*; use crate::AtomHeader; use std::mem::{size_of, size_of_val}; use urid::*; #[test] - fn align_from_bytes() { + fn from_bytes() { let values = &mut [42u64, 69]; let bytes = unsafe { values.align_to_mut().1 }; assert_eq!( - AlignedSpace::::try_from_bytes(bytes).unwrap().len(), + AlignedSpace::::from_bytes(bytes).unwrap().len(), ::core::mem::size_of::() * 2 ); assert_eq!( - AlignedSpace::::try_from_bytes_mut(bytes) - .unwrap() - .len(), + AlignedSpace::::from_bytes_mut(bytes).unwrap().len(), ::core::mem::size_of::() * 2 ); @@ -487,32 +500,94 @@ mod tests { ::core::mem::size_of::() * 2 ); - assert!(AlignedSpace::::try_from_bytes(&bytes[1..]).is_none()); - assert!(AlignedSpace::::try_from_bytes_mut(&mut bytes[1..]).is_none()); + assert_eq!( + AlignedSpace::::from_bytes(&bytes[1..]), + Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { + type_id: TypeData::of::(), + ptr: bytes[1..].as_ptr() + })) + ); + + let ptr = bytes[1..].as_ptr(); + assert_eq!( + AlignedSpace::::from_bytes_mut(&mut bytes[1..]), + Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { + type_id: TypeData::of::(), + ptr + })) + ); } #[test] - fn test_split_atom() { - let mut space = VecSpace::::new_with_capacity(64); - let space = space.as_space_mut(); - let urid: URID = unsafe { URID::new_unchecked(17) }; + fn align_from_bytes() { + let values = &mut [42u64, 69]; + let bytes = unsafe { values.align_to_mut().1 }; - // Writing an integer atom. - unsafe { - *(space.as_bytes_mut().as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { - atom: sys::LV2_Atom { - size: size_of::() as u32, - type_: urid.get(), - }, - body: 42, - }; + let size = ::core::mem::size_of::(); + assert_eq!( + AlignedSpace::::align_from_bytes(bytes).unwrap().len(), + size * 2 + ); + + assert_eq!( + AlignedSpace::::align_from_bytes_mut(bytes) + .unwrap() + .len(), + size * 2 + ); + + assert_eq!( + AlignedSpace::::align_from_bytes(&bytes[1..]) + .unwrap() + .len(), + size + ); - let atom = space.read().next_atom().unwrap(); - let body = atom.body().as_bytes(); + assert_eq!( + AlignedSpace::::align_from_bytes_mut(&mut bytes[1..]) + .unwrap() + .len(), + size + ); - assert_eq!(size_of::(), size_of_val(body)); - assert_eq!(42, *(body.as_ptr() as *const i32)); - } + assert_eq!( + AlignedSpace::::align_from_bytes(&bytes[9..]) + .unwrap() + .len(), + 0 + ); + + assert_eq!( + AlignedSpace::::align_from_bytes_mut(&mut bytes[9..]) + .unwrap() + .len(), + 0 + ); + + assert_eq!( + AlignedSpace::::align_from_bytes(&bytes[9..11]), + Err(AlignmentError( + AlignmentErrorInner::NotEnoughSpaceToRealign { + type_id: TypeData::of::(), + ptr: bytes[9..11].as_ptr(), + available_size: 2, + required_padding: 8 + } + )) + ); + + let ptr = bytes[9..11].as_ptr(); + assert_eq!( + AlignedSpace::::align_from_bytes_mut(&mut bytes[9..11]), + Err(AlignmentError( + AlignmentErrorInner::NotEnoughSpaceToRealign { + type_id: TypeData::of::(), + ptr, + available_size: 2, + required_padding: 8 + } + )) + ); } fn test_mut_space<'a>(mut space: impl SpaceAllocator) { diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 26857f2d..ca89831f 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -12,6 +12,10 @@ use core::mem::{size_of, size_of_val, MaybeUninit}; /// // TODO: Find proper name pub trait SpaceAllocatorImpl { + /// + /// # Safety + /// + /// While implementations MUST return a fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomWriteError>; @@ -31,7 +35,9 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. #[inline] fn allocate(&mut self, size: usize) -> Result<&mut [u8], AtomWriteError> { - self.allocate_and_split(size).map(|(_, s)| s) + let (_previous, allocated) = self.allocate_and_split(size)?; + assert_eq!(allocated.len(), size); + Ok(allocated) } #[inline] @@ -50,7 +56,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; let raw = self.allocate(size + required_padding)?; - AlignedSpace::try_align_from_bytes_mut(raw) + Ok(AlignedSpace::align_from_bytes_mut(raw)?) } #[inline] diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 9411288d..5217b3c1 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -27,7 +27,7 @@ impl<'a> AtomSpaceWriter<'a> { .allocated_bytes() .get(self.atom_header_index..) .unwrap(); - let space = AlignedSpace::try_from_bytes(previous).unwrap(); + let space = AlignedSpace::from_bytes(previous).unwrap(); unsafe { *space.assume_init_value().unwrap() } } @@ -38,18 +38,11 @@ impl<'a> AtomSpaceWriter<'a> { .allocated_bytes_mut() .get_mut(self.atom_header_index..) .unwrap(); - let space = AlignedSpace::::try_from_bytes_mut(previous).unwrap(); + let space = AlignedSpace::::from_bytes_mut(previous).unwrap(); unsafe { space.assume_init_value_mut().unwrap() } } - pub fn allocate_and_unwrap Result>( - mut self, - operation: F, - ) -> Result { - operation(&mut self) - } - /// Create a new framed space with the given parent and type URID. pub fn write_new( parent: &'a mut impl SpaceAllocator, @@ -75,12 +68,11 @@ impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { let (previous, current) = self.parent.allocate_and_split(size)?; - let space = AlignedSpace::::try_from_bytes_mut( + let space = AlignedSpace::::from_bytes_mut( previous .get_mut(self.atom_header_index..) .ok_or(AtomWriteError::CannotUpdateAtomHeader)?, - ) - .ok_or(AtomWriteError::CannotUpdateAtomHeader)?; + )?; let header = unsafe { space.assume_init_value_mut() } .ok_or(AtomWriteError::CannotUpdateAtomHeader)?; diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs index cabfad3a..01c795b7 100644 --- a/atom/src/space/error.rs +++ b/atom/src/space/error.rs @@ -1,14 +1,56 @@ -use std::any::TypeId; use std::error::Error; use std::fmt::{Display, Formatter}; use urid::{Uri, URID}; +#[derive(Debug, Clone, Eq, PartialEq)] +pub(crate) struct TypeData { + name: &'static str, + size: usize, + align: usize, +} + +impl TypeData { + pub(crate) fn of() -> Self { + Self { + name: core::any::type_name::(), + size: core::mem::size_of::(), + align: core::mem::align_of::(), + } + } +} + +impl Display for TypeData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} (size: {}, align: {})", + self.name, self.size, self.align + ) + } +} + #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] -pub enum AlignmentError { - CannotComputeAlignment { type_id: TypeId, ptr: *const u8 }, +pub(crate) enum AlignmentErrorInner { + CannotComputeAlignment { + type_id: TypeData, + ptr: *const u8, + }, + UnalignedBuffer { + type_id: TypeData, + ptr: *const u8, + }, + NotEnoughSpaceToRealign { + type_id: TypeData, + ptr: *const u8, + required_padding: usize, + available_size: usize, + }, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AlignmentError(pub(crate) AlignmentErrorInner); + impl From for AtomWriteError { #[inline] fn from(error: AlignmentError) -> Self { diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index a261b6a3..be3bdd0b 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -3,6 +3,7 @@ use crate::space::error::AtomReadError; use crate::{AtomHeader, UnidentifiedAtom}; use std::mem::MaybeUninit; +#[derive(Clone)] pub struct SpaceReader<'a> { space: &'a [u8], } @@ -15,13 +16,13 @@ impl<'a> SpaceReader<'a> { #[inline] fn next_uninit_value(&mut self) -> Result<&'a MaybeUninit, AtomReadError> { - let space = AlignedSpace::try_align_from_bytes(self.space)?; + let space = AlignedSpace::align_from_bytes(self.space)?; let value_size = ::core::mem::size_of::(); - let (value, remaining) = space.try_split_at(value_size)?; + let (value, remaining) = space.split_at(value_size)?; self.space = remaining.as_bytes(); - // This shouldn't be possible, but it doesn't hurt to check + // Failure shouldn't be possible, but it doesn't hurt to check value.as_uninit().ok_or(AtomReadError::Unknown) } @@ -30,10 +31,10 @@ impl<'a> SpaceReader<'a> { &mut self, length: usize, ) -> Result<&'a [MaybeUninit], AtomReadError> { - let space = AlignedSpace::try_align_from_bytes(self.space)?; + let space = AlignedSpace::align_from_bytes(self.space)?; let split_point = crate::util::value_index_to_byte_index::(length); - let (data, remaining) = space.try_split_at(split_point)?; + let (data, remaining) = space.split_at(split_point)?; self.space = remaining.as_bytes(); @@ -42,7 +43,7 @@ impl<'a> SpaceReader<'a> { #[inline] fn as_uninit_slice(&self) -> Result<&'a [MaybeUninit], AtomReadError> { - let space = AlignedSpace::try_align_from_bytes(self.space)?; + let space = AlignedSpace::align_from_bytes(self.space)?; Ok(space.as_uninit_slice()) } @@ -84,14 +85,14 @@ impl<'a> SpaceReader<'a> { #[inline] pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomReadError> { - let space = AlignedSpace::::try_align_from_bytes(&self.space)?; + let space = AlignedSpace::::align_from_bytes(&self.space)?; let header = space .assume_init_value() .ok_or(AtomReadError::ReadingOutOfBounds { available: space.len(), requested: core::mem::size_of::(), })?; - let (_, rest) = space.try_split_at(header.size_of_atom())?; + let (_, rest) = space.split_at(header.size_of_atom())?; let atom = UnidentifiedAtom::from_header(header); self.space = rest.as_bytes(); @@ -116,3 +117,35 @@ impl<'a> SpaceReader<'a> { Ok(value) } } + +#[cfg(test)] +mod test { + use super::*; + use crate::space::VecSpace; + use std::mem::{size_of, size_of_val}; + use urid::URID; + + #[test] + fn test_read_atom() { + let mut space = VecSpace::::new_with_capacity(64); + let space = space.as_space_mut(); + let urid: URID = unsafe { URID::new_unchecked(17) }; + + // Writing an integer atom. + unsafe { + *(space.as_bytes_mut().as_mut_ptr() as *mut sys::LV2_Atom_Int) = sys::LV2_Atom_Int { + atom: sys::LV2_Atom { + size: size_of::() as u32, + type_: urid.get(), + }, + body: 42, + }; + + let atom = space.read().next_atom().unwrap(); + let body = atom.body().as_bytes(); + + assert_eq!(size_of::(), size_of_val(body)); + assert_eq!(42, *(body.as_ptr() as *const i32)); + } + } +} diff --git a/atom/src/util.rs b/atom/src/util.rs index ee5a562f..03aef8e8 100644 --- a/atom/src/util.rs +++ b/atom/src/util.rs @@ -1,5 +1,4 @@ -use crate::space::error::AlignmentError; -use std::any::TypeId; +use crate::space::error::{AlignmentError, AlignmentErrorInner, TypeData}; use std::mem::MaybeUninit; // This function is separate to ensure proper lifetimes @@ -47,10 +46,12 @@ pub(crate) fn byte_index_to_value_index(size: usize) -> usize { pub(crate) fn try_padding_for(data: &[u8]) -> Result { let value = data.as_ptr().align_offset(::core::mem::align_of::()); if value == usize::MAX { - Err(AlignmentError::CannotComputeAlignment { - type_id: TypeId::of::(), - ptr: data.as_ptr(), - }) + Err(AlignmentError( + AlignmentErrorInner::CannotComputeAlignment { + type_id: TypeData::of::(), + ptr: data.as_ptr(), + }, + )) } else { Ok(value) } diff --git a/state/src/raw.rs b/state/src/raw.rs index f444ced6..f0bd5923 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -175,7 +175,7 @@ impl<'a> RetrieveHandle<'a> { Ok(StatePropertyReader::new( type_, - AlignedSpace::try_from_bytes(space).ok_or(StateErr::BadData)?, + AlignedSpace::from_bytes(space).map_err(|_| StateErr::BadData)?, )) } } @@ -321,7 +321,7 @@ mod tests { } 3 => { assert_eq!(urids.vector, *type_); - let space = AlignedSpace::try_from_bytes(value.as_slice()).unwrap(); + let space = AlignedSpace::from_bytes(value.as_slice()).unwrap(); let data = unsafe { Vector::read(space) } .unwrap() .of_type(urids.int) From 767519eb0b77e3c9a2fed3e647677dea6cc5be60 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 23 Sep 2021 15:36:10 +0200 Subject: [PATCH 37/54] Some more docs and some fewer errors --- atom/src/atoms/sequence.rs | 4 +++- atom/src/port.rs | 21 ++++++++------------- atom/src/space/allocatable.rs | 5 +++++ atom/src/space/atom_writer.rs | 9 +++++---- atom/src/space/cursor.rs | 6 +++--- atom/src/space/error.rs | 32 ++++++++++++++++++++++---------- atom/src/space/reader.rs | 6 ++++-- atom/src/space/vec.rs | 19 ++++++++----------- midi/src/wmidi_binding.rs | 6 +++--- 9 files changed, 61 insertions(+), 47 deletions(-) diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 3725d8e2..94b6b377 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -202,7 +202,9 @@ pub struct SequenceWriter<'a, U: SequenceUnit> { impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { /// Write out the time stamp and update `last_stamp`. /// - /// This method returns `Ǹone` if: + /// # Errors + /// + /// This method returns an error if either: /// * The last time stamp is younger than the time stamp. /// * Space is insufficient. fn write_time_stamp(&mut self, time_stamp: U::Value) -> Result<(), AtomWriteError> { diff --git a/atom/src/port.rs b/atom/src/port.rs index 3e78c708..11895af0 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -64,7 +64,6 @@ impl<'a> PortReader<'a> { /// If you add an [`AtomPort`](struct.AtomPort.html) to your ports struct, you will receive an instance of this struct to write atoms. pub struct PortWriter<'a> { space: SpaceCursor<'a>, - has_been_written: bool, } impl<'a> PortWriter<'a> { @@ -72,7 +71,6 @@ impl<'a> PortWriter<'a> { fn new(space: &'a mut AtomSpace) -> Self { Self { space: SpaceCursor::new(space.as_bytes_mut()), - has_been_written: false, } } @@ -82,21 +80,18 @@ impl<'a> PortWriter<'a> { /// /// Please note that you can call this method once only, because any atoms written behind the first one will not be identified. /// - /// This method returns `None` if the space of the port isn't big enough or if the method was called multiple times. + /// # Errors + /// + /// This method can return an error if the buffer isn't big enough to initialize the given atom's header. pub fn init<'b, 'write, A: crate::Atom>( &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, ) -> Result<>::Handle, AtomWriteError> { - if !self.has_been_written { - self.has_been_written = true; - // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. - let space: &'write mut SpaceCursor<'write> = unsafe { - ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) - }; - space.init_atom(urid) - } else { - Err(AtomWriteError::AtomAlreadyWritten) - } + // SAFETY: Nope. That's super unsound, but we need it because ports are 'static right now. + let space: &'write mut SpaceCursor<'write> = unsafe { + ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) + }; + space.init_atom(urid) } } diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index ca89831f..dfa29705 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -16,6 +16,11 @@ pub trait SpaceAllocatorImpl { /// # Safety /// /// While implementations MUST return a + /// + /// # Panics + /// + /// This function may panic if the given size, added to the length of the total allocated bytes + /// overflow an [`usize`]. fn allocate_and_split(&mut self, size: usize) -> Result<(&mut [u8], &mut [u8]), AtomWriteError>; diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 5217b3c1..6417ebbb 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -69,12 +69,13 @@ impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { let (previous, current) = self.parent.allocate_and_split(size)?; let space = AlignedSpace::::from_bytes_mut( - previous - .get_mut(self.atom_header_index..) - .ok_or(AtomWriteError::CannotUpdateAtomHeader)?, + // PANIC: We rely on the parent allocator not shifting bytes around + &mut previous[self.atom_header_index..], )?; + + // SAFETY: We rely on the parent allocator not shifting bytes around let header = unsafe { space.assume_init_value_mut() } - .ok_or(AtomWriteError::CannotUpdateAtomHeader)?; + .expect("Unable to locate Atom Header. This is a bug due to an incorrect Allocator implementation"); // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated unsafe { header.set_size_of_body(header.size_of_body() + size) }; diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 168d660b..1eed6152 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -36,7 +36,7 @@ impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { self.allocated_length = self .allocated_length .checked_add(size) - .ok_or(AtomWriteError::AllocatorOverflow)?; + .expect("Allocation overflow"); Ok((allocated, new_allocation)) } @@ -44,9 +44,9 @@ impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { #[inline] unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError> { if self.allocated_length < byte_count { - return Err(AtomWriteError::RewindError { + return Err(AtomWriteError::RewindBeyondAllocated { requested: byte_count, - available: self.allocated_length, + allocated: self.allocated_length, }); } diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs index 01c795b7..fd8a173a 100644 --- a/atom/src/space/error.rs +++ b/atom/src/space/error.rs @@ -2,6 +2,7 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use urid::{Uri, URID}; +/// A Helper struct to store data about a type for alignment error messages #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct TypeData { name: &'static str, @@ -29,6 +30,7 @@ impl Display for TypeData { } } +/// The actual, currently private, alignment error #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub(crate) enum AlignmentErrorInner { @@ -48,6 +50,9 @@ pub(crate) enum AlignmentErrorInner { }, } +/// An alignment error, returned by [`AlignedSpace`]. +/// +/// This error occurs when a byte buffer is unaligned, or could not be aligned. #[derive(Debug, Clone, Eq, PartialEq)] pub struct AlignmentError(pub(crate) AlignmentErrorInner); @@ -65,32 +70,41 @@ impl From for AtomReadError { } } +/// Errors that can occur while writing atoms to a byte buffer. #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum AtomWriteError { + /// A write operation could not proceed because there is not enough space in the allocatable buffer. OutOfSpace { + /// The amount currently used in the buffer, in bytes. used: usize, + /// The total capacity of the buffer, in bytes. capacity: usize, + /// The requested amount of bytes to be allocated in the buffer, in bytes. + /// + /// If this error occurred, most likely this is higher than the remaining amount of bytes available. requested: usize, }, - AllocatorOverflow, - ResizeFailed, - CannotUpdateAtomHeader, - AtomAlreadyWritten, - RewindError { - available: usize, + /// An allocator tried to be rewound beyond the amount of already allocated bytes + RewindBeyondAllocated { + /// The amount of already allocated bytes + allocated: usize, + /// The amount of bytes requested to be rewound + /// + /// If this error occurred, most likely this is higher than the amount of allocated bytes requested: usize, }, + /// A write operation tried to occur outside of the buffer's bounds WritingOutOfBounds { + /// The amount of available bytes in the buffer available: usize, + /// The requested amount of bytes requested: usize, }, WritingIllegalState { writing_type_uri: &'static Uri, }, AlignmentError(AlignmentError), - - Unknown, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -114,8 +128,6 @@ pub enum AtomReadError { reading_type_uri: &'static Uri, }, AlignmentError(AlignmentError), - - Unknown, } #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index be3bdd0b..058fcdca 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -22,8 +22,10 @@ impl<'a> SpaceReader<'a> { self.space = remaining.as_bytes(); - // Failure shouldn't be possible, but it doesn't hurt to check - value.as_uninit().ok_or(AtomReadError::Unknown) + // PANIC: We just split_at the right amount of bytes for a value of T, there should be enough space + Ok(value + .as_uninit() + .expect("Not enough space for an uninit value")) } #[inline] diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 0feef7a6..e77dbaf1 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -56,17 +56,14 @@ impl VecSpace { } let bytes = self.as_bytes_mut(); - bytes - .get(byte_range.clone()) - .ok_or(AtomWriteError::ResizeFailed)?; // To make sure everything is in range instead of panicking on split_at_mut + + // PANIC: We just resized to the accommodate the maximum value in the given range. let (previous, allocatable) = bytes.split_at_mut(byte_range.start); - return Ok(( + Ok(( previous, - allocatable - .get_mut(..byte_range.end - byte_range.start) - .ok_or(AtomWriteError::ResizeFailed)?, - )); + &mut allocatable[..byte_range.end - byte_range.start], + )) } #[inline] @@ -91,7 +88,7 @@ impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { let end = self .allocated_length .checked_add(size) - .ok_or(AtomWriteError::AllocatorOverflow)?; + .expect("Allocation overflow"); let result = VecSpace::::reallocate_bytes_mut(self.vec, self.allocated_length..end); @@ -106,9 +103,9 @@ impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { #[allow(unsafe_code)] unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError> { if self.allocated_length < byte_count { - return Err(AtomWriteError::RewindError { + return Err(AtomWriteError::RewindBeyondAllocated { requested: byte_count, - available: self.allocated_length, + allocated: self.allocated_length, }); } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 2e68bcd5..ff334ecb 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -44,9 +44,9 @@ impl<'a> WMidiEventWriter<'a> { let space = self.writer.allocate(message.bytes_size())?; // The error shouldn't be happening, as we allocated just as many bytes as needed - message - .copy_to_slice(space) - .map_err(|_| AtomWriteError::Unknown)?; + message.copy_to_slice(space).expect( + "Could not copy MIDI message to allocated bytes. This is a bug, most likely due to an incorrect allocator implementation", + ); Ok(()) } } From 89bb24fd9b7cd4fa23dcdb2eb8da6ed940d8fd67 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 25 Sep 2021 03:05:16 +0200 Subject: [PATCH 38/54] Remove some unneeded methods --- atom/src/lib.rs | 22 +++---- atom/src/space/aligned.rs | 119 ++++++++++------------------------ atom/src/space/allocatable.rs | 4 +- atom/src/space/atom_writer.rs | 20 +++--- atom/src/space/reader.rs | 31 ++++++--- atom/src/util.rs | 5 ++ 6 files changed, 85 insertions(+), 116 deletions(-) diff --git a/atom/src/lib.rs b/atom/src/lib.rs index ddae4778..87c92617 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -157,12 +157,7 @@ impl UnidentifiedAtom { /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] pub unsafe fn from_space(space: &AtomSpace) -> Result<&Self, AtomReadError> { - Ok(Self::from_header(space.assume_init_value().ok_or_else( - || AtomReadError::ReadingOutOfBounds { - available: space.len(), - requested: ::core::mem::size_of::(), - }, - )?)) + Ok(Self::from_header(space.read().next_value()?)) } /// Construct a new unidentified atom. @@ -174,12 +169,15 @@ impl UnidentifiedAtom { pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomWriteError> { let available = space.len(); - Ok(Self::from_header_mut(space.assume_init_value_mut().ok_or( - AtomWriteError::WritingOutOfBounds { - available, - requested: ::core::mem::size_of::(), - }, - )?)) + Ok(Self::from_header_mut( + space + .assume_init_slice_mut() + .get_mut(0) + .ok_or(AtomWriteError::WritingOutOfBounds { + available, + requested: ::core::mem::size_of::(), + })?, + )) } #[inline] diff --git a/atom/src/space/aligned.rs b/atom/src/space/aligned.rs index e78e2a94..0b3cf04a 100644 --- a/atom/src/space/aligned.rs +++ b/atom/src/space/aligned.rs @@ -30,7 +30,7 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// ``` /// # use lv2_atom::space::AlignedSpace; /// let values = &[42u64, 69]; -/// // Transmuting to a slice of bytes +/// // Transmuting to a slice of bytes. Imagine those bytes are sent over an external buffer /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// /// // --- @@ -56,7 +56,7 @@ impl AlignedSpace { /// /// # Errors /// - /// This method returns [`None`](Option::None) if the given slice's offset is not aligned + /// This method returns an [`AlignmentError`] if the given slice is not aligned /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). /// /// # Example @@ -87,7 +87,7 @@ impl AlignedSpace { /// /// # Errors /// - /// This method returns [`None`](Option::None) if the given slice's offset is not aligned + /// This method returns an [`AlignmentError`] if the given slice is not aligned /// (i.e. if it's pointer's value is not a multiple of `align_of::()` bytes). /// /// # Example @@ -114,11 +114,11 @@ impl AlignedSpace { Ok(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) } - /// Creates a new space from a slice of bytes, slicing some bytes off its start it if necessary. + /// Creates a new space from a slice of bytes, slicing some bytes off its start if necessary. /// /// # Errors /// - /// This method returns [`None`](Option::None) if the given slice's is too small to contain + /// This method returns an [`AlignmentError`] if the given slice's is too small to contain /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). /// /// # Example @@ -156,12 +156,12 @@ impl AlignedSpace { Ok(unsafe { AlignedSpace::from_bytes_unchecked(data) }) } - /// Creates a new mutable space from a mutable slice of bytes, slicing some bytes off its start it if necessary. + /// Creates a new mutable space from a mutable slice of bytes, slicing some bytes off its start if necessary. /// /// # Errors /// - /// This method returns [`None`](Option::None) if the given slice's is too small to contain - /// aligned bytes (e.g. if it's smaller than `align_of::()` bytes). + /// This method returns an [`AlignmentError`] if the given slice's is too small to contain + /// aligned bytes (e.g. if no byte in it is properly aligned). /// /// # Example /// @@ -210,8 +210,7 @@ impl AlignedSpace { /// ``` #[inline] pub fn empty<'a>() -> &'a AlignedSpace { - // SAFETY: empty slices are always aligned - unsafe { Self::from_bytes_unchecked(&[]) } + Self::from_slice(&[]) } /// Creates an empty mutable space. @@ -225,8 +224,7 @@ impl AlignedSpace { /// ``` #[inline] pub fn empty_mut<'a>() -> &'a mut AlignedSpace { - // SAFETY: empty slices are always aligned - unsafe { Self::from_bytes_mut_unchecked(&mut []) } + Self::from_slice_mut(&mut []) } /// Creates a new space from a slice of bytes, without checking for padding correctness. @@ -303,34 +301,30 @@ impl AlignedSpace { /// A checked version of slice::split_at, which returns the first part as an already-aligned slice. #[inline] - fn split_bytes_at(&self, mid: usize) -> Result<(&Self, &[u8]), AlignmentError> { + pub fn split_at(&self, mid: usize) -> Option<(&Self, &[u8])> { if mid > self.data.len() { - return Err(AlignmentError( - AlignmentErrorInner::NotEnoughSpaceToRealign { - ptr: self.data.as_ptr(), - available_size: self.data.len(), - required_padding: mid + 1, - type_id: TypeData::of::(), - }, - )); + return None; } let (start, end) = self.data.split_at(mid); // SAFETY: Because this data was the start of an existing Space, it was aligned already. let start = unsafe { Self::from_bytes_unchecked(start) }; - Ok((start, end)) + Some((start, end)) } - /// Try to retrieve space. - /// - /// This method calls [`split_raw`](#method.split_raw) and wraps the returned slice in an atom space. The second space is the space after the first one. + /// A checked version of slice::split_at, which returns the first part as an already-aligned slice. #[inline] - pub fn split_at(&self, mid: usize) -> Result<(&Self, &Self), AlignmentError> { - let (start, end) = self.split_bytes_at(mid)?; - let end = Self::align_from_bytes(end).unwrap_or_else(|_| AlignedSpace::empty()); + pub fn split_at_mut(&mut self, mid: usize) -> Option<(&mut Self, &mut [u8])> { + if mid > self.data.len() { + return None; + } + + let (start, end) = self.data.split_at_mut(mid); + // SAFETY: Because this data was the start of an existing Space, it was aligned already. + let start = unsafe { Self::from_bytes_mut_unchecked(start) }; - Ok((start, end)) + Some((start, end)) } /// Return the internal slice of the space. @@ -363,58 +357,6 @@ impl AlignedSpace { self.data.is_empty() } - #[inline] - pub(crate) unsafe fn assume_init_value(&self) -> Option<&T> { - // SAFETY: The caller has to ensure this slice actually points to initialized memory. - Some(crate::util::assume_init_ref(self.as_uninit()?)) - } - - #[inline] - pub(crate) unsafe fn assume_init_value_mut(&mut self) -> Option<&mut T> { - // SAFETY: The caller has to ensure this slice actually points to initialized memory. - Some(crate::util::assume_init_mut(self.as_uninit_mut()?)) - } - - /// Gets a `T`-aligned pointer to the contents. - ///split_for_type - /// This methods returns [`None`](Option::None) if the space is not large enough for a value of type `T`. - #[inline] - pub fn as_uninit(&self) -> Option<&MaybeUninit> { - if self.data.len() < size_of::() { - return None; - } - - // SAFETY: We just checked that the space was actually big enough, and the alignment is guaranteed by this type. - Some(unsafe { self.as_uninit_unchecked() }) - } - - /// Gets a `T`-aligned pointer to the contents. - ///split_for_type - /// This methods returns [`None`](Option::None) if the space is not large enough for a value of type `T`. - #[inline] - fn as_uninit_mut(&mut self) -> Option<&mut MaybeUninit> { - if self.data.len() < size_of::() { - return None; - } - - // SAFETY: We just checked that the space was actually big enough, and the alignment is guaranteed by this type. - Some(unsafe { self.as_uninit_mut_unchecked() }) - } - - /// Gets a `T`-aligned pointer to the contents, but without checking that there actually is enough space to hold `T`. - #[inline] - unsafe fn as_uninit_unchecked(&self) -> &MaybeUninit { - // SAFETY: The caller has to ensure that the space is actually big enough. - &*(self.data.as_ptr() as *const MaybeUninit) - } - - /// Gets a `T`-aligned mutable pointer to the contents, but without checking that there actually is enough space to hold `T`. - #[inline] - pub(crate) unsafe fn as_uninit_mut_unchecked(&mut self) -> &mut MaybeUninit { - // SAFETY: The caller has to ensure that the space is actually big enough. - &mut *(self.data.as_ptr() as *mut MaybeUninit) - } - /// Gets the contents as a slice of potentially uninitialized `T`s. /// /// The resulting slice contains as many values as can fit in the original space. @@ -430,11 +372,6 @@ impl AlignedSpace { } } - #[inline] - pub unsafe fn assume_init_slice(&self) -> &[T] { - crate::util::assume_init_slice(self.as_uninit_slice()) - } - /// Gets the contents as a slice of potentially uninitialized `T`s. /// /// The resulting slice contains as many values as can fit in the original space. @@ -450,6 +387,16 @@ impl AlignedSpace { } } + #[inline] + pub unsafe fn assume_init_slice(&self) -> &[T] { + crate::util::assume_init_slice(self.as_uninit_slice()) + } + + #[inline] + pub unsafe fn assume_init_slice_mut(&mut self) -> &mut [T] { + crate::util::assume_init_slice_mut(self.as_uninit_slice_mut()) + } + #[inline] pub fn read(&self) -> SpaceReader { SpaceReader::new(self.as_bytes()) diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index dfa29705..4f166b59 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -68,7 +68,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { fn allocate_value(&mut self) -> Result<&mut MaybeUninit, AtomWriteError> { let space = self.allocate_aligned(size_of::>())?; // SAFETY: We used size_of, so we are sure that the allocated space is exactly big enough for T. - Ok(unsafe { space.as_uninit_mut_unchecked() }) + Ok(unsafe { space.as_uninit_slice_mut().get_unchecked_mut(0) }) } #[inline] @@ -117,7 +117,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { { let space = self.allocate_aligned(size_of_val(&value))?; // SAFETY: We used size_of_val, so we are sure that the allocated space is exactly big enough for T. - let space = unsafe { space.as_uninit_mut_unchecked() }; + let space = unsafe { space.as_uninit_slice_mut().get_unchecked_mut(0) }; Ok(crate::util::write_uninit(space, value)) } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 6417ebbb..79f5d1b8 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,5 +1,7 @@ use crate::header::AtomHeader; -use crate::space::{error::AtomWriteError, AlignedSpace, SpaceAllocator, SpaceAllocatorImpl}; +use crate::space::{ + error::AtomWriteError, AlignedSpace, AtomSpace, SpaceAllocator, SpaceAllocatorImpl, +}; use urid::URID; /// A `MutSpace` that tracks the amount of allocated space in an atom header. @@ -27,20 +29,21 @@ impl<'a> AtomSpaceWriter<'a> { .allocated_bytes() .get(self.atom_header_index..) .unwrap(); - let space = AlignedSpace::from_bytes(previous).unwrap(); + let space = AtomSpace::from_bytes(previous).unwrap(); - unsafe { *space.assume_init_value().unwrap() } + unsafe { space.assume_init_slice()[0] } } + #[inline] fn atom_header_mut(&mut self) -> &mut AtomHeader { let previous = self .parent .allocated_bytes_mut() .get_mut(self.atom_header_index..) .unwrap(); - let space = AlignedSpace::::from_bytes_mut(previous).unwrap(); + let space = AtomSpace::from_bytes_mut(previous).unwrap(); - unsafe { space.assume_init_value_mut().unwrap() } + unsafe { &mut space.assume_init_slice_mut()[0] } } /// Create a new framed space with the given parent and type URID. @@ -68,13 +71,14 @@ impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { let (previous, current) = self.parent.allocate_and_split(size)?; - let space = AlignedSpace::::from_bytes_mut( + let space = AtomSpace::from_bytes_mut( // PANIC: We rely on the parent allocator not shifting bytes around &mut previous[self.atom_header_index..], )?; - // SAFETY: We rely on the parent allocator not shifting bytes around - let header = unsafe { space.assume_init_value_mut() } + // SAFETY: We already initialized that part of the buffer + let header = unsafe { space.assume_init_slice_mut() } + .get_mut(0) .expect("Unable to locate Atom Header. This is a bug due to an incorrect Allocator implementation"); // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index 058fcdca..d47a945e 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -8,6 +8,19 @@ pub struct SpaceReader<'a> { space: &'a [u8], } +#[inline] +fn split_space( + space: &AlignedSpace, + bytes: usize, +) -> Result<(&AlignedSpace, &[u8]), AtomReadError> { + space + .split_at(bytes) + .ok_or(AtomReadError::ReadingOutOfBounds { + requested: bytes, + available: space.len(), + }) +} + impl<'a> SpaceReader<'a> { #[inline] pub fn new(space: &'a [u8]) -> Self { @@ -18,13 +31,14 @@ impl<'a> SpaceReader<'a> { fn next_uninit_value(&mut self) -> Result<&'a MaybeUninit, AtomReadError> { let space = AlignedSpace::align_from_bytes(self.space)?; let value_size = ::core::mem::size_of::(); - let (value, remaining) = space.split_at(value_size)?; + let (value, remaining) = split_space(space, value_size)?; - self.space = remaining.as_bytes(); + self.space = remaining; // PANIC: We just split_at the right amount of bytes for a value of T, there should be enough space Ok(value - .as_uninit() + .as_uninit_slice() + .get(0) .expect("Not enough space for an uninit value")) } @@ -36,9 +50,9 @@ impl<'a> SpaceReader<'a> { let space = AlignedSpace::align_from_bytes(self.space)?; let split_point = crate::util::value_index_to_byte_index::(length); - let (data, remaining) = space.split_at(split_point)?; + let (data, remaining) = split_space(space, split_point)?; - self.space = remaining.as_bytes(); + self.space = remaining; Ok(data.as_uninit_slice()) } @@ -89,15 +103,16 @@ impl<'a> SpaceReader<'a> { pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomReadError> { let space = AlignedSpace::::align_from_bytes(&self.space)?; let header = space - .assume_init_value() + .assume_init_slice() + .get(0) .ok_or(AtomReadError::ReadingOutOfBounds { available: space.len(), requested: core::mem::size_of::(), })?; - let (_, rest) = space.split_at(header.size_of_atom())?; + let (_, rest) = split_space(space, header.size_of_atom())?; let atom = UnidentifiedAtom::from_header(header); - self.space = rest.as_bytes(); + self.space = rest; Ok(atom) } diff --git a/atom/src/util.rs b/atom/src/util.rs index 03aef8e8..623220be 100644 --- a/atom/src/util.rs +++ b/atom/src/util.rs @@ -20,6 +20,11 @@ pub(crate) unsafe fn assume_init_slice(slice: &[MaybeUninit]) -> &[T] { &*(slice as *const _ as *const [T]) } +#[inline] +pub(crate) unsafe fn assume_init_slice_mut(slice: &mut [MaybeUninit]) -> &mut [T] { + &mut *(slice as *mut _ as *mut [T]) +} + #[inline] pub(crate) fn write_uninit(uninit: &mut MaybeUninit, value: T) -> &mut T { *uninit = MaybeUninit::new(value); From ad1172fdf998cedfb814504048161b28f273977e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 25 Sep 2021 17:00:33 +0200 Subject: [PATCH 39/54] Remove some more unneeded methods, add some docs and rename SpaceAllocator to a more accurate SpaceWriter --- atom/src/atoms/chunk.rs | 2 +- atom/src/atoms/object.rs | 2 +- atom/src/atoms/scalar.rs | 2 +- atom/src/lib.rs | 4 +- atom/src/space/aligned.rs | 147 ++++++++++++++++++++-------------- atom/src/space/allocatable.rs | 14 ++-- atom/src/space/atom_writer.rs | 23 ++---- atom/src/space/cursor.rs | 4 +- atom/src/space/reader.rs | 4 +- atom/src/space/terminated.rs | 38 ++++++++- atom/src/space/vec.rs | 6 +- 11 files changed, 142 insertions(+), 104 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index ac64c61d..95e657a4 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -112,7 +112,7 @@ mod tests { { let data = unsafe { Chunk::read(raw_space.read().next_atom().unwrap().body()) }.unwrap(); - assert_eq!(data.len(), SLICE_LENGTH); + assert_eq!(data.bytes_len(), SLICE_LENGTH); for (i, value) in data.as_bytes().iter().enumerate() { assert_eq!(*value as usize, i); diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 767f12db..7f99100c 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -296,7 +296,7 @@ impl Property { /// This method simply writes out the content of the header to the space and returns `Some(())` if it's successful. #[inline] fn write_header( - space: &mut impl SpaceAllocator, + space: &mut impl SpaceWriter, key: URID, context: Option>, ) -> Result<(), AtomWriteError> { diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 173ad4c5..960d7536 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -58,7 +58,7 @@ pub trait ScalarAtom: UriBound { fn write_scalar( frame: AtomSpaceWriter, ) -> Result, AtomWriteError> { - Ok(ScalarWriter(frame.re_borrow(), PhantomData)) + Ok(ScalarWriter(frame, PhantomData)) } } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 87c92617..33e6eadf 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -87,7 +87,7 @@ pub mod prelude { pub use atoms::tuple::Tuple; pub use atoms::vector::Vector; pub use port::AtomPort; - pub use space::{AlignedSpace, AtomSpace, AtomSpaceWriter, SpaceAllocator}; + pub use space::{AlignedSpace, AtomSpace, AtomSpaceWriter, SpaceWriter}; use crate::*; pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; @@ -167,7 +167,7 @@ impl UnidentifiedAtom { /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. #[inline] pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomWriteError> { - let available = space.len(); + let available = space.bytes_len(); Ok(Self::from_header_mut( space diff --git a/atom/src/space/aligned.rs b/atom/src/space/aligned.rs index 0b3cf04a..4eb3de65 100644 --- a/atom/src/space/aligned.rs +++ b/atom/src/space/aligned.rs @@ -30,10 +30,10 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// ``` /// # use lv2_atom::space::AlignedSpace; /// let values = &[42u64, 69]; -/// // Transmuting to a slice of bytes. Imagine those bytes are sent over an external buffer +/// // Transmuting to a slice of bytes. /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// -/// // --- +/// // --- Imagine those bytes are sent over and then received from an external buffer /// /// // Bytes are already aligned, the whole slice will be available /// let space: &AlignedSpace = AlignedSpace::align_from_bytes(bytes).unwrap(); @@ -72,12 +72,7 @@ impl AlignedSpace { /// ``` #[inline] pub fn from_bytes(data: &[u8]) -> Result<&Self, AlignmentError> { - if data.as_ptr() as usize % align_of::() != 0 { - return Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { - type_id: TypeData::of::(), - ptr: data.as_ptr(), - })); - } + check_alignment::(data)?; // SAFETY: We just checked above that the pointer is correctly aligned Ok(unsafe { AlignedSpace::from_bytes_unchecked(data) }) @@ -103,12 +98,7 @@ impl AlignedSpace { /// ``` #[inline] pub fn from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, AlignmentError> { - if data.as_ptr() as usize % align_of::() != 0 { - return Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { - type_id: TypeData::of::(), - ptr: data.as_ptr(), - })); - } + check_alignment::(data)?; // SAFETY: We just checked above that the pointer is correctly aligned Ok(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) @@ -199,42 +189,25 @@ impl AlignedSpace { Ok(unsafe { AlignedSpace::from_bytes_mut_unchecked(data) }) } - /// Creates a space from an empty slice. + /// Creates a new space from a slice of bytes, without checking for padding correctness. /// - /// # Example + /// # Safety /// - /// ``` - /// # use lv2_atom::prelude::AlignedSpace; - /// let space = AlignedSpace::::empty(); - /// assert!(space.as_bytes().is_empty()); - /// ``` - #[inline] - pub fn empty<'a>() -> &'a AlignedSpace { - Self::from_slice(&[]) - } - - /// Creates an empty mutable space. + /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. + /// Calling this method with an unaligned slice will result in Undefined Behavior. + /// + /// For a safe, checked version, see [`Space::from_bytes`](Space::from_bytes). /// /// # Example /// /// ``` - /// # use lv2_atom::prelude::AlignedSpace; - /// let space = AlignedSpace::::empty_mut(); - /// assert!(space.as_bytes().is_empty()); - /// ``` - #[inline] - pub fn empty_mut<'a>() -> &'a mut AlignedSpace { - Self::from_slice_mut(&mut []) - } - - /// Creates a new space from a slice of bytes, without checking for padding correctness. - /// - /// # Safety - /// - /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. - /// Calling this method with an unaligned slice will result from UB. + /// # use lv2_atom::space::{AlignedSpace, error::AtomWriteError}; + /// let values = &[42u64, 69]; + /// // Transmuting to a slice of bytes + /// let bytes: &[u8] = unsafe { values.align_to().1 }; /// - /// For a safe, checked version, see [`Space::from_bytes`]. + /// assert_eq!(unsafe { AlignedSpace::::from_bytes_unchecked(bytes) }.values_len(), 2); + /// ``` // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] @@ -251,7 +224,17 @@ impl AlignedSpace { /// The caller of this method is responsible for ensuring that the slice's contents are correctly aligned. /// Otherwise, reads will be performed unaligned, which are either slow, a CPU crash, or UB depending on platforms. /// - /// For a safe, checked version, see [`Space::from_bytes_mut`]. + /// For a safe, checked version, see [`Space::from_bytes_mut`](Space::from_bytes_mut). + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::space::{AlignedSpace, error::AtomWriteError}; + /// let values = &mut [42u64, 69]; + /// // Transmuting to a slice of bytes + /// let bytes: &mut [u8] = unsafe { values.align_to_mut().1 }; + /// + /// assert_eq!(unsafe { AlignedSpace::::from_bytes_mut_unchecked(bytes) }.values_len(), 2); // NOTE: This method will always be used internally instead of the constructor, to make sure that // the unsafety is explicit and accounted for. #[inline(always)] @@ -262,6 +245,19 @@ impl AlignedSpace { } /// Creates a new space from an already aligned slice of T values. + /// + /// The slice type guarantees alignment, therefore this operation is infallible. + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::space::{AlignedSpace, error::AtomWriteError}; + /// let values = &[42u64, 69]; + /// + /// let space: &AlignedSpace = AlignedSpace::from_slice(values); + /// assert_eq!(space.values_len(), 2); + /// assert_eq!(space.bytes_len(), 2 * ::core::mem::size_of::()); + /// ``` #[inline] pub fn from_slice(slice: &[T]) -> &Self { // SAFETY: reinterpreting as raw bytes is safe for any value @@ -271,6 +267,18 @@ impl AlignedSpace { } /// Creates a new mutable space from an already aligned slice of T values. + /// + /// The slice type guarantees alignment, therefore this operation is infallible. + /// + /// # Example + /// + /// ``` + /// # use lv2_atom::space::{AlignedSpace, error::AtomWriteError}; + /// let values = &[42u64, 69]; + /// + /// let space: &AlignedSpace = AlignedSpace::from_slice(values); + /// assert_eq!(space.values_len(), 2); + /// assert_eq!(space.bytes_len(), 2 * ::core::mem::size_of::()); #[inline] pub fn from_slice_mut(slice: &mut [T]) -> &mut Self { // SAFETY: reinterpreting as raw bytes is safe for any value @@ -339,11 +347,15 @@ impl AlignedSpace { &mut self.data } + /// Returns the total length of the space, in bytes. #[inline] - pub fn len(&self) -> usize { + pub fn bytes_len(&self) -> usize { self.data.len() } + /// Returns the number of values that can fit in the space. + /// + /// If `T` is a zero-sized type, this returns [`usize::MAX`](usize::MAX). #[inline] pub fn values_len(&self) -> usize { self.data @@ -352,11 +364,6 @@ impl AlignedSpace { .unwrap_or(usize::MAX) } - #[inline] - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } - /// Gets the contents as a slice of potentially uninitialized `T`s. /// /// The resulting slice contains as many values as can fit in the original space. @@ -415,6 +422,18 @@ impl Debug for AlignedSpace { } } +#[inline] +fn check_alignment(data: &[u8]) -> Result<(), AlignmentError> { + if data.as_ptr() as usize % align_of::() != 0 { + return Err(AlignmentError(AlignmentErrorInner::UnalignedBuffer { + type_id: TypeData::of::(), + ptr: data.as_ptr(), + })); + } + + Ok(()) +} + #[cfg(test)] mod tests { use crate::space::error::{AlignmentError, AlignmentErrorInner, TypeData}; @@ -429,21 +448,23 @@ mod tests { let bytes = unsafe { values.align_to_mut().1 }; assert_eq!( - AlignedSpace::::from_bytes(bytes).unwrap().len(), + AlignedSpace::::from_bytes(bytes).unwrap().bytes_len(), ::core::mem::size_of::() * 2 ); assert_eq!( - AlignedSpace::::from_bytes_mut(bytes).unwrap().len(), + AlignedSpace::::from_bytes_mut(bytes) + .unwrap() + .bytes_len(), ::core::mem::size_of::() * 2 ); assert_eq!( - unsafe { AlignedSpace::::from_bytes_unchecked(bytes) }.len(), + unsafe { AlignedSpace::::from_bytes_unchecked(bytes) }.bytes_len(), ::core::mem::size_of::() * 2 ); assert_eq!( - unsafe { AlignedSpace::::from_bytes_mut_unchecked(bytes) }.len(), + unsafe { AlignedSpace::::from_bytes_mut_unchecked(bytes) }.bytes_len(), ::core::mem::size_of::() * 2 ); @@ -472,42 +493,44 @@ mod tests { let size = ::core::mem::size_of::(); assert_eq!( - AlignedSpace::::align_from_bytes(bytes).unwrap().len(), + AlignedSpace::::align_from_bytes(bytes) + .unwrap() + .bytes_len(), size * 2 ); assert_eq!( AlignedSpace::::align_from_bytes_mut(bytes) .unwrap() - .len(), + .bytes_len(), size * 2 ); assert_eq!( AlignedSpace::::align_from_bytes(&bytes[1..]) .unwrap() - .len(), + .bytes_len(), size ); assert_eq!( AlignedSpace::::align_from_bytes_mut(&mut bytes[1..]) .unwrap() - .len(), + .bytes_len(), size ); assert_eq!( AlignedSpace::::align_from_bytes(&bytes[9..]) .unwrap() - .len(), + .bytes_len(), 0 ); assert_eq!( AlignedSpace::::align_from_bytes_mut(&mut bytes[9..]) .unwrap() - .len(), + .bytes_len(), 0 ); @@ -537,7 +560,7 @@ mod tests { ); } - fn test_mut_space<'a>(mut space: impl SpaceAllocator) { + fn test_mut_space<'a>(mut space: impl SpaceWriter) { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); @@ -565,7 +588,7 @@ mod tests { written_atom_addr, created_space.as_bytes().as_ptr() )); - assert_eq!(created_space.len(), size_of::() + 42); + assert_eq!(created_space.bytes_len(), size_of::() + 42); { let space: &mut _ = &mut space; diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 4f166b59..cf264065 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -10,8 +10,7 @@ use core::mem::{size_of, size_of_val, MaybeUninit}; /// /// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. /// -// TODO: Find proper name -pub trait SpaceAllocatorImpl { +pub trait SpaceWriterImpl { /// /// # Safety /// @@ -33,8 +32,7 @@ pub trait SpaceAllocatorImpl { fn remaining_bytes_mut(&mut self) -> &mut [u8]; } -// TODO: Find proper name -pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { +pub trait SpaceWriter: SpaceWriterImpl + Sized { /// Try to allocate memory on the internal data slice. /// /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. @@ -94,7 +92,7 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { &mut self, atom: &UnidentifiedAtom, ) -> Result<&mut UnidentifiedAtom, AtomWriteError> { - let resulting_space = self.allocate_aligned(atom.atom_space().len())?; + let resulting_space = self.allocate_aligned(atom.atom_space().bytes_len())?; resulting_space .as_bytes_mut() .copy_from_slice(atom.atom_space().as_bytes()); @@ -143,13 +141,13 @@ pub trait SpaceAllocator: SpaceAllocatorImpl + Sized { } } -impl SpaceAllocator for H {} +impl SpaceWriter for H where H: SpaceWriterImpl {} #[cfg(test)] mod tests { - use crate::prelude::{Int, SpaceAllocator}; + use crate::prelude::{Int, SpaceWriter}; use crate::space::cursor::SpaceCursor; - use crate::space::{SpaceAllocatorImpl, VecSpace}; + use crate::space::{SpaceWriterImpl, VecSpace}; use crate::AtomHeader; use urid::URID; diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 79f5d1b8..bd349284 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,27 +1,14 @@ use crate::header::AtomHeader; -use crate::space::{ - error::AtomWriteError, AlignedSpace, AtomSpace, SpaceAllocator, SpaceAllocatorImpl, -}; +use crate::space::{error::AtomWriteError, AtomSpace, SpaceWriter, SpaceWriterImpl}; use urid::URID; /// A `MutSpace` that tracks the amount of allocated space in an atom header. pub struct AtomSpaceWriter<'a> { atom_header_index: usize, - parent: &'a mut (dyn SpaceAllocatorImpl), + parent: &'a mut (dyn SpaceWriterImpl), } impl<'a> AtomSpaceWriter<'a> { - #[inline] - pub fn re_borrow<'b>(self) -> AtomSpaceWriter<'b> - where - 'a: 'b, - { - AtomSpaceWriter { - atom_header_index: self.atom_header_index, - parent: self.parent, - } - } - #[inline] pub fn atom_header(&self) -> AtomHeader { let previous = self @@ -48,7 +35,7 @@ impl<'a> AtomSpaceWriter<'a> { /// Create a new framed space with the given parent and type URID. pub fn write_new( - parent: &'a mut impl SpaceAllocator, + parent: &'a mut impl SpaceWriter, urid: URID, ) -> Result { let atom = AtomHeader::new(urid); @@ -63,7 +50,7 @@ impl<'a> AtomSpaceWriter<'a> { } } -impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { +impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { #[inline] fn allocate_and_split( &mut self, @@ -123,7 +110,7 @@ impl<'a> SpaceAllocatorImpl for AtomSpaceWriter<'a> { mod tests { use crate::prelude::AtomSpaceWriter; use crate::space::cursor::SpaceCursor; - use crate::space::{SpaceAllocator, VecSpace}; + use crate::space::{SpaceWriter, VecSpace}; use crate::AtomHeader; use core::mem::size_of; use urid::URID; diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 1eed6152..7c59d710 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,5 +1,5 @@ use crate::space::error::AtomWriteError; -use crate::space::SpaceAllocatorImpl; +use crate::space::SpaceWriterImpl; pub struct SpaceCursor<'a> { data: &'a mut [u8], @@ -15,7 +15,7 @@ impl<'a> SpaceCursor<'a> { } } -impl<'a> SpaceAllocatorImpl for SpaceCursor<'a> { +impl<'a> SpaceWriterImpl for SpaceCursor<'a> { #[inline] fn allocate_and_split( &mut self, diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index d47a945e..b3c97ccd 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -17,7 +17,7 @@ fn split_space( .split_at(bytes) .ok_or(AtomReadError::ReadingOutOfBounds { requested: bytes, - available: space.len(), + available: space.bytes_len(), }) } @@ -106,7 +106,7 @@ impl<'a> SpaceReader<'a> { .assume_init_slice() .get(0) .ok_or(AtomReadError::ReadingOutOfBounds { - available: space.len(), + available: space.bytes_len(), requested: core::mem::size_of::(), })?; let (_, rest) = split_space(space, header.size_of_atom())?; diff --git a/atom/src/space/terminated.rs b/atom/src/space/terminated.rs index 989b3fff..0ef9df1e 100644 --- a/atom/src/space/terminated.rs +++ b/atom/src/space/terminated.rs @@ -1,13 +1,38 @@ use crate::space::error::AtomWriteError; -use crate::space::SpaceAllocatorImpl; +use crate::space::SpaceWriterImpl; -pub struct Terminated { +/// An helper space writer, that wraps an existing writer and makes sure all writes are +/// terminated with a given terminator byte. +/// +/// Further writes overwrite the added terminator byte, and append a new one. +/// +/// This helper is useful to implement Atom writer for null-terminated strings, for instance. +/// +/// # Example +/// +/// ``` +/// use lv2_atom::space::{SpaceCursor, SpaceWriter, SpaceWriterImpl, Terminated}; +/// let mut buffer = [0; 20]; +/// // Our underlying allocator +/// let cursor = SpaceCursor::new(&mut buffer); +/// +/// let mut writer = Terminated::new(cursor, 0x42); // Alternative: use cursor.terminated(). +/// +/// writer.write_bytes(b"Hello, world!").unwrap(); +/// assert_eq!(writer.allocated_bytes(), b"Hello, world!\x42"); +/// +/// writer.write_bytes(b" Boop!").unwrap(); +/// assert_eq!(&buffer, b"Hello, world! Boop!\x42"); +/// +/// ``` +pub struct Terminated { inner: W, terminator: u8, wrote_terminator_byte: bool, } -impl Terminated { +impl Terminated { + /// Creates a new Terminated writer, from an inner writer and a given terminator byte. pub fn new(inner: W, terminator: u8) -> Self { Self { inner, @@ -15,9 +40,14 @@ impl Terminated { wrote_terminator_byte: false, } } + + /// Unwraps the `Terminated` helper and returns the underlying allocator. + pub fn into_inner(self) -> W { + self.inner + } } -impl SpaceAllocatorImpl for Terminated { +impl SpaceWriterImpl for Terminated { fn allocate_and_split( &mut self, size: usize, diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index e77dbaf1..5e608376 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,7 +1,7 @@ #![deny(unsafe_code)] use crate::space::error::AtomWriteError; -use crate::space::{AlignedSpace, SpaceAllocatorImpl}; +use crate::space::{AlignedSpace, SpaceWriterImpl}; use std::mem::MaybeUninit; use std::ops::Range; @@ -80,7 +80,7 @@ pub struct VecSpaceCursor<'vec, T> { allocated_length: usize, } -impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { +impl<'vec, T: Copy + 'static> SpaceWriterImpl for VecSpaceCursor<'vec, T> { fn allocate_and_split( &mut self, size: usize, @@ -143,7 +143,7 @@ impl<'vec, T: Copy + 'static> SpaceAllocatorImpl for VecSpaceCursor<'vec, T> { #[cfg(test)] mod tests { - use crate::space::{SpaceAllocator, VecSpace}; + use crate::space::{SpaceWriter, VecSpace}; #[test] pub fn test_lifetimes() { From 5478673274def368c9ccf922693a095dd442b888 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 25 Sep 2021 22:14:54 +0200 Subject: [PATCH 40/54] Various module refactorings --- atom/src/atoms.rs | 3 +- atom/src/atoms/chunk.rs | 9 +- atom/src/atoms/object.rs | 12 ++- atom/src/atoms/scalar.rs | 4 +- atom/src/atoms/sequence.rs | 4 +- atom/src/atoms/string.rs | 6 +- atom/src/atoms/tuple.rs | 4 +- atom/src/atoms/vector.rs | 4 +- atom/src/lib.rs | 188 +++++++-------------------------- atom/src/port.rs | 11 +- atom/src/space/allocatable.rs | 8 +- atom/src/space/atom_writer.rs | 5 +- atom/src/space/reader.rs | 4 +- atom/src/unidentified.rs | 133 +++++++++++++++++++++++ atom/tests/atom_integration.rs | 2 +- docs/fifths/src/lib.rs | 2 +- midi/src/lib.rs | 2 +- midi/src/raw.rs | 6 +- midi/src/wmidi_binding.rs | 8 +- 19 files changed, 217 insertions(+), 198 deletions(-) create mode 100644 atom/src/unidentified.rs diff --git a/atom/src/atoms.rs b/atom/src/atoms.rs index 501a7ff7..9d875a53 100644 --- a/atom/src/atoms.rs +++ b/atom/src/atoms.rs @@ -6,11 +6,10 @@ pub mod string; pub mod tuple; pub mod vector; -pub use crate::header::AtomHeader; use urid::*; +/// An URID collection of all standard atom types, provided for convenience. #[derive(Clone, URIDCollection)] -/// Collection with the URIDs of all `UriBound`s in this crate. pub struct AtomURIDCollection { pub blank: URID, pub double: URID, diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 95e657a4..84231c5f 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -7,6 +7,8 @@ //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! +//! use lv2_atom::space::{AtomSpace, AtomSpaceWriter, SpaceWriter}; +//! //! #[derive(PortCollection)] //! struct MyPorts { //! input: InputPort, @@ -15,9 +17,10 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let in_chunk: &AtomSpace = ports.input.read(urids.chunk).unwrap(); -//! let mut out_chunk: AtomSpaceWriter = ports.output.init(urids.chunk).unwrap(); +//! let mut out_chunk: AtomSpaceWriter = ports.output.write(urids.chunk).unwrap(); //! -//! out_chunk.write_bytes(in_chunk.as_bytes()).unwrap(); +//! let bytes = in_chunk.as_bytes(); +//! out_chunk.write_bytes(bytes).unwrap(); //! } //! ``` //! @@ -61,7 +64,7 @@ impl Atom for Chunk { } #[inline] - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(frame) diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 7f99100c..3f36ae92 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -7,6 +7,7 @@ //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! use urid::*; +//! use lv2_atom::atoms::object::ObjectHeader; //! //! #[uri("urn:object-class")] //! struct ObjectClass; @@ -52,7 +53,7 @@ //! } //! //! // Initialize the object. -//! let mut object_writer = ports.output.init(urids.atom.object) +//! let mut object_writer = ports.output.write(urids.atom.object) //! .unwrap() //! .write_header( //! ObjectHeader { @@ -145,7 +146,7 @@ impl Atom for Object { Ok((header, reader)) } - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(ObjectHeaderWriter { frame }) @@ -175,10 +176,10 @@ impl Atom for Blank { } #[inline] - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { - Object::init(frame) + Object::write(frame) } } @@ -312,6 +313,7 @@ impl Property { #[cfg(test)] mod tests { + use crate::atoms::object::{ObjectHeader, PropertyHeader}; use crate::prelude::*; use crate::space::*; use crate::AtomHeader; @@ -345,7 +347,7 @@ mod tests { { let mut cursor = SpaceCursor::new(raw_space.as_bytes_mut()); let frame = AtomSpaceWriter::write_new(&mut cursor, urids.object).unwrap(); - let mut writer = Object::init(frame) + let mut writer = Object::write(frame) .unwrap() .write_header(ObjectHeader { id: None, diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 960d7536..2651bdd6 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -22,7 +22,7 @@ //! let read_value: &f32 = ports.input.read(urids.float).unwrap(); //! //! // Writing is done with the value of the atom. -//! ports.output.init(urids.float).unwrap().set(17.0); +//! ports.output.write(urids.float).unwrap().set(17.0); //! } //! ``` //! @@ -100,7 +100,7 @@ impl Atom for A { ::read_scalar(body) } - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { ::write_scalar(frame) diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 94b6b377..6f394912 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -36,7 +36,7 @@ //! //! // Get the write handle to the sequence. //! // You have to provide the unit of the time stamps. -//! let mut output_sequence: SequenceWriter = ports.output.init(urids.atom.sequence) +//! let mut output_sequence: SequenceWriter = ports.output.write(urids.atom.sequence) //! .unwrap() //! .with_unit(urids.units.frame) //! .unwrap(); @@ -159,7 +159,7 @@ impl Atom for Sequence { Ok(SequenceHeaderReader { reader, header }) } - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(SequenceHeaderWriter { writer: frame }) diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 8a90ecbc..62946afa 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -18,7 +18,7 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let input: &str = ports.input.read(urids.string).unwrap(); -//! let mut writer: StringWriter = ports.output.init(urids.string).unwrap(); +//! let mut writer: StringWriter = ports.output.write(urids.string).unwrap(); //! writer.append(input).unwrap(); //! } //! ``` @@ -127,7 +127,7 @@ impl Atom for Literal { } #[inline] - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(LiteralInfoWriter { writer: frame }) @@ -177,7 +177,7 @@ impl Atom for String { Ok(str) } - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(StringWriter { diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 5441516b..423691f8 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -16,7 +16,7 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let input: TupleIterator = ports.input.read(urids.tuple).unwrap(); -//! let mut output: TupleWriter = ports.output.init(urids.tuple).unwrap(); +//! let mut output: TupleWriter = ports.output.write(urids.tuple).unwrap(); //! for atom in input { //! if let Ok(integer) = atom.read(urids.int) { //! output.init(urids.int).unwrap().set(*integer * 2).unwrap(); @@ -66,7 +66,7 @@ impl Atom for Tuple { }) } - fn init( + fn write( frame: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(TupleWriter { frame }) diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 6000d638..23d79581 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -20,7 +20,7 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let input: &[i32] = ports.input.read(urids.vector).unwrap().of_type(urids.int).unwrap(); -//! let mut output: VectorWriter = ports.output.init(urids.vector).unwrap().of_type(urids.int).unwrap(); +//! let mut output: VectorWriter = ports.output.write(urids.vector).unwrap().of_type(urids.int).unwrap(); //! output.append(input).unwrap(); //! } //! ``` @@ -128,7 +128,7 @@ impl Atom for Vector { Ok(VectorReader { reader, header }) } - fn init( + fn write( writer: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError> { Ok(VectorTypeWriter { writer }) diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 33e6eadf..b26005ee 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -15,6 +15,7 @@ //! use lv2_core::prelude::*; //! use lv2_units::prelude::*; //! use urid::*; +//! use lv2_atom::space::error::AtomError; //! //! #[derive(PortCollection)] //! struct MyPorts { @@ -29,19 +30,16 @@ //! } //! //! /// Something like a plugin's run method. -//! fn run(ports: &mut MyPorts, urids: &MyURIDs) { +//! fn run(ports: &mut MyPorts, urids: &MyURIDs) -> Result<(), AtomError> { //! // Get the read handle to the sequence. //! let input_sequence = ports.input -//! .read(urids.atom.sequence) -//! .unwrap() -//! .with_unit(urids.units.frame) -//! .unwrap(); +//! .read(urids.atom.sequence)? +//! .with_unit(urids.units.frame)?; //! //! // Get the write handle to the sequence. -//! let mut output_sequence = ports.output.init(urids.atom.sequence) -//! .unwrap() -//! .with_unit(urids.units.frame) -//! .unwrap(); +//! let mut output_sequence = ports.output +//! .write(urids.atom.sequence)? +//! .with_unit(urids.units.frame)?; //! //! // Iterate through all events in the input sequence. //! // An event contains a timestamp and an atom. @@ -49,18 +47,24 @@ //! // If the read atom is a 32-bit integer... //! if let Ok(integer) = atom.read(urids.atom.int) { //! // Multiply it by two and write it to the sequence. -//! output_sequence.new_event(timestamp, urids.atom.int).unwrap().set(*integer * 2).unwrap(); +//! output_sequence.new_event(timestamp, urids.atom.int)?.set(*integer * 2)?; //! } else { //! // Forward the atom to the sequence without a change. -//! output_sequence.forward(timestamp, atom).unwrap(); +//! output_sequence.forward(timestamp, atom)?; //! } //! } +//! +//! Ok(()) //! } //! ``` //! //! # Internals //! //! Internally, all atoms are powered by the structs in the [`space`](space/index.html) module. They safely abstract the reading and writing process and assure that no memory is improperly accessed or leaked and that alignments are upheld. If you simply want to use the atoms in this crate, you don't need to deal with. They are only interesting if you want to create your own atom types. + +#![warn(clippy::missing_errors_doc)] +#![warn(clippy::missing_panics_doc)] + extern crate lv2_sys as sys; extern crate lv2_units as units; @@ -75,24 +79,39 @@ mod header; pub mod port; pub mod space; +mod unidentified; pub(crate) mod util; +pub use unidentified::UnidentifiedAtom; /// Prelude of `lv2_atom` for wildcard usage. pub mod prelude { - pub use atoms::chunk::Chunk; - pub use atoms::object::{Object, ObjectHeader, PropertyHeader}; - pub use atoms::scalar::{AtomURID, Bool, Double, Float, Int, Long}; - pub use atoms::sequence::Sequence; - pub use atoms::string::{Literal, LiteralInfo, String}; - pub use atoms::tuple::Tuple; - pub use atoms::vector::Vector; + pub use atoms::{ + chunk::Chunk, + object::{Object, ObjectHeader, PropertyHeader}, + scalar::{AtomURID, Bool, Double, Float, Int, Long}, + sequence::Sequence, + string::{Literal, LiteralInfo, String}, + tuple::Tuple, + vector::Vector, + }; pub use port::AtomPort; - pub use space::{AlignedSpace, AtomSpace, AtomSpaceWriter, SpaceWriter}; use crate::*; pub use crate::{atoms::AtomURIDCollection, Atom, UnidentifiedAtom}; } +/// A special prelude re-exporting all utilities to implement custom atom types. +pub mod atom_prelude { + pub use crate::prelude::*; + + pub use crate::space::{ + error::{AlignmentError, AtomError, AtomReadError, AtomWriteError}, + AlignedSpace, AtomSpace, AtomSpaceWriter, SpaceCursor, SpaceWriter, SpaceWriterImpl, + Terminated, VecSpace, + }; + pub use crate::{Atom, AtomHandle, AtomHeader, UnidentifiedAtom}; +} + pub trait AtomHandle<'a> { type Handle: 'a; } @@ -135,136 +154,7 @@ pub trait Atom: UriBound { /// The frame of the atom was already initialized, containing the URID. /// /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. - fn init( + fn write( writer: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError>; } - -/// An atom of yet unknown type. -/// -/// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. -#[derive(Clone, Copy)] -#[repr(C)] -pub struct UnidentifiedAtom { - header: AtomHeader, -} - -impl UnidentifiedAtom { - /// Construct a new unidentified atom. - /// - /// # Safety - /// - /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. - #[inline] - pub unsafe fn from_space(space: &AtomSpace) -> Result<&Self, AtomReadError> { - Ok(Self::from_header(space.read().next_value()?)) - } - - /// Construct a new unidentified atom. - /// - /// # Safety - /// - /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. - #[inline] - pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomWriteError> { - let available = space.bytes_len(); - - Ok(Self::from_header_mut( - space - .assume_init_slice_mut() - .get_mut(0) - .ok_or(AtomWriteError::WritingOutOfBounds { - available, - requested: ::core::mem::size_of::(), - })?, - )) - } - - #[inline] - pub(crate) unsafe fn from_header(header: &AtomHeader) -> &Self { - // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. - &*(header as *const _ as *const _) - } - - #[inline] - pub(crate) unsafe fn from_header_mut(header: &mut AtomHeader) -> &mut Self { - // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. - &mut *(header as *mut _ as *mut _) - } - - /// Try to read the atom. - /// - /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. - pub fn read( - &self, - urid: URID, - ) -> Result<::Handle, AtomReadError> { - self.header.check_urid(urid)?; - - // SAFETY: the fact that this contains a valid instance of A is checked above. - unsafe { A::read(self.body()) } - } - - #[inline] - pub fn header(&self) -> &AtomHeader { - &self.header - } - - #[inline] - fn body_bytes(&self) -> &[u8] { - if self.header.size_of_body() == 0 { - &[] - } else { - // SAFETY: This type's constructor ensures the atom's body is valid - // The edge case of an empty body is also checked above. - let ptr = unsafe { (self as *const UnidentifiedAtom).add(1) }; - - // SAFETY: This type's constructor ensures the atom's body is valid - unsafe { ::core::slice::from_raw_parts(ptr.cast(), self.header.size_of_body()) } - } - } - - #[inline] - fn body_bytes_mut(&mut self) -> &mut [u8] { - if self.header.size_of_body() == 0 { - &mut [] - } else { - // SAFETY: This type's constructor ensures the atom's body is valid - // The edge case of an empty body is also checked above. - let ptr = unsafe { (self as *mut UnidentifiedAtom).add(1) }; - - // SAFETY: This type's constructor ensures the atom's body is valid - unsafe { ::core::slice::from_raw_parts_mut(ptr.cast(), self.header.size_of_body()) } - } - } - - #[inline] - pub fn atom_space(&self) -> &AtomSpace { - let ptr = self as *const UnidentifiedAtom as *const u8; - let bytes = unsafe { ::core::slice::from_raw_parts(ptr, self.header.size_of_atom()) }; - - // SAFETY: the bytes are necessarily aligned, since they point to the aligned AtomHeader - unsafe { AtomSpace::from_bytes_unchecked(bytes) } - } - - #[inline] - pub fn atom_space_mut(&mut self) -> &mut AtomSpace { - let ptr = self as *mut UnidentifiedAtom as *mut u8; - let bytes = unsafe { ::core::slice::from_raw_parts_mut(ptr, self.header.size_of_atom()) }; - - // SAFETY: the bytes are necessarily aligned, since they point to the aligned AtomHeader - unsafe { AtomSpace::from_bytes_mut_unchecked(bytes) } - } - - #[inline] - pub fn body(&self) -> &AtomSpace { - // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader - unsafe { AtomSpace::from_bytes_unchecked(self.body_bytes()) } - } - - #[inline] - pub fn body_mut(&mut self) -> &mut AtomSpace { - // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader - unsafe { AtomSpace::from_bytes_mut_unchecked(self.body_bytes_mut()) } - } -} diff --git a/atom/src/port.rs b/atom/src/port.rs index 11895af0..91ed060c 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -20,7 +20,7 @@ //! // Read an integer from the port and print it. //! println!("My input is: {}", ports.input.read(urids.int).unwrap()); //! // Write the integer `42` to the port. -//! ports.output.init(urids.int).unwrap(); +//! ports.output.write(urids.int).unwrap(); //! } //! ``` use crate::header::AtomHeader; @@ -49,7 +49,10 @@ impl<'a> PortReader<'a> { /// /// In order to identify the atom, the reader needs to know it's URID. Also, some atoms require a parameter. However, you can simply pass `()` in most cases. /// - /// This method returns `None` if the atom is malformed or simply isn't of the specified type. + /// # Errors + /// + /// This method can return any read error if the given URID doesn't match the contained atom, + /// or if any other read error occurred. #[inline] pub fn read( &self, @@ -83,7 +86,7 @@ impl<'a> PortWriter<'a> { /// # Errors /// /// This method can return an error if the buffer isn't big enough to initialize the given atom's header. - pub fn init<'b, 'write, A: crate::Atom>( + pub fn write<'b, 'write, A: crate::Atom>( &'b mut self, // SAFETY: 'write should be :'a , but for now we have to return 'static arbitrary lifetimes. urid: URID, ) -> Result<>::Handle, AtomWriteError> { @@ -149,7 +152,7 @@ mod tests { let mut writer = unsafe { AtomPort::output_from_raw(NonNull::from(raw_space.as_bytes_mut()).cast(), 0) }; - writer.init::(urids.int).unwrap().set(42).unwrap(); + writer.write::(urids.int).unwrap().set(42).unwrap(); } // Reading diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index cf264065..54f8dbeb 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -84,7 +84,7 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { atom_type: URID, ) -> Result<::Handle, AtomWriteError> { let space = AtomSpaceWriter::write_new(self, atom_type)?; - A::init(space) + A::write(space) } #[inline] @@ -145,10 +145,8 @@ impl SpaceWriter for H where H: SpaceWriterImpl {} #[cfg(test)] mod tests { - use crate::prelude::{Int, SpaceWriter}; - use crate::space::cursor::SpaceCursor; - use crate::space::{SpaceWriterImpl, VecSpace}; - use crate::AtomHeader; + use crate::atom_prelude::*; + use crate::prelude::*; use urid::URID; // SAFETY: this is just for testing, values aren't actually read using this URID. diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index bd349284..b00b386e 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -108,10 +108,7 @@ impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { #[cfg(test)] mod tests { - use crate::prelude::AtomSpaceWriter; - use crate::space::cursor::SpaceCursor; - use crate::space::{SpaceWriter, VecSpace}; - use crate::AtomHeader; + use crate::atom_prelude::*; use core::mem::size_of; use urid::URID; diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index b3c97ccd..c752ee51 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -1,6 +1,4 @@ -use crate::prelude::AlignedSpace; -use crate::space::error::AtomReadError; -use crate::{AtomHeader, UnidentifiedAtom}; +use crate::atom_prelude::*; use std::mem::MaybeUninit; #[derive(Clone)] diff --git a/atom/src/unidentified.rs b/atom/src/unidentified.rs new file mode 100644 index 00000000..c9fc216f --- /dev/null +++ b/atom/src/unidentified.rs @@ -0,0 +1,133 @@ +use crate::space::error::{AtomReadError, AtomWriteError}; +use crate::space::AtomSpace; +use crate::{Atom, AtomHandle, AtomHeader}; +use urid::URID; + +/// An atom of yet unknown type. +/// +/// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct UnidentifiedAtom { + header: AtomHeader, +} + +impl UnidentifiedAtom { + /// Construct a new unidentified atom. + /// + /// # Safety + /// + /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. + #[inline] + pub unsafe fn from_space(space: &AtomSpace) -> Result<&Self, AtomReadError> { + Ok(Self::from_header(space.read().next_value()?)) + } + + /// Construct a new unidentified atom. + /// + /// # Safety + /// + /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. + #[inline] + pub unsafe fn from_space_mut(space: &mut AtomSpace) -> Result<&mut Self, AtomWriteError> { + let available = space.bytes_len(); + + Ok(Self::from_header_mut( + space + .assume_init_slice_mut() + .get_mut(0) + .ok_or(AtomWriteError::WritingOutOfBounds { + available, + requested: ::core::mem::size_of::(), + })?, + )) + } + + #[inline] + pub(crate) unsafe fn from_header(header: &AtomHeader) -> &Self { + // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. + &*(header as *const _ as *const _) + } + + #[inline] + pub(crate) unsafe fn from_header_mut(header: &mut AtomHeader) -> &mut Self { + // SAFETY: UnidentifiedAtom is repr(C) and has AtomHeader as its only field, so transmuting between the two is safe. + &mut *(header as *mut _ as *mut _) + } + + /// Try to read the atom. + /// + /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. + pub fn read( + &self, + urid: URID, + ) -> Result<::Handle, AtomReadError> { + self.header.check_urid(urid)?; + + // SAFETY: the fact that this contains a valid instance of A is checked above. + unsafe { A::read(self.body()) } + } + + #[inline] + pub fn header(&self) -> &AtomHeader { + &self.header + } + + #[inline] + fn body_bytes(&self) -> &[u8] { + if self.header.size_of_body() == 0 { + &[] + } else { + // SAFETY: This type's constructor ensures the atom's body is valid + // The edge case of an empty body is also checked above. + let ptr = unsafe { (self as *const UnidentifiedAtom).add(1) }; + + // SAFETY: This type's constructor ensures the atom's body is valid + unsafe { ::core::slice::from_raw_parts(ptr.cast(), self.header.size_of_body()) } + } + } + + #[inline] + fn body_bytes_mut(&mut self) -> &mut [u8] { + if self.header.size_of_body() == 0 { + &mut [] + } else { + // SAFETY: This type's constructor ensures the atom's body is valid + // The edge case of an empty body is also checked above. + let ptr = unsafe { (self as *mut UnidentifiedAtom).add(1) }; + + // SAFETY: This type's constructor ensures the atom's body is valid + unsafe { ::core::slice::from_raw_parts_mut(ptr.cast(), self.header.size_of_body()) } + } + } + + #[inline] + pub fn atom_space(&self) -> &AtomSpace { + let ptr = self as *const UnidentifiedAtom as *const u8; + let bytes = unsafe { ::core::slice::from_raw_parts(ptr, self.header.size_of_atom()) }; + + // SAFETY: the bytes are necessarily aligned, since they point to the aligned AtomHeader + unsafe { AtomSpace::from_bytes_unchecked(bytes) } + } + + #[inline] + pub fn atom_space_mut(&mut self) -> &mut AtomSpace { + let ptr = self as *mut UnidentifiedAtom as *mut u8; + let bytes = unsafe { ::core::slice::from_raw_parts_mut(ptr, self.header.size_of_atom()) }; + + // SAFETY: the bytes are necessarily aligned, since they point to the aligned AtomHeader + unsafe { AtomSpace::from_bytes_mut_unchecked(bytes) } + } + + #[inline] + pub fn body(&self) -> &AtomSpace { + // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader + unsafe { AtomSpace::from_bytes_unchecked(self.body_bytes()) } + } + + #[inline] + pub fn body_mut(&mut self) -> &mut AtomSpace { + // SAFETY: the bytes are necessarily aligned, since they are right after the aligned AtomHeader + unsafe { AtomSpace::from_bytes_mut_unchecked(self.body_bytes_mut()) } + } +} diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index a58a90a0..3f0b5966 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -53,7 +53,7 @@ impl Plugin for AtomPlugin { let mut sequence_writer = ports .output - .init(self.urids.atom.sequence) + .write(self.urids.atom.sequence) .unwrap() .with_unit(self.urids.units.frame) .unwrap(); diff --git a/docs/fifths/src/lib.rs b/docs/fifths/src/lib.rs index 7cf71cc1..9e165d3e 100644 --- a/docs/fifths/src/lib.rs +++ b/docs/fifths/src/lib.rs @@ -50,7 +50,7 @@ impl Plugin for Fifths { // Initialise the output sequence and get the writing handle. let mut output_sequence = ports .output - .init(self.urids.atom.sequence) + .write(self.urids.atom.sequence) .unwrap() .with_unit(self.urids.unit.frame) .unwrap(); diff --git a/midi/src/lib.rs b/midi/src/lib.rs index 29fb7a42..181b17fe 100644 --- a/midi/src/lib.rs +++ b/midi/src/lib.rs @@ -39,7 +39,7 @@ //! .unwrap(); //! //! let mut output_sequence = ports.output -//! .init(urids.atom.sequence) +//! .write(urids.atom.sequence) //! .unwrap() //! .with_unit(urids.units.frame) //! .unwrap(); diff --git a/midi/src/raw.rs b/midi/src/raw.rs index 0c155d13..f9522dbf 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -3,9 +3,7 @@ //! This implementation is very low-level; Basically an alias for a chunk. It should only be used by those who don't want additional dependencies or want to modify messages directly. //! //! If you just want to use MIDI messages in your plugin, you should use the optional `wmidi` feature. -use atom::prelude::*; -use atom::space::error::*; -use atom::AtomHandle; +use atom::atom_prelude::*; use urid::*; /// Midi Event. @@ -37,7 +35,7 @@ impl Atom for MidiEvent { Ok(body.as_bytes()) } - fn init(frame: AtomSpaceWriter) -> Result { + fn write(frame: AtomSpaceWriter) -> Result { Ok(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index ff334ecb..44e5a58e 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -3,9 +3,7 @@ //! This is the high-level and recommended module for MIDI message handling. The contained atom type can convert the raw MIDI message to rustic enumerations and back. //! //! If you want to have raw, low-level access to the messages, you should use the [raw module](../raw/index.html). -use atom::prelude::*; -use atom::space::{error::*, Terminated}; -use atom::AtomHandle; +use atom::atom_prelude::*; use std::convert::TryFrom; use urid::*; @@ -65,7 +63,7 @@ impl Atom for WMidiEvent { } #[inline] - fn init(writer: AtomSpaceWriter) -> Result { + fn write(writer: AtomSpaceWriter) -> Result { Ok(WMidiEventWriter { writer }) } } @@ -96,7 +94,7 @@ impl Atom for SystemExclusiveWMidiEvent { WMidiEvent::read(space) } - fn init(mut frame: AtomSpaceWriter) -> Result { + fn write(mut frame: AtomSpaceWriter) -> Result { frame.write_value(0xf0u8)?; Ok(Writer { From d37c11f41fefba4d964284cc2ed924ebd1d3c0d1 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 26 Sep 2021 15:09:02 +0200 Subject: [PATCH 41/54] Remove unsafe copy/clone impl on UnidentifiedAtom --- atom/src/unidentified.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/atom/src/unidentified.rs b/atom/src/unidentified.rs index c9fc216f..e3734e2f 100644 --- a/atom/src/unidentified.rs +++ b/atom/src/unidentified.rs @@ -6,7 +6,6 @@ use urid::URID; /// An atom of yet unknown type. /// /// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it. -#[derive(Clone, Copy)] #[repr(C)] pub struct UnidentifiedAtom { header: AtomHeader, From 6f9330dfc7198754377e983ffd1451706b0b7d37 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 27 Sep 2021 01:58:23 +0200 Subject: [PATCH 42/54] Add some more docs --- atom/src/lib.rs | 14 +++++-- atom/src/space/aligned.rs | 24 +++++++++--- atom/src/space/allocatable.rs | 72 ++++++++++++++++++++++++++++------- atom/src/space/atom_writer.rs | 12 +++--- atom/src/space/cursor.rs | 13 ++++--- atom/src/space/terminated.rs | 23 +++++------ atom/src/space/vec.rs | 12 +++--- atom/src/unidentified.rs | 3 ++ docs/metro/src/pipes.rs | 2 +- 9 files changed, 125 insertions(+), 50 deletions(-) diff --git a/atom/src/lib.rs b/atom/src/lib.rs index b26005ee..5ba7a82f 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -136,12 +136,14 @@ pub trait Atom: UriBound { /// /// The passed space exactly covers the body of the atom, excluding the header. /// - /// If the atom is malformed, this method returns `None`. + /// # Errors + /// This method may return any error if the atom in the given space is somehow malformed, or if + /// there wasn't enough space to read it properly. /// /// # Safety /// - /// The caller needs to ensure that the given [`Space`] contains a valid instance of this atom, - /// or the resulting `ReadHandle` will be completely invalid, and Undefined Behavior will happen. + /// The caller needs to ensure that the given [`AtomSpace`] contains a valid instance of this atom, + /// or the resulting `ReadHandle` will be completely invalid, triggering Undefined Behavior. unsafe fn read( body: &AtomSpace, ) -> Result<::Handle, AtomReadError>; @@ -153,7 +155,11 @@ pub trait Atom: UriBound { /// /// The frame of the atom was already initialized, containing the URID. /// - /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed. + /// # Errors + /// + /// This method may return an error if the buffer is out of space, or if any invalid state is + /// observed. In those cases, the written data may be incomplete and should be discarded. + /// fn write( writer: AtomSpaceWriter, ) -> Result<::Handle, AtomWriteError>; diff --git a/atom/src/space/aligned.rs b/atom/src/space/aligned.rs index 4eb3de65..3629642a 100644 --- a/atom/src/space/aligned.rs +++ b/atom/src/space/aligned.rs @@ -394,21 +394,35 @@ impl AlignedSpace { } } + /// Gets the contents as a slice of `T`s that are all assumed to be initialized. + /// + /// # Safety + /// + /// Calling this when the space's content is not yet fully initialized causes undefined behavior. + /// It is up to the caller to guarantee that the underlying buffer really is in an initialized state. #[inline] pub unsafe fn assume_init_slice(&self) -> &[T] { crate::util::assume_init_slice(self.as_uninit_slice()) } + /// Gets the contents as a mutable slice of `T`s that are all assumed to be initialized. + /// + /// # Safety + /// + /// Calling this when the space's content is not yet fully initialized causes undefined behavior. + /// It is up to the caller to guarantee that the underlying buffer really is in an initialized state. #[inline] pub unsafe fn assume_init_slice_mut(&mut self) -> &mut [T] { crate::util::assume_init_slice_mut(self.as_uninit_slice_mut()) } + /// An helper method that creates a new [`SpaceReader`] from the space's contents. #[inline] pub fn read(&self) -> SpaceReader { SpaceReader::new(self.as_bytes()) } + /// An helper method that creates a new [`Cursor`] from the mutable space's contents. #[inline] pub fn write(&mut self) -> SpaceCursor { SpaceCursor::new(self.as_bytes_mut()) @@ -560,13 +574,13 @@ mod tests { ); } - fn test_mut_space<'a>(mut space: impl SpaceWriter) { + fn test_mut_space(mut space: impl SpaceWriter) { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); let mut test_data: Vec = vec![0; 128]; - for i in 0..test_data.len() { - test_data[i] = i as u8; + for (i, data) in test_data.iter_mut().enumerate() { + *data = i as u8; } let written_data = space.write_bytes(test_data.as_slice()).unwrap(); @@ -595,8 +609,8 @@ mod tests { let mut atom_frame = AtomSpaceWriter::write_new(space, urids.chunk).unwrap(); let mut test_data: Vec = vec![0; 24]; - for i in 0..test_data.len() { - test_data[i] = i as u8; + for (i, data) in test_data.iter_mut().enumerate() { + *data = i as u8; } let written_data = atom_frame.write_bytes(&test_data).unwrap(); diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 54f8dbeb..6c8bdcba 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -6,30 +6,76 @@ use crate::space::error::AtomWriteError; use crate::space::terminated::Terminated; use core::mem::{size_of, size_of_val, MaybeUninit}; -/// A smart pointer that writes atom data to an internal slice. +/// The result of a [`SpaceWriter`](SpaceWriterImpl) allocation. /// -/// The methods provided by this trait are fairly minimalistic. More convenient writing methods are implemented for `dyn MutSpace`. +/// This structure allows simultaneous access to both the newly allocated slice, and all previously +/// allocated bytes. +pub struct SpaceWriterSplitAllocation<'a> { + pub previous: &'a mut [u8], + pub allocated: &'a mut [u8], +} + +/// An object-safe trait to allocate bytes from a contiguous buffer to write Atom data into. +/// +/// Implementors of this trait act like a sort of cursor, continuously +/// +/// This trait is very bare-bones, in order to be trait-object-safe. As an user, you probably want +/// to use the [`SpaceWriter`] trait, a child trait with many more utilities available, and with a +/// blanket implementation for all types that implement [`SpaceWriterImpl`]. +/// +/// The term "allocate" is used very loosely here, as even a simple cursor over a mutable byte +/// buffer (e.g. [`SpaceCursor`](crate::space::SpaceCursor)) can "allocate" bytes using this trait. /// +/// This trait is useful to abstract over many types of buffers, including ones than can track the +/// amount of allocated bytes into an atom header (i.e. [`AtomSpaceWriter`]). pub trait SpaceWriterImpl { + /// Allocates a new byte buffer of the requested size. A mutable reference to both the newly + /// allocated slice and all previously allocated bytes is returned (through [`SpaceWriterSplitAllocation`]), + /// allowing some implementations to update previous data as well. /// - /// # Safety + /// # Errors /// - /// While implementations MUST return a + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate.. /// /// # Panics /// - /// This function may panic if the given size, added to the length of the total allocated bytes - /// overflow an [`usize`]. - fn allocate_and_split(&mut self, size: usize) - -> Result<(&mut [u8], &mut [u8]), AtomWriteError>; + /// This function may panic if the given size, added to the length of the total allocated bytes, + /// overflows an [`usize`]. + fn allocate_and_split( + &mut self, + size: usize, + ) -> Result; + /// Rewinds the writer by a given amount of bytes, allowing to overwrite previously allocated + /// bytes. + /// + /// # Errors + /// + /// This method may return an error if `byte_count` is greater than the amount of all already + /// allocated bytes. + /// + /// # Safety + /// + /// Rewinding may allow other atoms to be overwritten, and thus completely invalidate their + /// contents and internal structure. The caller is responsible to ensure that the exposed data + /// is safe to be overwritten. unsafe fn rewind(&mut self, byte_count: usize) -> Result<(), AtomWriteError>; + /// Returns a slice pointing to the previously allocated bytes. fn allocated_bytes(&self) -> &[u8]; - fn allocated_bytes_mut(&mut self) -> &mut [u8]; + /// Returns a mutable slice pointing to the previously allocated bytes. + /// + /// # Safety + /// + /// Accessing allocated bytes may allow other atoms to be overwritten, and thus completely + /// invalidate their contents and internal structure. The caller is responsible to ensure that + /// the exposed data is safe to be overwritten. + unsafe fn allocated_bytes_mut(&mut self) -> &mut [u8]; + + /// Returns a slice pointing to the remaining, uninitialized bytes. fn remaining_bytes(&self) -> &[u8]; - fn remaining_bytes_mut(&mut self) -> &mut [u8]; } pub trait SpaceWriter: SpaceWriterImpl + Sized { @@ -38,9 +84,9 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. #[inline] fn allocate(&mut self, size: usize) -> Result<&mut [u8], AtomWriteError> { - let (_previous, allocated) = self.allocate_and_split(size)?; - assert_eq!(allocated.len(), size); - Ok(allocated) + let allocated = self.allocate_and_split(size)?; + assert_eq!(allocated.allocated.len(), size); + Ok(allocated.allocated) } #[inline] diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index b00b386e..a77c2b86 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,5 +1,7 @@ use crate::header::AtomHeader; -use crate::space::{error::AtomWriteError, AtomSpace, SpaceWriter, SpaceWriterImpl}; +use crate::space::{ + error::AtomWriteError, AtomSpace, SpaceWriter, SpaceWriterImpl, SpaceWriterSplitAllocation, +}; use urid::URID; /// A `MutSpace` that tracks the amount of allocated space in an atom header. @@ -55,12 +57,12 @@ impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { fn allocate_and_split( &mut self, size: usize, - ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { - let (previous, current) = self.parent.allocate_and_split(size)?; + ) -> Result { + let alloc = self.parent.allocate_and_split(size)?; let space = AtomSpace::from_bytes_mut( // PANIC: We rely on the parent allocator not shifting bytes around - &mut previous[self.atom_header_index..], + &mut alloc.previous[self.atom_header_index..], )?; // SAFETY: We already initialized that part of the buffer @@ -71,7 +73,7 @@ impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { // SAFETY: We just allocated `size` additional bytes for the body, we know they are properly allocated unsafe { header.set_size_of_body(header.size_of_body() + size) }; - Ok((previous, current)) + Ok(alloc) } #[inline] diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 7c59d710..e37c6f6d 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,5 +1,5 @@ use crate::space::error::AtomWriteError; -use crate::space::SpaceWriterImpl; +use crate::space::{SpaceWriterImpl, SpaceWriterSplitAllocation}; pub struct SpaceCursor<'a> { data: &'a mut [u8], @@ -20,12 +20,12 @@ impl<'a> SpaceWriterImpl for SpaceCursor<'a> { fn allocate_and_split( &mut self, size: usize, - ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { + ) -> Result { let allocated_length = self.allocated_length; let data_len = self.data.len(); - let (allocated, allocatable) = self.data.split_at_mut(allocated_length); + let (previous, allocatable) = self.data.split_at_mut(allocated_length); - let new_allocation = allocatable + let allocated = allocatable .get_mut(..size) .ok_or(AtomWriteError::OutOfSpace { used: allocated_length, @@ -38,7 +38,10 @@ impl<'a> SpaceWriterImpl for SpaceCursor<'a> { .checked_add(size) .expect("Allocation overflow"); - Ok((allocated, new_allocation)) + Ok(SpaceWriterSplitAllocation { + previous, + allocated, + }) } #[inline] diff --git a/atom/src/space/terminated.rs b/atom/src/space/terminated.rs index 0ef9df1e..84eb862a 100644 --- a/atom/src/space/terminated.rs +++ b/atom/src/space/terminated.rs @@ -1,5 +1,5 @@ use crate::space::error::AtomWriteError; -use crate::space::SpaceWriterImpl; +use crate::space::{SpaceWriterImpl, SpaceWriterSplitAllocation}; /// An helper space writer, that wraps an existing writer and makes sure all writes are /// terminated with a given terminator byte. @@ -51,17 +51,23 @@ impl SpaceWriterImpl for Terminated { fn allocate_and_split( &mut self, size: usize, - ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { + ) -> Result { if self.wrote_terminator_byte { // SAFETY: We checked we already wrote the terminator byte, and it is safe to be overwritten unsafe { self.inner.rewind(1)? }; } - let (allocated, allocatable) = self.inner.allocate_and_split(size + 1)?; - allocatable[size] = self.terminator; + let SpaceWriterSplitAllocation { + previous, + allocated, + } = self.inner.allocate_and_split(size + 1)?; + allocated[size] = self.terminator; self.wrote_terminator_byte = true; - Ok((allocated, &mut allocatable[..size])) + Ok(SpaceWriterSplitAllocation { + previous, + allocated: &mut allocated[..size], + }) } #[inline] @@ -75,7 +81,7 @@ impl SpaceWriterImpl for Terminated { } #[inline] - fn allocated_bytes_mut(&mut self) -> &mut [u8] { + unsafe fn allocated_bytes_mut(&mut self) -> &mut [u8] { self.inner.allocated_bytes_mut() } @@ -83,9 +89,4 @@ impl SpaceWriterImpl for Terminated { fn remaining_bytes(&self) -> &[u8] { self.inner.remaining_bytes() } - - #[inline] - fn remaining_bytes_mut(&mut self) -> &mut [u8] { - self.inner.remaining_bytes_mut() - } } diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 5e608376..51123502 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,7 +1,7 @@ #![deny(unsafe_code)] use crate::space::error::AtomWriteError; -use crate::space::{AlignedSpace, SpaceWriterImpl}; +use crate::space::{AlignedSpace, SpaceWriterImpl, SpaceWriterSplitAllocation}; use std::mem::MaybeUninit; use std::ops::Range; @@ -46,7 +46,7 @@ impl VecSpace { fn reallocate_bytes_mut( &mut self, byte_range: Range, - ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { + ) -> Result { let byte_len = self.inner.len() * std::mem::size_of::(); let max = byte_range.start.max(byte_range.end); @@ -60,10 +60,10 @@ impl VecSpace { // PANIC: We just resized to the accommodate the maximum value in the given range. let (previous, allocatable) = bytes.split_at_mut(byte_range.start); - Ok(( + Ok(SpaceWriterSplitAllocation { previous, - &mut allocatable[..byte_range.end - byte_range.start], - )) + allocated: &mut allocatable[..byte_range.end - byte_range.start], + }) } #[inline] @@ -84,7 +84,7 @@ impl<'vec, T: Copy + 'static> SpaceWriterImpl for VecSpaceCursor<'vec, T> { fn allocate_and_split( &mut self, size: usize, - ) -> Result<(&mut [u8], &mut [u8]), AtomWriteError> { + ) -> Result { let end = self .allocated_length .checked_add(size) diff --git a/atom/src/unidentified.rs b/atom/src/unidentified.rs index e3734e2f..751936d8 100644 --- a/atom/src/unidentified.rs +++ b/atom/src/unidentified.rs @@ -14,6 +14,9 @@ pub struct UnidentifiedAtom { impl UnidentifiedAtom { /// Construct a new unidentified atom. /// + /// # Errors + /// This methods returns a read error if the given space is too small to accommodate the atom header. + /// /// # Safety /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. diff --git a/docs/metro/src/pipes.rs b/docs/metro/src/pipes.rs index b72553a5..f51fd252 100644 --- a/docs/metro/src/pipes.rs +++ b/docs/metro/src/pipes.rs @@ -43,7 +43,7 @@ where fn test_sampler() { let sample: Vec = vec![1, 2, 3, 4]; let mut sampler = Sampler::new(sample); - for i in (0..32).chain(32..0) { + for i in (0..32).chain((0..32).rev()) { assert_eq!((i % 4 + 1) as u8, sampler.next(i)); } } From a340168d126c0119cd74db4782a9986f34cb3151 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Mon, 27 Sep 2021 18:45:57 +0200 Subject: [PATCH 43/54] Write more docs --- atom/src/atoms/chunk.rs | 2 +- atom/src/atoms/object.rs | 4 +- atom/src/atoms/scalar.rs | 2 +- atom/src/atoms/sequence.rs | 8 ++-- atom/src/atoms/string.rs | 4 +- atom/src/atoms/tuple.rs | 4 +- atom/src/atoms/vector.rs | 2 +- atom/src/port.rs | 4 +- atom/src/space/allocatable.rs | 80 ++++++++++++++++++++++++++++------ atom/src/space/atom_writer.rs | 11 +---- atom/src/space/cursor.rs | 7 +-- atom/src/space/vec.rs | 11 +---- atom/tests/atom_integration.rs | 4 +- midi/src/wmidi_binding.rs | 4 +- state/src/raw.rs | 2 +- 15 files changed, 91 insertions(+), 58 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 84231c5f..771955cd 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -89,7 +89,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.chunk).unwrap(); + let mut writer = space.write_atom(urids.chunk).unwrap(); let data = writer.allocate(SLICE_LENGTH).unwrap(); for (i, value) in data.into_iter().enumerate() { diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 3f36ae92..39c7eb05 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -219,7 +219,7 @@ impl<'a> ObjectWriter<'a> { child_urid: URID, ) -> Result<::Handle, AtomWriteError> { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; - self.frame.init_atom(child_urid) + self.frame.write_atom(child_urid) } /// Initialize a new property. @@ -233,7 +233,7 @@ impl<'a> ObjectWriter<'a> { child_urid: URID, ) -> Result<::Handle, AtomWriteError> { Property::write_header(&mut self.frame, key, None::>)?; - self.frame.init_atom(child_urid) + self.frame.write_atom(child_urid) } } diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 2651bdd6..38852f30 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -205,7 +205,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - space.init_atom(urid).unwrap().set(value).unwrap(); + space.write_atom(urid).unwrap().set(value).unwrap(); } // verifying diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 6f394912..0b691ff2 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -222,7 +222,7 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { Ok(()) } - /// Initialize an event. + /// Initialize an event. /// /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. pub fn new_event( @@ -231,7 +231,7 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { urid: URID, ) -> Result<::Handle, AtomWriteError> { self.write_time_stamp(time_stamp)?; - self.writer.init_atom(urid) + self.writer.write_atom(urid) } /// Forward an unidentified atom to the sequence. @@ -246,7 +246,7 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { ) -> Result<(), AtomWriteError> { self.write_time_stamp(time_stamp)?; - self.writer.forward_atom(atom)?; + self.writer.copy_atom(atom)?; Ok(()) } @@ -277,7 +277,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space - .init_atom(urids.atom.sequence) + .write_atom(urids.atom.sequence) .unwrap() .with_unit(urids.units.frame) .unwrap(); diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 62946afa..8604ab74 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -241,7 +241,7 @@ mod tests { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space - .init_atom(urids.atom.literal) + .write_atom(urids.atom.literal) .unwrap() .write_info(LiteralInfo::Language(urids.german.into_general())) .unwrap(); @@ -302,7 +302,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.string).unwrap(); + let mut writer = space.write_atom(urids.string).unwrap(); writer.append(SAMPLE0).unwrap(); writer.append(SAMPLE1).unwrap(); } diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 423691f8..dfc64ce2 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -100,7 +100,7 @@ impl<'a> TupleWriter<'a> { &mut self, child_urid: URID, ) -> Result<::Handle, AtomWriteError> { - self.frame.init_atom(child_urid) + self.frame.write_atom(child_urid) } } @@ -123,7 +123,7 @@ mod tests { // writing { let mut cursor = raw_space.write(); - let mut writer = cursor.init_atom(urids.tuple).unwrap(); + let mut writer = cursor.write_atom(urids.tuple).unwrap(); { let mut vector_writer = writer .init(urids.vector) diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 23d79581..ff4390b2 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -193,7 +193,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); let mut writer = space - .init_atom(urids.vector) + .write_atom(urids.vector) .unwrap() .of_type(urids.int) .unwrap(); diff --git a/atom/src/port.rs b/atom/src/port.rs index 91ed060c..b3fa8183 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -94,7 +94,7 @@ impl<'a> PortWriter<'a> { let space: &'write mut SpaceCursor<'write> = unsafe { ::core::mem::transmute::<_, &'write mut SpaceCursor<'write>>(&mut self.space) }; - space.init_atom(urid) + space.write_atom(urid) } } @@ -143,7 +143,7 @@ mod tests { // writing a chunk to indicate the size of the space. { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urids.chunk).unwrap(); + let mut writer = space.write_atom(urids.chunk).unwrap(); writer.allocate(256 - size_of::()).unwrap(); } diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocatable.rs index 6c8bdcba..9e6b2db2 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocatable.rs @@ -36,7 +36,7 @@ pub trait SpaceWriterImpl { /// # Errors /// /// This method may return an error if the writer ran out of space in its internal buffer, and - /// is unable to reallocate.. + /// is unable to reallocate the buffer. /// /// # Panics /// @@ -79,9 +79,12 @@ pub trait SpaceWriterImpl { } pub trait SpaceWriter: SpaceWriterImpl + Sized { - /// Try to allocate memory on the internal data slice. + /// Allocates and returns a new mutable byte buffer of the requested size, in bytes. /// - /// After the memory has been allocated, the `MutSpace` can not allocate it again. The next allocated slice is directly behind it. + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer. #[inline] fn allocate(&mut self, size: usize) -> Result<&mut [u8], AtomWriteError> { let allocated = self.allocate_and_split(size)?; @@ -89,14 +92,16 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(allocated.allocated) } - #[inline] - fn allocate_padding_for(&mut self) -> Result<(), AtomWriteError> { - let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; - self.allocate(required_padding)?; - - Ok(()) - } - + /// Allocates and returns a new aligned mutable byte buffer of the requested size, in bytes. + /// + /// The resulting buffer is guaranteed to be aligned to the alignment requirements of the given + /// type `T`, meaning a value of type `T` can be safely written into it directly. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. #[inline] fn allocate_aligned( &mut self, @@ -108,6 +113,17 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(AlignedSpace::align_from_bytes_mut(raw)?) } + /// Allocates room in the byte buffer for a single value of type `T`. + /// + /// A mutable reference to the allocated buffer is returned as a + /// [`MaybeUninit`](core::mem::maybe_uninit::MaybeUninit), + /// as the resulting memory buffer is most likely going to be uninitialized. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. #[inline] fn allocate_value(&mut self) -> Result<&mut MaybeUninit, AtomWriteError> { let space = self.allocate_aligned(size_of::>())?; @@ -115,6 +131,17 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(unsafe { space.as_uninit_slice_mut().get_unchecked_mut(0) }) } + /// Allocates room in the byte buffer for multiple values of type `T`. + /// + /// A mutable reference to the allocated buffer is returned as a slice of + /// [`MaybeUninit`](core::mem::maybe_uninit::MaybeUninit)s, + /// as the resulting memory buffer is most likely going to be uninitialized. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. #[inline] fn allocate_values( &mut self, @@ -124,8 +151,18 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(space.as_uninit_slice_mut()) } + /// Writes an atom of a given type into the buffer. + /// + /// This method only initializes the new Atom header with the given type, tracking its + /// size, and returns the [writer](crate::Atom::write) associated to the given Atom type. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. #[inline] - fn init_atom( + fn write_atom( &mut self, atom_type: URID, ) -> Result<::Handle, AtomWriteError> { @@ -133,8 +170,18 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { A::write(space) } + /// Copies an already fully initialized atom of any type into the buffer. + /// + /// This method will simply copy the atom's bytes into the buffer, unchanged, and unaware of its + /// internal representation. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. #[inline] - fn forward_atom( + fn copy_atom( &mut self, atom: &UnidentifiedAtom, ) -> Result<&mut UnidentifiedAtom, AtomWriteError> { @@ -181,6 +228,11 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(unsafe { &mut *(space as *mut [_] as *mut [T]) }) } + /// Makes all further operations from this writer write a given terminator byte. + /// + /// This method is a simple helper for [`Terminated::new`](Terminated::new) + /// + /// See the documentation for [`Terminated`](Terminated) for more information. #[inline] fn terminated(self, terminator: u8) -> Terminated { Terminated::new(self, terminator) @@ -210,7 +262,7 @@ mod tests { assert_eq!(31, cursor.remaining_bytes().len()); { - cursor.init_atom(INT_URID).unwrap().set(69).unwrap(); + cursor.write_atom(INT_URID).unwrap().set(69).unwrap(); assert_eq!(8, cursor.remaining_bytes().len()); } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index a77c2b86..98a326dd 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -25,9 +25,7 @@ impl<'a> AtomSpaceWriter<'a> { #[inline] fn atom_header_mut(&mut self) -> &mut AtomHeader { - let previous = self - .parent - .allocated_bytes_mut() + let previous = unsafe { self.parent.allocated_bytes_mut() } .get_mut(self.atom_header_index..) .unwrap(); let space = AtomSpace::from_bytes_mut(previous).unwrap(); @@ -93,7 +91,7 @@ impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { } #[inline] - fn allocated_bytes_mut(&mut self) -> &mut [u8] { + unsafe fn allocated_bytes_mut(&mut self) -> &mut [u8] { self.parent.allocated_bytes_mut() } @@ -101,11 +99,6 @@ impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { fn remaining_bytes(&self) -> &[u8] { self.parent.remaining_bytes() } - - #[inline] - fn remaining_bytes_mut(&mut self) -> &mut [u8] { - self.parent.remaining_bytes_mut() - } } #[cfg(test)] diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index e37c6f6d..d0da2255 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -64,7 +64,7 @@ impl<'a> SpaceWriterImpl for SpaceCursor<'a> { } #[inline] - fn allocated_bytes_mut(&mut self) -> &mut [u8] { + unsafe fn allocated_bytes_mut(&mut self) -> &mut [u8] { &mut self.data[..self.allocated_length] } @@ -72,9 +72,4 @@ impl<'a> SpaceWriterImpl for SpaceCursor<'a> { fn remaining_bytes(&self) -> &[u8] { &self.data[self.allocated_length..] } - - #[inline] - fn remaining_bytes_mut(&mut self) -> &mut [u8] { - &mut self.data[self.allocated_length..] - } } diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 51123502..98ea1728 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -120,7 +120,8 @@ impl<'vec, T: Copy + 'static> SpaceWriterImpl for VecSpaceCursor<'vec, T> { } #[inline] - fn allocated_bytes_mut(&mut self) -> &mut [u8] { + #[allow(unsafe_code)] + unsafe fn allocated_bytes_mut(&mut self) -> &mut [u8] { &mut self.vec.as_bytes_mut()[..self.allocated_length] } @@ -131,14 +132,6 @@ impl<'vec, T: Copy + 'static> SpaceWriterImpl for VecSpaceCursor<'vec, T> { .get(self.allocated_length..) .unwrap_or(&[]) } - - #[inline] - fn remaining_bytes_mut(&mut self) -> &mut [u8] { - self.vec - .as_bytes_mut() - .get_mut(self.allocated_length..) - .unwrap_or(&mut []) - } } #[cfg(test)] diff --git a/atom/tests/atom_integration.rs b/atom/tests/atom_integration.rs index 3f0b5966..2215705e 100644 --- a/atom/tests/atom_integration.rs +++ b/atom/tests/atom_integration.rs @@ -108,7 +108,7 @@ fn main() { { let mut space = SpaceCursor::new(input_atom_space.as_bytes_mut()); let mut writer = space - .init_atom(urids.atom.sequence) + .write_atom(urids.atom.sequence) .unwrap() .with_unit(urids.units.frame) .unwrap(); @@ -134,7 +134,7 @@ fn main() { { let mut space = SpaceCursor::new(output_atom_space.as_bytes_mut()); space - .init_atom(urids.atom.chunk) + .write_atom(urids.atom.chunk) .unwrap() .allocate(256 - size_of::()) .unwrap(); diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 44e5a58e..7e0f5caa 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -149,7 +149,7 @@ mod tests { { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); space - .init_atom(urid) + .write_atom(urid) .unwrap() .set(reference_message.clone()) .unwrap(); @@ -188,7 +188,7 @@ mod tests { // writing { let mut space = SpaceCursor::new(raw_space.as_bytes_mut()); - let mut writer = space.init_atom(urid).unwrap(); + let mut writer = space.write_atom(urid).unwrap(); writer.write_raw(&[1, 2, 3, 4]).unwrap(); } diff --git a/state/src/raw.rs b/state/src/raw.rs index f0bd5923..36af76b8 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -123,7 +123,7 @@ impl<'a> StatePropertyWriter<'a> { ) -> Result<>::Handle, StateErr> { if !self.initialized { self.initialized = true; - self.cursor.init_atom(urid).map_err(|_| StateErr::Unknown) + self.cursor.write_atom(urid).map_err(|_| StateErr::Unknown) } else { Err(StateErr::Unknown) } From 16a5c0aae0da241d4dde950e22a6c7d22c83ad46 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 29 Sep 2021 02:06:36 +0200 Subject: [PATCH 44/54] Rename AtomSpaceWriter to AtomWriter, and add more docs --- atom/src/atoms/chunk.rs | 10 +- atom/src/atoms/object.rs | 10 +- atom/src/atoms/scalar.rs | 8 +- atom/src/atoms/sequence.rs | 6 +- atom/src/atoms/string.rs | 8 +- atom/src/atoms/tuple.rs | 4 +- atom/src/atoms/vector.rs | 6 +- atom/src/lib.rs | 6 +- atom/src/space.rs | 6 +- atom/src/space/aligned.rs | 10 +- .../space/{allocatable.rs => allocator.rs} | 114 +++++++++++++----- atom/src/space/atom_writer.rs | 48 ++++++-- atom/src/space/cursor.rs | 4 +- atom/src/space/terminated.rs | 10 +- atom/src/space/vec.rs | 23 +++- midi/src/raw.rs | 4 +- midi/src/wmidi_binding.rs | 8 +- 17 files changed, 196 insertions(+), 89 deletions(-) rename atom/src/space/{allocatable.rs => allocator.rs} (77%) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 771955cd..042ea7c7 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -7,7 +7,7 @@ //! use lv2_core::prelude::*; //! use lv2_atom::prelude::*; //! -//! use lv2_atom::space::{AtomSpace, AtomSpaceWriter, SpaceWriter}; +//! use lv2_atom::space::{AtomSpace, AtomWriter, SpaceWriter}; //! //! #[derive(PortCollection)] //! struct MyPorts { @@ -17,7 +17,7 @@ //! //! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) { //! let in_chunk: &AtomSpace = ports.input.read(urids.chunk).unwrap(); -//! let mut out_chunk: AtomSpaceWriter = ports.output.write(urids.chunk).unwrap(); +//! let mut out_chunk: AtomWriter = ports.output.write(urids.chunk).unwrap(); //! //! let bytes = in_chunk.as_bytes(); //! out_chunk.write_bytes(bytes).unwrap(); @@ -29,7 +29,7 @@ //! [http://lv2plug.in/ns/ext/atom/atom.html#Chunk](http://lv2plug.in/ns/ext/atom/atom.html#Chunk) use crate::space::error::{AtomReadError, AtomWriteError}; use crate::space::*; -use crate::AtomSpaceWriter; +use crate::AtomWriter; use crate::{Atom, AtomHandle}; use urid::UriBound; @@ -49,7 +49,7 @@ impl<'a> AtomHandle<'a> for ChunkReaderHandle { pub struct ChunkWriterHandle; impl<'a> AtomHandle<'a> for ChunkWriterHandle { - type Handle = AtomSpaceWriter<'a>; + type Handle = AtomWriter<'a>; } impl Atom for Chunk { @@ -65,7 +65,7 @@ impl Atom for Chunk { #[inline] fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(frame) } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 39c7eb05..28c766be 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -107,7 +107,7 @@ impl<'a> AtomHandle<'a> for ObjectWriterHandle { } pub struct ObjectHeaderWriter<'a> { - frame: AtomSpaceWriter<'a>, + frame: AtomWriter<'a>, } impl<'a> ObjectHeaderWriter<'a> { @@ -147,7 +147,7 @@ impl Atom for Object { } fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(ObjectHeaderWriter { frame }) } @@ -177,7 +177,7 @@ impl Atom for Blank { #[inline] fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Object::write(frame) } @@ -205,7 +205,7 @@ impl<'a> Iterator for ObjectReader<'a> { /// /// This handle is a safeguard to assure that a object is always a series of properties. pub struct ObjectWriter<'a> { - frame: AtomSpaceWriter<'a>, + frame: AtomWriter<'a>, } impl<'a> ObjectWriter<'a> { @@ -346,7 +346,7 @@ mod tests { // writing { let mut cursor = SpaceCursor::new(raw_space.as_bytes_mut()); - let frame = AtomSpaceWriter::write_new(&mut cursor, urids.object).unwrap(); + let frame = AtomWriter::write_new(&mut cursor, urids.object).unwrap(); let mut writer = Object::write(frame) .unwrap() .write_header(ObjectHeader { diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 38852f30..f1b3ba12 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -55,14 +55,12 @@ pub trait ScalarAtom: UriBound { /// /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. #[inline] - fn write_scalar( - frame: AtomSpaceWriter, - ) -> Result, AtomWriteError> { + fn write_scalar(frame: AtomWriter) -> Result, AtomWriteError> { Ok(ScalarWriter(frame, PhantomData)) } } -pub struct ScalarWriter<'a, T: Copy + 'static>(AtomSpaceWriter<'a>, PhantomData); +pub struct ScalarWriter<'a, T: Copy + 'static>(AtomWriter<'a>, PhantomData); impl<'a, T: Copy + 'static> ScalarWriter<'a, T> { #[inline] @@ -101,7 +99,7 @@ impl Atom for A { } fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { ::write_scalar(frame) } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 0b691ff2..c6d1a4a5 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -124,7 +124,7 @@ impl<'a> SequenceHeaderReader<'a> { } pub struct SequenceHeaderWriter<'a> { - writer: AtomSpaceWriter<'a>, + writer: AtomWriter<'a>, } impl<'a> SequenceHeaderWriter<'a> { @@ -160,7 +160,7 @@ impl Atom for Sequence { } fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(SequenceHeaderWriter { writer: frame }) } @@ -195,7 +195,7 @@ impl<'a, U: SequenceUnit> Iterator for SequenceIterator<'a, U> { /// The writing handle for sequences. pub struct SequenceWriter<'a, U: SequenceUnit> { - writer: AtomSpaceWriter<'a>, + writer: AtomWriter<'a>, last_stamp: Option, } diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 8604ab74..d810e18d 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -76,7 +76,7 @@ impl LiteralInfo { } pub struct LiteralInfoWriter<'a> { - writer: AtomSpaceWriter<'a>, + writer: AtomWriter<'a>, } impl<'a> LiteralInfoWriter<'a> { @@ -128,7 +128,7 @@ impl Atom for Literal { #[inline] fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(LiteralInfoWriter { writer: frame }) } @@ -178,7 +178,7 @@ impl Atom for String { } fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(StringWriter { writer: frame.terminated(0), @@ -188,7 +188,7 @@ impl Atom for String { /// Handle to append strings to a string or literal. pub struct StringWriter<'a> { - writer: Terminated>, + writer: Terminated>, } impl<'a> StringWriter<'a> { diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index dfc64ce2..517606e3 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -67,7 +67,7 @@ impl Atom for Tuple { } fn write( - frame: AtomSpaceWriter, + frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(TupleWriter { frame }) } @@ -91,7 +91,7 @@ impl<'a> Iterator for TupleIterator<'a> { /// The writing handle to add atoms to a tuple. pub struct TupleWriter<'a> { - frame: AtomSpaceWriter<'a>, + frame: AtomWriter<'a>, } impl<'a> TupleWriter<'a> { diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index ff4390b2..24cf4be6 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -93,7 +93,7 @@ impl<'a> VectorReader<'a> { } pub struct VectorTypeWriter<'a> { - writer: AtomSpaceWriter<'a>, + writer: AtomWriter<'a>, } impl<'a> VectorTypeWriter<'a> { @@ -129,7 +129,7 @@ impl Atom for Vector { } fn write( - writer: AtomSpaceWriter, + writer: AtomWriter, ) -> Result<::Handle, AtomWriteError> { Ok(VectorTypeWriter { writer }) } @@ -139,7 +139,7 @@ impl Atom for Vector { /// /// This works by allocating a slice of memory behind the vector and then writing your data to it. pub struct VectorWriter<'a, A: ScalarAtom> { - writer: AtomSpaceWriter<'a>, + writer: AtomWriter<'a>, type_: PhantomData, } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 5ba7a82f..67071385 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -106,8 +106,8 @@ pub mod atom_prelude { pub use crate::space::{ error::{AlignmentError, AtomError, AtomReadError, AtomWriteError}, - AlignedSpace, AtomSpace, AtomSpaceWriter, SpaceCursor, SpaceWriter, SpaceWriterImpl, - Terminated, VecSpace, + AlignedSpace, AtomSpace, AtomWriter, SpaceAllocator, SpaceCursor, SpaceWriter, Terminated, + VecSpace, }; pub use crate::{Atom, AtomHandle, AtomHeader, UnidentifiedAtom}; } @@ -161,6 +161,6 @@ pub trait Atom: UriBound { /// observed. In those cases, the written data may be incomplete and should be discarded. /// fn write( - writer: AtomSpaceWriter, + writer: AtomWriter, ) -> Result<::Handle, AtomWriteError>; } diff --git a/atom/src/space.rs b/atom/src/space.rs index aeb30716..dae9b714 100644 --- a/atom/src/space.rs +++ b/atom/src/space.rs @@ -1,7 +1,7 @@ //! A collection of tools to assist reading and writing custom Atom types in Atom byte buffers (referred as **Spaces**). mod aligned; -mod allocatable; +mod allocator; mod atom_writer; mod cursor; pub mod error; @@ -10,8 +10,8 @@ mod terminated; mod vec; pub use aligned::{AlignedSpace, AtomSpace}; -pub use allocatable::*; -pub use atom_writer::AtomSpaceWriter; +pub use allocator::*; +pub use atom_writer::AtomWriter; pub use cursor::SpaceCursor; pub use reader::SpaceReader; pub use terminated::Terminated; diff --git a/atom/src/space/aligned.rs b/atom/src/space/aligned.rs index 3629642a..32118450 100644 --- a/atom/src/space/aligned.rs +++ b/atom/src/space/aligned.rs @@ -369,7 +369,7 @@ impl AlignedSpace { /// The resulting slice contains as many values as can fit in the original space. /// This means there might be less bytes in this slice than in this space, or zero if the space is too small for a single value. #[inline] - pub(crate) fn as_uninit_slice(&self) -> &[MaybeUninit] { + pub fn as_uninit_slice(&self) -> &[MaybeUninit] { // SAFETY: This type ensures alignment, so casting aligned bytes to uninitialized memory is safe. unsafe { ::core::slice::from_raw_parts( @@ -384,7 +384,7 @@ impl AlignedSpace { /// The resulting slice contains as many values as can fit in the original space. /// This means there might be less bytes in this slice than in this space, or zero if the space is too small for a single value. #[inline] - pub(crate) fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + pub fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: This type ensures alignment, so casting aligned bytes to uninitialized memory is safe. unsafe { ::core::slice::from_raw_parts_mut( @@ -574,7 +574,7 @@ mod tests { ); } - fn test_mut_space(mut space: impl SpaceWriter) { + fn test_writer(mut space: impl SpaceWriter) { let map = HashURIDMapper::new(); let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap(); @@ -606,7 +606,7 @@ mod tests { { let space: &mut _ = &mut space; - let mut atom_frame = AtomSpaceWriter::write_new(space, urids.chunk).unwrap(); + let mut atom_frame = AtomWriter::write_new(space, urids.chunk).unwrap(); let mut test_data: Vec = vec![0; 24]; for (i, data) in test_data.iter_mut().enumerate() { @@ -634,7 +634,7 @@ mod tests { let mut memory = [0; MEMORY_SIZE]; let cursor = SpaceCursor::new(&mut memory[..]); - test_mut_space(cursor); + test_writer(cursor); } #[test] diff --git a/atom/src/space/allocatable.rs b/atom/src/space/allocator.rs similarity index 77% rename from atom/src/space/allocatable.rs rename to atom/src/space/allocator.rs index 9e6b2db2..23cf10cf 100644 --- a/atom/src/space/allocatable.rs +++ b/atom/src/space/allocator.rs @@ -1,4 +1,4 @@ -use crate::space::{AlignedSpace, AtomSpaceWriter}; +use crate::space::{AlignedSpace, AtomWriter}; use crate::{Atom, AtomHandle, UnidentifiedAtom}; use urid::URID; @@ -6,7 +6,7 @@ use crate::space::error::AtomWriteError; use crate::space::terminated::Terminated; use core::mem::{size_of, size_of_val, MaybeUninit}; -/// The result of a [`SpaceWriter`](SpaceWriterImpl) allocation. +/// The result of a [`SpaceAllocator`](SpaceAllocator) allocation. /// /// This structure allows simultaneous access to both the newly allocated slice, and all previously /// allocated bytes. @@ -17,18 +17,19 @@ pub struct SpaceWriterSplitAllocation<'a> { /// An object-safe trait to allocate bytes from a contiguous buffer to write Atom data into. /// -/// Implementors of this trait act like a sort of cursor, continuously +/// Implementors of this trait act like a sort of cursor, each allocation being contiguous with the +/// previous one. /// /// This trait is very bare-bones, in order to be trait-object-safe. As an user, you probably want /// to use the [`SpaceWriter`] trait, a child trait with many more utilities available, and with a -/// blanket implementation for all types that implement [`SpaceWriterImpl`]. +/// blanket implementation for all types that implement [`SpaceAllocator`]. /// /// The term "allocate" is used very loosely here, as even a simple cursor over a mutable byte /// buffer (e.g. [`SpaceCursor`](crate::space::SpaceCursor)) can "allocate" bytes using this trait. /// /// This trait is useful to abstract over many types of buffers, including ones than can track the /// amount of allocated bytes into an atom header (i.e. [`AtomSpaceWriter`]). -pub trait SpaceWriterImpl { +pub trait SpaceAllocator { /// Allocates a new byte buffer of the requested size. A mutable reference to both the newly /// allocated slice and all previously allocated bytes is returned (through [`SpaceWriterSplitAllocation`]), /// allowing some implementations to update previous data as well. @@ -78,9 +79,21 @@ pub trait SpaceWriterImpl { fn remaining_bytes(&self) -> &[u8]; } -pub trait SpaceWriter: SpaceWriterImpl + Sized { +pub trait SpaceWriter: SpaceAllocator + Sized { /// Allocates and returns a new mutable byte buffer of the requested size, in bytes. /// + /// # Example + /// + /// ``` + /// use lv2_atom::atom_prelude::*; + /// + /// let mut buffer = vec![0; 64]; + /// let mut writer = SpaceCursor::new(&mut buffer); + /// + /// let allocated = writer.allocate(5).unwrap(); + /// assert_eq!(allocated.len(), 5); + /// ``` + /// /// # Errors /// /// This method may return an error if the writer ran out of space in its internal buffer, and @@ -97,6 +110,18 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { /// The resulting buffer is guaranteed to be aligned to the alignment requirements of the given /// type `T`, meaning a value of type `T` can be safely written into it directly. /// + /// # Example + /// + /// ``` + /// use lv2_atom::atom_prelude::*; + /// + /// let mut buffer = vec![0; 64]; + /// let mut writer = SpaceCursor::new(&mut buffer); + /// + /// let allocated = writer.allocate_aligned(5).unwrap(); + /// assert_eq!(allocated.len(), 5); + /// ``` + /// /// # Errors /// /// This method may return an error if the writer ran out of space in its internal buffer, and @@ -105,10 +130,10 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { #[inline] fn allocate_aligned( &mut self, - size: usize, + byte_size: usize, ) -> Result<&mut AlignedSpace, AtomWriteError> { let required_padding = crate::util::try_padding_for::(self.remaining_bytes())?; - let raw = self.allocate(size + required_padding)?; + let raw = self.allocate(byte_size + required_padding)?; Ok(AlignedSpace::align_from_bytes_mut(raw)?) } @@ -119,6 +144,20 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { /// [`MaybeUninit`](core::mem::maybe_uninit::MaybeUninit), /// as the resulting memory buffer is most likely going to be uninitialized. /// + /// # Example + /// + /// ``` + /// use lv2_atom::atom_prelude::*; + /// use std::mem::MaybeUninit; + /// + /// let mut buffer = vec![0; 64]; + /// let mut writer = SpaceCursor::new(&mut buffer); + /// + /// let allocated = writer.allocate_value().unwrap(); + /// *allocated = MaybeUninit::new(42u32); + /// assert_eq!(unsafe { allocated.assume_init() }, 42); + /// ``` + /// /// # Errors /// /// This method may return an error if the writer ran out of space in its internal buffer, and @@ -166,7 +205,7 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { &mut self, atom_type: URID, ) -> Result<::Handle, AtomWriteError> { - let space = AtomSpaceWriter::write_new(self, atom_type)?; + let space = AtomWriter::write_new(self, atom_type)?; A::write(space) } @@ -194,6 +233,11 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { unsafe { UnidentifiedAtom::from_space_mut(resulting_space) } } + /// Writes the given bytes into the buffer. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer. #[inline] fn write_bytes(&mut self, bytes: &[u8]) -> Result<&mut [u8], AtomWriteError> { let space = self.allocate(bytes.len())?; @@ -201,8 +245,15 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(space) } + /// Writes the given value into the buffer. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. #[inline] - fn write_value(&mut self, value: T) -> Result<&mut T, AtomWriteError> + fn write_value(&mut self, value: T) -> Result<&mut T, AtomWriteError> where T: Copy + Sized + 'static, { @@ -213,6 +264,13 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { Ok(crate::util::write_uninit(space, value)) } + /// Writes the given values into the buffer. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. fn write_values(&mut self, values: &[T]) -> Result<&mut [T], AtomWriteError> where T: Copy + Sized + 'static, @@ -239,7 +297,7 @@ pub trait SpaceWriter: SpaceWriterImpl + Sized { } } -impl SpaceWriter for H where H: SpaceWriterImpl {} +impl SpaceWriter for H where H: SpaceAllocator {} #[cfg(test)] mod tests { @@ -247,33 +305,33 @@ mod tests { use crate::prelude::*; use urid::URID; - // SAFETY: this is just for testing, values aren't actually read using this URID. - const INT_URID: URID = unsafe { URID::new_unchecked(5) }; - #[test] - fn test_init_atom_lifetimes() { - let mut space = VecSpace::::new_with_capacity(4); - assert_eq!(space.as_bytes().as_ptr() as usize % 8, 0); // TODO: move this, this is a test for boxed + fn test_write_value() { + let mut space = vec![0; 32]; - let mut cursor = SpaceCursor::new(space.as_bytes_mut()); // The pointer that is going to be moved as we keep writing. + let mut cursor = SpaceCursor::new(&mut space); let new_value = cursor.write_value(42u8).unwrap(); assert_eq!(42, *new_value); assert_eq!(31, cursor.remaining_bytes().len()); + } + + // SAFETY: this is just for testing, values aren't actually read using this URID. + const INT_URID: URID = unsafe { URID::new_unchecked(5) }; + + #[test] + fn test_write_atom() { + let mut space = vec![0; 32]; + + let mut cursor = SpaceCursor::new(&mut space); { cursor.write_atom(INT_URID).unwrap().set(69).unwrap(); - assert_eq!(8, cursor.remaining_bytes().len()); + assert_eq!(16, cursor.remaining_bytes().len()); } - /*assert_eq!( - space.as_bytes(), - [ - // FIXME: this test is endianness-sensitive - 42, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - ] - );*/ - assert_eq!(32, space.as_bytes().len()); + assert_eq!(space[0..4], 8u32.to_ne_bytes()); + assert_eq!(space[4..8], 5u32.to_ne_bytes()); + assert_eq!(space[8..12], 69u32.to_ne_bytes()); } } diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 98a326dd..79b438d5 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -1,16 +1,41 @@ use crate::header::AtomHeader; use crate::space::{ - error::AtomWriteError, AtomSpace, SpaceWriter, SpaceWriterImpl, SpaceWriterSplitAllocation, + error::AtomWriteError, AtomSpace, SpaceAllocator, SpaceWriter, SpaceWriterSplitAllocation, }; use urid::URID; -/// A `MutSpace` that tracks the amount of allocated space in an atom header. -pub struct AtomSpaceWriter<'a> { +/// A [`SpaceWriter`] that tracks the amount of allocated space in an atom header. +/// +/// This allows for writing dynamic, variable-size atoms without having to track their size manually. +/// +/// # Example +/// +/// ``` +/// use lv2_atom::atom_prelude::*; +/// use urid::URID; +/// +/// let mut buf = vec![0; 64]; +/// let mut cursor = SpaceCursor::new(&mut buf); +/// +/// let mut writer = AtomWriter::write_new(&mut cursor, URID::new(42).unwrap()).unwrap(); +/// +/// let message = b"Hello, world!"; +/// writer.write_bytes(message).unwrap(); +/// assert_eq!(writer.atom_header().size_of_body(), message.len()); +/// ``` +pub struct AtomWriter<'a> { atom_header_index: usize, - parent: &'a mut (dyn SpaceWriterImpl), + parent: &'a mut (dyn SpaceAllocator), } -impl<'a> AtomSpaceWriter<'a> { +impl<'a> AtomWriter<'a> { + /// Retrieves a copy of the header this writer is currently tracking. + /// + /// # Panics + /// + /// This method may panic if it cannot locate the atom header it is currently tracking. Such + /// an error can only happen in the event of an invalid [`SpaceAllocator`] implementation, + /// however. #[inline] pub fn atom_header(&self) -> AtomHeader { let previous = self @@ -33,7 +58,14 @@ impl<'a> AtomSpaceWriter<'a> { unsafe { &mut space.assume_init_slice_mut()[0] } } - /// Create a new framed space with the given parent and type URID. + /// Writes an atom header into the given [`SpaceWriter`], and returns a new writer that starts + /// tracking its size. + /// + /// # Errors + /// + /// This method may return an error if the writer ran out of space in its internal buffer, and + /// is unable to reallocate the buffer, or if the padding and/or alignment requirements couldn't + /// be met. pub fn write_new( parent: &'a mut impl SpaceWriter, urid: URID, @@ -50,7 +82,7 @@ impl<'a> AtomSpaceWriter<'a> { } } -impl<'a> SpaceWriterImpl for AtomSpaceWriter<'a> { +impl<'a> SpaceAllocator for AtomWriter<'a> { #[inline] fn allocate_and_split( &mut self, @@ -115,7 +147,7 @@ mod tests { // writing { let mut root = SpaceCursor::new(raw_space); - let mut frame = AtomSpaceWriter::write_new(&mut root, URID::new(1).unwrap()).unwrap(); + let mut frame = AtomWriter::write_new(&mut root, URID::new(1).unwrap()).unwrap(); frame.write_value(42u32).unwrap(); frame.write_value(17u32).unwrap(); } diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index d0da2255..7ed6fda1 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,5 +1,5 @@ use crate::space::error::AtomWriteError; -use crate::space::{SpaceWriterImpl, SpaceWriterSplitAllocation}; +use crate::space::{SpaceAllocator, SpaceWriterSplitAllocation}; pub struct SpaceCursor<'a> { data: &'a mut [u8], @@ -15,7 +15,7 @@ impl<'a> SpaceCursor<'a> { } } -impl<'a> SpaceWriterImpl for SpaceCursor<'a> { +impl<'a> SpaceAllocator for SpaceCursor<'a> { #[inline] fn allocate_and_split( &mut self, diff --git a/atom/src/space/terminated.rs b/atom/src/space/terminated.rs index 84eb862a..d8427d5d 100644 --- a/atom/src/space/terminated.rs +++ b/atom/src/space/terminated.rs @@ -1,5 +1,5 @@ use crate::space::error::AtomWriteError; -use crate::space::{SpaceWriterImpl, SpaceWriterSplitAllocation}; +use crate::space::{SpaceAllocator, SpaceWriterSplitAllocation}; /// An helper space writer, that wraps an existing writer and makes sure all writes are /// terminated with a given terminator byte. @@ -11,7 +11,7 @@ use crate::space::{SpaceWriterImpl, SpaceWriterSplitAllocation}; /// # Example /// /// ``` -/// use lv2_atom::space::{SpaceCursor, SpaceWriter, SpaceWriterImpl, Terminated}; +/// use lv2_atom::space::{SpaceCursor, SpaceWriter, SpaceAllocator, Terminated}; /// let mut buffer = [0; 20]; /// // Our underlying allocator /// let cursor = SpaceCursor::new(&mut buffer); @@ -25,13 +25,13 @@ use crate::space::{SpaceWriterImpl, SpaceWriterSplitAllocation}; /// assert_eq!(&buffer, b"Hello, world! Boop!\x42"); /// /// ``` -pub struct Terminated { +pub struct Terminated { inner: W, terminator: u8, wrote_terminator_byte: bool, } -impl Terminated { +impl Terminated { /// Creates a new Terminated writer, from an inner writer and a given terminator byte. pub fn new(inner: W, terminator: u8) -> Self { Self { @@ -47,7 +47,7 @@ impl Terminated { } } -impl SpaceWriterImpl for Terminated { +impl SpaceAllocator for Terminated { fn allocate_and_split( &mut self, size: usize, diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index 98ea1728..da955583 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -1,7 +1,7 @@ #![deny(unsafe_code)] use crate::space::error::AtomWriteError; -use crate::space::{AlignedSpace, SpaceWriterImpl, SpaceWriterSplitAllocation}; +use crate::space::{AlignedSpace, SpaceAllocator, SpaceWriterSplitAllocation}; use std::mem::MaybeUninit; use std::ops::Range; @@ -80,7 +80,7 @@ pub struct VecSpaceCursor<'vec, T> { allocated_length: usize, } -impl<'vec, T: Copy + 'static> SpaceWriterImpl for VecSpaceCursor<'vec, T> { +impl<'vec, T: Copy + 'static> SpaceAllocator for VecSpaceCursor<'vec, T> { fn allocate_and_split( &mut self, size: usize, @@ -137,6 +137,7 @@ impl<'vec, T: Copy + 'static> SpaceWriterImpl for VecSpaceCursor<'vec, T> { #[cfg(test)] mod tests { use crate::space::{SpaceWriter, VecSpace}; + use crate::AtomHeader; #[test] pub fn test_lifetimes() { @@ -151,4 +152,22 @@ mod tests { let _other_cursor = buffer.cursor(); let _other_cursor2 = buffer.cursor(); } + + #[test] + pub fn test_alignment() { + fn aligned_vec() { + let space = VecSpace::::new_with_capacity(4); + assert_eq!( + space.as_bytes().as_ptr() as usize % ::core::mem::align_of::(), + 0 + ); + } + + // Testing with some random types with different alignments + aligned_vec::(); + aligned_vec::(); + aligned_vec::(); + aligned_vec::(); + aligned_vec::(); + } } diff --git a/midi/src/raw.rs b/midi/src/raw.rs index f9522dbf..68d62113 100644 --- a/midi/src/raw.rs +++ b/midi/src/raw.rs @@ -24,7 +24,7 @@ impl<'a> AtomHandle<'a> for MidiEventReadHandle { pub struct MidiEventWriteHandle; impl<'a> AtomHandle<'a> for MidiEventWriteHandle { - type Handle = AtomSpaceWriter<'a>; + type Handle = AtomWriter<'a>; } impl Atom for MidiEvent { @@ -35,7 +35,7 @@ impl Atom for MidiEvent { Ok(body.as_bytes()) } - fn write(frame: AtomSpaceWriter) -> Result { + fn write(frame: AtomWriter) -> Result { Ok(frame) } } diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 7e0f5caa..3c192a12 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -33,7 +33,7 @@ impl<'a> AtomHandle<'a> for WMidiEventWriteHandle { } pub struct WMidiEventWriter<'a> { - writer: AtomSpaceWriter<'a>, + writer: AtomWriter<'a>, } impl<'a> WMidiEventWriter<'a> { @@ -63,7 +63,7 @@ impl Atom for WMidiEvent { } #[inline] - fn write(writer: AtomSpaceWriter) -> Result { + fn write(writer: AtomWriter) -> Result { Ok(WMidiEventWriter { writer }) } } @@ -94,7 +94,7 @@ impl Atom for SystemExclusiveWMidiEvent { WMidiEvent::read(space) } - fn write(mut frame: AtomSpaceWriter) -> Result { + fn write(mut frame: AtomWriter) -> Result { frame.write_value(0xf0u8)?; Ok(Writer { @@ -109,7 +109,7 @@ impl Atom for SystemExclusiveWMidiEvent { /// /// The "start of system exclusive" status byte is written by [`SystemExclusiveWMidiEvent::init`](struct.SystemExclusiveWMidiEvent.html#method.init) method and the "end of system exclusive" status byte is written when the writer is dropped. pub struct Writer<'a> { - frame: Terminated>, + frame: Terminated>, } impl<'a> Writer<'a> { From 2dbeabdad418b6d1d0a13b65f40aca8eb700a4d9 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 29 Sep 2021 03:45:54 +0200 Subject: [PATCH 45/54] Implemented proper error messages for AtomError --- atom/src/atoms/object.rs | 2 + atom/src/atoms/sequence.rs | 4 +- atom/src/atoms/string.rs | 18 ++--- atom/src/atoms/tuple.rs | 2 +- atom/src/atoms/vector.rs | 10 +-- atom/src/header.rs | 2 +- atom/src/space/cursor.rs | 2 + atom/src/space/error.rs | 136 +++++++++++++++++++++++++++++++++++-- atom/src/space/reader.rs | 2 +- midi/src/wmidi_binding.rs | 1 + urid/src/lib.rs | 7 +- 11 files changed, 161 insertions(+), 25 deletions(-) diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index 28c766be..e9c4cdcb 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -138,6 +138,7 @@ impl Atom for Object { id: URID::try_from(header.id).ok(), otype: URID::try_from(header.otype).map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: "Invalid object type URID: 0", })?, }; @@ -283,6 +284,7 @@ impl Property { let header = PropertyHeader { key: URID::try_from(header.key).map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: "Invalid object property key URID: 0", })?, context: URID::try_from(header.context).ok(), }; diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index c6d1a4a5..6dcad946 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -210,8 +210,10 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { fn write_time_stamp(&mut self, time_stamp: U::Value) -> Result<(), AtomWriteError> { if let Some(last_stamp) = self.last_stamp { if last_stamp > time_stamp { - return Err(AtomWriteError::WritingIllegalState { + return Err(AtomWriteError::IllegalOperation { writing_type_uri: Sequence::uri(), + error_message: + "Attempted to write event with an earlier timestamp than the previous event", }); } } diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index d810e18d..4b1455d2 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -51,13 +51,11 @@ pub enum LiteralInfo { } impl LiteralInfo { - fn try_from_raw(header: &sys::LV2_Atom_Literal_Body) -> Option { - if header.lang != 0 && header.datatype == 0 { - Some(LiteralInfo::Language(URID::new(header.lang)?)) - } else if header.lang == 0 && header.datatype != 0 { - Some(LiteralInfo::Datatype(URID::new(header.datatype)?)) - } else { - None + fn try_from_raw(header: &sys::LV2_Atom_Literal_Body) -> Result { + match (URID::new(header.lang), URID::new(header.datatype)) { + (Some(urid), _) => Ok(LiteralInfo::Language(urid)), + (None, Some(urid)) => Ok(LiteralInfo::Datatype(urid)), + (None, None) => Err("Invalid Literal header: neither lang or datatype URIDs are set"), } } @@ -112,8 +110,9 @@ impl Atom for Literal { let header: &sys::LV2_Atom_Literal_Body = reader.next_value()?; let info = - LiteralInfo::try_from_raw(header).ok_or_else(|| AtomReadError::InvalidAtomValue { + LiteralInfo::try_from_raw(header).map_err(|err| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: err, })?; let data = reader.remaining_bytes(); @@ -122,6 +121,7 @@ impl Atom for Literal { .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()])) .map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: "Literal contents are invalid UTF-8", }) .map(|string| (info, string)) } @@ -165,6 +165,7 @@ impl Atom for String { let c_str = CStr::from_bytes_with_nul(body.as_bytes()).map_err(|_| { AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: "String value is not null-terminated", } })?; @@ -172,6 +173,7 @@ impl Atom for String { .to_str() .map_err(|_| AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: "String contents are invalid UTF-8", })?; Ok(str) diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index 517606e3..ab7818de 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -159,7 +159,7 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int); - let vector_items = unsafe { reader.next_slice::(9) }.unwrap(); + let vector_items = unsafe { reader.next_values::(9) }.unwrap(); assert_eq!(vector_items, &[17; 9]); let int: &sys::LV2_Atom_Int = unsafe { reader.next_value() }.unwrap(); diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 24cf4be6..ca538f69 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -36,7 +36,7 @@ use crate::*; use std::marker::PhantomData; use std::mem::{size_of, MaybeUninit}; -/// An atom containg an array of scalar atom bodies. +/// An atom containing an homogenous array of scalar atom bodies. /// /// [See also the module documentation.](index.html) pub struct Vector; @@ -69,11 +69,12 @@ impl<'a> VectorReader<'a> { ) -> Result<&'a [C::InternalType], AtomReadError> { if self.header.child_type != atom_type { let found_urid = - URID::new(self.header.child_size).ok_or(AtomReadError::InvalidAtomValue { + URID::new(self.header.child_type).ok_or(AtomReadError::InvalidAtomValue { reading_type_uri: Vector::uri(), + error_message: "Invalid child type URID (0)", })?; - return Err(AtomReadError::InvalidAtomUrid { + return Err(AtomReadError::AtomUridMismatch { found_urid, expected_urid: atom_type.into_general(), expected_uri: C::uri(), @@ -83,6 +84,7 @@ impl<'a> VectorReader<'a> { if self.header.child_size as usize != size_of::() { return Err(AtomReadError::InvalidAtomValue { reading_type_uri: Vector::uri(), + error_message: "child_size value does not match actual size of type", }); } @@ -214,7 +216,7 @@ mod tests { assert_eq!(vector.body.child_size as usize, size_of::()); assert_eq!(vector.body.child_type, urids.int.get()); - let children = unsafe { reader.next_slice::(CHILD_COUNT) }.unwrap(); + let children = unsafe { reader.next_values::(CHILD_COUNT) }.unwrap(); for value in &children[0..children.len() - 1] { assert_eq!(*value, 42); } diff --git a/atom/src/header.rs b/atom/src/header.rs index bfd8151c..993c2e10 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -66,7 +66,7 @@ impl AtomHeader { if other == self.urid() { Ok(()) } else { - Err(AtomReadError::InvalidAtomUrid { + Err(AtomReadError::AtomUridMismatch { expected_uri: A::uri(), expected_urid: other.into_general(), found_urid: self.urid(), diff --git a/atom/src/space/cursor.rs b/atom/src/space/cursor.rs index 7ed6fda1..9abfec80 100644 --- a/atom/src/space/cursor.rs +++ b/atom/src/space/cursor.rs @@ -1,12 +1,14 @@ use crate::space::error::AtomWriteError; use crate::space::{SpaceAllocator, SpaceWriterSplitAllocation}; +/// A lightweight [`SpaceWriter`] that writes into a mutable byte buffer using a cursor. pub struct SpaceCursor<'a> { data: &'a mut [u8], allocated_length: usize, } impl<'a> SpaceCursor<'a> { + /// Create a new [`SpaceCursor`] from a given mutable byte buffer. pub fn new(data: &'a mut [u8]) -> Self { Self { data, diff --git a/atom/src/space/error.rs b/atom/src/space/error.rs index fd8a173a..0f24d5e2 100644 --- a/atom/src/space/error.rs +++ b/atom/src/space/error.rs @@ -70,6 +70,34 @@ impl From for AtomReadError { } } +impl Display for AlignmentError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.0 { + AlignmentErrorInner::CannotComputeAlignment { type_id, ptr } => { + write!(f, "Could not compute alignment for pointer {:?} while trying to align it for type {}", ptr, type_id) + } + AlignmentErrorInner::UnalignedBuffer { type_id, ptr } => { + write!( + f, + "Pointer {:?} is not properly aligned for type {}", + ptr, type_id + ) + } + AlignmentErrorInner::NotEnoughSpaceToRealign { + type_id, + ptr, + required_padding, + available_size, + } => { + write!(f, "Not enough space to realign pointer {:?} for type {} (needs {} padding bytes, but only {} bytes are available)", + ptr, type_id, required_padding, available_size) + } + } + } +} + +impl Error for AlignmentError {} + /// Errors that can occur while writing atoms to a byte buffer. #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] @@ -101,16 +129,63 @@ pub enum AtomWriteError { /// The requested amount of bytes requested: usize, }, - WritingIllegalState { + /// An error generated by a given atom type while writing + IllegalOperation { + /// The type URI of the atom that raised the error writing_type_uri: &'static Uri, + /// An user-friendly error message + error_message: &'static str, }, AlignmentError(AlignmentError), } +impl Display for AtomWriteError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AtomWriteError::OutOfSpace { + used, + capacity, + requested, + } => { + write!(f, "Failed to allocate {} bytes: buffer is too small (used: {} / {} bytes, remaining: {} bytes", + requested, used, capacity, capacity - used) + } + AtomWriteError::RewindBeyondAllocated { + allocated, + requested, + } => { + write!(f, "Attempted to rewind {} bytes before the start of buffer (buffer position: {}, requested a rewind of {} bytes", + requested - allocated, allocated, requested) + } + AtomWriteError::WritingOutOfBounds { + requested, + available, + } => { + write!(f, "Attempted to write {} bytes past the end of buffer (buffer size: {}, requested write of {} bytes", + requested - available, available, requested) + } + AtomWriteError::IllegalOperation { + writing_type_uri, + error_message, + } => { + write!( + f, + "Illegal operation when trying to write Atom of type {}: {}", + writing_type_uri.to_string_lossy(), + error_message + ) + } + AtomWriteError::AlignmentError(e) => Display::fmt(e, f), + } + } +} + +impl Error for AtomWriteError {} + #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] pub enum AtomReadError { - InvalidAtomUrid { + AtomUridMismatch { expected_uri: &'static Uri, expected_urid: URID, found_urid: URID, @@ -126,10 +201,60 @@ pub enum AtomReadError { }, InvalidAtomValue { reading_type_uri: &'static Uri, + error_message: &'static str, }, AlignmentError(AlignmentError), } +impl Display for AtomReadError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AtomReadError::AtomUridMismatch { + expected_uri, + expected_urid, + found_urid, + } => { + write!( + f, + "Mismatched Atom URID for Type '{}': Expected URID #{}, found #{}", + expected_uri.to_string_lossy(), + expected_urid.get(), + found_urid.get() + ) + } + AtomReadError::InvalidUrid { + expected_uri, + expected_urid, + found_urid, + } => { + write!(f, "Found invalid URID value ({}) while trying to read Atom URID Type {} (URID #{})", + found_urid, expected_uri.to_string_lossy(), expected_urid.get()) + } + AtomReadError::ReadingOutOfBounds { + available, + requested, + } => { + write!(f, "Attempted to read {} bytes past the end of buffer (buffer size: {}, requested write of {} bytes", + requested - available, available, requested) + } + AtomReadError::InvalidAtomValue { + reading_type_uri, + error_message, + } => { + write!( + f, + "Invalid Atom value for type {}: {}", + reading_type_uri.to_string_lossy(), + error_message + ) + } + AtomReadError::AlignmentError(e) => Display::fmt(e, f), + } + } +} + +impl Error for AtomReadError {} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum AtomError { ReadError(AtomReadError), @@ -137,8 +262,11 @@ pub enum AtomError { } impl Display for AtomError { - fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { - todo!() + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AtomError::ReadError(e) => write!(f, "Could not read atom data: {}", e), + AtomError::WriteError(e) => write!(f, "Could not write atom data: {}", e), + } } } diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index c752ee51..5273f2a5 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -68,7 +68,7 @@ impl<'a> SpaceReader<'a> { } #[inline] - pub unsafe fn next_slice( + pub unsafe fn next_values( &mut self, length: usize, ) -> Result<&'a [U], AtomReadError> { diff --git a/midi/src/wmidi_binding.rs b/midi/src/wmidi_binding.rs index 3c192a12..f25689c3 100644 --- a/midi/src/wmidi_binding.rs +++ b/midi/src/wmidi_binding.rs @@ -58,6 +58,7 @@ impl Atom for WMidiEvent { wmidi::MidiMessage::try_from(space.as_bytes()).map_err(|_| { AtomReadError::InvalidAtomValue { reading_type_uri: Self::uri(), + error_message: "Invalid MIDI message", } }) } diff --git a/urid/src/lib.rs b/urid/src/lib.rs index cf6c754e..9d1290ff 100644 --- a/urid/src/lib.rs +++ b/urid/src/lib.rs @@ -270,12 +270,9 @@ impl Hash for URID { impl std::convert::TryFrom for URID { type Error = (); + #[inline] fn try_from(value: u32) -> Result { - if value == 0 { - Err(()) - } else { - Ok(unsafe { URID::new_unchecked(value) }) - } + URID::new(value).ok_or(()) } } From d6ee079daae498461c2202375f20e48e9a2d27cf Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 29 Sep 2021 05:08:59 +0200 Subject: [PATCH 46/54] Some clippy fixes --- atom/src/atoms/chunk.rs | 2 +- atom/src/atoms/object.rs | 2 +- atom/src/atoms/vector.rs | 4 ++-- atom/src/header.rs | 14 ++------------ atom/src/port.rs | 4 ++-- atom/src/space/aligned.rs | 6 ++---- atom/src/space/atom_writer.rs | 11 +++-------- atom/src/space/reader.rs | 2 +- atom/src/space/vec.rs | 16 ++++++++++++++++ state/src/raw.rs | 16 +++++++++------- state/tests/integration.rs | 8 +++----- 11 files changed, 42 insertions(+), 43 deletions(-) diff --git a/atom/src/atoms/chunk.rs b/atom/src/atoms/chunk.rs index 042ea7c7..4133f20f 100644 --- a/atom/src/atoms/chunk.rs +++ b/atom/src/atoms/chunk.rs @@ -92,7 +92,7 @@ mod tests { let mut writer = space.write_atom(urids.chunk).unwrap(); let data = writer.allocate(SLICE_LENGTH).unwrap(); - for (i, value) in data.into_iter().enumerate() { + for (i, value) in data.iter_mut().enumerate() { *value = i as u8; } diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index e9c4cdcb..a9ddaf89 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -213,7 +213,7 @@ impl<'a> ObjectWriter<'a> { /// Initialize a new property with a context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. - pub fn init_with_context<'read, K: ?Sized, T: ?Sized, A: Atom>( + pub fn init_with_context( &mut self, key: URID, context: URID, diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index ca538f69..840e532a 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -229,8 +229,8 @@ mod tests { let children: &[i32] = atom.read(urids.vector).unwrap().of_type(urids.int).unwrap(); assert_eq!(children.len(), CHILD_COUNT); - for i in 0..children.len() - 1 { - assert_eq!(children[i], 42); + for i in children { + assert_eq!(*i, 42); } assert_eq!(children[children.len() - 1], 1); } diff --git a/atom/src/header.rs b/atom/src/header.rs index 993c2e10..5a0a6eb8 100644 --- a/atom/src/header.rs +++ b/atom/src/header.rs @@ -1,5 +1,5 @@ use crate::space::error::AtomReadError; -use crate::{Atom, UnidentifiedAtom}; +use crate::Atom; use urid::URID; #[repr(C, align(8))] @@ -31,16 +31,6 @@ impl AtomHeader { unsafe { &mut *(raw as *mut lv2_sys::LV2_Atom as *mut _) } } - #[inline] - pub unsafe fn assume_full_atom(&self) -> &UnidentifiedAtom { - UnidentifiedAtom::from_header(self) - } - - #[inline] - pub unsafe fn assume_full_atom_mut(&mut self) -> &mut UnidentifiedAtom { - UnidentifiedAtom::from_header_mut(self) - } - #[inline] pub(crate) unsafe fn set_size_of_body(&mut self, size: usize) { self.inner.size = size as u32; @@ -58,7 +48,7 @@ impl AtomHeader { #[inline] pub fn urid(self) -> URID { - URID::new(self.inner.type_).unwrap() + URID::new(self.inner.type_).expect("Invalid header URID type.") } #[inline] diff --git a/atom/src/port.rs b/atom/src/port.rs index b3fa8183..f493d2b2 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -112,13 +112,13 @@ impl PortType for AtomPort { #[inline] unsafe fn input_from_raw(pointer: NonNull, _sample_count: u32) -> PortReader<'static> { let header = AtomHeader::from_raw(pointer.cast().as_ref()); - PortReader::new(header.assume_full_atom()) + PortReader::new(UnidentifiedAtom::from_header(header)) } #[inline] unsafe fn output_from_raw(pointer: NonNull, _sample_count: u32) -> PortWriter<'static> { let header = AtomHeader::from_raw_mut(pointer.cast().as_mut()); - PortWriter::new(header.assume_full_atom_mut().body_mut()) + PortWriter::new(UnidentifiedAtom::from_header_mut(header).body_mut()) } } diff --git a/atom/src/space/aligned.rs b/atom/src/space/aligned.rs index 32118450..45b4bbe9 100644 --- a/atom/src/space/aligned.rs +++ b/atom/src/space/aligned.rs @@ -452,7 +452,7 @@ fn check_alignment(data: &[u8]) -> Result<(), AlignmentError> { mod tests { use crate::space::error::{AlignmentError, AlignmentErrorInner, TypeData}; use crate::space::*; - use crate::AtomHeader; + use crate::{AtomHeader, UnidentifiedAtom}; use std::mem::{size_of, size_of_val}; use urid::*; @@ -593,9 +593,7 @@ mod tests { let written_atom_addr = written_atom as *mut _ as *mut _; let created_space = unsafe { - AtomHeader::from_raw(written_atom) - .assume_full_atom() - .atom_space() + UnidentifiedAtom::from_header(AtomHeader::from_raw(written_atom)).atom_space() }; assert!(::core::ptr::eq( diff --git a/atom/src/space/atom_writer.rs b/atom/src/space/atom_writer.rs index 79b438d5..1067ac3c 100644 --- a/atom/src/space/atom_writer.rs +++ b/atom/src/space/atom_writer.rs @@ -30,20 +30,15 @@ pub struct AtomWriter<'a> { impl<'a> AtomWriter<'a> { /// Retrieves a copy of the header this writer is currently tracking. - /// - /// # Panics - /// - /// This method may panic if it cannot locate the atom header it is currently tracking. Such - /// an error can only happen in the event of an invalid [`SpaceAllocator`] implementation, - /// however. #[inline] pub fn atom_header(&self) -> AtomHeader { let previous = self .parent .allocated_bytes() .get(self.atom_header_index..) - .unwrap(); - let space = AtomSpace::from_bytes(previous).unwrap(); + .expect("Unable to locate atom header"); + + let space = AtomSpace::from_bytes(previous).expect("Atom header location is unaligned"); unsafe { space.assume_init_slice()[0] } } diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index 5273f2a5..93d65058 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -99,7 +99,7 @@ impl<'a> SpaceReader<'a> { #[inline] pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomReadError> { - let space = AlignedSpace::::align_from_bytes(&self.space)?; + let space = AlignedSpace::::align_from_bytes(self.space)?; let header = space .assume_init_slice() .get(0) diff --git a/atom/src/space/vec.rs b/atom/src/space/vec.rs index da955583..46949c84 100644 --- a/atom/src/space/vec.rs +++ b/atom/src/space/vec.rs @@ -9,6 +9,22 @@ pub struct VecSpace { inner: Vec>, } +impl Default for VecSpace { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Clone for VecSpace { + #[inline] + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + impl VecSpace { #[inline] pub fn new() -> Self { diff --git a/state/src/raw.rs b/state/src/raw.rs index 36af76b8..7c559ec1 100644 --- a/state/src/raw.rs +++ b/state/src/raw.rs @@ -79,7 +79,7 @@ impl<'a> StoreHandle<'a> { /// Commit one specific property. /// - /// This method returns `None` if the requested property was not marked for commit, `Some(Ok(()))` if the property was stored and `Some(Err(_))` if an error occured while storing the property. + /// This method returns `None` if the requested property was not marked for commit, `Some(Ok(()))` if the property was stored and `Some(Err(_))` if an error occurred while storing the property. pub fn commit(&mut self, key: URID) -> Option> { let key = key.into_general(); let space = self.properties.remove(&key)?; @@ -117,7 +117,7 @@ impl<'a> StatePropertyWriter<'a> { /// Initialize the property. /// /// This works like any other atom writer: You have to provide the URID of the atom type you want to write, as well as the type-specific parameter. If the property hasn't been initialized before, it will be initialized and the writing handle is returned. Otherwise, `Err(StateErr::Unknown)` is returned. - pub fn init<'read, A: Atom>( + pub fn init( &'a mut self, urid: URID, ) -> Result<>::Handle, StateErr> { @@ -278,12 +278,13 @@ mod tests { .unwrap() ); assert_eq!( - 1.0, - *retrieve_handle + 1.0f32.to_ne_bytes(), + retrieve_handle .retrieve(URID::new(2).unwrap()) .unwrap() .read(urids.float) .unwrap() + .to_ne_bytes() ); assert_eq!( [1, 2, 3, 4], @@ -315,9 +316,10 @@ mod tests { } 2 => { assert_eq!(urids.float, *type_); - assert_eq!(1.0, unsafe { - *(value.as_slice() as *const _ as *const f32) - }); + assert_eq!( + 1.0f32.to_ne_bytes(), + unsafe { *(value.as_slice() as *const _ as *const f32) }.to_ne_bytes() + ); } 3 => { assert_eq!(urids.vector, *type_); diff --git a/state/tests/integration.rs b/state/tests/integration.rs index 2f4a83d6..45a5c1df 100644 --- a/state/tests/integration.rs +++ b/state/tests/integration.rs @@ -101,7 +101,7 @@ fn create_plugin(mapper: Pin<&mut HostMap>) -> Stateful { .unwrap() }; - assert_eq!(42.0, plugin.internal); + assert_eq!(42.0f32.to_ne_bytes(), plugin.internal.to_ne_bytes()); assert_eq!(0, plugin.audio.len()); plugin @@ -120,14 +120,12 @@ fn test_save_n_restore() { .unwrap(); (extension.save.unwrap(), extension.restore.unwrap()) }; - assert!(store_fn == StateDescriptor::::extern_save); - assert!(restore_fn == StateDescriptor::::extern_restore); let mut first_plugin = create_plugin(mapper.as_mut()); first_plugin.run(&mut (), &mut (), 32); - assert_eq!(17.0, first_plugin.internal); + assert_eq!(17.0f32.to_ne_bytes(), first_plugin.internal.to_ne_bytes()); assert_eq!(32, first_plugin.audio.len()); unsafe { @@ -152,6 +150,6 @@ fn test_save_n_restore() { ) }; - assert_eq!(17.0, second_plugin.internal); + assert_eq!(17.0f32.to_ne_bytes(), second_plugin.internal.to_ne_bytes()); assert_eq!(32, second_plugin.audio.len()); } From 6152505cb307cc8a09788de72dfee32d0a3aff76 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 1 Oct 2021 14:50:31 +0200 Subject: [PATCH 47/54] Fix tests, add more docs --- atom/src/atoms/object.rs | 39 +++++++++++++++------ atom/src/atoms/scalar.rs | 29 +++++----------- atom/src/atoms/sequence.rs | 55 +++++++++++++++++++++++------- atom/src/atoms/string.rs | 17 ++++++++-- atom/src/atoms/tuple.rs | 7 +++- atom/src/atoms/vector.rs | 7 ++-- atom/src/space/allocator.rs | 4 +-- atom/src/space/reader.rs | 68 +++++++++++++++++++++++++++++++++---- 8 files changed, 166 insertions(+), 60 deletions(-) diff --git a/atom/src/atoms/object.rs b/atom/src/atoms/object.rs index a9ddaf89..9a7e94f2 100644 --- a/atom/src/atoms/object.rs +++ b/atom/src/atoms/object.rs @@ -63,7 +63,7 @@ //! ).unwrap(); //! //! // Write a property to the object. -//! object_writer.init(urids.property_a, urids.atom.int).unwrap(); +//! object_writer.new_property(urids.property_a, urids.atom.int).unwrap(); //! } //! ``` //! @@ -106,11 +106,18 @@ impl<'a> AtomHandle<'a> for ObjectWriterHandle { type Handle = ObjectHeaderWriter<'a>; } +/// A type-state for the Object Writer, that writes the header of an object. pub struct ObjectHeaderWriter<'a> { frame: AtomWriter<'a>, } impl<'a> ObjectHeaderWriter<'a> { + /// Initializes the object with the given header. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn write_header( mut self, header: ObjectHeader, @@ -210,31 +217,41 @@ pub struct ObjectWriter<'a> { } impl<'a> ObjectWriter<'a> { - /// Initialize a new property with a context. + /// Initializes a new property, with a given context. /// /// This method does the same as [`init`](#method.init), but also sets the context URID. - pub fn init_with_context( + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. + pub fn new_property_with_context( &mut self, key: URID, context: URID, - child_urid: URID, + atom_type: URID, ) -> Result<::Handle, AtomWriteError> { Property::write_header(&mut self.frame, key.into_general(), Some(context))?; - self.frame.write_atom(child_urid) + self.frame.write_atom(atom_type) } - /// Initialize a new property. + /// Initializes a new property. /// /// This method writes out the header of a property and returns a reference to the space, so the property values can be written. /// /// Properties also have a context URID internally, which is rarely used. If you want to add one, use [`init_with_context`](#method.init_with_context). - pub fn init( + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. + pub fn new_property( &mut self, key: URID, - child_urid: URID, + atom_type: URID, ) -> Result<::Handle, AtomWriteError> { Property::write_header(&mut self.frame, key, None::>)?; - self.frame.write_atom(child_urid) + self.frame.write_atom(atom_type) } } @@ -358,14 +375,14 @@ mod tests { .unwrap(); { writer - .init(first_key, urids.int) + .new_property(first_key, urids.int) .unwrap() .set(first_value) .unwrap(); } { writer - .init(second_key, urids.float) + .new_property(second_key, urids.float) .unwrap() .set(second_value) .unwrap(); diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index f1b3ba12..53eaf84c 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -35,34 +35,21 @@ use urid::UriBound; use urid::URID; /// An atom that only contains a single, scalar value. -/// -/// Since scalar values are so simple, the reading and writing methods are exactly the same. pub trait ScalarAtom: UriBound { /// The internal representation of the atom. /// - /// For example, the `Int` atom has the internal type of `i32`, which is `i32` on most platforms. + /// For example, the `Int` atom has the internal type of `i32`. type InternalType: Unpin + Copy + Send + Sync + Sized + 'static; - - /// Try to read the atom from a space. - /// - /// If the space does not contain the atom or is not big enough, return `None`. The second return value is the space behind the atom. - #[inline] - unsafe fn read_scalar(body: &AtomSpace) -> Result<&Self::InternalType, AtomReadError> { - body.read().next_value() - } - - /// Try to write the atom into a space. - /// - /// Write an atom with the value of `value` into the space and return a mutable reference to the written value. If the space is not big enough, return `None`. - #[inline] - fn write_scalar(frame: AtomWriter) -> Result, AtomWriteError> { - Ok(ScalarWriter(frame, PhantomData)) - } } pub struct ScalarWriter<'a, T: Copy + 'static>(AtomWriter<'a>, PhantomData); impl<'a, T: Copy + 'static> ScalarWriter<'a, T> { + /// Sets the value of the scalar + /// + /// # Errors + /// + /// Returns an error is the underlying buffer is out of space for the new scalar value. #[inline] pub fn set(&mut self, value: T) -> Result<&mut T, AtomWriteError> { #[repr(align(8))] @@ -95,13 +82,13 @@ impl Atom for A { unsafe fn read( body: &AtomSpace, ) -> Result<::Handle, AtomReadError> { - ::read_scalar(body) + body.read().next_value() } fn write( frame: AtomWriter, ) -> Result<::Handle, AtomWriteError> { - ::write_scalar(frame) + Ok(ScalarWriter(frame, PhantomData)) } } diff --git a/atom/src/atoms/sequence.rs b/atom/src/atoms/sequence.rs index 6dcad946..3e9d28d0 100644 --- a/atom/src/atoms/sequence.rs +++ b/atom/src/atoms/sequence.rs @@ -96,18 +96,26 @@ impl<'a> AtomHandle<'a> for SequenceWriteHandle { type Handle = SequenceHeaderWriter<'a>; } +/// A type-state for the Sequence Reader, that reads the header of a sequence. +#[derive(Clone)] pub struct SequenceHeaderReader<'a> { header: &'a sys::LV2_Atom_Sequence_Body, reader: SpaceReader<'a>, } impl<'a> SequenceHeaderReader<'a> { + /// Tries to read the sequence as having timestamps of the given type. + /// + /// # Errors + /// + /// This method will return an `InvalidUrid` error if the given timestamp type URID does not + /// match the one of the sequence being currently read. pub fn with_unit( self, - unit_urid: URID, + timestamp_unit_urid: URID, ) -> Result, AtomReadError> { if (self.header.unit == 0 && U::TYPE == SequenceUnitType::Frame) - || (self.header.unit == unit_urid) + || (self.header.unit == timestamp_unit_urid) { Ok(SequenceIterator { reader: self.reader, @@ -116,24 +124,33 @@ impl<'a> SequenceHeaderReader<'a> { } else { Err(AtomReadError::InvalidUrid { expected_uri: U::uri(), - expected_urid: unit_urid.into_general(), + expected_urid: timestamp_unit_urid.into_general(), found_urid: self.header.unit, }) } } } +/// A type-state for the Sequence Writer, that writes the header of a sequence. pub struct SequenceHeaderWriter<'a> { writer: AtomWriter<'a>, } impl<'a> SequenceHeaderWriter<'a> { + /// Initializes the sequence with the given timestamp type URID. + /// + /// The timestamp type can be either [`Frame`](lv2_units::units::Frame) or [`Beat`](lv2_units::units::Beat). + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn with_unit( mut self, - unit_urid: URID, + timestamp_unit_urid: URID, ) -> Result, AtomWriteError> { let header = SequenceBody(sys::LV2_Atom_Sequence_Body { - unit: unit_urid.get(), + unit: timestamp_unit_urid.get(), pad: 0, }); @@ -204,9 +221,11 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { /// /// # Errors /// - /// This method returns an error if either: - /// * The last time stamp is younger than the time stamp. - /// * Space is insufficient. + /// This method will return an error if the given timestamp is smaller than the last written + /// timestamp (if any). + /// + /// This method will also return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. fn write_time_stamp(&mut self, time_stamp: U::Value) -> Result<(), AtomWriteError> { if let Some(last_stamp) = self.last_stamp { if last_stamp > time_stamp { @@ -224,9 +243,15 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { Ok(()) } - /// Initialize an event. + /// Initialize an event's atom, with the given timestamp. + /// + /// # Errors /// - /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. + /// This method will return an error if the given timestamp is smaller than the last written + /// timestamp (if any). + /// + /// This method will also return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn new_event( &mut self, time_stamp: U::Value, @@ -236,11 +261,15 @@ impl<'a, U: SequenceUnit> SequenceWriter<'a, U> { self.writer.write_atom(urid) } - /// Forward an unidentified atom to the sequence. + /// Writes an unidentified atom to the sequence, with the given timestamp. + /// + /// # Errors /// - /// If your cannot identify the type of the atom but have to write it, you can simply forward it. + /// This method will return an error if the given timestamp is smaller than the last written + /// timestamp (if any). /// - /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`. + /// This method will also return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn forward( &mut self, time_stamp: U::Value, diff --git a/atom/src/atoms/string.rs b/atom/src/atoms/string.rs index 4b1455d2..68522378 100644 --- a/atom/src/atoms/string.rs +++ b/atom/src/atoms/string.rs @@ -73,11 +73,18 @@ impl LiteralInfo { } } +/// A type-state for the Literal Writer, that writes the info header of a literal. pub struct LiteralInfoWriter<'a> { writer: AtomWriter<'a>, } impl<'a> LiteralInfoWriter<'a> { + /// Initializes the literal with the given info. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn write_info(mut self, info: LiteralInfo) -> Result, AtomWriteError> { self.writer.write_value(info.into_raw())?; @@ -194,11 +201,15 @@ pub struct StringWriter<'a> { } impl<'a> StringWriter<'a> { - /// Append a string. + /// Appends a string to the atom's buffer. + /// + /// This method copies the given string to the end of the string atom, and then returns a + /// mutable reference to the copy. /// - /// This method copies the given string to the end of the string atom/literal and then returns a mutable reference to the copy. + /// # Errors /// - /// If the internal space for the atom is not big enough, this method returns `None`. + /// This method will return an error if there is not enough space in the underlying buffer for, + /// the given additional string, or if any other write error occurs. pub fn append(&mut self, string: &str) -> Result<&mut str, AtomWriteError> { let bytes = self.writer.write_bytes(string.as_bytes())?; diff --git a/atom/src/atoms/tuple.rs b/atom/src/atoms/tuple.rs index ab7818de..22c5d41e 100644 --- a/atom/src/atoms/tuple.rs +++ b/atom/src/atoms/tuple.rs @@ -95,7 +95,12 @@ pub struct TupleWriter<'a> { } impl<'a> TupleWriter<'a> { - /// Initialize a new tuple element. + /// Initialize a new tuple element atom of a given type. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn init( &mut self, child_urid: URID, diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index 840e532a..cc2fb279 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -90,7 +90,7 @@ impl<'a> VectorReader<'a> { // SAFETY: The data type has just been checked above, and we can assume this data was // properly initialized by the host. - unsafe { self.reader.as_slice() } + Ok(unsafe { self.reader.as_slice() }?) } } @@ -217,7 +217,8 @@ mod tests { assert_eq!(vector.body.child_type, urids.int.get()); let children = unsafe { reader.next_values::(CHILD_COUNT) }.unwrap(); - for value in &children[0..children.len() - 1] { + assert_eq!(children.len(), CHILD_COUNT); + for value in &children[..CHILD_COUNT - 1] { assert_eq!(*value, 42); } assert_eq!(children[children.len() - 1], 1); @@ -229,7 +230,7 @@ mod tests { let children: &[i32] = atom.read(urids.vector).unwrap().of_type(urids.int).unwrap(); assert_eq!(children.len(), CHILD_COUNT); - for i in children { + for i in &children[..CHILD_COUNT - 1] { assert_eq!(*i, 42); } assert_eq!(children[children.len() - 1], 1); diff --git a/atom/src/space/allocator.rs b/atom/src/space/allocator.rs index 23cf10cf..737ade2e 100644 --- a/atom/src/space/allocator.rs +++ b/atom/src/space/allocator.rs @@ -118,8 +118,8 @@ pub trait SpaceWriter: SpaceAllocator + Sized { /// let mut buffer = vec![0; 64]; /// let mut writer = SpaceCursor::new(&mut buffer); /// - /// let allocated = writer.allocate_aligned(5).unwrap(); - /// assert_eq!(allocated.len(), 5); + /// let allocated = writer.allocate_aligned::(5).unwrap(); + /// assert_eq!(allocated.bytes_len(), 5); /// ``` /// /// # Errors diff --git a/atom/src/space/reader.rs b/atom/src/space/reader.rs index 93d65058..90805d08 100644 --- a/atom/src/space/reader.rs +++ b/atom/src/space/reader.rs @@ -1,6 +1,7 @@ use crate::atom_prelude::*; use std::mem::MaybeUninit; +/// A cursor-like struct to help read contiguous memory regions for atoms. #[derive(Clone)] pub struct SpaceReader<'a> { space: &'a [u8], @@ -56,26 +57,52 @@ impl<'a> SpaceReader<'a> { } #[inline] - fn as_uninit_slice(&self) -> Result<&'a [MaybeUninit], AtomReadError> { + fn as_uninit_slice(&self) -> Result<&'a [MaybeUninit], AlignmentError> { let space = AlignedSpace::align_from_bytes(self.space)?; Ok(space.as_uninit_slice()) } + /// Returns the remaining bytes as a slice of type a given type `T`. + /// + /// # Errors + /// + /// This methods returns an error if the slice couldn't get correctly aligned for the type `T`. + /// + /// # Safety + /// + /// The caller of this method has to ensure the buffer is filled with properly initialized + /// values of type `T`. #[inline] - pub unsafe fn as_slice(&self) -> Result<&'a [T], AtomReadError> { + pub unsafe fn as_slice(&self) -> Result<&'a [T], AlignmentError> { self.as_uninit_slice() .map(|s| crate::util::assume_init_slice(s)) } + /// Returns the next remaining bytes as a slice of type a given type `T` and length. + /// + /// # Errors + /// + /// This methods returns an error if the slice couldn't get correctly aligned for the type `T`, + /// or if `length` is out of bounds. + /// + /// # Safety + /// + /// The caller of this method has to ensure the requested slice is filled with properly + /// initialized values of type `T`. #[inline] - pub unsafe fn next_values( + pub unsafe fn next_values( &mut self, length: usize, - ) -> Result<&'a [U], AtomReadError> { + ) -> Result<&'a [T], AtomReadError> { self.next_uninit_value_slice(length) .map(|s| crate::util::assume_init_slice(s)) } + /// Returns the next `length` bytes. + /// + /// # Errors + /// + /// Returns an error if `length` is out of bounds. #[inline] pub fn next_bytes(&mut self, length: usize) -> Result<&'a [u8], AtomReadError> { let bytes = self @@ -91,12 +118,36 @@ impl<'a> SpaceReader<'a> { Ok(bytes) } + /// Returns the next value as a given type `T`. + /// + /// # Errors + /// + /// Returns an error if the value is too big for the remaining buffer, or if the buffer cannot + /// be aligned to match the value's alignment requirements. + /// + /// # Safety + /// + /// The caller is responsible to ensure that a properly initialized value of type `T` is present. #[inline] - pub unsafe fn next_value(&mut self) -> Result<&'a U, AtomReadError> { + pub unsafe fn next_value(&mut self) -> Result<&'a T, AtomReadError> { self.next_uninit_value() .map(|v| crate::util::assume_init_ref(v)) } + /// Returns the next atom. + /// + /// This method reads the next atom header, and then returns both the header and the associated + /// body as an [`UnidentifiedAtom`]. + /// + /// # Errors + /// + /// Returns an error if the value is too big for the remaining buffer, if the buffer cannot + /// be aligned to match the value's alignment requirements, or if the atom's body side is out + /// of bounds. + /// + /// # Safety + /// + /// The caller is responsible to ensure that a properly initialized atom is present. #[inline] pub unsafe fn next_atom(&mut self) -> Result<&'a UnidentifiedAtom, AtomReadError> { let space = AlignedSpace::::align_from_bytes(self.space)?; @@ -120,12 +171,17 @@ impl<'a> SpaceReader<'a> { self.space } + /// Performs a given reading operation, but only advance the cursor if the operation is successful. + /// + /// # Errors + /// + /// Returns whichever errors the given operation handler returned. #[inline] pub fn try_read(&mut self, read_handler: F) -> Result where F: FnOnce(&mut Self) -> Result, { - let mut reader = Self { space: self.space }; + let mut reader = self.clone(); let value = read_handler(&mut reader)?; self.space = reader.remaining_bytes(); From 1f46b6087b228e69b538dc0928c12cafc82af98e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:25:56 +0200 Subject: [PATCH 48/54] Finished all clippy-required docs! --- atom/src/atoms/vector.rs | 53 ++++++++++++++++++++++++++++++++++++++-- atom/src/unidentified.rs | 15 ++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/atom/src/atoms/vector.rs b/atom/src/atoms/vector.rs index cc2fb279..e09a81cb 100644 --- a/atom/src/atoms/vector.rs +++ b/atom/src/atoms/vector.rs @@ -57,12 +57,20 @@ impl<'a> AtomHandle<'a> for VectorWriteHandle { type Handle = VectorTypeWriter<'a>; } +/// A type-state for the Vector Reader, that reads the header of a vector to figure out its internal +/// type. pub struct VectorReader<'a> { reader: SpaceReader<'a>, header: &'a sys::LV2_Atom_Vector_Body, } impl<'a> VectorReader<'a> { + /// Attempts to read the vector as containing a given atom type. + /// + /// # Errors + /// + /// This method will return an error if the type or size of the atoms contained do not match the + /// vector being currently read. pub fn of_type( self, atom_type: URID, @@ -92,6 +100,26 @@ impl<'a> VectorReader<'a> { // properly initialized by the host. Ok(unsafe { self.reader.as_slice() }?) } + + /// Returns the length, i.e. number of elements in the vector, without knowing their type. + /// + /// This can be figured out thanks to the `child_size` attribute in a vector atom header. + /// + /// This will always return zero if the elements are zero-sized. + #[inline] + pub fn len(&self) -> usize { + self.reader + .remaining_bytes() + .len() + .checked_div(self.header.child_size as usize) + .unwrap_or(0) + } + + /// Returns if the vector is empty, i.e. its `len` is zero. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } pub struct VectorTypeWriter<'a> { @@ -99,6 +127,12 @@ pub struct VectorTypeWriter<'a> { } impl<'a> VectorTypeWriter<'a> { + /// Initializes the vector with the given child type URID. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. pub fn of_type( mut self, atom_type: URID, @@ -147,14 +181,24 @@ pub struct VectorWriter<'a, A: ScalarAtom> { impl<'a, A: ScalarAtom> VectorWriter<'a, A> { /// Push a single value to the vector. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. #[inline] pub fn push(&mut self, child: A::InternalType) -> Result<&mut A::InternalType, AtomWriteError> { self.writer.write_value(child) } - /// Append a slice of undefined memory to the vector. + /// Allocates a slice of initialized memory from the vector. /// - /// Using this method, you don't need to have the elements in memory before you can write them. + /// This is useful if you need deferred initialization of the vector's contents. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. #[inline] pub fn allocate_uninit( &mut self, @@ -164,6 +208,11 @@ impl<'a, A: ScalarAtom> VectorWriter<'a, A> { } /// Append multiple elements to the vector. + /// + /// # Errors + /// + /// This method will return an error if there is not enough space in the underlying buffer, + /// or if any other write error occurs. #[inline] pub fn append( &mut self, diff --git a/atom/src/unidentified.rs b/atom/src/unidentified.rs index 751936d8..399eea7c 100644 --- a/atom/src/unidentified.rs +++ b/atom/src/unidentified.rs @@ -27,6 +27,10 @@ impl UnidentifiedAtom { /// Construct a new unidentified atom. /// + /// # Errors + /// + /// This method will return an error if the atom's header is out of bounds of the given buffer. + /// /// # Safety /// /// The caller has to ensure that the given space actually contains both a valid atom header, and a valid corresponding atom body. @@ -57,9 +61,16 @@ impl UnidentifiedAtom { &mut *(header as *mut _ as *mut _) } - /// Try to read the atom. + /// Try to read the atom as being of a given type. + /// + /// If the atom was identified, a reading handle is returned. + /// + /// # Errors + /// + /// This method will return an error if the atom's type does not match the given URID. /// - /// To identify the atom, its URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned. + /// An error will also be returned if the atom's header is out of bounds, or if any other + /// read error occurs. pub fn read( &self, urid: URID, From 328abe4e6807d55a5ccd392c68ebeeeda98eb49f Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Sep 2021 22:47:12 +0200 Subject: [PATCH 49/54] Initial WIP on implementing lv2-options and lv2-buf-size (squashed & rebased) --- Cargo.toml | 14 +++ buf-size/Cargo.toml | 14 +++ buf-size/src/buffer_sizes.rs | 51 +++++++++ buf-size/src/lib.rs | 4 + buf-size/src/options.rs | 44 ++++++++ options/Cargo.toml | 14 +++ options/LICENSE-APACHE | 201 ++++++++++++++++++++++++++++++++++ options/LICENSE-MIT | 23 ++++ options/src/collection.rs | 90 +++++++++++++++ options/src/extensions.rs | 187 +++++++++++++++++++++++++++++++ options/src/lib.rs | 41 +++++++ options/src/list.rs | 70 ++++++++++++ options/src/option.rs | 48 ++++++++ options/src/option/error.rs | 54 +++++++++ options/src/option/request.rs | 118 ++++++++++++++++++++ options/src/option/subject.rs | 40 +++++++ options/src/option/value.rs | 60 ++++++++++ options/tests/optionable.rs | 200 +++++++++++++++++++++++++++++++++ sys/src/windows.rs | 2 +- 19 files changed, 1274 insertions(+), 1 deletion(-) create mode 100644 buf-size/Cargo.toml create mode 100644 buf-size/src/buffer_sizes.rs create mode 100644 buf-size/src/lib.rs create mode 100644 buf-size/src/options.rs create mode 100644 options/Cargo.toml create mode 100644 options/LICENSE-APACHE create mode 100644 options/LICENSE-MIT create mode 100644 options/src/collection.rs create mode 100644 options/src/extensions.rs create mode 100644 options/src/lib.rs create mode 100644 options/src/list.rs create mode 100644 options/src/option.rs create mode 100644 options/src/option/error.rs create mode 100644 options/src/option/request.rs create mode 100644 options/src/option/subject.rs create mode 100644 options/src/option/value.rs create mode 100644 options/tests/optionable.rs diff --git a/Cargo.toml b/Cargo.toml index 01abe7d6..6037f529 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,10 @@ maintenance = { status = "actively-developed" } version = "2.0.0" optional = true +[dependencies.lv2-buf-size] +version = "0.1.0" +optional = true + [dependencies.lv2-core] version = "3.0.0" optional = true @@ -27,6 +31,10 @@ optional = true version = "1.2.0" optional = true +[dependencies.lv2-options] +version = "0.1.0" +optional = true + [dependencies.lv2-time] version = "0.1.3" optional = true @@ -71,8 +79,10 @@ plugin = [ ] full = [ "lv2-atom", + "lv2-buf-size", "lv2-core", "lv2-midi", + "lv2-options", "lv2-time", "lv2-units", "urid", @@ -86,9 +96,11 @@ wmidi = ["lv2-midi", "lv2-midi/wmidi"] [workspace] members = [ "atom", + "buf-size", "core", "core/derive", "midi", + "options", "state", "sys", "sys/tool", @@ -110,9 +122,11 @@ lto = true [patch.crates-io] lv2 = { path = "." } lv2-atom = { path = "atom" } +lv2-buf-size = { path = "buf-size" } lv2-core = { path = "core" } lv2-core-derive = { path = "core/derive" } lv2-midi = { path = "midi" } +lv2-options = { path = "options" } lv2-state = { path = "state" } lv2-sys = { path = "sys" } lv2-time = { path = "time" } diff --git a/buf-size/Cargo.toml b/buf-size/Cargo.toml new file mode 100644 index 00000000..b553a094 --- /dev/null +++ b/buf-size/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lv2-buf-size" +version = "0.1.0" +authors = ["Adrien Prokopowicz "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lv2-atom = "2.0.0" +lv2-core = "3.0.0" +lv2-options = "0.1.0" +lv2-sys = "2.0.0" +urid = "0.1.0" \ No newline at end of file diff --git a/buf-size/src/buffer_sizes.rs b/buf-size/src/buffer_sizes.rs new file mode 100644 index 00000000..ba135161 --- /dev/null +++ b/buf-size/src/buffer_sizes.rs @@ -0,0 +1,51 @@ +use crate::options::*; +use lv2_atom::prelude::Int; +use lv2_options::features::OptionsList; +use urid::{Map, URIDCollection, URID}; + +#[derive(URIDCollection)] +pub struct BufferSizesURIDCollection { + pub atom_int: URID, + pub min_block_length: URID, + pub max_block_length: URID, + pub nominal_block_length: URID, + pub sequence_size: URID, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct BufferSizes { + min_block_length: Option, + max_block_length: Option, + nominal_block_length: Option, + sequence_size: Option, +} + +impl BufferSizes { + pub fn from_options(options: &OptionsList, mapper: &impl Map) -> Self { + let collection = match mapper.populate_collection() { + Some(c) => c, + None => return Default::default(), + }; + BufferSizes::from_options_with_urids(options, &collection) + } + + pub fn from_options_with_urids( + options: &OptionsList, + urids: &BufferSizesURIDCollection, + ) -> Self { + BufferSizes { + min_block_length: options + .read(urids.min_block_length, urids.atom_int, ()) + .map(|x| x.get()), + max_block_length: options + .read(urids.max_block_length, urids.atom_int, ()) + .map(|x| x.get()), + nominal_block_length: options + .read(urids.nominal_block_length, urids.atom_int, ()) + .map(|x| x.get()), + sequence_size: options + .read(urids.sequence_size, urids.atom_int, ()) + .map(|x| x.get()), + } + } +} diff --git a/buf-size/src/lib.rs b/buf-size/src/lib.rs new file mode 100644 index 00000000..9e2006d0 --- /dev/null +++ b/buf-size/src/lib.rs @@ -0,0 +1,4 @@ +mod buffer_sizes; +pub mod options; + +pub use buffer_sizes::{BufferSizes, BufferSizesURIDCollection}; diff --git a/buf-size/src/options.rs b/buf-size/src/options.rs new file mode 100644 index 00000000..83b60ac1 --- /dev/null +++ b/buf-size/src/options.rs @@ -0,0 +1,44 @@ +use lv2_atom::Atom; +use urid::UriBound; + +/// A simple macro to automate the definition of the u32 options available in this module +macro_rules! make_option { + ($name:ident, $uri:expr) => { + pub struct $name(i32); + + impl $name { + #[inline] + pub fn get(&self) -> u32 { + self.0 as u32 + } + } + + unsafe impl UriBound for $name { + const URI: &'static [u8] = $uri; + } + + impl lv2_options::OptionType for $name { + type AtomType = lv2_atom::scalar::Int; + + #[inline] + fn from_option_value<'a>( + value: >::ReadHandle, + ) -> Option { + Some(Self((*value))) + } + + #[inline] + fn as_option_value<'a>(&'a self) -> >::ReadHandle { + &self.0 + } + } + }; +} + +make_option!(MinBlockLength, lv2_sys::LV2_BUF_SIZE__minBlockLength); +make_option!(MaxBlockLength, lv2_sys::LV2_BUF_SIZE__maxBlockLength); +make_option!( + NominalBlockLength, + lv2_sys::LV2_BUF_SIZE__nominalBlockLength +); +make_option!(SequenceSize, lv2_sys::LV2_BUF_SIZE__sequenceSize); diff --git a/options/Cargo.toml b/options/Cargo.toml new file mode 100644 index 00000000..8db4726e --- /dev/null +++ b/options/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lv2-options" +version = "0.1.0" +authors = ["Adrien Prokopowicz "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lv2-atom = "2.0.0" +lv2-core = "3.0.0" +lv2-sys = "2.0.0" +lv2-urid = "2.1.0" +urid = "0.1.0" \ No newline at end of file diff --git a/options/LICENSE-APACHE b/options/LICENSE-APACHE new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/options/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/options/LICENSE-MIT b/options/LICENSE-MIT new file mode 100644 index 00000000..468cd79a --- /dev/null +++ b/options/LICENSE-MIT @@ -0,0 +1,23 @@ +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 +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. \ No newline at end of file diff --git a/options/src/collection.rs b/options/src/collection.rs new file mode 100644 index 00000000..383289cc --- /dev/null +++ b/options/src/collection.rs @@ -0,0 +1,90 @@ +use urid::{URIDCollection, Map}; +use crate::request::OptionRequestList; +use crate::{OptionsError, OptionValue}; +use crate::list::OptionsList; +use crate::option::request::OptionRequest; + +pub trait OptionsCollection: Sized { + type Serializer; + + #[inline] + fn new_serializer<'a, M: Map + ?Sized>(map: &M) -> Option> + where Self::Serializer: OptionsSerializationContext<'a, Self> { // FIXME + Some(OptionsSerializer { inner: Self::Serializer::from_map(map)? }) + } +} + +#[doc(hidden)] +pub mod implementation { + use crate::{OptionType, OptionsError, OptionValue}; + use std::marker::PhantomData; + use urid::{URID, URIDCollection, Map}; + use crate::collection::{OptionsSerializationContext, OptionsCollection}; + use crate::option::request::OptionRequest; + use lv2_atom::{Atom, BackAsSpace}; + use lv2_atom::scalar::ScalarAtom; + + pub struct OptionTypeSerializationContext { + option_urid: URID, + option_type_atom_urid: URID + } + + impl<'a, O: OptionType> OptionsCollection for O + where ::AtomType: BackAsSpace<'a>, + <::AtomType as Atom<'a, 'a>>::ReadParameter: Default { + type Serializer = OptionTypeSerializationContext; + } + + impl URIDCollection for OptionTypeSerializationContext { + #[inline] + fn from_map(map: &M) -> Option { + Some(Self { + option_urid: map.populate_collection()?, + option_type_atom_urid: map.populate_collection()?, + }) + } + } + + impl<'a, O: OptionType> OptionsSerializationContext<'a, O> for OptionTypeSerializationContext + where ::AtomType: BackAsSpace<'a>, + <::AtomType as Atom<'a, 'a>>::ReadParameter: Default { + #[inline] + fn deserialize_new(&self, option: &'a OptionValue) -> Option { + option.read(self.option_urid, self.option_type_atom_urid, Default::default()) + } + + fn deserialize_to(&self, options: &mut O, option: &OptionValue) -> Result<(), OptionsError> { + todo!() + } + + fn respond_to_request<'r>(&self, options: &'r O, requests: &'r mut OptionRequest) -> Result<(), OptionsError> { + todo!() + } + } +} + +pub struct OptionsSerializer { + inner: T::Serializer +} + +impl OptionsSerializer { + pub fn deserialize_new(&self, list: &OptionsList) -> Option { + todo!() + } + + pub fn deserialize_to(&self, options: &mut T, list: &OptionsList) -> Result<(), OptionsError> { + todo!() + } + + pub fn respond_to_requests<'a>(&self, options: &T, requests: &mut OptionRequestList) -> Result<(), OptionsError> { + todo!() + } +} + +pub trait OptionsSerializationContext<'a, T: OptionsCollection>: URIDCollection { + fn deserialize_new(&self, option: &'a OptionValue) -> Option; + + fn deserialize_to(&self, options: &mut T, option: &OptionValue) -> Result<(), OptionsError>; + + fn respond_to_request<'r>(&self, options: &'r T, request: &'r mut OptionRequest) -> Result<(), OptionsError>; +} \ No newline at end of file diff --git a/options/src/extensions.rs b/options/src/extensions.rs new file mode 100644 index 00000000..be92e6d5 --- /dev/null +++ b/options/src/extensions.rs @@ -0,0 +1,187 @@ +//! Contains the [`OptionsInterface`](crate::extensions::OptionsInterface) extension interface. +use crate::features::OptionsList; +use crate::OptionsError; +use lv2_core::feature::Feature; +use lv2_core::plugin::PluginInstance; +use lv2_core::prelude::Plugin; +use lv2_core::prelude::{ExtensionDescriptor, ThreadingClass}; +use std::ffi::c_void; +use std::marker::PhantomData; +use std::panic::AssertUnwindSafe; +use urid::UriBound; +use crate::option::request::OptionRequestList; + +/// An interface to allow dynamically setting options from the host. +/// +/// # Example +/// +/// +/// +/// ``` +/// # use lv2_core::prelude::*; +/// # use lv2_options::OptionType; +/// # use lv2_options::prelude::*; +/// # +/// # use urid::{URID, Uri, URIDCollection, uri, Map, UriBound}; +/// # use std::any::Any; +/// # +/// # impl<'a> OptionType<'a> for SomeIntOption { +/// # type AtomType = lv2_atom::scalar::Int; +/// # +/// # fn from_option_value(value: &i32) -> Option { +/// # Some(Self(*value)) +/// # } +/// # +/// # fn as_option_value(&'a self) -> &'a i32 { +/// # &self.0 +/// # } +/// # } +/// # +/// # #[derive(URIDCollection)] +/// pub struct PluginUridCollection { +/// some_int_option: URID, +/// int: URID, +/// } +/// # +/// # #[derive(FeatureCollection)] +/// # pub struct PluginFeatures<'a> { +/// # options: OptionsList<'a>, +/// # } +/// +/// # #[uri("urn:lv2_options:test:OptionablePlugin")] +/// pub struct OptionablePlugin { +/// some_int: SomeIntOption, +/// urids: PluginUridCollection, +/// } +/// # +/// # impl Plugin for OptionablePlugin { +/// # type Ports = (); +/// # type InitFeatures = PluginFeatures<'static>; +/// # type AudioFeatures = (); +/// # +/// # fn new(_plugin_info: &PluginInfo, _features: &mut Self::InitFeatures) -> Option { +/// # unimplemented!() +/// # } +///# +/// # fn run( +/// # &mut self, +/// # _ports: &mut Self::Ports, +/// # _features: &mut Self::AudioFeatures, +/// # _sample_count: u32, +/// # ) { +/// # unimplemented!() +/// # } +/// # +/// # fn extension_data(uri: &Uri) -> Option<&'static dyn Any> { +/// # unimplemented!() +/// # } +/// # } +/// # +/// +/// #[uri("urn:lv2_options:test:SomeIntOption")] +/// pub struct SomeIntOption(i32); +/// +/// impl OptionsInterface for OptionablePlugin { +/// fn get<'a>(&'a self, mut writer: OptionsWriter<'a>) -> Result<(), OptionsError> { +/// writer.process(|subject, options| match subject { // We will want to get/set our opions differently depending on the subject +/// Subject::Instance => { // In our case however, only our instance has an option +/// options.handle(self.urids.some_int_option, self.urids.int, || { +/// &self.some_int +/// }); +/// } +/// _ => {} +/// }) +/// } +/// +/// fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { +/// options.process(|subject, options| match subject { +/// Subject::Instance => { +/// options.handle(self.urids.some_int_option, self.urids.int, (), |value| { +/// self.some_int = value +/// }) +/// } +/// _ => {} +/// }) +/// } +/// } +/// ``` +pub trait OptionsInterface: Plugin { + /// Allows the host to retrieve the value of the given options, as currently stored by the plugin. + /// + /// If the given options are unknown or somehow invalid, the appropriate [`OptionsError`] is returned. + /// + /// See the documentation for the [`OptionsInterface`] type for an example of how to implement this method. + fn get(&self, requests: OptionRequestList) -> Result<(), OptionsError>; + + /// Allows the host to set the plugin's values for the given options. + /// + /// If the given options are unknown or somehow invalid, the appropriate [`OptionsError`] is returned. + /// + /// See the documentation for the [`OptionsInterface`] type for an example of how to implement this method. + fn set(&mut self, options: OptionsList) -> Result<(), OptionsError>; +} + +/// The Extension Descriptor associated to [`OptionsInterface`]. +pub struct OptionsDescriptor { + plugin: PhantomData

, +} + +unsafe impl UriBound for OptionsDescriptor

{ + const URI: &'static [u8] = lv2_sys::LV2_OPTIONS__interface; +} + +impl OptionsDescriptor

{ + unsafe extern "C" fn get( + instance: *mut c_void, + options_list: *mut lv2_sys::LV2_Options_Option, + ) -> lv2_sys::LV2_Options_Status { + let instance = match (instance as *mut PluginInstance

).as_mut() { + Some(instance) => instance, + None => return lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + } + .plugin_handle(); + + let options_list = match options_list.as_mut() { + Some(options_list) => options_list, + None => return lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + }; + + let requests = OptionRequestList::from_mut(options_list); + + match std::panic::catch_unwind(AssertUnwindSafe(|| instance.get(requests))) { + Ok(r) => OptionsError::result_into_raw(r), + Err(_) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + } + } + + unsafe extern "C" fn set( + instance: *mut c_void, + options_list: *const lv2_sys::LV2_Options_Option, + ) -> lv2_sys::LV2_Options_Status { + let instance = match (instance as *mut PluginInstance

).as_mut() { + Some(instance) => instance, + None => return lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + } + .plugin_handle(); + + let options = + match OptionsList::from_feature_ptr(options_list.cast(), ThreadingClass::Instantiation) + { + Some(options) => options, + None => return lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + }; + + match std::panic::catch_unwind(AssertUnwindSafe(|| instance.set(options))) { + Ok(r) => OptionsError::result_into_raw(r), + Err(_) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + } + } +} + +impl ExtensionDescriptor for OptionsDescriptor

{ + type ExtensionInterface = lv2_sys::LV2_Options_Interface; + const INTERFACE: &'static Self::ExtensionInterface = &lv2_sys::LV2_Options_Interface { + get: Some(Self::get), + set: Some(Self::set), + }; +} diff --git a/options/src/lib.rs b/options/src/lib.rs new file mode 100644 index 00000000..5ba23eb8 --- /dev/null +++ b/options/src/lib.rs @@ -0,0 +1,41 @@ +//! This crate enables the use of the [Options](http://lv2plug.in/ns/ext/options) LV2 Extension. +//! +//! Options are configuration values which the host passes to the plugin (or its UI) at runtime. +//! +//! There are two facilities available for plugins to deal with options: +//! +//! * The [`OptionsList`](features::OptionsList) [feature](lv2_core::feature), to get a list of all +//! available options at directly instantiation time. +//! * The [`OptionsInterface`](extensions::OptionsInterface) [extension interface](lv2_core::extension), +//! to allow the host to dynamically set or retrieve options after instantiation. +//! +//! Note that this extension is only for allowing hosts to configure plugins, and is not a "live" +//! control mechanism. +//! For real-time control, use event-based control via an [`AtomPort`](lv2_atom::port::AtomPort) +//! with a [`Sequence`](lv2_atom::sequence::Sequence) buffer. +//! +//! See the [LV2 Options documentation](http://lv2plug.in/ns/ext/options) for more information. + +pub use option::error::OptionsError; +pub use option::OptionType; +pub use option::request; +pub use option::subject::Subject; +pub use option::value::OptionValue; + +pub mod extensions; +mod option; +pub mod list; +pub mod collection; + +/// Contains the [`OptionsList`](features::OptionsList) feature. +pub mod features { + pub use crate::list::OptionsList; +} + +/// Prelude of `lv2_options` for wildcard usage. +pub mod prelude { + pub use crate::list::OptionsList; + pub use crate::OptionsError; + pub use crate::Subject; + pub use crate::extensions::{OptionsDescriptor, OptionsInterface}; +} \ No newline at end of file diff --git a/options/src/list.rs b/options/src/list.rs new file mode 100644 index 00000000..ffd215b7 --- /dev/null +++ b/options/src/list.rs @@ -0,0 +1,70 @@ +use crate::OptionValue; +use core::ffi::c_void; +use lv2_core::feature::{Feature, ThreadingClass}; +use urid::UriBound; + +/// A read-only list of [`OptionValue`]s, sent from the host. +/// +/// This list type doesn't own the contained values: they are simply borrowed from the host. +/// Cloning this struct only clones the list, not its contents. +#[derive(Copy, Clone)] +pub struct OptionsList<'a> { + options_list: &'a lv2_sys::LV2_Options_Option, +} + +impl<'f> OptionsList<'f> { + /// Returns an iterator over the slice. + #[inline] + pub fn iter(&self) -> OptionsListIter<'f> { + OptionsListIter { current: self.options_list } + } +} + +unsafe impl<'a> UriBound for OptionsList<'a> { + const URI: &'static [u8] = lv2_sys::LV2_OPTIONS__options; +} + +unsafe impl<'a> Feature for OptionsList<'a> { + #[inline] + unsafe fn from_feature_ptr(feature: *const c_void, _class: ThreadingClass) -> Option { + // SAFETY: Type and contents of feature data pointer is guaranteed by caller + Some(OptionsList { + options_list: (feature as *const _ as *mut lv2_sys::LV2_Options_Option).as_ref()?, + }) + } +} + +impl<'a> IntoIterator for &'a OptionsList<'a> { + type Item = &'a OptionValue; + type IntoIter = OptionsListIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub struct OptionsListIter<'a> { + current: &'a lv2_sys::LV2_Options_Option +} + +impl<'a> Iterator for OptionsListIter<'a> { + type Item = &'a OptionValue; + + #[inline] + fn next(&mut self) -> Option { + let item = self.current; + if item.key == 0 { + return None; + } + + // SAFETY: list is guaranteed by the host to end with a zeroed struct. + // Therefore the pointer always points to a valid lv2_sys::LV2_Options_Option value + // (only its contents may be incorrect if zeroed) + unsafe { + self.current = &*((self.current as *const lv2_sys::LV2_Options_Option).add(1)); + } + + Some(OptionValue::from_ref(item)) + } +} diff --git a/options/src/option.rs b/options/src/option.rs new file mode 100644 index 00000000..8cbcbdc9 --- /dev/null +++ b/options/src/option.rs @@ -0,0 +1,48 @@ +use lv2_atom::Atom; +use urid::UriBound; + +pub mod error; +pub mod subject; +pub mod value; +pub mod request; + +/// A trait representing an LV2 Option type. +/// +/// # Example +/// +/// This example implements a simple option type named "MyIntOption" backed by an Int atom. +/// +/// ``` +/// use lv2_options::OptionType; +/// use urid::*; +/// +/// #[uri("urn:lv2_options:test:SomeIntOption")] +/// pub struct SomeIntOption(i32); +/// +/// impl OptionType for SomeIntOption { +/// type AtomType = lv2_atom::scalar::Int; +/// +/// fn from_option_value(value: &i32) -> Option { +/// Some(Self(*value)) +/// } +/// +/// fn as_option_value(&self) -> &i32 { +/// &self.0 +/// } +/// } +/// ``` +pub trait OptionType: UriBound + Sized { + type AtomType: UriBound; + + /// Creates a new instance of this Option type from a given atom value. + /// + /// This method may return `None` if the Atom's value is invalid for this option type. + /// + /// This method is used to store option data when received by the host. + fn from_option_value<'a>(value: >::ReadHandle) -> Option where Self::AtomType: Atom<'a, 'a>; + + /// Returns this Option's value as a reference to its Atom type. + /// + /// This method is used to send the option's value to the host when it is requested. + fn as_option_value<'a>(&'a self) -> >::ReadHandle where Self::AtomType: Atom<'a, 'a>; +} diff --git a/options/src/option/error.rs b/options/src/option/error.rs new file mode 100644 index 00000000..1120acdf --- /dev/null +++ b/options/src/option/error.rs @@ -0,0 +1,54 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; + +/// Errors raised when interacting with LV2 Options. +/// +/// See the [LV2 Documentation](https://lv2plug.in/doc/html/group__options.html#ga94d649a74ab340dfc6c6751dbe92ca07) +/// for more information. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +pub enum OptionsError { + Unknown = lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN as isize, + BadSubject = lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT as isize, + BadKey = lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY as isize, + BadValue = lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE as isize, +} + +impl OptionsError { + #[inline] + pub(crate) fn result_into_raw(value: Result<(), OptionsError>) -> lv2_sys::LV2_Options_Status { + match value { + Ok(()) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS, + Err(OptionsError::BadSubject) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT, + Err(OptionsError::BadKey) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY, + Err(OptionsError::BadValue) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE, + Err(_) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, + } + } + + #[inline] + pub(crate) fn from_raw(status: lv2_sys::LV2_Options_Status) -> Result<(), OptionsError> { + match status { + lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS => Ok(()), + lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT => Err(OptionsError::BadSubject), + lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY => Err(OptionsError::BadKey), + lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE => Err(OptionsError::BadValue), + _ => Err(OptionsError::Unknown), + } + } +} + +impl Display for OptionsError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let msg = match self { + OptionsError::Unknown => "Unknown error while reading/writing Option", + OptionsError::BadSubject => "Unknown Option subject", + OptionsError::BadKey => "Unknown Option key", + OptionsError::BadValue => "Invalid Option value" + }; + + write!(f, "{}", msg) + } +} + +impl Error for OptionsError {} diff --git a/options/src/option/request.rs b/options/src/option/request.rs new file mode 100644 index 00000000..916b4981 --- /dev/null +++ b/options/src/option/request.rs @@ -0,0 +1,118 @@ +use urid::URID; +use lv2_atom::{Atom, BackAsSpace}; +use crate::{Subject, OptionType, OptionsError}; + +#[repr(C)] +// SAFETY: The last fields are left uninitialized by the host, it's up to the plugin to set them +pub struct OptionRequest { + inner: lv2_sys::LV2_Options_Option +} + +impl OptionRequest { + #[inline] + pub(crate)fn from_mut(option: &mut lv2_sys::LV2_Options_Option) -> &mut Self { + // SAFETY: lv2_sys::LV2_Options_Option and OptionRequest have the same memory layout + unsafe {&mut *(option as *mut lv2_sys::LV2_Options_Option as *mut Self) } + } + + #[inline] + pub fn subject(&self) -> core::option::Option { + Subject::from_raw(self.inner.context, self.inner.subject) + } + + #[inline] + pub fn option_type(&self) -> Option { + URID::new(self.inner.key) + } + + #[inline] + pub fn is(&self, urid: URID) -> bool { + self.inner.key == urid + } + + #[inline] + pub fn has_response(&self) -> bool { self.inner.type_ != 0 } + + #[inline] + pub fn try_respond<'a, T: OptionType>(&'a mut self, + option_type: URID, + atom_type: URID, + value: &'a T + ) -> Result<(), OptionsError> where T::AtomType: BackAsSpace<'a> { + if !self.is(option_type) { + return Err(OptionsError::BadKey); + } + + unsafe { self.set_value_unchecked(atom_type, value.as_option_value()) }; + + Ok(()) + } + + #[inline] + unsafe fn set_value_unchecked<'a, T: Atom<'a, 'a>>( + &'a mut self, + value_type: URID, + value_handle: T::ReadHandle, + ) where + T: BackAsSpace<'a>, + { + let data = T::back_as_space(value_handle); + self.inner.type_ = value_type.get(); + self.inner.size = data.len() as u32; + self.inner.value = data.data().unwrap().as_ptr().cast(); + } +} + +pub struct OptionRequestList<'a> { + ptr: &'a mut lv2_sys::LV2_Options_Option +} + +impl<'a> OptionRequestList<'a> { + pub fn iter_mut<'list: 'a>(&'list mut self) -> OptionRequestListIter<'a> { + OptionRequestListIter { current: self.ptr } + } +} + +impl<'a> OptionRequestList<'a> { + /// SAFETY: Caller must ensure pointer actually points to the start of a zero-terminated list. + #[inline] + pub(crate) unsafe fn from_mut(ptr: &'a mut lv2_sys::LV2_Options_Option) -> Self { + Self { ptr } + } +} + +impl<'a, 'list: 'a> IntoIterator for &'list mut OptionRequestList<'a >{ + type Item = &'a mut OptionRequest; + type IntoIter = OptionRequestListIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +pub struct OptionRequestListIter<'a> { + current: &'a mut lv2_sys::LV2_Options_Option +} + +impl<'a> Iterator for OptionRequestListIter<'a> { + type Item = &'a mut OptionRequest; + + #[inline] + fn next(&mut self) -> Option { + if self.current.key == 0 { + return None; + } + + // SAFETY: list is guaranteed by the host to end with a zeroed struct. + // Therefore the pointer always points to a valid lv2_sys::LV2_Options_Option value + // (only its contents may be incorrect if zeroed) + let next = unsafe { + &mut *((self.current as *mut lv2_sys::LV2_Options_Option).add(1)) + }; + + let item = core::mem::replace(&mut self.current, next); + + Some(OptionRequest::from_mut(item)) + } +} \ No newline at end of file diff --git a/options/src/option/subject.rs b/options/src/option/subject.rs new file mode 100644 index 00000000..a04f15d3 --- /dev/null +++ b/options/src/option/subject.rs @@ -0,0 +1,40 @@ +use urid::URID; + +/// The subject of an Option, i.e. what the option applies to. +/// +/// For instance, using a given Option `Foo`: +/// * A value of [`Subject::Instance`] means we are referring to the instance's `Foo` option; +/// * A value of [`Subject::Port`] means we are referring to a given port's `Foo` option; +/// * … and so on. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Subject { + /// The option applies to the instance itself. + Instance, + /// The option applies to some specific named ([`URID`](urid::URID)) resource. + /// The inner subject is the URID of said resource. + Resource(URID), + /// The option applies to some blank node. + /// The inner value is a blank node identifier, which is valid only within the current local scope. + Blank(u32), + /// This option applies to a port on the instance. + /// The inner value is the port's index. + Port(u32), // TODO: handle PortIndex more gracefully +} + +impl Subject { + #[inline] + pub(crate) fn from_raw( + context: lv2_sys::LV2_Options_Context, + subject: u32, + ) -> core::option::Option { + match context { + lv2_sys::LV2_Options_Context_LV2_OPTIONS_INSTANCE => Some(Subject::Instance), + lv2_sys::LV2_Options_Context_LV2_OPTIONS_RESOURCE => { + Some(Subject::Resource(URID::new(subject)?)) + } + lv2_sys::LV2_Options_Context_LV2_OPTIONS_BLANK => Some(Subject::Blank(subject)), + lv2_sys::LV2_Options_Context_LV2_OPTIONS_PORT => Some(Subject::Port(subject)), + _ => None, + } + } +} diff --git a/options/src/option/value.rs b/options/src/option/value.rs new file mode 100644 index 00000000..d763c473 --- /dev/null +++ b/options/src/option/value.rs @@ -0,0 +1,60 @@ +use crate::option::subject::Subject; +use lv2_atom::prelude::Space; +use lv2_atom::Atom; +use urid::URID; + +#[repr(C)] +pub struct OptionValue { + inner: lv2_sys::LV2_Options_Option, +} + +impl OptionValue { + #[inline] + pub(crate) fn from_ref(raw: &lv2_sys::LV2_Options_Option) -> &Self { + // SAFETY: lv2_sys::LV2_Options_Option and OptionValue are guaranteed to have the same memory layout. + unsafe { &*(raw as *const lv2_sys::LV2_Options_Option).cast() } + } + + #[inline] + pub fn subject(&self) -> core::option::Option { + Subject::from_raw(self.inner.context, self.inner.subject) + } + + #[inline] + pub fn is(&self, urid: URID) -> bool { + self.inner.key == urid + } + + #[inline] + pub fn data(&self) -> Option { + // SAFETY: lifetime of the returned atom value is guaranteed by lifetime of the current Option pointer + // And the validity of these pointers are guaranteed by the host + let slice = unsafe { + std::slice::from_raw_parts( + self.inner.value.cast::().as_ref()?, + self.inner.size as usize, + ) + }; + + Some(Space::from_slice(slice)) + } + + #[inline] + pub fn read<'a, T: crate::option::OptionType>( + &'a self, + option_type: URID, + data_type: URID, + data_type_parameter: >::ReadParameter, + ) -> Option where T::AtomType: Atom<'a, 'a> + { + if !self.is(option_type) { + return None; + } + + if self.inner.type_ != data_type { + return None; + } + + T::from_option_value(T::AtomType::read(self.data()?, data_type_parameter)?) + } +} diff --git a/options/tests/optionable.rs b/options/tests/optionable.rs new file mode 100644 index 00000000..c317c243 --- /dev/null +++ b/options/tests/optionable.rs @@ -0,0 +1,200 @@ +use lv2_atom::scalar::Int; +use lv2_core::prelude::*; +use lv2_options::prelude::*; +use lv2_urid::{HostMap, LV2Map}; +use std::any::Any; +use std::ffi::c_void; +use std::os::raw::c_char; +use std::pin::Pin; +use urid::{uri, HashURIDMapper, Uri, UriBound, URID}; +use urid::{Map, URIDCollection}; +use lv2_options::request::OptionRequestList; +use lv2_options::collection::{OptionsSerializer, OptionsCollection}; +use lv2_atom::Atom; + +#[uri("urn:lv2_options:test:SomeIntOption")] +pub struct MyIntOption(i32); + +impl lv2_options::OptionType for MyIntOption { + type AtomType = lv2_atom::scalar::Int; + + fn from_option_value<'a>(value: &'a i32) -> Option where Self::AtomType: Atom<'a, 'a> { + Some(Self(*value)) + } + + fn as_option_value<'a>(&'a self) -> &'a i32 where Self::AtomType: Atom<'a, 'a> { + &self.0 + } +} + +pub struct MyPluginOptions { + some_int_option: MyIntOption +} + +#[derive(FeatureCollection)] +pub struct PluginFeatures<'a> { + map: LV2Map<'a>, + options: OptionsList<'a>, +} + +#[uri("urn:lv2_options:test:OptionablePlugin")] +pub struct OptionablePlugin { + options: MyIntOption, + options_serializer: OptionsSerializer, +} + +impl Plugin for OptionablePlugin { + type Ports = (); + type InitFeatures = PluginFeatures<'static>; + type AudioFeatures = (); + + fn new(_plugin_info: &PluginInfo, features: &mut Self::InitFeatures) -> Option { + let options_serializer = MyIntOption::new_serializer(&features.map)?; + + let mut plugin = OptionablePlugin { + options_serializer, + options: options_serializer.deserialize_new(&features.options)? + }; + + Some(plugin) + } + + fn run( + &mut self, + _ports: &mut Self::Ports, + _features: &mut Self::AudioFeatures, + _sample_count: u32, + ) { + todo!() + } + + fn extension_data(uri: &Uri) -> Option<&'static dyn Any> { + match_extensions![uri, OptionsDescriptor] + } +} + +impl OptionsInterface for OptionablePlugin { + fn get<'a>(&'a self, mut requests: OptionRequestList<'a>) -> Result<(), OptionsError> { + self.options_serializer.respond_to_requests(&self.options, &mut requests) + } + + fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { + self.options_serializer.deserialize_to(&mut self.options, &options) + } +} + +lv2_descriptors! { + OptionablePlugin +} + +#[test] +pub fn test_optionable_plugin() { + use lv2_sys::*; + use urid::UriBound; + + // Instantiating all features. + let mut mapper: Pin>> = Box::pin(HashURIDMapper::new().into()); + let map_interface = Box::pin(mapper.as_mut().make_map_interface()); + let map = LV2Map::new(map_interface.as_ref().get_ref()); + + let mut map_feature_interface = Box::pin(mapper.as_mut().make_map_interface()); + let map_feature = Box::pin(lv2_sys::LV2_Feature { + URI: LV2Map::URI.as_ptr() as *const i8, + data: map_feature_interface.as_mut().get_mut() as *mut _ as *mut c_void, + }); + + let option_value = 42; + + let option = lv2_sys::LV2_Options_Option { + context: lv2_sys::LV2_Options_Context_LV2_OPTIONS_INSTANCE, + subject: 0, + key: map.map_type::().unwrap().get(), + size: ::core::mem::size_of::() as u32, + type_: map.map_type::().unwrap().get(), + value: core::ptr::null(), + }; + + let end = lv2_sys::LV2_Options_Option { + context: 0, + subject: 0, + key: 0, + size: 0, + type_: 0, + value: core::ptr::null(), + }; + + let options = &mut [option, end]; + options[0].value = &option_value as *const i32 as *const _; + + let options_feature = Box::pin(lv2_sys::LV2_Feature { + URI: OptionsList::URI.as_ptr() as *const i8, + data: options.as_mut() as *mut _ as *mut c_void, + }); + + let features_list: &[*const lv2_sys::LV2_Feature] = &[ + map_feature.as_ref().get_ref(), + options_feature.as_ref().get_ref(), + std::ptr::null(), + ]; + + unsafe { + // Retrieving the descriptor. + let descriptor: &LV2_Descriptor = lv2_descriptor(0).as_ref().unwrap(); + let option_interface: &LV2_Options_Interface = + descriptor.extension_data.unwrap()(lv2_sys::LV2_OPTIONS__interface.as_ptr().cast()) + .cast::() + .as_ref() + .unwrap(); + + // Constructing the plugin. + let plugin: LV2_Handle = (descriptor.instantiate.unwrap())( + descriptor, + 44100.0, + "/home/lv2/amp.lv2/\0".as_ptr() as *const c_char, + features_list.as_ptr(), + ); + assert_ne!(plugin, std::ptr::null_mut()); + + // Activating the plugin. + (descriptor.activate.unwrap())(plugin); + + // Getting option + options[0].value = core::ptr::null(); + options[0].type_ = 0; + options[0].size = 0; + + let ret = (option_interface.get.unwrap())(plugin, options.as_mut_ptr()); + assert_eq!(lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS, ret); + assert_eq!(map.map_type::().unwrap().get(), options[0].type_); + assert_eq!(::core::mem::size_of::() as u32, options[0].size); + assert_ne!(::core::ptr::null(), options[0].value); + assert_eq!(42, *(options[0].value as *const i32)); + + // Setting option + let new_value = 69; + options[0].value = &new_value as *const i32 as *const _; + + let ret = (option_interface.set.unwrap())(plugin, options.as_mut_ptr()); + assert_eq!(lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS, ret); + assert_eq!(69, *(options[0].value as *const i32)); + + // Getting new option back + options[0].value = core::ptr::null(); + options[0].type_ = 0; + options[0].size = 0; + + let ret = (option_interface.get.unwrap())(plugin, options.as_mut_ptr()); + + assert_eq!(lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS, ret); + assert_eq!(map.map_type::().unwrap().get(), options[0].type_); + assert_eq!(::core::mem::size_of::() as u32, options[0].size); + assert_ne!(::core::ptr::null(), options[0].value); + assert_eq!(69, *(options[0].value as *const i32)); + + // Deactivating the plugin. + (descriptor.deactivate.unwrap())(plugin); + + // Destroying the plugin. + (descriptor.cleanup.unwrap())(plugin) + } +} diff --git a/sys/src/windows.rs b/sys/src/windows.rs index 27397a77..001d6852 100644 --- a/sys/src/windows.rs +++ b/sys/src/windows.rs @@ -1755,7 +1755,7 @@ pub const LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY: LV2_Options_Status = 4; #[doc = "< Invalid/unsupported value."] pub const LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE: LV2_Options_Status = 8; #[doc = " A status code for option functions."] -pub type LV2_Options_Status = i32; +pub type LV2_Options_Status = u32; #[doc = "Interface for dynamically setting options (LV2_OPTIONS__interface)."] #[repr(C)] #[derive(Debug, Copy, Clone)] From 47a36bb1ba5b8760797b75abe4c95e8f14fcc4b5 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Sep 2021 00:03:59 +0200 Subject: [PATCH 50/54] Fix everything --- atom/src/atoms/scalar.rs | 6 ++++ buf-size/src/buffer_sizes.rs | 4 ++- buf-size/src/options.rs | 12 ++++--- options/src/collection.rs | 67 +++++++++++++++++++++++------------ options/src/extensions.rs | 2 +- options/src/lib.rs | 10 +++--- options/src/list.rs | 6 ++-- options/src/option.rs | 14 +++++--- options/src/option/error.rs | 10 ++++-- options/src/option/request.rs | 50 ++++++++++++++------------ options/src/option/value.rs | 19 +++++----- options/tests/optionable.rs | 24 +++++++------ 12 files changed, 137 insertions(+), 87 deletions(-) diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 53eaf84c..3b2e5a6c 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -92,6 +92,12 @@ impl Atom for A { } } +impl BackAsSpace for A { + fn back_as_space<'a>(handle: >::Handle) -> &'a [u8] { + AlignedSpace::from_slice(::core::slice::from_ref(handle)).as_bytes() + } +} + /// Macro to atomate the definition of scalar atoms. macro_rules! make_scalar_atom { ($atom:ty, $internal:ty, $uri:expr, $urid:expr) => { diff --git a/buf-size/src/buffer_sizes.rs b/buf-size/src/buffer_sizes.rs index ba135161..ccbcc9ad 100644 --- a/buf-size/src/buffer_sizes.rs +++ b/buf-size/src/buffer_sizes.rs @@ -33,6 +33,8 @@ impl BufferSizes { options: &OptionsList, urids: &BufferSizesURIDCollection, ) -> Self { + todo!() + /* BufferSizes { min_block_length: options .read(urids.min_block_length, urids.atom_int, ()) @@ -46,6 +48,6 @@ impl BufferSizes { sequence_size: options .read(urids.sequence_size, urids.atom_int, ()) .map(|x| x.get()), - } + }*/ } } diff --git a/buf-size/src/options.rs b/buf-size/src/options.rs index 83b60ac1..e3067529 100644 --- a/buf-size/src/options.rs +++ b/buf-size/src/options.rs @@ -1,4 +1,4 @@ -use lv2_atom::Atom; +use lv2_atom::{Atom, AtomHandle}; use urid::UriBound; /// A simple macro to automate the definition of the u32 options available in this module @@ -18,17 +18,19 @@ macro_rules! make_option { } impl lv2_options::OptionType for $name { - type AtomType = lv2_atom::scalar::Int; + type AtomType = lv2_atom::atoms::scalar::Int; #[inline] - fn from_option_value<'a>( - value: >::ReadHandle, + fn from_option_value( + value: <::ReadHandle as AtomHandle>::Handle, ) -> Option { Some(Self((*value))) } #[inline] - fn as_option_value<'a>(&'a self) -> >::ReadHandle { + fn as_option_value<'a>( + &'a self, + ) -> <::ReadHandle as AtomHandle>::Handle { &self.0 } } diff --git a/options/src/collection.rs b/options/src/collection.rs index 383289cc..27909f2d 100644 --- a/options/src/collection.rs +++ b/options/src/collection.rs @@ -1,37 +1,43 @@ -use urid::{URIDCollection, Map}; -use crate::request::OptionRequestList; -use crate::{OptionsError, OptionValue}; use crate::list::OptionsList; use crate::option::request::OptionRequest; +use crate::request::OptionRequestList; +use crate::{OptionValue, OptionsError}; +use urid::{Map, URIDCollection}; pub trait OptionsCollection: Sized { type Serializer; #[inline] fn new_serializer<'a, M: Map + ?Sized>(map: &M) -> Option> - where Self::Serializer: OptionsSerializationContext<'a, Self> { // FIXME - Some(OptionsSerializer { inner: Self::Serializer::from_map(map)? }) + where + Self::Serializer: OptionsSerializationContext<'a, Self>, + { + // FIXME + Some(OptionsSerializer { + inner: Self::Serializer::from_map(map)?, + }) } } #[doc(hidden)] pub mod implementation { - use crate::{OptionType, OptionsError, OptionValue}; - use std::marker::PhantomData; - use urid::{URID, URIDCollection, Map}; - use crate::collection::{OptionsSerializationContext, OptionsCollection}; + use crate::collection::{OptionsCollection, OptionsSerializationContext}; use crate::option::request::OptionRequest; + use crate::{OptionType, OptionValue, OptionsError}; + use lv2_atom::atoms::scalar::ScalarAtom; use lv2_atom::{Atom, BackAsSpace}; - use lv2_atom::scalar::ScalarAtom; + use std::marker::PhantomData; + use urid::{Map, URIDCollection, URID}; pub struct OptionTypeSerializationContext { option_urid: URID, - option_type_atom_urid: URID + option_type_atom_urid: URID, } impl<'a, O: OptionType> OptionsCollection for O - where ::AtomType: BackAsSpace<'a>, - <::AtomType as Atom<'a, 'a>>::ReadParameter: Default { + where + ::AtomType: BackAsSpace, + { type Serializer = OptionTypeSerializationContext; } @@ -46,25 +52,34 @@ pub mod implementation { } impl<'a, O: OptionType> OptionsSerializationContext<'a, O> for OptionTypeSerializationContext - where ::AtomType: BackAsSpace<'a>, - <::AtomType as Atom<'a, 'a>>::ReadParameter: Default { + where + ::AtomType: BackAsSpace, + { #[inline] fn deserialize_new(&self, option: &'a OptionValue) -> Option { - option.read(self.option_urid, self.option_type_atom_urid, Default::default()) + option.read(self.option_urid, self.option_type_atom_urid) } - fn deserialize_to(&self, options: &mut O, option: &OptionValue) -> Result<(), OptionsError> { + fn deserialize_to( + &self, + options: &mut O, + option: &OptionValue, + ) -> Result<(), OptionsError> { todo!() } - fn respond_to_request<'r>(&self, options: &'r O, requests: &'r mut OptionRequest) -> Result<(), OptionsError> { + fn respond_to_request<'r>( + &self, + options: &'r O, + requests: &'r mut OptionRequest, + ) -> Result<(), OptionsError> { todo!() } } } pub struct OptionsSerializer { - inner: T::Serializer + inner: T::Serializer, } impl OptionsSerializer { @@ -76,7 +91,11 @@ impl OptionsSerializer { todo!() } - pub fn respond_to_requests<'a>(&self, options: &T, requests: &mut OptionRequestList) -> Result<(), OptionsError> { + pub fn respond_to_requests<'a>( + &self, + options: &T, + requests: &mut OptionRequestList, + ) -> Result<(), OptionsError> { todo!() } } @@ -86,5 +105,9 @@ pub trait OptionsSerializationContext<'a, T: OptionsCollection>: URIDCollection fn deserialize_to(&self, options: &mut T, option: &OptionValue) -> Result<(), OptionsError>; - fn respond_to_request<'r>(&self, options: &'r T, request: &'r mut OptionRequest) -> Result<(), OptionsError>; -} \ No newline at end of file + fn respond_to_request<'r>( + &self, + options: &'r T, + request: &'r mut OptionRequest, + ) -> Result<(), OptionsError>; +} diff --git a/options/src/extensions.rs b/options/src/extensions.rs index be92e6d5..84efb188 100644 --- a/options/src/extensions.rs +++ b/options/src/extensions.rs @@ -1,5 +1,6 @@ //! Contains the [`OptionsInterface`](crate::extensions::OptionsInterface) extension interface. use crate::features::OptionsList; +use crate::option::request::OptionRequestList; use crate::OptionsError; use lv2_core::feature::Feature; use lv2_core::plugin::PluginInstance; @@ -9,7 +10,6 @@ use std::ffi::c_void; use std::marker::PhantomData; use std::panic::AssertUnwindSafe; use urid::UriBound; -use crate::option::request::OptionRequestList; /// An interface to allow dynamically setting options from the host. /// diff --git a/options/src/lib.rs b/options/src/lib.rs index 5ba23eb8..db53ac86 100644 --- a/options/src/lib.rs +++ b/options/src/lib.rs @@ -17,15 +17,15 @@ //! See the [LV2 Options documentation](http://lv2plug.in/ns/ext/options) for more information. pub use option::error::OptionsError; -pub use option::OptionType; pub use option::request; pub use option::subject::Subject; pub use option::value::OptionValue; +pub use option::OptionType; +pub mod collection; pub mod extensions; -mod option; pub mod list; -pub mod collection; +mod option; /// Contains the [`OptionsList`](features::OptionsList) feature. pub mod features { @@ -34,8 +34,8 @@ pub mod features { /// Prelude of `lv2_options` for wildcard usage. pub mod prelude { + pub use crate::extensions::{OptionsDescriptor, OptionsInterface}; pub use crate::list::OptionsList; pub use crate::OptionsError; pub use crate::Subject; - pub use crate::extensions::{OptionsDescriptor, OptionsInterface}; -} \ No newline at end of file +} diff --git a/options/src/list.rs b/options/src/list.rs index ffd215b7..a9ac2396 100644 --- a/options/src/list.rs +++ b/options/src/list.rs @@ -16,7 +16,9 @@ impl<'f> OptionsList<'f> { /// Returns an iterator over the slice. #[inline] pub fn iter(&self) -> OptionsListIter<'f> { - OptionsListIter { current: self.options_list } + OptionsListIter { + current: self.options_list, + } } } @@ -45,7 +47,7 @@ impl<'a> IntoIterator for &'a OptionsList<'a> { } pub struct OptionsListIter<'a> { - current: &'a lv2_sys::LV2_Options_Option + current: &'a lv2_sys::LV2_Options_Option, } impl<'a> Iterator for OptionsListIter<'a> { diff --git a/options/src/option.rs b/options/src/option.rs index 8cbcbdc9..daa5af09 100644 --- a/options/src/option.rs +++ b/options/src/option.rs @@ -1,10 +1,10 @@ -use lv2_atom::Atom; +use lv2_atom::{Atom, AtomHandle}; use urid::UriBound; pub mod error; +pub mod request; pub mod subject; pub mod value; -pub mod request; /// A trait representing an LV2 Option type. /// @@ -32,17 +32,21 @@ pub mod request; /// } /// ``` pub trait OptionType: UriBound + Sized { - type AtomType: UriBound; + type AtomType: Atom; /// Creates a new instance of this Option type from a given atom value. /// /// This method may return `None` if the Atom's value is invalid for this option type. /// /// This method is used to store option data when received by the host. - fn from_option_value<'a>(value: >::ReadHandle) -> Option where Self::AtomType: Atom<'a, 'a>; + fn from_option_value( + value: <<::AtomType as Atom>::ReadHandle as AtomHandle>::Handle, + ) -> Option; /// Returns this Option's value as a reference to its Atom type. /// /// This method is used to send the option's value to the host when it is requested. - fn as_option_value<'a>(&'a self) -> >::ReadHandle where Self::AtomType: Atom<'a, 'a>; + fn as_option_value( + &self, + ) -> <<::AtomType as Atom>::ReadHandle as AtomHandle>::Handle; } diff --git a/options/src/option/error.rs b/options/src/option/error.rs index 1120acdf..027507ba 100644 --- a/options/src/option/error.rs +++ b/options/src/option/error.rs @@ -19,7 +19,9 @@ impl OptionsError { pub(crate) fn result_into_raw(value: Result<(), OptionsError>) -> lv2_sys::LV2_Options_Status { match value { Ok(()) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS, - Err(OptionsError::BadSubject) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT, + Err(OptionsError::BadSubject) => { + lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT + } Err(OptionsError::BadKey) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY, Err(OptionsError::BadValue) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE, Err(_) => lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_UNKNOWN, @@ -30,7 +32,9 @@ impl OptionsError { pub(crate) fn from_raw(status: lv2_sys::LV2_Options_Status) -> Result<(), OptionsError> { match status { lv2_sys::LV2_Options_Status_LV2_OPTIONS_SUCCESS => Ok(()), - lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT => Err(OptionsError::BadSubject), + lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_SUBJECT => { + Err(OptionsError::BadSubject) + } lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_KEY => Err(OptionsError::BadKey), lv2_sys::LV2_Options_Status_LV2_OPTIONS_ERR_BAD_VALUE => Err(OptionsError::BadValue), _ => Err(OptionsError::Unknown), @@ -44,7 +48,7 @@ impl Display for OptionsError { OptionsError::Unknown => "Unknown error while reading/writing Option", OptionsError::BadSubject => "Unknown Option subject", OptionsError::BadKey => "Unknown Option key", - OptionsError::BadValue => "Invalid Option value" + OptionsError::BadValue => "Invalid Option value", }; write!(f, "{}", msg) diff --git a/options/src/option/request.rs b/options/src/option/request.rs index 916b4981..02983fd5 100644 --- a/options/src/option/request.rs +++ b/options/src/option/request.rs @@ -1,18 +1,18 @@ +use crate::{OptionType, OptionsError, Subject}; +use lv2_atom::{Atom, AtomHandle, BackAsSpace}; use urid::URID; -use lv2_atom::{Atom, BackAsSpace}; -use crate::{Subject, OptionType, OptionsError}; #[repr(C)] // SAFETY: The last fields are left uninitialized by the host, it's up to the plugin to set them pub struct OptionRequest { - inner: lv2_sys::LV2_Options_Option + inner: lv2_sys::LV2_Options_Option, } impl OptionRequest { #[inline] - pub(crate)fn from_mut(option: &mut lv2_sys::LV2_Options_Option) -> &mut Self { + pub(crate) fn from_mut(option: &mut lv2_sys::LV2_Options_Option) -> &mut Self { // SAFETY: lv2_sys::LV2_Options_Option and OptionRequest have the same memory layout - unsafe {&mut *(option as *mut lv2_sys::LV2_Options_Option as *mut Self) } + unsafe { &mut *(option as *mut lv2_sys::LV2_Options_Option as *mut Self) } } #[inline] @@ -31,14 +31,20 @@ impl OptionRequest { } #[inline] - pub fn has_response(&self) -> bool { self.inner.type_ != 0 } + pub fn has_response(&self) -> bool { + self.inner.type_ != 0 + } #[inline] - pub fn try_respond<'a, T: OptionType>(&'a mut self, - option_type: URID, - atom_type: URID, - value: &'a T - ) -> Result<(), OptionsError> where T::AtomType: BackAsSpace<'a> { + pub fn try_respond<'a, T: OptionType>( + &'a mut self, + option_type: URID, + atom_type: URID, + value: &'a T, + ) -> Result<(), OptionsError> + where + T::AtomType: BackAsSpace, + { if !self.is(option_type) { return Err(OptionsError::BadKey); } @@ -49,22 +55,22 @@ impl OptionRequest { } #[inline] - unsafe fn set_value_unchecked<'a, T: Atom<'a, 'a>>( - &'a mut self, + unsafe fn set_value_unchecked( + &mut self, value_type: URID, - value_handle: T::ReadHandle, + value_handle: <::ReadHandle as AtomHandle>::Handle, ) where - T: BackAsSpace<'a>, + T: BackAsSpace, { let data = T::back_as_space(value_handle); self.inner.type_ = value_type.get(); self.inner.size = data.len() as u32; - self.inner.value = data.data().unwrap().as_ptr().cast(); + self.inner.value = data.as_ptr().cast(); } } pub struct OptionRequestList<'a> { - ptr: &'a mut lv2_sys::LV2_Options_Option + ptr: &'a mut lv2_sys::LV2_Options_Option, } impl<'a> OptionRequestList<'a> { @@ -81,7 +87,7 @@ impl<'a> OptionRequestList<'a> { } } -impl<'a, 'list: 'a> IntoIterator for &'list mut OptionRequestList<'a >{ +impl<'a, 'list: 'a> IntoIterator for &'list mut OptionRequestList<'a> { type Item = &'a mut OptionRequest; type IntoIter = OptionRequestListIter<'a>; @@ -92,7 +98,7 @@ impl<'a, 'list: 'a> IntoIterator for &'list mut OptionRequestList<'a >{ } pub struct OptionRequestListIter<'a> { - current: &'a mut lv2_sys::LV2_Options_Option + current: &'a mut lv2_sys::LV2_Options_Option, } impl<'a> Iterator for OptionRequestListIter<'a> { @@ -107,12 +113,10 @@ impl<'a> Iterator for OptionRequestListIter<'a> { // SAFETY: list is guaranteed by the host to end with a zeroed struct. // Therefore the pointer always points to a valid lv2_sys::LV2_Options_Option value // (only its contents may be incorrect if zeroed) - let next = unsafe { - &mut *((self.current as *mut lv2_sys::LV2_Options_Option).add(1)) - }; + let next = unsafe { &mut *((self.current as *mut lv2_sys::LV2_Options_Option).add(1)) }; let item = core::mem::replace(&mut self.current, next); Some(OptionRequest::from_mut(item)) } -} \ No newline at end of file +} diff --git a/options/src/option/value.rs b/options/src/option/value.rs index d763c473..b049486d 100644 --- a/options/src/option/value.rs +++ b/options/src/option/value.rs @@ -1,5 +1,5 @@ use crate::option::subject::Subject; -use lv2_atom::prelude::Space; +use lv2_atom::prelude::AlignedSpace; use lv2_atom::Atom; use urid::URID; @@ -26,7 +26,7 @@ impl OptionValue { } #[inline] - pub fn data(&self) -> Option { + pub fn data(&self) -> Option<&AlignedSpace> { // SAFETY: lifetime of the returned atom value is guaranteed by lifetime of the current Option pointer // And the validity of these pointers are guaranteed by the host let slice = unsafe { @@ -36,17 +36,15 @@ impl OptionValue { ) }; - Some(Space::from_slice(slice)) + Some(AlignedSpace::from_slice(slice)) } #[inline] - pub fn read<'a, T: crate::option::OptionType>( - &'a self, + pub fn read( + &self, option_type: URID, data_type: URID, - data_type_parameter: >::ReadParameter, - ) -> Option where T::AtomType: Atom<'a, 'a> - { + ) -> Option { if !self.is(option_type) { return None; } @@ -55,6 +53,9 @@ impl OptionValue { return None; } - T::from_option_value(T::AtomType::read(self.data()?, data_type_parameter)?) + // SAFETY: data is guaranteed to be an atom by the host + let atom = unsafe { self.data()?.read().next_atom()? }.read(data_type)?; + + T::from_option_value(atom) } } diff --git a/options/tests/optionable.rs b/options/tests/optionable.rs index c317c243..18a5f763 100644 --- a/options/tests/optionable.rs +++ b/options/tests/optionable.rs @@ -1,6 +1,9 @@ -use lv2_atom::scalar::Int; +use lv2_atom::atoms::scalar::Int; +use lv2_atom::Atom; use lv2_core::prelude::*; +use lv2_options::collection::{OptionsCollection, OptionsSerializer}; use lv2_options::prelude::*; +use lv2_options::request::OptionRequestList; use lv2_urid::{HostMap, LV2Map}; use std::any::Any; use std::ffi::c_void; @@ -8,27 +11,24 @@ use std::os::raw::c_char; use std::pin::Pin; use urid::{uri, HashURIDMapper, Uri, UriBound, URID}; use urid::{Map, URIDCollection}; -use lv2_options::request::OptionRequestList; -use lv2_options::collection::{OptionsSerializer, OptionsCollection}; -use lv2_atom::Atom; #[uri("urn:lv2_options:test:SomeIntOption")] pub struct MyIntOption(i32); impl lv2_options::OptionType for MyIntOption { - type AtomType = lv2_atom::scalar::Int; + type AtomType = lv2_atom::atoms::scalar::Int; - fn from_option_value<'a>(value: &'a i32) -> Option where Self::AtomType: Atom<'a, 'a> { + fn from_option_value(value: &i32) -> Option { Some(Self(*value)) } - fn as_option_value<'a>(&'a self) -> &'a i32 where Self::AtomType: Atom<'a, 'a> { + fn as_option_value(&self) -> &i32 { &self.0 } } pub struct MyPluginOptions { - some_int_option: MyIntOption + some_int_option: MyIntOption, } #[derive(FeatureCollection)] @@ -52,8 +52,8 @@ impl Plugin for OptionablePlugin { let options_serializer = MyIntOption::new_serializer(&features.map)?; let mut plugin = OptionablePlugin { + options: options_serializer.deserialize_new(&features.options)?, options_serializer, - options: options_serializer.deserialize_new(&features.options)? }; Some(plugin) @@ -75,11 +75,13 @@ impl Plugin for OptionablePlugin { impl OptionsInterface for OptionablePlugin { fn get<'a>(&'a self, mut requests: OptionRequestList<'a>) -> Result<(), OptionsError> { - self.options_serializer.respond_to_requests(&self.options, &mut requests) + self.options_serializer + .respond_to_requests(&self.options, &mut requests) } fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { - self.options_serializer.deserialize_to(&mut self.options, &options) + self.options_serializer + .deserialize_to(&mut self.options, &options) } } From e19ffbe9c7249955ba77ad43979f6f02bca98d62 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Sep 2021 02:25:33 +0200 Subject: [PATCH 51/54] Option collections now work! --- buf-size/src/buffer_sizes.rs | 112 +++++++++------ buf-size/src/lib.rs | 2 +- buf-size/src/options.rs | 1 + options/src/collection.rs | 248 +++++++++++++++++++++++----------- options/src/option.rs | 21 ++- options/src/option/request.rs | 38 ++++-- options/src/option/value.rs | 28 ++-- options/tests/optionable.rs | 26 ++-- urid/src/lib.rs | 4 + 9 files changed, 317 insertions(+), 163 deletions(-) diff --git a/buf-size/src/buffer_sizes.rs b/buf-size/src/buffer_sizes.rs index ccbcc9ad..6581d260 100644 --- a/buf-size/src/buffer_sizes.rs +++ b/buf-size/src/buffer_sizes.rs @@ -1,53 +1,77 @@ use crate::options::*; -use lv2_atom::prelude::Int; -use lv2_options::features::OptionsList; -use urid::{Map, URIDCollection, URID}; - -#[derive(URIDCollection)] -pub struct BufferSizesURIDCollection { - pub atom_int: URID, - pub min_block_length: URID, - pub max_block_length: URID, - pub nominal_block_length: URID, - pub sequence_size: URID, -} #[derive(Copy, Clone, Debug, Default)] pub struct BufferSizes { - min_block_length: Option, - max_block_length: Option, - nominal_block_length: Option, - sequence_size: Option, + min_block_length: Option, + max_block_length: Option, + nominal_block_length: Option, + sequence_size: Option, } -impl BufferSizes { - pub fn from_options(options: &OptionsList, mapper: &impl Map) -> Self { - let collection = match mapper.populate_collection() { - Some(c) => c, - None => return Default::default(), - }; - BufferSizes::from_options_with_urids(options, &collection) +const _: () = { + extern crate lv2_options as _lv2_options; + extern crate urid as _urid; + use _lv2_options::collection::{OptionsCollection, OptionsSerializationContext}; + use _lv2_options::features::OptionsList; + use _lv2_options::request::OptionRequestList; + use _lv2_options::OptionsError; + + use _urid::*; + + #[derive(_urid::URIDCollection)] + pub struct BufferSizesSerializationContext { + pub min_block_length: as OptionsCollection>::Serializer, + pub max_block_length: as OptionsCollection>::Serializer, + pub nominal_block_length: as OptionsCollection>::Serializer, + pub sequence_size: as OptionsCollection>::Serializer, } - pub fn from_options_with_urids( - options: &OptionsList, - urids: &BufferSizesURIDCollection, - ) -> Self { - todo!() - /* - BufferSizes { - min_block_length: options - .read(urids.min_block_length, urids.atom_int, ()) - .map(|x| x.get()), - max_block_length: options - .read(urids.max_block_length, urids.atom_int, ()) - .map(|x| x.get()), - nominal_block_length: options - .read(urids.nominal_block_length, urids.atom_int, ()) - .map(|x| x.get()), - sequence_size: options - .read(urids.sequence_size, urids.atom_int, ()) - .map(|x| x.get()), - }*/ + impl OptionsCollection for BufferSizes { + type Serializer = BufferSizesSerializationContext; } -} + + impl OptionsSerializationContext for BufferSizesSerializationContext { + fn deserialize_new(&self, options: &OptionsList) -> Result { + Ok(BufferSizes { + min_block_length: self.min_block_length.deserialize_new(options)?, + max_block_length: self.max_block_length.deserialize_new(options)?, + nominal_block_length: self.nominal_block_length.deserialize_new(options)?, + sequence_size: self.sequence_size.deserialize_new(options)?, + }) + } + + fn deserialize_to( + &self, + destination: &mut BufferSizes, + options: &OptionsList, + ) -> Result<(), OptionsError> { + self.min_block_length + .deserialize_to(&mut destination.min_block_length, options)?; + self.max_block_length + .deserialize_to(&mut destination.max_block_length, options)?; + self.nominal_block_length + .deserialize_to(&mut destination.nominal_block_length, options)?; + self.sequence_size + .deserialize_to(&mut destination.sequence_size, options)?; + + Ok(()) + } + + fn respond_to_requests<'a>( + &self, + options: &'a BufferSizes, + requests: &mut OptionRequestList<'a>, + ) -> Result<(), OptionsError> { + self.min_block_length + .respond_to_requests(&options.min_block_length, requests)?; + self.max_block_length + .respond_to_requests(&options.max_block_length, requests)?; + self.nominal_block_length + .respond_to_requests(&options.nominal_block_length, requests)?; + self.sequence_size + .respond_to_requests(&options.sequence_size, requests)?; + + Ok(()) + } + } +}; diff --git a/buf-size/src/lib.rs b/buf-size/src/lib.rs index 9e2006d0..568a36d5 100644 --- a/buf-size/src/lib.rs +++ b/buf-size/src/lib.rs @@ -1,4 +1,4 @@ mod buffer_sizes; pub mod options; -pub use buffer_sizes::{BufferSizes, BufferSizesURIDCollection}; +pub use buffer_sizes::BufferSizes; diff --git a/buf-size/src/options.rs b/buf-size/src/options.rs index e3067529..236ec8c2 100644 --- a/buf-size/src/options.rs +++ b/buf-size/src/options.rs @@ -4,6 +4,7 @@ use urid::UriBound; /// A simple macro to automate the definition of the u32 options available in this module macro_rules! make_option { ($name:ident, $uri:expr) => { + #[derive(Copy, Clone, Debug, Default)] pub struct $name(i32); impl $name { diff --git a/options/src/collection.rs b/options/src/collection.rs index 27909f2d..97c19572 100644 --- a/options/src/collection.rs +++ b/options/src/collection.rs @@ -1,17 +1,13 @@ use crate::list::OptionsList; -use crate::option::request::OptionRequest; use crate::request::OptionRequestList; -use crate::{OptionValue, OptionsError}; +use crate::OptionsError; use urid::{Map, URIDCollection}; pub trait OptionsCollection: Sized { - type Serializer; + type Serializer: OptionsSerializationContext; #[inline] - fn new_serializer<'a, M: Map + ?Sized>(map: &M) -> Option> - where - Self::Serializer: OptionsSerializationContext<'a, Self>, - { + fn new_serializer<'a, M: Map + ?Sized>(map: &M) -> Option> { // FIXME Some(OptionsSerializer { inner: Self::Serializer::from_map(map)?, @@ -19,95 +15,191 @@ pub trait OptionsCollection: Sized { } } -#[doc(hidden)] -pub mod implementation { - use crate::collection::{OptionsCollection, OptionsSerializationContext}; - use crate::option::request::OptionRequest; - use crate::{OptionType, OptionValue, OptionsError}; - use lv2_atom::atoms::scalar::ScalarAtom; - use lv2_atom::{Atom, BackAsSpace}; - use std::marker::PhantomData; - use urid::{Map, URIDCollection, URID}; - - pub struct OptionTypeSerializationContext { - option_urid: URID, - option_type_atom_urid: URID, - } - - impl<'a, O: OptionType> OptionsCollection for O - where - ::AtomType: BackAsSpace, - { - type Serializer = OptionTypeSerializationContext; - } - - impl URIDCollection for OptionTypeSerializationContext { - #[inline] - fn from_map(map: &M) -> Option { - Some(Self { - option_urid: map.populate_collection()?, - option_type_atom_urid: map.populate_collection()?, - }) - } - } - - impl<'a, O: OptionType> OptionsSerializationContext<'a, O> for OptionTypeSerializationContext - where - ::AtomType: BackAsSpace, - { - #[inline] - fn deserialize_new(&self, option: &'a OptionValue) -> Option { - option.read(self.option_urid, self.option_type_atom_urid) - } - - fn deserialize_to( - &self, - options: &mut O, - option: &OptionValue, - ) -> Result<(), OptionsError> { - todo!() - } - - fn respond_to_request<'r>( - &self, - options: &'r O, - requests: &'r mut OptionRequest, - ) -> Result<(), OptionsError> { - todo!() - } - } -} - pub struct OptionsSerializer { inner: T::Serializer, } impl OptionsSerializer { - pub fn deserialize_new(&self, list: &OptionsList) -> Option { - todo!() + #[inline] + pub fn deserialize_new<'a>(&'a self, list: &'a OptionsList<'a>) -> Result { + self.inner.deserialize_new(list) } + #[inline] pub fn deserialize_to(&self, options: &mut T, list: &OptionsList) -> Result<(), OptionsError> { - todo!() + self.inner.deserialize_to(options, list) } + #[inline] pub fn respond_to_requests<'a>( &self, - options: &T, - requests: &mut OptionRequestList, + options: &'a T, + requests: &mut OptionRequestList<'a>, ) -> Result<(), OptionsError> { - todo!() + self.inner.respond_to_requests(options, requests) } } -pub trait OptionsSerializationContext<'a, T: OptionsCollection>: URIDCollection { - fn deserialize_new(&self, option: &'a OptionValue) -> Option; +pub trait OptionsSerializationContext: URIDCollection { + fn deserialize_new(&self, options: &OptionsList) -> Result; - fn deserialize_to(&self, options: &mut T, option: &OptionValue) -> Result<(), OptionsError>; + fn deserialize_to( + &self, + destination: &mut T, + options: &OptionsList, + ) -> Result<(), OptionsError>; - fn respond_to_request<'r>( + fn respond_to_requests<'a>( &self, - options: &'r T, - request: &'r mut OptionRequest, + options: &'a T, + requests: &mut OptionRequestList<'a>, ) -> Result<(), OptionsError>; } + +#[doc(hidden)] +pub mod __implementation { + use crate::collection::{OptionsCollection, OptionsSerializationContext}; + use crate::list::OptionsList; + use crate::request::OptionRequestList; + use crate::{OptionType, OptionsError}; + use urid::{Map, URIDCollection, URID}; + + pub mod option_value { + use super::*; + pub struct OptionTypeSerializationContext { + option_urid: URID, + option_type_atom_urid: URID, + } + + impl<'a, O: OptionType> OptionsCollection for O { + type Serializer = OptionTypeSerializationContext; + } + + impl URIDCollection for OptionTypeSerializationContext { + #[inline] + fn from_map(map: &M) -> Option { + Some(Self { + option_urid: map.populate_collection()?, + option_type_atom_urid: map.populate_collection()?, + }) + } + } + + impl OptionsSerializationContext for OptionTypeSerializationContext { + fn deserialize_new(&self, options: &OptionsList) -> Result { + for option in options { + match option.read(self.option_urid, self.option_type_atom_urid) { + Ok(value) => return Ok(value), + Err(OptionsError::BadKey) => {} + Err(e) => return Err(e), + } + } + + Err(OptionsError::BadKey) + } + + fn deserialize_to( + &self, + destination: &mut O, + options: &OptionsList, + ) -> Result<(), OptionsError> { + for option in options { + match option.read(self.option_urid, self.option_type_atom_urid) { + Ok(value) => { + *destination = value; + return Ok(()); + } + Err(OptionsError::BadKey) => {} + Err(e) => return Err(e), + } + } + + Err(OptionsError::BadKey) + } + + fn respond_to_requests<'a>( + &self, + options: &'a O, + requests: &mut OptionRequestList<'a>, + ) -> Result<(), OptionsError> { + for request in requests { + match request.try_respond(self.option_urid, self.option_type_atom_urid, options) + { + Ok(()) => return Ok(()), + Err(OptionsError::BadKey) => {} + Err(e) => return Err(e), + } + } + + Ok(()) + } + } + } + + mod option { + use super::*; + + pub struct OptionSerializationContext { + inner: O::Serializer, + } + + impl<'a, O: OptionsCollection> OptionsCollection for Option { + type Serializer = OptionSerializationContext; + } + + impl URIDCollection for OptionSerializationContext { + #[inline] + fn from_map(map: &M) -> Option { + Some(Self { + inner: map.populate_collection()?, + }) + } + } + + impl OptionsSerializationContext> + for OptionSerializationContext + { + fn deserialize_new(&self, options: &OptionsList) -> Result, OptionsError> { + match self.inner.deserialize_new(options) { + Ok(value) => Ok(Some(value)), + Err(OptionsError::BadKey) => Ok(None), + Err(e) => Err(e), + } + } + + fn deserialize_to( + &self, + destination: &mut Option, + options: &OptionsList, + ) -> Result<(), OptionsError> { + let result = match destination { + Some(value) => self.inner.deserialize_to(value, options), + None => match self.inner.deserialize_new(options) { + Ok(v) => { + destination.insert(v); + Ok(()) + } + Err(e) => Err(e), + }, + }; + + match result { + Ok(()) | Err(OptionsError::BadKey) => Ok(()), + Err(e) => Err(e), + } + } + + fn respond_to_requests<'a>( + &self, + options: &'a Option, + requests: &mut OptionRequestList<'a>, + ) -> Result<(), OptionsError> { + if let Some(value) = options { + self.inner.respond_to_requests(value, requests) + } else { + Ok(()) + } + } + } + } +} diff --git a/options/src/option.rs b/options/src/option.rs index daa5af09..65ce9321 100644 --- a/options/src/option.rs +++ b/options/src/option.rs @@ -1,4 +1,4 @@ -use lv2_atom::{Atom, AtomHandle}; +use lv2_atom::{Atom, AtomHandle, BackAsSpace}; use urid::UriBound; pub mod error; @@ -32,7 +32,7 @@ pub mod value; /// } /// ``` pub trait OptionType: UriBound + Sized { - type AtomType: Atom; + type AtomType: BackAsSpace; /// Creates a new instance of this Option type from a given atom value. /// @@ -50,3 +50,20 @@ pub trait OptionType: UriBound + Sized { &self, ) -> <<::AtomType as Atom>::ReadHandle as AtomHandle>::Handle; } + +/* +impl OptionType for Option { + type AtomType = O::AtomType; + + fn from_option_value( + value: <<::AtomType as Atom>::ReadHandle as AtomHandle>::Handle, + ) -> Option { + Some(O::from_option_value(value)) + } + + fn as_option_value( + &self, + ) -> <<::AtomType as Atom>::ReadHandle as AtomHandle>::Handle { + todo!() + } +}*/ diff --git a/options/src/option/request.rs b/options/src/option/request.rs index 02983fd5..8451492b 100644 --- a/options/src/option/request.rs +++ b/options/src/option/request.rs @@ -1,14 +1,16 @@ -use crate::{OptionType, OptionsError, Subject}; +use crate::{OptionType, OptionValue, OptionsError, Subject}; use lv2_atom::{Atom, AtomHandle, BackAsSpace}; +use std::marker::PhantomData; use urid::URID; #[repr(C)] // SAFETY: The last fields are left uninitialized by the host, it's up to the plugin to set them -pub struct OptionRequest { +pub struct OptionRequest<'a> { inner: lv2_sys::LV2_Options_Option, + lifetime: PhantomData<&'a OptionValue>, } -impl OptionRequest { +impl<'a> OptionRequest<'a> { #[inline] pub(crate) fn from_mut(option: &mut lv2_sys::LV2_Options_Option) -> &mut Self { // SAFETY: lv2_sys::LV2_Options_Option and OptionRequest have the same memory layout @@ -36,8 +38,8 @@ impl OptionRequest { } #[inline] - pub fn try_respond<'a, T: OptionType>( - &'a mut self, + pub fn try_respond( + &mut self, option_type: URID, atom_type: URID, value: &'a T, @@ -74,8 +76,15 @@ pub struct OptionRequestList<'a> { } impl<'a> OptionRequestList<'a> { - pub fn iter_mut<'list: 'a>(&'list mut self) -> OptionRequestListIter<'a> { - OptionRequestListIter { current: self.ptr } + #[inline] + pub fn iter_mut<'list>(&'list mut self) -> OptionRequestListIter<'a, 'list> + where + 'a: 'list, + { + OptionRequestListIter { + current: self.ptr, + value_lifetime: PhantomData, + } } } @@ -87,9 +96,9 @@ impl<'a> OptionRequestList<'a> { } } -impl<'a, 'list: 'a> IntoIterator for &'list mut OptionRequestList<'a> { - type Item = &'a mut OptionRequest; - type IntoIter = OptionRequestListIter<'a>; +impl<'value: 'list, 'list> IntoIterator for &'list mut OptionRequestList<'value> { + type Item = &'list mut OptionRequest<'value>; + type IntoIter = OptionRequestListIter<'value, 'list>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -97,12 +106,13 @@ impl<'a, 'list: 'a> IntoIterator for &'list mut OptionRequestList<'a> { } } -pub struct OptionRequestListIter<'a> { - current: &'a mut lv2_sys::LV2_Options_Option, +pub struct OptionRequestListIter<'value: 'list, 'list> { + current: &'list mut lv2_sys::LV2_Options_Option, + value_lifetime: PhantomData<&'value OptionValue>, } -impl<'a> Iterator for OptionRequestListIter<'a> { - type Item = &'a mut OptionRequest; +impl<'value: 'list, 'list> Iterator for OptionRequestListIter<'value, 'list> { + type Item = &'list mut OptionRequest<'value>; #[inline] fn next(&mut self) -> Option { diff --git a/options/src/option/value.rs b/options/src/option/value.rs index b049486d..a52cd750 100644 --- a/options/src/option/value.rs +++ b/options/src/option/value.rs @@ -1,6 +1,7 @@ use crate::option::subject::Subject; -use lv2_atom::prelude::AlignedSpace; -use lv2_atom::Atom; +use crate::OptionsError; +use lv2_atom::space::AtomSpace; +use lv2_atom::{Atom, AtomHandle}; use urid::URID; #[repr(C)] @@ -26,7 +27,7 @@ impl OptionValue { } #[inline] - pub fn data(&self) -> Option<&AlignedSpace> { + pub fn data(&self) -> Option<&[u8]> { // SAFETY: lifetime of the returned atom value is guaranteed by lifetime of the current Option pointer // And the validity of these pointers are guaranteed by the host let slice = unsafe { @@ -36,7 +37,7 @@ impl OptionValue { ) }; - Some(AlignedSpace::from_slice(slice)) + Some(slice) } #[inline] @@ -44,18 +45,25 @@ impl OptionValue { &self, option_type: URID, data_type: URID, - ) -> Option { + ) -> Result { if !self.is(option_type) { - return None; + return Err(OptionsError::BadKey); } if self.inner.type_ != data_type { - return None; + return Err(OptionsError::BadValue); } - // SAFETY: data is guaranteed to be an atom by the host - let atom = unsafe { self.data()?.read().next_atom()? }.read(data_type)?; + // SAFETY: data is guaranteed to be an atom by the host, and atom type is checked above + let atom = unsafe { self.atom_value::() }.ok_or(OptionsError::BadValue)?; - T::from_option_value(atom) + T::from_option_value(atom).ok_or(OptionsError::BadValue) + } + + unsafe fn atom_value( + &self, + ) -> Option<<::ReadHandle as AtomHandle>::Handle> { + // TODO: Atoms can actually be from non-aligned spaces + T::read(AtomSpace::from_bytes_unchecked(self.data()?)) } } diff --git a/options/tests/optionable.rs b/options/tests/optionable.rs index 18a5f763..62fd15d9 100644 --- a/options/tests/optionable.rs +++ b/options/tests/optionable.rs @@ -1,5 +1,4 @@ use lv2_atom::atoms::scalar::Int; -use lv2_atom::Atom; use lv2_core::prelude::*; use lv2_options::collection::{OptionsCollection, OptionsSerializer}; use lv2_options::prelude::*; @@ -9,8 +8,8 @@ use std::any::Any; use std::ffi::c_void; use std::os::raw::c_char; use std::pin::Pin; -use urid::{uri, HashURIDMapper, Uri, UriBound, URID}; -use urid::{Map, URIDCollection}; +use urid::Map; +use urid::{uri, HashURIDMapper, Uri, UriBound}; #[uri("urn:lv2_options:test:SomeIntOption")] pub struct MyIntOption(i32); @@ -39,8 +38,8 @@ pub struct PluginFeatures<'a> { #[uri("urn:lv2_options:test:OptionablePlugin")] pub struct OptionablePlugin { - options: MyIntOption, - options_serializer: OptionsSerializer, + options: Option, + options_serializer: OptionsSerializer>, } impl Plugin for OptionablePlugin { @@ -49,14 +48,14 @@ impl Plugin for OptionablePlugin { type AudioFeatures = (); fn new(_plugin_info: &PluginInfo, features: &mut Self::InitFeatures) -> Option { - let options_serializer = MyIntOption::new_serializer(&features.map)?; + let options_serializer = >::new_serializer(&features.map)?; - let mut plugin = OptionablePlugin { - options: options_serializer.deserialize_new(&features.options)?, + Some(OptionablePlugin { + options: options_serializer + .deserialize_new(&features.options) + .unwrap(), options_serializer, - }; - - Some(plugin) + }) } fn run( @@ -65,7 +64,7 @@ impl Plugin for OptionablePlugin { _features: &mut Self::AudioFeatures, _sample_count: u32, ) { - todo!() + unimplemented!() } fn extension_data(uri: &Uri) -> Option<&'static dyn Any> { @@ -113,7 +112,7 @@ pub fn test_optionable_plugin() { key: map.map_type::().unwrap().get(), size: ::core::mem::size_of::() as u32, type_: map.map_type::().unwrap().get(), - value: core::ptr::null(), + value: &option_value as *const _ as *const _, }; let end = lv2_sys::LV2_Options_Option { @@ -126,7 +125,6 @@ pub fn test_optionable_plugin() { }; let options = &mut [option, end]; - options[0].value = &option_value as *const i32 as *const _; let options_feature = Box::pin(lv2_sys::LV2_Feature { URI: OptionsList::URI.as_ptr() as *const i8, diff --git a/urid/src/lib.rs b/urid/src/lib.rs index 9d1290ff..83d5658c 100644 --- a/urid/src/lib.rs +++ b/urid/src/lib.rs @@ -119,6 +119,10 @@ pub unsafe trait UriBound { } } +unsafe impl UriBound for Option { + const URI: &'static [u8] = U::URI; +} + /// Representation of a URI for fast comparisons. /// /// A URID is basically a number which represents a URI, which makes the identification of other features faster and easier. The mapping of URIs to URIDs is handled by a something that implements the [`Map`](trait.Map.html) trait. A given URID can also be converted back to a URI with an implementation of the [`Unmap`](trait.Unmap.html) trait. However, these implementations should obviously be linked. From a6d22fd9cfc264ad06a47e2de6f92cd9fa370d69 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 2 Oct 2021 00:48:42 +0200 Subject: [PATCH 52/54] Post-merge fixes --- atom/src/atoms/scalar.rs | 4 ++-- atom/src/lib.rs | 25 ++++++++++++++++++++++++ options/src/collection.rs | 2 +- options/src/extensions.rs | 36 +++++++++-------------------------- options/src/lib.rs | 10 ++++++---- options/src/option.rs | 6 +++--- options/src/option/request.rs | 8 ++++---- options/src/option/value.rs | 2 +- 8 files changed, 51 insertions(+), 42 deletions(-) diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 3b2e5a6c..7a3a63f8 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -92,8 +92,8 @@ impl Atom for A { } } -impl BackAsSpace for A { - fn back_as_space<'a>(handle: >::Handle) -> &'a [u8] { +impl AtomAsBytes for A { + fn read_as_bytes<'a>(handle: >::Handle) -> &'a [u8] { AlignedSpace::from_slice(::core::slice::from_ref(handle)).as_bytes() } } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 67071385..178c92f1 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -164,3 +164,28 @@ pub trait Atom: UriBound { writer: AtomWriter, ) -> Result<::Handle, AtomWriteError>; } + +/// An Atom super-trait that allows to get a byte slice from an atom's read handle. +/// +/// Some LV2 APIs (such as `Option`) request a data pointer to the value of a given atom type, but +/// in many cases that pointer can be simply retrieved from a reference to a raw value. Most notably, +/// pointers to any scalar value (e.g. `&i32`) can be safely turned into a byte slice (`&[u8]). +/// +/// However, not all atoms have this capability, hence the need for a separate trait that is not +/// implemented for all types. +/// +/// # Example +/// +/// ``` +/// use lv2_atom::atoms::scalar::Int; +/// use lv2_atom::AtomAsBytes; +/// +/// let value: i32 = 42; +/// let bytes: &[u8] = Int::read_as_bytes(&value); +/// +/// assert_eq!(bytes.len(), ::core::mem::size_of::()) +/// ``` +pub trait AtomAsBytes: Atom { + /// Returns the type returned by an Atom's read handle as a byte slice. + fn read_as_bytes<'a>(handle: >::Handle) -> &'a [u8]; +} diff --git a/options/src/collection.rs b/options/src/collection.rs index 97c19572..882869c8 100644 --- a/options/src/collection.rs +++ b/options/src/collection.rs @@ -176,7 +176,7 @@ pub mod __implementation { Some(value) => self.inner.deserialize_to(value, options), None => match self.inner.deserialize_new(options) { Ok(v) => { - destination.insert(v); + let _ = destination.insert(v); Ok(()) } Err(e) => Err(e), diff --git a/options/src/extensions.rs b/options/src/extensions.rs index 84efb188..70513d3a 100644 --- a/options/src/extensions.rs +++ b/options/src/extensions.rs @@ -24,25 +24,21 @@ use urid::UriBound; /// # /// # use urid::{URID, Uri, URIDCollection, uri, Map, UriBound}; /// # use std::any::Any; +/// # use lv2_atom::atoms::scalar::Int; +/// use lv2_options::collection::OptionsSerializer; /// # -/// # impl<'a> OptionType<'a> for SomeIntOption { -/// # type AtomType = lv2_atom::scalar::Int; +/// # impl OptionType for SomeIntOption { +/// # type AtomType = Int; /// # /// # fn from_option_value(value: &i32) -> Option { /// # Some(Self(*value)) /// # } /// # -/// # fn as_option_value(&'a self) -> &'a i32 { +/// # fn as_option_value(&self) -> &i32 { /// # &self.0 /// # } /// # } /// # -/// # #[derive(URIDCollection)] -/// pub struct PluginUridCollection { -/// some_int_option: URID, -/// int: URID, -/// } -/// # /// # #[derive(FeatureCollection)] /// # pub struct PluginFeatures<'a> { /// # options: OptionsList<'a>, @@ -51,7 +47,7 @@ use urid::UriBound; /// # #[uri("urn:lv2_options:test:OptionablePlugin")] /// pub struct OptionablePlugin { /// some_int: SomeIntOption, -/// urids: PluginUridCollection, +/// some_int_serializer: OptionsSerializer, /// } /// # /// # impl Plugin for OptionablePlugin { @@ -82,26 +78,12 @@ use urid::UriBound; /// pub struct SomeIntOption(i32); /// /// impl OptionsInterface for OptionablePlugin { -/// fn get<'a>(&'a self, mut writer: OptionsWriter<'a>) -> Result<(), OptionsError> { -/// writer.process(|subject, options| match subject { // We will want to get/set our opions differently depending on the subject -/// Subject::Instance => { // In our case however, only our instance has an option -/// options.handle(self.urids.some_int_option, self.urids.int, || { -/// &self.some_int -/// }); -/// } -/// _ => {} -/// }) +/// fn get<'a>(&'a self, mut requests: OptionRequestList<'a>) -> Result<(), OptionsError> { +/// self.some_int_serializer.respond_to_requests(&self.some_int, &mut requests) /// } /// /// fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { -/// options.process(|subject, options| match subject { -/// Subject::Instance => { -/// options.handle(self.urids.some_int_option, self.urids.int, (), |value| { -/// self.some_int = value -/// }) -/// } -/// _ => {} -/// }) +/// self.some_int_serializer.deserialize_to(&mut self.some_int, &options) /// } /// } /// ``` diff --git a/options/src/lib.rs b/options/src/lib.rs index db53ac86..c0cf5094 100644 --- a/options/src/lib.rs +++ b/options/src/lib.rs @@ -34,8 +34,10 @@ pub mod features { /// Prelude of `lv2_options` for wildcard usage. pub mod prelude { - pub use crate::extensions::{OptionsDescriptor, OptionsInterface}; - pub use crate::list::OptionsList; - pub use crate::OptionsError; - pub use crate::Subject; + pub use crate::{ + extensions::{OptionsDescriptor, OptionsInterface}, + list::OptionsList, + request::{OptionRequest, OptionRequestList}, + OptionsError, Subject, + }; } diff --git a/options/src/option.rs b/options/src/option.rs index 65ce9321..4863b218 100644 --- a/options/src/option.rs +++ b/options/src/option.rs @@ -1,4 +1,4 @@ -use lv2_atom::{Atom, AtomHandle, BackAsSpace}; +use lv2_atom::{Atom, AtomAsBytes, AtomHandle}; use urid::UriBound; pub mod error; @@ -20,7 +20,7 @@ pub mod value; /// pub struct SomeIntOption(i32); /// /// impl OptionType for SomeIntOption { -/// type AtomType = lv2_atom::scalar::Int; +/// type AtomType = lv2_atom::atoms::scalar::Int; /// /// fn from_option_value(value: &i32) -> Option { /// Some(Self(*value)) @@ -32,7 +32,7 @@ pub mod value; /// } /// ``` pub trait OptionType: UriBound + Sized { - type AtomType: BackAsSpace; + type AtomType: AtomAsBytes; /// Creates a new instance of this Option type from a given atom value. /// diff --git a/options/src/option/request.rs b/options/src/option/request.rs index 8451492b..d9a0abf8 100644 --- a/options/src/option/request.rs +++ b/options/src/option/request.rs @@ -1,5 +1,5 @@ use crate::{OptionType, OptionValue, OptionsError, Subject}; -use lv2_atom::{Atom, AtomHandle, BackAsSpace}; +use lv2_atom::{Atom, AtomAsBytes, AtomHandle}; use std::marker::PhantomData; use urid::URID; @@ -45,7 +45,7 @@ impl<'a> OptionRequest<'a> { value: &'a T, ) -> Result<(), OptionsError> where - T::AtomType: BackAsSpace, + T::AtomType: AtomAsBytes, { if !self.is(option_type) { return Err(OptionsError::BadKey); @@ -62,9 +62,9 @@ impl<'a> OptionRequest<'a> { value_type: URID, value_handle: <::ReadHandle as AtomHandle>::Handle, ) where - T: BackAsSpace, + T: AtomAsBytes, { - let data = T::back_as_space(value_handle); + let data = T::read_as_bytes(value_handle); self.inner.type_ = value_type.get(); self.inner.size = data.len() as u32; self.inner.value = data.as_ptr().cast(); diff --git a/options/src/option/value.rs b/options/src/option/value.rs index a52cd750..5a5c094a 100644 --- a/options/src/option/value.rs +++ b/options/src/option/value.rs @@ -64,6 +64,6 @@ impl OptionValue { &self, ) -> Option<<::ReadHandle as AtomHandle>::Handle> { // TODO: Atoms can actually be from non-aligned spaces - T::read(AtomSpace::from_bytes_unchecked(self.data()?)) + T::read(AtomSpace::from_bytes_unchecked(self.data()?)).ok() } } From 82278dc66a7e822ebd9bca6b8a9274c894452487 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 2 Oct 2021 18:11:50 +0200 Subject: [PATCH 53/54] Merge option values and option serializer into a single struct, and add helper derive macro for OptionsCollection --- Cargo.toml | 1 + atom/src/atoms/scalar.rs | 1 + atom/src/lib.rs | 1 + buf-size/src/buffer_sizes.rs | 71 +-------- options/Cargo.toml | 3 +- options/derive/Cargo.toml | 14 ++ options/derive/src/lib.rs | 119 ++++++++++++++ options/src/collection.rs | 209 +++++-------------------- options/src/collection/option.rs | 66 ++++++++ options/src/collection/option_value.rs | 65 ++++++++ options/src/extensions.rs | 4 +- options/src/lib.rs | 3 + options/tests/optionable.rs | 69 ++++---- 13 files changed, 349 insertions(+), 277 deletions(-) create mode 100644 options/derive/Cargo.toml create mode 100644 options/derive/src/lib.rs create mode 100644 options/src/collection/option.rs create mode 100644 options/src/collection/option_value.rs diff --git a/Cargo.toml b/Cargo.toml index 6037f529..537b1c53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,7 @@ members = [ "core/derive", "midi", "options", + "options/derive", "state", "sys", "sys/tool", diff --git a/atom/src/atoms/scalar.rs b/atom/src/atoms/scalar.rs index 7a3a63f8..fddaf7e0 100644 --- a/atom/src/atoms/scalar.rs +++ b/atom/src/atoms/scalar.rs @@ -93,6 +93,7 @@ impl Atom for A { } impl AtomAsBytes for A { + #[allow(clippy::needless_lifetimes)] fn read_as_bytes<'a>(handle: >::Handle) -> &'a [u8] { AlignedSpace::from_slice(::core::slice::from_ref(handle)).as_bytes() } diff --git a/atom/src/lib.rs b/atom/src/lib.rs index 178c92f1..178639d6 100644 --- a/atom/src/lib.rs +++ b/atom/src/lib.rs @@ -187,5 +187,6 @@ pub trait Atom: UriBound { /// ``` pub trait AtomAsBytes: Atom { /// Returns the type returned by an Atom's read handle as a byte slice. + #[allow(clippy::needless_lifetimes)] // Clippy false positive fn read_as_bytes<'a>(handle: >::Handle) -> &'a [u8]; } diff --git a/buf-size/src/buffer_sizes.rs b/buf-size/src/buffer_sizes.rs index 6581d260..3e2306aa 100644 --- a/buf-size/src/buffer_sizes.rs +++ b/buf-size/src/buffer_sizes.rs @@ -1,77 +1,10 @@ use crate::options::*; +use lv2_options::prelude::OptionsCollection; -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, OptionsCollection)] pub struct BufferSizes { min_block_length: Option, max_block_length: Option, nominal_block_length: Option, sequence_size: Option, } - -const _: () = { - extern crate lv2_options as _lv2_options; - extern crate urid as _urid; - use _lv2_options::collection::{OptionsCollection, OptionsSerializationContext}; - use _lv2_options::features::OptionsList; - use _lv2_options::request::OptionRequestList; - use _lv2_options::OptionsError; - - use _urid::*; - - #[derive(_urid::URIDCollection)] - pub struct BufferSizesSerializationContext { - pub min_block_length: as OptionsCollection>::Serializer, - pub max_block_length: as OptionsCollection>::Serializer, - pub nominal_block_length: as OptionsCollection>::Serializer, - pub sequence_size: as OptionsCollection>::Serializer, - } - - impl OptionsCollection for BufferSizes { - type Serializer = BufferSizesSerializationContext; - } - - impl OptionsSerializationContext for BufferSizesSerializationContext { - fn deserialize_new(&self, options: &OptionsList) -> Result { - Ok(BufferSizes { - min_block_length: self.min_block_length.deserialize_new(options)?, - max_block_length: self.max_block_length.deserialize_new(options)?, - nominal_block_length: self.nominal_block_length.deserialize_new(options)?, - sequence_size: self.sequence_size.deserialize_new(options)?, - }) - } - - fn deserialize_to( - &self, - destination: &mut BufferSizes, - options: &OptionsList, - ) -> Result<(), OptionsError> { - self.min_block_length - .deserialize_to(&mut destination.min_block_length, options)?; - self.max_block_length - .deserialize_to(&mut destination.max_block_length, options)?; - self.nominal_block_length - .deserialize_to(&mut destination.nominal_block_length, options)?; - self.sequence_size - .deserialize_to(&mut destination.sequence_size, options)?; - - Ok(()) - } - - fn respond_to_requests<'a>( - &self, - options: &'a BufferSizes, - requests: &mut OptionRequestList<'a>, - ) -> Result<(), OptionsError> { - self.min_block_length - .respond_to_requests(&options.min_block_length, requests)?; - self.max_block_length - .respond_to_requests(&options.max_block_length, requests)?; - self.nominal_block_length - .respond_to_requests(&options.nominal_block_length, requests)?; - self.sequence_size - .respond_to_requests(&options.sequence_size, requests)?; - - Ok(()) - } - } -}; diff --git a/options/Cargo.toml b/options/Cargo.toml index 8db4726e..3a56051d 100644 --- a/options/Cargo.toml +++ b/options/Cargo.toml @@ -11,4 +11,5 @@ lv2-atom = "2.0.0" lv2-core = "3.0.0" lv2-sys = "2.0.0" lv2-urid = "2.1.0" -urid = "0.1.0" \ No newline at end of file +urid = "0.1.0" +lv2-options-derive = { path = "./derive" } diff --git a/options/derive/Cargo.toml b/options/derive/Cargo.toml new file mode 100644 index 00000000..bd9bf2bd --- /dev/null +++ b/options/derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lv2-options-derive" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +syn = {version = "1.0.5", features = ["full"]} +quote = "1.0.2" +proc-macro2 = "1.0.9" \ No newline at end of file diff --git a/options/derive/src/lib.rs b/options/derive/src/lib.rs new file mode 100644 index 00000000..139d0bd2 --- /dev/null +++ b/options/derive/src/lib.rs @@ -0,0 +1,119 @@ +//! Procedural macros for `options`. +#![recursion_limit = "128"] +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Ident}; + +#[proc_macro_derive(OptionsCollection)] +pub fn options_collection_derive(input: TokenStream) -> TokenStream { + let input: DeriveInput = parse_macro_input!(input); + + let struct_name = input.ident; + let serializer_name = Ident::new(&format!("__{}_Serializer", struct_name), struct_name.span()); + + let fields = match input.data { + Data::Struct(DataStruct { fields, .. }) => fields, + _ => panic!("Only structs can implement `OptionsCollection`"), + }; + + let field_inits = fields + .iter() + .map(|field| field.ident.as_ref().unwrap()) + .map(|ident| quote! {#ident: map.populate_collection()?,}); + + let serializer_fields = fields.iter().map(|field| { + let ident = field.ident.as_ref().unwrap(); + let ty = &field.ty; + + quote! { + #ident: <#ty as __lv2_options::collection::OptionsCollection>::Serializer, + } + }); + + let fields_deserialize_new = fields.iter().map(|field| { + let ident = field.ident.as_ref().unwrap(); + + quote! { + #ident: self.#ident.deserialize_new(options)?, + } + }); + + let fields_deserialize_to = fields.iter().map(|field| { + let ident = field.ident.as_ref().unwrap(); + + quote! { + self.#ident.deserialize_to(&mut destination.#ident, options)?; + } + }); + + let fields_respond_to_request = fields.iter().map(|field| { + let ident = field.ident.as_ref().unwrap(); + + quote! { + match self.#ident.respond_to_request(&options.#ident, request) { + Err(__lv2_options::OptionsError::BadKey) => {} + r => return r, + }; + } + }); + + let implementation = quote! { + const _: () = { + extern crate urid as __urid; + extern crate lv2_options as __lv2_options; + + #[allow(non_camel_case_types)] + pub struct #serializer_name { + #(#serializer_fields)* + } + + impl lv2_options::collection::OptionsSerializationContext<#struct_name> + for #serializer_name + { + fn deserialize_new( + &self, + options: &__lv2_options::list::OptionsList, + ) -> Result<#struct_name, __lv2_options::OptionsError> { + Ok(#struct_name { + #(#fields_deserialize_new)* + }) + } + + fn deserialize_to( + &self, + destination: &mut #struct_name, + options: &__lv2_options::list::OptionsList, + ) -> Result<(), __lv2_options::OptionsError> { + #(#fields_deserialize_to)* + Ok(()) + } + + fn respond_to_request<'a>( + &self, + options: &'a #struct_name, + request: &mut __lv2_options::prelude::OptionRequest<'a>, + ) -> Result<(), __lv2_options::OptionsError> { + #(#fields_respond_to_request)* + + Err(__lv2_options::OptionsError::BadKey) + } + } + + impl __lv2_options::collection::OptionsCollection for #struct_name { + type Serializer = #serializer_name; + } + + impl __urid::URIDCollection for #serializer_name { + fn from_map(map: &M) -> Option { + Some(Self { + #(#field_inits)* + }) + } + } + }; + }; + + implementation.into() +} diff --git a/options/src/collection.rs b/options/src/collection.rs index 882869c8..eb37537e 100644 --- a/options/src/collection.rs +++ b/options/src/collection.rs @@ -1,42 +1,61 @@ +use urid::{Map, URIDCollection}; + use crate::list::OptionsList; +use crate::option::request::OptionRequest; use crate::request::OptionRequestList; use crate::OptionsError; -use urid::{Map, URIDCollection}; + +mod option; +mod option_value; + +pub use lv2_options_derive::OptionsCollection; pub trait OptionsCollection: Sized { type Serializer: OptionsSerializationContext; - - #[inline] - fn new_serializer<'a, M: Map + ?Sized>(map: &M) -> Option> { - // FIXME - Some(OptionsSerializer { - inner: Self::Serializer::from_map(map)?, - }) - } } -pub struct OptionsSerializer { - inner: T::Serializer, +pub struct Options { + pub values: T, + serializer: T::Serializer, } -impl OptionsSerializer { +impl Options { #[inline] - pub fn deserialize_new<'a>(&'a self, list: &'a OptionsList<'a>) -> Result { - self.inner.deserialize_new(list) + pub fn default(features_map: &M) -> Option + where + T: Default, + { + Some(Self { + serializer: T::Serializer::from_map(features_map)?, + values: Default::default(), + }) } #[inline] - pub fn deserialize_to(&self, options: &mut T, list: &OptionsList) -> Result<(), OptionsError> { - self.inner.deserialize_to(options, list) + pub fn deserialize_new<'a, M: Map + ?Sized>( + features_map: &M, + options: &'a OptionsList<'a>, + ) -> Result { + let serializer = T::Serializer::from_map(features_map).ok_or(OptionsError::Unknown)?; + let values = serializer.deserialize_new(options)?; + + Ok(Self { serializer, values }) } #[inline] + pub fn deserialize(&mut self, list: &OptionsList) -> Result<(), OptionsError> { + self.serializer.deserialize_to(&mut self.values, list) + } + pub fn respond_to_requests<'a>( - &self, - options: &'a T, + &'a self, requests: &mut OptionRequestList<'a>, ) -> Result<(), OptionsError> { - self.inner.respond_to_requests(options, requests) + for request in requests { + self.serializer.respond_to_request(&self.values, request)? + } + + Ok(()) } } @@ -49,157 +68,9 @@ pub trait OptionsSerializationContext: URIDCollection { options: &OptionsList, ) -> Result<(), OptionsError>; - fn respond_to_requests<'a>( + fn respond_to_request<'a>( &self, options: &'a T, - requests: &mut OptionRequestList<'a>, + requests: &mut OptionRequest<'a>, ) -> Result<(), OptionsError>; } - -#[doc(hidden)] -pub mod __implementation { - use crate::collection::{OptionsCollection, OptionsSerializationContext}; - use crate::list::OptionsList; - use crate::request::OptionRequestList; - use crate::{OptionType, OptionsError}; - use urid::{Map, URIDCollection, URID}; - - pub mod option_value { - use super::*; - pub struct OptionTypeSerializationContext { - option_urid: URID, - option_type_atom_urid: URID, - } - - impl<'a, O: OptionType> OptionsCollection for O { - type Serializer = OptionTypeSerializationContext; - } - - impl URIDCollection for OptionTypeSerializationContext { - #[inline] - fn from_map(map: &M) -> Option { - Some(Self { - option_urid: map.populate_collection()?, - option_type_atom_urid: map.populate_collection()?, - }) - } - } - - impl OptionsSerializationContext for OptionTypeSerializationContext { - fn deserialize_new(&self, options: &OptionsList) -> Result { - for option in options { - match option.read(self.option_urid, self.option_type_atom_urid) { - Ok(value) => return Ok(value), - Err(OptionsError::BadKey) => {} - Err(e) => return Err(e), - } - } - - Err(OptionsError::BadKey) - } - - fn deserialize_to( - &self, - destination: &mut O, - options: &OptionsList, - ) -> Result<(), OptionsError> { - for option in options { - match option.read(self.option_urid, self.option_type_atom_urid) { - Ok(value) => { - *destination = value; - return Ok(()); - } - Err(OptionsError::BadKey) => {} - Err(e) => return Err(e), - } - } - - Err(OptionsError::BadKey) - } - - fn respond_to_requests<'a>( - &self, - options: &'a O, - requests: &mut OptionRequestList<'a>, - ) -> Result<(), OptionsError> { - for request in requests { - match request.try_respond(self.option_urid, self.option_type_atom_urid, options) - { - Ok(()) => return Ok(()), - Err(OptionsError::BadKey) => {} - Err(e) => return Err(e), - } - } - - Ok(()) - } - } - } - - mod option { - use super::*; - - pub struct OptionSerializationContext { - inner: O::Serializer, - } - - impl<'a, O: OptionsCollection> OptionsCollection for Option { - type Serializer = OptionSerializationContext; - } - - impl URIDCollection for OptionSerializationContext { - #[inline] - fn from_map(map: &M) -> Option { - Some(Self { - inner: map.populate_collection()?, - }) - } - } - - impl OptionsSerializationContext> - for OptionSerializationContext - { - fn deserialize_new(&self, options: &OptionsList) -> Result, OptionsError> { - match self.inner.deserialize_new(options) { - Ok(value) => Ok(Some(value)), - Err(OptionsError::BadKey) => Ok(None), - Err(e) => Err(e), - } - } - - fn deserialize_to( - &self, - destination: &mut Option, - options: &OptionsList, - ) -> Result<(), OptionsError> { - let result = match destination { - Some(value) => self.inner.deserialize_to(value, options), - None => match self.inner.deserialize_new(options) { - Ok(v) => { - let _ = destination.insert(v); - Ok(()) - } - Err(e) => Err(e), - }, - }; - - match result { - Ok(()) | Err(OptionsError::BadKey) => Ok(()), - Err(e) => Err(e), - } - } - - fn respond_to_requests<'a>( - &self, - options: &'a Option, - requests: &mut OptionRequestList<'a>, - ) -> Result<(), OptionsError> { - if let Some(value) = options { - self.inner.respond_to_requests(value, requests) - } else { - Ok(()) - } - } - } - } -} diff --git a/options/src/collection/option.rs b/options/src/collection/option.rs new file mode 100644 index 00000000..02b04b1f --- /dev/null +++ b/options/src/collection/option.rs @@ -0,0 +1,66 @@ +use crate::option::request::OptionRequest; + +use super::*; + +pub struct OptionSerializationContext { + inner: O::Serializer, +} + +impl<'a, O: OptionsCollection> OptionsCollection for Option { + type Serializer = OptionSerializationContext; +} + +impl URIDCollection for OptionSerializationContext { + #[inline] + fn from_map(map: &M) -> Option { + Some(Self { + inner: map.populate_collection()?, + }) + } +} + +impl OptionsSerializationContext> + for OptionSerializationContext +{ + fn deserialize_new(&self, options: &OptionsList) -> Result, OptionsError> { + match self.inner.deserialize_new(options) { + Ok(value) => Ok(Some(value)), + Err(OptionsError::BadKey) => Ok(None), + Err(e) => Err(e), + } + } + + fn deserialize_to( + &self, + destination: &mut Option, + options: &OptionsList, + ) -> Result<(), OptionsError> { + let result = match destination { + Some(value) => self.inner.deserialize_to(value, options), + None => match self.inner.deserialize_new(options) { + Ok(v) => { + let _ = destination.insert(v); + Ok(()) + } + Err(e) => Err(e), + }, + }; + + match result { + Ok(()) | Err(OptionsError::BadKey) => Ok(()), + Err(e) => Err(e), + } + } + + fn respond_to_request<'a>( + &self, + options: &'a Option, + requests: &mut OptionRequest<'a>, + ) -> Result<(), OptionsError> { + if let Some(value) = options { + self.inner.respond_to_request(value, requests) + } else { + Ok(()) + } + } +} diff --git a/options/src/collection/option_value.rs b/options/src/collection/option_value.rs new file mode 100644 index 00000000..147f0572 --- /dev/null +++ b/options/src/collection/option_value.rs @@ -0,0 +1,65 @@ +use crate::collection::{OptionsCollection, OptionsSerializationContext}; +use crate::list::OptionsList; +use crate::option::request::OptionRequest; +use crate::{OptionType, OptionsError}; +use urid::{Map, URIDCollection, URID}; + +pub struct OptionTypeSerializationContext { + option_urid: URID, + option_type_atom_urid: URID, +} + +impl<'a, O: OptionType> OptionsCollection for O { + type Serializer = OptionTypeSerializationContext; +} + +impl URIDCollection for OptionTypeSerializationContext { + #[inline] + fn from_map(map: &M) -> Option { + Some(Self { + option_urid: map.populate_collection()?, + option_type_atom_urid: map.populate_collection()?, + }) + } +} + +impl OptionsSerializationContext for OptionTypeSerializationContext { + fn deserialize_new(&self, options: &OptionsList) -> Result { + for option in options { + match option.read(self.option_urid, self.option_type_atom_urid) { + Ok(value) => return Ok(value), + Err(OptionsError::BadKey) => {} + Err(e) => return Err(e), + } + } + + Err(OptionsError::BadKey) + } + + fn deserialize_to( + &self, + destination: &mut O, + options: &OptionsList, + ) -> Result<(), OptionsError> { + for option in options { + match option.read(self.option_urid, self.option_type_atom_urid) { + Ok(value) => { + *destination = value; + return Ok(()); + } + Err(OptionsError::BadKey) => {} + Err(e) => return Err(e), + } + } + + Err(OptionsError::BadKey) + } + + fn respond_to_request<'a>( + &self, + options: &'a O, + request: &mut OptionRequest<'a>, + ) -> Result<(), OptionsError> { + request.try_respond(self.option_urid, self.option_type_atom_urid, options) + } +} diff --git a/options/src/extensions.rs b/options/src/extensions.rs index 70513d3a..176b2974 100644 --- a/options/src/extensions.rs +++ b/options/src/extensions.rs @@ -25,7 +25,7 @@ use urid::UriBound; /// # use urid::{URID, Uri, URIDCollection, uri, Map, UriBound}; /// # use std::any::Any; /// # use lv2_atom::atoms::scalar::Int; -/// use lv2_options::collection::OptionsSerializer; +/// use lv2_options::collection::Options; /// # /// # impl OptionType for SomeIntOption { /// # type AtomType = Int; @@ -47,7 +47,7 @@ use urid::UriBound; /// # #[uri("urn:lv2_options:test:OptionablePlugin")] /// pub struct OptionablePlugin { /// some_int: SomeIntOption, -/// some_int_serializer: OptionsSerializer, +/// some_int_serializer: Options, /// } /// # /// # impl Plugin for OptionablePlugin { diff --git a/options/src/lib.rs b/options/src/lib.rs index c0cf5094..3152fbd5 100644 --- a/options/src/lib.rs +++ b/options/src/lib.rs @@ -22,6 +22,8 @@ pub use option::subject::Subject; pub use option::value::OptionValue; pub use option::OptionType; +pub use lv2_options_derive::*; + pub mod collection; pub mod extensions; pub mod list; @@ -35,6 +37,7 @@ pub mod features { /// Prelude of `lv2_options` for wildcard usage. pub mod prelude { pub use crate::{ + collection::OptionsCollection, extensions::{OptionsDescriptor, OptionsInterface}, list::OptionsList, request::{OptionRequest, OptionRequestList}, diff --git a/options/tests/optionable.rs b/options/tests/optionable.rs index 62fd15d9..13a81772 100644 --- a/options/tests/optionable.rs +++ b/options/tests/optionable.rs @@ -1,18 +1,13 @@ use lv2_atom::atoms::scalar::Int; use lv2_core::prelude::*; -use lv2_options::collection::{OptionsCollection, OptionsSerializer}; +use lv2_options::collection::Options; use lv2_options::prelude::*; -use lv2_options::request::OptionRequestList; use lv2_urid::{HostMap, LV2Map}; use std::any::Any; use std::ffi::c_void; use std::os::raw::c_char; use std::pin::Pin; -use urid::Map; -use urid::{uri, HashURIDMapper, Uri, UriBound}; - -#[uri("urn:lv2_options:test:SomeIntOption")] -pub struct MyIntOption(i32); +use urid::{uri, HashURIDMapper, Map, Uri, UriBound}; impl lv2_options::OptionType for MyIntOption { type AtomType = lv2_atom::atoms::scalar::Int; @@ -26,38 +21,52 @@ impl lv2_options::OptionType for MyIntOption { } } -pub struct MyPluginOptions { - some_int_option: MyIntOption, -} - #[derive(FeatureCollection)] pub struct PluginFeatures<'a> { map: LV2Map<'a>, options: OptionsList<'a>, } +#[uri("urn:lv2_options:test:SomeIntOption")] +pub struct MyIntOption(pub i32); + #[uri("urn:lv2_options:test:OptionablePlugin")] pub struct OptionablePlugin { - options: Option, - options_serializer: OptionsSerializer>, + options: Options, +} + +#[derive(OptionsCollection)] +pub struct MyPluginOptions { + some_int_option: MyIntOption, +} + +impl OptionsInterface for OptionablePlugin { + fn get<'a>(&'a self, mut requests: OptionRequestList<'a>) -> Result<(), OptionsError> { + self.options.respond_to_requests(&mut requests) + } + + fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { + assert_eq!(self.options.values.some_int_option.0, 42); + let result = self.options.deserialize(&options); + assert_eq!(self.options.values.some_int_option.0, 69); + + result + } } impl Plugin for OptionablePlugin { + fn new(_plugin_info: &PluginInfo, features: &mut Self::InitFeatures) -> Option { + let options: Options = + Options::deserialize_new(&features.map, &features.options).unwrap(); + + assert_eq!(options.values.some_int_option.0, 42); + Some(OptionablePlugin { options }) + } + type Ports = (); type InitFeatures = PluginFeatures<'static>; type AudioFeatures = (); - fn new(_plugin_info: &PluginInfo, features: &mut Self::InitFeatures) -> Option { - let options_serializer = >::new_serializer(&features.map)?; - - Some(OptionablePlugin { - options: options_serializer - .deserialize_new(&features.options) - .unwrap(), - options_serializer, - }) - } - fn run( &mut self, _ports: &mut Self::Ports, @@ -72,18 +81,6 @@ impl Plugin for OptionablePlugin { } } -impl OptionsInterface for OptionablePlugin { - fn get<'a>(&'a self, mut requests: OptionRequestList<'a>) -> Result<(), OptionsError> { - self.options_serializer - .respond_to_requests(&self.options, &mut requests) - } - - fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { - self.options_serializer - .deserialize_to(&mut self.options, &options) - } -} - lv2_descriptors! { OptionablePlugin } From 33111ed7cce5d5ccd0481aa842dfe0ed265b1312 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 2 Oct 2021 19:51:40 +0200 Subject: [PATCH 54/54] Minor fix --- options/tests/optionable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options/tests/optionable.rs b/options/tests/optionable.rs index 13a81772..62e1c3c5 100644 --- a/options/tests/optionable.rs +++ b/options/tests/optionable.rs @@ -47,10 +47,10 @@ impl OptionsInterface for OptionablePlugin { fn set(&mut self, options: OptionsList) -> Result<(), OptionsError> { assert_eq!(self.options.values.some_int_option.0, 42); - let result = self.options.deserialize(&options); + self.options.deserialize(&options)?; assert_eq!(self.options.values.some_int_option.0, 69); - result + Ok(()) } }