lv2rs_atom/
ports.rs

1//! Wrappers for raw atom IO.
2//!
3//! The wrappers provided by this module increase the safety and usability of atom IO.
4use crate::atom::*;
5use crate::frame::RootFrame;
6use std::marker::PhantomData;
7use std::ptr::{null, null_mut};
8
9/// Wrapper for atom writing operations.
10pub struct AtomOutputPort<A: AtomBody + ?Sized> {
11    atom: *mut Atom,
12    phantom: PhantomData<A>,
13}
14
15/// Errors that may occur when calling
16/// [`AtomOutputPort::write_atom_body`](struct.AtomOuputPort.html#method.write_atom_body)
17#[derive(Debug)]
18pub enum WriteAtomError {
19    /// The internal pointer points to zero.
20    ///
21    /// Maybe `connect_port` is not implemented correctly?
22    NullPointer,
23    /// The host hasn't allocated enough memory to initialize the atom.
24    InsufficientSpace,
25}
26
27#[derive(Debug)]
28/// Error that may occur when calling [`AtomInputPort::get_atom_body`](struct.AtomInputPort.html#method.get_atom_body).
29pub enum GetAtomError {
30    /// The internal pointer points to zero.
31    ///
32    /// Maybe `connect_port` is not implemented correctly?
33    NullPointer,
34    /// Widening the atom header failed.
35    GetBody(GetBodyError),
36}
37
38impl<A: AtomBody + ?Sized> AtomOutputPort<A> {
39    /// Create a new port.
40    ///
41    /// Please note that the newly created port wil point to null and therefore,
42    /// [`write_atom_body`](#method.write_atom_body) will yield undefined behaviour.
43    pub fn new() -> Self {
44        Self {
45            atom: null_mut(),
46            phantom: PhantomData,
47        }
48    }
49
50    /// Set the internal atom pointer.
51    ///
52    /// As implied by the name, this method should be called by an atom's `connect_port`. However,
53    /// you have to cast the passed pointer to the correct type!
54    pub fn connect_port(&mut self, atom: *mut Atom) {
55        self.atom = atom;
56    }
57
58    /// Write an atom to the internal atom pointer.
59    ///
60    /// This method will create a [`RootFrame`](../frame/struct.RootFrame.html) and initialize the
61    /// body. For [scalar atoms](../scalar/index.html), this is all you can and need to
62    /// do. For all other atoms, you can write additional data using the `RootFrame`.
63    ///
64    /// This method is unsafe since it dereferences the raw, internal pointer and therefore could
65    /// yield undefined behaviour. Make sure that your plugin's `connect_port` method calls this
66    /// port's [`connect_port`](#method.connect_port) method correctly!
67    pub unsafe fn write_atom_body<'a>(
68        &'a mut self,
69        parameter: &A::InitializationParameter,
70        urids: &mut urid::CachedMap,
71    ) -> Result<RootFrame<'a, A>, WriteAtomError> {
72        let header = match self.atom.as_mut() {
73            Some(header) => header,
74            None => return Err(WriteAtomError::NullPointer),
75        };
76        let data = std::slice::from_raw_parts_mut(self.atom as *mut u8, header.size() as usize);
77        let mut frame =
78            RootFrame::new(data, urids).map_err(|_| WriteAtomError::InsufficientSpace)?;
79        A::initialize_body(&mut frame, parameter, urids)
80            .map_err(|_| WriteAtomError::InsufficientSpace)?;
81        Ok(frame)
82    }
83}
84
85/// Wrapper for atom reading operations.
86pub struct AtomInputPort<A: AtomBody + ?Sized> {
87    atom: *const Atom,
88    phantom: PhantomData<A>,
89}
90
91impl<A: AtomBody + ?Sized> AtomInputPort<A> {
92    /// Create a new port.
93    ///
94    /// Please note that the newly created port wil point to null and therefore,
95    /// [`write_atom_body`](#method.write_atom_body) will yield undefined behaviour.
96    pub fn new() -> Self {
97        Self {
98            atom: null(),
99            phantom: PhantomData,
100        }
101    }
102
103    /// Set the internal atom pointer.
104    ///
105    /// As implied by the name, this method should be called by an atom's `connect_port`. However,
106    /// you have to cast the passed pointer to the correct type!
107    pub fn connect_port(&mut self, atom: *const Atom) {
108        self.atom = atom;
109    }
110
111    /// Dereference the internal raw pointer to an atom body reference.
112    ///
113    /// This method is unsafe since it dereferences the raw, internal pointer and therefore could
114    /// yield undefined behaviour. Make sure that your plugin's `connect_port` method calls this
115    /// port's [`connect_port`](#method.connect_port) method correctly!
116    pub unsafe fn get_atom_body(&self, urids: &mut urid::CachedMap) -> Result<&A, GetAtomError> {
117        let atom = match self.atom.as_ref() {
118            Some(atom) => atom,
119            None => return Err(GetAtomError::NullPointer),
120        };
121        atom.get_body(urids)
122            .map_err(|err| GetAtomError::GetBody(err))
123    }
124}