diff --git a/Cargo.toml b/Cargo.toml index c883c99..dfe9460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,12 @@ criterion = "0.4.0" compiletest_rs = "0.10.0" [features] -default = ["alloc"] +default = ["alloc", "batched_extend"] # disable the alloc based ringbuffer, to make RingBuffers work in no_alloc environments alloc = [] +batched_extend = [] + [[bench]] name = "bench" harness = false diff --git a/benches/bench.rs b/benches/bench.rs index 784283a..3289d0e 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -235,7 +235,7 @@ fn extend_too_few(b: &mut Bencher) { fn extend_after_one(b: &mut Bencher) { let mut rb = ConstGenericRingBuffer::new::<8192>(); - rb.push(0); + rb.push(&0); let input = (0..4096).collect::>(); b.iter_batched( diff --git a/src/lib.rs b/src/lib.rs index 9f0f09d..fa3dd17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1356,8 +1356,8 @@ mod tests { } test_fill(AllocRingBuffer::new(4)); - // test_fill(GrowableAllocRingBuffer::with_capacity(4)); - // test_fill(ConstGenericRingBuffer::::new()); + test_fill(GrowableAllocRingBuffer::with_capacity(4)); + test_fill(ConstGenericRingBuffer::::new()); } mod test_dropping { diff --git a/src/with_const_generics.rs b/src/with_const_generics.rs index aadbb38..c4d9ad8 100644 --- a/src/with_const_generics.rs +++ b/src/with_const_generics.rs @@ -248,6 +248,7 @@ impl ConstGenericRingBuffer { /// # Safety /// Only safe when old != new #[inline] + #[cfg(feature = "batched_extend")] unsafe fn split_pointer_move( &mut self, old: usize, @@ -273,6 +274,7 @@ impl ConstGenericRingBuffer { /// # Safety /// Only safe when `CAP` >= `BATCH_SIZE` #[inline] + #[cfg(feature = "batched_extend")] unsafe fn extend_from_arr_batch(&mut self, data: [T; BATCH_SIZE]) { debug_assert!(CAP >= BATCH_SIZE); @@ -374,6 +376,7 @@ impl ConstGenericRingBuffer { } #[inline] + #[cfg(feature = "batched_extend")] fn fill_batch( batch: &mut [MaybeUninit; BATCH_SIZE], iter: &mut impl Iterator, @@ -390,6 +393,7 @@ impl ConstGenericRingBuffer { } #[inline] + #[cfg(feature = "batched_extend")] fn extend_batched(&mut self, mut other: impl Iterator) { // SAFETY: if CAP < Self::BATCH_SIZE we can't run extend_from_arr_batch so we catch that here if CAP < BATCH_SIZE { @@ -431,6 +435,7 @@ impl ConstGenericRingBuffer { /// # Safety /// ONLY USE WHEN WORKING ON A CLEARED RINGBUFFER + #[cfg(feature = "batched_extend")] #[inline] unsafe fn finish_iter(&mut self, mut iter: impl Iterator) { let mut index = 0; @@ -458,33 +463,42 @@ impl Extend for ConstGenericRingBuffer { /// NOTE: correctness (but not soundness) of extend depends on `size_hint` on iter being correct. #[inline] fn extend>(&mut self, iter: A) { - const BATCH_SIZE: usize = 1; - // const BATCH_SIZE: usize = 1024; + #[cfg(not(feature = "batched_extend"))] + { + for i in iter { + self.push(i); + } + } - let iter = iter.into_iter(); + #[cfg(feature = "batched_extend")] + { + const BATCH_SIZE: usize = 30; - let (lower, _) = iter.size_hint(); + let iter = iter.into_iter(); - if lower >= CAP { - // if there are more elements in our iterator than we have size in the ringbuffer - // drain the ringbuffer - self.clear(); + let (lower, _) = iter.size_hint(); - // we need exactly CAP elements. - // so we need to drop until the number of elements in the iterator is exactly CAP - let num_we_can_drop = lower - CAP; + if lower >= CAP { + // if there are more elements in our iterator than we have size in the ringbuffer + // drain the ringbuffer + self.clear(); - let iter = iter.skip(num_we_can_drop); + // we need exactly CAP elements. + // so we need to drop until the number of elements in the iterator is exactly CAP + let num_we_can_drop = lower - CAP; - // Safety: clear above - unsafe { self.finish_iter::(iter) }; - } else if self.is_empty() { - self.clear(); + let iter = iter.skip(num_we_can_drop); - // Safety: clear above - unsafe { self.finish_iter::(iter) }; - } else { - self.extend_batched::(iter); + // Safety: clear above + unsafe { self.finish_iter::(iter) }; + } else if self.is_empty() { + self.clear(); + + // Safety: clear above + unsafe { self.finish_iter::(iter) }; + } else { + self.extend_batched::(iter); + } } } } diff --git a/tests/compile-fail/test_const_generic_array_zero_length.rs b/tests/compile-fail/test_const_generic_array_zero_length.rs new file mode 100644 index 0000000..3b69f1a --- /dev/null +++ b/tests/compile-fail/test_const_generic_array_zero_length.rs @@ -0,0 +1,9 @@ +extern crate ringbuffer; + +use ringbuffer::ConstGenericRingBuffer; + +fn main() { + let _ = ConstGenericRingBuffer::::new(); + //~^ note: the above error was encountered while instantiating `fn ringbuffer::ConstGenericRingBuffer::::new::<0>` + // ringbuffer can't be zero length +} diff --git a/tests/compile-fail/test_const_generic_array_zero_length_new.rs b/tests/compile-fail/test_const_generic_array_zero_length_new.rs new file mode 100644 index 0000000..b080de1 --- /dev/null +++ b/tests/compile-fail/test_const_generic_array_zero_length_new.rs @@ -0,0 +1,10 @@ +extern crate ringbuffer; + +use ringbuffer::{ConstGenericRingBuffer, RingBuffer}; + +fn main() { + let mut buf = ConstGenericRingBuffer::new::<0>(); + //~^ note: the above error was encountered while instantiating `fn ringbuffer::ConstGenericRingBuffer::::new::<0>` + // ringbuffer can't be zero length + buf.push(5); +} diff --git a/tests/compiletests.rs b/tests/compiletests.rs new file mode 100644 index 0000000..f48163e --- /dev/null +++ b/tests/compiletests.rs @@ -0,0 +1,23 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; + +#[cfg(test)] +mod conversions; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::Config::default(); + + config.mode = mode.parse().expect("Invalid mode"); + config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.link_deps(); // Populate config.target_rustcflags with dependencies on the path + config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 + + compiletest::run_tests(&config); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn compile_test() { + run_mode("compile-fail"); +} diff --git a/tests/conversions.rs b/tests/conversions.rs new file mode 100644 index 0000000..e3c13a2 --- /dev/null +++ b/tests/conversions.rs @@ -0,0 +1,135 @@ +extern crate alloc; + +use alloc::collections::{LinkedList, VecDeque}; +use alloc::string::ToString; +use core::ops::Deref; +use ringbuffer::RingBuffer; +use ringbuffer::{AllocRingBuffer, ConstGenericRingBuffer, GrowableAllocRingBuffer}; +use std::vec; + +macro_rules! convert_test { + ($name: ident: $from: expr => $to: ty) => { + #[test] + fn $name() { + let a = $from; + + let mut b: $to = a.into(); + assert_eq!(b.to_vec(), vec!['1', '2']); + b.push('3'); + assert_eq!(b, b); + } + }; +} + +macro_rules! convert_tests { + ( + [$($name: ident: $from: expr),* $(,)?] + => $to: ty + ) => { + $( + convert_test!($name: $from => $to); + )* + }; +} + +convert_tests!( + [ + alloc_from_vec: vec!['1', '2'], + alloc_from_ll: {let mut l = LinkedList::new(); l.push_back('1'); l.push_back('2'); l}, + alloc_from_vd: {let mut l = VecDeque::new(); l.push_back('1'); l.push_back('2'); l}, + alloc_from_str: "12".to_string(), + alloc_from_str_slice: "12", + alloc_from_slice: {let a: &[char] = &['1', '2']; a}, + alloc_from_const_slice: {let a: &[char; 2] = &['1', '2']; a}, + alloc_from_arr: {let a: [char; 2] = ['1', '2']; a}, + + alloc_from_cgrb: {let a = ConstGenericRingBuffer::from(['1', '2']); a}, + alloc_from_garb: {let a = GrowableAllocRingBuffer::from(['1', '2']); a}, + ] => AllocRingBuffer::<_> +); + +convert_tests!( + [ + growable_alloc_from_vec: vec!['1', '2'], + growable_alloc_from_ll: {let mut l = LinkedList::new(); l.push_back('1'); l.push_back('2'); l}, + growable_alloc_from_vd: {let mut l = VecDeque::new(); l.push_back('1'); l.push_back('2'); l}, + growable_alloc_from_str: "12".to_string(), + growable_alloc_from_str_slice: "12", + growable_alloc_from_slice: {let a: &[char] = &['1', '2']; a}, + growable_alloc_from_const_slice: {let a: &[char; 2] = &['1', '2']; a}, + growable_alloc_from_arr: {let a: [char; 2] = ['1', '2']; a}, + + growable_alloc_from_cgrb: {let a = ConstGenericRingBuffer::from(['1', '2']); a}, + growable_alloc_from_arb: {let a = AllocRingBuffer::from(['1', '2']); a}, + ] => GrowableAllocRingBuffer::<_> +); + +convert_tests!( + [ + const_from_vec: vec!['1', '2'], + const_from_ll: {let mut l = LinkedList::new(); l.push_back('1'); l.push_back('2'); l}, + const_from_vd: {let mut l = VecDeque::new(); l.push_back('1'); l.push_back('2'); l}, + const_from_str: "12".to_string(), + const_from_str_slice: "12", + const_from_slice: {let a: &[char] = &['1', '2']; a}, + const_from_const_slice: {let a: &[char; 2] = &['1', '2']; a}, + const_from_arr: {let a: [char; 2] = ['1', '2']; a}, + + const_from_garb: {let a = GrowableAllocRingBuffer::from(['1', '2']); a}, + const_from_arb: {let a = AllocRingBuffer::from(['1', '2']); a}, + ] => ConstGenericRingBuffer::<_, 2> +); + +#[test] +fn test_extra_conversions_growable() { + let a: &mut [i32; 2] = &mut [1, 2]; + let a = GrowableAllocRingBuffer::from(a); + assert_eq!(a.to_vec(), vec![1, 2]); + + let a: &mut [i32] = &mut [1, 2]; + let a = GrowableAllocRingBuffer::from(a); + assert_eq!(a.to_vec(), vec![1, 2]); + + let mut b = VecDeque::::new(); + b.push_back(1); + b.push_back(2); + assert_eq!(a.deref(), &b); + assert_eq!(a.as_ref(), &b); +} + +#[test] +fn test_extra_conversions_alloc() { + let a: &mut [i32; 2] = &mut [1, 2]; + let a = AllocRingBuffer::from(a); + assert_eq!(a.to_vec(), vec![1, 2]); + + let a: &mut [i32] = &mut [1, 2]; + let a = AllocRingBuffer::from(a); + assert_eq!(a.to_vec(), vec![1, 2]); +} + +#[test] +fn test_extra_conversions_const() { + let a: &mut [i32; 2] = &mut [1, 2]; + let a = ConstGenericRingBuffer::<_, 2>::from(a); + assert_eq!(a.to_vec(), vec![1, 2]); + + let a: &mut [i32] = &mut [1, 2]; + let a = ConstGenericRingBuffer::<_, 2>::from(a); + assert_eq!(a.to_vec(), vec![1, 2]); +} + +#[test] +fn test_const_generic_new_parameter() { + // Can we specify size only on the method? + let mut a = ConstGenericRingBuffer::new::<2>(); + a.push(5); + + // Can we specify size in both positions? + let mut a = ConstGenericRingBuffer::::new::<50>(); + a.push(5); + + // Can we specify size only on the struct? + let mut a = ConstGenericRingBuffer::::new(); + a.push(5); +}