Skip to content

Commit

Permalink
Refactor modules and add atomic bounded buffer
Browse files Browse the repository at this point in the history
Modules are now more organized with room for more queue and buffer types in the future. All buffers now implement a common set of traits.
  • Loading branch information
sagebind committed May 17, 2018
1 parent a73a0f9 commit 466c8a2
Show file tree
Hide file tree
Showing 7 changed files with 616 additions and 256 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ringtail"
version = "0.1.1"
version = "0.2.0"
description = "Efficient ring buffer for byte buffers, FIFO queues, and SPSC channels"
authors = ["Stephen M. Coakley <[email protected]>"]
license = "MIT"
Expand Down
144 changes: 143 additions & 1 deletion src/arrays.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Provides functions for dynamic array manipulation.
use std::ops::{Index, IndexMut, Range};

/// Allocate an uninitialized array of a given size.
///
Expand All @@ -9,7 +10,7 @@ pub unsafe fn allocate<T>(len: usize) -> Box<[T]> {
vec.into_boxed_slice()
}

/// Copy as many elements as possible from one array to another.
/// Copy as many elements as possible from one slice to another.
///
/// Returns the number of elements copied.
#[inline]
Expand All @@ -19,6 +20,23 @@ pub fn copy<T: Copy>(src: &[T], dest: &mut [T]) -> usize {
len
}

/// Copy as many elements as possible from a slice of slices to another.
///
/// Returns the number of elements copied.
pub fn copy_seq<T: Copy>(seq: &[&[T]], dest: &mut [T]) -> usize {
let mut copied = 0;

for slice in seq {
if copied < dest.len() {
copied += copy(slice, &mut dest[copied..]);
} else {
break;
}
}

copied
}

/// Extension trait for slices for working with wrapping ranges and indicies.
pub trait WrappingSlice<T> {
/// Gets a pair of slices in the given range, wrapping around length.
Expand Down Expand Up @@ -47,3 +65,127 @@ impl<T> WrappingSlice<T> for [T] {
}
}
}

/// A heap-allocated circular array, useful for implementing ring buffers.
///
/// This array type uses a _virtual indexing_ system. Indexing into the array applies a virtual mapping such that any
/// index is always mapped to a valid position in the array. More than one virtual index may map to the same position.
pub struct CircularArray<T> {
array: Box<[T]>,
mask: usize,
}

impl<T> CircularArray<T> {
/// Create a new array of a given length containing uninitialized data.
pub unsafe fn uninitialized(len: usize) -> Self {
let len = len.next_power_of_two();

Self {
array: allocate(len),
mask: len - 1,
}
}

/// Get the length of the array.
#[inline]
pub fn len(&self) -> usize {
self.array.len()
}

/// Gets a pair of slices in the given range, wrapping around length.
pub fn as_slices(&self, range: Range<usize>) -> [&[T]; 2] {
let start = self.internal_index(range.start);
let end = self.internal_index(range.end);

if start < end {
[&self.array[start..end], &[]]
} else {
[&self.array[start..], &self.array[..end]]
}
}

/// Gets a pair of mutable slices in the given range, wrapping around length.
pub fn as_slices_mut(&mut self, range: Range<usize>) -> [&mut [T]; 2] {
let start = self.internal_index(range.start);
let end = self.internal_index(range.end);

if start < end {
[&mut self.array[start..end], &mut []]
} else {
let (mid, right) = self.array.split_at_mut(start);
let left = mid.split_at_mut(end).0;
[right, left]
}
}

// /// Copies elements from this array into
// pub fn copy_to_slice(&self, dest: &mut [T]) -> usize {
// if self.is_empty() {
// return 0;
// }

// let slices = self.array
// .wrapping_range(self.mask(self.head), self.mask(self.tail));

// let mut copied = arrays::copy(slices.0, dest);
// copied += arrays::copy(slices.1, &mut dest[copied..]);

// copied
// }

/// Get the internal index the given virtual index is mapped to.
#[inline]
fn internal_index(&self, virtual_index: usize) -> usize {
virtual_index & self.mask
}
}

impl<T> AsRef<[T]> for CircularArray<T> {
fn as_ref(&self) -> &[T] {
&self.array
}
}

impl<T> AsMut<[T]> for CircularArray<T> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.array
}
}

impl<T> Index<usize> for CircularArray<T> {
type Output = T;

fn index(&self, index: usize) -> &T {
self.array.index(self.internal_index(index))
}
}

impl<T> IndexMut<usize> for CircularArray<T> {
fn index_mut(&mut self, index: usize) -> &mut T {
let index = self.internal_index(index);
self.array.index_mut(index)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn copy_seq_with_less_elements() {
let chunks: [&[i32]; 3] = [&[], &[1, 2], &[3]];
let mut dest = [0; 6];

assert_eq!(copy_seq(&chunks, &mut dest), 3);
assert_eq!(&dest, &[1, 2, 3, 0, 0, 0]);
}

#[test]
fn copy_seq_with_more_elements() {
let chunks: [&[i32]; 5] = [&[], &[1, 2], &[], &[3], &[4, 5, 6]];
let mut dest = [0; 4];

assert_eq!(copy_seq(&chunks, &mut dest), 4);
assert_eq!(&dest, &[1, 2, 3, 4]);
}
}
Loading

0 comments on commit 466c8a2

Please sign in to comment.