Skip to content

Commit 3c23263

Browse files
committed
refactor: reuse copy_{from,to}_slice for {extend_from,drain_to}_slice + loosen slice size checks
1 parent 1584c06 commit 3c23263

File tree

3 files changed

+165
-100
lines changed

3 files changed

+165
-100
lines changed

src/lib.rs

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,47 @@ mod tests {
16961696
});
16971697
}
16981698

1699+
#[test]
1700+
fn test_copy_from_slice_partial() {
1701+
macro_rules! test_concrete {
1702+
($rb_init: expr) => {
1703+
let mut rb = $rb_init(&[3, 2, 1]);
1704+
assert_eq!(rb.capacity(), 7);
1705+
// we have some space left
1706+
assert!(rb.len() < rb.capacity());
1707+
1708+
// copy preserves length
1709+
rb.copy_from_slice(0, &[1, 2]);
1710+
assert_eq!(rb.to_vec(), alloc::vec![1, 2, 1]);
1711+
1712+
let _ = rb.enqueue(4);
1713+
let _ = rb.enqueue(5);
1714+
let _ = rb.enqueue(6);
1715+
assert_eq!(rb.to_vec(), alloc::vec![1, 2, 1, 4, 5, 6]);
1716+
1717+
// still preserving length
1718+
rb.copy_from_slice(1, &[5, 4, 3, 2, 1]);
1719+
assert_eq!(rb.to_vec(), alloc::vec![1, 5, 4, 3, 2, 1]);
1720+
};
1721+
}
1722+
1723+
test_concrete!(|values: &[i32]| {
1724+
let mut rb = ConstGenericRingBuffer::<_, 7>::new();
1725+
rb.extend(values.iter().copied());
1726+
rb
1727+
});
1728+
test_concrete!(|values: &[i32]| {
1729+
let mut rb = GrowableAllocRingBuffer::<_>::with_capacity(7);
1730+
rb.extend(values.iter().copied());
1731+
rb
1732+
});
1733+
test_concrete!(|values: &[i32]| {
1734+
let mut rb = AllocRingBuffer::<_>::new(7);
1735+
rb.extend(values.iter().copied());
1736+
rb
1737+
});
1738+
}
1739+
16991740
#[test]
17001741
fn test_copy_from_slice_empty() {
17011742
macro_rules! test_concrete {
@@ -1907,6 +1948,60 @@ mod tests {
19071948
});
19081949
}
19091950

1951+
#[test]
1952+
fn test_copy_to_slice_partial() {
1953+
macro_rules! test_concrete {
1954+
($rb_init: expr) => {
1955+
let mut rb = $rb_init(&[1, 2, 3]);
1956+
assert_eq!(rb.capacity(), 7);
1957+
// we have some space left
1958+
assert!(rb.len() < rb.capacity());
1959+
1960+
// copy based on length
1961+
let mut slice = [0; 2];
1962+
rb.copy_to_slice(0, &mut slice);
1963+
assert_eq!(slice.as_slice(), &[1, 2]);
1964+
1965+
let _ = rb.enqueue(4);
1966+
let _ = rb.enqueue(5);
1967+
let _ = rb.enqueue(6);
1968+
// still based on length
1969+
let mut slice = [0; 5];
1970+
rb.copy_to_slice(0, &mut slice);
1971+
assert_eq!(slice.as_slice(), &[1, 2, 3, 4, 5]);
1972+
1973+
// making sure the read/write ptrs have traversed the ring
1974+
for i in 0..6 {
1975+
let _ = rb.enqueue(i + 1);
1976+
let _ = rb.dequeue();
1977+
}
1978+
1979+
// sanity check
1980+
assert_eq!(rb.to_vec(), alloc::vec![1, 2, 3, 4, 5, 6]);
1981+
// copy again
1982+
let mut slice = [0; 5];
1983+
rb.copy_to_slice(1, &mut slice);
1984+
assert_eq!(slice.as_slice(), &[2, 3, 4, 5, 6]);
1985+
};
1986+
}
1987+
1988+
test_concrete!(|values: &[i32]| {
1989+
let mut rb = ConstGenericRingBuffer::<_, 7>::new();
1990+
rb.extend(values.iter().copied());
1991+
rb
1992+
});
1993+
test_concrete!(|values: &[i32]| {
1994+
let mut rb = GrowableAllocRingBuffer::<_>::with_capacity(7);
1995+
rb.extend(values.iter().copied());
1996+
rb
1997+
});
1998+
test_concrete!(|values: &[i32]| {
1999+
let mut rb = AllocRingBuffer::<_>::new(7);
2000+
rb.extend(values.iter().copied());
2001+
rb
2002+
});
2003+
}
2004+
19102005
#[test]
19112006
fn test_copy_to_slice_empty() {
19122007
macro_rules! test_concrete {
@@ -2124,6 +2219,20 @@ mod tests {
21242219
test_concrete!(|| AllocRingBuffer::<i32>::new(3));
21252220
}
21262221

2222+
#[test]
2223+
fn test_extend_from_slice_partial() {
2224+
macro_rules! test_concrete {
2225+
($rb_init: expr) => {
2226+
let mut rb = $rb_init();
2227+
rb.extend_from_slice(&[9; 2]);
2228+
assert_eq!(&rb.to_vec(), &[9, 9]);
2229+
};
2230+
}
2231+
2232+
test_concrete!(ConstGenericRingBuffer::<i32, 3>::new);
2233+
test_concrete!(|| AllocRingBuffer::<i32>::new(3));
2234+
}
2235+
21272236
#[test]
21282237
fn test_extend_from_slice_empty() {
21292238
macro_rules! test_concrete {
@@ -2218,6 +2327,32 @@ mod tests {
22182327
test_concrete!(|| AllocRingBuffer::<i32>::new(3));
22192328
}
22202329

2330+
#[test]
2331+
fn test_drain_to_slice_partial() {
2332+
macro_rules! test_concrete {
2333+
($rb_init: expr, $growable: expr) => {
2334+
let mut rb = $rb_init();
2335+
rb.extend_from_slice(&[9; 3]);
2336+
let mut slice = [0; 2];
2337+
rb.drain_to_slice(&mut slice);
2338+
assert_eq!(&slice, &[9; 2]);
2339+
rb.extend_from_slice(&[1, 2, 3]);
2340+
rb.drain_to_slice(&mut slice);
2341+
if $growable {
2342+
assert_eq!(&slice, &[9, 1]);
2343+
assert_eq!(&rb.to_vec(), &[2, 3]);
2344+
} else {
2345+
assert_eq!(&slice, &[1, 2]);
2346+
assert_eq!(&rb.to_vec(), &[3]);
2347+
}
2348+
};
2349+
}
2350+
2351+
test_concrete!(ConstGenericRingBuffer::<i32, 3>::new, false);
2352+
test_concrete!(|| GrowableAllocRingBuffer::<i32>::with_capacity(3), true);
2353+
test_concrete!(|| AllocRingBuffer::<i32>::new(3), false);
2354+
}
2355+
22212356
#[test]
22222357
fn test_drain_to_slice_empty() {
22232358
macro_rules! test_concrete {
@@ -2269,13 +2404,16 @@ mod tests {
22692404
}
22702405

22712406
#[test]
2272-
#[should_panic = "destination slice length (10) greater than buffer length (0)"]
22732407
fn test_drain_to_slice_longer_than_len() {
22742408
macro_rules! test_concrete {
22752409
($rb_init: expr) => {
22762410
let mut rb = $rb_init();
22772411
let mut slice = [0; 10];
22782412
rb.drain_to_slice(&mut slice);
2413+
assert_eq!(slice, [0; 10]);
2414+
rb.extend_from_slice(&[1, 2]);
2415+
rb.drain_to_slice(&mut slice);
2416+
assert_eq!(slice, [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]);
22792417
};
22802418
}
22812419

src/ringbuffer_trait.rs

Lines changed: 16 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ pub unsafe trait RingBuffer<T>:
257257
/// Efficiently copy items from the ringbuffer to a target slice.
258258
///
259259
/// # Panics
260-
/// Panics if the buffer length minus the offset is NOT equal to `target.len()`.
260+
/// Panics if the buffer length minus the offset is smaller than `dst.len()`.
261261
///
262262
/// # Safety
263263
/// ONLY SAFE WHEN self is a *const to to an implementor of `RingBuffer`
@@ -268,7 +268,7 @@ pub unsafe trait RingBuffer<T>:
268268
/// Efficiently copy items from the ringbuffer to a target slice.
269269
///
270270
/// # Panics
271-
/// Panics if the buffer length minus the offset is NOT equal to `target.len()`.
271+
/// Panics if the buffer length minus the offset is smaller than `dst.len()`.
272272
fn copy_to_slice(&self, offset: usize, dst: &mut [T])
273273
where
274274
T: Copy,
@@ -277,8 +277,9 @@ pub unsafe trait RingBuffer<T>:
277277
}
278278

279279
/// Efficiently copy items from a slice to the ringbuffer.
280+
///
280281
/// # Panics
281-
/// Panics if the buffer length minus the offset is NOT equal to `source.len()`.
282+
/// Panics if the buffer length minus the offset is smaller than `src.len()`.
282283
///
283284
/// # Safety
284285
/// ONLY SAFE WHEN self is a *mut to to an implementor of `RingBuffer`
@@ -289,7 +290,7 @@ pub unsafe trait RingBuffer<T>:
289290
/// Efficiently copy items from a slice to the ringbuffer.
290291
///
291292
/// # Panics
292-
/// Panics if the buffer length minus the offset is NOT equal to `source.len()`.
293+
/// Panics if the buffer length minus the offset is smaller than `src.len()`.
293294
fn copy_from_slice(&mut self, offset: usize, src: &[T])
294295
where
295296
T: Copy,
@@ -688,7 +689,7 @@ macro_rules! impl_ringbuffer_ext {
688689
(offset == 0 && len == 0) || offset < len,
689690
"offset ({offset}) is out of bounds for the current buffer length ({len})"
690691
);
691-
assert!(len - offset == dst_len, "destination slice length ({dst_len}) doesn't match buffer length ({len}) when considering the specified offset ({offset})");
692+
assert!(len - offset >= dst_len, "destination slice length ({dst_len}) greater than buffer length ({len}) when considering the specified offset ({offset})");
692693

693694
if dst_len == 0 {
694695
return;
@@ -731,7 +732,7 @@ macro_rules! impl_ringbuffer_ext {
731732
(offset == 0 && len == 0) || offset < len,
732733
"offset ({offset}) is out of bounds for the current buffer length ({len})"
733734
);
734-
assert!(len - offset == src_len, "source slice length ({src_len}) doesn't match buffer length ({len}) when considering the specified offset ({offset})");
735+
assert!(len - offset >= src_len, "source slice length ({src_len}) greater than buffer length ({len}) when considering the specified offset ({offset})");
735736

736737
if src_len == 0 {
737738
return;
@@ -771,97 +772,25 @@ macro_rules! impl_ringbuffer_ext {
771772
where
772773
T: Copy,
773774
{
775+
let len = Self::ptr_len(rb);
774776
let capacity = Self::ptr_capacity(rb);
777+
(*rb).$writeptr += src.len();
778+
(*rb).$readptr = (*rb).$writeptr - (len + src.len()).min(capacity);
779+
let final_len = Self::ptr_len(rb);
775780

776-
if src.len() == 0 {
777-
return;
778-
}
779-
780-
let truncated_src = if src.len() > capacity {
781-
&src[src.len() - capacity..]
782-
} else {
783-
src
784-
};
785-
786-
let truncated_src_len = truncated_src.len();
787-
788-
let base: *mut T = $get_base_mut_ptr(rb);
789-
let size = Self::ptr_buffer_size(rb);
790-
791-
let from_idx = $mask(size, (*rb).$writeptr);
792-
let to_idx = $mask(size, (*rb).$writeptr + truncated_src_len);
793-
794-
if from_idx < to_idx {
795-
unsafe {
796-
// SAFETY: index has been modulo-ed to be within range
797-
// to be within bounds
798-
core::slice::from_raw_parts_mut(base.add(from_idx), truncated_src_len)
799-
}
800-
.copy_from_slice(truncated_src);
801-
} else {
802-
unsafe {
803-
// SAFETY: index has been modulo-ed to be within range
804-
// to be within bounds
805-
core::slice::from_raw_parts_mut(base.add(from_idx), size - from_idx)
806-
}
807-
.copy_from_slice(&truncated_src[..size - from_idx]);
808-
unsafe {
809-
// SAFETY: index has been modulo-ed to be within range
810-
// to be within bounds
811-
core::slice::from_raw_parts_mut(base, to_idx)
812-
}
813-
.copy_from_slice(&truncated_src[size - from_idx..]);
814-
}
815-
816-
817-
let initially_available = capacity - Self::ptr_len(rb);
818-
if truncated_src_len > initially_available {
819-
(*rb).$readptr += truncated_src_len - initially_available;
820-
}
821-
(*rb).$writeptr += truncated_src_len;
781+
let src_offset = src.len().saturating_sub(capacity);
782+
Self::ptr_copy_from_slice(rb, final_len - (src.len() - src_offset), &src[src_offset..]);
822783
}
823784

824785
unsafe fn ptr_drain_to_slice(rb: *mut Self, dst: &mut [T])
825786
where
826787
T: Copy,
827788
{
828-
let dst_len = dst.len();
829789
let len = Self::ptr_len(rb);
830-
assert!(
831-
dst_len <= len,
832-
"destination slice length ({dst_len}) greater than buffer length ({len})"
833-
);
834-
835-
if dst_len == 0 {
836-
return;
837-
}
838-
839-
let base: *mut T = $get_base_mut_ptr(rb);
840-
let size = Self::ptr_buffer_size(rb);
841-
842-
let from_idx = $mask(size, (*rb).$readptr);
843-
let to_idx = $mask(size, (*rb).$readptr + dst_len);
844-
845-
if from_idx < to_idx {
846-
dst.copy_from_slice(unsafe {
847-
// SAFETY: index has been modulo-ed to be within range
848-
// to be within bounds
849-
core::slice::from_raw_parts(base.add(from_idx), dst_len)
850-
});
851-
} else {
852-
dst[..size - from_idx].copy_from_slice(unsafe {
853-
// SAFETY: index has been modulo-ed to be within range
854-
// to be within bounds
855-
core::slice::from_raw_parts(base.add(from_idx), size - from_idx)
856-
});
857-
dst[size - from_idx..].copy_from_slice(unsafe {
858-
// SAFETY: index has been modulo-ed to be within range
859-
// to be within bounds
860-
core::slice::from_raw_parts(base, to_idx)
861-
});
862-
}
790+
let truncated_dst_len = dst.len().min(len);
791+
Self::ptr_copy_to_slice(rb, 0, &mut dst[..truncated_dst_len]);
863792

864-
(*rb).$readptr += dst_len;
793+
(*rb).$readptr += truncated_dst_len;
865794
}
866795
};
867796
}

0 commit comments

Comments
 (0)