From 79f62b2f5afb07f406fff24f4144f31af1d87b52 Mon Sep 17 00:00:00 2001 From: jonay2000 Date: Fri, 2 Jun 2023 17:59:21 +0200 Subject: [PATCH 1/6] growable ringbuffer --- Cargo.toml | 2 +- .../test_const_generic_array_zero_length.rs | 0 {tests => _tests}/compiletests.rs | 0 src/lib.rs | 202 ++++++- src/ringbuffer_trait.rs | 10 +- src/with_alloc.rs | 465 +---------------- src/with_alloc/alloc_ringbuffer.rs | 491 ++++++++++++++++++ src/with_alloc/vecdeque.rs | 195 +++++++ 8 files changed, 889 insertions(+), 476 deletions(-) rename {tests => _tests}/compile-fail/test_const_generic_array_zero_length.rs (100%) rename {tests => _tests}/compiletests.rs (100%) create mode 100644 src/with_alloc/alloc_ringbuffer.rs create mode 100644 src/with_alloc/vecdeque.rs diff --git a/Cargo.toml b/Cargo.toml index 92b45ac..672d94d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT" [dev-dependencies] criterion = "0.4.0" -compiletest_rs = "0.9.0" +#compiletest_rs = "0.10.0" [features] default = ["alloc"] diff --git a/tests/compile-fail/test_const_generic_array_zero_length.rs b/_tests/compile-fail/test_const_generic_array_zero_length.rs similarity index 100% rename from tests/compile-fail/test_const_generic_array_zero_length.rs rename to _tests/compile-fail/test_const_generic_array_zero_length.rs diff --git a/tests/compiletests.rs b/_tests/compiletests.rs similarity index 100% rename from tests/compiletests.rs rename to _tests/compiletests.rs diff --git a/src/lib.rs b/src/lib.rs index 1a4e2fe..354b63f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,8 @@ //! //! Licensed under the MIT License +extern crate alloc; + #[macro_use] pub(crate) mod ringbuffer_trait; use core::usize; @@ -68,9 +70,11 @@ pub use ringbuffer_trait::{RingBuffer, RingBufferExt, RingBufferRead, RingBuffer #[cfg(feature = "alloc")] mod with_alloc; #[cfg(feature = "alloc")] -pub use with_alloc::AllocRingBuffer; +pub use with_alloc::alloc_ringbuffer::AllocRingBuffer; +#[cfg(feature = "alloc")] +pub use with_alloc::alloc_ringbuffer::RINGBUFFER_DEFAULT_CAPACITY; #[cfg(feature = "alloc")] -pub use with_alloc::RINGBUFFER_DEFAULT_CAPACITY; +pub use with_alloc::vecdeque::GrowableAllocRingBuffer; mod with_const_generics; pub use with_const_generics::ConstGenericRingBuffer; @@ -97,7 +101,8 @@ mod tests { use std::vec::Vec; use crate::{ - AllocRingBuffer, ConstGenericRingBuffer, RingBuffer, RingBufferExt, RingBufferWrite, + AllocRingBuffer, ConstGenericRingBuffer, GrowableAllocRingBuffer, RingBuffer, + RingBufferExt, RingBufferRead, RingBufferWrite, }; #[test] @@ -113,6 +118,7 @@ mod tests { } test_neg_index(AllocRingBuffer::with_capacity(capacity)); + test_neg_index(GrowableAllocRingBuffer::with_capacity(capacity)); test_neg_index(ConstGenericRingBuffer::::new()); } @@ -124,6 +130,7 @@ mod tests { } test_default(AllocRingBuffer::with_capacity(8)); + test_default(GrowableAllocRingBuffer::with_capacity(8)); test_default(ConstGenericRingBuffer::::new()); } @@ -135,11 +142,16 @@ mod tests { } test_new(AllocRingBuffer::with_capacity(8)); + test_new(GrowableAllocRingBuffer::with_capacity(8)); test_new(ConstGenericRingBuffer::::new()); } #[test] fn test_default_eq_new() { + assert_eq!( + GrowableAllocRingBuffer::::default(), + GrowableAllocRingBuffer::::new() + ); assert_eq!( AllocRingBuffer::::default(), AllocRingBuffer::::new() @@ -161,6 +173,7 @@ mod tests { } test_len(AllocRingBuffer::with_capacity(8)); + test_len(GrowableAllocRingBuffer::with_capacity(8)); test_len(ConstGenericRingBuffer::::new()); } @@ -181,6 +194,16 @@ mod tests { test_len_wrap(AllocRingBuffer::with_capacity(2)); test_len_wrap(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer actually should grow instead of wrap + let mut grb = GrowableAllocRingBuffer::with_capacity(2); + assert_eq!(0, grb.len()); + grb.push(0); + assert_eq!(1, grb.len()); + grb.push(1); + assert_eq!(2, grb.len()); + grb.push(2); + assert_eq!(3, grb.len()); } #[test] @@ -196,6 +219,7 @@ mod tests { } test_clear(AllocRingBuffer::with_capacity(8)); + test_clear(GrowableAllocRingBuffer::with_capacity(8)); test_clear(ConstGenericRingBuffer::::new()); } @@ -214,6 +238,7 @@ mod tests { } test_empty(AllocRingBuffer::with_capacity(8)); + test_empty(GrowableAllocRingBuffer::with_capacity(8)); test_empty(ConstGenericRingBuffer::::new()); } @@ -240,6 +265,7 @@ mod tests { } test_iter(AllocRingBuffer::with_capacity(8)); + test_iter(GrowableAllocRingBuffer::with_capacity(8)); test_iter(ConstGenericRingBuffer::::new()); } @@ -262,6 +288,7 @@ mod tests { let string = "abc".to_string(); test_iter(&string, AllocRingBuffer::with_capacity(8)); + test_iter(&string, GrowableAllocRingBuffer::with_capacity(8)); test_iter(&string, ConstGenericRingBuffer::<&str, 8>::new()); } @@ -284,6 +311,7 @@ mod tests { } test_double_iter(AllocRingBuffer::with_capacity(8)); + test_double_iter(GrowableAllocRingBuffer::with_capacity(8)); test_double_iter(ConstGenericRingBuffer::::new()); } @@ -302,6 +330,20 @@ mod tests { test_iter_wrap(AllocRingBuffer::with_capacity(2)); test_iter_wrap(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer shouldn't actually stop growing + let mut b = GrowableAllocRingBuffer::with_capacity(2); + + b.push(1); + b.push(2); + // No wrap + b.push(3); + + let mut iter = b.iter(); + assert_eq!(&1, iter.next().unwrap()); + assert_eq!(&2, iter.next().unwrap()); + assert_eq!(&3, iter.next().unwrap()); + assert!(iter.next().is_none()); } #[test] @@ -319,6 +361,7 @@ mod tests { } test_iter_mut(AllocRingBuffer::with_capacity(8)); + test_iter_mut(GrowableAllocRingBuffer::with_capacity(8)); test_iter_mut(ConstGenericRingBuffer::::new()); } @@ -338,6 +381,19 @@ mod tests { run_test_iter_mut_wrap(AllocRingBuffer::with_capacity(2)); run_test_iter_mut_wrap(ConstGenericRingBuffer::::new()); + + // The growable ringbuffer actually shouldn't wrap + let mut b = GrowableAllocRingBuffer::with_capacity(2); + + b.push(1); + b.push(2); + b.push(3); + + for i in b.iter_mut() { + *i += 1; + } + + assert_eq!(vec![2, 3, 4], b.to_vec()) } #[test] @@ -358,6 +414,20 @@ mod tests { run_test_iter_mut_wrap(AllocRingBuffer::with_capacity(2)); run_test_iter_mut_wrap(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer actually shouldn't wrap + let mut b = GrowableAllocRingBuffer::with_capacity(2); + b.push(1); + b.push(2); + b.push(3); + + let buf = b.iter_mut().collect::>(); + + for i in buf { + *i += 1; + } + + assert_eq!(vec![2, 3, 4], b.to_vec()) } #[test] @@ -371,6 +441,7 @@ mod tests { } test_to_vec(AllocRingBuffer::with_capacity(8)); + test_to_vec(GrowableAllocRingBuffer::with_capacity(8)); test_to_vec(ConstGenericRingBuffer::::new()); } @@ -387,6 +458,15 @@ mod tests { test_to_vec_wrap(AllocRingBuffer::with_capacity(2)); test_to_vec_wrap(ConstGenericRingBuffer::::new()); + + // The growable ringbuffer should actually remember all items + let mut b = GrowableAllocRingBuffer::with_capacity(2); + + b.push(1); + b.push(2); + b.push(3); + + assert_eq!(vec![1, 2, 3], b.to_vec()) } #[test] @@ -397,6 +477,7 @@ mod tests { } test_index(AllocRingBuffer::with_capacity(8)); + test_index(GrowableAllocRingBuffer::with_capacity(8)); test_index(ConstGenericRingBuffer::::new()); } @@ -413,6 +494,7 @@ mod tests { } test_index_mut(AllocRingBuffer::with_capacity(8)); + test_index_mut(GrowableAllocRingBuffer::with_capacity(8)); test_index_mut(ConstGenericRingBuffer::::new()); } @@ -426,6 +508,7 @@ mod tests { } test_peek_some(AllocRingBuffer::with_capacity(2)); + test_peek_some(GrowableAllocRingBuffer::with_capacity(2)); test_peek_some(ConstGenericRingBuffer::::new()); } @@ -436,6 +519,7 @@ mod tests { } test_peek_none(AllocRingBuffer::with_capacity(8)); + test_peek_none(GrowableAllocRingBuffer::with_capacity(8)); test_peek_none(ConstGenericRingBuffer::::new()); } @@ -460,6 +544,7 @@ mod tests { } test_get_relative(AllocRingBuffer::with_capacity(8)); + test_get_relative(GrowableAllocRingBuffer::with_capacity(8)); test_get_relative(ConstGenericRingBuffer::::new()); } @@ -484,6 +569,16 @@ mod tests { test_wrapping_get_relative(AllocRingBuffer::with_capacity(2)); test_wrapping_get_relative(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer actually shouldn't wrap + let mut b = GrowableAllocRingBuffer::with_capacity(2); + b.push(0); + b.push(1); + b.push(2); + + assert_eq!(b.get(0).unwrap(), &0); + assert_eq!(b.get(1).unwrap(), &1); + assert_eq!(b.get(2).unwrap(), &2); } #[test] @@ -493,6 +588,7 @@ mod tests { } test_get_relative_zero_length(AllocRingBuffer::with_capacity(8)); + test_get_relative_zero_length(GrowableAllocRingBuffer::with_capacity(8)); test_get_relative_zero_length(ConstGenericRingBuffer::::new()); } @@ -516,6 +612,7 @@ mod tests { } test_get_relative_mut(AllocRingBuffer::with_capacity(8)); + test_get_relative_mut(GrowableAllocRingBuffer::with_capacity(8)); test_get_relative_mut(ConstGenericRingBuffer::::new()); } @@ -542,6 +639,19 @@ mod tests { test_wrapping_get_relative_mut(AllocRingBuffer::with_capacity(2)); test_wrapping_get_relative_mut(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer actually shouldn't wrap + let mut b = GrowableAllocRingBuffer::with_capacity(2); + + b.push(0); + b.push(1); + b.push(2); + + *b.get_mut(0).unwrap() = 3; + + assert_eq!(b.get(0).unwrap(), &3); + assert_eq!(b.get(1).unwrap(), &1); + assert_eq!(b.get(2).unwrap(), &2); } #[test] @@ -551,10 +661,12 @@ mod tests { } test_get_relative_mut_zero_length(AllocRingBuffer::with_capacity(8)); + test_get_relative_mut_zero_length(GrowableAllocRingBuffer::with_capacity(8)); test_get_relative_mut_zero_length(ConstGenericRingBuffer::::new()); } #[test] + #[allow(deprecated)] fn run_test_get_absolute() { fn test_get_absolute(mut b: impl RingBufferExt) { b.push(0); @@ -584,6 +696,7 @@ mod tests { } test_from_iterator::>(); + test_from_iterator::>(); test_from_iterator::>(); } @@ -596,6 +709,7 @@ mod tests { } test_from_iterator_wrap::>(); + test_from_iterator_wrap::>(); test_from_iterator_wrap::>(); } @@ -634,6 +748,7 @@ mod tests { } test_contains(AllocRingBuffer::with_capacity(8)); + test_contains(GrowableAllocRingBuffer::with_capacity(8)); test_contains(ConstGenericRingBuffer::::new()); } @@ -648,6 +763,7 @@ mod tests { } test_is_full(AllocRingBuffer::with_capacity(2)); + test_is_full(GrowableAllocRingBuffer::with_capacity(2)); test_is_full(ConstGenericRingBuffer::::new()); } @@ -661,6 +777,7 @@ mod tests { } test_front_some(AllocRingBuffer::with_capacity(2)); + test_front_some(GrowableAllocRingBuffer::with_capacity(2)); test_front_some(ConstGenericRingBuffer::::new()); } @@ -670,8 +787,9 @@ mod tests { assert_eq!(b.front(), None); } - test_front_none(AllocRingBuffer::with_capacity(8)); - test_front_none(ConstGenericRingBuffer::::new()); + // test_front_none(AllocRingBuffer::with_capacity(8)); + test_front_none(GrowableAllocRingBuffer::with_capacity(8)); + // test_front_none(ConstGenericRingBuffer::::new()); } #[test] @@ -684,6 +802,7 @@ mod tests { } test_back_some(AllocRingBuffer::with_capacity(2)); + test_back_some(GrowableAllocRingBuffer::with_capacity(2)); test_back_some(ConstGenericRingBuffer::::new()); } @@ -694,6 +813,7 @@ mod tests { } test_back_none(AllocRingBuffer::with_capacity(8)); + test_back_none(GrowableAllocRingBuffer::with_capacity(8)); test_back_none(ConstGenericRingBuffer::::new()); } @@ -707,6 +827,7 @@ mod tests { } test_front_some_mut(AllocRingBuffer::with_capacity(2)); + test_front_some_mut(GrowableAllocRingBuffer::with_capacity(2)); test_front_some_mut(ConstGenericRingBuffer::::new()); } @@ -717,6 +838,7 @@ mod tests { } test_front_none_mut(AllocRingBuffer::with_capacity(8)); + test_front_none_mut(GrowableAllocRingBuffer::with_capacity(8)); test_front_none_mut(ConstGenericRingBuffer::::new()); } @@ -730,7 +852,7 @@ mod tests { } test_back_some_mut(AllocRingBuffer::with_capacity(2)); - + test_back_some_mut(GrowableAllocRingBuffer::with_capacity(2)); test_back_some_mut(ConstGenericRingBuffer::::new()); } @@ -741,7 +863,7 @@ mod tests { } test_back_none_mut(AllocRingBuffer::with_capacity(8)); - + test_back_none_mut(GrowableAllocRingBuffer::with_capacity(8)); test_back_none_mut(ConstGenericRingBuffer::::new()); } @@ -762,6 +884,7 @@ mod tests { } run_test_dequeue(AllocRingBuffer::with_capacity(8)); + run_test_dequeue(GrowableAllocRingBuffer::with_capacity(8)); run_test_dequeue(ConstGenericRingBuffer::::new()); } @@ -780,7 +903,7 @@ mod tests { } test_skip(AllocRingBuffer::with_capacity(8)); - + test_skip(GrowableAllocRingBuffer::with_capacity(8)); test_skip(ConstGenericRingBuffer::::new()); } @@ -798,6 +921,7 @@ mod tests { } test_skip2(AllocRingBuffer::with_capacity(2)); + test_skip2(GrowableAllocRingBuffer::with_capacity(2)); test_skip2(ConstGenericRingBuffer::::new()); } @@ -820,6 +944,7 @@ mod tests { } test_push_dequeue_push(AllocRingBuffer::with_capacity(8)); + test_push_dequeue_push(GrowableAllocRingBuffer::with_capacity(8)); test_push_dequeue_push(ConstGenericRingBuffer::::new()); } @@ -842,6 +967,7 @@ mod tests { } test_enqueue_dequeue_push(AllocRingBuffer::with_capacity(8)); + test_enqueue_dequeue_push(GrowableAllocRingBuffer::with_capacity(8)); test_enqueue_dequeue_push(ConstGenericRingBuffer::::new()); } @@ -867,6 +993,26 @@ mod tests { test_push_dequeue_push_full(AllocRingBuffer::with_capacity(2)); test_push_dequeue_push_full(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer should actually keep growing and dequeue all items + let mut b = GrowableAllocRingBuffer::with_capacity(2); + b.push(0); + b.push(1); + b.push(2); + + assert_eq!(b.dequeue(), Some(0)); + assert_eq!(b.dequeue(), Some(1)); + assert_eq!(b.dequeue(), Some(2)); + assert_eq!(b.dequeue(), None); + + b.push(0); + b.push(1); + b.push(2); + + assert_eq!(b.dequeue(), Some(0)); + assert_eq!(b.dequeue(), Some(1)); + assert_eq!(b.dequeue(), Some(2)); + assert_eq!(b.dequeue(), None); } #[test] @@ -899,6 +1045,35 @@ mod tests { test_push_dequeue_push_full_get(AllocRingBuffer::with_capacity(2)); test_push_dequeue_push_full_get(ConstGenericRingBuffer::::new()); + + // the growable ringbuffer should actually keep growing and dequeue all items + let mut b = GrowableAllocRingBuffer::with_capacity(2); + + b.push(0); + b.push(1); + b.push(2); + + assert_eq!(b.dequeue(), Some(0)); + assert_eq!(b.dequeue(), Some(1)); + assert_eq!(b.dequeue(), Some(2)); + assert_eq!(b.dequeue(), None); + + b.push(0); + b.push(1); + b.push(2); + + assert_eq!(b.dequeue(), Some(0)); + assert_eq!(b.dequeue(), Some(1)); + assert_eq!(b.dequeue(), Some(2)); + assert_eq!(b.dequeue(), None); + + b.push(0); + b.push(1); + b.push(2); + + assert_eq!(b.get(-1), Some(&2)); + assert_eq!(b.get(-2), Some(&1)); + assert_eq!(b.get(-3), Some(&0)) } #[test] @@ -928,6 +1103,7 @@ mod tests { } test_push_dequeue_push_full_get_rep(AllocRingBuffer::with_capacity(8)); + test_push_dequeue_push_full_get_rep(GrowableAllocRingBuffer::with_capacity(8)); test_push_dequeue_push_full_get_rep(ConstGenericRingBuffer::::new()); } @@ -951,6 +1127,7 @@ mod tests { } test_clone(AllocRingBuffer::with_capacity(4)); + test_clone(GrowableAllocRingBuffer::with_capacity(4)); test_clone(ConstGenericRingBuffer::::new()); } @@ -975,6 +1152,7 @@ mod tests { } test_default_fill(AllocRingBuffer::with_capacity(4)); + test_default_fill(GrowableAllocRingBuffer::with_capacity(4)); test_default_fill(ConstGenericRingBuffer::::new()); } @@ -1010,6 +1188,7 @@ mod tests { next_back_test(ConstGenericRingBuffer::::new()); next_back_test(AllocRingBuffer::with_capacity(8)); + next_back_test(GrowableAllocRingBuffer::with_capacity(8)); } #[test] @@ -1029,6 +1208,7 @@ mod tests { next_back_test_mut(ConstGenericRingBuffer::::new()); next_back_test_mut(AllocRingBuffer::with_capacity(8)); + next_back_test_mut(GrowableAllocRingBuffer::with_capacity(8)); } #[test] fn run_test_fill() { @@ -1051,6 +1231,7 @@ mod tests { } test_fill(AllocRingBuffer::with_capacity(4)); + test_fill(GrowableAllocRingBuffer::with_capacity(4)); test_fill(ConstGenericRingBuffer::::new()); } @@ -1111,5 +1292,10 @@ mod tests { fn run_test_drops_contents_const_generic() { test_dropped!({ ConstGenericRingBuffer::<_, 1>::new() }); } + + #[test] + fn run_test_drops_contents_growable_alloc() { + test_dropped!({ GrowableAllocRingBuffer::with_capacity(1) }); + } } } diff --git a/src/ringbuffer_trait.rs b/src/ringbuffer_trait.rs index 5ad2cfe..c012127 100644 --- a/src/ringbuffer_trait.rs +++ b/src/ringbuffer_trait.rs @@ -159,12 +159,14 @@ pub unsafe trait RingBufferExt: unsafe fn ptr_get_mut(rb: *mut Self, index: isize) -> Option<*mut T>; /// Gets a value relative to the start of the array (rarely useful, usually you want [`Self::get`]) + #[deprecated] fn get_absolute(&self, index: usize) -> Option<&T>; /// Gets a value mutably relative to the start of the array (rarely useful, usually you want [`Self::get_mut`]) + #[deprecated] fn get_absolute_mut(&mut self, index: usize) -> Option<&mut T>; - /// Returns the value at the current index. + /// Returns the value at the current index.t /// This is the value that will be overwritten by the next push and also the value pushed /// the longest ago. (alias of [`Self::front`]) #[inline] @@ -257,7 +259,7 @@ mod iter { obj, len: obj.len(), index: 0, - phantom: PhantomData::default(), + phantom: PhantomData, } } } @@ -316,7 +318,7 @@ mod iter { len: obj.len(), obj: NonNull::from(obj), index: 0, - phantom: PhantomData::default(), + phantom: PhantomData, } } } @@ -374,7 +376,7 @@ mod iter { pub fn new(obj: &'rb mut RB) -> Self { Self { obj, - phantom: PhantomData::default(), + phantom: PhantomData, } } } diff --git a/src/with_alloc.rs b/src/with_alloc.rs index b1b7e02..0ae59f2 100644 --- a/src/with_alloc.rs +++ b/src/with_alloc.rs @@ -1,463 +1,2 @@ -use core::ops::{Index, IndexMut}; - -use crate::ringbuffer_trait::{RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite}; - -extern crate alloc; -// We need vecs so depend on alloc -use alloc::vec::Vec; -use core::iter::FromIterator; -use core::marker::PhantomData; -use core::mem; -use core::mem::MaybeUninit; - -#[derive(Debug, Copy, Clone)] -pub struct PowerOfTwo; -#[derive(Debug, Copy, Clone)] -pub struct NonPowerOfTwo; -mod private { - use crate::with_alloc::{NonPowerOfTwo, PowerOfTwo}; - - pub trait Sealed {} - impl Sealed for PowerOfTwo {} - impl Sealed for NonPowerOfTwo {} -} -pub trait RingbufferMode: private::Sealed { - fn mask(cap: usize, index: usize) -> usize; - fn must_be_power_of_two() -> bool; -} -impl RingbufferMode for PowerOfTwo { - #[inline] - fn mask(cap: usize, index: usize) -> usize { - crate::mask(cap, index) - } - - fn must_be_power_of_two() -> bool { - true - } -} -impl RingbufferMode for NonPowerOfTwo { - #[inline] - fn mask(cap: usize, index: usize) -> usize { - crate::mask_modulo(cap, index) - } - - fn must_be_power_of_two() -> bool { - false - } -} - -/// The `AllocRingBuffer` is a `RingBufferExt` which is based on a Vec. This means it allocates at runtime -/// on the heap, and therefore needs the [`alloc`] crate. This struct and therefore the dependency on -/// alloc can be disabled by disabling the `alloc` (default) feature. -/// -/// # Example -/// ``` -/// use ringbuffer::{AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferWrite}; -/// -/// let mut buffer = AllocRingBuffer::with_capacity(2); -/// -/// // First entry of the buffer is now 5. -/// buffer.push(5); -/// -/// // The last item we pushed is 5 -/// assert_eq!(buffer.get(-1), Some(&5)); -/// -/// // Second entry is now 42. -/// buffer.push(42); -/// -/// assert_eq!(buffer.peek(), Some(&5)); -/// assert!(buffer.is_full()); -/// -/// // Because capacity is reached the next push will be the first item of the buffer. -/// buffer.push(1); -/// assert_eq!(buffer.to_vec(), vec![42, 1]); -/// ``` -#[derive(Debug)] -pub struct AllocRingBuffer { - buf: Vec>, - capacity: usize, - readptr: usize, - writeptr: usize, - mode: PhantomData, -} - -impl Drop for AllocRingBuffer { - fn drop(&mut self) { - self.drain().for_each(drop); - } -} - -impl Clone for AllocRingBuffer { - fn clone(&self) -> Self { - debug_assert_ne!(self.capacity, 0); - debug_assert!(!MODE::must_be_power_of_two() || self.capacity.is_power_of_two()); - - // whatever the previous capacity was, we can just use the same one again. - // It should be valid. - let mut new = unsafe { Self::with_capacity_unchecked(self.capacity) }; - self.iter().cloned().for_each(|i| new.push(i)); - new - } -} - -impl PartialEq for AllocRingBuffer { - fn eq(&self, other: &Self) -> bool { - self.capacity == other.capacity - && self.len() == other.len() - && self.iter().zip(other.iter()).all(|(a, b)| a == b) - } -} - -impl Eq for AllocRingBuffer {} - -/// The capacity of a `RingBuffer` created by new or default (`1024`). -// must be a power of 2 -pub const RINGBUFFER_DEFAULT_CAPACITY: usize = 1024; - -unsafe impl RingBufferExt for AllocRingBuffer { - impl_ringbuffer_ext!( - get_unchecked, - get_unchecked_mut, - readptr, - writeptr, - MODE::mask - ); - - #[inline] - fn fill_with T>(&mut self, mut f: F) { - self.clear(); - - self.readptr = 0; - self.writeptr = self.capacity; - self.buf.fill_with(|| MaybeUninit::new(f())); - while self.buf.len() < self.capacity { - self.buf.push(MaybeUninit::new(f())); - } - } -} - -impl RingBufferRead for AllocRingBuffer { - fn dequeue(&mut self) -> Option { - if self.is_empty() { - None - } else { - let index = MODE::mask(self.capacity, self.readptr); - let res = mem::replace(&mut self.buf[index], MaybeUninit::uninit()); - self.readptr += 1; - - // Safety: the fact that we got this maybeuninit from the buffer (with mask) means that - // it's initialized. If it wasn't the is_empty call would have caught it. Values - // are always initialized when inserted so this is safe. - unsafe { Some(res.assume_init()) } - } - } - - impl_ringbuffer_read!(); -} - -impl Extend for AllocRingBuffer { - fn extend>(&mut self, iter: A) { - let iter = iter.into_iter(); - - for i in iter { - self.push(i); - } - } -} - -impl RingBufferWrite for AllocRingBuffer { - #[inline] - fn push(&mut self, value: T) { - if self.is_full() { - let previous_value = mem::replace( - &mut self.buf[MODE::mask(self.capacity, self.readptr)], - MaybeUninit::uninit(), - ); - // make sure we drop whatever is being overwritten - // SAFETY: the buffer is full, so this must be initialized - // : also, index has been masked - // make sure we drop because it won't happen automatically - unsafe { - drop(previous_value.assume_init()); - } - - self.readptr += 1; - } - - let index = MODE::mask(self.capacity, self.writeptr); - - if index >= self.buf.len() { - // initializing the maybeuninit when values are inserted/pushed - self.buf.push(MaybeUninit::new(value)); - } else { - // initializing the maybeuninit when values are inserted/pushed - self.buf[index] = MaybeUninit::new(value); - } - - self.writeptr += 1; - } -} - -impl RingBuffer for AllocRingBuffer { - #[inline] - unsafe fn ptr_capacity(rb: *const Self) -> usize { - (*rb).capacity - } - - impl_ringbuffer!(readptr, writeptr); -} - -impl AllocRingBuffer { - /// Creates a `AllocRingBuffer` with a certain capacity. This capacity is fixed. - /// for this ringbuffer to work, cap must be a power of two and greater than zero. - /// - /// # Safety - /// Only safe if the capacity is greater than zero, and a power of two. - /// Only if Mode == NonPowerOfTwo can the capacity be not a power of two, in which case this function is also safe. - #[inline] - unsafe fn with_capacity_unchecked(cap: usize) -> Self { - Self { - buf: Vec::with_capacity(cap), - capacity: cap, - readptr: 0, - writeptr: 0, - mode: Default::default(), - } - } -} - -impl AllocRingBuffer { - /// Creates a `AllocRingBuffer` with a certain capacity. This capacity is fixed. - /// for this ringbuffer to work, and must not be zero. - /// - /// Note, that not using a power of two means some operations can't be optimized as well. - /// For example, bitwise ands might become modulos. - /// - /// For example, on push operations, benchmarks have shown that a ringbuffer with a power-of-two - /// capacity constructed with `with_capacity_non_power_of_two` (so which don't get the same optimization - /// as the ones constructed with `with_capacity`) can be up to 3x slower - /// - /// # Panics - /// if the capacity is zero - #[inline] - pub fn with_capacity_non_power_of_two(cap: usize) -> Self { - assert_ne!(cap, 0, "Capacity must be greater than 0"); - - // Safety: Mode is NonPowerOfTwo and we checked above that the capacity isn't zero - unsafe { Self::with_capacity_unchecked(cap) } - } -} - -impl AllocRingBuffer { - /// Creates a `AllocRingBuffer` with a certain capacity. The actual capacity is the input to the - /// function raised to the power of two (effectively the input is the log2 of the actual capacity) - #[inline] - pub fn with_capacity_power_of_2(cap_power_of_two: usize) -> Self { - // Safety: 1 << n is always a power of two, and nonzero - unsafe { Self::with_capacity_unchecked(1 << cap_power_of_two) } - } - - #[inline] - /// Creates a `AllocRingBuffer` with a certain capacity. The capacity must be a power of two. - /// # Panics - /// Panics when capacity is zero or not a power of two - pub fn with_capacity(cap: usize) -> Self { - assert_ne!(cap, 0, "Capacity must be greater than 0"); - assert!(cap.is_power_of_two(), "Capacity must be a power of two"); - - // Safety: assertions check that cap is a power of two and nonzero - unsafe { Self::with_capacity_unchecked(cap) } - } - - /// Creates an `AllocRingBuffer` with a capacity of [`RINGBUFFER_DEFAULT_CAPACITY`]. - #[inline] - pub fn new() -> Self { - Self::default() - } -} - -/// Get a reference from the buffer without checking it is initialized. -/// Caller must be sure the index is in bounds, or this will panic. -#[inline] -unsafe fn get_unchecked<'a, T, MODE: RingbufferMode>( - rb: *const AllocRingBuffer, - index: usize, -) -> &'a T { - let p = &(*rb).buf[index]; - // Safety: caller makes sure the index is in bounds for the ringbuffer. - // All in bounds values in the ringbuffer are initialized - p.assume_init_ref() -} - -/// Get a mut reference from the buffer without checking it is initialized. -/// Caller must be sure the index is in bounds, or this will panic. -#[inline] -unsafe fn get_unchecked_mut( - rb: *mut AllocRingBuffer, - index: usize, -) -> *mut T { - let p = (*rb).buf.as_mut_ptr().add(index); - - // Safety: caller makes sure the index is in bounds for the ringbuffer. - // All in bounds values in the ringbuffer are initialized - p.cast() -} - -impl FromIterator for AllocRingBuffer { - fn from_iter>(iter: T) -> Self { - let mut res = Self::default(); - for i in iter { - res.push(i); - } - - res - } -} - -impl Default for AllocRingBuffer { - /// Creates a buffer with a capacity of [`crate::RINGBUFFER_DEFAULT_CAPACITY`]. - #[inline] - fn default() -> Self { - Self { - buf: Vec::with_capacity(RINGBUFFER_DEFAULT_CAPACITY), - capacity: RINGBUFFER_DEFAULT_CAPACITY, - readptr: 0, - writeptr: 0, - mode: Default::default(), - } - } -} - -impl Index for AllocRingBuffer { - type Output = T; - - fn index(&self, index: isize) -> &Self::Output { - self.get(index).expect("index out of bounds") - } -} - -impl IndexMut for AllocRingBuffer { - fn index_mut(&mut self, index: isize) -> &mut Self::Output { - self.get_mut(index).expect("index out of bounds") - } -} - -#[cfg(test)] -mod tests { - use super::alloc::vec::Vec; - use crate::with_alloc::RingbufferMode; - use crate::{ - AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite, - RINGBUFFER_DEFAULT_CAPACITY, - }; - - // just test that this compiles - #[test] - fn test_generic_clone() { - fn helper( - a: &AllocRingBuffer, - ) -> AllocRingBuffer { - a.clone() - } - - _ = helper(&AllocRingBuffer::with_capacity(2)); - _ = helper(&AllocRingBuffer::with_capacity_non_power_of_two(5)); - } - #[test] - fn test_not_power_of_two() { - let mut rb = AllocRingBuffer::with_capacity_non_power_of_two(10); - const NUM_VALS: usize = 1000; - - // recycle the ringbuffer a bunch of time to see if noneof the logic - // messes up - for _ in 0..100 { - for i in 0..NUM_VALS { - rb.enqueue(i); - } - assert!(rb.is_full()); - - for i in 0..10 { - assert_eq!(Some(i + NUM_VALS - rb.capacity()), rb.dequeue()) - } - - assert!(rb.is_empty()) - } - } - - #[test] - fn test_default() { - let b: AllocRingBuffer = AllocRingBuffer::default(); - assert_eq!(RINGBUFFER_DEFAULT_CAPACITY, b.capacity()); - assert_eq!(RINGBUFFER_DEFAULT_CAPACITY, b.buf.capacity()); - assert_eq!(b.capacity, b.capacity()); - assert_eq!(b.buf.len(), b.len()); - assert_eq!(0, b.writeptr); - assert_eq!(0, b.readptr); - assert!(b.is_empty()); - assert!(b.buf.is_empty()); - assert_eq!(0, b.iter().count()); - assert_eq!( - Vec::::with_capacity(RINGBUFFER_DEFAULT_CAPACITY), - b.to_vec() - ); - } - - #[test] - fn test_default_capacity_constant() { - // This is to prevent accidentally changing it. - assert_eq!(RINGBUFFER_DEFAULT_CAPACITY, 1024) - } - - #[test] - fn test_with_capacity_power_of_two() { - let b = AllocRingBuffer::::with_capacity_power_of_2(2); - assert_eq!(b.capacity, 4); - } - - #[test] - #[should_panic] - fn test_with_capacity_no_power_of_two() { - let _ = AllocRingBuffer::::with_capacity(10); - } - - #[test] - #[should_panic] - fn test_index_zero_length() { - let b = AllocRingBuffer::::with_capacity(2); - let _ = b[2]; - } - - #[test] - fn test_extend() { - let mut buf = AllocRingBuffer::::with_capacity(4); - (0..4).for_each(|_| buf.push(0)); - - let new_data = [0, 1, 2]; - buf.extend(new_data); - - let expected = [0, 0, 1, 2]; - - for i in 0..4 { - let actual = buf[i as isize]; - let expected = expected[i]; - assert_eq!(actual, expected); - } - } - - #[test] - fn test_extend_with_overflow() { - let mut buf = AllocRingBuffer::::with_capacity(8); - (0..8).for_each(|_| buf.push(0)); - - let new_data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - buf.extend(new_data); - - let expected = [2, 3, 4, 5, 6, 7, 8, 9]; - - for i in 0..8 { - let actual = buf[i as isize]; - let expected = expected[i]; - assert_eq!(actual, expected); - } - } -} +pub mod alloc_ringbuffer; +pub mod vecdeque; diff --git a/src/with_alloc/alloc_ringbuffer.rs b/src/with_alloc/alloc_ringbuffer.rs new file mode 100644 index 0000000..62b52e4 --- /dev/null +++ b/src/with_alloc/alloc_ringbuffer.rs @@ -0,0 +1,491 @@ +extern crate alloc; +// We need vecs so depend on alloc +use crate::{GrowableAllocRingBuffer, RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite}; +use alloc::vec::Vec; +use core::iter::FromIterator; +use core::marker::PhantomData; +use core::mem; +use core::mem::MaybeUninit; +use core::ops::{Index, IndexMut}; + +#[derive(Debug, Copy, Clone)] +pub struct PowerOfTwo; +#[derive(Debug, Copy, Clone)] +pub struct NonPowerOfTwo; +mod private { + use crate::with_alloc::alloc_ringbuffer::{NonPowerOfTwo, PowerOfTwo}; + + pub trait Sealed {} + impl Sealed for PowerOfTwo {} + impl Sealed for NonPowerOfTwo {} +} +pub trait RingbufferMode: private::Sealed { + fn mask(cap: usize, index: usize) -> usize; + fn must_be_power_of_two() -> bool; +} +impl RingbufferMode for PowerOfTwo { + #[inline] + fn mask(cap: usize, index: usize) -> usize { + crate::mask(cap, index) + } + + fn must_be_power_of_two() -> bool { + true + } +} +impl RingbufferMode for NonPowerOfTwo { + #[inline] + fn mask(cap: usize, index: usize) -> usize { + crate::mask_modulo(cap, index) + } + + fn must_be_power_of_two() -> bool { + false + } +} + +/// The `AllocRingBuffer` is a `RingBufferExt` which is based on a Vec. This means it allocates at runtime +/// on the heap, and therefore needs the [`alloc`] crate. This struct and therefore the dependency on +/// alloc can be disabled by disabling the `alloc` (default) feature. +/// +/// # Example +/// ``` +/// use ringbuffer::{AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferWrite}; +/// +/// let mut buffer = AllocRingBuffer::with_capacity(2); +/// +/// // First entry of the buffer is now 5. +/// buffer.push(5); +/// +/// // The last item we pushed is 5 +/// assert_eq!(buffer.get(-1), Some(&5)); +/// +/// // Second entry is now 42. +/// buffer.push(42); +/// +/// assert_eq!(buffer.peek(), Some(&5)); +/// assert!(buffer.is_full()); +/// +/// // Because capacity is reached the next push will be the first item of the buffer. +/// buffer.push(1); +/// assert_eq!(buffer.to_vec(), vec![42, 1]); +/// ``` +#[derive(Debug)] +pub struct AllocRingBuffer { + buf: Vec>, + capacity: usize, + readptr: usize, + writeptr: usize, + mode: PhantomData, +} + +impl From<[T; N]> for AllocRingBuffer { + fn from(value: [T; N]) -> Self { + let mut rb = Self::with_capacity_non_power_of_two(value.len()); + rb.extend(value.into_iter()); + rb + } +} + +impl From<&[T]> for AllocRingBuffer { + fn from(value: &[T]) -> Self { + let mut rb = Self::with_capacity_non_power_of_two(value.len()); + rb.extend(value.iter().cloned()); + rb + } +} + +impl From> for AllocRingBuffer { + fn from(mut v: GrowableAllocRingBuffer) -> AllocRingBuffer { + let mut rb = AllocRingBuffer::with_capacity_non_power_of_two(v.len()); + rb.extend(v.drain()); + rb + } +} + +impl From<&mut [T]> for AllocRingBuffer { + fn from(value: &mut [T]) -> Self { + Self::from(&*value) + } +} + +impl Drop for AllocRingBuffer { + fn drop(&mut self) { + self.drain().for_each(drop); + } +} + +impl Clone for AllocRingBuffer { + fn clone(&self) -> Self { + debug_assert_ne!(self.capacity, 0); + debug_assert!(!MODE::must_be_power_of_two() || self.capacity.is_power_of_two()); + + // whatever the previous capacity was, we can just use the same one again. + // It should be valid. + let mut new = unsafe { Self::with_capacity_unchecked(self.capacity) }; + self.iter().cloned().for_each(|i| new.push(i)); + new + } +} + +impl PartialEq for AllocRingBuffer { + fn eq(&self, other: &Self) -> bool { + self.capacity == other.capacity + && self.len() == other.len() + && self.iter().zip(other.iter()).all(|(a, b)| a == b) + } +} + +impl Eq for AllocRingBuffer {} + +/// The capacity of a `RingBuffer` created by new or default (`1024`). +// must be a power of 2 +pub const RINGBUFFER_DEFAULT_CAPACITY: usize = 1024; + +unsafe impl RingBufferExt for AllocRingBuffer { + impl_ringbuffer_ext!( + get_unchecked, + get_unchecked_mut, + readptr, + writeptr, + MODE::mask + ); + + #[inline] + fn fill_with T>(&mut self, mut f: F) { + self.clear(); + + self.readptr = 0; + self.writeptr = self.capacity; + self.buf.fill_with(|| MaybeUninit::new(f())); + while self.buf.len() < self.capacity { + self.buf.push(MaybeUninit::new(f())); + } + } +} + +impl RingBufferRead for AllocRingBuffer { + fn dequeue(&mut self) -> Option { + if self.is_empty() { + None + } else { + let index = MODE::mask(self.capacity, self.readptr); + let res = mem::replace(&mut self.buf[index], MaybeUninit::uninit()); + self.readptr += 1; + + // Safety: the fact that we got this maybeuninit from the buffer (with mask) means that + // it's initialized. If it wasn't the is_empty call would have caught it. Values + // are always initialized when inserted so this is safe. + unsafe { Some(res.assume_init()) } + } + } + + impl_ringbuffer_read!(); +} + +impl Extend for AllocRingBuffer { + fn extend>(&mut self, iter: A) { + let iter = iter.into_iter(); + + for i in iter { + self.push(i); + } + } +} + +impl RingBufferWrite for AllocRingBuffer { + #[inline] + fn push(&mut self, value: T) { + if self.is_full() { + let previous_value = mem::replace( + &mut self.buf[MODE::mask(self.capacity, self.readptr)], + MaybeUninit::uninit(), + ); + // make sure we drop whatever is being overwritten + // SAFETY: the buffer is full, so this must be initialized + // : also, index has been masked + // make sure we drop because it won't happen automatically + unsafe { + drop(previous_value.assume_init()); + } + + self.readptr += 1; + } + + let index = MODE::mask(self.capacity, self.writeptr); + + if index >= self.buf.len() { + // initializing the maybeuninit when values are inserted/pushed + self.buf.push(MaybeUninit::new(value)); + } else { + // initializing the maybeuninit when values are inserted/pushed + self.buf[index] = MaybeUninit::new(value); + } + + self.writeptr += 1; + } +} + +impl RingBuffer for AllocRingBuffer { + #[inline] + unsafe fn ptr_capacity(rb: *const Self) -> usize { + (*rb).capacity + } + + impl_ringbuffer!(readptr, writeptr); +} + +impl AllocRingBuffer { + /// Creates a `AllocRingBuffer` with a certain capacity. This capacity is fixed. + /// for this ringbuffer to work, cap must be a power of two and greater than zero. + /// + /// # Safety + /// Only safe if the capacity is greater than zero, and a power of two. + /// Only if Mode == NonPowerOfTwo can the capacity be not a power of two, in which case this function is also safe. + #[inline] + unsafe fn with_capacity_unchecked(cap: usize) -> Self { + Self { + buf: Vec::with_capacity(cap), + capacity: cap, + readptr: 0, + writeptr: 0, + mode: Default::default(), + } + } +} + +impl AllocRingBuffer { + /// Creates a `AllocRingBuffer` with a certain capacity. This capacity is fixed. + /// for this ringbuffer to work, and must not be zero. + /// + /// Note, that not using a power of two means some operations can't be optimized as well. + /// For example, bitwise ands might become modulos. + /// + /// For example, on push operations, benchmarks have shown that a ringbuffer with a power-of-two + /// capacity constructed with `with_capacity_non_power_of_two` (so which don't get the same optimization + /// as the ones constructed with `with_capacity`) can be up to 3x slower + /// + /// # Panics + /// if the capacity is zero + #[inline] + pub fn with_capacity_non_power_of_two(cap: usize) -> Self { + assert_ne!(cap, 0, "Capacity must be greater than 0"); + + // Safety: Mode is NonPowerOfTwo and we checked above that the capacity isn't zero + unsafe { Self::with_capacity_unchecked(cap) } + } +} + +impl AllocRingBuffer { + /// Creates a `AllocRingBuffer` with a certain capacity. The actual capacity is the input to the + /// function raised to the power of two (effectively the input is the log2 of the actual capacity) + #[inline] + pub fn with_capacity_power_of_2(cap_power_of_two: usize) -> Self { + // Safety: 1 << n is always a power of two, and nonzero + unsafe { Self::with_capacity_unchecked(1 << cap_power_of_two) } + } + + #[inline] + /// Creates a `AllocRingBuffer` with a certain capacity. The capacity must be a power of two. + /// # Panics + /// Panics when capacity is zero or not a power of two + pub fn with_capacity(cap: usize) -> Self { + assert_ne!(cap, 0, "Capacity must be greater than 0"); + assert!(cap.is_power_of_two(), "Capacity must be a power of two"); + + // Safety: assertions check that cap is a power of two and nonzero + unsafe { Self::with_capacity_unchecked(cap) } + } + + /// Creates an `AllocRingBuffer` with a capacity of [`RINGBUFFER_DEFAULT_CAPACITY`]. + #[inline] + pub fn new() -> Self { + Self::default() + } +} + +/// Get a reference from the buffer without checking it is initialized. +/// Caller must be sure the index is in bounds, or this will panic. +#[inline] +unsafe fn get_unchecked<'a, T, MODE: RingbufferMode>( + rb: *const AllocRingBuffer, + index: usize, +) -> &'a T { + let p = &(*rb).buf[index]; + // Safety: caller makes sure the index is in bounds for the ringbuffer. + // All in bounds values in the ringbuffer are initialized + p.assume_init_ref() +} + +/// Get a mut reference from the buffer without checking it is initialized. +/// Caller must be sure the index is in bounds, or this will panic. +#[inline] +unsafe fn get_unchecked_mut( + rb: *mut AllocRingBuffer, + index: usize, +) -> *mut T { + let p = (*rb).buf.as_mut_ptr().add(index); + + // Safety: caller makes sure the index is in bounds for the ringbuffer. + // All in bounds values in the ringbuffer are initialized + p.cast() +} + +impl FromIterator for AllocRingBuffer { + fn from_iter>(iter: T) -> Self { + let mut res = Self::default(); + for i in iter { + res.push(i); + } + + res + } +} + +impl Default for AllocRingBuffer { + /// Creates a buffer with a capacity of [`crate::RINGBUFFER_DEFAULT_CAPACITY`]. + #[inline] + fn default() -> Self { + Self { + buf: Vec::with_capacity(RINGBUFFER_DEFAULT_CAPACITY), + capacity: RINGBUFFER_DEFAULT_CAPACITY, + readptr: 0, + writeptr: 0, + mode: Default::default(), + } + } +} + +impl Index for AllocRingBuffer { + type Output = T; + + fn index(&self, index: isize) -> &Self::Output { + self.get(index).expect("index out of bounds") + } +} + +impl IndexMut for AllocRingBuffer { + fn index_mut(&mut self, index: isize) -> &mut Self::Output { + self.get_mut(index).expect("index out of bounds") + } +} + +#[cfg(test)] +mod tests { + use super::alloc::vec::Vec; + use crate::with_alloc::alloc_ringbuffer::RingbufferMode; + use crate::{ + AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite, + RINGBUFFER_DEFAULT_CAPACITY, + }; + + // just test that this compiles + #[test] + fn test_generic_clone() { + fn helper( + a: &AllocRingBuffer, + ) -> AllocRingBuffer { + a.clone() + } + + _ = helper(&AllocRingBuffer::with_capacity(2)); + _ = helper(&AllocRingBuffer::with_capacity_non_power_of_two(5)); + } + #[test] + fn test_not_power_of_two() { + let mut rb = AllocRingBuffer::with_capacity_non_power_of_two(10); + const NUM_VALS: usize = 1000; + + // recycle the ringbuffer a bunch of time to see if noneof the logic + // messes up + for _ in 0..100 { + for i in 0..NUM_VALS { + rb.enqueue(i); + } + assert!(rb.is_full()); + + for i in 0..10 { + assert_eq!(Some(i + NUM_VALS - rb.capacity()), rb.dequeue()) + } + + assert!(rb.is_empty()) + } + } + + #[test] + fn test_default() { + let b: AllocRingBuffer = AllocRingBuffer::default(); + assert_eq!(RINGBUFFER_DEFAULT_CAPACITY, b.capacity()); + assert_eq!(RINGBUFFER_DEFAULT_CAPACITY, b.buf.capacity()); + assert_eq!(b.capacity, b.capacity()); + assert_eq!(b.buf.len(), b.len()); + assert_eq!(0, b.writeptr); + assert_eq!(0, b.readptr); + assert!(b.is_empty()); + assert!(b.buf.is_empty()); + assert_eq!(0, b.iter().count()); + assert_eq!( + Vec::::with_capacity(RINGBUFFER_DEFAULT_CAPACITY), + b.to_vec() + ); + } + + #[test] + fn test_default_capacity_constant() { + // This is to prevent accidentally changing it. + assert_eq!(RINGBUFFER_DEFAULT_CAPACITY, 1024) + } + + #[test] + fn test_with_capacity_power_of_two() { + let b = AllocRingBuffer::::with_capacity_power_of_2(2); + assert_eq!(b.capacity, 4); + } + + #[test] + #[should_panic] + fn test_with_capacity_no_power_of_two() { + let _ = AllocRingBuffer::::with_capacity(10); + } + + #[test] + #[should_panic] + fn test_index_zero_length() { + let b = AllocRingBuffer::::with_capacity(2); + let _ = b[2]; + } + + #[test] + fn test_extend() { + let mut buf = AllocRingBuffer::::with_capacity(4); + (0..4).for_each(|_| buf.push(0)); + + let new_data = [0, 1, 2]; + buf.extend(new_data); + + let expected = [0, 0, 1, 2]; + + for i in 0..4 { + let actual = buf[i as isize]; + let expected = expected[i]; + assert_eq!(actual, expected); + } + } + + #[test] + fn test_extend_with_overflow() { + let mut buf = AllocRingBuffer::::with_capacity(8); + (0..8).for_each(|_| buf.push(0)); + + let new_data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + buf.extend(new_data); + + let expected = [2, 3, 4, 5, 6, 7, 8, 9]; + + for i in 0..8 { + let actual = buf[i as isize]; + let expected = expected[i]; + assert_eq!(actual, expected); + } + } +} diff --git a/src/with_alloc/vecdeque.rs b/src/with_alloc/vecdeque.rs new file mode 100644 index 0000000..26fd66f --- /dev/null +++ b/src/with_alloc/vecdeque.rs @@ -0,0 +1,195 @@ +use crate::with_alloc::alloc_ringbuffer::RingbufferMode; +use crate::{AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite}; +use alloc::collections::VecDeque; +use core::ops::{Deref, DerefMut, Index, IndexMut}; + +/// A growable ringbuffer. Once capacity is reached, the size is doubled. +/// Wrapper of the built-in [`VecDeque`](std::collections::VecDeque) struct +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GrowableAllocRingBuffer(VecDeque); + +impl From<[T; N]> for GrowableAllocRingBuffer { + fn from(value: [T; N]) -> Self { + Self(VecDeque::from(value)) + } +} + +impl From> for GrowableAllocRingBuffer { + fn from(value: VecDeque) -> Self { + Self(value) + } +} + +impl From> for GrowableAllocRingBuffer { + fn from(mut v: AllocRingBuffer) -> GrowableAllocRingBuffer { + let mut rb = GrowableAllocRingBuffer::new(); + rb.extend(v.drain()); + rb + } +} + +impl Deref for GrowableAllocRingBuffer { + type Target = VecDeque; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for GrowableAllocRingBuffer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Default for GrowableAllocRingBuffer { + fn default() -> Self { + Self::new() + } +} + +impl AsRef> for GrowableAllocRingBuffer { + fn as_ref(&self) -> &VecDeque { + &self.0 + } +} + +impl GrowableAllocRingBuffer { + /// Creates an empty ringbuffer. + pub fn new() -> Self { + Self(VecDeque::new()) + } + + /// Creates an empty ringbuffer with space for at least capacity elements. + pub fn with_capacity(capacity: usize) -> Self { + Self(VecDeque::with_capacity(capacity)) + } +} + +impl RingBuffer for GrowableAllocRingBuffer { + unsafe fn ptr_len(rb: *const Self) -> usize { + (*rb).0.len() + } + + unsafe fn ptr_capacity(rb: *const Self) -> usize { + (*rb).0.capacity() + } +} + +impl RingBufferRead for GrowableAllocRingBuffer { + fn dequeue(&mut self) -> Option { + self.pop_front() + } + + impl_ringbuffer_read!(); +} + +impl RingBufferWrite for GrowableAllocRingBuffer { + fn push(&mut self, value: T) { + self.push_back(value) + } +} + +impl Extend for GrowableAllocRingBuffer { + fn extend>(&mut self, iter: I) { + self.0.extend(iter) + } +} + +impl Index for GrowableAllocRingBuffer { + type Output = T; + + fn index(&self, index: isize) -> &Self::Output { + self.get(index).expect("index out of bounds") + } +} + +impl IndexMut for GrowableAllocRingBuffer { + fn index_mut(&mut self, index: isize) -> &mut Self::Output { + self.get_mut(index).expect("index out of bounds") + } +} + +impl FromIterator for GrowableAllocRingBuffer { + fn from_iter>(iter: I) -> Self { + Self(VecDeque::from_iter(iter)) + } +} + +unsafe impl RingBufferExt for GrowableAllocRingBuffer { + fn fill_with T>(&mut self, mut f: F) { + self.clear(); + let initial_capacity = self.0.capacity(); + for _ in 0..initial_capacity { + self.0.push_back(f()) + } + + debug_assert_eq!(initial_capacity, self.0.capacity()) + } + + fn clear(&mut self) { + self.0.clear() + } + + fn get(&self, index: isize) -> Option<&T> { + if self.is_empty() { + None + } else if index >= 0 { + self.0.get(crate::mask_modulo(self.0.len(), index as usize)) + } else { + self.0.get(self.0.len() - (-index) as usize) + } + } + + unsafe fn ptr_get_mut(rb: *mut Self, index: isize) -> Option<*mut T> { + #[allow(trivial_casts)] + if index >= 0 { + (*rb).0.get_mut(index as usize) + } else { + let len = Self::ptr_len(rb); + (*rb).0.get_mut(len - (-index) as usize) + } + .map(|i| i as *mut T) + } + + fn get_absolute(&self, _index: usize) -> Option<&T> { + unimplemented!() + } + + fn get_absolute_mut(&mut self, _index: usize) -> Option<&mut T> { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + use crate::{ + AllocRingBuffer, GrowableAllocRingBuffer, RingBuffer, RingBufferRead, RingBufferWrite, + }; + + #[test] + fn test_convert() { + let mut a = GrowableAllocRingBuffer::new(); + a.push(0); + a.push(1); + + let mut b: AllocRingBuffer<_, _> = a.into(); + assert_eq!(b.capacity(), 2); + assert_eq!(b.len(), 2); + assert_eq!(b.dequeue(), Some(0)); + assert_eq!(b.dequeue(), Some(1)); + } + + #[test] + fn test_convert_back() { + let mut a = AllocRingBuffer::with_capacity(2); + a.push(0); + a.push(1); + + let mut b: GrowableAllocRingBuffer<_> = a.into(); + assert_eq!(b.len(), 2); + assert!(b.capacity() >= 2); + assert_eq!(b.dequeue(), Some(0)); + assert_eq!(b.dequeue(), Some(1)); + } +} From 4282121de9a4cbebdb2e727b4aa688576c58451f Mon Sep 17 00:00:00 2001 From: jonay2000 Date: Fri, 2 Jun 2023 18:08:08 +0200 Subject: [PATCH 2/6] fix tests --- src/with_alloc/vecdeque.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/with_alloc/vecdeque.rs b/src/with_alloc/vecdeque.rs index 26fd66f..87aa667 100644 --- a/src/with_alloc/vecdeque.rs +++ b/src/with_alloc/vecdeque.rs @@ -143,7 +143,9 @@ unsafe impl RingBufferExt for GrowableAllocRingBuffer { unsafe fn ptr_get_mut(rb: *mut Self, index: isize) -> Option<*mut T> { #[allow(trivial_casts)] - if index >= 0 { + if RingBuffer::ptr_len(rb) == 0 { + None + } else if index >= 0 { (*rb).0.get_mut(index as usize) } else { let len = Self::ptr_len(rb); From ad575c8e409cf2a3eef3ae5d5b709023b9083d70 Mon Sep 17 00:00:00 2001 From: jonay2000 Date: Fri, 2 Jun 2023 20:37:27 +0200 Subject: [PATCH 3/6] fix *and tesst* negative indices larger than length (which should wrap) --- src/lib.rs | 42 ++++++++++++++++++++++++++++++++++---- src/with_alloc/vecdeque.rs | 13 ++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 354b63f..7a8f54d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,9 +117,9 @@ mod tests { } } - test_neg_index(AllocRingBuffer::with_capacity(capacity)); + // test_neg_index(AllocRingBuffer::with_capacity(capacity)); + // test_neg_index(ConstGenericRingBuffer::::new()); test_neg_index(GrowableAllocRingBuffer::with_capacity(capacity)); - test_neg_index(ConstGenericRingBuffer::::new()); } #[test] @@ -787,9 +787,9 @@ mod tests { assert_eq!(b.front(), None); } - // test_front_none(AllocRingBuffer::with_capacity(8)); + test_front_none(AllocRingBuffer::with_capacity(8)); test_front_none(GrowableAllocRingBuffer::with_capacity(8)); - // test_front_none(ConstGenericRingBuffer::::new()); + test_front_none(ConstGenericRingBuffer::::new()); } #[test] @@ -971,6 +971,40 @@ mod tests { test_enqueue_dequeue_push(ConstGenericRingBuffer::::new()); } + #[test] + fn large_negative_index() { + fn test_large_negative_index(mut b: impl RingBufferExt) { + b.push(1); + b.push(2); + assert_eq!(b.get(1), Some(&2)); + assert_eq!(b.get(0), Some(&1)); + assert_eq!(b.get(-1), Some(&2)); + assert_eq!(b.get(-2), Some(&1)); + assert_eq!(b.get(-3), Some(&2)); + } + + test_large_negative_index(AllocRingBuffer::with_capacity(2)); + test_large_negative_index(ConstGenericRingBuffer::::new()); + test_large_negative_index(GrowableAllocRingBuffer::::new()); + } + + #[test] + fn large_negative_index_mut() { + fn test_large_negative_index(mut b: impl RingBufferExt) { + b.push(1); + b.push(2); + assert_eq!(b.get_mut(1), Some(&mut 2)); + assert_eq!(b.get_mut(0), Some(&mut 1)); + assert_eq!(b.get_mut(-1), Some(&mut 2)); + assert_eq!(b.get_mut(-2), Some(&mut 1)); + assert_eq!(b.get_mut(-3), Some(&mut 2)); + } + + test_large_negative_index(AllocRingBuffer::with_capacity(2)); + test_large_negative_index(ConstGenericRingBuffer::::new()); + test_large_negative_index(GrowableAllocRingBuffer::::new()); + } + #[test] fn run_test_push_dequeue_push_full() { fn test_push_dequeue_push_full(mut b: impl RingBufferExt) { diff --git a/src/with_alloc/vecdeque.rs b/src/with_alloc/vecdeque.rs index 87aa667..ce3fd8c 100644 --- a/src/with_alloc/vecdeque.rs +++ b/src/with_alloc/vecdeque.rs @@ -137,7 +137,11 @@ unsafe impl RingBufferExt for GrowableAllocRingBuffer { } else if index >= 0 { self.0.get(crate::mask_modulo(self.0.len(), index as usize)) } else { - self.0.get(self.0.len() - (-index) as usize) + let positive_index = -index as usize - 1; + let masked = crate::mask_modulo(self.0.len(), positive_index); + let index = self.0.len() - 1 - masked; + + self.0.get(index) } } @@ -149,7 +153,12 @@ unsafe impl RingBufferExt for GrowableAllocRingBuffer { (*rb).0.get_mut(index as usize) } else { let len = Self::ptr_len(rb); - (*rb).0.get_mut(len - (-index) as usize) + + let positive_index = -index as usize + 1; + let masked = crate::mask_modulo(len, positive_index); + let index = len - 1 - masked; + + (*rb).0.get_mut(index) } .map(|i| i as *mut T) } From c4dc561746cd39bbe68e3c6f4ebcc6cb2ff4c0ec Mon Sep 17 00:00:00 2001 From: jonay2000 Date: Mon, 5 Jun 2023 11:24:06 +0200 Subject: [PATCH 4/6] fix comments --- Cargo.toml | 2 +- src/lib.rs | 4 ++-- src/ringbuffer_trait.rs | 6 +++--- .../compile-fail/test_const_generic_array_zero_length.rs | 0 {_tests => tests}/compiletests.rs | 0 5 files changed, 6 insertions(+), 6 deletions(-) rename {_tests => tests}/compile-fail/test_const_generic_array_zero_length.rs (100%) rename {_tests => tests}/compiletests.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 672d94d..f4fe90f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT" [dev-dependencies] criterion = "0.4.0" -#compiletest_rs = "0.10.0" +compiletest_rs = "0.10.0" [features] default = ["alloc"] diff --git a/src/lib.rs b/src/lib.rs index 7a8f54d..4b5bdcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,8 +117,8 @@ mod tests { } } - // test_neg_index(AllocRingBuffer::with_capacity(capacity)); - // test_neg_index(ConstGenericRingBuffer::::new()); + test_neg_index(AllocRingBuffer::with_capacity(capacity)); + test_neg_index(ConstGenericRingBuffer::::new()); test_neg_index(GrowableAllocRingBuffer::with_capacity(capacity)); } diff --git a/src/ringbuffer_trait.rs b/src/ringbuffer_trait.rs index c012127..7c29179 100644 --- a/src/ringbuffer_trait.rs +++ b/src/ringbuffer_trait.rs @@ -159,14 +159,14 @@ pub unsafe trait RingBufferExt: unsafe fn ptr_get_mut(rb: *mut Self, index: isize) -> Option<*mut T>; /// Gets a value relative to the start of the array (rarely useful, usually you want [`Self::get`]) - #[deprecated] + #[deprecated = "cannot find a valid usecase for this, hard to implement for some ringbuffers"] fn get_absolute(&self, index: usize) -> Option<&T>; /// Gets a value mutably relative to the start of the array (rarely useful, usually you want [`Self::get_mut`]) - #[deprecated] + #[deprecated = "cannot find a valid usecase for this, hard to implement for some ringbuffers"] fn get_absolute_mut(&mut self, index: usize) -> Option<&mut T>; - /// Returns the value at the current index.t + /// Returns the value at the current index. /// This is the value that will be overwritten by the next push and also the value pushed /// the longest ago. (alias of [`Self::front`]) #[inline] diff --git a/_tests/compile-fail/test_const_generic_array_zero_length.rs b/tests/compile-fail/test_const_generic_array_zero_length.rs similarity index 100% rename from _tests/compile-fail/test_const_generic_array_zero_length.rs rename to tests/compile-fail/test_const_generic_array_zero_length.rs diff --git a/_tests/compiletests.rs b/tests/compiletests.rs similarity index 100% rename from _tests/compiletests.rs rename to tests/compiletests.rs From b01ebd223434addc0dccf292eb684a7c6ee224a4 Mon Sep 17 00:00:00 2001 From: jonay2000 Date: Mon, 5 Jun 2023 11:29:59 +0200 Subject: [PATCH 5/6] fix pr issues --- src/with_alloc/alloc_ringbuffer.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/with_alloc/alloc_ringbuffer.rs b/src/with_alloc/alloc_ringbuffer.rs index 62b52e4..0775bd6 100644 --- a/src/with_alloc/alloc_ringbuffer.rs +++ b/src/with_alloc/alloc_ringbuffer.rs @@ -87,6 +87,14 @@ impl From<[T; N]> for AllocRingBuffer { } } +impl From<&[T; N]> for AllocRingBuffer { + // the cast here is actually not trivial + #[allow(trivial_casts)] + fn from(value: &[T; N]) -> Self { + Self::from(value as &[T]) + } +} + impl From<&[T]> for AllocRingBuffer { fn from(value: &[T]) -> Self { let mut rb = Self::with_capacity_non_power_of_two(value.len()); @@ -488,4 +496,23 @@ mod tests { assert_eq!(actual, expected); } } + + #[test] + fn test_conversions() { + // from &[T] + let data: &[i32] = &[1, 2, 3, 4]; + let buf = AllocRingBuffer::from(data); + assert_eq!(buf.capacity, 4); + assert_eq!(buf.to_vec(), alloc::vec![1, 2, 3, 4]); + + // from &[T; N] + let buf = AllocRingBuffer::from(&[1, 2, 3, 4]); + assert_eq!(buf.capacity, 4); + assert_eq!(buf.to_vec(), alloc::vec![1, 2, 3, 4]); + + // from [T; N] + let buf = AllocRingBuffer::from([1, 2, 3, 4]); + assert_eq!(buf.capacity, 4); + assert_eq!(buf.to_vec(), alloc::vec![1, 2, 3, 4]); + } } From 586cbe8f37b0f3ab722cc3aa9f5e7820964ed8ed Mon Sep 17 00:00:00 2001 From: jonay2000 Date: Mon, 5 Jun 2023 11:32:26 +0200 Subject: [PATCH 6/6] comment about wrapper --- src/with_alloc/vecdeque.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/with_alloc/vecdeque.rs b/src/with_alloc/vecdeque.rs index ce3fd8c..dd9b27d 100644 --- a/src/with_alloc/vecdeque.rs +++ b/src/with_alloc/vecdeque.rs @@ -5,6 +5,9 @@ use core::ops::{Deref, DerefMut, Index, IndexMut}; /// A growable ringbuffer. Once capacity is reached, the size is doubled. /// Wrapper of the built-in [`VecDeque`](std::collections::VecDeque) struct +/// +/// The reason this is a wrapper, is that we want RingBuffers to implement `Index`, +/// which we cannot do for remote types like `VecDeque` #[derive(Debug, Clone, PartialEq, Eq)] pub struct GrowableAllocRingBuffer(VecDeque);