diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bea10baa6..951a4409c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `VecView`, the `!Sized` version of `Vec`. - Added pool implementations for 64-bit architectures. - Added `IntoIterator` implementation for `LinearMap` +- Added `HistoryBufferView`, the `!Sized` version of `HistoryBuffer`. ### Changed diff --git a/src/histbuf.rs b/src/histbuf.rs index 98f7556181..c6413a5dc0 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -1,9 +1,45 @@ use core::fmt; +use core::iter; +use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ops::Deref; use core::ptr; use core::slice; +pub struct HistoryBufferInner { + write_at: usize, + filled: bool, + data: B, +} + +///
This is private API and should not be used
+pub trait HistBuffer { + type T; + + fn as_hist_view(this: &HistoryBufferInner) -> &HistoryBufferView; + fn as_mut_hist_view(this: &mut HistoryBufferInner) -> &mut HistoryBufferView; +} + +impl HistBuffer for [MaybeUninit; N] { + type T = T; + fn as_hist_view(this: &HistoryBufferInner) -> &HistoryBufferView { + this + } + fn as_mut_hist_view(this: &mut HistoryBufferInner) -> &mut HistoryBufferView { + this + } +} + +impl HistBuffer for [MaybeUninit] { + type T = T; + fn as_hist_view(this: &HistoryBufferInner) -> &HistoryBufferView { + this + } + fn as_mut_hist_view(this: &mut HistoryBufferInner) -> &mut HistoryBufferView { + this + } +} + /// A "history buffer", similar to a write-only ring buffer of fixed length. /// /// This buffer keeps a fixed number of elements. On write, the oldest element @@ -36,11 +72,42 @@ use core::slice; /// let avg = buf.as_slice().iter().sum::() / buf.len(); /// assert_eq!(avg, 4); /// ``` -pub struct HistoryBuffer { - data: [MaybeUninit; N], - write_at: usize, - filled: bool, -} +pub type HistoryBuffer = HistoryBufferInner<[MaybeUninit; N]>; + +/// A "history buffer", similar to a write-only ring buffer of fixed length. +/// +/// This buffer keeps a fixed number of elements. On write, the oldest element +/// is overwritten. Thus, the buffer is useful to keep a history of values with +/// some desired depth, and for example calculate a rolling average. +/// +/// # Examples +/// ``` +/// use heapless::{HistoryBuffer, HistoryBufferView}; +/// +/// // Initialize a new buffer with 8 elements. +/// let mut buf = HistoryBuffer::<_, 8>::new(); +/// let buf_view: &mut HistoryBufferView<_> = &mut buf; +/// +/// // Starts with no data +/// assert_eq!(buf_view.recent(), None); +/// +/// buf_view.write(3); +/// buf_view.write(5); +/// buf_view.extend(&[4, 4]); +/// +/// // The most recent written element is a four. +/// assert_eq!(buf_view.recent(), Some(&4)); +/// +/// // To access all elements in an unspecified order, use `as_slice()`. +/// for el in buf_view.as_slice() { +/// println!("{:?}", el); +/// } +/// +/// // Now we can prepare an average of all values, which comes out to 4. +/// let avg = buf_view.as_slice().iter().sum::() / buf_view.len(); +/// assert_eq!(avg, 4); +/// ``` +pub type HistoryBufferView = HistoryBufferInner<[MaybeUninit]>; impl HistoryBuffer { const INIT: MaybeUninit = MaybeUninit::uninit(); @@ -73,7 +140,63 @@ impl HistoryBuffer { /// Clears the buffer, replacing every element with the default value of /// type `T`. pub fn clear(&mut self) { - *self = Self::new(); + self.as_mut_view().clear(); + } + + pub fn as_view(&self) -> &HistoryBufferView { + self + } + + pub fn as_mut_view(&mut self) -> &mut HistoryBufferView { + self + } +} + +impl HistoryBufferView { + fn drop_impl(&mut self) { + unsafe { + ptr::drop_in_place(ptr::slice_from_raw_parts_mut( + self.data.as_mut_ptr() as *mut T, + self.len(), + )) + } + } + + /// Clears the buffer, replacing every element with the default value of + /// type `T`. + pub fn clear(&mut self) { + // Drop all current stored values + self.drop_impl(); + let Self { + // Data is all MaybeUninit, we don't need to reset it + data: _, + write_at, + filled, + } = self; + *write_at = 0; + *filled = false; + } +} + +impl HistoryBufferView +where + T: Copy, +{ + /// Clears the buffer, replacing every element with the given value. + pub fn clear_with(&mut self, t: T) { + // Drop all current stored values + self.drop_impl(); + let Self { + // Data is all MaybeUninit, we don't need to reset it + data, + write_at, + filled, + } = self; + *write_at = 0; + *filled = true; + for slot in data { + *slot = MaybeUninit::new(t); + } } } @@ -104,21 +227,19 @@ where /// Clears the buffer, replacing every element with the given value. pub fn clear_with(&mut self, t: T) { - *self = Self::new_with(t); + self.as_mut_view().clear_with(t) } } - -impl HistoryBuffer { +impl HistoryBufferView { /// Returns the current fill level of the buffer. #[inline] pub fn len(&self) -> usize { if self.filled { - N + self.data.len() } else { self.write_at } } - /// Returns true if the buffer is empty. /// /// # Examples @@ -138,7 +259,7 @@ impl HistoryBuffer { /// underlying backing array. #[inline] pub fn capacity(&self) -> usize { - N + self.data.len() } /// Returns whether the buffer is full @@ -180,12 +301,13 @@ impl HistoryBuffer { /// # Examples /// /// ``` - /// use heapless::HistoryBuffer; + /// use heapless::{HistoryBuffer, HistoryBufferView}; /// /// let mut x: HistoryBuffer = HistoryBuffer::new(); - /// x.write(4); - /// x.write(10); - /// assert_eq!(x.recent(), Some(&10)); + /// let mut x_view = &mut x; + /// x_view.write(4); + /// x_view.write(10); + /// assert_eq!(x_view.recent(), Some(&10)); /// ``` pub fn recent(&self) -> Option<&T> { self.recent_index() @@ -197,12 +319,13 @@ impl HistoryBuffer { /// # Examples /// /// ``` - /// use heapless::HistoryBuffer; + /// use heapless::{HistoryBuffer, HistoryBufferView}; /// /// let mut x: HistoryBuffer = HistoryBuffer::new(); - /// x.write(4); - /// x.write(10); - /// assert_eq!(x.recent_index(), Some(1)); + /// let mut x_view = &mut x; + /// x_view.write(4); + /// x_view.write(10); + /// assert_eq!(x_view.recent_index(), Some(1)); /// ``` pub fn recent_index(&self) -> Option { if self.write_at == 0 { @@ -221,12 +344,13 @@ impl HistoryBuffer { /// # Examples /// /// ``` - /// use heapless::HistoryBuffer; + /// use heapless::{HistoryBuffer, HistoryBufferView}; /// /// let mut x: HistoryBuffer = HistoryBuffer::new(); - /// x.write(4); - /// x.write(10); - /// assert_eq!(x.oldest(), Some(&4)); + /// let mut x_view = &mut x; + /// x_view.write(4); + /// x_view.write(10); + /// assert_eq!(x_view.oldest(), Some(&4)); /// ``` pub fn oldest(&self) -> Option<&T> { self.oldest_index() @@ -238,12 +362,13 @@ impl HistoryBuffer { /// # Examples /// /// ``` - /// use heapless::HistoryBuffer; + /// use heapless::{HistoryBuffer, HistoryBufferView}; /// /// let mut x: HistoryBuffer = HistoryBuffer::new(); - /// x.write(4); - /// x.write(10); - /// assert_eq!(x.oldest_index(), Some(0)); + /// let mut x_view = &mut x; + /// x_view.write(4); + /// x_view.write(10); + /// assert_eq!(x_view.oldest_index(), Some(0)); /// ``` pub fn oldest_index(&self) -> Option { if self.filled { @@ -266,12 +391,12 @@ impl HistoryBuffer { /// # Examples /// /// ``` - /// use heapless::HistoryBuffer; - /// + /// use heapless::{HistoryBuffer, HistoryBufferView}; /// let mut buffer: HistoryBuffer = HistoryBuffer::new(); - /// buffer.extend([0, 0, 0]); - /// buffer.extend([1, 2, 3, 4, 5, 6]); - /// assert_eq!(buffer.as_slices(), (&[1, 2, 3][..], &[4, 5, 6][..])); + /// let mut buffer_view: &mut HistoryBufferView = &mut buffer; + /// buffer_view.extend([0, 0, 0]); + /// buffer_view.extend([1, 2, 3, 4, 5, 6]); + /// assert_eq!(buffer_view.as_slices(), (&[1, 2, 3][..], &[4, 5, 6][..])); /// ``` pub fn as_slices(&self) -> (&[T], &[T]) { let buffer = self.as_slice(); @@ -283,6 +408,168 @@ impl HistoryBuffer { } } + /// Returns an iterator for iterating over the buffer from oldest to newest. + /// + /// # Examples + /// + /// ``` + /// use heapless::{HistoryBuffer, HistoryBufferView}; + /// + /// let mut buffer: HistoryBuffer = HistoryBuffer::new(); + /// let mut buffer_view: &mut HistoryBufferView = &mut buffer; + /// buffer_view.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); + /// let expected = [1, 2, 3, 4, 5, 6]; + /// for (x, y) in buffer_view.oldest_ordered().zip(expected.iter()) { + /// assert_eq!(x, y) + /// } + /// ``` + pub fn oldest_ordered(&self) -> impl Iterator { + self.oldest_ordered_inner() + } + + fn oldest_ordered_inner(&self) -> iter::Chain, slice::Iter<'_, T>> { + let (old, new) = self.as_slices(); + old.iter().chain(new) + } +} + +impl HistoryBuffer { + /// Returns the current fill level of the buffer. + #[inline] + pub fn len(&self) -> usize { + self.as_view().len() + } + + /// Returns true if the buffer is empty. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let x: HistoryBuffer = HistoryBuffer::new(); + /// assert!(x.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.as_view().is_empty() + } + + /// Returns the capacity of the buffer, which is the length of the + /// underlying backing array. + #[inline] + pub fn capacity(&self) -> usize { + N + } + + /// Returns whether the buffer is full + #[inline] + pub fn is_full(&self) -> bool { + self.filled + } + + /// Writes an element to the buffer, overwriting the oldest value. + pub fn write(&mut self, t: T) { + self.as_mut_view().write(t) + } + + /// Clones and writes all elements in a slice to the buffer. + /// + /// If the slice is longer than the buffer, only the last `self.len()` + /// elements will actually be stored. + pub fn extend_from_slice(&mut self, other: &[T]) + where + T: Clone, + { + self.as_mut_view().extend_from_slice(other) + } + + /// Returns a reference to the most recently written value. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let mut x: HistoryBuffer = HistoryBuffer::new(); + /// x.write(4); + /// x.write(10); + /// assert_eq!(x.recent(), Some(&10)); + /// ``` + pub fn recent(&self) -> Option<&T> { + self.as_view().recent() + } + + /// Returns index of the most recently written value in the underlying slice. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let mut x: HistoryBuffer = HistoryBuffer::new(); + /// x.write(4); + /// x.write(10); + /// assert_eq!(x.recent_index(), Some(1)); + /// ``` + pub fn recent_index(&self) -> Option { + self.as_view().recent_index() + } + + /// Returns a reference to the oldest value in the buffer. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let mut x: HistoryBuffer = HistoryBuffer::new(); + /// x.write(4); + /// x.write(10); + /// assert_eq!(x.oldest(), Some(&4)); + /// ``` + pub fn oldest(&self) -> Option<&T> { + self.as_view().oldest() + } + + /// Returns index of the oldest value in the underlying slice. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let mut x: HistoryBuffer = HistoryBuffer::new(); + /// x.write(4); + /// x.write(10); + /// assert_eq!(x.oldest_index(), Some(0)); + /// ``` + pub fn oldest_index(&self) -> Option { + self.as_view().oldest_index() + } + + /// Returns the array slice backing the buffer, without keeping track + /// of the write position. Therefore, the element order is unspecified. + pub fn as_slice(&self) -> &[T] { + self.as_view().as_slice() + } + + /// Returns a pair of slices which contain, in order, the contents of the buffer. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// + /// let mut buffer: HistoryBuffer = HistoryBuffer::new(); + /// buffer.extend([0, 0, 0]); + /// buffer.extend([1, 2, 3, 4, 5, 6]); + /// assert_eq!(buffer.as_slices(), (&[1, 2, 3][..], &[4, 5, 6][..])); + /// ``` + pub fn as_slices(&self) -> (&[T], &[T]) { + self.as_view().as_slices() + } + /// Returns an iterator for iterating over the buffer from oldest to newest. /// /// # Examples @@ -298,24 +585,23 @@ impl HistoryBuffer { /// } /// ``` pub fn oldest_ordered(&self) -> OldestOrdered<'_, T, N> { - if self.filled { - OldestOrdered { - buf: self, - cur: self.write_at, - wrapped: false, - } - } else { - // special case: act like we wrapped already to handle empty buffer. - OldestOrdered { - buf: self, - cur: 0, - wrapped: true, - } + OldestOrdered { + inner: self.as_view().oldest_ordered_inner(), + phantom: PhantomData, } } } impl Extend for HistoryBuffer { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + self.as_mut_view().extend(iter) + } +} + +impl Extend for HistoryBufferView { fn extend(&mut self, iter: I) where I: IntoIterator, @@ -327,6 +613,18 @@ impl Extend for HistoryBuffer { } impl<'a, T, const N: usize> Extend<&'a T> for HistoryBuffer +where + T: 'a + Clone, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + self.as_mut_view().extend(iter) + } +} + +impl<'a, T> Extend<&'a T> for HistoryBufferView where T: 'a + Clone, { @@ -353,20 +651,23 @@ where } } -impl Drop for HistoryBuffer { +impl Drop for HistoryBufferInner { fn drop(&mut self) { - unsafe { - ptr::drop_in_place(ptr::slice_from_raw_parts_mut( - self.data.as_mut_ptr() as *mut T, - self.len(), - )) - } + B::as_mut_hist_view(self).drop_impl(); } } impl Deref for HistoryBuffer { type Target = [T]; + fn deref(&self) -> &[T] { + self.as_view() + } +} + +impl Deref for HistoryBufferView { + type Target = [T]; + fn deref(&self) -> &[T] { self.as_slice() } @@ -379,7 +680,23 @@ impl AsRef<[T]> for HistoryBuffer { } } +impl AsRef<[T]> for HistoryBufferView { + #[inline] + fn as_ref(&self) -> &[T] { + self + } +} + impl fmt::Debug for HistoryBuffer +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_view().fmt(f) + } +} + +impl fmt::Debug for HistoryBufferView where T: fmt::Debug, { @@ -395,6 +712,15 @@ impl Default for HistoryBuffer { } impl PartialEq for HistoryBuffer +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.as_view().eq(other.as_view()) + } +} + +impl PartialEq for HistoryBufferView where T: PartialEq, { @@ -403,31 +729,36 @@ where } } +impl PartialEq> for HistoryBufferView +where + T: PartialEq, +{ + fn eq(&self, other: &HistoryBuffer) -> bool { + self.oldest_ordered().eq(other.as_view().oldest_ordered()) + } +} + +impl PartialEq> for HistoryBuffer +where + T: PartialEq, +{ + fn eq(&self, other: &HistoryBufferView) -> bool { + self.as_view().oldest_ordered().eq(other.oldest_ordered()) + } +} + /// An iterator on the underlying buffer ordered from oldest data to newest #[derive(Clone)] pub struct OldestOrdered<'a, T, const N: usize> { - buf: &'a HistoryBuffer, - cur: usize, - wrapped: bool, + inner: iter::Chain, slice::Iter<'a, T>>, + phantom: PhantomData<[T; N]>, } impl<'a, T, const N: usize> Iterator for OldestOrdered<'a, T, N> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { - if self.cur == self.buf.len() && self.buf.filled { - // roll-over - self.cur = 0; - self.wrapped = true; - } - - if self.cur == self.buf.write_at && self.wrapped { - return None; - } - - let item = &self.buf[self.cur]; - self.cur += 1; - Some(item) + self.inner.next() } } diff --git a/src/lib.rs b/src/lib.rs index b5238c672c..57b3d6bcc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ pub use binary_heap::BinaryHeap; pub use deque::Deque; -pub use histbuf::{HistoryBuffer, OldestOrdered}; +pub use histbuf::{HistoryBuffer, HistoryBufferView, OldestOrdered}; pub use indexmap::{ Bucket, Entry, FnvIndexMap, IndexMap, Iter as IndexMapIter, IterMut as IndexMapIterMut, Keys as IndexMapKeys, OccupiedEntry, Pos, VacantEntry, Values as IndexMapValues,