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> {}