From 102856315542dd67c3889e2ed4b6b53590c7df59 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 18 Dec 2019 07:47:24 +0100 Subject: [PATCH 1/9] add the HistoryBuffer type --- src/histbuf.rs | 325 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + tests/cpass.rs | 3 +- 3 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 src/histbuf.rs diff --git a/src/histbuf.rs b/src/histbuf.rs new file mode 100644 index 00000000..b10dab60 --- /dev/null +++ b/src/histbuf.rs @@ -0,0 +1,325 @@ +use generic_array::{ArrayLength, GenericArray, sequence::GenericSequence}; +use as_slice::{AsSlice, AsMutSlice}; + +/// A "history buffer", similar to a write-only ring buffer. +/// +/// This buffer keeps a fixed number of elements. On push, the oldest element +/// is overwritten. It 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.push(3); +/// buf.push(5); +/// buf.extend(&[4, 4]); +/// +/// // The first (oldest) element is still zero. +/// assert_eq!(buf.first(), &0); +/// // The last (newest) element is a four. +/// assert_eq!(buf.last(), &4); +/// for el in buf.iter() { println!("{:?}", el); } +/// +/// // Now we can prepare an average of all values, which comes out to 2. +/// let avg = buf.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!(x.iter().eq([0; 16].iter())); + /// ``` + 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!(x.iter().eq([4; 16].iter())); + /// ``` + 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 length 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 push(&mut self, t: T) { + self.data[self.write_at] = t; + self.write_at = (self.write_at + 1) % self.len(); + } + + /// Clones and pushes 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.push(item.clone()); + } + } + + /// Returns a reference to the oldest (least recently pushed) value. + pub fn first(&self) -> &T { + &self.data[self.write_at] + } + + /// Returns a reference to the newest (most recently pushed) value. + /// + /// # Examples + /// + /// ``` + /// use heapless::HistoryBuffer; + /// use heapless::consts::*; + /// + /// let mut x: HistoryBuffer = HistoryBuffer::new(); + /// x.push(4); + /// x.push(10); + /// assert_eq!(x.last(), &10); + /// ``` + pub fn last(&self) -> &T { + &self.data[(self.write_at + self.len() - 1) % self.len()] + } + + /// Returns an iterator over the elements of the buffer. + /// + /// Note: if the order of elements is not important, use + /// `.as_slice().iter()` instead. + pub fn iter(&self) -> Iter<'_, T> { + Iter { + data: &self.data, + cur: self.write_at, + left: self.len(), + } + } + + /// Returns an iterator over mutable elements of the buffer. + /// + /// Note: if the order of elements is not important, use + /// `.as_mut_slice().iter_mut()` instead. + pub fn iter_mut(&mut self) -> impl Iterator { + let (p1, p2) = self.data.split_at_mut(self.write_at); + p2.iter_mut().chain(p1) + } +} + +pub struct Iter<'a, T> { + data: &'a [T], + cur: usize, + left: usize, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + if self.left == 0 { + None + } else { + let el = &self.data[self.cur]; + self.cur = (self.cur + 1) % self.data.len(); + self.left -= 1; + Some(el) + } + } +} + +impl Extend for HistoryBuffer +where + N: ArrayLength, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter.into_iter() { + self.push(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()) + } +} + +impl AsSlice for HistoryBuffer +where + N: ArrayLength, +{ + type Element = T; + + /// Returns the array slice backing the buffer, without keeping track + /// of the write position. Therefore, the element order is unspecified. + fn as_slice(&self) -> &[T] { + &self.data + } +} + +impl AsMutSlice for HistoryBuffer +where + N: ArrayLength, +{ + /// Returns the array slice backing the buffer, without keeping track + /// of the write position. Therefore, the element order is unspecified. + fn as_mut_slice(&mut self) -> &mut [T] { + &mut self.data + } +} + +#[cfg(test)] +mod tests { + use crate::{consts::*, HistoryBuffer}; + use as_slice::{AsSlice, AsMutSlice}; + + #[test] + fn new() { + let x: HistoryBuffer = HistoryBuffer::new_with(1); + assert!(x.iter().eq([1; 4].iter())); + + let x: HistoryBuffer = HistoryBuffer::new(); + assert!(x.iter().eq([0; 4].iter())); + } + + #[test] + fn push_iter() { + let mut x: HistoryBuffer = HistoryBuffer::new(); + x.push(1); + x.push(4); + assert!(x.iter().eq([0, 0, 1, 4].iter())); + + x.push(5); + x.push(6); + x.push(10); + assert!(x.iter().eq([4, 5, 6, 10].iter())); + + x.extend([11, 12].iter()); + assert!(x.iter().eq([6, 10, 11, 12].iter())); + + assert!(x.iter_mut().eq([6, 10, 11, 12].iter())); + } + + #[test] + fn clear() { + let mut x: HistoryBuffer = HistoryBuffer::new_with(1); + x.clear(); + assert!(x.iter().eq([0; 4].iter())); + + let mut x: HistoryBuffer = HistoryBuffer::new(); + x.clear_with(1); + assert!(x.iter().eq([1; 4].iter())); + } + + #[test] + fn first_last() { + let mut x: HistoryBuffer = HistoryBuffer::new(); + x.push(1); + x.push(4); + assert_eq!(x.first(), &0); + assert_eq!(x.last(), &4); + + x.push(5); + x.push(6); + x.push(10); + assert_eq!(x.first(), &4); + assert_eq!(x.last(), &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]); + assert_eq!(x.as_mut_slice(), &mut [5, 2, 3, 4]); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9a81010a..c4141b87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,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 +83,7 @@ mod indexset; mod linear_map; mod string; mod vec; +mod histbuf; #[cfg(feature = "serde")] mod de; 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::>(); } From 1625603982db41d6f01bbfa84d53bd06eaa83f58 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 7 Jan 2020 08:26:00 +0100 Subject: [PATCH 2/9] reduce HistoryBuffer API as discussed in PR --- src/histbuf.rs | 177 +++++++++++++++---------------------------------- 1 file changed, 54 insertions(+), 123 deletions(-) diff --git a/src/histbuf.rs b/src/histbuf.rs index b10dab60..31d36cd9 100644 --- a/src/histbuf.rs +++ b/src/histbuf.rs @@ -1,11 +1,10 @@ use generic_array::{ArrayLength, GenericArray, sequence::GenericSequence}; -use as_slice::{AsSlice, AsMutSlice}; -/// A "history buffer", similar to a write-only ring buffer. +/// A "history buffer", similar to a write-only ring buffer of fixed length. /// -/// This buffer keeps a fixed number of elements. On push, the oldest element -/// is overwritten. It is useful to keep a history of values with some desired -/// depth, and for example calculate a rolling average. +/// 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 @@ -20,18 +19,18 @@ use as_slice::{AsSlice, AsMutSlice}; /// // Initialize a new buffer with 8 elements, all initially zero. /// let mut buf = HistoryBuffer::<_, U8>::new(); /// -/// buf.push(3); -/// buf.push(5); +/// buf.write(3); +/// buf.write(5); /// buf.extend(&[4, 4]); /// -/// // The first (oldest) element is still zero. -/// assert_eq!(buf.first(), &0); -/// // The last (newest) element is a four. -/// assert_eq!(buf.last(), &4); -/// for el in buf.iter() { println!("{:?}", el); } +/// // 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.iter().sum::() / buf.len(); +/// let avg = buf.as_slice().iter().sum::() / buf.len(); /// assert_eq!(avg, 2); /// ``` #[derive(Clone)] @@ -63,7 +62,7 @@ where /// // Allocate a 16-element buffer on the stack /// let mut x: HistoryBuffer = HistoryBuffer::new(); /// // All elements are zero - /// assert!(x.iter().eq([0; 16].iter())); + /// assert_eq!(x.as_slice(), [0; 16]); /// ``` pub fn new() -> Self { Self { @@ -95,7 +94,7 @@ where /// // Allocate a 16-element buffer on the stack /// let mut x: HistoryBuffer = HistoryBuffer::new_with(4); /// // All elements are four - /// assert!(x.iter().eq([4; 16].iter())); + /// assert_eq!(x.as_slice(), [4; 16]); /// ``` pub fn new_with(t: T) -> Self { Self { @@ -114,19 +113,19 @@ impl HistoryBuffer where N: ArrayLength, { - /// Returns the length of the buffer, which is the length of the underlying - /// backing array. + /// 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 push(&mut self, t: T) { + pub fn write(&mut self, t: T) { self.data[self.write_at] = t; self.write_at = (self.write_at + 1) % self.len(); } - /// Clones and pushes all elements in a slice to the buffer. + /// 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. @@ -135,16 +134,11 @@ where T: Clone, { for item in other { - self.push(item.clone()); + self.write(item.clone()); } } - /// Returns a reference to the oldest (least recently pushed) value. - pub fn first(&self) -> &T { - &self.data[self.write_at] - } - - /// Returns a reference to the newest (most recently pushed) value. + /// Returns a reference to the most recently written value. /// /// # Examples /// @@ -153,54 +147,18 @@ where /// use heapless::consts::*; /// /// let mut x: HistoryBuffer = HistoryBuffer::new(); - /// x.push(4); - /// x.push(10); - /// assert_eq!(x.last(), &10); + /// x.write(4); + /// x.write(10); + /// assert_eq!(x.recent(), &10); /// ``` - pub fn last(&self) -> &T { + pub fn recent(&self) -> &T { &self.data[(self.write_at + self.len() - 1) % self.len()] } - /// Returns an iterator over the elements of the buffer. - /// - /// Note: if the order of elements is not important, use - /// `.as_slice().iter()` instead. - pub fn iter(&self) -> Iter<'_, T> { - Iter { - data: &self.data, - cur: self.write_at, - left: self.len(), - } - } - - /// Returns an iterator over mutable elements of the buffer. - /// - /// Note: if the order of elements is not important, use - /// `.as_mut_slice().iter_mut()` instead. - pub fn iter_mut(&mut self) -> impl Iterator { - let (p1, p2) = self.data.split_at_mut(self.write_at); - p2.iter_mut().chain(p1) - } -} - -pub struct Iter<'a, T> { - data: &'a [T], - cur: usize, - left: usize, -} - -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - if self.left == 0 { - None - } else { - let el = &self.data[self.cur]; - self.cur = (self.cur + 1) % self.data.len(); - self.left -= 1; - Some(el) - } + /// 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 } } @@ -213,7 +171,7 @@ where I: IntoIterator, { for item in iter.into_iter() { - self.push(item); + self.write(item); } } } @@ -231,86 +189,60 @@ where } } -impl AsSlice for HistoryBuffer -where - N: ArrayLength, -{ - type Element = T; - - /// Returns the array slice backing the buffer, without keeping track - /// of the write position. Therefore, the element order is unspecified. - fn as_slice(&self) -> &[T] { - &self.data - } -} - -impl AsMutSlice for HistoryBuffer -where - N: ArrayLength, -{ - /// Returns the array slice backing the buffer, without keeping track - /// of the write position. Therefore, the element order is unspecified. - fn as_mut_slice(&mut self) -> &mut [T] { - &mut self.data - } -} - #[cfg(test)] mod tests { use crate::{consts::*, HistoryBuffer}; - use as_slice::{AsSlice, AsMutSlice}; #[test] fn new() { let x: HistoryBuffer = HistoryBuffer::new_with(1); - assert!(x.iter().eq([1; 4].iter())); + assert_eq!(x.len(), 4); + assert_eq!(x.as_slice(), [1; 4]); let x: HistoryBuffer = HistoryBuffer::new(); - assert!(x.iter().eq([0; 4].iter())); + assert_eq!(x.as_slice(), [0; 4]); } #[test] - fn push_iter() { + fn write() { let mut x: HistoryBuffer = HistoryBuffer::new(); - x.push(1); - x.push(4); - assert!(x.iter().eq([0, 0, 1, 4].iter())); + x.write(1); + x.write(4); + assert_eq!(x.as_slice(), [1, 4, 0, 0]); - x.push(5); - x.push(6); - x.push(10); - assert!(x.iter().eq([4, 5, 6, 10].iter())); + x.write(5); + x.write(6); + x.write(10); + assert_eq!(x.as_slice(), [10, 4, 5, 6]); x.extend([11, 12].iter()); - assert!(x.iter().eq([6, 10, 11, 12].iter())); - - assert!(x.iter_mut().eq([6, 10, 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!(x.iter().eq([0; 4].iter())); + assert_eq!(x.as_slice(), [0; 4]); let mut x: HistoryBuffer = HistoryBuffer::new(); x.clear_with(1); - assert!(x.iter().eq([1; 4].iter())); + assert_eq!(x.as_slice(), [1; 4]); } #[test] - fn first_last() { + fn recent() { let mut x: HistoryBuffer = HistoryBuffer::new(); - x.push(1); - x.push(4); - assert_eq!(x.first(), &0); - assert_eq!(x.last(), &4); + assert_eq!(x.recent(), &0); - x.push(5); - x.push(6); - x.push(10); - assert_eq!(x.first(), &4); - assert_eq!(x.last(), &10); + 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] @@ -319,7 +251,6 @@ mod tests { x.extend([1, 2, 3, 4, 5].iter()); - assert_eq!(x.as_slice(), &[5, 2, 3, 4]); - assert_eq!(x.as_mut_slice(), &mut [5, 2, 3, 4]); + assert_eq!(x.as_slice(), [5, 2, 3, 4]); } } From b45ea9bd187e32775ee86dbd736bf4209aeb095b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 Mar 2020 13:05:51 +0100 Subject: [PATCH 3/9] Made box StableDeref --- Cargo.toml | 4 ++++ src/pool/mod.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1ff776e9..e16f9b5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,7 @@ hash32 = "0.1.0" version = "1" optional = true default-features = false + +[dependencies.stable_deref_trait] +version = "1" +default-features = false diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 6b945c00..fe31952e 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, From bb712eb2b05948ba635ff452ff10ba8952420841 Mon Sep 17 00:00:00 2001 From: thalesfragoso Date: Mon, 6 Apr 2020 15:24:37 -0300 Subject: [PATCH 4/9] Implement StableDeref for singleton::Box --- CHANGELOG.md | 4 ++++ src/pool/mod.rs | 2 +- src/pool/singleton.rs | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd126328..10028894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Added `StableDeref` implementation for `pool::Box` and `pool::singleton::Box`. + ## [v0.5.3] - 2020-01-27 ### Added diff --git a/src/pool/mod.rs b/src/pool/mod.rs index fe31952e..a8dafdfd 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -375,7 +375,7 @@ unsafe impl Send for Box where T: Send {} unsafe impl Sync for Box where T: Sync {} -unsafe impl stable_deref_trait::StableDeref for Box {} +unsafe impl stable_deref_trait::StableDeref for Box {} impl AsSlice for Box where 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, From f7f0dcdb441c999ad380695815606358d2fe5b8d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 6 Apr 2020 20:57:27 +0200 Subject: [PATCH 5/9] Preparing for v0.5.4 --- CHANGELOG.md | 6 +++++- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10028894..51abed01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ 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`. @@ -292,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 85708f0e..5a164076 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ 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"] From 779564cb9f1c3289906c2c314caadafe32a6bedc Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Fri, 17 Apr 2020 10:01:39 +0200 Subject: [PATCH 6/9] Implement Vec::from_slice --- src/vec.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/vec.rs b/src/vec.rs index b3dbee7c..1ff94949 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 { @@ -644,6 +666,7 @@ where #[cfg(test)] mod tests { + use as_slice::AsSlice; use crate::{consts::*, Vec}; #[test] @@ -911,4 +934,30 @@ mod tests { v.resize_default(1).unwrap(); assert_eq!(v[0], 0); } + + #[test] + 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()); + } } From 5e32bf724492a08e732d3ee48a59f6819b008189 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Fri, 17 Apr 2020 10:13:47 +0200 Subject: [PATCH 7/9] Implement Vec::starts_with and Vec::ends_with --- src/vec.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/vec.rs b/src/vec.rs index 1ff94949..7989fef5 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -366,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 @@ -960,4 +1008,28 @@ mod tests { // 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")); + } } From 8bcf9d89701303a2909957ecff5baabee7f69f07 Mon Sep 17 00:00:00 2001 From: Richard Berry Date: Thu, 30 Apr 2020 20:13:18 +0100 Subject: [PATCH 8/9] Extend ARMv7-R `Pool` support to the bare-metal `armebv7r-` targets --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"); From 92d1a399e8c95d12c79ad92e0456438e10b84254 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Sun, 3 May 2020 13:29:59 +0200 Subject: [PATCH 9/9] Add optional ufmt impls --- Cargo.toml | 8 ++++++ src/lib.rs | 11 +++++++++ src/ufmt.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/ufmt.rs diff --git a/Cargo.toml b/Cargo.toml index 5a164076..f1a0cb11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ version = "0.5.4" [features] default = ["cas"] cas = [] +ufmt-impl = ["ufmt-write"] # only for tests __trybuild = [] @@ -41,3 +42,10 @@ 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/src/lib.rs b/src/lib.rs index c4141b87..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. @@ -99,4 +107,7 @@ pub mod pool; #[cfg(has_atomics)] pub mod spsc; +#[cfg(feature = "ufmt-impl")] +mod ufmt; + mod sealed; 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 }"); + } +}