Skip to main content

lv2_state/
raw.rs

1use crate::StateErr;
2use atom::prelude::*;
3use atom::space::*;
4use std::collections::HashMap;
5use std::ffi::c_void;
6use std::marker::PhantomData;
7use urid::*;
8
9/// Property storage handle.
10///
11/// This handle can be used to store the properties of a plugin. It uses the atom system to encode the properties and is backed by a storage callback function.
12///
13/// 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.
14pub struct StoreHandle<'a> {
15    properties: HashMap<URID, SpaceElement>,
16    store_fn: sys::LV2_State_Store_Function,
17    handle: sys::LV2_State_Handle,
18    lifetime: PhantomData<&'a mut c_void>,
19}
20
21impl<'a> StoreHandle<'a> {
22    /// Create a new store handle.
23    pub fn new(store_fn: sys::LV2_State_Store_Function, handle: sys::LV2_State_Handle) -> Self {
24        StoreHandle {
25            properties: HashMap::new(),
26            store_fn,
27            handle,
28            lifetime: PhantomData,
29        }
30    }
31
32    /// Draft a new property.
33    ///
34    /// This will return a new handle to create a property. Once the property is completely written, you can commit it by calling [`commit`](#method.commit) or [`commit_all`](#method.commit_all). Then, and only then, it will be saved by the host.
35    ///
36    /// 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).
37    pub fn draft<K: ?Sized>(&mut self, property_key: URID<K>) -> StatePropertyWriter {
38        let property_key = property_key.into_general();
39        self.properties
40            .insert(property_key.into_general(), SpaceElement::default());
41        StatePropertyWriter::new(SpaceHead::new(
42            self.properties
43                .get_mut(&property_key.into_general())
44                .unwrap(),
45        ))
46    }
47
48    /// Internal helper function to store a property.
49    pub fn commit_pair<K: ?Sized>(
50        store_fn: sys::LV2_State_Store_Function,
51        handle: sys::LV2_State_Handle,
52        key: URID<K>,
53        space: SpaceElement,
54    ) -> Result<(), StateErr> {
55        let store_fn = store_fn.ok_or(StateErr::BadCallback)?;
56        let space: Vec<u8> = space.to_vec();
57        let space = Space::from_slice(space.as_ref());
58        let (header, data) = space
59            .split_type::<sys::LV2_Atom>()
60            .ok_or(StateErr::BadData)?;
61        let data = data
62            .split_raw(header.size as usize)
63            .map(|(data, _)| data)
64            .ok_or(StateErr::BadData)?;
65
66        let key = key.get();
67        let data_ptr = data as *const _ as *const c_void;
68        let data_size = header.size as usize;
69        let data_type = header.type_;
70        let flags: u32 = (sys::LV2_State_Flags::LV2_STATE_IS_POD
71            | sys::LV2_State_Flags::LV2_STATE_IS_PORTABLE)
72            .into();
73        StateErr::from(unsafe { (store_fn)(handle, key, data_ptr, data_size, data_type, flags) })
74    }
75
76    /// Commit all created properties.
77    ///
78    /// This will also clear the property buffer.
79    pub fn commit_all(&mut self) -> Result<(), StateErr> {
80        for (key, space) in self.properties.drain() {
81            Self::commit_pair(self.store_fn, self.handle, key, space)?;
82        }
83        Ok(())
84    }
85
86    /// Commit one specific property.
87    ///
88    /// 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.
89    pub fn commit<K: ?Sized>(&mut self, key: URID<K>) -> Option<Result<(), StateErr>> {
90        let key = key.into_general();
91        let space = self.properties.remove(&key)?;
92        Some(Self::commit_pair(self.store_fn, self.handle, key, space))
93    }
94
95    /// Discard all drafted properties.
96    pub fn discard_all(&mut self) {
97        self.properties.clear();
98    }
99
100    /// Discard a drafted property.
101    ///
102    /// If no property with the given key was drafted before, this is a no-op.
103    pub fn discard<K: ?Sized>(&mut self, key: URID<K>) {
104        self.properties.remove(&key.into_general());
105    }
106}
107
108/// Writing handle for properties.
109pub struct StatePropertyWriter<'a> {
110    head: SpaceHead<'a>,
111    initialized: bool,
112}
113
114impl<'a> StatePropertyWriter<'a> {
115    /// Create a new property writer that uses the given space head.
116    pub fn new(head: SpaceHead<'a>) -> Self {
117        Self {
118            head,
119            initialized: false,
120        }
121    }
122
123    /// Initialize the property.
124    ///
125    /// 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.
126    pub fn init<'b, A: Atom<'a, 'b>>(
127        &'b mut self,
128        urid: URID<A>,
129        parameter: A::WriteParameter,
130    ) -> Result<A::WriteHandle, StateErr> {
131        if !self.initialized {
132            self.initialized = true;
133            (&mut self.head as &mut dyn MutSpace)
134                .init(urid, parameter)
135                .ok_or(StateErr::Unknown)
136        } else {
137            Err(StateErr::Unknown)
138        }
139    }
140}
141
142/// Property retrieval handle.
143pub struct RetrieveHandle<'a> {
144    retrieve_fn: sys::LV2_State_Retrieve_Function,
145    handle: sys::LV2_State_Handle,
146    lifetime: PhantomData<&'a mut c_void>,
147}
148
149impl<'a> RetrieveHandle<'a> {
150    /// Create a new retrieval handle that uses the given callback function and handle.
151    pub fn new(
152        retrieve_fn: sys::LV2_State_Retrieve_Function,
153        handle: sys::LV2_State_Handle,
154    ) -> Self {
155        RetrieveHandle {
156            retrieve_fn,
157            handle,
158            lifetime: PhantomData,
159        }
160    }
161
162    /// Try to retrieve a property from the host.
163    ///
164    /// This method calls the internal retrieve callback with the given URID. If there's no property with the given URID, `Err(StateErr::NoProperty)` is returned. Otherwise, a reading handle is returned that contains the type and the data of the property and can interpret it as an atom.
165    pub fn retrieve<K: ?Sized>(&self, key: URID<K>) -> Result<StatePropertyReader, StateErr> {
166        let mut size: usize = 0;
167        let mut type_: u32 = 0;
168        let property_ptr: *const std::ffi::c_void = unsafe {
169            (self.retrieve_fn.ok_or(StateErr::BadCallback)?)(
170                self.handle,
171                key.get(),
172                &mut size,
173                &mut type_,
174                std::ptr::null_mut(),
175            )
176        };
177
178        let type_ = URID::new(type_).ok_or(StateErr::Unknown)?;
179        let space = if !property_ptr.is_null() {
180            unsafe { std::slice::from_raw_parts(property_ptr as *const u8, size) }
181        } else {
182            return Err(StateErr::NoProperty);
183        };
184
185        Ok(StatePropertyReader::new(type_, Space::from_slice(space)))
186    }
187}
188
189/// Reading handle for properties.
190///
191/// This handle contains the type and the data of a property retrieved from the [`RetrieveHandle`](struct.RetrieveHandle.html).
192pub struct StatePropertyReader<'a> {
193    type_: URID,
194    body: Space<'a>,
195}
196
197impl<'a> StatePropertyReader<'a> {
198    /// Create a new reading handle with the given type and data.
199    pub fn new<T: ?Sized>(type_: URID<T>, body: Space<'a>) -> Self {
200        Self {
201            type_: type_.into_general(),
202            body,
203        }
204    }
205
206    /// Return the type of the property.
207    pub fn type_(&self) -> URID {
208        self.type_
209    }
210
211    /// Return the data of the property.
212    pub fn body(&self) -> Space {
213        self.body
214    }
215
216    /// Try to interpret the property as an atom.
217    ///
218    /// 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.
219    ///
220    /// If the desired and actual data types don't match, `Err(StateErr::BadType)` is returned.
221    pub fn read<A: Atom<'a, 'a>>(
222        &self,
223        urid: URID<A>,
224        parameter: A::ReadParameter,
225    ) -> Result<A::ReadHandle, StateErr> {
226        if urid == self.type_ {
227            A::read(self.body, parameter).ok_or(StateErr::Unknown)
228        } else {
229            Err(StateErr::BadType)
230        }
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use crate::raw::*;
237    use crate::storage::Storage;
238    use atom::space::Space;
239
240    fn store(storage: &mut Storage, urids: &AtomURIDCollection) {
241        let mut store_handle = storage.store_handle();
242
243        store_handle
244            .draft(URID::new(1).unwrap())
245            .init(urids.int, 17)
246            .unwrap();
247        store_handle
248            .draft(URID::new(2).unwrap())
249            .init(urids.float, 1.0)
250            .unwrap();
251
252        store_handle.commit(URID::new(1).unwrap()).unwrap().unwrap();
253
254        let mut vector_writer = store_handle.draft(URID::new(3).unwrap());
255        let mut vector_writer = vector_writer.init(urids.vector(), urids.int).unwrap();
256        vector_writer.append(&[1, 2, 3, 4]).unwrap();
257
258        store_handle.commit_all().unwrap();
259
260        store_handle
261            .draft(URID::new(4).unwrap())
262            .init(urids.int, 0)
263            .unwrap();
264    }
265
266    fn retrieve(storage: &mut Storage, urids: &AtomURIDCollection) {
267        let retrieve_handle = storage.retrieve_handle();
268
269        assert_eq!(
270            17,
271            retrieve_handle
272                .retrieve(URID::new(1).unwrap())
273                .unwrap()
274                .read(urids.int, ())
275                .unwrap()
276        );
277        assert_eq!(
278            1.0,
279            retrieve_handle
280                .retrieve(URID::new(2).unwrap())
281                .unwrap()
282                .read(urids.float, ())
283                .unwrap()
284        );
285        assert_eq!(
286            [1, 2, 3, 4],
287            retrieve_handle
288                .retrieve(URID::new(3).unwrap())
289                .unwrap()
290                .read(urids.vector(), urids.int)
291                .unwrap()
292        );
293        assert!(retrieve_handle.retrieve(URID::new(4).unwrap()).is_err());
294    }
295
296    #[test]
297    fn test_storage() {
298        let map = HashURIDMapper::new();
299        let urids = AtomURIDCollection::from_map(&map).unwrap();
300
301        let mut storage = Storage::default();
302
303        store(&mut storage, &urids);
304
305        for (key, (type_, value)) in storage.iter() {
306            match key.get() {
307                1 => {
308                    assert_eq!(urids.int, *type_);
309                    assert_eq!(17, unsafe { *(value.as_slice() as *const _ as *const i32) });
310                }
311                2 => {
312                    assert_eq!(urids.float, *type_);
313                    assert_eq!(1.0, unsafe {
314                        *(value.as_slice() as *const _ as *const f32)
315                    });
316                }
317                3 => {
318                    assert_eq!(urids.vector::<Int>(), *type_);
319                    let space = Space::from_slice(value.as_slice());
320                    let data = Vector::read(space, urids.int).unwrap();
321                    assert_eq!([1, 2, 3, 4], data);
322                }
323                _ => panic!("Invalid key!"),
324            }
325        }
326
327        retrieve(&mut storage, &urids);
328    }
329}