Skip to content

Commit d71d0ea

Browse files
committed
More Chunk methods on RingBuffer.
1 parent 07b7d08 commit d71d0ea

File tree

4 files changed

+348
-15
lines changed

4 files changed

+348
-15
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project
66
adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### ADDED
11+
12+
- The `insert_from` and `insert_ordered` methods recently added to `Chunk` have now also been
13+
added to `RingBuffer`.
14+
- `RingBuffer`'s `Slice` and `SliceMut` now also have the three `binary_search` methods regular
15+
slices have.
16+
- `RingBuffer`, `Slice` and `SliceMut` now have unsafe `get_unchecked` and `get_unchecked_mut`
17+
methods.
18+
- `PartialEq` implementations allowing you to compare `RingBuffer`s, `Slice`s and `SliceMut`s
19+
interchangeably have been added.
20+
821
## [0.5.3] - 2020-03-11
922

1023
### FIXED

fuzz/fuzz_targets/ring_buffer.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ where
4343
DrainFromBack(Construct<A>, usize),
4444
Set(usize, A),
4545
Insert(usize, A),
46+
InsertFrom(Vec<A>, usize),
47+
InsertOrdered(A),
4648
Remove(usize),
4749
Drain,
4850
Clear,
@@ -235,6 +237,27 @@ fuzz_target!(|input: (Construct<u32>, Vec<Action<u32>>)| {
235237
guide.insert(index, value);
236238
}
237239
}
240+
Action::InsertFrom(values, index) => {
241+
if index > chunk.len() || chunk.len() + values.len() > capacity {
242+
assert_panic(|| chunk.insert_from(index, values));
243+
} else {
244+
chunk.insert_from(index, values.clone());
245+
for value in values.into_iter().rev() {
246+
guide.insert(index, value);
247+
}
248+
}
249+
}
250+
Action::InsertOrdered(value) => {
251+
if chunk.is_full() {
252+
assert_panic(|| chunk.insert_ordered(value));
253+
} else {
254+
chunk.insert_ordered(value);
255+
match guide.binary_search(&value) {
256+
Ok(index) => guide.insert(index, value),
257+
Err(index) => guide.insert(index, value),
258+
}
259+
}
260+
}
238261
Action::Remove(index) => {
239262
if index >= chunk.len() {
240263
assert_panic(|| chunk.remove(index));

src/ring_buffer/mod.rs

Lines changed: 138 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -372,20 +372,40 @@ where
372372
if index >= self.len() {
373373
None
374374
} else {
375-
Some(unsafe { &*self.ptr(self.raw(index)) })
375+
Some(unsafe { self.get_unchecked(index) })
376376
}
377377
}
378378

379+
/// Get an unchecked reference to the value at the given index.
380+
///
381+
/// # Safety
382+
///
383+
/// You must ensure the index is not out of bounds.
384+
#[must_use]
385+
pub unsafe fn get_unchecked(&self, index: usize) -> &A {
386+
&*self.ptr(self.raw(index))
387+
}
388+
379389
/// Get a mutable reference to the value at a given index.
380390
#[must_use]
381391
pub fn get_mut(&mut self, index: usize) -> Option<&mut A> {
382392
if index >= self.len() {
383393
None
384394
} else {
385-
Some(unsafe { &mut *self.mut_ptr(self.raw(index)) })
395+
Some(unsafe { self.get_unchecked_mut(index) })
386396
}
387397
}
388398

399+
/// Get an unchecked mutable reference to the value at the given index.
400+
///
401+
/// # Safety
402+
///
403+
/// You must ensure the index is not out of bounds.
404+
#[must_use]
405+
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut A {
406+
&mut *self.mut_ptr(self.raw(index))
407+
}
408+
389409
/// Get a reference to the first value in the buffer.
390410
#[inline]
391411
#[must_use]
@@ -647,6 +667,94 @@ where
647667
unsafe { self.force_write(self.raw(index), value) };
648668
}
649669

670+
/// Insert a new value into the buffer in sorted order.
671+
///
672+
/// This assumes every element of the buffer is already in sorted order.
673+
/// If not, the value will still be inserted but the ordering is not
674+
/// guaranteed.
675+
///
676+
/// Time: O(log n) to find the insert position, then O(n) for the number
677+
/// of elements shifted.
678+
///
679+
/// # Examples
680+
///
681+
/// ```rust
682+
/// # use std::iter::FromIterator;
683+
/// # use sized_chunks::Chunk;
684+
/// # use typenum::U64;
685+
/// let mut chunk = Chunk::<i32, U64>::from_iter(0..5);
686+
/// chunk.insert_ordered(3);
687+
/// assert_eq!(&[0, 1, 2, 3, 3, 4], chunk.as_slice());
688+
/// ```
689+
pub fn insert_ordered(&mut self, value: A)
690+
where
691+
A: Ord,
692+
{
693+
if self.is_full() {
694+
panic!("Chunk::insert: chunk is full");
695+
}
696+
match self.slice(..).binary_search(&value) {
697+
Ok(index) => self.insert(index, value),
698+
Err(index) => self.insert(index, value),
699+
}
700+
}
701+
702+
/// Insert multiple values at index `index`, shifting all the following values
703+
/// to the right.
704+
///
705+
/// Panics if the index is out of bounds or the chunk doesn't have room for
706+
/// all the values.
707+
///
708+
/// Time: O(m+n) where m is the number of elements inserted and n is the number
709+
/// of elements following the insertion index. Calling `insert`
710+
/// repeatedly would be O(m*n).
711+
pub fn insert_from<Iterable, I>(&mut self, index: usize, iter: Iterable)
712+
where
713+
Iterable: IntoIterator<Item = A, IntoIter = I>,
714+
I: ExactSizeIterator<Item = A>,
715+
{
716+
let iter = iter.into_iter();
717+
let insert_size = iter.len();
718+
if self.len() + insert_size > Self::CAPACITY {
719+
panic!(
720+
"Chunk::insert_from: chunk cannot fit {} elements",
721+
insert_size
722+
);
723+
}
724+
if index > self.len() {
725+
panic!("Chunk::insert_from: index out of bounds");
726+
}
727+
if index == self.len() {
728+
self.extend(iter);
729+
return;
730+
}
731+
let right_count = self.len() - index;
732+
// Check which side has fewer elements to shift.
733+
if right_count < index {
734+
// Shift to the right.
735+
let mut i = self.raw(self.len() - 1);
736+
let target = self.raw(index);
737+
while i != target {
738+
unsafe { self.force_write(i + insert_size, self.force_read(i)) };
739+
i -= 1;
740+
}
741+
unsafe { self.force_write(target + insert_size, self.force_read(target)) };
742+
self.length += insert_size;
743+
} else {
744+
// Shift to the left.
745+
self.origin -= insert_size;
746+
self.length += insert_size;
747+
for i in self.range().take(index) {
748+
unsafe { self.force_write(i, self.force_read(i + insert_size)) };
749+
}
750+
}
751+
let mut index = self.raw(index);
752+
for value in iter {
753+
unsafe { self.force_write(index, value) };
754+
index += 1;
755+
}
756+
}
757+
650758
/// Remove the value at index `index`, shifting all the following values to
651759
/// the left.
652760
///
@@ -765,20 +873,40 @@ impl<A: PartialEq, N: ChunkLength<A>> PartialEq for RingBuffer<A, N> {
765873
}
766874
}
767875

768-
impl<A, N, Slice> PartialEq<Slice> for RingBuffer<A, N>
876+
impl<A, N, PrimSlice> PartialEq<PrimSlice> for RingBuffer<A, N>
769877
where
770-
Slice: Borrow<[A]>,
878+
PrimSlice: Borrow<[A]>,
771879
A: PartialEq,
772880
N: ChunkLength<A>,
773881
{
774882
#[inline]
775883
#[must_use]
776-
fn eq(&self, other: &Slice) -> bool {
884+
fn eq(&self, other: &PrimSlice) -> bool {
777885
let other = other.borrow();
778886
self.len() == other.len() && self.iter().eq(other.iter())
779887
}
780888
}
781889

890+
impl<A, N> PartialEq<Slice<'_, A, N>> for RingBuffer<A, N>
891+
where
892+
A: PartialEq,
893+
N: ChunkLength<A>,
894+
{
895+
fn eq(&self, other: &Slice<'_, A, N>) -> bool {
896+
self.len() == other.len() && self.iter().eq(other.iter())
897+
}
898+
}
899+
900+
impl<A, N> PartialEq<SliceMut<'_, A, N>> for RingBuffer<A, N>
901+
where
902+
A: PartialEq,
903+
N: ChunkLength<A>,
904+
{
905+
fn eq(&self, other: &SliceMut<'_, A, N>) -> bool {
906+
self.len() == other.len() && self.iter().eq(other.iter())
907+
}
908+
}
909+
782910
impl<A: Eq, N: ChunkLength<A>> Eq for RingBuffer<A, N> {}
783911

784912
impl<A: PartialOrd, N: ChunkLength<A>> PartialOrd for RingBuffer<A, N> {
@@ -910,6 +1038,11 @@ impl<'a, A, N: ChunkLength<A>> IntoIterator for &'a mut RingBuffer<A, N> {
9101038
mod test {
9111039
use super::*;
9121040

1041+
#[test]
1042+
fn validity_invariant() {
1043+
assert!(Some(RingBuffer::<Box<()>>::new()).is_some());
1044+
}
1045+
9131046
#[test]
9141047
fn is_full() {
9151048
let mut chunk = RingBuffer::<_, U64>::new();

0 commit comments

Comments
 (0)