The RingBuffer now needs a capacity of power of 2.
This helps fix some wrapping issues and mapping down counter space to buffer space. Essentially if the size of the buffer isn't a power of two, then it breaks the counter space up into uneven chunks. So when mapping to the buffer space the indexing gets off when the counters wrap around.
This commit is contained in:
@ -9,7 +9,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
pub enum BufferError
|
||||
{
|
||||
/// This will occur when you try to set the capacity of the [`RingBuffer`] to
|
||||
/// zero.
|
||||
/// a size other than a power of two.
|
||||
InvalidCapacity,
|
||||
|
||||
/// This will occur when the [`RingBuffer`] is unable to
|
||||
@ -77,22 +77,29 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// Create a new RingBuffer that can hold N items of type T.
|
||||
///
|
||||
/// # Capacity
|
||||
/// - Must be greater than 0.
|
||||
/// - Must be a power of 2.
|
||||
/// - Faster operations if capacity is a power of 2.
|
||||
///
|
||||
/// This was changed from being anything except zero because values that
|
||||
/// were not a power of two would incorrectly map to the buffer space
|
||||
/// when wrapping occured in the counter space. This caused an uneven
|
||||
/// mapping of values in counter space to indices in buffer space.
|
||||
/// A smooth transition is required and the power of two fixes that
|
||||
/// because the max size of the usize value will always be a power of two.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<u8, 32> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
///
|
||||
/// assert_eq!(buffer.len(), 0);
|
||||
/// assert_eq!(buffer.capacity(), 32);
|
||||
/// ```
|
||||
pub fn new() -> Result<Self, BufferError>
|
||||
{
|
||||
if N == 0 { return Err(BufferError::InvalidCapacity) }
|
||||
if !N.is_power_of_two() { return Err(BufferError::InvalidCapacity) }
|
||||
|
||||
Ok(RingBuffer
|
||||
{
|
||||
@ -109,7 +116,7 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<u8, 32> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
/// let cap = buffer.capacity();
|
||||
/// let len = buffer.len();
|
||||
///
|
||||
@ -126,7 +133,7 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<u8, 32> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
///
|
||||
/// buffer.push_back(25).expect("Buffer is full.");
|
||||
/// buffer.push_back(42).expect("Buffer is full.");
|
||||
@ -161,7 +168,7 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<u8, 32> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
///
|
||||
/// buffer.push_back(25).expect("Buffer is full.");
|
||||
/// let _ = buffer.pop_front();
|
||||
@ -188,11 +195,10 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// ```rust
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<u8, 5> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// let buffer: RingBuffer<u8, 4> =
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
///
|
||||
/// buffer.push_back(25).expect("Buffer is full.");
|
||||
/// buffer.push_back(15).expect("Buffer is full.");
|
||||
/// buffer.push_back(42).expect("Buffer is full.");
|
||||
/// buffer.push_back(1).expect("Buffer is full.");
|
||||
/// buffer.push_back(2).expect("Buffer is full.");
|
||||
@ -221,8 +227,8 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// ```rust
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<i32, 10> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// let buffer: RingBuffer<i32, 16> =
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
///
|
||||
/// buffer.push_back(1).expect("Buffer is full.");
|
||||
/// buffer.push_back(2).expect("Buffer is full.");
|
||||
@ -269,8 +275,8 @@ impl<T, const N: usize> RingBuffer<T, N>
|
||||
/// ```rust
|
||||
/// use example::RingBuffer;
|
||||
///
|
||||
/// let buffer: RingBuffer<i32, 10> =
|
||||
/// RingBuffer::new().expect("Buffer size was set to 0");
|
||||
/// let buffer: RingBuffer<i32, 16> =
|
||||
/// RingBuffer::new().expect("Buffer size must be a power of 2.");
|
||||
///
|
||||
/// buffer.push_back(1).expect("Buffer is full.");
|
||||
/// buffer.push_back(2).expect("Buffer is full.");
|
||||
@ -340,8 +346,11 @@ impl<T, const N: usize> Drop for RingBuffer<T, N>
|
||||
// allows us to state that the buffer is designed to be used from multiple
|
||||
// threads from the same reference. The RingBuffer requires that its items all
|
||||
// be Send so that they can traverse the threads pushing and pulling from it.
|
||||
//
|
||||
// Normally these are derived automatically, but because this in now using
|
||||
// UnsafeCell they need to be added manually.
|
||||
unsafe impl<T: Send, const N: usize> Sync for RingBuffer<T, N> {}
|
||||
|
||||
unsafe impl<T: Send, const N: usize> Send for RingBuffer<T, N> {}
|
||||
|
||||
|
||||
/// Map the counter space to the buffer space.
|
||||
|
||||
Reference in New Issue
Block a user