diff --git a/Cargo.toml b/Cargo.toml index 2c95e459..e7ba77f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ authors = [ "Jorge Aparicio ", "Per Lindgren ", + "Emil Fresk ", ] categories = [ "data-structures", diff --git a/src/histbuf.rs b/src/histbuf.rs index df67844f..4fed6565 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -1,14 +1,13 @@ +use core::mem::MaybeUninit; +use core::ptr; +use core::slice; + /// 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. /// -/// The buffer is always fully initialized; depending on the constructor, the -/// initial value is either the default value for the element type or a supplied -/// initial value. This simplifies the API and is mostly irrelevant for the -/// intended use case. -/// /// # Examples /// ``` /// use heapless::HistoryBuffer; @@ -16,34 +15,35 @@ /// // Initialize a new buffer with 8 elements, all initially zero. /// let mut buf = HistoryBuffer::<_, 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(), &4); +/// 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 2. +/// // 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, 2); +/// assert_eq!(avg, 4); /// ``` -#[derive(Clone)] pub struct HistoryBuffer { - data: [T; N], + data: [MaybeUninit; N], write_at: usize, + filled: bool, } -impl HistoryBuffer -where - T: Default + Copy, -{ - /// Constructs a new history buffer, where every element is filled with the - /// default value of the type `T`. +impl HistoryBuffer { + const INIT: MaybeUninit = MaybeUninit::uninit(); + + /// Constructs a new history buffer. /// - /// `HistoryBuffer` currently cannot be constructed in `const` context. + /// The construction of a `HistoryBuffer` works in `const` contexts. /// /// # Examples /// @@ -51,16 +51,15 @@ where /// use heapless::HistoryBuffer; /// /// // Allocate a 16-element buffer on the stack - /// let mut x: HistoryBuffer = HistoryBuffer::new(); - /// // All elements are zero - /// assert_eq!(x.as_slice(), [0; 16]); + /// let x: HistoryBuffer = HistoryBuffer::new(); + /// assert_eq!(x.len(), 0); /// ``` - pub fn new() -> Self { + #[inline] + pub const fn new() -> Self { Self { - // seems not yet implemented - // data: Default::default(), - data: [T::default(); N], + data: [Self::INIT; N], write_at: 0, + filled: false, } } @@ -87,10 +86,12 @@ where /// // All elements are four /// assert_eq!(x.as_slice(), [4; 16]); /// ``` + #[inline] pub fn new_with(t: T) -> Self { Self { - data: [t; N], + data: [MaybeUninit::new(t); N], write_at: 0, + filled: true, } } @@ -101,18 +102,35 @@ where } impl HistoryBuffer { + /// Returns the current fill level of the buffer. + #[inline] + pub fn len(&self) -> usize { + if self.filled { + N + } else { + self.write_at + } + } + /// Returns the capacity of the buffer, which is the length of the /// underlying backing array. - pub fn len(&self) -> usize { - self.data.len() + #[inline] + pub fn capacity(&self) -> usize { + N } /// Writes an element to the buffer, overwriting the oldest value. pub fn write(&mut self, t: T) { - self.data[self.write_at] = t; + if self.filled { + // Drop the old before we overwrite it. + unsafe { ptr::drop_in_place(self.data[self.write_at].as_mut_ptr()) } + } + self.data[self.write_at] = MaybeUninit::new(t); + self.write_at += 1; - if self.write_at == self.len() { + if self.write_at == self.capacity() { self.write_at = 0; + self.filled = true; } } @@ -139,20 +157,28 @@ impl HistoryBuffer { /// let mut x: HistoryBuffer = HistoryBuffer::new(); /// x.write(4); /// x.write(10); - /// assert_eq!(x.recent(), &10); + /// assert_eq!(x.recent(), Some(&10)); /// ``` - pub fn recent(&self) -> &T { + pub fn recent(&self) -> Option<&T> { if self.write_at == 0 { - &self.data[self.len() - 1] + if self.filled { + Some(unsafe { &*self.data[self.capacity() - 1].as_ptr() }) + } else { + None + } } else { - &self.data[self.write_at - 1] + Some(unsafe { &*self.data[self.write_at - 1].as_ptr() }) } } /// 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.data + if self.filled { + unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.capacity()) } + } else { + unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.write_at) } + } } } @@ -179,6 +205,17 @@ where } } +impl Drop for HistoryBuffer { + 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(), + )) + } + } +} + #[cfg(test)] mod tests { use crate::HistoryBuffer; @@ -190,7 +227,7 @@ mod tests { assert_eq!(x.as_slice(), [1; 4]); let x: HistoryBuffer = HistoryBuffer::new(); - assert_eq!(x.as_slice(), [0; 4]); + assert_eq!(x.as_slice(), []); } #[test] @@ -198,7 +235,7 @@ mod tests { let mut x: HistoryBuffer = HistoryBuffer::new(); x.write(1); x.write(4); - assert_eq!(x.as_slice(), [1, 4, 0, 0]); + assert_eq!(x.as_slice(), [1, 4]); x.write(5); x.write(6); @@ -213,7 +250,7 @@ mod tests { fn clear() { let mut x: HistoryBuffer = HistoryBuffer::new_with(1); x.clear(); - assert_eq!(x.as_slice(), [0; 4]); + assert_eq!(x.as_slice(), []); let mut x: HistoryBuffer = HistoryBuffer::new(); x.clear_with(1); @@ -223,16 +260,16 @@ mod tests { #[test] fn recent() { let mut x: HistoryBuffer = HistoryBuffer::new(); - assert_eq!(x.recent(), &0); + assert_eq!(x.recent(), None); x.write(1); x.write(4); - assert_eq!(x.recent(), &4); + assert_eq!(x.recent(), Some(&4)); x.write(5); x.write(6); x.write(10); - assert_eq!(x.recent(), &10); + assert_eq!(x.recent(), Some(&10)); } #[test] diff --git a/src/indexmap.rs b/src/indexmap.rs index 6b4064a3..20b1b5e4 100644 --- a/src/indexmap.rs +++ b/src/indexmap.rs @@ -375,7 +375,6 @@ where K: Eq + Hash, S: Default + Hasher, { - // TODO turn into a `const fn`; needs `mem::zeroed` to be a `const fn` /// Creates an empty `IndexMap`. /// /// **NOTE** This constructor will become a `const fn` in the future diff --git a/src/lib.rs b/src/lib.rs index c4619052..54b4f653 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,15 +63,14 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.36 and up with its default set of features. +//! This crate is guaranteed to compile on stable Rust 1.51 and up with its default set of features. //! It *might* compile on older versions but that may change in any new patch release. -// experimental usage of const generics, requires nightly 2020-08-18 (or newer) #![cfg_attr(not(test), no_std)] #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -// #![deny(warnings)] +#![deny(warnings)] pub use binary_heap::BinaryHeap; pub use histbuf::HistoryBuffer; diff --git a/src/spsc/mod.rs b/src/spsc/mod.rs index 0aea97b0..ef7820b0 100644 --- a/src/spsc/mod.rs +++ b/src/spsc/mod.rs @@ -142,7 +142,7 @@ where /// A statically allocated single producer single consumer queue with a capacity of `N` elements /// -/// *IMPORTANT*: To get better performance use a capacity that is a power of 2 (e.g. `U16`, `U32`, +/// *IMPORTANT*: To get better performance use a capacity that is a power of 2 (e.g. `16`, `32`, /// etc.). /// /// By default `spsc::Queue` will use `usize` integers to hold the indices to its head and tail. For