diff --git a/CHANGELOG.md b/CHANGELOG.md index fd126328..51abed01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.5.4] - 2020-04-06 + +### Added + +- Added `StableDeref` implementation for `pool::Box` and `pool::singleton::Box`. + ## [v0.5.3] - 2020-01-27 ### Added @@ -288,7 +294,9 @@ architecture. - Initial release -[Unreleased]: https://github.com/japaric/heapless/compare/v0.5.2...HEAD +[Unreleased]: https://github.com/japaric/heapless/compare/v0.5.4...HEAD +[v0.5.4]: https://github.com/japaric/heapless/compare/v0.5.3...v0.5.4 +[v0.5.3]: https://github.com/japaric/heapless/compare/v0.5.2...v0.5.3 [v0.5.2]: https://github.com/japaric/heapless/compare/v0.5.1...v0.5.2 [v0.5.1]: https://github.com/japaric/heapless/compare/v0.5.0...v0.5.1 [v0.5.0]: https://github.com/japaric/heapless/compare/v0.4.4...v0.5.0 diff --git a/Cargo.toml b/Cargo.toml index 7f13d87b..f1a0cb11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,11 +17,12 @@ keywords = [ license = "MIT OR Apache-2.0" name = "heapless" repository = "https://github.com/japaric/heapless" -version = "0.5.3" +version = "0.5.4" [features] default = ["cas"] cas = [] +ufmt-impl = ["ufmt-write"] # only for tests __trybuild = [] @@ -37,3 +38,14 @@ hash32 = "0.1.0" version = "1" optional = true default-features = false + +[dependencies.stable_deref_trait] +version = "1" +default-features = false + +[dependencies.ufmt-write] +version = "0.1" +optional = true + +[dev-dependencies.ufmt] +version = "0.1" diff --git a/build.rs b/build.rs index 203c7e9f..8a29d9e5 100644 --- a/build.rs +++ b/build.rs @@ -11,7 +11,7 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-cfg=armv7m"); } else if target.starts_with("thumbv7em-") { println!("cargo:rustc-cfg=armv7m"); - } else if target.starts_with("armv7r-") { + } else if target.starts_with("armv7r-") | target.starts_with("armebv7r-") { println!("cargo:rustc-cfg=armv7r"); } else if target.starts_with("thumbv8m.base") { println!("cargo:rustc-cfg=armv8m_base"); diff --git a/src/histbuf.rs b/src/histbuf.rs new file mode 100644 index 00000000..31d36cd9 --- /dev/null +++ b/src/histbuf.rs @@ -0,0 +1,256 @@ +use generic_array::{ArrayLength, GenericArray, sequence::GenericSequence}; + +/// 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; +/// use heapless::consts::*; +/// +/// // Initialize a new buffer with 8 elements, all initially zero. +/// let mut buf = HistoryBuffer::<_, U8>::new(); +/// +/// buf.write(3); +/// buf.write(5); +/// buf.extend(&[4, 4]); +/// +/// // The most recent written element is a four. +/// assert_eq!(buf.recent(), &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. +/// let avg = buf.as_slice().iter().sum::() / buf.len(); +/// assert_eq!(avg, 2); +/// ``` +#[derive(Clone)] +pub struct HistoryBuffer +where + N: ArrayLength, +{ + data: GenericArray, + write_at: usize, +} + + +impl HistoryBuffer +where + N: ArrayLength, + T: Default, +{ + /// Constructs a new history buffer, where every element is filled with the + /// default value of the type `T`. + /// + /// `HistoryBuffer` currently cannot be constructed in `const` context. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// use heapless::consts::*; + /// + /// // 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]); + /// ``` + pub fn new() -> Self { + Self { + data: Default::default(), + write_at: 0, + } + } + + /// Clears the buffer, replacing every element with the default value of + /// type `T`. + pub fn clear(&mut self) { + *self = Self::new(); + } +} + +impl HistoryBuffer +where + N: ArrayLength, + T: Clone, +{ + /// Constructs a new history buffer, where every element is the given value. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// use heapless::consts::*; + /// + /// // Allocate a 16-element buffer on the stack + /// let mut x: HistoryBuffer = HistoryBuffer::new_with(4); + /// // All elements are four + /// assert_eq!(x.as_slice(), [4; 16]); + /// ``` + pub fn new_with(t: T) -> Self { + Self { + data: GenericArray::generate(|_| t.clone()), + write_at: 0, + } + } + + /// Clears the buffer, replacing every element with the given value. + pub fn clear_with(&mut self, t: T) { + *self = Self::new_with(t); + } +} + +impl HistoryBuffer +where + N: ArrayLength, +{ + /// Returns the capacity of the buffer, which is the length of the + /// underlying backing array. + pub fn len(&self) -> usize { + self.data.len() + } + + /// Writes an element to the buffer, overwriting the oldest value. + pub fn write(&mut self, t: T) { + self.data[self.write_at] = t; + self.write_at = (self.write_at + 1) % self.len(); + } + + /// 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::HistoryBuffer; + /// use heapless::consts::*; + /// + /// let mut x: HistoryBuffer = HistoryBuffer::new(); + /// x.write(4); + /// x.write(10); + /// assert_eq!(x.recent(), &10); + /// ``` + pub fn recent(&self) -> &T { + &self.data[(self.write_at + self.len() - 1) % self.len()] + } + + /// 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 + } +} + +impl Extend for HistoryBuffer +where + N: ArrayLength, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter.into_iter() { + self.write(item); + } + } +} + +impl<'a, T, N> Extend<&'a T> for HistoryBuffer +where + T: 'a + Clone, + N: ArrayLength, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + self.extend(iter.into_iter().cloned()) + } +} + +#[cfg(test)] +mod tests { + use crate::{consts::*, HistoryBuffer}; + + #[test] + fn new() { + let x: HistoryBuffer = HistoryBuffer::new_with(1); + assert_eq!(x.len(), 4); + assert_eq!(x.as_slice(), [1; 4]); + + let x: HistoryBuffer = HistoryBuffer::new(); + assert_eq!(x.as_slice(), [0; 4]); + } + + #[test] + fn write() { + let mut x: HistoryBuffer = HistoryBuffer::new(); + x.write(1); + x.write(4); + assert_eq!(x.as_slice(), [1, 4, 0, 0]); + + 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: HistoryBuffer = HistoryBuffer::new_with(1); + x.clear(); + assert_eq!(x.as_slice(), [0; 4]); + + let mut x: HistoryBuffer = HistoryBuffer::new(); + x.clear_with(1); + assert_eq!(x.as_slice(), [1; 4]); + } + + #[test] + fn recent() { + let mut x: HistoryBuffer = HistoryBuffer::new(); + assert_eq!(x.recent(), &0); + + x.write(1); + x.write(4); + assert_eq!(x.recent(), &4); + + x.write(5); + x.write(6); + x.write(10); + assert_eq!(x.recent(), &10); + } + + #[test] + fn as_slice() { + let mut x: HistoryBuffer = HistoryBuffer::new(); + + x.extend([1, 2, 3, 4, 5].iter()); + + assert_eq!(x.as_slice(), [5, 2, 3, 4]); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9a81010a..221e169c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,14 @@ //! - [`mpmc::Q*`](mpmc/index.html) -- multiple producer multiple consumer lock-free queue //! - [`spsc::Queue`](spsc/struct.Queue.html) -- single producer single consumer lock-free queue //! +//! # Optional Features +//! +//! The `heapless` crate provides the following optional Cargo features: +//! +//! - `ufmt-impl`: Implement [`ufmt_write::uWrite`] for `String` and `Vec` +//! +//! [`ufmt_write::uWrite`]: https://docs.rs/ufmt-write/ +//! //! # Minimum Supported Rust Version (MSRV) //! //! This crate is guaranteed to compile on stable Rust 1.36 and up with its default set of features. @@ -75,6 +83,7 @@ pub use indexset::{FnvIndexSet, IndexSet}; pub use linear_map::LinearMap; pub use string::String; pub use vec::Vec; +pub use histbuf::HistoryBuffer; // NOTE this code was last ported from v0.4.1 of the indexmap crate mod indexmap; @@ -82,6 +91,7 @@ mod indexset; mod linear_map; mod string; mod vec; +mod histbuf; #[cfg(feature = "serde")] mod de; @@ -97,4 +107,7 @@ pub mod pool; #[cfg(has_atomics)] pub mod spsc; +#[cfg(feature = "ufmt-impl")] +mod ufmt; + mod sealed; diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 6b945c00..a8dafdfd 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -375,6 +375,8 @@ unsafe impl Send for Box where T: Send {} unsafe impl Sync for Box where T: Sync {} +unsafe impl stable_deref_trait::StableDeref for Box {} + impl AsSlice for Box where A: AsSlice, diff --git a/src/pool/singleton.rs b/src/pool/singleton.rs index d7e5a2a6..785c9000 100644 --- a/src/pool/singleton.rs +++ b/src/pool/singleton.rs @@ -160,6 +160,8 @@ where } } +unsafe impl stable_deref_trait::StableDeref for Box

{} + impl

fmt::Debug for Box

where P: Pool, diff --git a/src/ufmt.rs b/src/ufmt.rs new file mode 100644 index 00000000..b3f30e63 --- /dev/null +++ b/src/ufmt.rs @@ -0,0 +1,71 @@ +use ufmt_write::uWrite; + +use crate::{ + ArrayLength, + string::String, + vec::Vec, +}; + +impl uWrite for String +where + N: ArrayLength, +{ + type Error = (); + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + self.push_str(s) + } +} + +impl uWrite for Vec +where + N: ArrayLength, +{ + type Error = (); + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + self.extend_from_slice(s.as_bytes()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use ufmt::{derive::uDebug, uwrite}; + + use crate::consts::*; + + #[derive(uDebug)] + struct Pair { + x: u32, + y: u32, + } + + #[test] + fn test_string() { + let a = 123; + let b = Pair { x: 0, y: 1234 }; + + let mut s = String::::new(); + uwrite!(s, "{} -> {:?}", a, b).unwrap(); + + assert_eq!(s, "123 -> Pair { x: 0, y: 1234 }"); + } + + #[test] + fn test_string_err() { + let p = Pair { x: 0, y: 1234 }; + let mut s = String::::new(); + assert!(uwrite!(s, "{:?}", p).is_err()); + } + + #[test] + fn test_vec() { + let a = 123; + let b = Pair { x: 0, y: 1234 }; + + let mut v = Vec::::new(); + uwrite!(v, "{} -> {:?}", a, b).unwrap(); + + assert_eq!(v, b"123 -> Pair { x: 0, y: 1234 }"); + } +} diff --git a/src/vec.rs b/src/vec.rs index f8265476..8d5345fc 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -197,6 +197,28 @@ where Vec(crate::i::Vec::new()) } + /// Constructs a new vector with a fixed capacity of `N` and fills it + /// with the provided slice. + /// + /// This is equivalent to the following code: + /// + /// ``` + /// use heapless::Vec; + /// use heapless::consts::*; + /// + /// let mut v: Vec = Vec::new(); + /// v.extend_from_slice(&[1, 2, 3]).unwrap(); + /// ``` + #[inline] + pub fn from_slice(other: &[T]) -> Result + where + T: Clone, + { + let mut v = Vec::new(); + v.extend_from_slice(other)?; + Ok(v) + } + /* Public API */ /// Returns the maximum number of elements the vector can hold pub fn capacity(&self) -> usize { @@ -344,6 +366,54 @@ where pub(crate) fn is_full(&self) -> bool { self.0.is_full() } + + /// Returns `true` if `needle` is a prefix of the Vec. + /// + /// Always returns `true` if `needle` is an empty slice. + /// + /// # Examples + /// + /// ``` + /// use heapless::Vec; + /// use heapless::consts::*; + /// + /// let v: Vec<_, U8> = Vec::from_slice(b"abc").unwrap(); + /// assert_eq!(v.starts_with(b""), true); + /// assert_eq!(v.starts_with(b"ab"), true); + /// assert_eq!(v.starts_with(b"bc"), false); + /// ``` + #[inline] + pub fn starts_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + let n = needle.len(); + self.len() >= n && needle == &self[..n] + } + + /// Returns `true` if `needle` is a suffix of the Vec. + /// + /// Always returns `true` if `needle` is an empty slice. + /// + /// # Examples + /// + /// ``` + /// use heapless::Vec; + /// use heapless::consts::*; + /// + /// let v: Vec<_, U8> = Vec::from_slice(b"abc").unwrap(); + /// assert_eq!(v.ends_with(b""), true); + /// assert_eq!(v.ends_with(b"ab"), false); + /// assert_eq!(v.ends_with(b"bc"), true); + /// ``` + #[inline] + pub fn ends_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + let (v, n) = (self.len(), needle.len()); + v >= n && needle == &self[v - n..] + } } impl Default for Vec @@ -656,6 +726,7 @@ where #[cfg(test)] mod tests { + use as_slice::AsSlice; use crate::{consts::*, Vec}; use core::fmt::Write; @@ -929,6 +1000,55 @@ mod tests { fn write() { let mut v: Vec = Vec::new(); write!(v, "{:x}", 1234).unwrap(); - assert_eq!(&v[..], b"4d2") + assert_eq!(&v[..], b"4d2"); + } + + fn extend_from_slice() { + let mut v: Vec = Vec::new(); + assert_eq!(v.len(), 0); + v.extend_from_slice(&[1, 2]).unwrap(); + assert_eq!(v.len(), 2); + assert_eq!(v.as_slice(), &[1, 2]); + v.extend_from_slice(&[3]).unwrap(); + assert_eq!(v.len(), 3); + assert_eq!(v.as_slice(), &[1, 2, 3]); + assert!(v.extend_from_slice(&[4, 5]).is_err()); + assert_eq!(v.len(), 3); + assert_eq!(v.as_slice(), &[1, 2, 3]); + } + + #[test] + fn from_slice() { + // Successful construction + let v: Vec = Vec::from_slice(&[1, 2, 3]).unwrap(); + assert_eq!(v.len(), 3); + assert_eq!(v.as_slice(), &[1, 2, 3]); + + // Slice too large + assert!(Vec::::from_slice(&[1, 2, 3]).is_err()); + } + + #[test] + fn starts_with() { + let v: Vec<_, U8> = Vec::from_slice(b"ab").unwrap(); + assert!(v.starts_with(&[])); + assert!(v.starts_with(b"")); + assert!(v.starts_with(b"a")); + assert!(v.starts_with(b"ab")); + assert!(!v.starts_with(b"abc")); + assert!(!v.starts_with(b"ba")); + assert!(!v.starts_with(b"b")); + } + + #[test] + fn ends_with() { + let v: Vec<_, U8> = Vec::from_slice(b"ab").unwrap(); + assert!(v.ends_with(&[])); + assert!(v.ends_with(b"")); + assert!(v.ends_with(b"b")); + assert!(v.ends_with(b"ab")); + assert!(!v.ends_with(b"abc")); + assert!(!v.ends_with(b"ba")); + assert!(!v.ends_with(b"a")); } } diff --git a/tests/cpass.rs b/tests/cpass.rs index bf5e60c8..f834e9d1 100644 --- a/tests/cpass.rs +++ b/tests/cpass.rs @@ -3,7 +3,7 @@ use heapless::{ consts, spsc::{Consumer, Producer, Queue}, - Vec, + Vec, HistoryBuffer, }; #[test] @@ -22,4 +22,5 @@ fn send() { is_send::>(); is_send::>(); is_send::>(); + is_send::>(); }