mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-09-26 20:10:24 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
3b9b9f7548
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -53,6 +53,7 @@ jobs:
|
||||
- nightly
|
||||
features:
|
||||
- ""
|
||||
- "cas"
|
||||
- "serde"
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -92,7 +93,7 @@ jobs:
|
||||
with:
|
||||
use-cross: false
|
||||
command: check
|
||||
args: --target=${{ matrix.target }} --features=${{ matrix.features }}
|
||||
args: --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
|
||||
|
||||
# Run cpass tests
|
||||
testcpass:
|
||||
|
47
CHANGELOG.md
47
CHANGELOG.md
@ -7,17 +7,49 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed `pool` example in docstring.
|
||||
|
||||
### Added
|
||||
- Added support for AVR architecture.
|
||||
|
||||
- Added support for AVR architecture
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
## [v0.7.13] - 2022-05-16
|
||||
|
||||
### Added
|
||||
|
||||
- Added `into_vec` to `BinaryHeap`
|
||||
|
||||
## [v0.7.12] - 2022-05-12
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for AVR architecture.
|
||||
* Add `entry` API to `IndexMap`
|
||||
* Implement `IntoIterator` trait for `Indexmap`
|
||||
* Implement `FromIterator` for `String`
|
||||
* Add `first` and `last` methods to `IndexMap` and `IndexSet`
|
||||
* Add `pop_{front_back}_unchecked` methods to `Deque`
|
||||
|
||||
### Changed
|
||||
|
||||
* Optimize the codegen of `Vec::clone`
|
||||
* `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`
|
||||
|
||||
### Fixed
|
||||
|
||||
* Inserting an item that replaces an already present item will no longer
|
||||
fail with an error
|
||||
|
||||
## [v0.7.11] - 2022-05-09
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed `pool` example in docstring.
|
||||
* Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
|
||||
and `Hole::move_to()` (internal to the binary heap implementation).
|
||||
* Fixed `BinaryHeap` elements are being dropped twice
|
||||
|
||||
## [v0.7.10] - 2022-01-21
|
||||
|
||||
### Fixed
|
||||
@ -455,7 +487,10 @@ architecture.
|
||||
|
||||
- Initial release
|
||||
|
||||
[Unreleased]: https://github.com/japaric/heapless/compare/v0.7.10...HEAD
|
||||
[Unreleased]: https://github.com/japaric/heapless/compare/v0.7.13...HEAD
|
||||
[v0.7.13]: https://github.com/japaric/heapless/compare/v0.7.12...v0.7.13
|
||||
[v0.7.12]: https://github.com/japaric/heapless/compare/v0.7.11...v0.7.12
|
||||
[v0.7.11]: https://github.com/japaric/heapless/compare/v0.7.10...v0.7.11
|
||||
[v0.7.10]: https://github.com/japaric/heapless/compare/v0.7.9...v0.7.10
|
||||
[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
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -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.10"
|
||||
version = "0.7.13"
|
||||
|
||||
[features]
|
||||
default = ["cas"]
|
||||
@ -27,17 +27,14 @@ mpmc_large = []
|
||||
# This flag has no version guarantee, the `defmt` dependency can be updated in a patch release
|
||||
defmt-impl = ["defmt"]
|
||||
|
||||
[target.'cfg(not(target_os = "none"))'.dev-dependencies]
|
||||
scoped_threadpool = "0.1.8"
|
||||
|
||||
[target.thumbv6m-none-eabi.dependencies]
|
||||
atomic-polyfill = { version = "0.1.2", optional = true }
|
||||
|
||||
[target.riscv32i-unknown-none-elf.dependencies]
|
||||
atomic-polyfill = { version = "0.1.4", optional = true }
|
||||
atomic-polyfill = { version = "0.1.4" }
|
||||
|
||||
[target.riscv32imc-unknown-none-elf.dependencies]
|
||||
atomic-polyfill = { version = "0.1.4", optional = true }
|
||||
atomic-polyfill = { version = "0.1.4" }
|
||||
|
||||
[target.avr-atmega328p.dependencies]
|
||||
atomic-polyfill = { version = "0.1.8", optional = true }
|
||||
@ -72,5 +69,8 @@ version = "0.1"
|
||||
version = ">=0.2.0,<0.4"
|
||||
optional = true
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.4.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
9
build.rs
9
build.rs
@ -2,6 +2,8 @@
|
||||
|
||||
use std::{env, error::Error};
|
||||
|
||||
use rustc_version::Channel;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let target = env::var("TARGET")?;
|
||||
|
||||
@ -71,5 +73,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if !matches!(
|
||||
rustc_version::version_meta().unwrap().channel,
|
||||
Channel::Stable | Channel::Beta
|
||||
) {
|
||||
println!("cargo:rustc-cfg=unstable_channel");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -336,6 +336,11 @@ where
|
||||
self.sift_up(0, old_len);
|
||||
}
|
||||
|
||||
/// Returns the underlying ```Vec<T,N>```. Order is arbitrary and time is O(1).
|
||||
pub fn into_vec(self) -> Vec<T, N> {
|
||||
self.data
|
||||
}
|
||||
|
||||
/* Private API */
|
||||
fn sift_down_to_bottom(&mut self, mut pos: usize) {
|
||||
let end = self.len();
|
||||
@ -428,8 +433,9 @@ impl<'a, T> Hole<'a, T> {
|
||||
unsafe fn move_to(&mut self, index: usize) {
|
||||
debug_assert!(index != self.pos);
|
||||
debug_assert!(index < self.data.len());
|
||||
let index_ptr: *const _ = self.data.get_unchecked(index);
|
||||
let hole_ptr = self.data.get_unchecked_mut(self.pos);
|
||||
let ptr = self.data.as_mut_ptr();
|
||||
let index_ptr: *const _ = ptr.add(index);
|
||||
let hole_ptr = ptr.add(self.pos);
|
||||
ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1);
|
||||
self.pos = index;
|
||||
}
|
||||
@ -536,12 +542,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K, const N: usize> Drop for BinaryHeap<T, K, N> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ptr::drop_in_place(self.data.as_mut_slice()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K, const N: usize> fmt::Debug for BinaryHeap<T, K, N>
|
||||
where
|
||||
K: Kind,
|
||||
@ -576,6 +576,65 @@ mod tests {
|
||||
static mut _B: BinaryHeap<i32, Min, 16> = BinaryHeap::new();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop() {
|
||||
droppable!();
|
||||
|
||||
{
|
||||
let mut v: BinaryHeap<Droppable, Max, 2> = BinaryHeap::new();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
v.pop().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut v: BinaryHeap<Droppable, Max, 2> = BinaryHeap::new();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut v: BinaryHeap<Droppable, Min, 2> = BinaryHeap::new();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
v.pop().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut v: BinaryHeap<Droppable, Min, 2> = BinaryHeap::new();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_vec() {
|
||||
droppable!();
|
||||
|
||||
let mut h: BinaryHeap<Droppable, Max, 2> = BinaryHeap::new();
|
||||
h.push(Droppable::new()).ok().unwrap();
|
||||
h.push(Droppable::new()).ok().unwrap();
|
||||
h.pop().unwrap();
|
||||
|
||||
assert_eq!(Droppable::count(), 1);
|
||||
|
||||
let v = h.into_vec();
|
||||
|
||||
assert_eq!(Droppable::count(), 1);
|
||||
|
||||
core::mem::drop(v);
|
||||
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn min() {
|
||||
let mut heap = BinaryHeap::<_, Min, 16>::new();
|
||||
|
49
src/deque.rs
49
src/deque.rs
@ -271,12 +271,13 @@ impl<T, const N: usize> Deque<T, N> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes an item from the front of the deque and returns it
|
||||
/// Removes an item from the front of the deque and returns it, without checking that the deque
|
||||
/// is not empty
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This assumes the deque is not empty.
|
||||
pub(crate) unsafe fn pop_front_unchecked(&mut self) -> T {
|
||||
/// It's undefined behavior to call this on an empty deque
|
||||
pub unsafe fn pop_front_unchecked(&mut self) -> T {
|
||||
debug_assert!(!self.is_empty());
|
||||
|
||||
let index = self.front;
|
||||
@ -285,12 +286,13 @@ impl<T, const N: usize> Deque<T, N> {
|
||||
(self.buffer.get_unchecked_mut(index).as_ptr() as *const T).read()
|
||||
}
|
||||
|
||||
/// Removes an item from the back of the deque and returns it
|
||||
/// Removes an item from the back of the deque and returns it, without checking that the deque
|
||||
/// is not empty
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This assumes the deque is not empty.
|
||||
pub(crate) unsafe fn pop_back_unchecked(&mut self) -> T {
|
||||
/// It's undefined behavior to call this on an empty deque
|
||||
pub unsafe fn pop_back_unchecked(&mut self) -> T {
|
||||
debug_assert!(!self.is_empty());
|
||||
|
||||
self.full = false;
|
||||
@ -565,29 +567,6 @@ mod tests {
|
||||
let mut _v: Deque<i32, 4> = Deque::new();
|
||||
}
|
||||
|
||||
macro_rules! droppable {
|
||||
() => {
|
||||
struct Droppable;
|
||||
impl Droppable {
|
||||
fn new() -> Self {
|
||||
unsafe {
|
||||
COUNT += 1;
|
||||
}
|
||||
Droppable
|
||||
}
|
||||
}
|
||||
impl Drop for Droppable {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
COUNT -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut COUNT: i32 = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop() {
|
||||
droppable!();
|
||||
@ -599,7 +578,7 @@ mod tests {
|
||||
v.pop_front().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut v: Deque<Droppable, 2> = Deque::new();
|
||||
@ -607,14 +586,14 @@ mod tests {
|
||||
v.push_back(Droppable::new()).ok().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
{
|
||||
let mut v: Deque<Droppable, 2> = Deque::new();
|
||||
v.push_front(Droppable::new()).ok().unwrap();
|
||||
v.push_front(Droppable::new()).ok().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -754,7 +733,7 @@ mod tests {
|
||||
let _ = items.next();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut deque: Deque<Droppable, 2> = Deque::new();
|
||||
@ -764,7 +743,7 @@ mod tests {
|
||||
// Move none
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut deque: Deque<Droppable, 2> = Deque::new();
|
||||
@ -774,7 +753,7 @@ mod tests {
|
||||
let _ = items.next(); // Move partly
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
494
src/indexmap.rs
494
src/indexmap.rs
@ -98,10 +98,13 @@ impl Pos {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Inserted<V> {
|
||||
Done,
|
||||
Swapped { prev_value: V },
|
||||
RobinHood { probe: usize, old_pos: Pos },
|
||||
enum Insert<K, V> {
|
||||
Success(Inserted<V>),
|
||||
Full((K, V)),
|
||||
}
|
||||
struct Inserted<V> {
|
||||
index: usize,
|
||||
old_value: Option<V>,
|
||||
}
|
||||
|
||||
macro_rules! probe_loop {
|
||||
@ -176,16 +179,10 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
// First phase: Look for the preferred location for key.
|
||||
//
|
||||
// We will know if `key` is already in the map, before we need to insert it.
|
||||
// When we insert they key, it might be that we need to continue displacing
|
||||
// entries (robin hood hashing), in which case Inserted::RobinHood is returned
|
||||
fn insert_phase_1(&mut self, hash: HashValue, key: K, value: V) -> Inserted<V> {
|
||||
fn insert(&mut self, hash: HashValue, key: K, value: V) -> Insert<K, V> {
|
||||
let mut probe = hash.desired_pos(Self::mask());
|
||||
let mut dist = 0;
|
||||
|
||||
let inserted;
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
let pos = &mut self.indices[probe];
|
||||
|
||||
@ -198,39 +195,45 @@ where
|
||||
let their_dist = entry_hash.probe_distance(Self::mask(), probe);
|
||||
|
||||
if their_dist < dist {
|
||||
if self.entries.is_full() {
|
||||
return Insert::Full((key, value));
|
||||
}
|
||||
// robin hood: steal the spot if it's better for us
|
||||
let index = self.entries.len();
|
||||
inserted = Inserted::RobinHood {
|
||||
probe: probe,
|
||||
old_pos: Pos::new(index, hash),
|
||||
};
|
||||
break;
|
||||
unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) };
|
||||
return Insert::Success(Inserted {
|
||||
index: self.insert_phase_2(probe, Pos::new(index, hash)),
|
||||
old_value: None,
|
||||
});
|
||||
} else if entry_hash == hash && unsafe { self.entries.get_unchecked(i).key == key }
|
||||
{
|
||||
return Inserted::Swapped {
|
||||
prev_value: mem::replace(
|
||||
return Insert::Success(Inserted {
|
||||
index: i,
|
||||
old_value: Some(mem::replace(
|
||||
unsafe { &mut self.entries.get_unchecked_mut(i).value },
|
||||
value,
|
||||
),
|
||||
};
|
||||
)),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if self.entries.is_full() {
|
||||
return Insert::Full((key, value));
|
||||
}
|
||||
// empty bucket, insert here
|
||||
let index = self.entries.len();
|
||||
*pos = Some(Pos::new(index, hash));
|
||||
inserted = Inserted::Done;
|
||||
break;
|
||||
unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) };
|
||||
return Insert::Success(Inserted {
|
||||
index,
|
||||
old_value: None,
|
||||
});
|
||||
}
|
||||
dist += 1;
|
||||
});
|
||||
|
||||
// NOTE(unsafe) we already checked (in `insert`) that we aren't exceeding the capacity
|
||||
unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) }
|
||||
inserted
|
||||
}
|
||||
|
||||
// phase 2 is post-insert where we forward-shift `Pos` in the indices.
|
||||
fn insert_phase_2(&mut self, mut probe: usize, mut old_pos: Pos) {
|
||||
fn insert_phase_2(&mut self, mut probe: usize, mut old_pos: Pos) -> usize {
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
let pos = unsafe { self.indices.get_unchecked_mut(probe) };
|
||||
|
||||
@ -242,7 +245,7 @@ where
|
||||
|
||||
if is_none {
|
||||
*pos = Some(old_pos);
|
||||
break;
|
||||
return probe;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -313,6 +316,115 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into an entry in the map
|
||||
pub enum Entry<'a, K, V, const N: usize> {
|
||||
/// The entry corresponding to the key `K` exists in the map
|
||||
Occupied(OccupiedEntry<'a, K, V, N>),
|
||||
/// The entry corresponding to the key `K` does not exist in the map
|
||||
Vacant(VacantEntry<'a, K, V, N>),
|
||||
}
|
||||
|
||||
/// An occupied entry which can be manipulated
|
||||
pub struct OccupiedEntry<'a, K, V, const N: usize> {
|
||||
key: K,
|
||||
probe: usize,
|
||||
pos: usize,
|
||||
core: &'a mut CoreMap<K, V, N>,
|
||||
}
|
||||
|
||||
impl<'a, K, V, const N: usize> OccupiedEntry<'a, K, V, N>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
/// Gets a reference to the key that this entity corresponds to
|
||||
pub fn key(&self) -> &K {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// Removes this entry from the map and yields its corresponding key and value
|
||||
pub fn remove_entry(self) -> (K, V) {
|
||||
self.core.remove_found(self.probe, self.pos)
|
||||
}
|
||||
|
||||
/// Gets a reference to the value associated with this entry
|
||||
pub fn get(&self) -> &V {
|
||||
// SAFETY: Already checked existence at instantiation and the only mutable reference
|
||||
// to the map is internally held.
|
||||
unsafe { &self.core.entries.get_unchecked(self.pos).value }
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the value associated with this entry
|
||||
pub fn get_mut(&mut self) -> &mut V {
|
||||
// SAFETY: Already checked existence at instantiation and the only mutable reference
|
||||
// to the map is internally held.
|
||||
unsafe { &mut self.core.entries.get_unchecked_mut(self.pos).value }
|
||||
}
|
||||
|
||||
/// Consumes this entry and yields a reference to the underlying value
|
||||
pub fn into_mut(self) -> &'a mut V {
|
||||
// SAFETY: Already checked existence at instantiation and the only mutable reference
|
||||
// to the map is internally held.
|
||||
unsafe { &mut self.core.entries.get_unchecked_mut(self.pos).value }
|
||||
}
|
||||
|
||||
/// Overwrites the underlying map's value with this entry's value
|
||||
pub fn insert(self, value: V) -> V {
|
||||
// SAFETY: Already checked existence at instantiation and the only mutable reference
|
||||
// to the map is internally held.
|
||||
unsafe {
|
||||
mem::replace(
|
||||
&mut self.core.entries.get_unchecked_mut(self.pos).value,
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes this entry from the map and yields its value
|
||||
pub fn remove(self) -> V {
|
||||
self.remove_entry().1
|
||||
}
|
||||
}
|
||||
|
||||
/// A view into an empty slot in the underlying map
|
||||
pub struct VacantEntry<'a, K, V, const N: usize> {
|
||||
key: K,
|
||||
hash_val: HashValue,
|
||||
core: &'a mut CoreMap<K, V, N>,
|
||||
}
|
||||
impl<'a, K, V, const N: usize> VacantEntry<'a, K, V, N>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
/// Get the key associated with this entry
|
||||
pub fn key(&self) -> &K {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// Consumes this entry to yield to key associated with it
|
||||
pub fn into_key(self) -> K {
|
||||
self.key
|
||||
}
|
||||
|
||||
/// Inserts this entry into to underlying map, yields a mutable reference to the inserted value.
|
||||
/// If the map is at capacity the value is returned instead.
|
||||
pub fn insert(self, value: V) -> Result<&'a mut V, V> {
|
||||
if self.core.entries.is_full() {
|
||||
Err(value)
|
||||
} else {
|
||||
match self.core.insert(self.hash_val, self.key, value) {
|
||||
Insert::Success(inserted) => {
|
||||
unsafe {
|
||||
// SAFETY: Already checked existence at instantiation and the only mutable reference
|
||||
// to the map is internally held.
|
||||
Ok(&mut self.core.entries.get_unchecked_mut(inserted.index).value)
|
||||
}
|
||||
}
|
||||
Insert::Full((_, v)) => Err(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fixed capacity [`IndexMap`](https://docs.rs/indexmap/1/indexmap/map/struct.IndexMap.html)
|
||||
///
|
||||
/// Note that you cannot use `IndexMap` directly, since it is generic around the hashing algorithm
|
||||
@ -492,8 +604,78 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// pub fn entry(&mut self, key: K) -> Entry<K, V> { .. }
|
||||
/// Get the first key-value pair
|
||||
///
|
||||
/// Computes in **O(1)** time
|
||||
pub fn first(&self) -> Option<(&K, &V)> {
|
||||
self.core
|
||||
.entries
|
||||
.first()
|
||||
.map(|bucket| (&bucket.key, &bucket.value))
|
||||
}
|
||||
|
||||
/// Get the first key-value pair, with mutable access to the value
|
||||
///
|
||||
/// Computes in **O(1)** time
|
||||
pub fn first_mut(&mut self) -> Option<(&K, &mut V)> {
|
||||
self.core
|
||||
.entries
|
||||
.first_mut()
|
||||
.map(|bucket| (&bucket.key, &mut bucket.value))
|
||||
}
|
||||
|
||||
/// Get the last key-value pair
|
||||
///
|
||||
/// Computes in **O(1)** time
|
||||
pub fn last(&self) -> Option<(&K, &V)> {
|
||||
self.core
|
||||
.entries
|
||||
.last()
|
||||
.map(|bucket| (&bucket.key, &bucket.value))
|
||||
}
|
||||
|
||||
/// Get the last key-value pair, with mutable access to the value
|
||||
///
|
||||
/// Computes in **O(1)** time
|
||||
pub fn last_mut(&mut self) -> Option<(&K, &mut V)> {
|
||||
self.core
|
||||
.entries
|
||||
.last_mut()
|
||||
.map(|bucket| (&bucket.key, &mut bucket.value))
|
||||
}
|
||||
|
||||
/// Returns an entry for the corresponding key
|
||||
/// ```
|
||||
/// use heapless::FnvIndexMap;
|
||||
/// use heapless::Entry;
|
||||
/// let mut map = FnvIndexMap::<_, _, 16>::new();
|
||||
/// if let Entry::Vacant(v) = map.entry("a") {
|
||||
/// v.insert(1).unwrap();
|
||||
/// }
|
||||
/// if let Entry::Occupied(mut o) = map.entry("a") {
|
||||
/// println!("found {}", *o.get()); // Prints 1
|
||||
/// o.insert(2);
|
||||
/// }
|
||||
/// // Prints 2
|
||||
/// println!("val: {}", *map.get("a").unwrap());
|
||||
/// ```
|
||||
pub fn entry(&mut self, key: K) -> Entry<'_, K, V, N> {
|
||||
let hash_val = hash_with(&key, &self.build_hasher);
|
||||
if let Some((probe, pos)) = self.core.find(hash_val, &key) {
|
||||
Entry::Occupied(OccupiedEntry {
|
||||
key,
|
||||
probe,
|
||||
pos,
|
||||
core: &mut self.core,
|
||||
})
|
||||
} else {
|
||||
Entry::Vacant(VacantEntry {
|
||||
key,
|
||||
hash_val,
|
||||
core: &mut self.core,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of key-value pairs in the map.
|
||||
///
|
||||
@ -654,17 +836,10 @@ where
|
||||
/// assert_eq!(map[&37], "c");
|
||||
/// ```
|
||||
pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>, (K, V)> {
|
||||
if self.core.entries.is_full() {
|
||||
Err((key, value))
|
||||
} else {
|
||||
Ok(match self.insert_phase_1(key, value) {
|
||||
Inserted::Swapped { prev_value } => Some(prev_value),
|
||||
Inserted::Done => None,
|
||||
Inserted::RobinHood { probe, old_pos } => {
|
||||
self.core.insert_phase_2(probe, old_pos);
|
||||
None
|
||||
}
|
||||
})
|
||||
let hash = hash_with(&key, &self.build_hasher);
|
||||
match self.core.insert(hash, key, value) {
|
||||
Insert::Success(inserted) => Ok(inserted.old_value),
|
||||
Insert::Full((k, v)) => Err((k, v)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,11 +895,6 @@ where
|
||||
let h = hash_with(key, &self.build_hasher);
|
||||
self.core.find(h, key)
|
||||
}
|
||||
|
||||
fn insert_phase_1(&mut self, key: K, value: V) -> Inserted<V> {
|
||||
let hash = hash_with(&key, &self.build_hasher);
|
||||
self.core.insert_phase_1(hash, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, Q, V, S, const N: usize> ops::Index<&'a Q> for IndexMap<K, V, S, N>
|
||||
@ -857,6 +1027,34 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IntoIter<K, V, const N: usize> {
|
||||
entries: Vec<Bucket<K, V>, N>,
|
||||
}
|
||||
|
||||
impl<K, V, const N: usize> Iterator for IntoIter<K, V, N> {
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.entries.pop().map(|bucket| (bucket.key, bucket.value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S, const N: usize> IntoIterator for IndexMap<K, V, S, N>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = (K, V);
|
||||
type IntoIter = IntoIter<K, V, N>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter {
|
||||
entries: self.core.entries,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, S, const N: usize> IntoIterator for &'a IndexMap<K, V, S, N>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
@ -929,7 +1127,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::FnvIndexMap;
|
||||
use crate::{indexmap::Entry, FnvIndexMap};
|
||||
|
||||
use core::mem;
|
||||
|
||||
#[test]
|
||||
@ -974,4 +1173,205 @@ mod tests {
|
||||
assert!(a == b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_iter() {
|
||||
let mut src: FnvIndexMap<_, _, 4> = FnvIndexMap::new();
|
||||
src.insert("k1", "v1").unwrap();
|
||||
src.insert("k2", "v2").unwrap();
|
||||
src.insert("k3", "v3").unwrap();
|
||||
src.insert("k4", "v4").unwrap();
|
||||
let clone = src.clone();
|
||||
for (k, v) in clone.into_iter() {
|
||||
assert_eq!(v, *src.get(k).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_replaces_on_full_map() {
|
||||
let mut a: FnvIndexMap<_, _, 2> = FnvIndexMap::new();
|
||||
a.insert("k1", "v1").unwrap();
|
||||
a.insert("k2", "v2").unwrap();
|
||||
a.insert("k1", "v2").unwrap();
|
||||
assert_eq!(a.get("k1"), a.get("k2"));
|
||||
}
|
||||
|
||||
const MAP_SLOTS: usize = 4096;
|
||||
fn almost_filled_map() -> FnvIndexMap<usize, usize, MAP_SLOTS> {
|
||||
let mut almost_filled = FnvIndexMap::new();
|
||||
for i in 1..MAP_SLOTS {
|
||||
almost_filled.insert(i, i).unwrap();
|
||||
}
|
||||
almost_filled
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_find() {
|
||||
let key = 0;
|
||||
let value = 0;
|
||||
let mut src = almost_filled_map();
|
||||
let entry = src.entry(key);
|
||||
match entry {
|
||||
Entry::Occupied(_) => {
|
||||
panic!("Found entry without inserting");
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
assert_eq!(&key, v.key());
|
||||
assert_eq!(key, v.into_key());
|
||||
}
|
||||
}
|
||||
src.insert(key, value).unwrap();
|
||||
let entry = src.entry(key);
|
||||
match entry {
|
||||
Entry::Occupied(mut o) => {
|
||||
assert_eq!(&key, o.key());
|
||||
assert_eq!(&value, o.get());
|
||||
assert_eq!(&value, o.get_mut());
|
||||
assert_eq!(&value, o.into_mut());
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
panic!("Entry not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_vacant_insert() {
|
||||
let key = 0;
|
||||
let value = 0;
|
||||
let mut src = almost_filled_map();
|
||||
assert_eq!(MAP_SLOTS - 1, src.len());
|
||||
let entry = src.entry(key);
|
||||
match entry {
|
||||
Entry::Occupied(_) => {
|
||||
panic!("Entry found when empty");
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(value).unwrap();
|
||||
}
|
||||
};
|
||||
assert_eq!(value, *src.get(&key).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_occupied_insert() {
|
||||
let key = 0;
|
||||
let value = 0;
|
||||
let value2 = 5;
|
||||
let mut src = almost_filled_map();
|
||||
assert_eq!(MAP_SLOTS - 1, src.len());
|
||||
src.insert(key, value).unwrap();
|
||||
let entry = src.entry(key);
|
||||
match entry {
|
||||
Entry::Occupied(o) => {
|
||||
assert_eq!(value, o.insert(value2));
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
panic!("Entry not found");
|
||||
}
|
||||
};
|
||||
assert_eq!(value2, *src.get(&key).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_remove_entry() {
|
||||
let key = 0;
|
||||
let value = 0;
|
||||
let mut src = almost_filled_map();
|
||||
src.insert(key, value).unwrap();
|
||||
assert_eq!(MAP_SLOTS, src.len());
|
||||
let entry = src.entry(key);
|
||||
match entry {
|
||||
Entry::Occupied(o) => {
|
||||
assert_eq!((key, value), o.remove_entry());
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
panic!("Entry not found")
|
||||
}
|
||||
};
|
||||
assert_eq!(MAP_SLOTS - 1, src.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_remove() {
|
||||
let key = 0;
|
||||
let value = 0;
|
||||
let mut src = almost_filled_map();
|
||||
src.insert(key, value).unwrap();
|
||||
assert_eq!(MAP_SLOTS, src.len());
|
||||
let entry = src.entry(key);
|
||||
match entry {
|
||||
Entry::Occupied(o) => {
|
||||
assert_eq!(value, o.remove());
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
panic!("Entry not found");
|
||||
}
|
||||
};
|
||||
assert_eq!(MAP_SLOTS - 1, src.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_roll_through_all() {
|
||||
let mut src: FnvIndexMap<usize, usize, MAP_SLOTS> = FnvIndexMap::new();
|
||||
for i in 0..MAP_SLOTS {
|
||||
match src.entry(i) {
|
||||
Entry::Occupied(_) => {
|
||||
panic!("Entry found before insert");
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(i).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
let add_mod = 99;
|
||||
for i in 0..MAP_SLOTS {
|
||||
match src.entry(i) {
|
||||
Entry::Occupied(o) => {
|
||||
assert_eq!(i, o.insert(i + add_mod));
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
panic!("Entry not found after insert");
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in 0..MAP_SLOTS {
|
||||
match src.entry(i) {
|
||||
Entry::Occupied(o) => {
|
||||
assert_eq!((i, i + add_mod), o.remove_entry());
|
||||
}
|
||||
Entry::Vacant(_) => {
|
||||
panic!("Entry not found after insert");
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in 0..MAP_SLOTS {
|
||||
assert!(matches!(src.entry(i), Entry::Vacant(_)));
|
||||
}
|
||||
assert!(src.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_last() {
|
||||
let mut map = FnvIndexMap::<_, _, 4>::new();
|
||||
|
||||
assert_eq!(None, map.first());
|
||||
assert_eq!(None, map.last());
|
||||
|
||||
map.insert(0, 0).unwrap();
|
||||
map.insert(2, 2).unwrap();
|
||||
|
||||
assert_eq!(Some((&0, &0)), map.first());
|
||||
assert_eq!(Some((&2, &2)), map.last());
|
||||
|
||||
map.insert(1, 1).unwrap();
|
||||
|
||||
assert_eq!(Some((&1, &1)), map.last());
|
||||
|
||||
*map.first_mut().unwrap().1 += 1;
|
||||
*map.last_mut().unwrap().1 += 1;
|
||||
|
||||
assert_eq!(Some((&0, &1)), map.first());
|
||||
assert_eq!(Some((&1, &2)), map.last());
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +128,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the first value
|
||||
///
|
||||
/// Computes in **O(1)** time
|
||||
pub fn first(&self) -> Option<&T> {
|
||||
self.map.first().map(|(k, _v)| k)
|
||||
}
|
||||
|
||||
/// Get the last value
|
||||
///
|
||||
/// Computes in **O(1)** time
|
||||
pub fn last(&self) -> Option<&T> {
|
||||
self.map.last().map(|(k, _v)| k)
|
||||
}
|
||||
|
||||
/// Visits the values representing the difference, i.e. the values that are in `self` but not in
|
||||
/// `other`.
|
||||
///
|
||||
|
@ -77,7 +77,7 @@
|
||||
pub use binary_heap::BinaryHeap;
|
||||
pub use deque::Deque;
|
||||
pub use histbuf::{HistoryBuffer, OldestOrdered};
|
||||
pub use indexmap::{Bucket, FnvIndexMap, IndexMap, Pos};
|
||||
pub use indexmap::{Bucket, Entry, FnvIndexMap, IndexMap, OccupiedEntry, Pos, VacantEntry};
|
||||
pub use indexset::{FnvIndexSet, IndexSet};
|
||||
pub use linear_map::LinearMap;
|
||||
#[cfg(all(has_cas, feature = "cas"))]
|
||||
@ -85,6 +85,10 @@ pub use pool::singleton::arc::Arc;
|
||||
pub use string::String;
|
||||
pub use vec::Vec;
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(test)]
|
||||
mod test_helpers;
|
||||
|
||||
mod deque;
|
||||
mod histbuf;
|
||||
mod indexmap;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::{cmp::Ordering, fmt, fmt::Write, hash, ops, str};
|
||||
use core::{cmp::Ordering, fmt, fmt::Write, hash, iter, ops, str};
|
||||
|
||||
use hash32;
|
||||
|
||||
@ -307,6 +307,36 @@ impl<const N: usize> str::FromStr for String<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> iter::FromIterator<char> for String<N> {
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
let mut new = String::new();
|
||||
for c in iter {
|
||||
new.push(c).unwrap();
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> iter::FromIterator<&'a char> for String<N> {
|
||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||
let mut new = String::new();
|
||||
for c in iter {
|
||||
new.push(*c).unwrap();
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> iter::FromIterator<&'a str> for String<N> {
|
||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||
let mut new = String::new();
|
||||
for c in iter {
|
||||
new.push_str(c).unwrap();
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Clone for String<N> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@ -558,6 +588,20 @@ mod tests {
|
||||
assert_eq!(e, ());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_iter() {
|
||||
let mut v: Vec<char, 5> = Vec::new();
|
||||
v.push('h').unwrap();
|
||||
v.push('e').unwrap();
|
||||
v.push('l').unwrap();
|
||||
v.push('l').unwrap();
|
||||
v.push('o').unwrap();
|
||||
let string1: String<5> = v.iter().collect(); //&char
|
||||
let string2: String<5> = "hello".chars().collect(); //char
|
||||
assert_eq!(string1, "hello");
|
||||
assert_eq!(string2, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn from_panic() {
|
||||
|
23
src/test_helpers.rs
Normal file
23
src/test_helpers.rs
Normal file
@ -0,0 +1,23 @@
|
||||
macro_rules! droppable {
|
||||
() => {
|
||||
static COUNT: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(0);
|
||||
|
||||
#[derive(Eq, Ord, PartialEq, PartialOrd)]
|
||||
struct Droppable(i32);
|
||||
impl Droppable {
|
||||
fn new() -> Self {
|
||||
COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
Droppable(Self::count())
|
||||
}
|
||||
|
||||
fn count() -> i32 {
|
||||
COUNT.load(core::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
impl Drop for Droppable {
|
||||
fn drop(&mut self) {
|
||||
COUNT.fetch_sub(1, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
87
src/vec.rs
87
src/vec.rs
@ -34,12 +34,18 @@ use hash32;
|
||||
/// assert_eq!(*vec, [7, 1, 2, 3]);
|
||||
/// ```
|
||||
pub struct Vec<T, const N: usize> {
|
||||
buffer: [MaybeUninit<T>; N],
|
||||
// NOTE order is important for optimizations. the `len` first layout lets the compiler optimize
|
||||
// `new` to: reserve stack space and zero the first word. With the fields in the reverse order
|
||||
// the compiler optimizes `new` to `memclr`-ing the *entire* stack space, including the `buffer`
|
||||
// field which should be left uninitialized. Optimizations were last checked with Rust 1.60
|
||||
len: usize,
|
||||
|
||||
buffer: [MaybeUninit<T>; N],
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Vec<T, N> {
|
||||
const INIT: MaybeUninit<T> = MaybeUninit::uninit();
|
||||
const ELEM: MaybeUninit<T> = MaybeUninit::uninit();
|
||||
const INIT: [MaybeUninit<T>; N] = [Self::ELEM; N]; // important for optimization of `new`
|
||||
|
||||
/// Constructs a new, empty vector with a fixed capacity of `N`
|
||||
///
|
||||
@ -60,8 +66,8 @@ impl<T, const N: usize> Vec<T, N> {
|
||||
crate::sealed::greater_than_eq_0::<N>();
|
||||
|
||||
Self {
|
||||
buffer: [Self::INIT; N],
|
||||
len: 0,
|
||||
buffer: Self::INIT,
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +98,12 @@ impl<T, const N: usize> Vec<T, N> {
|
||||
T: Clone,
|
||||
{
|
||||
let mut new = Self::new();
|
||||
new.extend_from_slice(self.as_slice()).unwrap();
|
||||
// avoid `extend_from_slice` as that introduces a runtime check / panicking branch
|
||||
for elem in self {
|
||||
unsafe {
|
||||
new.push_unchecked(elem.clone());
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
|
||||
@ -263,13 +274,24 @@ impl<T, const N: usize> Vec<T, N> {
|
||||
|
||||
/// Shortens the vector, keeping the first `len` elements and dropping the rest.
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
// drop any extra elements
|
||||
while len < self.len {
|
||||
// decrement len before the drop_in_place(), so a panic on Drop
|
||||
// doesn't re-drop the just-failed value.
|
||||
self.len -= 1;
|
||||
let len = self.len;
|
||||
unsafe { ptr::drop_in_place(self.as_mut_slice().get_unchecked_mut(len)) };
|
||||
// This is safe because:
|
||||
//
|
||||
// * the slice passed to `drop_in_place` is valid; the `len > self.len`
|
||||
// case avoids creating an invalid slice, and
|
||||
// * the `len` of the vector is shrunk before calling `drop_in_place`,
|
||||
// such that no value will be dropped twice in case `drop_in_place`
|
||||
// were to panic once (if it panics twice, the program aborts).
|
||||
unsafe {
|
||||
// Note: It's intentional that this is `>` and not `>=`.
|
||||
// Changing it to `>=` has negative performance
|
||||
// implications in some cases. See rust-lang/rust#78884 for more.
|
||||
if len > self.len {
|
||||
return;
|
||||
}
|
||||
let remaining_len = self.len - len;
|
||||
let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len);
|
||||
self.len = len;
|
||||
ptr::drop_in_place(s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,11 +493,11 @@ impl<T, const N: usize> Vec<T, N> {
|
||||
pub unsafe fn swap_remove_unchecked(&mut self, index: usize) -> T {
|
||||
let length = self.len();
|
||||
debug_assert!(index < length);
|
||||
ptr::swap(
|
||||
self.as_mut_slice().get_unchecked_mut(index),
|
||||
self.as_mut_slice().get_unchecked_mut(length - 1),
|
||||
);
|
||||
self.pop_unchecked()
|
||||
let value = ptr::read(self.as_ptr().add(index));
|
||||
let base_ptr = self.as_mut_ptr();
|
||||
ptr::copy(base_ptr.add(length - 1), base_ptr.add(index), 1);
|
||||
self.len -= 1;
|
||||
value
|
||||
}
|
||||
|
||||
/// Returns true if the vec is full
|
||||
@ -890,29 +912,6 @@ mod tests {
|
||||
assert!(v.is_full());
|
||||
}
|
||||
|
||||
macro_rules! droppable {
|
||||
() => {
|
||||
struct Droppable;
|
||||
impl Droppable {
|
||||
fn new() -> Self {
|
||||
unsafe {
|
||||
COUNT += 1;
|
||||
}
|
||||
Droppable
|
||||
}
|
||||
}
|
||||
impl Drop for Droppable {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
COUNT -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut COUNT: i32 = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop() {
|
||||
droppable!();
|
||||
@ -924,7 +923,7 @@ mod tests {
|
||||
v.pop().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut v: Vec<Droppable, 2> = Vec::new();
|
||||
@ -932,7 +931,7 @@ mod tests {
|
||||
v.push(Droppable::new()).ok().unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1067,7 +1066,7 @@ mod tests {
|
||||
let _ = items.next();
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut vec: Vec<Droppable, 2> = Vec::new();
|
||||
@ -1077,7 +1076,7 @@ mod tests {
|
||||
// Move none
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
|
||||
{
|
||||
let mut vec: Vec<Droppable, 2> = Vec::new();
|
||||
@ -1087,7 +1086,7 @@ mod tests {
|
||||
let _ = items.next(); // Move partly
|
||||
}
|
||||
|
||||
assert_eq!(unsafe { COUNT }, 0);
|
||||
assert_eq!(Droppable::count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,7 +1,7 @@
|
||||
# false positives in thread::spawn (?)
|
||||
race:*dealloc
|
||||
race:*drop_slow*
|
||||
race:__call_tls_dtors
|
||||
race:std::panic::catch_unwind
|
||||
race:std::thread::scope
|
||||
|
||||
# false positives in scoped_threadpool (?)
|
||||
race:*drop*
|
||||
# std::thread::spawn false positive; seen on Ubuntu 20.04 but not on Arch Linux (2022-04-29)
|
||||
race:drop_in_place*JoinHandle
|
||||
race:alloc::sync::Arc<*>::drop_slow
|
||||
race:__call_tls_dtors
|
||||
|
@ -1,11 +1,11 @@
|
||||
#![cfg_attr(unstable_channel, feature(scoped_threads))]
|
||||
#![deny(rust_2018_compatibility)]
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(warnings)]
|
||||
|
||||
use std::{sync::mpsc, thread};
|
||||
use std::thread;
|
||||
|
||||
use heapless::{mpmc::Q64, spsc};
|
||||
use scoped_threadpool::Pool;
|
||||
use heapless::spsc;
|
||||
|
||||
#[test]
|
||||
fn once() {
|
||||
@ -51,6 +51,7 @@ fn twice() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unstable_channel)]
|
||||
fn scoped() {
|
||||
let mut rb: spsc::Queue<i32, 5> = spsc::Queue::new();
|
||||
|
||||
@ -59,12 +60,12 @@ fn scoped() {
|
||||
{
|
||||
let (mut p, mut c) = rb.split();
|
||||
|
||||
Pool::new(2).scoped(move |scope| {
|
||||
scope.execute(move || {
|
||||
thread::scope(move |scope| {
|
||||
scope.spawn(move || {
|
||||
p.enqueue(1).unwrap();
|
||||
});
|
||||
|
||||
scope.execute(move || {
|
||||
scope.spawn(move || {
|
||||
c.dequeue().unwrap();
|
||||
});
|
||||
});
|
||||
@ -75,6 +76,7 @@ fn scoped() {
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // too slow
|
||||
#[cfg(unstable_channel)]
|
||||
fn contention() {
|
||||
const N: usize = 1024;
|
||||
|
||||
@ -83,8 +85,8 @@ fn contention() {
|
||||
{
|
||||
let (mut p, mut c) = rb.split();
|
||||
|
||||
Pool::new(2).scoped(move |scope| {
|
||||
scope.execute(move || {
|
||||
thread::scope(move |scope| {
|
||||
scope.spawn(move || {
|
||||
let mut sum: u32 = 0;
|
||||
|
||||
for i in 0..(2 * N) {
|
||||
@ -95,7 +97,7 @@ fn contention() {
|
||||
println!("producer: {}", sum);
|
||||
});
|
||||
|
||||
scope.execute(move || {
|
||||
scope.spawn(move || {
|
||||
let mut sum: u32 = 0;
|
||||
|
||||
for _ in 0..(2 * N) {
|
||||
@ -120,15 +122,20 @@ fn contention() {
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // too slow
|
||||
#[cfg(unstable_channel)]
|
||||
fn mpmc_contention() {
|
||||
use std::sync::mpsc;
|
||||
|
||||
use heapless::mpmc::Q64;
|
||||
|
||||
const N: u32 = 64;
|
||||
|
||||
static Q: Q64<u32> = Q64::new();
|
||||
|
||||
let (s, r) = mpsc::channel();
|
||||
Pool::new(2).scoped(|scope| {
|
||||
thread::scope(|scope| {
|
||||
let s1 = s.clone();
|
||||
scope.execute(move || {
|
||||
scope.spawn(move || {
|
||||
let mut sum: u32 = 0;
|
||||
|
||||
for i in 0..(16 * N) {
|
||||
@ -141,7 +148,7 @@ fn mpmc_contention() {
|
||||
});
|
||||
|
||||
let s2 = s.clone();
|
||||
scope.execute(move || {
|
||||
scope.spawn(move || {
|
||||
let mut sum: u32 = 0;
|
||||
|
||||
for _ in 0..(16 * N) {
|
||||
@ -166,6 +173,7 @@ fn mpmc_contention() {
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // too slow
|
||||
#[cfg(unstable_channel)]
|
||||
fn unchecked() {
|
||||
const N: usize = 1024;
|
||||
|
||||
@ -178,14 +186,14 @@ fn unchecked() {
|
||||
{
|
||||
let (mut p, mut c) = rb.split();
|
||||
|
||||
Pool::new(2).scoped(move |scope| {
|
||||
scope.execute(move || {
|
||||
thread::scope(move |scope| {
|
||||
scope.spawn(move || {
|
||||
for _ in 0..N / 2 - 1 {
|
||||
p.enqueue(2).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
scope.execute(move || {
|
||||
scope.spawn(move || {
|
||||
let mut sum: usize = 0;
|
||||
|
||||
for _ in 0..N / 2 - 1 {
|
||||
@ -246,8 +254,8 @@ fn pool() {
|
||||
|
||||
A::grow(unsafe { &mut M });
|
||||
|
||||
Pool::new(2).scoped(move |scope| {
|
||||
scope.execute(move || {
|
||||
thread::scope(move |scope| {
|
||||
scope.spawn(move || {
|
||||
for _ in 0..N / 4 {
|
||||
let a = A::alloc().unwrap();
|
||||
let b = A::alloc().unwrap();
|
||||
@ -257,7 +265,7 @@ fn pool() {
|
||||
}
|
||||
});
|
||||
|
||||
scope.execute(move || {
|
||||
scope.spawn(move || {
|
||||
for _ in 0..N / 2 {
|
||||
let a = A::alloc().unwrap();
|
||||
let a = a.init([2; 8]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user