Merge branch 'master' into feature/historybuf_ordered_iterator

This commit is contained in:
Finn Bear 2022-01-15 23:34:01 -08:00 committed by GitHub
commit 4d0a160638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 187 additions and 141 deletions

28
.github/workflows/changelog.yml vendored Normal file
View File

@ -0,0 +1,28 @@
# Check that the changelog is updated for all changes.
#
# This is only run for PRs.
on:
pull_request:
# opened, reopened, synchronize are the default types for pull_request.
# labeled, unlabeled ensure this check is also run if a label is added or removed.
types: [opened, reopened, labeled, unlabeled, synchronize]
name: Changelog
jobs:
changelog:
name: Changelog
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Check that changelog updated
uses: dangoslen/changelog-enforcer@v3
with:
changeLogPath: CHANGELOG.md
skipLabels: 'needs-changelog, skip-changelog'
missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -11,6 +11,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `OldestOrdered` iterator for `HistoryBuffer`
## [v0.7.9] - 2021-12-16
### Fixed
- Fix `IndexMap` and `IndexSet` bounds
- Make `IndexSet::new()` a `const fn`
## [v0.7.8] - 2021-11-11
### Added
- A span of `defmt` versions is now supported (`0.2` and `0.3`)
## [v0.7.7] - 2021-09-22
### Fixed
@ -421,7 +434,9 @@ architecture.
- Initial release
[Unreleased]: https://github.com/japaric/heapless/compare/v0.7.7...HEAD
[Unreleased]: https://github.com/japaric/heapless/compare/v0.7.9...HEAD
[v0.7.9]: https://github.com/japaric/heapless/compare/v0.7.8...v0.7.9
[v0.7.8]: https://github.com/japaric/heapless/compare/v0.7.7...v0.7.8
[v0.7.7]: https://github.com/japaric/heapless/compare/v0.7.6...v0.7.7
[v0.7.6]: https://github.com/japaric/heapless/compare/v0.7.5...v0.7.6
[v0.7.5]: https://github.com/japaric/heapless/compare/v0.7.4...v0.7.5

View File

@ -12,7 +12,7 @@ keywords = ["static", "no-heap"]
license = "MIT OR Apache-2.0"
name = "heapless"
repository = "https://github.com/japaric/heapless"
version = "0.7.7"
version = "0.7.9"
[features]
default = ["cas"]
@ -24,6 +24,7 @@ x86-sync-pool = []
__trybuild = []
# Enable larger MPMC sizes.
mpmc_large = []
# This flag has no version guarantee, the `defmt` dependency can be updated in a patch release
defmt-impl = ["defmt"]
[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dev-dependencies]
@ -55,9 +56,5 @@ optional = true
version = "0.1"
[dependencies.defmt]
version = "0.2.1"
version = ">=0.2.0,<0.4"
optional = true
[dev-dependencies.defmt]
version = "0.2.1"
features = ["unstable-test"]

View File

@ -9,6 +9,7 @@
// n)` in-place heapsort.
use core::{
cmp::Ordering,
fmt,
marker::PhantomData,
mem::{self, ManuallyDrop},
@ -16,7 +17,6 @@ use core::{
ptr, slice,
};
use crate::sealed::binary_heap::Kind;
use crate::vec::Vec;
/// Min-heap
@ -25,6 +25,32 @@ pub enum Min {}
/// Max-heap
pub enum Max {}
/// The binary heap kind: min-heap or max-heap
pub trait Kind: private::Sealed {
#[doc(hidden)]
fn ordering() -> Ordering;
}
impl Kind for Min {
fn ordering() -> Ordering {
Ordering::Less
}
}
impl Kind for Max {
fn ordering() -> Ordering {
Ordering::Greater
}
}
/// Sealed traits
mod private {
pub trait Sealed {}
}
impl private::Sealed for Max {}
impl private::Sealed for Min {}
/// A priority queue implemented with a binary heap.
///
/// This can be either a min-heap or a max-heap.

View File

@ -1,6 +1,5 @@
use crate::{
sealed::binary_heap::Kind as BinaryHeapKind, BinaryHeap, IndexMap, IndexSet, LinearMap, String,
Vec,
binary_heap::Kind as BinaryHeapKind, BinaryHeap, IndexMap, IndexSet, LinearMap, String, Vec,
};
use core::{fmt, marker::PhantomData};
use hash32::{BuildHasherDefault, Hash, Hasher};

View File

@ -21,36 +21,3 @@ where
defmt::write!(fmt, "{=str}", self.as_str());
}
}
#[cfg(test)]
mod tests {
use crate::Vec;
use defmt::Format;
#[test]
/// Tests encoding Vec with defmt, asserting these types may be serialized
/// Note: the exact wire format is NOT checked since its an unstable implementation detail of an external crate.
/// based on https://github.com/knurling-rs/defmt/blob/697a8e807bd766a80ada2d57514a9da1232dbc9a/tests/encode.rs#L523
fn test_defmt_format_vec() {
let val: Vec<_, 8> = Vec::from_slice(b"abc").unwrap();
let mut f = defmt::InternalFormatter::new();
let g = defmt::Formatter { inner: &mut f };
val.format(g);
f.finalize();
}
/// Tests encoding String with defmt, asserting these types may be serialized
/// Note: the exact wire format is NOT checked since its an unstable implementation detail of an external crate.
/// based loosely on https://github.com/knurling-rs/defmt/blob/main/tests/encode.rs#L483
#[test]
fn test_defmt_format_str() {
let mut val: crate::String<32> = crate::String::new();
val.push_str("foo").unwrap();
let mut f = defmt::InternalFormatter::new();
let g = defmt::Formatter { inner: &mut f };
val.format(g);
f.finalize();
}
}

View File

@ -368,6 +368,10 @@ pub struct IndexMap<K, V, S, const N: usize> {
impl<K, V, S, const N: usize> IndexMap<K, V, BuildHasherDefault<S>, N> {
/// Creates an empty `IndexMap`.
pub const fn new() -> Self {
// Const assert
crate::sealed::greater_than_1::<N>();
crate::sealed::power_of_two::<N>();
IndexMap {
build_hasher: BuildHasherDefault::new(),
core: CoreMap::new(),

View File

@ -1,6 +1,6 @@
use crate::indexmap::{self, IndexMap};
use core::{borrow::Borrow, fmt, iter::FromIterator};
use hash32::{BuildHasher, BuildHasherDefault, FnvHasher, Hash, Hasher};
use hash32::{BuildHasher, BuildHasherDefault, FnvHasher, Hash};
/// A [`heapless::IndexSet`](./struct.IndexSet.html) using the
/// default FNV hasher.
@ -74,22 +74,13 @@ pub type FnvIndexSet<T, const N: usize> = IndexSet<T, BuildHasherDefault<FnvHash
/// println!("{}", book);
/// }
/// ```
pub struct IndexSet<T, S, const N: usize>
where
T: Eq + Hash,
{
pub struct IndexSet<T, S, const N: usize> {
map: IndexMap<T, (), S, N>,
}
impl<T, S, const N: usize> IndexSet<T, BuildHasherDefault<S>, N>
where
T: Eq + Hash,
S: Default + Hasher,
{
impl<T, S, const N: usize> IndexSet<T, BuildHasherDefault<S>, N> {
/// Creates an empty `IndexSet`
pub fn new() -> Self {
assert!(N.is_power_of_two());
pub const fn new() -> Self {
IndexSet {
map: IndexMap::new(),
}

View File

@ -1,57 +1,15 @@
/// Sealed traits and implementations for `binary_heap`
pub mod binary_heap {
use crate::binary_heap::{Max, Min};
use core::cmp::Ordering;
/// The binary heap kind: min-heap or max-heap
pub unsafe trait Kind {
#[doc(hidden)]
fn ordering() -> Ordering;
}
unsafe impl Kind for Min {
fn ordering() -> Ordering {
Ordering::Less
}
}
unsafe impl Kind for Max {
fn ordering() -> Ordering {
Ordering::Greater
}
}
}
/// Sealed traits and implementations for `LinkedList`
pub mod sorted_linked_list {
use crate::sorted_linked_list::{Max, Min};
use core::cmp::Ordering;
/// The linked list kind: min-list or max-list
pub unsafe trait Kind {
#[doc(hidden)]
fn ordering() -> Ordering;
}
unsafe impl Kind for Min {
fn ordering() -> Ordering {
Ordering::Less
}
}
unsafe impl Kind for Max {
fn ordering() -> Ordering {
Ordering::Greater
}
}
}
#[allow(dead_code)]
#[allow(path_statements)]
pub(crate) const fn smaller_than<const N: usize, const MAX: usize>() {
Assert::<N, MAX>::LESS;
}
#[allow(dead_code)]
#[allow(path_statements)]
pub(crate) const fn greater_than_eq_0<const N: usize>() {
Assert::<N, 0>::GREATER_EQ;
}
#[allow(dead_code)]
#[allow(path_statements)]
pub(crate) const fn greater_than_0<const N: usize>() {

View File

@ -1,6 +1,5 @@
use crate::{
sealed::binary_heap::Kind as BinaryHeapKind, BinaryHeap, IndexMap, IndexSet, LinearMap, String,
Vec,
binary_heap::Kind as BinaryHeapKind, BinaryHeap, IndexMap, IndexSet, LinearMap, String, Vec,
};
use hash32::{BuildHasher, Hash};
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};

View File

@ -25,7 +25,7 @@
//!
//! [`BinaryHeap`]: `crate::binary_heap::BinaryHeap`
use crate::sealed::sorted_linked_list::Kind as LLKind;
use core::cmp::Ordering;
use core::fmt;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
@ -50,6 +50,32 @@ pub struct Min;
/// Marker for Max sorted [`SortedLinkedList`].
pub struct Max;
/// The linked list kind: min-list or max-list
pub trait Kind: private::Sealed {
#[doc(hidden)]
fn ordering() -> Ordering;
}
impl Kind for Min {
fn ordering() -> Ordering {
Ordering::Less
}
}
impl Kind for Max {
fn ordering() -> Ordering {
Ordering::Greater
}
}
/// Sealed traits
mod private {
pub trait Sealed {}
}
impl private::Sealed for Max {}
impl private::Sealed for Min {}
/// A node in the [`SortedLinkedList`].
pub struct Node<T, Idx> {
val: MaybeUninit<T>,
@ -57,14 +83,14 @@ pub struct Node<T, Idx> {
}
/// The linked list.
pub struct SortedLinkedList<T, Idx, Kind, const N: usize>
pub struct SortedLinkedList<T, Idx, K, const N: usize>
where
Idx: SortedLinkedListIndex,
{
list: [Node<T, Idx>; N],
head: Idx,
free: Idx,
_kind: PhantomData<Kind>,
_kind: PhantomData<K>,
}
// Internal macro for generating indexes for the linkedlist and const new for the linked list
@ -115,7 +141,7 @@ macro_rules! impl_index_and_const_new {
}
}
impl<T, Kind, const N: usize> SortedLinkedList<T, $name, Kind, N> {
impl<T, K, const N: usize> SortedLinkedList<T, $name, K, N> {
const UNINIT: Node<T, $name> = Node {
val: MaybeUninit::uninit(),
next: $name::none(),
@ -156,7 +182,7 @@ impl_index_and_const_new!(LinkedIndexU8, u8, new_u8, 254); // val is 2^8 - 2 (on
impl_index_and_const_new!(LinkedIndexU16, u16, new_u16, 65_534); // val is 2^16 - 2
impl_index_and_const_new!(LinkedIndexUsize, usize, new_usize, 4_294_967_294); // val is 2^32 - 2
impl<T, Idx, Kind, const N: usize> SortedLinkedList<T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> SortedLinkedList<T, Idx, K, N>
where
Idx: SortedLinkedListIndex,
{
@ -205,11 +231,11 @@ where
}
}
impl<T, Idx, Kind, const N: usize> SortedLinkedList<T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> SortedLinkedList<T, Idx, K, N>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
/// Pushes a value onto the list without checking if the list is full.
///
@ -230,7 +256,7 @@ where
if self
.read_data_in_node_at(head)
.cmp(self.read_data_in_node_at(new))
!= Kind::ordering()
!= K::ordering()
{
self.node_at_mut(new).next = self.head;
self.head = Idx::new_unchecked(new);
@ -242,7 +268,7 @@ where
if self
.read_data_in_node_at(next)
.cmp(self.read_data_in_node_at(new))
!= Kind::ordering()
!= K::ordering()
{
break;
}
@ -307,7 +333,7 @@ where
/// assert_eq!(iter.next(), Some(&1));
/// assert_eq!(iter.next(), None);
/// ```
pub fn iter(&self) -> Iter<'_, T, Idx, Kind, N> {
pub fn iter(&self) -> Iter<'_, T, Idx, K, N> {
Iter {
list: self,
index: self.head,
@ -336,7 +362,7 @@ where
/// assert_eq!(ll.pop(), Ok(1));
/// assert_eq!(ll.pop(), Err(()));
/// ```
pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, T, Idx, Kind, N>>
pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, T, Idx, K, N>>
where
F: FnMut(&T) -> bool,
{
@ -486,21 +512,21 @@ where
}
/// Iterator for the linked list.
pub struct Iter<'a, T, Idx, Kind, const N: usize>
pub struct Iter<'a, T, Idx, K, const N: usize>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
list: &'a SortedLinkedList<T, Idx, Kind, N>,
list: &'a SortedLinkedList<T, Idx, K, N>,
index: Idx,
}
impl<'a, T, Idx, Kind, const N: usize> Iterator for Iter<'a, T, Idx, Kind, N>
impl<'a, T, Idx, K, const N: usize> Iterator for Iter<'a, T, Idx, K, N>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
type Item = &'a T;
@ -515,24 +541,24 @@ where
}
/// Comes from [`SortedLinkedList::find_mut`].
pub struct FindMut<'a, T, Idx, Kind, const N: usize>
pub struct FindMut<'a, T, Idx, K, const N: usize>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
list: &'a mut SortedLinkedList<T, Idx, Kind, N>,
list: &'a mut SortedLinkedList<T, Idx, K, N>,
is_head: bool,
prev_index: Idx,
index: Idx,
maybe_changed: bool,
}
impl<'a, T, Idx, Kind, const N: usize> FindMut<'a, T, Idx, Kind, N>
impl<'a, T, Idx, K, const N: usize> FindMut<'a, T, Idx, K, N>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
fn pop_internal(&mut self) -> T {
if self.is_head {
@ -616,11 +642,11 @@ where
}
}
impl<T, Idx, Kind, const N: usize> Drop for FindMut<'_, T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> Drop for FindMut<'_, T, Idx, K, N>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
fn drop(&mut self) {
// Only resort the list if the element has changed
@ -631,11 +657,11 @@ where
}
}
impl<T, Idx, Kind, const N: usize> Deref for FindMut<'_, T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> Deref for FindMut<'_, T, Idx, K, N>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
type Target = T;
@ -645,11 +671,11 @@ where
}
}
impl<T, Idx, Kind, const N: usize> DerefMut for FindMut<'_, T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> DerefMut for FindMut<'_, T, Idx, K, N>
where
T: Ord,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.maybe_changed = true;
@ -659,11 +685,11 @@ where
}
// /// Useful for debug during development.
// impl<T, Idx, Kind, const N: usize> fmt::Debug for FindMut<'_, T, Idx, Kind, N>
// impl<T, Idx, K, const N: usize> fmt::Debug for FindMut<'_, T, Idx, K, N>
// where
// T: Ord + core::fmt::Debug,
// Idx: SortedLinkedListIndex,
// Kind: LLKind,
// K: Kind,
// {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// f.debug_struct("FindMut")
@ -683,18 +709,18 @@ where
// }
// }
impl<T, Idx, Kind, const N: usize> fmt::Debug for SortedLinkedList<T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> fmt::Debug for SortedLinkedList<T, Idx, K, N>
where
T: Ord + core::fmt::Debug,
Idx: SortedLinkedListIndex,
Kind: LLKind,
K: Kind,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T, Idx, Kind, const N: usize> Drop for SortedLinkedList<T, Idx, Kind, N>
impl<T, Idx, K, const N: usize> Drop for SortedLinkedList<T, Idx, K, N>
where
Idx: SortedLinkedListIndex,
{

View File

@ -1,4 +1,7 @@
use core::{cmp::Ordering, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, ptr, slice};
use core::{
cmp::Ordering, convert::TryFrom, fmt, hash, iter::FromIterator, mem::MaybeUninit, ops, ptr,
slice,
};
use hash32;
/// A fixed capacity [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)
@ -53,8 +56,8 @@ impl<T, const N: usize> Vec<T, N> {
/// ```
/// `Vec` `const` constructor; wrap the returned value in [`Vec`](../struct.Vec.html)
pub const fn new() -> Self {
// Const assert N > 0
crate::sealed::greater_than_0::<N>();
// Const assert N >= 0
crate::sealed::greater_than_eq_0::<N>();
Self {
buffer: [Self::INIT; N],
@ -569,6 +572,14 @@ impl<T, const N: usize> Drop for Vec<T, N> {
}
}
impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec<T, N> {
type Error = ();
fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
Vec::from_slice(slice)
}
}
impl<T, const N: usize> Extend<T> for Vec<T, N> {
fn extend<I>(&mut self, iter: I)
where
@ -1222,4 +1233,29 @@ mod tests {
assert!(!v.ends_with(b"ba"));
assert!(!v.ends_with(b"a"));
}
#[test]
fn zero_capacity() {
let mut v: Vec<u8, 0> = Vec::new();
// Validate capacity
assert_eq!(v.capacity(), 0);
// Make sure there is no capacity
assert!(v.push(1).is_err());
// Validate length
assert_eq!(v.len(), 0);
// Validate pop
assert_eq!(v.pop(), None);
// Validate slice
assert_eq!(v.as_slice(), &[]);
// Validate empty
assert!(v.is_empty());
// Validate full
assert!(v.is_full());
}
}