lv2rs_atom/atom.rs
1//! Fundamental type definitions.
2use crate::frame::{WritingFrame, WritingFrameExt};
3use std::ffi::CStr;
4use std::marker::PhantomData;
5use std::os::raw::c_int;
6use urid::URID;
7
8/// Marker or header of an atom data structure.
9///
10/// This type is used to interpret data coming from the host or other plugins. It is always written
11/// in the beginning of a data packet and denotes the size of the packet and it's type.
12#[repr(C)]
13pub struct Atom {
14 size: c_int,
15 atom_type: URID,
16}
17
18impl Atom {
19 /// Return the size of the body.
20 pub fn size(&self) -> usize {
21 self.size as usize
22 }
23
24 /// Return a mutable reference to the body size.
25 pub fn mut_size(&mut self) -> &mut i32 {
26 &mut self.size
27 }
28
29 /// Return the type of the body.
30 pub fn atom_type(&self) -> URID {
31 self.atom_type
32 }
33
34 // Return a mutable reference to the atom body type.
35 pub fn mut_atom_type(&mut self) -> &mut URID {
36 &mut self.atom_type
37 }
38
39 /// Write an empty header to a writing frame.
40 ///
41 /// This function is for internal use and you should not use it externally. Since it does not
42 /// check what's around it, this function may invalidate the written atom structure. This is
43 /// also the reason why it's unsafe.
44 pub unsafe fn write_empty_header<
45 'a,
46 W: WritingFrame<'a> + WritingFrameExt<'a, A>,
47 A: AtomBody + ?Sized,
48 >(
49 frame: &mut W,
50 atom_type: URID,
51 ) -> Result<&'a mut Self, ()> {
52 let atom = Atom {
53 size: 0,
54 atom_type: atom_type,
55 };
56 frame.write_sized(&atom)
57 }
58
59 /// Return a slice of bytes containing the data.
60 ///
61 /// The returned slice will start directly after the atom and will have the size noted in the
62 /// header.
63 pub fn get_raw_body(&self) -> &[u8] {
64 unsafe {
65 std::slice::from_raw_parts(
66 (self as *const Atom).add(1) as *const u8,
67 self.size as usize,
68 )
69 }
70 }
71
72 /// Try the return a reference to the body.
73 ///
74 /// This function fails if a) the type URID in the atom does not match with A's URID or b)
75 /// the internal casting function tells that the data is malformed.
76 pub fn get_body<A: AtomBody + ?Sized>(
77 &self,
78 urids: &mut urid::CachedMap,
79 ) -> Result<&A, GetBodyError> {
80 if self.atom_type != urids.map(A::get_uri()) {
81 return Err(GetBodyError::WrongURID);
82 }
83 let raw_body = self.get_raw_body();
84 A::create_ref(raw_body).map_err(|_| GetBodyError::MalformedAtom)
85 }
86}
87
88/// Errors that may occur when calling [`Atom::get_body`](trait.Atom.html#method.get_body).
89#[derive(Debug)]
90pub enum GetBodyError {
91 /// The URID noted in the atom header is wrong.
92 ///
93 /// Maybe you tried to use the wrong atom type?
94 WrongURID,
95 /// The atom is malformed.
96 ///
97 /// You can't do much about it; This is another plugin's fault.
98 MalformedAtom,
99}
100
101/// Abstraction of atom bodies.
102///
103/// Atom bodies can be very different in size and shape and therefore, this trait contains only a
104/// small set of things atoms are capable of.
105///
106/// ## Implementing your own atom bodies.
107///
108/// First of all, you shouldn't. The set of included atom bodies is enough to express almost any
109/// information. On the other hand, if you really need to implement a new atom body, just implement
110/// this trait. This will give you most of the features you will need. However, this trait only
111/// lets you initialize a body; It does not give you means to extend it afterwards. If you want to
112/// do that, you should create an extension trait for [`WritingFrame`s](../frame/trait.WritingFrame.html),
113/// just like the [`TupleWritingFrame`](../tuple/trait.TupleWritingFrame.html).
114pub trait AtomBody {
115 /// The type of the parameter for [`initialize_body`](#tymethod.initialize_body)
116 ///
117 /// Since Rust does not support generic associated types yet, you can not use references here.
118 /// However, since `initialize_body` will receive a reference of this type, you can place
119 /// unsized types in here, like slices.
120 type InitializationParameter: ?Sized;
121
122 /// Return the URI of the atom type.
123 fn get_uri() -> &'static CStr;
124
125 /// Write out a basic but valid atom body.
126 ///
127 /// Implementors should use the writing frame to write out general information about the atom,
128 /// like body-specific headers or, in the case of scalars, the value itself. Please note that
129 /// * The [`Atom`](struct.Atom.html) was already written, you do not need to write
130 /// it yourself.
131 /// * You cannot alter the data after it was written. Once this method call is over, you only have
132 /// reading access to it by using the
133 /// [`get_atom_body`](../frame/trait.WritingFrameExt.html#method.get_atom_body) method of the
134 /// writing frame.
135 /// * The result must be a valid atom. You may not rely on future calls to make it valid.
136 /// * In most cases, you don't need to include padding. If padding is required, the writer will
137 /// include it when it is dropped.
138 /// * You do not need (and definitely should not try) to update the atom header for the new
139 /// size. The writer will keep track of that.
140 /// * Your implementation should work in a way that it can only return `Err` in cases
141 /// of insufficient memory.
142 ///
143 /// This method is unsafe since it can tamper if the integrity of the atom structure, for example
144 /// if called twice.
145 unsafe fn initialize_body<'a, W>(
146 writer: &mut W,
147 parameter: &Self::InitializationParameter,
148 urids: &mut urid::CachedMap,
149 ) -> Result<(), ()>
150 where
151 W: WritingFrame<'a> + WritingFrameExt<'a, Self>;
152
153 /// Try to create a `Self` reference from a slice of raw data.
154 ///
155 /// When implementing, you have to check if the data makes up a valid object of your type. If
156 /// this is not the case, return an `Err`.
157 fn create_ref<'a>(raw_body: &'a [u8]) -> Result<&'a Self, ()>;
158}
159
160/// Iterator over atoms.
161///
162/// This iterator takes a slice of bytes and tries to iterate over all atoms in this slice. If
163/// there is an error while iterating, iteration will end.
164pub struct AtomIterator<'a, H: 'static + Sized> {
165 data: &'a [u8],
166 position: usize,
167 phantom: PhantomData<H>,
168}
169
170impl<'a, H: 'static + Sized> AtomIterator<'a, H> {
171 /// Create a new atom iterator.
172 pub fn new(data: &'a [u8]) -> Self {
173 AtomIterator {
174 data: data,
175 position: 0,
176 phantom: PhantomData,
177 }
178 }
179}
180
181impl<'a, H: 'static + Sized> Iterator for AtomIterator<'a, H> {
182 type Item = (&'a H, &'a Atom);
183
184 fn next(&mut self) -> Option<(&'a H, &'a Atom)> {
185 use std::mem::size_of;
186
187 // pad to the next 64-bit aligned position, if nescessary.
188 if self.position % 8 != 0 {
189 self.position += 8 - self.position % 8;
190 }
191 if self.position >= self.data.len() {
192 return None;
193 }
194
195 let data = &self.data[self.position..];
196 if data.len() < size_of::<H>() + size_of::<Atom>() {
197 return None;
198 }
199
200 let pre_header_ptr = data.as_ptr() as *const H;
201 let pre_header = unsafe { pre_header_ptr.as_ref() }?;
202 let atom_ptr = unsafe { pre_header_ptr.add(1) } as *const Atom;
203 let atom = unsafe { atom_ptr.as_ref() }?;
204
205 // Apply the package of pre-header, atom and data to our position in the array.
206 self.position += size_of::<H>() + size_of::<Atom>() + atom.size as usize;
207
208 if self.position <= self.data.len() {
209 Some((pre_header, atom))
210 } else {
211 None
212 }
213 }
214}
215
216#[cfg(test)]
217mod test {
218 use crate::atom::*;
219
220 #[test]
221 fn test_chunk_iterator() {
222 struct TestPrefix {
223 value: u64,
224 }
225
226 // ##################
227 // creating the data.
228 // ##################
229 let mut data = Box::new([0u8; 256]);
230 let ptr = data.as_mut().as_mut_ptr();
231
232 // First prefix.
233 let mut ptr = ptr as *mut TestPrefix;
234 unsafe {
235 let mut_ref = ptr.as_mut().unwrap();
236 mut_ref.value = 650000;
237 // No padding needed, TestPrefix is eight bytes long.
238 ptr = ptr.add(1);
239 }
240
241 // First atom. We will fit a u8 after it, because it requires seven padding bytes, which
242 // is an important edge case.
243 let mut ptr = ptr as *mut Atom;
244 unsafe {
245 let mut_ref = ptr.as_mut().unwrap();
246 mut_ref.atom_type = 42;
247 mut_ref.size = 1;
248 ptr = ptr.add(1);
249 }
250 let mut ptr = ptr as *mut u8;
251 unsafe {
252 let mut_ref = ptr.as_mut().unwrap();
253 *mut_ref = 17;
254 ptr = ptr.add(1);
255 }
256
257 // Padding and second prefix.
258 let mut ptr = unsafe { ptr.add(7) } as *mut TestPrefix;
259 unsafe {
260 let mut_ref = ptr.as_mut().unwrap();
261 mut_ref.value = 4711;
262 // No padding needed, TestPrefix is eight bytes long.
263 ptr = ptr.add(1);
264 }
265
266 // Second atom.
267 let mut ptr = ptr as *mut Atom;
268 unsafe {
269 let mut_ref = ptr.as_mut().unwrap();
270 mut_ref.atom_type = 10;
271 mut_ref.size = 1;
272 ptr = ptr.add(1);
273 }
274 let ptr = ptr as *mut u8;
275 unsafe {
276 let mut_ref = ptr.as_mut().unwrap();
277 *mut_ref = 4;
278 }
279
280 // #####################
281 // Testing the iterator.
282 // #####################
283 let mut iter: AtomIterator<TestPrefix> = AtomIterator::new(data.as_ref());
284
285 // First atom
286 let (prefix, atom) = iter.next().unwrap();
287 assert_eq!(650000, prefix.value);
288 assert_eq!(42, atom.atom_type);
289 assert_eq!(1, atom.size);
290 assert_eq!(17, atom.get_raw_body()[0]);
291
292 // Second atom.
293 let (prefix, atom) = iter.next().unwrap();
294 assert_eq!(4711, prefix.value);
295 assert_eq!(10, atom.atom_type);
296 assert_eq!(1, atom.size);
297 assert_eq!(4, atom.get_raw_body()[0]);
298 }
299}
300
301/// Special templates for dynamically sized atoms.
302pub mod array {
303 use crate::atom::*;
304 use crate::frame::{WritingFrame, WritingFrameExt};
305 use std::mem::{size_of, transmute};
306
307 /// A header of an `ArrayAtomBody`.
308 ///
309 /// Many atoms have an additional body header and this trait represents said headers.
310 pub trait ArrayAtomHeader: Sized {
311 /// Type of the parameter for [`initialize`](#tymethod.initialize).
312 type InitializationParameter: ?Sized;
313
314 /// Write out the array atom header.
315 ///
316 /// The same rules from
317 /// [`AtomBody::initialize_body`](../trait.AtomBody.html#tymethod.initialize_body) apply.
318 unsafe fn initialize<'a, W, T>(
319 writer: &mut W,
320 parameter: &Self::InitializationParameter,
321 urids: &mut urid::CachedMap,
322 ) -> Result<(), ()>
323 where
324 T: 'static + Sized + Copy,
325 ArrayAtomBody<Self, T>: AtomBody,
326 W: WritingFrame<'a> + WritingFrameExt<'a, ArrayAtomBody<Self, T>>;
327 }
328
329 impl ArrayAtomHeader for () {
330 type InitializationParameter = ();
331
332 unsafe fn initialize<'a, W, T>(_: &mut W, _: &(), _: &mut urid::CachedMap) -> Result<(), ()>
333 where
334 T: 'static + Sized + Copy,
335 ArrayAtomBody<Self, T>: AtomBody,
336 W: WritingFrame<'a> + WritingFrameExt<'a, ArrayAtomBody<Self, T>>,
337 {
338 Ok(())
339 }
340 }
341
342 /// Abstract type for dynamically sized atom bodies.
343 ///
344 /// Many dynamically sized atoms bodies have a lot of their behaviour and raw representation in
345 /// common. Therefore, they are abstracted to this struct that contains a header and an array of
346 /// sized items.
347 ///
348 /// If you don't want to have a header, you can use `()` instead.
349 ///
350 /// Not all combinations of header and data items are atom bodies, but many methods rely on
351 /// the combination being an atom body.
352 #[repr(C)]
353 pub struct ArrayAtomBody<H, T>
354 where
355 H: ArrayAtomHeader,
356 T: 'static + Sized + Copy,
357 {
358 pub header: H,
359 pub data: [T],
360 }
361
362 impl<H, T> ArrayAtomBody<H, T>
363 where
364 Self: AtomBody,
365 H: ArrayAtomHeader,
366 T: 'static + Sized + Copy,
367 {
368 /// Internal method to initialize the body.
369 ///
370 /// It simply calls the initialization method of the header, the data array will be left
371 /// empty.
372 pub unsafe fn __initialize_body<'a, W>(
373 writer: &mut W,
374 parameter: &H::InitializationParameter,
375 urids: &mut urid::CachedMap,
376 ) -> Result<(), ()>
377 where
378 W: WritingFrame<'a> + WritingFrameExt<'a, Self>,
379 {
380 H::initialize(writer, parameter, urids)
381 }
382
383 /// Internal method to create an atom body reference.
384 pub fn __create_ref<'a>(raw_data: &'a [u8]) -> Result<&'a Self, ()> {
385 let array_header_size = size_of::<H>();
386 if raw_data.len() < array_header_size {
387 return Err(());
388 }
389
390 let tail_size = raw_data.len() - size_of::<H>();
391 // The size of the tail has to be a multiple of the contained type.
392 if tail_size % size_of::<T>() != 0 {
393 return Err(());
394 }
395 let tail_len = tail_size / size_of::<T>();
396
397 // This is were the unsafe things happen!
398 // We know the length of the string, therefore we can create a fat pointer to the atom.
399 let self_ptr: (*const u8, usize) = (raw_data.as_ptr(), tail_len);
400 let self_ref: &Self = unsafe { transmute(self_ptr) };
401
402 Ok(self_ref)
403 }
404
405 /// Push another value to the data array.
406 ///
407 /// In case of insufficient memory, an `Err` is returned.
408 ///
409 /// This method assumes that the atom was already initialized, but since can't be checked,
410 /// this method is unsafe.
411 pub unsafe fn push<'a, W>(writer: &mut W, value: T) -> Result<(), ()>
412 where
413 W: WritingFrame<'a> + WritingFrameExt<'a, Self>,
414 {
415 writer.write_sized(&value)?;
416 Ok(())
417 }
418
419 /// Append a `T` slice to the data.
420 ///
421 /// In case of insufficient memory, an `Err` is returned.
422 ///
423 /// This method assumes that the atom was already initialized, but since can't be checked,
424 /// this method is unsafe.
425 pub unsafe fn append<'a, W>(writer: &mut W, slice: &[T]) -> Result<(), ()>
426 where
427 W: WritingFrame<'a> + WritingFrameExt<'a, Self>,
428 {
429 let data = std::slice::from_raw_parts(
430 slice.as_ptr() as *const u8,
431 std::mem::size_of_val(slice),
432 );
433 writer.write_raw(data)?;
434 Ok(())
435 }
436 }
437}