diff --git a/src/lib.rs b/src/lib.rs index bd5264fc8fad3cc81deec2c4234e1804657ec605..4c4abf2337a8a28ac5242d4ceae8d490dcecfcdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! `static` friendly data structures that don't require dynamic memory //! allocation +#![deny(missing_docs)] #![feature(const_fn)] #![feature(shared)] #![feature(unsize)] @@ -14,8 +15,6 @@ pub use ring_buffer::RingBuffer; pub mod ring_buffer; mod vec; -/// Error +/// Error raised when the buffer is full #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Error { - Full, -} +pub struct BufferFullError; diff --git a/src/ring_buffer/mod.rs b/src/ring_buffer/mod.rs index 5647f77bdcfa789279ccfee4529ce68d8515a7a0..0b4e0ff6e1b0143a532226448fffc8b3111e7318 100644 --- a/src/ring_buffer/mod.rs +++ b/src/ring_buffer/mod.rs @@ -1,15 +1,17 @@ +//! Ring buffer + use core::marker::{PhantomData, Unsize}; use core::ptr; use untagged_option::UntaggedOption; -use Error; +use BufferFullError; pub use self::spsc::{Consumer, Producer}; mod spsc; -/// An statically allocated ring buffer backed by an array with type `A` +/// An statically allocated ring buffer backed by an array `A` pub struct RingBuffer<T, A> where // FIXME(rust-lang/rust#44580) use "const generics" instead of `Unsize` @@ -38,11 +40,13 @@ where } } + /// Returns the maximum number of elements the ring buffer can hold pub fn capacity(&self) -> usize { let buffer: &[T] = unsafe { self.buffer.as_ref() }; buffer.len() - 1 } + /// Returns the item in the front of the queue, or `None` if the queue is empty pub fn dequeue(&mut self) -> Option<T> { let n = self.capacity() + 1; let buffer: &[T] = unsafe { self.buffer.as_ref() }; @@ -56,7 +60,10 @@ where } } - pub fn enqueue(&mut self, item: T) -> Result<(), Error> { + /// Adds an `item` to the end of the queue + /// + /// Returns `BufferFullError` if the queue is full + pub fn enqueue(&mut self, item: T) -> Result<(), BufferFullError> { let n = self.capacity() + 1; let buffer: &mut [T] = unsafe { self.buffer.as_mut() }; @@ -68,10 +75,11 @@ where self.tail = next_tail; Ok(()) } else { - Err(Error::Full) + Err(BufferFullError) } } + /// Returns the number of elements in the queue pub fn len(&self) -> usize { if self.head > self.tail { self.head - self.tail @@ -89,7 +97,7 @@ where } } - /// Mutable version of `iter` + /// Returns an iterator that allows modifying each value. pub fn iter_mut(&mut self) -> IterMut<T, A> { let len = self.len(); IterMut { @@ -137,6 +145,7 @@ where } } +/// An iterator over a ring buffer items pub struct Iter<'a, T, A> where A: Unsize<[T]> + 'a, @@ -147,6 +156,7 @@ where len: usize, } +/// A mutable iterator over a ring buffer items pub struct IterMut<'a, T, A> where A: Unsize<[T]> + 'a, diff --git a/src/ring_buffer/spsc.rs b/src/ring_buffer/spsc.rs index dc6d0f53479888d4fe2cbb28b65a3bd809e2d747..1258cce495c047f13d6cad53a1a6393a720848af 100644 --- a/src/ring_buffer/spsc.rs +++ b/src/ring_buffer/spsc.rs @@ -1,7 +1,7 @@ use core::ptr::{self, Shared}; use core::marker::Unsize; -use Error; +use BufferFullError; use ring_buffer::RingBuffer; impl<T, A> RingBuffer<T, A> @@ -9,6 +9,9 @@ where A: Unsize<[T]>, { /// Splits a statically allocated ring buffer into producer and consumer end points + /// + /// *Warning* the current implementation only supports single core processors. It's also fine to + /// use both end points on the same core of a multi-core processor. pub fn split(&'static mut self) -> (Producer<T, A>, Consumer<T, A>) { ( Producer { @@ -35,13 +38,14 @@ impl<T, A> Consumer<T, A> where A: Unsize<[T]>, { + /// Returns the item in the front of the queue, or `None` if the queue is empty pub fn dequeue(&mut self) -> Option<T> { let rb = unsafe { self.rb.as_mut() }; let n = rb.capacity() + 1; let buffer: &[T] = unsafe { rb.buffer.as_ref() }; - // NOTE(volatile) the value of `tail` can change at any time in the context of the consumer - // so we inform this to the compiler using a volatile load + // NOTE(volatile) the value of `tail` can change at any time in the execution context of the + // consumer so we inform this to the compiler using a volatile load if rb.head != unsafe { ptr::read_volatile(&rb.tail) } { let item = unsafe { ptr::read(buffer.as_ptr().offset(rb.head as isize)) }; rb.head = (rb.head + 1) % n; @@ -67,14 +71,17 @@ impl<T, A> Producer<T, A> where A: Unsize<[T]>, { - pub fn enqueue(&mut self, item: T) -> Result<(), Error> { + /// Adds an `item` to the end of the queue + /// + /// Returns `BufferFullError` if the queue is full + pub fn enqueue(&mut self, item: T) -> Result<(), BufferFullError> { let rb = unsafe { self.rb.as_mut() }; let n = rb.capacity() + 1; let buffer: &mut [T] = unsafe { rb.buffer.as_mut() }; let next_tail = (rb.tail + 1) % n; - // NOTE(volatile) the value of `head` can change at any time in the context of the producer - // so we inform this to the compiler using a volatile load + // NOTE(volatile) the value of `head` can change at any time in the execution context of the + // producer so we inform this to the compiler using a volatile load if next_tail != unsafe { ptr::read_volatile(&rb.head) } { // NOTE(ptr::write) the memory slot that we are about to write to is uninitialized. We // use `ptr::write` to avoid running `T`'s destructor on the uninitialized memory @@ -82,7 +89,7 @@ where rb.tail = next_tail; Ok(()) } else { - Err(Error::Full) + Err(BufferFullError) } } } diff --git a/src/vec.rs b/src/vec.rs index ae9b70b1ea143345a574d954e37fb70ef48eb85b..9140cde1d14caa18682e83de5a84e5a94d468465 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -3,9 +3,9 @@ use core::{ops, ptr, slice}; use untagged_option::UntaggedOption; -use Error; +use BufferFullError; -/// [`Vec`] backed by a fixed size array +/// A [`Vec`], *vector*, backed by a fixed size array /// /// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html pub struct Vec<T, A> @@ -22,6 +22,7 @@ impl<T, A> Vec<T, A> where A: Unsize<[T]>, { + /// Constructs a new, empty `Vec<T>` backed by the array `A` pub const fn new() -> Self { Vec { _marker: PhantomData, @@ -30,19 +31,13 @@ where } } + /// Returns the maximum number of elements the vector can hold pub fn capacity(&self) -> usize { let buffer: &[T] = unsafe { self.buffer.as_ref() }; buffer.len() } - pub fn iter(&self) -> slice::Iter<T> { - (**self).iter() - } - - pub fn iter_mut(&mut self) -> slice::IterMut<T> { - (**self).iter_mut() - } - + /// Removes the last element from a vector and return it, or `None` if it's empty pub fn pop(&mut self) -> Option<T> { let buffer: &[T] = unsafe { self.buffer.as_ref() }; @@ -55,7 +50,10 @@ where } } - pub fn push(&mut self, item: T) -> Result<(), Error> { + /// Appends an element to the back of the collection + /// + /// Returns `BufferFullError` if the vector is full + pub fn push(&mut self, item: T) -> Result<(), BufferFullError> { let capacity = self.capacity(); let buffer: &mut [T] = unsafe { self.buffer.as_mut() }; @@ -66,7 +64,7 @@ where self.len += 1; Ok(()) } else { - Err(Error::Full) + Err(BufferFullError) } } } @@ -152,7 +150,6 @@ mod tests { static mut COUNT: i32 = 0; - { let mut v: Vec<Droppable, [Droppable; 2]> = Vec::new(); v.push(Droppable::new()).unwrap(); @@ -231,5 +228,4 @@ mod tests { assert_eq!(v.pop(), None); } - }