1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Wrappers for raw atom IO.
//!
//! The wrappers provided by this module increase the safety and usability of atom IO.
use crate::atom::*;
use crate::frame::RootFrame;
use std::marker::PhantomData;
use std::ptr::{null, null_mut};

/// Wrapper for atom writing operations.
pub struct AtomOutputPort<A: AtomBody + ?Sized> {
    atom: *mut Atom,
    phantom: PhantomData<A>,
}

/// Errors that may occur when calling
/// [`AtomOutputPort::write_atom_body`](struct.AtomOuputPort.html#method.write_atom_body)
#[derive(Debug)]
pub enum WriteAtomError {
    /// The internal pointer points to zero.
    ///
    /// Maybe `connect_port` is not implemented correctly?
    NullPointer,
    /// The host hasn't allocated enough memory to initialize the atom.
    InsufficientSpace,
}

#[derive(Debug)]
/// Error that may occur when calling [`AtomInputPort::get_atom_body`](struct.AtomInputPort.html#method.get_atom_body).
pub enum GetAtomError {
    /// The internal pointer points to zero.
    ///
    /// Maybe `connect_port` is not implemented correctly?
    NullPointer,
    /// Widening the atom header failed.
    GetBody(GetBodyError),
}

impl<A: AtomBody + ?Sized> AtomOutputPort<A> {
    /// Create a new port.
    ///
    /// Please note that the newly created port wil point to null and therefore,
    /// [`write_atom_body`](#method.write_atom_body) will yield undefined behaviour.
    pub fn new() -> Self {
        Self {
            atom: null_mut(),
            phantom: PhantomData,
        }
    }

    /// Set the internal atom pointer.
    ///
    /// As implied by the name, this method should be called by an atom's `connect_port`. However,
    /// you have to cast the passed pointer to the correct type!
    pub fn connect_port(&mut self, atom: *mut Atom) {
        self.atom = atom;
    }

    /// Write an atom to the internal atom pointer.
    ///
    /// This method will create a [`RootFrame`](../frame/struct.RootFrame.html) and initialize the
    /// body. For [scalar atoms](../scalar/index.html), this is all you can and need to
    /// do. For all other atoms, you can write additional data using the `RootFrame`.
    ///
    /// This method is unsafe since it dereferences the raw, internal pointer and therefore could
    /// yield undefined behaviour. Make sure that your plugin's `connect_port` method calls this
    /// port's [`connect_port`](#method.connect_port) method correctly!
    pub unsafe fn write_atom_body<'a>(
        &'a mut self,
        parameter: &A::InitializationParameter,
        urids: &mut urid::CachedMap,
    ) -> Result<RootFrame<'a, A>, WriteAtomError> {
        let header = match self.atom.as_mut() {
            Some(header) => header,
            None => return Err(WriteAtomError::NullPointer),
        };
        let data = std::slice::from_raw_parts_mut(self.atom as *mut u8, header.size() as usize);
        let mut frame =
            RootFrame::new(data, urids).map_err(|_| WriteAtomError::InsufficientSpace)?;
        A::initialize_body(&mut frame, parameter, urids)
            .map_err(|_| WriteAtomError::InsufficientSpace)?;
        Ok(frame)
    }
}

/// Wrapper for atom reading operations.
pub struct AtomInputPort<A: AtomBody + ?Sized> {
    atom: *const Atom,
    phantom: PhantomData<A>,
}

impl<A: AtomBody + ?Sized> AtomInputPort<A> {
    /// Create a new port.
    ///
    /// Please note that the newly created port wil point to null and therefore,
    /// [`write_atom_body`](#method.write_atom_body) will yield undefined behaviour.
    pub fn new() -> Self {
        Self {
            atom: null(),
            phantom: PhantomData,
        }
    }

    /// Set the internal atom pointer.
    ///
    /// As implied by the name, this method should be called by an atom's `connect_port`. However,
    /// you have to cast the passed pointer to the correct type!
    pub fn connect_port(&mut self, atom: *const Atom) {
        self.atom = atom;
    }

    /// Dereference the internal raw pointer to an atom body reference.
    ///
    /// This method is unsafe since it dereferences the raw, internal pointer and therefore could
    /// yield undefined behaviour. Make sure that your plugin's `connect_port` method calls this
    /// port's [`connect_port`](#method.connect_port) method correctly!
    pub unsafe fn get_atom_body(&self, urids: &mut urid::CachedMap) -> Result<&A, GetAtomError> {
        let atom = match self.atom.as_ref() {
            Some(atom) => atom,
            None => return Err(GetAtomError::NullPointer),
        };
        atom.get_body(urids)
            .map_err(|err| GetAtomError::GetBody(err))
    }
}