Skip to content

Commit

Permalink
Merge branch 'main' of github.com:NULLx76/ringbuffer into benchmarks-…
Browse files Browse the repository at this point in the history
…vs-others
  • Loading branch information
jdonszelmann committed Jun 9, 2023
2 parents 0bb68be + 3b85e63 commit 9987b1d
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 33 deletions.
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,25 @@ The ringbuffer crate provides safe fixed size circular buffers (ringbuffers) in

Implementations for three kinds of ringbuffers, with a mostly similar API are provided:

| type | description |
|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `AllocRingBuffer` | Ringbuffer allocated on the heap at runtime. This ringbuffer is still fixed size. This requires alloc and the alloc feature. |
| `GrowableAllocRingBuffer` | Ringbuffer allocated on the heap at runtime. This ringbuffer can grow in size, and is implemented as an `alloc::VecDeque` internally. This requires alloc and the alloc feature. |
| `ConstGenericRingBuffer` | Ringbuffer which uses const generics to allocate on the stack. |
| type | description |
|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`AllocRingBuffer`][1] | Ringbuffer allocated on the heap at runtime. This ringbuffer is still fixed size. This requires the alloc feature. |
| [`GrowableAllocRingBuffer`][2] | Ringbuffer allocated on the heap at runtime. This ringbuffer can grow in size, and is implemented as an `alloc::VecDeque` internally. This requires the alloc feature. |
| [`ConstGenericRingBuffer`][3] | Ringbuffer which uses const generics to allocate on the stack. |

All of these ringbuffers also implement the RingBuffer trait for their shared API surface.
All of these ringbuffers also implement the [RingBuffer][4] trait for their shared API surface.

[1]: https://docs.rs/ringbuffer/latest/ringbuffer/struct.AllocRingBuffer.html
[2]: https://docs.rs/ringbuffer/latest/ringbuffer/struct.GrowableAllocRingBuffer.html
[3]: https://docs.rs/ringbuffer/latest/ringbuffer/struct.ConstGenericRingBuffer.html
[4]: https://docs.rs/ringbuffer/latest/ringbuffer/trait.RingBuffer.html

MSRV: Rust 1.59

# Usage

```rust
use ringbuffer::{AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferWrite};
use ringbuffer::{AllocRingBuffer, RingBuffer};

fn main() {
let mut buffer = AllocRingBuffer::with_capacity(2);
Expand Down
1 change: 0 additions & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![cfg(not(tarpaulin))]
use crate::comparison::comparison_benches;
use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
use ringbuffer::{AllocRingBuffer, ConstGenericRingBuffer, RingBuffer};
Expand Down
56 changes: 56 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ mod tests {
use std::vec;
use std::vec::Vec;

use crate::ringbuffer_trait::{RingBufferIterator, RingBufferMutIterator};
use crate::{AllocRingBuffer, ConstGenericRingBuffer, GrowableAllocRingBuffer, RingBuffer};

#[test]
Expand Down Expand Up @@ -216,6 +217,37 @@ mod tests {
test_iter(ConstGenericRingBuffer::<i32, 8>::new());
}

#[test]
fn run_test_iter_ref() {
fn test_iter<B>(mut b: B)
where
B: RingBuffer<i32>,
for<'a> &'a B: IntoIterator<Item = &'a i32, IntoIter = RingBufferIterator<'a, i32, B>>,
{
b.push(1);
b.push(2);
b.push(3);
b.push(4);
b.push(5);
b.push(6);
b.push(7);

let mut iter = (&b).into_iter();
assert_eq!(&1, iter.next().unwrap());
assert_eq!(&7, iter.next_back().unwrap());
assert_eq!(&2, iter.next().unwrap());
assert_eq!(&3, iter.next().unwrap());
assert_eq!(&6, iter.next_back().unwrap());
assert_eq!(&5, iter.next_back().unwrap());
assert_eq!(&4, iter.next().unwrap());
assert_eq!(None, iter.next());
}

test_iter(AllocRingBuffer::new(8));
test_iter(GrowableAllocRingBuffer::with_capacity(8));
test_iter(ConstGenericRingBuffer::<i32, 8>::new());
}

#[test]
fn run_test_into_iter() {
fn test_iter(mut b: impl RingBuffer<i32>) {
Expand Down Expand Up @@ -339,6 +371,30 @@ mod tests {
test_iter_mut(ConstGenericRingBuffer::<i32, 8>::new());
}

#[test]
fn run_test_iter_mut_ref() {
fn test_iter_mut<B>(mut b: B)
where
B: RingBuffer<i32>,
for<'a> &'a mut B:
IntoIterator<Item = &'a mut i32, IntoIter = RingBufferMutIterator<'a, i32, B>>,
{
b.push(1);
b.push(2);
b.push(3);

for el in &mut b {
*el += 1;
}

assert_eq!(vec![2, 3, 4], b.to_vec())
}

test_iter_mut(AllocRingBuffer::new(8));
test_iter_mut(GrowableAllocRingBuffer::with_capacity(8));
test_iter_mut(ConstGenericRingBuffer::<i32, 8>::new());
}

#[test]
fn test_iter_mut_wrap() {
fn run_test_iter_mut_wrap(mut b: impl RingBuffer<i32>) {
Expand Down
16 changes: 4 additions & 12 deletions src/ringbuffer_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ pub unsafe trait RingBuffer<T>:

/// dequeues the top item off the queue, but does not return it. Instead it is dropped.
/// If the ringbuffer is empty, this function is a nop.
fn skip(&mut self);
#[inline]
fn skip(&mut self) {
let _ = self.dequeue();
}

/// Returns an iterator over the elements in the ringbuffer,
/// dequeueing elements as they are iterated over.
Expand Down Expand Up @@ -403,17 +406,6 @@ pub use iter::{
RingBufferDrainingIterator, RingBufferIntoIterator, RingBufferIterator, RingBufferMutIterator,
};

/// Implement various functions on implementors of [`RingBuffer`].
/// This is to avoid duplicate code.
macro_rules! impl_ringbuffer_read {
() => {
#[inline]
fn skip(&mut self) {
let _ = self.dequeue().map(drop);
}
};
}

/// Implement various functions on implementors of [`RingBuffer`].
/// This is to avoid duplicate code.
macro_rules! impl_ringbuffer {
Expand Down
24 changes: 21 additions & 3 deletions src/with_alloc/alloc_ringbuffer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::ops::{Index, IndexMut};

use crate::ringbuffer_trait::{RingBuffer, RingBufferIntoIterator};
use crate::ringbuffer_trait::{
RingBuffer, RingBufferIntoIterator, RingBufferIterator, RingBufferMutIterator,
};

extern crate alloc;

Expand Down Expand Up @@ -231,6 +233,24 @@ impl<T, SIZE: RingbufferSize> IntoIterator for AllocRingBuffer<T, SIZE> {
}
}

impl<'a, T, SIZE: RingbufferSize> IntoIterator for &'a AllocRingBuffer<T, SIZE> {
type Item = &'a T;
type IntoIter = RingBufferIterator<'a, T, AllocRingBuffer<T, SIZE>>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

impl<'a, T, SIZE: RingbufferSize> IntoIterator for &'a mut AllocRingBuffer<T, SIZE> {
type Item = &'a mut T;
type IntoIter = RingBufferMutIterator<'a, T, AllocRingBuffer<T, SIZE>>;

fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}

impl<T, SIZE: RingbufferSize> Extend<T> for AllocRingBuffer<T, SIZE> {
fn extend<A: IntoIterator<Item = T>>(&mut self, iter: A) {
let iter = iter.into_iter();
Expand Down Expand Up @@ -279,8 +299,6 @@ unsafe impl<T, SIZE: RingbufferSize> RingBuffer<T> for AllocRingBuffer<T, SIZE>
self.writeptr += 1;
}

impl_ringbuffer_read!();

fn dequeue(&mut self) -> Option<T> {
if self.is_empty() {
None
Expand Down
24 changes: 20 additions & 4 deletions src/with_alloc/vecdeque.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::ringbuffer_trait::RingBufferIntoIterator;
use crate::ringbuffer_trait::{RingBufferIntoIterator, RingBufferIterator, RingBufferMutIterator};
use crate::with_alloc::alloc_ringbuffer::RingbufferSize;
use crate::{AllocRingBuffer, RingBuffer};
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
/// Wrapper of the built-in [`VecDeque`] struct.
///
/// The reason this is a wrapper, is that we want `RingBuffers` to implement `Index<isize>`,
/// which we cannot do for remote types like `VecDeque`
Expand Down Expand Up @@ -151,6 +151,24 @@ impl<T> IntoIterator for GrowableAllocRingBuffer<T> {
}
}

impl<'a, T> IntoIterator for &'a GrowableAllocRingBuffer<T> {
type Item = &'a T;
type IntoIter = RingBufferIterator<'a, T, GrowableAllocRingBuffer<T>>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

impl<'a, T> IntoIterator for &'a mut GrowableAllocRingBuffer<T> {
type Item = &'a mut T;
type IntoIter = RingBufferMutIterator<'a, T, GrowableAllocRingBuffer<T>>;

fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}

unsafe impl<T> RingBuffer<T> for GrowableAllocRingBuffer<T> {
unsafe fn ptr_len(rb: *const Self) -> usize {
(*rb).0.len()
Expand All @@ -164,8 +182,6 @@ unsafe impl<T> RingBuffer<T> for GrowableAllocRingBuffer<T> {
self.pop_front()
}

impl_ringbuffer_read!();

fn push(&mut self, value: T) {
self.push_back(value);
}
Expand Down
25 changes: 20 additions & 5 deletions src/with_const_generics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::ringbuffer_trait::RingBufferIntoIterator;
use crate::ringbuffer_trait::{RingBufferIntoIterator, RingBufferIterator, RingBufferMutIterator};
use crate::with_alloc::alloc_ringbuffer::RingbufferSize;
use crate::RingBuffer;
use core::iter::FromIterator;
Expand Down Expand Up @@ -223,6 +223,24 @@ impl<T, const CAP: usize> IntoIterator for ConstGenericRingBuffer<T, CAP> {
}
}

impl<'a, T, const CAP: usize> IntoIterator for &'a ConstGenericRingBuffer<T, CAP> {
type Item = &'a T;
type IntoIter = RingBufferIterator<'a, T, ConstGenericRingBuffer<T, CAP>>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

impl<'a, T, const CAP: usize> IntoIterator for &'a mut ConstGenericRingBuffer<T, CAP> {
type Item = &'a mut T;
type IntoIter = RingBufferMutIterator<'a, T, ConstGenericRingBuffer<T, CAP>>;

fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}

impl<T, const CAP: usize> Extend<T> for ConstGenericRingBuffer<T, CAP> {
fn extend<A: IntoIterator<Item = T>>(&mut self, iter: A) {
let iter = iter.into_iter();
Expand All @@ -235,7 +253,6 @@ impl<T, const CAP: usize> Extend<T> for ConstGenericRingBuffer<T, CAP> {

unsafe impl<T, const CAP: usize> RingBuffer<T> for ConstGenericRingBuffer<T, CAP> {
#[inline]
#[cfg(not(tarpaulin_include))]
unsafe fn ptr_capacity(_: *const Self) -> usize {
CAP
}
Expand Down Expand Up @@ -263,8 +280,6 @@ unsafe impl<T, const CAP: usize> RingBuffer<T> for ConstGenericRingBuffer<T, CAP
self.writeptr += 1;
}

impl_ringbuffer_read!();

fn dequeue(&mut self) -> Option<T> {
if self.is_empty() {
None
Expand Down Expand Up @@ -300,7 +315,7 @@ unsafe impl<T, const CAP: usize> RingBuffer<T> for ConstGenericRingBuffer<T, CAP
impl<T, const CAP: usize> Default for ConstGenericRingBuffer<T, CAP> {
/// Creates a buffer with a capacity specified through the Cap type parameter.
/// # Panics
/// Panics if `CAP` is 0 or not a power of two
/// Panics if `CAP` is 0
#[inline]
fn default() -> Self {
Self::new()
Expand Down
1 change: 0 additions & 1 deletion tests/compiletests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ fn run_mode(mode: &'static str) {

#[test]
#[cfg_attr(miri, ignore)]
#[cfg(not(tarpaulin))]
fn compile_test() {
run_mode("compile-fail");
}

1 comment on commit 9987b1d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.