mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-10-02 14:54:30 +00:00
Queue: implement QueueView on top of #486
This commit is contained in:
parent
02cc4941d2
commit
6c2c077475
@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added `LinearMapView`, the `!Sized` version of `LinearMap`.
|
- Added `LinearMapView`, the `!Sized` version of `LinearMap`.
|
||||||
- Added `HistoryBufferView`, the `!Sized` version of `HistoryBuffer`.
|
- Added `HistoryBufferView`, the `!Sized` version of `HistoryBuffer`.
|
||||||
- Added `DequeView`, the `!Sized` version of `Deque`.
|
- Added `DequeView`, the `!Sized` version of `Deque`.
|
||||||
|
- Added `QueueView`, the `!Sized` version of `Queue`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
223
src/spsc.rs
223
src/spsc.rs
@ -97,7 +97,7 @@
|
|||||||
//! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue`
|
//! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue`
|
||||||
//! and `Ok` is returned by `enqueue`).
|
//! and `Ok` is returned by `enqueue`).
|
||||||
|
|
||||||
use core::{cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};
|
use core::{borrow::Borrow, cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};
|
||||||
|
|
||||||
#[cfg(not(feature = "portable-atomic"))]
|
#[cfg(not(feature = "portable-atomic"))]
|
||||||
use core::sync::atomic;
|
use core::sync::atomic;
|
||||||
@ -106,28 +106,36 @@ use portable_atomic as atomic;
|
|||||||
|
|
||||||
use atomic::{AtomicUsize, Ordering};
|
use atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
/// A statically allocated single producer single consumer queue with a capacity of `N - 1` elements
|
use crate::storage::{OwnedStorage, Storage, ViewStorage};
|
||||||
|
|
||||||
|
/// Base struct for [`Queue`] and [`QueueView`], generic over the [`Storage`].
|
||||||
///
|
///
|
||||||
/// *IMPORTANT*: To get better performance use a value for `N` that is a power of 2 (e.g. `16`, `32`,
|
/// In most cases you should use [`Queue`] or [`QueueView`] directly. Only use this
|
||||||
/// etc.).
|
/// struct if you want to write code that's generic over both.
|
||||||
pub struct Queue<T, const N: usize> {
|
pub struct QueueInner<T, S: Storage> {
|
||||||
// this is from where we dequeue items
|
// this is from where we dequeue items
|
||||||
pub(crate) head: AtomicUsize,
|
pub(crate) head: AtomicUsize,
|
||||||
|
|
||||||
// this is where we enqueue new items
|
// this is where we enqueue new items
|
||||||
pub(crate) tail: AtomicUsize,
|
pub(crate) tail: AtomicUsize,
|
||||||
|
|
||||||
pub(crate) buffer: [UnsafeCell<MaybeUninit<T>>; N],
|
pub(crate) buffer: S::Buffer<UnsafeCell<MaybeUninit<T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A statically allocated single producer single consumer queue with a capacity of `N - 1` elements
|
||||||
|
///
|
||||||
|
/// *IMPORTANT*: To get better performance use a value for `N` that is a power of 2 (e.g. `16`, `32`,
|
||||||
|
/// etc.).
|
||||||
|
pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
|
||||||
|
|
||||||
|
/// Asingle producer single consumer queue
|
||||||
|
///
|
||||||
|
/// *IMPORTANT*: To get better performance use a value for `N` that is a power of 2 (e.g. `16`, `32`,
|
||||||
|
/// etc.).
|
||||||
|
pub type QueueView<T> = QueueInner<T, ViewStorage>;
|
||||||
|
|
||||||
impl<T, const N: usize> Queue<T, N> {
|
impl<T, const N: usize> Queue<T, N> {
|
||||||
const INIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
|
const INIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn increment(val: usize) -> usize {
|
|
||||||
(val + 1) % N
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an empty queue with a fixed capacity of `N - 1`
|
/// Creates an empty queue with a fixed capacity of `N - 1`
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
// Const assert N > 1
|
// Const assert N > 1
|
||||||
@ -141,18 +149,51 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the maximum number of elements the queue can hold
|
/// Returns the maximum number of elements the queue can hold
|
||||||
|
///
|
||||||
|
/// For the same method on [`QueueView`], see [`storage_capacity`](QueueInner::storage_capacity)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn capacity(&self) -> usize {
|
pub const fn capacity(&self) -> usize {
|
||||||
N - 1
|
N - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the `Queue`, erasing the `N` const-generic.
|
||||||
|
pub fn as_view(&self) -> &QueueView<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the `Queue`, erasing the `N` const-generic.
|
||||||
|
pub fn as_mut_view(&mut self) -> &mut QueueView<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S: Storage> QueueInner<T, S> {
|
||||||
|
#[inline]
|
||||||
|
fn increment(&self, val: usize) -> usize {
|
||||||
|
(val + 1) % self.n()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn n(&self) -> usize {
|
||||||
|
self.buffer.borrow().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the maximum number of elements the queue can hold
|
||||||
|
#[inline]
|
||||||
|
pub fn storage_capacity(&self) -> usize {
|
||||||
|
self.n() - 1
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of elements in the queue
|
/// Returns the number of elements in the queue
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
let current_head = self.head.load(Ordering::Relaxed);
|
let current_head = self.head.load(Ordering::Relaxed);
|
||||||
let current_tail = self.tail.load(Ordering::Relaxed);
|
let current_tail = self.tail.load(Ordering::Relaxed);
|
||||||
|
|
||||||
current_tail.wrapping_sub(current_head).wrapping_add(N) % N
|
current_tail
|
||||||
|
.wrapping_sub(current_head)
|
||||||
|
.wrapping_add(self.n())
|
||||||
|
% self.n()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the queue is empty
|
/// Returns `true` if the queue is empty
|
||||||
@ -164,12 +205,12 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
/// Returns `true` if the queue is full
|
/// Returns `true` if the queue is full
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_full(&self) -> bool {
|
pub fn is_full(&self) -> bool {
|
||||||
Self::increment(self.tail.load(Ordering::Relaxed)) == self.head.load(Ordering::Relaxed)
|
self.increment(self.tail.load(Ordering::Relaxed)) == self.head.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates from the front of the queue to the back
|
/// Iterates from the front of the queue to the back
|
||||||
pub fn iter(&self) -> Iter<'_, T, N> {
|
pub fn iter(&self) -> IterInner<'_, T, S> {
|
||||||
Iter {
|
IterInner {
|
||||||
rb: self,
|
rb: self,
|
||||||
index: 0,
|
index: 0,
|
||||||
len: self.len(),
|
len: self.len(),
|
||||||
@ -177,9 +218,9 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator that allows modifying each value
|
/// Returns an iterator that allows modifying each value
|
||||||
pub fn iter_mut(&mut self) -> IterMut<'_, T, N> {
|
pub fn iter_mut(&mut self) -> IterMutInner<'_, T, S> {
|
||||||
let len = self.len();
|
let len = self.len();
|
||||||
IterMut {
|
IterMutInner {
|
||||||
rb: self,
|
rb: self,
|
||||||
index: 0,
|
index: 0,
|
||||||
len,
|
len,
|
||||||
@ -218,7 +259,7 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
pub fn peek(&self) -> Option<&T> {
|
pub fn peek(&self) -> Option<&T> {
|
||||||
if !self.is_empty() {
|
if !self.is_empty() {
|
||||||
let head = self.head.load(Ordering::Relaxed);
|
let head = self.head.load(Ordering::Relaxed);
|
||||||
Some(unsafe { &*(self.buffer.get_unchecked(head).get() as *const T) })
|
Some(unsafe { &*(self.buffer.borrow().get_unchecked(head).get() as *const T) })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -229,10 +270,10 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
// items without doing pointer arithmetic and accessing internal fields of this type.
|
// items without doing pointer arithmetic and accessing internal fields of this type.
|
||||||
unsafe fn inner_enqueue(&self, val: T) -> Result<(), T> {
|
unsafe fn inner_enqueue(&self, val: T) -> Result<(), T> {
|
||||||
let current_tail = self.tail.load(Ordering::Relaxed);
|
let current_tail = self.tail.load(Ordering::Relaxed);
|
||||||
let next_tail = Self::increment(current_tail);
|
let next_tail = self.increment(current_tail);
|
||||||
|
|
||||||
if next_tail != self.head.load(Ordering::Acquire) {
|
if next_tail != self.head.load(Ordering::Acquire) {
|
||||||
(self.buffer.get_unchecked(current_tail).get()).write(MaybeUninit::new(val));
|
(self.buffer.borrow().get_unchecked(current_tail).get()).write(MaybeUninit::new(val));
|
||||||
self.tail.store(next_tail, Ordering::Release);
|
self.tail.store(next_tail, Ordering::Release);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -247,9 +288,9 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
unsafe fn inner_enqueue_unchecked(&self, val: T) {
|
unsafe fn inner_enqueue_unchecked(&self, val: T) {
|
||||||
let current_tail = self.tail.load(Ordering::Relaxed);
|
let current_tail = self.tail.load(Ordering::Relaxed);
|
||||||
|
|
||||||
(self.buffer.get_unchecked(current_tail).get()).write(MaybeUninit::new(val));
|
(self.buffer.borrow().get_unchecked(current_tail).get()).write(MaybeUninit::new(val));
|
||||||
self.tail
|
self.tail
|
||||||
.store(Self::increment(current_tail), Ordering::Release);
|
.store(self.increment(current_tail), Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an `item` to the end of the queue, without checking if it's full
|
/// Adds an `item` to the end of the queue, without checking if it's full
|
||||||
@ -273,10 +314,10 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
if current_head == self.tail.load(Ordering::Acquire) {
|
if current_head == self.tail.load(Ordering::Acquire) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let v = (self.buffer.get_unchecked(current_head).get() as *const T).read();
|
let v = (self.buffer.borrow().get_unchecked(current_head).get() as *const T).read();
|
||||||
|
|
||||||
self.head
|
self.head
|
||||||
.store(Self::increment(current_head), Ordering::Release);
|
.store(self.increment(current_head), Ordering::Release);
|
||||||
|
|
||||||
Some(v)
|
Some(v)
|
||||||
}
|
}
|
||||||
@ -287,10 +328,10 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
// items without doing pointer arithmetic and accessing internal fields of this type.
|
// items without doing pointer arithmetic and accessing internal fields of this type.
|
||||||
unsafe fn inner_dequeue_unchecked(&self) -> T {
|
unsafe fn inner_dequeue_unchecked(&self) -> T {
|
||||||
let current_head = self.head.load(Ordering::Relaxed);
|
let current_head = self.head.load(Ordering::Relaxed);
|
||||||
let v = (self.buffer.get_unchecked(current_head).get() as *const T).read();
|
let v = (self.buffer.borrow().get_unchecked(current_head).get() as *const T).read();
|
||||||
|
|
||||||
self.head
|
self.head
|
||||||
.store(Self::increment(current_head), Ordering::Release);
|
.store(self.increment(current_head), Ordering::Release);
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
@ -306,8 +347,8 @@ impl<T, const N: usize> Queue<T, N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Splits a queue into producer and consumer endpoints
|
/// Splits a queue into producer and consumer endpoints
|
||||||
pub fn split(&mut self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) {
|
pub fn split(&mut self) -> (ProducerInner<'_, T, S>, ConsumerInner<'_, T, S>) {
|
||||||
(Producer { rb: self }, Consumer { rb: self })
|
(ProducerInner { rb: self }, ConsumerInner { rb: self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,24 +377,35 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize, const N2: usize> PartialEq<Queue<T, N2>> for Queue<T, N>
|
impl<T, S, S2> PartialEq<QueueInner<T, S2>> for QueueInner<T, S>
|
||||||
where
|
where
|
||||||
T: PartialEq,
|
T: PartialEq,
|
||||||
|
S: Storage,
|
||||||
|
S2: Storage,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &Queue<T, N2>) -> bool {
|
fn eq(&self, other: &QueueInner<T, S2>) -> bool {
|
||||||
self.len() == other.len() && self.iter().zip(other.iter()).all(|(v1, v2)| v1 == v2)
|
self.len() == other.len() && self.iter().zip(other.iter()).all(|(v1, v2)| v1 == v2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Eq for Queue<T, N> where T: Eq {}
|
impl<T, S: Storage> Eq for QueueInner<T, S> where T: Eq {}
|
||||||
|
|
||||||
/// An iterator over the items of a queue
|
/// Base struct for [`Iter`] and [`IterView`], generic over the [`Storage`].
|
||||||
pub struct Iter<'a, T, const N: usize> {
|
///
|
||||||
rb: &'a Queue<T, N>,
|
/// In most cases you should use [`Iter`] or [`IterView`] directly. Only use this
|
||||||
|
/// struct if you want to write code that's generic over both.
|
||||||
|
pub struct IterInner<'a, T, S: Storage> {
|
||||||
|
rb: &'a QueueInner<T, S>,
|
||||||
index: usize,
|
index: usize,
|
||||||
len: usize,
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An iterator over the items of a queue
|
||||||
|
pub type Iter<'a, T, const N: usize> = IterInner<'a, T, OwnedStorage<N>>;
|
||||||
|
|
||||||
|
/// An iterator over the items of a queue
|
||||||
|
pub type IterView<'a, T> = IterInner<'a, T, ViewStorage>;
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Clone for Iter<'a, T, N> {
|
impl<'a, T, const N: usize> Clone for Iter<'a, T, N> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -364,78 +416,87 @@ impl<'a, T, const N: usize> Clone for Iter<'a, T, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mutable iterator over the items of a queue
|
/// Base struct for [`IterMut`] and [`IterMutView`], generic over the [`Storage`].
|
||||||
pub struct IterMut<'a, T, const N: usize> {
|
///
|
||||||
rb: &'a mut Queue<T, N>,
|
/// In most cases you should use [`IterMut`] or [`IterMutView`] directly. Only use this
|
||||||
|
/// struct if you want to write code that's generic over both.
|
||||||
|
pub struct IterMutInner<'a, T, S: Storage> {
|
||||||
|
rb: &'a QueueInner<T, S>,
|
||||||
index: usize,
|
index: usize,
|
||||||
len: usize,
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> {
|
/// An iterator over the items of a queue
|
||||||
|
pub type IterMut<'a, T, const N: usize> = IterMutInner<'a, T, OwnedStorage<N>>;
|
||||||
|
|
||||||
|
/// An iterator over the items of a queue
|
||||||
|
pub type IterMutView<'a, T> = IterMutInner<'a, T, ViewStorage>;
|
||||||
|
|
||||||
|
impl<'a, T, S: Storage> Iterator for IterInner<'a, T, S> {
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.index < self.len {
|
if self.index < self.len {
|
||||||
let head = self.rb.head.load(Ordering::Relaxed);
|
let head = self.rb.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
let i = (head + self.index) % N;
|
let i = (head + self.index) % self.rb.n();
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
|
|
||||||
Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) })
|
Some(unsafe { &*(self.rb.buffer.borrow().get_unchecked(i).get() as *const T) })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Iterator for IterMut<'a, T, N> {
|
impl<'a, T, S: Storage> Iterator for IterMutInner<'a, T, S> {
|
||||||
type Item = &'a mut T;
|
type Item = &'a mut T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.index < self.len {
|
if self.index < self.len {
|
||||||
let head = self.rb.head.load(Ordering::Relaxed);
|
let head = self.rb.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
let i = (head + self.index) % N;
|
let i = (head + self.index) % self.rb.n();
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
|
|
||||||
Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) })
|
Some(unsafe { &mut *(self.rb.buffer.borrow().get_unchecked(i).get() as *mut T) })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> DoubleEndedIterator for Iter<'a, T, N> {
|
impl<'a, T, S: Storage> DoubleEndedIterator for IterInner<'a, T, S> {
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
if self.index < self.len {
|
if self.index < self.len {
|
||||||
let head = self.rb.head.load(Ordering::Relaxed);
|
let head = self.rb.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
// self.len > 0, since it's larger than self.index > 0
|
// self.len > 0, since it's larger than self.index > 0
|
||||||
let i = (head + self.len - 1) % N;
|
let i = (head + self.len - 1) % self.rb.n();
|
||||||
self.len -= 1;
|
self.len -= 1;
|
||||||
Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) })
|
Some(unsafe { &*(self.rb.buffer.borrow().get_unchecked(i).get() as *const T) })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> DoubleEndedIterator for IterMut<'a, T, N> {
|
impl<'a, T, S: Storage> DoubleEndedIterator for IterMutInner<'a, T, S> {
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
if self.index < self.len {
|
if self.index < self.len {
|
||||||
let head = self.rb.head.load(Ordering::Relaxed);
|
let head = self.rb.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
// self.len > 0, since it's larger than self.index > 0
|
// self.len > 0, since it's larger than self.index > 0
|
||||||
let i = (head + self.len - 1) % N;
|
let i = (head + self.len - 1) % self.rb.n();
|
||||||
self.len -= 1;
|
self.len -= 1;
|
||||||
Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) })
|
Some(unsafe { &mut *(self.rb.buffer.borrow().get_unchecked(i).get() as *mut T) })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Drop for Queue<T, N> {
|
impl<T, S: Storage> Drop for QueueInner<T, S> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
for item in self {
|
for item in self {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -445,18 +506,20 @@ impl<T, const N: usize> Drop for Queue<T, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> fmt::Debug for Queue<T, N>
|
impl<T, S> fmt::Debug for QueueInner<T, S>
|
||||||
where
|
where
|
||||||
T: fmt::Debug,
|
T: fmt::Debug,
|
||||||
|
S: Storage,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_list().entries(self.iter()).finish()
|
f.debug_list().entries(self.iter()).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> hash::Hash for Queue<T, N>
|
impl<T, S> hash::Hash for QueueInner<T, S>
|
||||||
where
|
where
|
||||||
T: hash::Hash,
|
T: hash::Hash,
|
||||||
|
S: Storage,
|
||||||
{
|
{
|
||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
// iterate over self in order
|
// iterate over self in order
|
||||||
@ -466,41 +529,61 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> IntoIterator for &'a Queue<T, N> {
|
impl<'a, T, S: Storage> IntoIterator for &'a QueueInner<T, S> {
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
type IntoIter = Iter<'a, T, N>;
|
type IntoIter = IterInner<'a, T, S>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.iter()
|
self.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> IntoIterator for &'a mut Queue<T, N> {
|
impl<'a, T, S: Storage> IntoIterator for &'a mut QueueInner<T, S> {
|
||||||
type Item = &'a mut T;
|
type Item = &'a mut T;
|
||||||
type IntoIter = IterMut<'a, T, N>;
|
type IntoIter = IterMutInner<'a, T, S>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.iter_mut()
|
self.iter_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A queue "consumer"; it can dequeue items from the queue
|
/// Base struct for [`Consumer`] and [`ConsumerView`], generic over the [`Storage`].
|
||||||
/// NOTE the consumer semantically owns the `head` pointer of the queue
|
///
|
||||||
pub struct Consumer<'a, T, const N: usize> {
|
/// In most cases you should use [`Consumer`] or [`ConsumerView`] directly. Only use this
|
||||||
rb: &'a Queue<T, N>,
|
/// struct if you want to write code that's generic over both.
|
||||||
|
pub struct ConsumerInner<'a, T, S: Storage> {
|
||||||
|
rb: &'a QueueInner<T, S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a, T, const N: usize> Send for Consumer<'a, T, N> where T: Send {}
|
/// A queue "consumer"; it can dequeue items from the queue
|
||||||
|
/// NOTE the consumer semantically owns the `head` pointer of the queue
|
||||||
|
pub type Consumer<'a, T, const N: usize> = ConsumerInner<'a, T, OwnedStorage<N>>;
|
||||||
|
|
||||||
|
/// A queue "consumer"; it can dequeue items from the queue
|
||||||
|
/// NOTE the consumer semantically owns the `head` pointer of the queue
|
||||||
|
pub type ConsumerView<'a, T> = ConsumerInner<'a, T, ViewStorage>;
|
||||||
|
|
||||||
|
unsafe impl<'a, T, S: Storage> Send for ConsumerInner<'a, T, S> where T: Send {}
|
||||||
|
|
||||||
|
/// Base struct for [`Producer`] and [`ProducerView`], generic over the [`Storage`].
|
||||||
|
///
|
||||||
|
/// In most cases you should use [`Producer`] or [`ProducerView`] directly. Only use this
|
||||||
|
/// struct if you want to write code that's generic over both.
|
||||||
|
pub struct ProducerInner<'a, T, S: Storage> {
|
||||||
|
rb: &'a QueueInner<T, S>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A queue "producer"; it can enqueue items into the queue
|
/// A queue "producer"; it can enqueue items into the queue
|
||||||
/// NOTE the producer semantically owns the `tail` pointer of the queue
|
/// NOTE the producer semantically owns the `tail` pointer of the queue
|
||||||
pub struct Producer<'a, T, const N: usize> {
|
pub type Producer<'a, T, const N: usize> = ProducerInner<'a, T, OwnedStorage<N>>;
|
||||||
rb: &'a Queue<T, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<'a, T, const N: usize> Send for Producer<'a, T, N> where T: Send {}
|
/// A queue "producer"; it can enqueue items into the queue
|
||||||
|
/// NOTE the producer semantically owns the `tail` pointer of the queue
|
||||||
|
pub type ProducerView<'a, T> = ProducerInner<'a, T, ViewStorage>;
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Consumer<'a, T, N> {
|
unsafe impl<'a, T, S: Storage> Send for ProducerInner<'a, T, S> where T: Send {}
|
||||||
|
|
||||||
|
impl<'a, T, S: Storage> ConsumerInner<'a, T, S> {
|
||||||
/// Returns the item in the front of the queue, or `None` if the queue is empty
|
/// Returns the item in the front of the queue, or `None` if the queue is empty
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dequeue(&mut self) -> Option<T> {
|
pub fn dequeue(&mut self) -> Option<T> {
|
||||||
@ -550,7 +633,7 @@ impl<'a, T, const N: usize> Consumer<'a, T, N> {
|
|||||||
/// Returns the maximum number of elements the queue can hold
|
/// Returns the maximum number of elements the queue can hold
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn capacity(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.rb.capacity()
|
self.rb.storage_capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the item in the front of the queue without dequeuing, or `None` if the queue is
|
/// Returns the item in the front of the queue without dequeuing, or `None` if the queue is
|
||||||
@ -575,7 +658,7 @@ impl<'a, T, const N: usize> Consumer<'a, T, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const N: usize> Producer<'a, T, N> {
|
impl<'a, T, S: Storage> ProducerInner<'a, T, S> {
|
||||||
/// Adds an `item` to the end of the queue, returns back the `item` if the queue is full
|
/// Adds an `item` to the end of the queue, returns back the `item` if the queue is full
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn enqueue(&mut self, val: T) -> Result<(), T> {
|
pub fn enqueue(&mut self, val: T) -> Result<(), T> {
|
||||||
@ -624,7 +707,7 @@ impl<'a, T, const N: usize> Producer<'a, T, N> {
|
|||||||
/// Returns the maximum number of elements the queue can hold
|
/// Returns the maximum number of elements the queue can hold
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn capacity(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.rb.capacity()
|
self.rb.storage_capacity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
//! Collections of `Send`-able things are `Send`
|
//! Collections of `Send`-able things are `Send`
|
||||||
|
|
||||||
use heapless::{
|
use heapless::{
|
||||||
spsc::{Consumer, Producer, Queue},
|
histbuf::HistoryBufferView,
|
||||||
HistoryBuffer, Vec,
|
spsc::{Consumer, ConsumerView, Producer, ProducerView, Queue, QueueView},
|
||||||
|
HistoryBuffer, Vec, VecView,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -13,13 +14,18 @@ fn send() {
|
|||||||
|
|
||||||
fn is_send<T>()
|
fn is_send<T>()
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send + ?Sized,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
is_send::<Consumer<IsSend, 4>>();
|
is_send::<Consumer<IsSend, 4>>();
|
||||||
|
is_send::<ConsumerView<IsSend>>();
|
||||||
is_send::<Producer<IsSend, 4>>();
|
is_send::<Producer<IsSend, 4>>();
|
||||||
|
is_send::<ProducerView<IsSend>>();
|
||||||
is_send::<Queue<IsSend, 4>>();
|
is_send::<Queue<IsSend, 4>>();
|
||||||
|
is_send::<QueueView<IsSend>>();
|
||||||
is_send::<Vec<IsSend, 4>>();
|
is_send::<Vec<IsSend, 4>>();
|
||||||
|
is_send::<VecView<IsSend>>();
|
||||||
is_send::<HistoryBuffer<IsSend, 4>>();
|
is_send::<HistoryBuffer<IsSend, 4>>();
|
||||||
|
is_send::<HistoryBufferView<IsSend>>();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user