lv2rs_atom/
frame.rs

1//! Raw writing access to chunks of memory.
2//!
3//! This module contains the basic handles for writing atoms to a chunk of memory. This is done
4//! using a framing approach: Every atom is managed by a [`WritingFrame`](trait.WritingFrame.html)
5//! which is able to write data into memory and update the size noted in the atom header.
6//! Nested atoms can also be created by every writing frame using the
7//! [`create_nested_frame`](trait.WritingFrameExt.html#method.create_nested_frame) method.
8//! These nested frames additionally account for padding when dropped.
9use crate::atom::*;
10use std::marker::PhantomData;
11use std::mem::size_of;
12
13/// Basic functionality of a writing frame.
14///
15/// A writing frame manages an atom header and is able to append raw data to the atom. Additional
16/// methods are out-sourced to the [`WritingFrameExt`](trait.WritingFrameExt.html) trait since it
17/// contains generic methods and therefore can not be turned into trait objects. But don't worry,
18/// every type that implements the `WritingFrame` trait automatically implements the
19/// `WritingFrameExt` too.
20pub trait WritingFrame<'a> {
21    /// Try to write out a slice of bytes into the atom space.
22    ///
23    /// The data will be written directly after the previously written data and no padding will be
24    /// applied.
25    ///
26    /// If writing was successfull, a slice with the written data is returned. In case
27    /// of insufficient atom space, this function will return an error.
28    ///
29    /// Also, this function is unsafe since it does not check the resulting atom for consistency.
30    /// You have to know what you are doing!
31    unsafe fn write_raw(&mut self, data: &[u8]) -> Result<&'a mut [u8], ()>;
32
33    /// Return an immutable reference to the managed atom header.
34    fn get_atom(&self) -> &Atom;
35}
36
37/// Extended functionality for writing frames.
38///
39/// This extension trait makes [`WritingFrame`s](trait.WritingFrame.html) really usefull. First of
40/// all, this trait is generic for the managed atom type. This means that one can implement
41/// additional functions for writing frames that manage a specific type of atom body.
42///
43/// Also, you can write sized objects to the atom using the [`write_sized`](#method.write_sized)
44/// method without needing to turn a reference to a `u8` slice. Last but not least, it can create
45/// new, nested writing frames using [`create_nested_frame`](#method.create_nested_frame).
46pub trait WritingFrameExt<'a, A: AtomBody + ?Sized>: WritingFrame<'a> + Sized {
47    /// Try to write a sized object into the atom space.
48    ///
49    /// The data will be written directly after the previously written data and no padding will be
50    /// applied.
51    ///
52    /// If writing was successfull, a reference to the writen object is returned. In case
53    /// of insufficient atom space, this function will return an error.
54    ///
55    /// Also, this function is unsafe since it does not check the resulting structure for consistency.
56    /// You have to know what you are doing!
57    unsafe fn write_sized<T: Sized>(&mut self, object: &T) -> Result<&'a mut T, ()> {
58        let data: &[u8] =
59            std::slice::from_raw_parts(object as *const T as *const u8, size_of::<T>());
60        match self.write_raw(data) {
61            Ok(data) => Ok((data.as_mut_ptr() as *mut T).as_mut().unwrap()),
62            Err(_) => Err(()),
63        }
64    }
65
66    /// Create a new atom header and return a nested writing frame for it.
67    ///
68    /// This function can be used for container atoms. Please note that this function only writes
69    /// the atom header and does not initialize the atom body.
70    ///
71    /// Also, this function is unsafe since one can mess up atom structures.
72    unsafe fn create_nested_frame<'b, C: AtomBody + ?Sized>(
73        &'b mut self,
74        urids: &mut urid::CachedMap,
75    ) -> Result<NestedFrame<'b, 'a, C>, ()> {
76        let writer = NestedFrame {
77            atom: Atom::write_empty_header(self, urids.map(C::get_uri()))?,
78            parent: self,
79            phantom: PhantomData,
80        };
81
82        Ok(writer)
83    }
84
85    /// Try to get a reference to the body from our atom header.
86    ///
87    /// This is just a shortcut for `A::widen_ref(frame.get_header(), urids)`.
88    unsafe fn get_atom_body<'b>(
89        &'b self,
90        urids: &mut urid::CachedMap,
91    ) -> Result<&'b A, GetBodyError> {
92        self.get_atom().get_body(urids)
93    }
94}
95
96/// The ground level writing frame.
97///
98/// This writing frame manages the first atom and actually has access to the data. It is created by
99/// the [`AtomOutputPort`](../ports/struct.AtomOutputPort.html) and simply acts like any
100/// [`WritingFrame`](trait.WritingFrame.html).
101pub struct RootFrame<'a, A: AtomBody + ?Sized> {
102    atom: &'a mut Atom,
103    free_data: &'a mut [u8],
104    phantom: PhantomData<A>,
105}
106
107impl<'a, A: AtomBody + ?Sized> RootFrame<'a, A> {
108    /// Try to create a new root frame.
109    ///
110    /// All you need to create a root frame is a slice of writable memory and a way to retrieve
111    /// the URID of the managed atom. Then, this function will initialize the header in the
112    /// beginning of the slice and create the frame.
113    ///
114    /// If the slice is not big enough to hold the atom header, this function returns an `Err`.
115    pub fn new(free_space: &'a mut [u8], urids: &mut urid::CachedMap) -> Result<Self, ()> {
116        let atom_size = std::mem::size_of::<Atom>();
117        if free_space.len() < atom_size {
118            return Err(());
119        }
120
121        let atom_ptr = free_space.as_mut_ptr() as *mut Atom;
122        let atom = unsafe { atom_ptr.as_mut() }.unwrap();
123        let data = unsafe {
124            std::slice::from_raw_parts_mut(atom_ptr.add(1) as *mut u8, free_space.len() - atom_size)
125        };
126
127        *(atom.mut_atom_type()) = urids.map(A::get_uri());
128        *(atom.mut_size()) = 0;
129        Ok(RootFrame {
130            atom: atom,
131            free_data: data,
132            phantom: PhantomData,
133        })
134    }
135}
136
137impl<'a, A: AtomBody + ?Sized> WritingFrame<'a> for RootFrame<'a, A> {
138    unsafe fn write_raw(&mut self, data: &[u8]) -> Result<&'a mut [u8], ()> {
139        if data.len() > self.free_data.len() {
140            return Err(());
141        }
142
143        let data_ptr = self.free_data.as_mut_ptr();
144        let n_free_bytes = self.free_data.len() - data.len();
145
146        let target_data = std::slice::from_raw_parts_mut(data_ptr, data.len());
147        let free_data = std::slice::from_raw_parts_mut(data_ptr.add(data.len()), n_free_bytes);
148
149        target_data.copy_from_slice(data);
150        self.free_data = free_data;
151        *(self.atom.mut_size()) += data.len() as i32;
152
153        // Construct a reference to the newly written atom.
154        Ok(target_data)
155    }
156
157    fn get_atom(&self) -> &Atom {
158        self.atom
159    }
160}
161
162impl<'a, A: AtomBody + ?Sized> WritingFrameExt<'a, A> for RootFrame<'a, A> {}
163
164/// A writing frame managing nested atoms.
165///
166/// Unlike the [`RootFrame`](struct.RootFrame.html), which really manages memory, this frame only
167/// forwards writing calls to the next frame in the hierarchy and updates the atom header
168/// accordingly. Additionally, this frame will assure for padding when dropped. These padding bytes
169/// will not be included in the top level header, only in the surrounding ones.
170///
171/// Nested frames can only be created with the
172/// [`create_nested_frame`](trait.WritingFrameExt.html#method.create_nested_frame) method.
173pub struct NestedFrame<'a, 'b, A>
174where
175    A: AtomBody + ?Sized,
176{
177    atom: &'b mut Atom,
178    parent: &'a mut WritingFrame<'b>,
179    phantom: PhantomData<A>,
180}
181
182impl<'a, 'b, A> Drop for NestedFrame<'a, 'b, A>
183where
184    A: AtomBody + ?Sized,
185{
186    fn drop(&mut self) {
187        let pad: &[u8] = match 8 - (self.parent.get_atom().size() % 8) {
188            1 => &[0; 1],
189            2 => &[0; 2],
190            3 => &[0; 3],
191            4 => &[0; 4],
192            5 => &[0; 5],
193            6 => &[0; 6],
194            7 => &[0; 7],
195            8 => &[0; 0],
196            _ => panic!("invalid pad size"),
197        };
198        unsafe { self.parent.write_raw(pad).unwrap() };
199    }
200}
201
202impl<'a, 'b, A> WritingFrame<'b> for NestedFrame<'a, 'b, A>
203where
204    A: AtomBody + ?Sized,
205{
206    unsafe fn write_raw(&mut self, data: &[u8]) -> Result<&'b mut [u8], ()> {
207        let data = self.parent.write_raw(data)?;
208        *(self.atom.mut_size()) += data.len() as i32;
209        Ok(data)
210    }
211
212    fn get_atom(&self) -> &Atom {
213        self.atom
214    }
215}
216
217impl<'a, 'b, A: AtomBody + ?Sized> WritingFrameExt<'b, A> for NestedFrame<'a, 'b, A> {}