Skip to content

Commit

Permalink
Atom soundness and usability fixes (#100)
Browse files Browse the repository at this point in the history
Mark atom-reading methods as unsafe.
Renamed Space into AlignedSpace, and made it a Dynamically Sized Type.
Removed all of the use_padding parameters in atom writing methods.
Added a SpaceReader struct.
Removed all the odd split_* methods on Space.
Added VecSpace, replacing SpaceList.
Renamed FramedMutSpace into AtomSpaceWriter
Renamed RootMutSpace into SpaceCursor.
Renamed the MutSpace trait into SpaceWriter.
Added the Terminated struct.
Made UnidentifiedAtom a Dynamically Sized Type.
Added the AtomHeader struct.
Removed lifetimes from the Atom types (and removed many extra lifetimes accordingly).
Removed ReadParameter and WriteParameter associated types.
Moved all base atom types to a sub-module.
Removed lifetimes in the Atom type.
Removed Vector type parameter.
Moved Sequence unit type reading into a type parameter for its reader.
Added an AtomError error type, as well as sub-error types AtomReadError, AtomWriteError, and AlignmentError.
Added a special prelude for the atom-implementing side.
Added lots of extra documentation, especially in AlignedSpace.
  • Loading branch information
prokopyl authored Nov 29, 2021
1 parent b6d02f3 commit 44aac00
Show file tree
Hide file tree
Showing 64 changed files with 4,995 additions and 2,646 deletions.
3 changes: 2 additions & 1 deletion atom/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ maintenance = { status = "passively-maintained" }
[dependencies]
lv2-sys = "2.0.0"
lv2-units = "0.1.3"
urid = "0.1.0"
urid = { version = "0.1.0", default-features = false }

[dependencies.lv2-core]
version = "3.0.0"
optional = true
default-features = false

[dev-dependencies]
lv2-urid = "2.1.0"
Expand Down
51 changes: 51 additions & 0 deletions atom/src/atoms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
pub mod chunk;
pub mod object;
pub mod scalar;
pub mod sequence;
pub mod string;
pub mod tuple;
pub mod vector;

use urid::*;

/// An URID collection of all standard atom types, provided for convenience.
#[derive(Clone)]
pub struct AtomURIDCollection {
pub blank: URID<object::Blank>,
pub double: URID<scalar::Double>,
pub float: URID<scalar::Float>,
pub int: URID<scalar::Int>,
pub long: URID<scalar::Long>,
pub urid: URID<scalar::AtomURID>,
pub bool: URID<scalar::Bool>,
pub vector: URID<vector::Vector>,
pub chunk: URID<chunk::Chunk>,
pub literal: URID<string::Literal>,
pub object: URID<object::Object>,
pub property: URID<object::Property>,
pub string: URID<string::String>,
pub tuple: URID<tuple::Tuple>,
pub sequence: URID<sequence::Sequence>,
}

impl URIDCollection for AtomURIDCollection {
fn from_map<M: Map + ?Sized>(map: &M) -> Option<Self> {
Some(Self {
blank: map.map_type()?,
double: map.map_type()?,
float: map.map_type()?,
int: map.map_type()?,
long: map.map_type()?,
urid: map.map_type()?,
bool: map.map_type()?,
vector: map.map_type()?,
chunk: map.map_type()?,
literal: map.map_type()?,
object: map.map_type()?,
property: map.map_type()?,
string: map.map_type()?,
tuple: map.map_type()?,
sequence: map.map_type()?,
})
}
}
125 changes: 125 additions & 0 deletions atom/src/atoms/chunk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//! An atom containing memory of undefined type.
//!
//! This contents of this atom is considered as a simple blob of data. It used, for example, by the host to transmit the size of a writable atom port. Since it is so simple, it does not need a reading or writing parameter.
//!
//! # Example
//! ```
//! use lv2_core::prelude::*;
//! use lv2_atom::prelude::*;
//!
//! use lv2_atom::space::{AtomSpace, AtomWriter, SpaceWriter};
//!
//! #[derive(PortCollection)]
//! struct MyPorts {
//! input: InputPort<AtomPort>,
//! output: OutputPort<AtomPort>,
//! }
//!
//! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) {
//! let in_chunk: &AtomSpace = ports.input.read(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();
//! }
//! ```
//!
//! # 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::AtomWriter;
use crate::{Atom, AtomHandle};
use urid::UriBound;

/// An atom containing an arbitrary byte buffer.
///
/// [See also the module documentation.](index.html)
pub struct Chunk;

unsafe impl UriBound for Chunk {
const URI: &'static [u8] = sys::LV2_ATOM__Chunk;
}

pub struct ChunkReaderHandle;
impl<'a> AtomHandle<'a> for ChunkReaderHandle {
type Handle = &'a AtomSpace;
}

pub struct ChunkWriterHandle;
impl<'a> AtomHandle<'a> for ChunkWriterHandle {
type Handle = AtomWriter<'a>;
}

impl Atom for Chunk {
type ReadHandle = ChunkReaderHandle;
type WriteHandle = ChunkWriterHandle;

#[inline]
unsafe fn read(
body: &AtomSpace,
) -> Result<<Self::ReadHandle as AtomHandle>::Handle, AtomReadError> {
Ok(body)
}

#[inline]
fn write(
frame: AtomWriter,
) -> Result<<Self::WriteHandle as AtomHandle>::Handle, AtomWriteError> {
Ok(frame)
}
}

#[cfg(test)]
mod tests {
use crate::atoms::chunk::*;
use crate::*;

#[test]
fn test_chunk_and_slice_writer() {
const SLICE_LENGTH: usize = 42;

let map = HashURIDMapper::new();
let urids = crate::atoms::AtomURIDCollection::from_map(&map).unwrap();

let mut raw_space = AlignedVec::<AtomHeader>::new_with_capacity(64);
let raw_space = raw_space.as_space_mut();

// writing
{
let mut space = SpaceCursor::new(raw_space.as_bytes_mut());
let mut writer = space.write_atom(urids.chunk).unwrap();
let data = writer.allocate(SLICE_LENGTH).unwrap();

for (i, value) in data.iter_mut().enumerate() {
*value = i as u8;
}

space.write_value(41u8).unwrap();
}

// verifying
{
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 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.read().next_atom().unwrap().body()) }.unwrap();
assert_eq!(data.bytes_len(), SLICE_LENGTH);

for (i, value) in data.as_bytes().iter().enumerate() {
assert_eq!(*value as usize, i);
}
}
}
}
Loading

0 comments on commit 44aac00

Please sign in to comment.