//! 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::HistoryBuf; //! //! // Initialize a new buffer with 8 elements. //! let mut buf = HistoryBuf::<_, 8>::new(); //! //! // Starts with no data //! assert_eq!(buf.recent(), None); //! //! buf.write(3); //! buf.write(5); //! buf.extend(&[4, 4]); //! //! // The most recent written element is a four. //! assert_eq!(buf.recent(), Some(&4)); //! //! // To access all elements in an unspecified order, use `as_slice()`. //! for el in buf.as_slice() { //! println!("{:?}", el); //! } //! //! // Now we can prepare an average of all values, which comes out to 4. //! let avg = buf.as_slice().iter().sum::() / buf.len(); //! assert_eq!(avg, 4); //! ``` use core::fmt; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ops::Deref; use core::ptr; use core::slice; mod storage { use core::mem::MaybeUninit; use super::{HistoryBufInner, HistoryBufView}; /// Trait defining how data for a container is stored. /// /// There's two implementations available: /// /// - [`OwnedHistoryBufStorage`]: stores the data in an array `[T; N]` whose size is known at compile time. /// - [`ViewHistoryBufStorage`]: stores the data in an unsized `[T]`. /// /// This allows [`HistoryBuf`] to be generic over either sized or unsized storage. The [`histbuf`] /// module contains a [`HistoryBufInner`] struct that's generic on [`HistoryBufStorage`], /// and two type aliases for convenience: /// /// - [`HistoryBuf`](super::HistoryBuf) = `HistoryBufInner>` /// - [`HistoryBufView`](super::HistoryBufView) = `HistoryBufInner>` /// /// `HistoryBuf` can be unsized into `HistoryBufView`, either by unsizing coercions such as `&mut HistoryBuf -> &mut HistoryBufView` or /// `Box -> Box`, or explicitly with [`.as_view()`](super::HistoryBuf::as_view) or [`.as_mut_view()`](super::HistoryBuf::as_mut_view). /// /// This trait is sealed, so you cannot implement it for your own types. You can only use /// the implementations provided by this crate. /// /// [`HistoryBufInner`]: super::HistoryBufInner /// [`HistoryBuf`]: super::HistoryBuf /// [`HistoryBufView`]: super::HistoryBufView /// [`histbuf`]: super #[allow(private_bounds)] pub trait HistoryBufStorage: HistoryBufSealedStorage {} pub trait HistoryBufSealedStorage { // part of the sealed trait so that no trait is publicly implemented by `OwnedHistoryBufStorage` besides `Storage` fn borrow(&self) -> &[MaybeUninit]; fn borrow_mut(&mut self) -> &mut [MaybeUninit]; fn as_hist_buf_view(this: &HistoryBufInner) -> &HistoryBufView where Self: HistoryBufStorage; fn as_hist_buf_mut_view(this: &mut HistoryBufInner) -> &mut HistoryBufView where Self: HistoryBufStorage; } // One sealed layer of indirection to hide the internal details (The MaybeUninit). pub struct HistoryBufStorageInner { pub(crate) buffer: T, } /// Implementation of [`HistoryBufStorage`] that stores the data in an array `[T; N]` whose size is known at compile time. pub type OwnedHistoryBufStorage = HistoryBufStorageInner<[MaybeUninit; N]>; /// Implementation of [`HistoryBufStorage`] that stores the data in an unsized `[T]`. pub type ViewHistoryBufStorage = HistoryBufStorageInner<[MaybeUninit]>; impl HistoryBufSealedStorage for OwnedHistoryBufStorage { fn borrow(&self) -> &[MaybeUninit] { &self.buffer } fn borrow_mut(&mut self) -> &mut [MaybeUninit] { &mut self.buffer } fn as_hist_buf_view(this: &HistoryBufInner) -> &HistoryBufView where Self: HistoryBufStorage, { this } fn as_hist_buf_mut_view(this: &mut HistoryBufInner) -> &mut HistoryBufView where Self: HistoryBufStorage, { this } } impl HistoryBufStorage for OwnedHistoryBufStorage {} impl HistoryBufSealedStorage for ViewHistoryBufStorage { fn borrow(&self) -> &[MaybeUninit] { &self.buffer } fn borrow_mut(&mut self) -> &mut [MaybeUninit] { &mut self.buffer } fn as_hist_buf_view(this: &HistoryBufInner) -> &HistoryBufView where Self: HistoryBufStorage, { this } fn as_hist_buf_mut_view(this: &mut HistoryBufInner) -> &mut HistoryBufView where Self: HistoryBufStorage, { this } } impl HistoryBufStorage for ViewHistoryBufStorage {} } pub use storage::{HistoryBufStorage, OwnedHistoryBufStorage, ViewHistoryBufStorage}; use storage::HistoryBufStorageInner; use self::storage::HistoryBufSealedStorage; /// Base struct for [`HistoryBuf`] and [`HistoryBufView`], generic over the [`HistoryBufStorage`]. /// /// In most cases you should use [`HistoryBuf`] or [`HistoryBufView`] directly. Only use this /// struct if you want to write code that's generic over both. pub struct HistoryBufInner + ?Sized> { // This phantomdata is required because otherwise rustc thinks that `T` is not used phantom: PhantomData, write_at: usize, filled: bool, data: S, } /// 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::HistoryBuf; /// /// // Initialize a new buffer with 8 elements. /// let mut buf = HistoryBuf::<_, 8>::new(); /// /// // Starts with no data /// assert_eq!(buf.recent(), None); /// /// buf.write(3); /// buf.write(5); /// buf.extend(&[4, 4]); /// /// // The most recent written element is a four. /// assert_eq!(buf.recent(), Some(&4)); /// /// // To access all elements in an unspecified order, use `as_slice()`. /// for el in buf.as_slice() { /// println!("{:?}", el); /// } /// /// // Now we can prepare an average of all values, which comes out to 4. /// let avg = buf.as_slice().iter().sum::() / buf.len(); /// assert_eq!(avg, 4); /// ``` pub type HistoryBuf = HistoryBufInner>; /// A "view" into a [`HistoryBuf`] /// /// Unlike [`HistoryBuf`], it doesn't have the `const N: usize` in its type signature. /// /// # Examples /// ``` /// use heapless::history_buf::{HistoryBuf, HistoryBufView}; /// /// // Initialize a new buffer with 8 elements. /// let mut owned_buf = HistoryBuf::<_, 8>::new(); /// let buf: &mut HistoryBufView<_> = &mut owned_buf; /// /// // Starts with no data /// assert_eq!(buf.recent(), None); /// /// buf.write(3); /// buf.write(5); /// buf.extend(&[4, 4]); /// /// // The most recent written element is a four. /// assert_eq!(buf.recent(), Some(&4)); /// /// // To access all elements in an unspecified order, use `as_slice()`. /// for el in buf.as_slice() { /// println!("{:?}", el); /// } /// /// // Now we can prepare an average of all values, which comes out to 4. /// let avg = buf.as_slice().iter().sum::() / buf.len(); /// assert_eq!(avg, 4); /// ``` pub type HistoryBufView = HistoryBufInner>; impl HistoryBuf { const INIT: MaybeUninit = MaybeUninit::uninit(); /// Constructs a new history buffer. /// /// The construction of a `HistoryBuf` works in `const` contexts. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// // Allocate a 16-element buffer on the stack /// let x: HistoryBuf = HistoryBuf::new(); /// assert_eq!(x.len(), 0); /// ``` #[inline] pub const fn new() -> Self { const { assert!(N > 0); } Self { phantom: PhantomData, data: HistoryBufStorageInner { buffer: [Self::INIT; N], }, write_at: 0, filled: false, } } } impl HistoryBuf where T: Copy + Clone, { /// Constructs a new history buffer, where every element is the given value. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// // Allocate a 16-element buffer on the stack /// let mut x: HistoryBuf = HistoryBuf::new_with(4); /// // All elements are four /// assert_eq!(x.as_slice(), [4; 16]); /// ``` #[inline] pub fn new_with(t: T) -> Self { Self { phantom: PhantomData, data: HistoryBufStorageInner { buffer: [MaybeUninit::new(t); N], }, write_at: 0, filled: true, } } } impl + ?Sized> HistoryBufInner { /// Get a reference to the `HistoryBuf`, erasing the `N` const-generic. #[inline] pub fn as_view(&self) -> &HistoryBufView { S::as_hist_buf_view(self) } /// Get a mutable reference to the `HistoryBuf`, erasing the `N` const-generic. #[inline] pub fn as_mut_view(&mut self) -> &mut HistoryBufView { S::as_hist_buf_mut_view(self) } /// Clears the buffer, replacing every element with the given value. pub fn clear_with(&mut self, t: T) { // SAFETY: we reset the values just after unsafe { self.drop_contents() }; self.write_at = 0; self.filled = true; for d in self.data.borrow_mut() { *d = MaybeUninit::new(t); } } } impl + ?Sized> HistoryBufInner { /// Clears the buffer pub fn clear(&mut self) { // SAFETY: we reset the values just after unsafe { self.drop_contents() }; self.write_at = 0; self.filled = false; } } impl + ?Sized> HistoryBufInner { unsafe fn drop_contents(&mut self) { unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut( self.data.borrow_mut().as_mut_ptr().cast::(), self.len(), )); } } /// Returns the current fill level of the buffer. #[inline] pub fn len(&self) -> usize { if self.filled { self.capacity() } else { self.write_at } } /// Returns true if the buffer is empty. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let x: HistoryBuf = HistoryBuf::new(); /// assert!(x.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the capacity of the buffer, which is the length of the /// underlying backing array. #[inline] pub fn capacity(&self) -> usize { self.data.borrow().len() } /// 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) { if self.filled { // Drop the old before we overwrite it. unsafe { ptr::drop_in_place(self.data.borrow_mut()[self.write_at].as_mut_ptr()) } } self.data.borrow_mut()[self.write_at] = MaybeUninit::new(t); self.write_at += 1; if self.write_at == self.capacity() { self.write_at = 0; self.filled = true; } } /// 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, { for item in other { self.write(item.clone()); } } /// Returns a reference to the most recently written value. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let mut x: HistoryBuf = HistoryBuf::new(); /// x.write(4); /// x.write(10); /// assert_eq!(x.recent(), Some(&10)); /// ``` pub fn recent(&self) -> Option<&T> { self.recent_index() .map(|i| unsafe { &*self.data.borrow()[i].as_ptr() }) } /// Returns index of the most recently written value in the underlying slice. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let mut x: HistoryBuf = HistoryBuf::new(); /// x.write(4); /// x.write(10); /// assert_eq!(x.recent_index(), Some(1)); /// ``` pub fn recent_index(&self) -> Option { if self.write_at == 0 { if self.filled { Some(self.capacity() - 1) } else { None } } else { Some(self.write_at - 1) } } /// Returns a reference to the oldest value in the buffer. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let mut x: HistoryBuf = HistoryBuf::new(); /// x.write(4); /// x.write(10); /// assert_eq!(x.oldest(), Some(&4)); /// ``` pub fn oldest(&self) -> Option<&T> { self.oldest_index() .map(|i| unsafe { &*self.data.borrow()[i].as_ptr() }) } /// Returns index of the oldest value in the underlying slice. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let mut x: HistoryBuf = HistoryBuf::new(); /// x.write(4); /// x.write(10); /// assert_eq!(x.oldest_index(), Some(0)); /// ``` pub fn oldest_index(&self) -> Option { if self.filled { Some(self.write_at) } else if self.write_at == 0 { None } else { Some(0) } } /// 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] { unsafe { slice::from_raw_parts(self.data.borrow().as_ptr().cast(), self.len()) } } /// Returns a pair of slices which contain, in order, the contents of the buffer. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let mut buffer: HistoryBuf = HistoryBuf::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]) { let buffer = self.as_slice(); if self.filled { (&buffer[self.write_at..], &buffer[..self.write_at]) } else { (buffer, &[]) } } /// Returns double ended iterator for iterating over the buffer from /// the oldest to the newest and back. /// /// # Examples /// /// ``` /// use heapless::HistoryBuf; /// /// let mut buffer: HistoryBuf = HistoryBuf::new(); /// buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); /// let expected = [1, 2, 3, 4, 5, 6]; /// for (x, y) in buffer.oldest_ordered().zip(expected.iter()) { /// assert_eq!(x, y) /// } /// ``` pub fn oldest_ordered(&self) -> OldestOrderedInner<'_, T, S> { let (old, new) = self.as_slices(); OldestOrderedInner { phantom: PhantomData, inner: old.iter().chain(new), } } } impl + ?Sized> Extend for HistoryBufInner { fn extend(&mut self, iter: I) where I: IntoIterator, { for item in iter.into_iter() { self.write(item); } } } impl<'a, T, S: HistoryBufStorage + ?Sized> Extend<&'a T> for HistoryBufInner where T: 'a + Clone, { fn extend(&mut self, iter: I) where I: IntoIterator, { self.extend(iter.into_iter().cloned()); } } impl Clone for HistoryBuf where T: Clone, { fn clone(&self) -> Self { let mut ret = Self::new(); for (new, old) in ret.data.borrow_mut().iter_mut().zip(self.as_slice()) { new.write(old.clone()); } ret.filled = self.filled; ret.write_at = self.write_at; ret } } impl + ?Sized> Drop for HistoryBufInner { fn drop(&mut self) { unsafe { self.drop_contents() } } } impl + ?Sized> Deref for HistoryBufInner { type Target = [T]; fn deref(&self) -> &[T] { self.as_slice() } } impl + ?Sized> AsRef<[T]> for HistoryBufInner { #[inline] fn as_ref(&self) -> &[T] { self } } impl + ?Sized> fmt::Debug for HistoryBufInner where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { <[T] as fmt::Debug>::fmt(self, f) } } impl Default for HistoryBuf { fn default() -> Self { Self::new() } } impl + ?Sized> PartialEq for HistoryBufInner where T: PartialEq, { fn eq(&self, other: &Self) -> bool { self.oldest_ordered().eq(other.oldest_ordered()) } } /// Base struct for [`OldestOrdered`] and [`OldestOrderedView`], generic over the [`HistoryBufStorage`]. /// /// In most cases you should use [`OldestOrdered`] or [`OldestOrderedView`] directly. Only use this /// struct if you want to write code that's generic over both. pub struct OldestOrderedInner<'a, T, S: HistoryBufStorage + ?Sized> { phantom: PhantomData, inner: core::iter::Chain, core::slice::Iter<'a, T>>, } /// Double ended iterator on the underlying buffer ordered from the oldest data /// to the newest. /// /// This type exists for backwards compatibility. It is always better to convert it to an [`OldestOrderedView`] with [`into_view`](OldestOrdered::into_view) pub type OldestOrdered<'a, T, const N: usize> = OldestOrderedInner<'a, T, OwnedHistoryBufStorage>; /// Double ended iterator on the underlying buffer ordered from the oldest data /// to the newest pub type OldestOrderedView<'a, T> = OldestOrderedInner<'a, T, ViewHistoryBufStorage>; impl<'a, T, const N: usize> OldestOrdered<'a, T, N> { /// Remove the `N` const-generic parameter from the iterator /// /// For the opposite operation, see [`into_legacy_iter`](OldestOrderedView::into_legacy_iter) pub fn into_view(self) -> OldestOrderedView<'a, T> { OldestOrderedView { phantom: PhantomData, inner: self.inner, } } } impl<'a, T> OldestOrderedView<'a, T> { /// Add back the `N` const-generic parameter to use it with APIs expecting the legacy type /// /// You probably do not need this /// /// For the opposite operation, see [`into_view`](OldestOrdered::into_view) pub fn into_legacy_iter(self) -> OldestOrdered<'a, T, N> { OldestOrdered { phantom: PhantomData, inner: self.inner, } } } impl + ?Sized> Clone for OldestOrderedInner<'_, T, S> { fn clone(&self) -> Self { Self { phantom: PhantomData, inner: self.inner.clone(), } } } impl<'a, T, S: HistoryBufStorage + ?Sized> Iterator for OldestOrderedInner<'a, T, S> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { self.inner.next() } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl DoubleEndedIterator for OldestOrdered<'_, T, N> { fn next_back(&mut self) -> Option { self.inner.next_back() } } #[cfg(test)] mod tests { use core::fmt::Debug; use core::sync::atomic::{AtomicUsize, Ordering}; use static_assertions::assert_not_impl_any; use super::{HistoryBuf, HistoryBufView}; // Ensure a `HistoryBuf` containing `!Send` values stays `!Send` itself. assert_not_impl_any!(HistoryBuf<*const (), 4>: Send); #[test] fn new() { let x: HistoryBuf = HistoryBuf::new_with(1); assert_eq!(x.len(), 4); assert_eq!(x.as_slice(), [1; 4]); assert_eq!(*x, [1; 4]); assert!(x.is_full()); let x: HistoryBuf = HistoryBuf::new(); assert_eq!(x.as_slice(), []); assert!(!x.is_full()); } #[test] fn write() { let mut x: HistoryBuf = HistoryBuf::new(); x.write(1); x.write(4); assert_eq!(x.as_slice(), [1, 4]); x.write(5); x.write(6); x.write(10); assert_eq!(x.as_slice(), [10, 4, 5, 6]); x.extend([11, 12].iter()); assert_eq!(x.as_slice(), [10, 11, 12, 6]); } #[test] fn clear() { let mut x: HistoryBuf = HistoryBuf::new_with(1); x.clear(); assert_eq!(x.as_slice(), []); let mut x: HistoryBuf = HistoryBuf::new(); x.clear_with(1); assert_eq!(x.as_slice(), [1; 4]); } #[test] fn clone() { let mut x: HistoryBuf = HistoryBuf::new(); for i in 0..10 { assert_eq!(x.as_slice(), x.clone().as_slice()); x.write(i); } // Records number of clones locally and globally. static GLOBAL: AtomicUsize = AtomicUsize::new(0); #[derive(Default, PartialEq, Debug)] struct InstrumentedClone(usize); impl Clone for InstrumentedClone { fn clone(&self) -> Self { GLOBAL.fetch_add(1, Ordering::Relaxed); Self(self.0 + 1) } } let mut y: HistoryBuf = HistoryBuf::new(); let _ = y.clone(); assert_eq!(GLOBAL.load(Ordering::Relaxed), 0); y.write(InstrumentedClone(0)); assert_eq!(GLOBAL.load(Ordering::Relaxed), 0); assert_eq!(y.clone().as_slice(), [InstrumentedClone(1)]); assert_eq!(GLOBAL.load(Ordering::Relaxed), 1); y.write(InstrumentedClone(0)); assert_eq!(GLOBAL.load(Ordering::Relaxed), 1); assert_eq!( y.clone().as_slice(), [InstrumentedClone(1), InstrumentedClone(1)] ); assert_eq!(GLOBAL.load(Ordering::Relaxed), 3); assert_eq!( y.clone().clone().clone().as_slice(), [InstrumentedClone(3), InstrumentedClone(3)] ); assert_eq!(GLOBAL.load(Ordering::Relaxed), 9); } #[test] fn recent() { let mut x: HistoryBuf = HistoryBuf::new(); assert_eq!(x.recent_index(), None); assert_eq!(x.recent(), None); x.write(1); x.write(4); assert_eq!(x.recent_index(), Some(1)); assert_eq!(x.recent(), Some(&4)); x.write(5); x.write(6); x.write(10); assert_eq!(x.recent_index(), Some(0)); assert_eq!(x.recent(), Some(&10)); } #[test] fn oldest() { let mut x: HistoryBuf = HistoryBuf::new(); assert_eq!(x.oldest_index(), None); assert_eq!(x.oldest(), None); x.write(1); x.write(4); assert_eq!(x.oldest_index(), Some(0)); assert_eq!(x.oldest(), Some(&1)); x.write(5); x.write(6); x.write(10); assert_eq!(x.oldest_index(), Some(1)); assert_eq!(x.oldest(), Some(&4)); } #[test] fn as_slice() { let mut x: HistoryBuf = HistoryBuf::new(); assert_eq!(x.as_slice(), []); x.extend([1, 2, 3, 4, 5].iter()); assert_eq!(x.as_slice(), [5, 2, 3, 4]); } /// Test whether `.as_slices()` behaves as expected. #[test] fn as_slices() { let mut buffer: HistoryBuf = HistoryBuf::new(); let mut extend_then_assert = |extend: &[u8], assert: (&[u8], &[u8])| { buffer.extend(extend); assert_eq!(buffer.as_slices(), assert); }; extend_then_assert(b"a", (b"a", b"")); extend_then_assert(b"bcd", (b"abcd", b"")); extend_then_assert(b"efg", (b"d", b"efg")); extend_then_assert(b"h", (b"efgh", b"")); extend_then_assert(b"123456", (b"34", b"56")); } /// Test whether `.as_slices()` and `.oldest_ordered()` produce elements in the same order. #[test] fn as_slices_equals_ordered() { let mut buffer: HistoryBuf = HistoryBuf::new(); for n in 0..20 { buffer.write(n); let (head, tail) = buffer.as_slices(); assert_eq_iter( [head, tail].iter().copied().flatten(), buffer.oldest_ordered(), ); } } #[test] fn ordered() { // test on an empty buffer let buffer: HistoryBuf = HistoryBuf::new(); let mut iter = buffer.oldest_ordered(); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); assert_eq!(iter.next_back(), None); assert_eq!(iter.next_back(), None); // test on a un-filled buffer let mut buffer: HistoryBuf = HistoryBuf::new(); buffer.extend([1, 2, 3]); assert_eq!(buffer.len(), 3); assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3]); assert_eq_iter(buffer.oldest_ordered().rev(), &[3, 2, 1]); let mut iter = buffer.oldest_ordered(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next_back(), Some(&3)); assert_eq!(iter.next_back(), Some(&2)); assert_eq!(iter.next_back(), None); assert_eq!(iter.next(), None); // test on an exactly filled buffer let mut buffer: HistoryBuf = HistoryBuf::new(); buffer.extend([1, 2, 3, 4, 5, 6]); assert_eq!(buffer.len(), 6); assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]); assert_eq_iter(buffer.oldest_ordered().rev(), &[6, 5, 4, 3, 2, 1]); // test on a filled buffer let mut buffer: HistoryBuf = HistoryBuf::new(); buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]); assert_eq!(buffer.len(), 6); assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]); assert_eq_iter(buffer.oldest_ordered().rev(), &[6, 5, 4, 3, 2, 1]); // comprehensive test all cases for n in 0..50 { const N: usize = 7; let mut buffer: HistoryBuf = HistoryBuf::new(); buffer.extend(0..n); assert_eq_iter( buffer.oldest_ordered().copied(), n.saturating_sub(N as u8)..n, ); assert_eq_iter( buffer.oldest_ordered().rev().copied(), (n.saturating_sub(N as u8)..n).rev(), ); } } /// Compares two iterators item by item, making sure they stop at the same time. fn assert_eq_iter( a: impl IntoIterator, b: impl IntoIterator, ) { let mut a = a.into_iter(); let mut b = b.into_iter(); let mut i = 0; loop { let a_item = a.next(); let b_item = b.next(); assert_eq!(a_item, b_item, "{}", i); i += 1; if b_item.is_none() { break; } } } #[test] fn partial_eq() { let mut x: HistoryBuf = HistoryBuf::new(); let mut y: HistoryBuf = HistoryBuf::new(); assert_eq!(x, y); x.write(1); assert_ne!(x, y); y.write(1); assert_eq!(x, y); for _ in 0..4 { x.write(2); assert_ne!(x, y); for i in 0..5 { x.write(i); y.write(i); } assert_eq!( x, y, "{:?} {:?}", x.iter().collect::>(), y.iter().collect::>() ); } } #[test] fn clear_drops_values() { static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); struct DropCheck {} impl Drop for DropCheck { fn drop(&mut self) { DROP_COUNT.fetch_add(1, Ordering::SeqCst); } } let mut x: HistoryBuf = HistoryBuf::new(); x.write(DropCheck {}); x.write(DropCheck {}); x.write(DropCheck {}); assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0); x.clear(); assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); } fn _test_variance<'a: 'b, 'b>(x: HistoryBuf<&'a (), 42>) -> HistoryBuf<&'b (), 42> { x } fn _test_variance_view<'a: 'b, 'b, 'c>( x: &'c HistoryBufView<&'a ()>, ) -> &'c HistoryBufView<&'b ()> { x } }