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:
2026-02-15 13:59:03 -05:00
parent 7a83efe481
commit 98a650b46f

View File

@ -9,7 +9,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
pub enum BufferError pub enum BufferError
{ {
/// This will occur when you try to set the capacity of the [`RingBuffer`] to /// This will occur when you try to set the capacity of the [`RingBuffer`] to
/// zero. /// a size other than a power of two.
InvalidCapacity, InvalidCapacity,
/// This will occur when the [`RingBuffer`] is unable to /// 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. /// Create a new RingBuffer that can hold N items of type T.
/// ///
/// # Capacity /// # Capacity
/// - Must be greater than 0. /// - Must be a power of 2.
/// - Faster operations if capacity is 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 /// # Examples
/// ```rust /// ```rust
/// use example::RingBuffer; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<u8, 32> = /// 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.len(), 0);
/// assert_eq!(buffer.capacity(), 32); /// assert_eq!(buffer.capacity(), 32);
/// ``` /// ```
pub fn new() -> Result<Self, BufferError> 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 Ok(RingBuffer
{ {
@ -109,7 +116,7 @@ impl<T, const N: usize> RingBuffer<T, N>
/// use example::RingBuffer; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<u8, 32> = /// 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 cap = buffer.capacity();
/// let len = buffer.len(); /// let len = buffer.len();
/// ///
@ -126,7 +133,7 @@ impl<T, const N: usize> RingBuffer<T, N>
/// use example::RingBuffer; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<u8, 32> = /// 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(25).expect("Buffer is full.");
/// buffer.push_back(42).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; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<u8, 32> = /// 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(25).expect("Buffer is full.");
/// let _ = buffer.pop_front(); /// let _ = buffer.pop_front();
@ -188,11 +195,10 @@ impl<T, const N: usize> RingBuffer<T, N>
/// ```rust /// ```rust
/// use example::RingBuffer; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<u8, 5> = /// let buffer: RingBuffer<u8, 4> =
/// 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(25).expect("Buffer is full.");
/// buffer.push_back(15).expect("Buffer is full.");
/// buffer.push_back(42).expect("Buffer is full."); /// buffer.push_back(42).expect("Buffer is full.");
/// buffer.push_back(1).expect("Buffer is full."); /// buffer.push_back(1).expect("Buffer is full.");
/// buffer.push_back(2).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 /// ```rust
/// use example::RingBuffer; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<i32, 10> = /// let buffer: RingBuffer<i32, 16> =
/// RingBuffer::new().expect("Buffer size was set to 0"); /// RingBuffer::new().expect("Buffer size must be a power of 2.");
/// ///
/// buffer.push_back(1).expect("Buffer is full."); /// buffer.push_back(1).expect("Buffer is full.");
/// buffer.push_back(2).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 /// ```rust
/// use example::RingBuffer; /// use example::RingBuffer;
/// ///
/// let buffer: RingBuffer<i32, 10> = /// let buffer: RingBuffer<i32, 16> =
/// RingBuffer::new().expect("Buffer size was set to 0"); /// RingBuffer::new().expect("Buffer size must be a power of 2.");
/// ///
/// buffer.push_back(1).expect("Buffer is full."); /// buffer.push_back(1).expect("Buffer is full.");
/// buffer.push_back(2).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 // 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 // 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. // 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> 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. /// Map the counter space to the buffer space.