support arrays of any size, don't require an initialization value, ..

single producer single consumer support for ring buffer
This commit is contained in:
Jorge Aparicio 2017-10-03 16:04:27 +02:00
parent f515ed9ead
commit 7e91814cf8
6 changed files with 681 additions and 154 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
**/*.rs.bk
.#*
Cargo.lock
target/

View File

@ -1,12 +1,19 @@
[package]
authors = ["Jorge Aparicio <jorge@japaric.io>"]
categories = ["data-structures", "no-std"]
categories = [
"data-structures",
"no-std",
]
description = "`static` friendly data structures that don't require dynamic memory allocation"
documentation = "https://docs.rs/heapless"
keywords = ["static", "no-heap"]
keywords = [
"static",
"no-heap",
]
license = "MIT OR Apache-2.0"
name = "heapless"
repository = "https://github.com/japaric/heapless"
version = "0.1.0"
version = "0.2.0"
[dependencies]
untagged-option = "0.1.1"

View File

@ -1,159 +1,21 @@
//! `static` friendly data structures that don't require dynamic memory
//! allocation
#![deny(missing_docs)]
#![deny(warnings)]
#![feature(const_fn)]
#![feature(shared)]
#![feature(unsize)]
#![no_std]
use core::marker::PhantomData;
use core::ops::Deref;
use core::slice;
extern crate untagged_option;
/// A circular buffer
pub struct CircularBuffer<T, A>
where
A: AsMut<[T]> + AsRef<[T]>,
T: Copy,
{
_marker: PhantomData<[T]>,
array: A,
index: usize,
len: usize,
}
impl<T, A> CircularBuffer<T, A>
where
A: AsMut<[T]> + AsRef<[T]>,
T: Copy,
{
/// Creates a new empty circular buffer using `array` as backup storage
pub const fn new(array: A) -> Self {
CircularBuffer {
_marker: PhantomData,
array: array,
index: 0,
len: 0,
}
}
/// Returns the capacity of this buffer
pub fn capacity(&self) -> usize {
self.array.as_ref().len()
}
/// Pushes `elem`ent into the buffer
///
/// This will overwrite an old value if the buffer is full
pub fn push(&mut self, elem: T) {
let slice = self.array.as_mut();
if self.len < slice.len() {
self.len += 1;
}
unsafe { *slice.as_mut_ptr().offset(self.index as isize) = elem };
self.index = (self.index + 1) % slice.len();
}
}
impl<T, A> Deref for CircularBuffer<T, A>
where
A: AsMut<[T]> + AsRef<[T]>,
T: Copy,
{
type Target = [T];
fn deref(&self) -> &[T] {
let slice = self.array.as_ref();
if self.len == slice.len() {
slice
} else {
unsafe { slice::from_raw_parts(slice.as_ptr(), self.len) }
}
}
}
/// A continuous, growable array type
pub struct Vec<T, A>
where
A: AsMut<[T]> + AsRef<[T]>,
T: Copy,
{
_marker: PhantomData<[T]>,
array: A,
len: usize,
}
impl<T, A> Vec<T, A>
where
A: AsMut<[T]> + AsRef<[T]>,
T: Copy,
{
/// Creates a new vector using `array` as the backup storage
pub const fn new(array: A) -> Self {
Vec {
_marker: PhantomData,
array: array,
len: 0,
}
}
/// Returns the capacity of this vector
pub fn capacity(&self) -> usize {
self.array.as_ref().len()
}
/// Clears the vector, removing all values
pub fn clear(&mut self) {
self.len = 0;
}
/// Removes the last element from this vector and returns it, or `None` if
/// it's empty
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
unsafe {
Some(
*self.array
.as_mut()
.as_mut_ptr()
.offset(self.len as isize),
)
}
}
}
/// Appends an `elem`ent to the back of the collection
///
/// This method returns `Err` if the vector is full
pub fn push(&mut self, elem: T) -> Result<(), ()> {
let slice = self.array.as_mut();
if self.len == slice.len() {
Err(())
} else {
unsafe {
*slice.as_mut_ptr().offset(self.len as isize) = elem;
}
self.len += 1;
Ok(())
}
}
}
impl<T, A> Deref for Vec<T, A>
where
A: AsMut<[T]> + AsRef<[T]>,
T: Copy,
{
type Target = [T];
fn deref(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.array.as_ref().as_ptr(), self.len) }
}
pub use vec::Vec;
pub use ring_buffer::RingBuffer;
pub mod ring_buffer;
mod vec;
/// Error
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Error {
Full,
}

316
src/ring_buffer/mod.rs Normal file
View File

@ -0,0 +1,316 @@
use core::marker::{PhantomData, Unsize};
use core::ptr;
use untagged_option::UntaggedOption;
use Error;
pub use self::spsc::{Consumer, Producer};
mod spsc;
/// An statically allocated ring buffer backed by an array with type `A`
pub struct RingBuffer<T, A>
where
// FIXME(rust-lang/rust#44580) use "const generics" instead of `Unsize`
A: Unsize<[T]>,
{
_marker: PhantomData<[T]>,
buffer: UntaggedOption<A>,
// this is from where we dequeue items
head: usize,
// this is where we enqueue new items
tail: usize,
}
impl<T, A> RingBuffer<T, A>
where
A: Unsize<[T]>,
{
/// Creates an empty ring buffer with capacity equals to the length of the array `A` *minus
/// one*.
pub const fn new() -> Self {
RingBuffer {
_marker: PhantomData,
buffer: UntaggedOption::none(),
head: 0,
tail: 0,
}
}
pub fn capacity(&self) -> usize {
let buffer: &[T] = unsafe { self.buffer.as_ref() };
buffer.len() - 1
}
pub fn dequeue(&mut self) -> Option<T> {
let n = self.capacity() + 1;
let buffer: &[T] = unsafe { self.buffer.as_ref() };
if self.head != self.tail {
let item = unsafe { ptr::read(&buffer[self.head]) };
self.head = (self.head + 1) % n;
Some(item)
} else {
None
}
}
pub fn enqueue(&mut self, item: T) -> Result<(), Error> {
let n = self.capacity() + 1;
let buffer: &mut [T] = unsafe { self.buffer.as_mut() };
let next_tail = (self.tail + 1) % n;
if next_tail != self.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
unsafe { ptr::write(&mut buffer[self.tail], item) }
self.tail = next_tail;
Ok(())
} else {
Err(Error::Full)
}
}
pub fn len(&self) -> usize {
if self.head > self.tail {
self.head - self.tail
} else {
self.tail - self.head
}
}
/// Iterates from the front of the queue to the back
pub fn iter(&self) -> Iter<T, A> {
Iter {
rb: self,
index: 0,
len: self.len(),
}
}
/// Mutable version of `iter`
pub fn iter_mut(&mut self) -> IterMut<T, A> {
let len = self.len();
IterMut {
rb: self,
index: 0,
len,
}
}
}
impl<T, A> Drop for RingBuffer<T, A>
where
A: Unsize<[T]>,
{
fn drop(&mut self) {
for item in self {
unsafe {
ptr::drop_in_place(item);
}
}
}
}
impl<'a, T, A> IntoIterator for &'a RingBuffer<T, A>
where
A: Unsize<[T]>,
{
type Item = &'a T;
type IntoIter = Iter<'a, T, A>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T, A> IntoIterator for &'a mut RingBuffer<T, A>
where
A: Unsize<[T]>,
{
type Item = &'a mut T;
type IntoIter = IterMut<'a, T, A>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
pub struct Iter<'a, T, A>
where
A: Unsize<[T]> + 'a,
T: 'a,
{
rb: &'a RingBuffer<T, A>,
index: usize,
len: usize,
}
pub struct IterMut<'a, T, A>
where
A: Unsize<[T]> + 'a,
T: 'a,
{
rb: &'a mut RingBuffer<T, A>,
index: usize,
len: usize,
}
impl<'a, T, A> Iterator for Iter<'a, T, A>
where
A: Unsize<[T]> + 'a,
T: 'a,
{
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
if self.index < self.len {
let buffer: &[T] = unsafe { self.rb.buffer.as_ref() };
let ptr = buffer.as_ptr();
let i = (self.rb.head + self.index) % (self.rb.capacity() + 1);
self.index += 1;
Some(unsafe { &*ptr.offset(i as isize) })
} else {
None
}
}
}
impl<'a, T, A> Iterator for IterMut<'a, T, A>
where
A: Unsize<[T]> + 'a,
T: 'a,
{
type Item = &'a mut T;
fn next(&mut self) -> Option<&'a mut T> {
if self.index < self.len {
let capacity = self.rb.capacity() + 1;
let buffer: &mut [T] = unsafe { self.rb.buffer.as_mut() };
let ptr: *mut T = buffer.as_mut_ptr();
let i = (self.rb.head + self.index) % capacity;
self.index += 1;
Some(unsafe { &mut *ptr.offset(i as isize) })
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use RingBuffer;
#[test]
fn drop() {
struct Droppable;
impl Droppable {
fn new() -> Self {
unsafe {
COUNT += 1;
}
Droppable
}
}
impl Drop for Droppable {
fn drop(&mut self) {
unsafe {
COUNT -= 1;
}
}
}
static mut COUNT: i32 = 0;
{
let mut v: RingBuffer<Droppable, [Droppable; 4]> = RingBuffer::new();
v.enqueue(Droppable::new()).unwrap();
v.enqueue(Droppable::new()).unwrap();
v.dequeue().unwrap();
}
assert_eq!(unsafe { COUNT }, 0);
{
let mut v: RingBuffer<Droppable, [Droppable; 4]> = RingBuffer::new();
v.enqueue(Droppable::new()).unwrap();
v.enqueue(Droppable::new()).unwrap();
}
assert_eq!(unsafe { COUNT }, 0);
}
#[test]
fn full() {
let mut rb: RingBuffer<i32, [i32; 4]> = RingBuffer::new();
rb.enqueue(0).unwrap();
rb.enqueue(1).unwrap();
rb.enqueue(2).unwrap();
assert!(rb.enqueue(3).is_err());
}
#[test]
fn iter() {
let mut rb: RingBuffer<i32, [i32; 4]> = RingBuffer::new();
rb.enqueue(0).unwrap();
rb.enqueue(1).unwrap();
rb.enqueue(2).unwrap();
let mut items = rb.iter();
assert_eq!(items.next(), Some(&0));
assert_eq!(items.next(), Some(&1));
assert_eq!(items.next(), Some(&2));
assert_eq!(items.next(), None);
}
#[test]
fn iter_mut() {
let mut rb: RingBuffer<i32, [i32; 4]> = RingBuffer::new();
rb.enqueue(0).unwrap();
rb.enqueue(1).unwrap();
rb.enqueue(2).unwrap();
let mut items = rb.iter_mut();
assert_eq!(items.next(), Some(&mut 0));
assert_eq!(items.next(), Some(&mut 1));
assert_eq!(items.next(), Some(&mut 2));
assert_eq!(items.next(), None);
}
#[test]
fn sanity() {
let mut rb: RingBuffer<i32, [i32; 4]> = RingBuffer::new();
assert_eq!(rb.dequeue(), None);
rb.enqueue(0).unwrap();
assert_eq!(rb.dequeue(), Some(0));
assert_eq!(rb.dequeue(), None);
}
#[test]
fn wrap_around() {
let mut rb: RingBuffer<i32, [i32; 4]> = RingBuffer::new();
rb.enqueue(0).unwrap();
rb.enqueue(1).unwrap();
rb.enqueue(2).unwrap();
rb.dequeue().unwrap();
rb.dequeue().unwrap();
rb.dequeue().unwrap();
rb.enqueue(3).unwrap();
rb.enqueue(4).unwrap();
assert_eq!(rb.len(), 2);
}
}

106
src/ring_buffer/spsc.rs Normal file
View File

@ -0,0 +1,106 @@
use core::ptr::{self, Shared};
use core::marker::Unsize;
use Error;
use ring_buffer::RingBuffer;
impl<T, A> RingBuffer<T, A>
where
A: Unsize<[T]>,
{
/// Splits a statically allocated ring buffer into producer and consumer end points
pub fn split(&'static mut self) -> (Producer<T, A>, Consumer<T, A>) {
(
Producer {
rb: unsafe { Shared::new_unchecked(self) },
},
Consumer {
rb: unsafe { Shared::new_unchecked(self) },
},
)
}
}
/// A ring buffer "consumer"; it can dequeue items from the ring buffer
// NOTE the consumer semantically owns the `head` pointer of the ring buffer
pub struct Consumer<T, A>
where
A: Unsize<[T]>,
{
// XXX do we need to use `Shared` (for soundness) here?
rb: Shared<RingBuffer<T, A>>,
}
impl<T, A> Consumer<T, A>
where
A: Unsize<[T]>,
{
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
if rb.head != unsafe { ptr::read_volatile(&rb.tail) } {
let item = unsafe { ptr::read(&buffer[rb.head]) };
rb.head = (rb.head + 1) % n;
Some(item)
} else {
None
}
}
}
/// A ring buffer "producer"; it can enqueue items into the ring buffer
// NOTE the producer semantically owns the `tail` pointer of the ring buffer
pub struct Producer<T, A>
where
A: Unsize<[T]>,
{
// XXX do we need to use `Shared` (for soundness) here?
rb: Shared<RingBuffer<T, A>>,
}
impl<T, A> Producer<T, A>
where
A: Unsize<[T]>,
{
pub fn enqueue(&mut self, item: T) -> Result<(), Error> {
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
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
unsafe { ptr::write(&mut buffer[rb.tail], item) }
rb.tail = next_tail;
Ok(())
} else {
Err(Error::Full)
}
}
}
#[cfg(test)]
mod tests {
use RingBuffer;
#[test]
fn sanity() {
static mut RB: RingBuffer<i32, [i32; 2]> = RingBuffer::new();
let (mut p, mut c) = unsafe { RB.split() };
assert_eq!(c.dequeue(), None);
p.enqueue(0).unwrap();
assert_eq!(c.dequeue(), Some(0));
}
}

235
src/vec.rs Normal file
View File

@ -0,0 +1,235 @@
use core::marker::{PhantomData, Unsize};
use core::{ops, ptr, slice};
use untagged_option::UntaggedOption;
use Error;
/// [`Vec`] backed by a fixed size array
///
/// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
pub struct Vec<T, A>
where
// FIXME(rust-lang/rust#44580) use "const generics" instead of `Unsize`
A: Unsize<[T]>,
{
_marker: PhantomData<[T]>,
buffer: UntaggedOption<A>,
len: usize,
}
impl<T, A> Vec<T, A>
where
A: Unsize<[T]>,
{
pub const fn new() -> Self {
Vec {
_marker: PhantomData,
buffer: UntaggedOption::none(),
len: 0,
}
}
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()
}
pub fn pop(&mut self) -> Option<T> {
let buffer: &[T] = unsafe { self.buffer.as_ref() };
if self.len != 0 {
self.len -= 1;
let item = unsafe { ptr::read(&buffer[self.len]) };
Some(item)
} else {
None
}
}
pub fn push(&mut self, item: T) -> Result<(), Error> {
let capacity = self.capacity();
let buffer: &mut [T] = unsafe { self.buffer.as_mut() };
if self.len < capacity {
// 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
unsafe { ptr::write(&mut buffer[self.len], item) }
self.len += 1;
Ok(())
} else {
Err(Error::Full)
}
}
}
impl<T, A> Drop for Vec<T, A>
where
A: Unsize<[T]>,
{
fn drop(&mut self) {
unsafe { ptr::drop_in_place(&mut self[..]) }
}
}
impl<'a, T, A> IntoIterator for &'a Vec<T, A>
where
A: Unsize<[T]>,
{
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T, A> IntoIterator for &'a mut Vec<T, A>
where
A: Unsize<[T]>,
{
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<T, A> ops::Deref for Vec<T, A>
where
A: Unsize<[T]>,
{
type Target = [T];
fn deref(&self) -> &[T] {
let buffer: &[T] = unsafe { self.buffer.as_ref() };
&buffer[..self.len]
}
}
impl<T, A> ops::DerefMut for Vec<T, A>
where
A: Unsize<[T]>,
{
fn deref_mut(&mut self) -> &mut [T] {
let len = self.len();
let buffer: &mut [T] = unsafe { self.buffer.as_mut() };
&mut buffer[..len]
}
}
#[cfg(test)]
mod tests {
use Vec;
#[test]
fn drop() {
struct Droppable;
impl Droppable {
fn new() -> Self {
unsafe {
COUNT += 1;
}
Droppable
}
}
impl Drop for Droppable {
fn drop(&mut self) {
unsafe {
COUNT -= 1;
}
}
}
static mut COUNT: i32 = 0;
{
let mut v: Vec<Droppable, [Droppable; 2]> = Vec::new();
v.push(Droppable::new()).unwrap();
v.push(Droppable::new()).unwrap();
v.pop().unwrap();
}
assert_eq!(unsafe { COUNT }, 0);
{
let mut v: Vec<Droppable, [Droppable; 2]> = Vec::new();
v.push(Droppable::new()).unwrap();
v.push(Droppable::new()).unwrap();
}
assert_eq!(unsafe { COUNT }, 0);
}
#[test]
fn full() {
let mut v: Vec<i32, [i32; 4]> = Vec::new();
v.push(0).unwrap();
v.push(1).unwrap();
v.push(2).unwrap();
v.push(3).unwrap();
assert!(v.push(4).is_err());
}
#[test]
fn iter() {
let mut v: Vec<i32, [i32; 4]> = Vec::new();
v.push(0).unwrap();
v.push(1).unwrap();
v.push(2).unwrap();
v.push(3).unwrap();
let mut items = v.iter();
assert_eq!(items.next(), Some(&0));
assert_eq!(items.next(), Some(&1));
assert_eq!(items.next(), Some(&2));
assert_eq!(items.next(), Some(&3));
assert_eq!(items.next(), None);
}
#[test]
fn iter_mut() {
let mut v: Vec<i32, [i32; 4]> = Vec::new();
v.push(0).unwrap();
v.push(1).unwrap();
v.push(2).unwrap();
v.push(3).unwrap();
let mut items = v.iter_mut();
assert_eq!(items.next(), Some(&mut 0));
assert_eq!(items.next(), Some(&mut 1));
assert_eq!(items.next(), Some(&mut 2));
assert_eq!(items.next(), Some(&mut 3));
assert_eq!(items.next(), None);
}
#[test]
fn sanity() {
let mut v: Vec<i32, [i32; 4]> = Vec::new();
assert_eq!(v.pop(), None);
v.push(0).unwrap();
assert_eq!(v.pop(), Some(0));
assert_eq!(v.pop(), None);
}
}