diff --git a/benches/bench.rs b/benches/bench.rs index 67f50d8..eb32ad0 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -157,6 +157,98 @@ fn benchmark_copy_from_slice_vs_extend + SetLen, F: Fn() -> T group.finish(); } +fn benchmark_extend_from_slice_vs_extend, F: Fn() -> T>( + rb_size: usize, + rb_type: &str, + fn_name: &str, + c: &mut Criterion, + new: F, +) { + let mut group = c.benchmark_group(format!("{fn_name}({rb_type}, {rb_size})")); + let input = vec![9; rb_size]; + group.bench_function(format!("ExtendFromSlice({rb_type}; {rb_size})"), |b| { + let mut rb = new(); + rb.fill(9); + // making sure the read/write pointers wrap around + for _ in 0..rb_size / 2 { + let _ = rb.dequeue(); + let _ = rb.enqueue(6); + } + b.iter(|| { + rb.extend_from_slice(&input); + assert_eq!(input[input.len() / 2], 9); + }) + }); + group.bench_function(format!("ExtendFromIter({rb_type}; {rb_size})"), |b| { + let mut rb = new(); + rb.fill(9); + // making sure the read/write pointers wrap around + for _ in 0..rb_size / 2 { + let _ = rb.dequeue(); + let _ = rb.enqueue(9); + } + b.iter(|| { + rb.extend(input.iter().copied()); + assert_eq!(input[input.len() / 2], 9); + }) + }); + group.finish(); +} + +fn benchmark_drain_to_slice_vs_drain, F: Fn() -> T>( + rb_size: usize, + rb_type: &str, + fn_name: &str, + c: &mut Criterion, + new: F, +) { + let mut group = c.benchmark_group(format!("{fn_name}({rb_type}, {rb_size})")); + let mut output = vec![0; rb_size]; + group.bench_function(&format!("DrainToSlice({rb_type}; {rb_size})"), |b| { + b.iter_batched( + || { + let mut rb = new(); + rb.fill(9); + // making sure the read/write pointers wrap around + for _ in 0..rb_size / 2 { + let _ = rb.dequeue(); + let _ = rb.enqueue(9); + } + rb + }, + |mut rb| { + rb.drain_to_slice(&mut output); + assert_eq!(output[output.len() / 2], 9); + }, + criterion::BatchSize::NumIterations(1), + ) + }); + let mut output = vec![0; rb_size]; + group.bench_function(format!("DrainIter({rb_type}; {rb_size})"), |b| { + b.iter_batched( + || { + let mut rb = new(); + rb.fill(9); + // making sure the read/write pointers wrap around + for _ in 0..rb_size / 2 { + let _ = rb.dequeue(); + let _ = rb.enqueue(9); + } + rb + }, + |mut rb| { + output + .iter_mut() + .zip(rb.drain()) + .for_each(|(dst, src)| *dst = src); + assert_eq!(output[output.len() / 2], 9); + }, + criterion::BatchSize::NumIterations(1), + ) + }); + group.finish(); +} + macro_rules! generate_benches { (called, $c: tt, $rb: tt, $ty: tt, $fn: tt, $bmfunc: tt, $($i:tt),*) => { $( @@ -429,6 +521,60 @@ fn criterion_benchmark(c: &mut Criterion) { 1_000_000, 1_048_576 ]; + generate_benches![ + compare, + c, + AllocRingBuffer, + i32, + new, + benchmark_extend_from_slice_vs_extend, + 16, + 1024, + 4096, + 8192, + 1_000_000, + 1_048_576 + ]; + generate_benches![ + compare_typed, + c, + ConstGenericRingBuffer, + i32, + new, + benchmark_extend_from_slice_vs_extend, + 16, + 1024, + 4096, + 8192, + 1_000_000, + 1_048_576 + ]; + generate_benches![ + compare, + c, + AllocRingBuffer, + i32, + new, + benchmark_drain_to_slice_vs_drain, + 16, + 1024, + 4096, + 8192, + 1_000_000, + 1_048_576 + ]; + generate_benches![ + compare_typed, + c, + ConstGenericRingBuffer, + i32, + new, + benchmark_drain_to_slice_vs_drain, + 16, + 1024, + 4096, + 8192 + ]; } criterion_group!(benches, criterion_benchmark); diff --git a/src/lib.rs b/src/lib.rs index 23cafe7..c09136d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1696,12 +1696,53 @@ mod tests { }); } + #[test] + fn test_copy_from_slice_partial() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(&[3, 2, 1]); + assert_eq!(rb.capacity(), 7); + // we have some space left + assert!(rb.len() < rb.capacity()); + + // copy preserves length + rb.copy_from_slice(0, &[1, 2]); + assert_eq!(rb.to_vec(), alloc::vec![1, 2, 1]); + + let _ = rb.enqueue(4); + let _ = rb.enqueue(5); + let _ = rb.enqueue(6); + assert_eq!(rb.to_vec(), alloc::vec![1, 2, 1, 4, 5, 6]); + + // still preserving length + rb.copy_from_slice(1, &[5, 4, 3, 2, 1]); + assert_eq!(rb.to_vec(), alloc::vec![1, 5, 4, 3, 2, 1]); + }; + } + + test_concrete!(|values: &[i32]| { + let mut rb = ConstGenericRingBuffer::<_, 7>::new(); + rb.extend(values.iter().copied()); + rb + }); + test_concrete!(|values: &[i32]| { + let mut rb = GrowableAllocRingBuffer::<_>::with_capacity(7); + rb.extend(values.iter().copied()); + rb + }); + test_concrete!(|values: &[i32]| { + let mut rb = AllocRingBuffer::<_>::new(7); + rb.extend(values.iter().copied()); + rb + }); + } + #[test] fn test_copy_from_slice_empty() { macro_rules! test_concrete { ($rb_init: expr) => { let mut rb = $rb_init(); - rb.copy_from_slice(0, &[0; 0]); + rb.copy_from_slice(0, &[]); assert_eq!(rb.to_vec(), alloc::vec![]); }; } @@ -1907,6 +1948,60 @@ mod tests { }); } + #[test] + fn test_copy_to_slice_partial() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(&[1, 2, 3]); + assert_eq!(rb.capacity(), 7); + // we have some space left + assert!(rb.len() < rb.capacity()); + + // copy based on length + let mut slice = [0; 2]; + rb.copy_to_slice(0, &mut slice); + assert_eq!(slice.as_slice(), &[1, 2]); + + let _ = rb.enqueue(4); + let _ = rb.enqueue(5); + let _ = rb.enqueue(6); + // still based on length + let mut slice = [0; 5]; + rb.copy_to_slice(0, &mut slice); + assert_eq!(slice.as_slice(), &[1, 2, 3, 4, 5]); + + // making sure the read/write ptrs have traversed the ring + for i in 0..6 { + let _ = rb.enqueue(i + 1); + let _ = rb.dequeue(); + } + + // sanity check + assert_eq!(rb.to_vec(), alloc::vec![1, 2, 3, 4, 5, 6]); + // copy again + let mut slice = [0; 5]; + rb.copy_to_slice(1, &mut slice); + assert_eq!(slice.as_slice(), &[2, 3, 4, 5, 6]); + }; + } + + test_concrete!(|values: &[i32]| { + let mut rb = ConstGenericRingBuffer::<_, 7>::new(); + rb.extend(values.iter().copied()); + rb + }); + test_concrete!(|values: &[i32]| { + let mut rb = GrowableAllocRingBuffer::<_>::with_capacity(7); + rb.extend(values.iter().copied()); + rb + }); + test_concrete!(|values: &[i32]| { + let mut rb = AllocRingBuffer::<_>::new(7); + rb.extend(values.iter().copied()); + rb + }); + } + #[test] fn test_copy_to_slice_empty() { macro_rules! test_concrete { @@ -1914,7 +2009,7 @@ mod tests { let rb = $rb_init(); let mut slice = []; rb.copy_to_slice(0, &mut slice); - assert_eq!(slice.as_slice(), &[0; 0]); + assert_eq!(slice.as_slice(), &[]); }; } @@ -2059,4 +2154,304 @@ mod tests { rb }); } + + #[test] + fn test_extend_from_slice() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + // leave some space + rb.extend_from_slice(&[9; 3]); + assert_eq!(&rb.to_vec(), &[9; 3]); + // leave some space + rb.extend_from_slice(&[6; 5]); + assert_eq!(&rb.to_vec(), &[9, 9, 9, 6, 6, 6, 6, 6]); + // fill up remaining slots + rb.extend_from_slice(&[1; 2]); + assert_eq!(&rb.to_vec(), &[9, 9, 9, 6, 6, 6, 6, 6, 1, 1]); + assert!(&rb.is_full()); + // overwrite + rb.extend_from_slice(&[7; 2]); + assert_eq!(&rb.to_vec(), &[9, 6, 6, 6, 6, 6, 1, 1, 7, 7]); + }; + } + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(10)); + } + + #[test] + fn test_extend_from_slice_after_use() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[7, 8, 9]); + assert_eq!(&rb.to_vec(), &[4, 5, 6, 7, 8, 9]); + }; + } + + test_concrete!(|| { + let mut rb = ConstGenericRingBuffer::::new(); + let _ = rb.enqueue(4); + let _ = rb.enqueue(5); + let _ = rb.enqueue(6); + rb + }); + test_concrete!(|| { + let mut rb = AllocRingBuffer::::new(20); + let _ = rb.enqueue(4); + let _ = rb.enqueue(5); + let _ = rb.enqueue(6); + rb + }); + } + + #[test] + fn test_extend_from_slice_fill_capacity() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[9; 3]); + assert_eq!(&rb.to_vec(), &[9; 3]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(3)); + } + + #[test] + fn test_extend_from_slice_partial() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[9; 2]); + assert_eq!(&rb.to_vec(), &[9, 9]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(3)); + } + + #[test] + fn test_extend_from_slice_empty() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[]); + assert_eq!(&rb.to_vec(), &[]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(1)); + test_concrete!(|| AllocRingBuffer::::new(1)); + } + + #[test] + fn test_extend_from_slice_empty_alt() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + let _ = rb.enqueue(1); + rb.extend_from_slice(&[]); + assert_eq!(&rb.to_vec(), &[1]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(1)); + test_concrete!(|| AllocRingBuffer::::new(1)); + } + + #[test] + fn test_extend_from_slice_wrap_around() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[1, 2, 3, 4]); + assert_eq!(&rb.to_vec(), &[4]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(1)); + } + + #[test] + fn test_extend_from_slice_wrap_around_overwriting() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[1, 2, 3, 4]); + rb.extend_from_slice(&[5, 6, 7, 8]); + assert_eq!(&rb.to_vec(), &[5, 6, 7, 8]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(4)); + } + + #[test] + fn test_extend_from_slice_wrap_around_partially_overwriting() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[1, 2, 3, 4]); + rb.extend_from_slice(&[5, 6]); + assert_eq!(&rb.to_vec(), &[3, 4, 5, 6]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(4)); + } + + #[test] + fn test_extend_from_slice_longer_than_capacity() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[1, 2, 3, 4, 6, 7, 8, 9, 10]); + assert_eq!(&rb.to_vec(), &[7, 8, 9, 10]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(4)); + } + + #[test] + fn test_drain_to_slice_fill_capacity() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[9; 3]); + let mut slice = [0; 3]; + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[9; 3]); + rb.extend_from_slice(&[1, 2, 3]); + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[1, 2, 3]); + assert_eq!(&rb.to_vec(), &[]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(3)); + test_concrete!(|| AllocRingBuffer::::new(3)); + } + + #[test] + fn test_drain_to_slice_partial() { + macro_rules! test_concrete { + ($rb_init: expr, $growable: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[9; 3]); + let mut slice = [0; 2]; + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[9; 2]); + rb.extend_from_slice(&[1, 2, 3]); + rb.drain_to_slice(&mut slice); + if $growable { + assert_eq!(&slice, &[9, 1]); + assert_eq!(&rb.to_vec(), &[2, 3]); + } else { + assert_eq!(&slice, &[1, 2]); + assert_eq!(&rb.to_vec(), &[3]); + } + }; + } + + test_concrete!(ConstGenericRingBuffer::::new, false); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(3), true); + test_concrete!(|| AllocRingBuffer::::new(3), false); + } + + #[test] + fn test_drain_to_slice_empty() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + let mut slice = []; + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(1)); + test_concrete!(|| AllocRingBuffer::::new(1)); + } + + #[test] + fn test_drain_to_slice_empty_alt() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + let _ = rb.enqueue(1); + let mut slice = []; + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(1)); + test_concrete!(|| AllocRingBuffer::::new(1)); + } + + #[test] + fn test_drain_to_slice_wrap_around() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[1, 2, 3]); + rb.extend_from_slice(&[4, 5, 6]); + let mut slice = [0; 4]; + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[3, 4, 5, 6]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(4)); + } + + #[test] + fn test_drain_to_slice_wrap_around_overwrite() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + rb.extend_from_slice(&[1, 2, 3, 4]); + rb.extend_from_slice(&[5, 6, 7, 8]); + let mut slice = [0; 4]; + rb.drain_to_slice(&mut slice); + assert_eq!(&slice, &[5, 6, 7, 8]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| AllocRingBuffer::::new(4)); + } + + #[test] + fn test_drain_to_slice_longer_than_len() { + macro_rules! test_concrete { + ($rb_init: expr) => { + let mut rb = $rb_init(); + let mut slice = [0; 10]; + rb.drain_to_slice(&mut slice); + assert_eq!(slice, [0; 10]); + rb.extend_from_slice(&[1, 2]); + rb.drain_to_slice(&mut slice); + assert_eq!(slice, [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]); + }; + } + + test_concrete!(ConstGenericRingBuffer::::new); + test_concrete!(|| GrowableAllocRingBuffer::::with_capacity(4)); + test_concrete!(|| AllocRingBuffer::::new(4)); + } } diff --git a/src/ringbuffer_trait.rs b/src/ringbuffer_trait.rs index 046b845..a173042 100644 --- a/src/ringbuffer_trait.rs +++ b/src/ringbuffer_trait.rs @@ -257,7 +257,7 @@ pub unsafe trait RingBuffer: /// Efficiently copy items from the ringbuffer to a target slice. /// /// # Panics - /// Panics if the buffer length minus the offset is NOT equal to `target.len()`. + /// Panics if the buffer length minus the offset is smaller than `dst.len()`. /// /// # Safety /// ONLY SAFE WHEN self is a *const to to an implementor of `RingBuffer` @@ -268,7 +268,7 @@ pub unsafe trait RingBuffer: /// Efficiently copy items from the ringbuffer to a target slice. /// /// # Panics - /// Panics if the buffer length minus the offset is NOT equal to `target.len()`. + /// Panics if the buffer length minus the offset is smaller than `dst.len()`. fn copy_to_slice(&self, offset: usize, dst: &mut [T]) where T: Copy, @@ -277,8 +277,9 @@ pub unsafe trait RingBuffer: } /// Efficiently copy items from a slice to the ringbuffer. + /// /// # Panics - /// Panics if the buffer length minus the offset is NOT equal to `source.len()`. + /// Panics if the buffer length minus the offset is smaller than `src.len()`. /// /// # Safety /// ONLY SAFE WHEN self is a *mut to to an implementor of `RingBuffer` @@ -289,13 +290,84 @@ pub unsafe trait RingBuffer: /// Efficiently copy items from a slice to the ringbuffer. /// /// # Panics - /// Panics if the buffer length minus the offset is NOT equal to `source.len()`. + /// Panics if the buffer length minus the offset is smaller than `src.len()`. fn copy_from_slice(&mut self, offset: usize, src: &[T]) where T: Copy, { unsafe { Self::ptr_copy_from_slice(self, offset, src) } } + + /// Efficiently extend the ringbuffer with the content of a slice. + /// + /// Note: the semantics of this method are the same as `.extend`, + /// thus the slice can be longer than the capacity of the ringbuffer. + /// + /// # Safety + /// ONLY SAFE WHEN self is a *mut to to an implementor of `RingBuffer` + unsafe fn ptr_extend_from_slice(rb: *mut Self, src: &[T]) + where + T: Copy; + + /// Efficiently extend the ringbuffer with the content of a slice. + /// + /// Note: the semantics of this method are the same as `.extend`, + /// thus the slice can be longer than the capacity of the ringbuffer. + /// + /// # Examples + /// + /// ``` + /// use ringbuffer::AllocRingBuffer; + /// use crate::ringbuffer::RingBuffer; + /// + /// let mut rb = AllocRingBuffer::with_capacity(4); + /// rb.extend_from_slice(&[1, 2, 3]); + /// assert_eq!(&rb.to_vec(), &[1, 2, 3]); + /// rb.extend_from_slice(&[4, 5, 6]); + /// assert_eq!(&rb.to_vec(), &[3, 4, 5, 6]); + /// + /// let mut rb = AllocRingBuffer::with_capacity(4); + /// rb.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]); + /// assert_eq!(&rb.to_vec(), &[5, 6, 7, 8]); + /// ``` + fn extend_from_slice(&mut self, src: &[T]) + where + T: Copy, + { + unsafe { Self::ptr_extend_from_slice(self, src) } + } + + /// Efficiently drain the ringbuffer, transferring items to a slice. + /// + /// # Safety + /// ONLY SAFE WHEN self is a *mut to to an implementor of `RingBuffer` + unsafe fn ptr_drain_to_slice(rb: *mut Self, dst: &mut [T]) + where + T: Copy; + + /// Efficiently drain the ringbuffer, transferring items to a slice. + /// + /// # Examples + /// + /// ``` + /// use ringbuffer::AllocRingBuffer; + /// use crate::ringbuffer::RingBuffer; + /// + /// let mut slice = vec![0; 3]; + /// let mut rb = AllocRingBuffer::from([1, 2, 3, 4]); + /// rb.drain_to_slice(&mut slice); + /// assert_eq!(&slice, &[1, 2, 3]); + /// assert_eq!(&rb.to_vec(), &[4]); + /// rb.drain_to_slice(&mut slice[..1]); + /// assert_eq!(&slice, &[4, 2, 3]); + /// assert_eq!(&rb.to_vec(), &[]); + /// ``` + fn drain_to_slice(&mut self, dst: &mut [T]) + where + T: Copy, + { + unsafe { Self::ptr_drain_to_slice(self, dst) } + } } mod iter { @@ -608,10 +680,10 @@ macro_rules! impl_ringbuffer_ext { let len = Self::ptr_len(rb); let dst_len = dst.len(); assert!( - (offset == 0 && len == 0) || offset < len, + offset < len || (offset == len && dst_len == 0), "offset ({offset}) is out of bounds for the current buffer length ({len})" ); - assert!(len - offset == dst_len, "destination slice length ({dst_len}) doesn't match buffer length ({len}) when considering the specified offset ({offset})"); + assert!(len - offset >= dst_len, "destination slice length ({dst_len}) greater than buffer length ({len}) when considering the specified offset ({offset})"); if dst_len == 0 { return; @@ -651,10 +723,10 @@ macro_rules! impl_ringbuffer_ext { let len = Self::ptr_len(rb); let src_len = src.len(); assert!( - (offset == 0 && len == 0) || offset < len, + offset < len || (offset == len && src_len == 0), "offset ({offset}) is out of bounds for the current buffer length ({len})" ); - assert!(len - offset == src_len, "source slice length ({src_len}) doesn't match buffer length ({len}) when considering the specified offset ({offset})"); + assert!(len - offset >= src_len, "source slice length ({src_len}) greater than buffer length ({len}) when considering the specified offset ({offset})"); if src_len == 0 { return; @@ -689,5 +761,30 @@ macro_rules! impl_ringbuffer_ext { .copy_from_slice(&src[size - from_idx..]); } } + + unsafe fn ptr_extend_from_slice(rb: *mut Self, src: &[T]) + where + T: Copy, + { + let len = Self::ptr_len(rb); + let capacity = Self::ptr_capacity(rb); + (*rb).$writeptr += src.len(); + (*rb).$readptr = (*rb).$writeptr - (len + src.len()).min(capacity); + let final_len = Self::ptr_len(rb); + + let src_offset = src.len().saturating_sub(capacity); + Self::ptr_copy_from_slice(rb, final_len - (src.len() - src_offset), &src[src_offset..]); + } + + unsafe fn ptr_drain_to_slice(rb: *mut Self, dst: &mut [T]) + where + T: Copy, + { + let len = Self::ptr_len(rb); + let truncated_dst_len = dst.len().min(len); + Self::ptr_copy_to_slice(rb, 0, &mut dst[..truncated_dst_len]); + + (*rb).$readptr += truncated_dst_len; + } }; } diff --git a/src/with_alloc/vecdeque.rs b/src/with_alloc/vecdeque.rs index c195150..f5360ff 100644 --- a/src/with_alloc/vecdeque.rs +++ b/src/with_alloc/vecdeque.rs @@ -263,10 +263,10 @@ unsafe impl RingBuffer for GrowableAllocRingBuffer { let len = Self::ptr_len(rb); let dst_len = dst.len(); assert!( - (offset == 0 && len == 0) || offset < len, + offset < len || (offset == len && dst_len == 0), "offset ({offset}) is out of bounds for the current buffer length ({len})" ); - assert!(len - offset == dst_len, "destination slice length ({dst_len}) doesn't match buffer length ({len}) when considering the specified offset ({offset})"); + assert!(len - offset >= dst_len, "destination slice length ({dst_len}) greater than buffer length ({len}) when considering the specified offset ({offset})"); if dst_len == 0 { return; @@ -277,13 +277,14 @@ unsafe impl RingBuffer for GrowableAllocRingBuffer { if offset < first_len { let n_in_first = first_len - offset; - dst[..n_in_first].copy_from_slice(&front[offset..]); + dst[..n_in_first.min(dst_len)] + .copy_from_slice(&front[offset..][..n_in_first.min(dst_len)]); if n_in_first < dst_len { dst[n_in_first..].copy_from_slice(&back[..dst_len - n_in_first]); } } else { - dst.copy_from_slice(&back[offset - first_len..]); + dst.copy_from_slice(&back[offset - first_len..][..dst_len]); } } @@ -294,10 +295,10 @@ unsafe impl RingBuffer for GrowableAllocRingBuffer { let len = Self::ptr_len(rb); let src_len = src.len(); assert!( - (offset == 0 && len == 0) || offset < len, + offset < len || (offset == len && src_len == 0), "offset ({offset}) is out of bounds for the current buffer length ({len})" ); - assert!(len - offset == src_len, "source slice length ({src_len}) doesn't match buffer length ({len}) when considering the specified offset ({offset})"); + assert!(len - offset >= src_len, "source slice length ({src_len}) greater than buffer length ({len}) when considering the specified offset ({offset})"); if src_len == 0 { return; @@ -308,15 +309,37 @@ unsafe impl RingBuffer for GrowableAllocRingBuffer { if offset < first_len { let n_in_first = first_len - offset; - front[offset..].copy_from_slice(&src[..n_in_first]); + front[offset..][..n_in_first.min(src_len)] + .copy_from_slice(&src[..n_in_first.min(src_len)]); if n_in_first < src_len { back[..src_len - n_in_first].copy_from_slice(&src[n_in_first..]); } } else { - back[offset - first_len..].copy_from_slice(src); + back[offset - first_len..][..src_len].copy_from_slice(src); } } + + unsafe fn ptr_extend_from_slice(rb: *mut Self, src: &[T]) + where + T: Copy, + { + (*rb).0.extend(src.iter()); + } + + unsafe fn ptr_drain_to_slice(rb: *mut Self, dst: &mut [T]) + where + T: Copy, + { + let len = Self::ptr_len(rb); + let truncated_dst_len = dst.len().min(len); + + dst.iter_mut() + .zip((*rb).0.drain(0..truncated_dst_len)) + .for_each(|(dst, src)| { + *dst = src; + }); + } } impl Extend for GrowableAllocRingBuffer {