lv2_atom/
scalar.rs

1//! Scalar, single-value atoms.
2//!
3//! These atoms are the simplest of them all: They are simply represented by an internal type and their values can simply be copied. Due to this common behaviour, there is another trait called [`ScalarAtom`](trait.ScalarAtom.html) which provides this behaviour. Every type that implements `ScalarAtom` also implements `Atom`.
4//!
5//! Unlike other atoms, scalars do not need to be written after the initialization. However, you still can modify the scalar after it was initialized.
6//!
7//! # Example
8//!
9//! ```
10//! use lv2_core::prelude::*;
11//! use lv2_atom::prelude::*;
12//!
13//! #[derive(PortCollection)]
14//! struct MyPorts {
15//!     input: InputPort<AtomPort>,
16//!     output: OutputPort<AtomPort>,
17//! }
18//!
19//! /// Something like a plugin's run method.
20//! fn run(ports: &mut MyPorts, urids: &AtomURIDCollection) {
21//!     // Scalar atoms don't need a reading parameter.
22//!     let read_value: f32 = ports.input.read(urids.float, ()).unwrap();
23//!
24//!     // Writing is done with the value of the atom.
25//!     // You can modify it afterwards.
26//!     let written_value: &mut f32 = ports.output.init(urids.float, 17.0).unwrap();
27//! }
28//! ```
29//!
30//! # Specification
31//!
32//! [http://lv2plug.in/ns/ext/atom/atom.html#Number](http://lv2plug.in/ns/ext/atom/atom.html#Number)
33use crate::space::*;
34use crate::*;
35use std::marker::Unpin;
36use urid::UriBound;
37use urid::URID;
38
39/// An atom that only contains a single, scalar value.
40///
41/// Since scalar values are so simple, the reading and writing methods are exactly the same.
42pub trait ScalarAtom: UriBound {
43    /// The internal representation of the atom.
44    ///
45    /// For example, the `Int` atom has the internal type of `i32`, which is `i32` on most platforms.
46    type InternalType: Unpin + Copy + Send + Sync + Sized + 'static;
47
48    /// Try to read the atom from a space.
49    ///
50    /// 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.
51    fn read_scalar(body: Space) -> Option<Self::InternalType> {
52        body.split_type::<Self::InternalType>()
53            .map(|(value, _)| *value)
54    }
55
56    /// Try to write the atom into a space.
57    ///
58    /// 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`.
59    fn write_scalar<'a, 'b>(
60        mut frame: FramedMutSpace<'a, 'b>,
61        value: Self::InternalType,
62    ) -> Option<&'a mut Self::InternalType> {
63        (&mut frame as &mut dyn MutSpace).write(&value, true)
64    }
65}
66
67impl<'a, 'b, A: ScalarAtom> Atom<'a, 'b> for A
68where
69    'a: 'b,
70{
71    type ReadParameter = ();
72    type ReadHandle = A::InternalType;
73    type WriteParameter = A::InternalType;
74    type WriteHandle = &'a mut A::InternalType;
75
76    fn read(body: Space<'a>, _: ()) -> Option<A::InternalType> {
77        <A as ScalarAtom>::read_scalar(body)
78    }
79
80    fn init(
81        frame: FramedMutSpace<'a, 'b>,
82        value: A::InternalType,
83    ) -> Option<&'a mut A::InternalType> {
84        <A as ScalarAtom>::write_scalar(frame, value)
85    }
86}
87
88/// Macro to atomate the definition of scalar atoms.
89macro_rules! make_scalar_atom {
90    ($atom:ty, $internal:ty, $uri:expr, $urid:expr) => {
91        unsafe impl UriBound for $atom {
92            const URI: &'static [u8] = $uri;
93        }
94
95        impl ScalarAtom for $atom {
96            type InternalType = $internal;
97        }
98    };
99}
100
101/// A scalar atom containing a `f64` (`f64` on most platforms).
102pub struct Double;
103
104make_scalar_atom!(
105    Double,
106    f64,
107    sys::LV2_ATOM__Double,
108    |urids: &AtomURIDCollection| urids.double
109);
110
111/// A scalar atom containing a `f32` (`f32` on most platforms).
112pub struct Float;
113
114make_scalar_atom!(
115    Float,
116    f32,
117    sys::LV2_ATOM__Float,
118    |urids: &AtomURIDCollection| { urids.float }
119);
120
121/// A scalar atom containing a `i64` (`i64` on most platforms).
122pub struct Long;
123
124make_scalar_atom!(
125    Long,
126    i64,
127    sys::LV2_ATOM__Long,
128    |urids: &AtomURIDCollection| { urids.long }
129);
130
131/// A scalar atom containing a `i32` (`i32` on most platforms).
132pub struct Int;
133
134make_scalar_atom!(
135    Int,
136    i32,
137    sys::LV2_ATOM__Int,
138    |urids: &AtomURIDCollection| { urids.int }
139);
140
141/// A scalar atom representing a boolean.
142///
143/// Internally, this atom is represented by a `i32`, which is `==0` for `false` and `>= 1` for `true`
144pub struct Bool;
145
146make_scalar_atom!(
147    Bool,
148    i32,
149    sys::LV2_ATOM__Bool,
150    |urids: &AtomURIDCollection| { urids.bool }
151);
152
153/// A scalar atom containing a URID.
154pub struct AtomURID;
155
156make_scalar_atom!(
157    AtomURID,
158    URID,
159    sys::LV2_ATOM__URID,
160    |urids: &AtomURIDCollection| urids.urid
161);
162
163#[cfg(test)]
164mod tests {
165    use crate::prelude::*;
166    use crate::scalar::ScalarAtom;
167    use crate::space::*;
168    use std::convert::TryFrom;
169    use std::mem::size_of;
170    use urid::*;
171
172    fn test_scalar<A: ScalarAtom>(value: A::InternalType)
173    where
174        A::InternalType: PartialEq<A::InternalType>,
175        A::InternalType: std::fmt::Debug,
176    {
177        let map = HashURIDMapper::new();
178        let urid: URID<A> = map.map_type().unwrap();
179
180        let mut raw_space: Box<[u8]> = Box::new([0; 256]);
181
182        // writing
183        {
184            let mut space = RootMutSpace::new(raw_space.as_mut());
185            (&mut space as &mut dyn MutSpace).init(urid, value).unwrap();
186        }
187
188        // verifying
189        {
190            /// Generic version of the scalar atom structs.
191            #[repr(C)]
192            struct Scalar<B: Sized> {
193                atom: sys::LV2_Atom,
194                body: B,
195            }
196
197            let (scalar, _) = raw_space.split_at(size_of::<sys::LV2_Atom>());
198
199            let scalar = unsafe { &*(scalar.as_ptr() as *const Scalar<A::InternalType>) };
200            assert_eq!(scalar.atom.type_, urid);
201            assert_eq!(scalar.atom.size as usize, size_of::<A::InternalType>());
202            assert_eq!(scalar.body, value);
203        }
204
205        // reading
206        {
207            let space = Space::from_slice(raw_space.as_ref());
208            let (body, _) = space.split_atom_body(urid).unwrap();
209            assert_eq!(A::read(body, ()).unwrap(), value);
210        }
211    }
212
213    #[test]
214    fn test_scalars() {
215        test_scalar::<Double>(42.0);
216        test_scalar::<Float>(42.0);
217        test_scalar::<Long>(42);
218        test_scalar::<Int>(42);
219        test_scalar::<Bool>(1);
220        test_scalar::<AtomURID>(URID::try_from(1).unwrap());
221    }
222}