mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-26 03:49:34 +01:00
Merge #511
511: Remove linked list impl - use heapless, linked list init now const fn r=AfoHT a=korken89 Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
This commit is contained in:
commit
6b9966678c
8 changed files with 16 additions and 625 deletions
|
@ -44,7 +44,7 @@ cortex-m = "0.7.0"
|
||||||
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" }
|
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" }
|
||||||
rtic-monotonic = "0.1.0-alpha.2"
|
rtic-monotonic = "0.1.0-alpha.2"
|
||||||
rtic-core = "0.3.1"
|
rtic-core = "0.3.1"
|
||||||
heapless = "0.7.1"
|
heapless = "0.7.5"
|
||||||
bare-metal = "1.0.0"
|
bare-metal = "1.0.0"
|
||||||
|
|
||||||
[dependencies.dwt-systick-monotonic]
|
[dependencies.dwt-systick-monotonic]
|
||||||
|
|
|
@ -327,7 +327,7 @@ pub fn codegen(
|
||||||
impl #internal_spawn_handle_ident {
|
impl #internal_spawn_handle_ident {
|
||||||
pub fn cancel(self) -> Result<#ty, ()> {
|
pub fn cancel(self) -> Result<#ty, ()> {
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
rtic::export::interrupt::free(|_| unsafe {
|
||||||
let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr();
|
let tq = #tq.get_mut_unchecked();
|
||||||
if let Some((_task, index)) = tq.cancel_marker(self.marker) {
|
if let Some((_task, index)) = tq.cancel_marker(self.marker) {
|
||||||
// Get the message
|
// Get the message
|
||||||
let msg = #inputs
|
let msg = #inputs
|
||||||
|
@ -359,7 +359,7 @@ pub fn codegen(
|
||||||
let marker = *#tq_marker.get_mut_unchecked();
|
let marker = *#tq_marker.get_mut_unchecked();
|
||||||
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
||||||
|
|
||||||
let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr();
|
let tq = #tq.get_mut_unchecked();
|
||||||
|
|
||||||
tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
|
tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
|
||||||
})
|
})
|
||||||
|
@ -420,7 +420,7 @@ pub fn codegen(
|
||||||
|
|
||||||
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1);
|
||||||
|
|
||||||
let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr();
|
let tq = #tq.get_mut_unchecked();
|
||||||
|
|
||||||
tq.enqueue_unchecked(
|
tq.enqueue_unchecked(
|
||||||
nr,
|
nr,
|
||||||
|
|
|
@ -77,18 +77,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
);));
|
);));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize monotonic's interrupts and timer queues
|
// Initialize monotonic's interrupts
|
||||||
for (_, monotonic) in &app.monotonics {
|
for (_, monotonic) in &app.monotonics {
|
||||||
let priority = &monotonic.args.priority;
|
let priority = &monotonic.args.priority;
|
||||||
let binds = &monotonic.args.binds;
|
let binds = &monotonic.args.binds;
|
||||||
let monotonic_name = monotonic.ident.to_string();
|
|
||||||
let tq = util::tq_ident(&monotonic_name);
|
|
||||||
let tq = util::mark_internal_ident(&tq);
|
|
||||||
|
|
||||||
// Initialize timer queues
|
|
||||||
stmts.push(
|
|
||||||
quote!(#tq.get_mut_unchecked().as_mut_ptr().write(rtic::export::TimerQueue::new());),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compile time assert that this priority is supported by the device
|
// Compile time assert that this priority is supported by the device
|
||||||
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
||||||
|
|
|
@ -62,21 +62,20 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
{
|
{
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = &format!("Timer queue for {}", monotonic_name);
|
// let doc = &format!("Timer queue for {}", monotonic_name);
|
||||||
let cap: u8 = app
|
let cap: usize = app
|
||||||
.software_tasks
|
.software_tasks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_name, task)| task.args.capacity)
|
.map(|(_name, task)| task.args.capacity as usize)
|
||||||
.sum();
|
.sum();
|
||||||
let n = util::capacity_literal(cap as usize);
|
let n = util::capacity_literal(cap);
|
||||||
let tq_ty =
|
let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n>);
|
||||||
quote!(core::mem::MaybeUninit<rtic::export::TimerQueue<#mono_type, #t, #n>>);
|
|
||||||
|
|
||||||
// For future use
|
// For future use
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
static #tq: rtic::RacyCell<#tq_ty> =
|
static #tq: rtic::RacyCell<#tq_ty> =
|
||||||
rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
|
rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16()));
|
||||||
));
|
));
|
||||||
|
|
||||||
let mono = util::monotonic_ident(&monotonic_name);
|
let mono = util::monotonic_ident(&monotonic_name);
|
||||||
|
@ -138,7 +137,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
||||||
unsafe fn #bound_interrupt() {
|
unsafe fn #bound_interrupt() {
|
||||||
while let Some((task, index)) = rtic::export::interrupt::free(|_|
|
while let Some((task, index)) = rtic::export::interrupt::free(|_|
|
||||||
if let Some(mono) = #m_ident.get_mut_unchecked().as_mut() {
|
if let Some(mono) = #m_ident.get_mut_unchecked().as_mut() {
|
||||||
(&mut *#tq.get_mut_unchecked().as_mut_ptr()).dequeue(|| #disable_isr, mono)
|
#tq.get_mut_unchecked().dequeue(|| #disable_isr, mono)
|
||||||
} else {
|
} else {
|
||||||
// We can only use the timer queue if `init` has returned, and it
|
// We can only use the timer queue if `init` has returned, and it
|
||||||
// writes the `Some(monotonic)` we are accessing here.
|
// writes the `Some(monotonic)` we are accessing here.
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub use cortex_m::{
|
||||||
peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC},
|
peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC},
|
||||||
Peripherals,
|
Peripherals,
|
||||||
};
|
};
|
||||||
|
pub use heapless::sorted_linked_list::SortedLinkedList;
|
||||||
pub use heapless::spsc::Queue;
|
pub use heapless::spsc::Queue;
|
||||||
pub use heapless::BinaryHeap;
|
pub use heapless::BinaryHeap;
|
||||||
pub use rtic_monotonic as monotonic;
|
pub use rtic_monotonic as monotonic;
|
||||||
|
|
|
@ -43,8 +43,6 @@ pub use rtic_monotonic::{self, embedded_time as time, Monotonic};
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod export;
|
pub mod export;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
mod linked_list;
|
|
||||||
#[doc(hidden)]
|
|
||||||
mod tq;
|
mod tq;
|
||||||
|
|
||||||
/// Sets the given `interrupt` as pending
|
/// Sets the given `interrupt` as pending
|
||||||
|
|
|
@ -1,597 +0,0 @@
|
||||||
use core::fmt;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
use core::ptr;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
struct LinkedIndex(u16);
|
|
||||||
|
|
||||||
impl LinkedIndex {
|
|
||||||
#[inline]
|
|
||||||
const unsafe fn new_unchecked(value: u16) -> Self {
|
|
||||||
LinkedIndex(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn none() -> Self {
|
|
||||||
LinkedIndex(u16::MAX)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn option(self) -> Option<u16> {
|
|
||||||
if self.0 == u16::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A node in the linked list.
|
|
||||||
pub struct Node<T> {
|
|
||||||
val: MaybeUninit<T>,
|
|
||||||
next: LinkedIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator for the linked list.
|
|
||||||
pub struct Iter<'a, T, Kind, const N: usize>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
list: &'a LinkedList<T, Kind, N>,
|
|
||||||
index: LinkedIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, Kind, const N: usize> Iterator for Iter<'a, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
type Item = &'a T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let index = self.index.option()?;
|
|
||||||
|
|
||||||
let node = self.list.node_at(index as usize);
|
|
||||||
self.index = node.next;
|
|
||||||
|
|
||||||
Some(self.list.read_data_in_node_at(index as usize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Comes from [`LinkedList::find_mut`].
|
|
||||||
pub struct FindMut<'a, T, Kind, const N: usize>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
list: &'a mut LinkedList<T, Kind, N>,
|
|
||||||
is_head: bool,
|
|
||||||
prev_index: LinkedIndex,
|
|
||||||
index: LinkedIndex,
|
|
||||||
maybe_changed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, Kind, const N: usize> FindMut<'a, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn pop_internal(&mut self) -> T {
|
|
||||||
if self.is_head {
|
|
||||||
// If it is the head element, we can do a normal pop
|
|
||||||
unsafe { self.list.pop_unchecked() }
|
|
||||||
} else {
|
|
||||||
// Somewhere in the list
|
|
||||||
|
|
||||||
// Re-point the previous index
|
|
||||||
self.list.node_at_mut(self.prev_index.0 as usize).next =
|
|
||||||
self.list.node_at_mut(self.index.0 as usize).next;
|
|
||||||
|
|
||||||
// Release the index into the free queue
|
|
||||||
self.list.node_at_mut(self.index.0 as usize).next = self.list.free;
|
|
||||||
self.list.free = self.index;
|
|
||||||
|
|
||||||
self.list.extract_data_in_node_at(self.index.0 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will pop the element from the list.
|
|
||||||
///
|
|
||||||
/// Complexity is O(1).
|
|
||||||
#[inline]
|
|
||||||
pub fn pop(mut self) -> T {
|
|
||||||
self.pop_internal()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will resort the element into the correct position in the list in needed.
|
|
||||||
/// Same as calling `drop`.
|
|
||||||
///
|
|
||||||
/// Complexity is worst-case O(N).
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
drop(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> Drop for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Only resort the list if the element has changed
|
|
||||||
if self.maybe_changed {
|
|
||||||
let val = self.pop_internal();
|
|
||||||
unsafe { self.list.push_unchecked(val) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> Deref for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.list.read_data_in_node_at(self.index.0 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> DerefMut for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.maybe_changed = true;
|
|
||||||
self.list.read_mut_data_in_node_at(self.index.0 as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> fmt::Debug for FindMut<'_, T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd + core::fmt::Debug,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("FindMut")
|
|
||||||
.field("prev_index", &self.prev_index)
|
|
||||||
.field("index", &self.index)
|
|
||||||
.field(
|
|
||||||
"prev_value",
|
|
||||||
&self
|
|
||||||
.list
|
|
||||||
.read_data_in_node_at(self.prev_index.option().unwrap() as usize),
|
|
||||||
)
|
|
||||||
.field(
|
|
||||||
"value",
|
|
||||||
&self
|
|
||||||
.list
|
|
||||||
.read_data_in_node_at(self.index.option().unwrap() as usize),
|
|
||||||
)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The linked list.
|
|
||||||
pub struct LinkedList<T, Kind, const N: usize>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
list: MaybeUninit<[Node<T>; N]>,
|
|
||||||
head: LinkedIndex,
|
|
||||||
free: LinkedIndex,
|
|
||||||
_kind: PhantomData<Kind>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> LinkedList<T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn node_at(&self, index: usize) -> &Node<T> {
|
|
||||||
// Safety: The entire `self.list` is initialized in `new`, which makes this safe.
|
|
||||||
unsafe { &*(self.list.as_ptr() as *const Node<T>).add(index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn node_at_mut(&mut self, index: usize) -> &mut Node<T> {
|
|
||||||
// Safety: The entire `self.list` is initialized in `new`, which makes this safe.
|
|
||||||
unsafe { &mut *(self.list.as_mut_ptr() as *mut Node<T>).add(index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn write_data_in_node_at(&mut self, index: usize, data: T) {
|
|
||||||
unsafe {
|
|
||||||
self.node_at_mut(index).val.as_mut_ptr().write(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn read_data_in_node_at(&self, index: usize) -> &T {
|
|
||||||
unsafe { &*self.node_at(index).val.as_ptr() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn read_mut_data_in_node_at(&mut self, index: usize) -> &mut T {
|
|
||||||
unsafe { &mut *self.node_at_mut(index).val.as_mut_ptr() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
#[inline]
|
|
||||||
fn extract_data_in_node_at(&mut self, index: usize) -> T {
|
|
||||||
unsafe { self.node_at(index).val.as_ptr().read() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal helper to not do pointer arithmetic all over the place.
|
|
||||||
/// Safety: This can overwrite existing allocated nodes if used improperly, meaning their
|
|
||||||
/// `Drop` methods won't run.
|
|
||||||
#[inline]
|
|
||||||
unsafe fn write_node_at(&mut self, index: usize, node: Node<T>) {
|
|
||||||
(self.list.as_mut_ptr() as *mut Node<T>)
|
|
||||||
.add(index)
|
|
||||||
.write(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new linked list.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut list = LinkedList {
|
|
||||||
list: MaybeUninit::uninit(),
|
|
||||||
head: LinkedIndex::none(),
|
|
||||||
free: unsafe { LinkedIndex::new_unchecked(0) },
|
|
||||||
_kind: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
let len = N as u16;
|
|
||||||
let mut free = 0;
|
|
||||||
|
|
||||||
if len == 0 {
|
|
||||||
list.free = LinkedIndex::none();
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize indexes
|
|
||||||
while free < len - 1 {
|
|
||||||
unsafe {
|
|
||||||
list.write_node_at(
|
|
||||||
free as usize,
|
|
||||||
Node {
|
|
||||||
val: MaybeUninit::uninit(),
|
|
||||||
next: LinkedIndex::new_unchecked(free + 1),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
free += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize final index
|
|
||||||
unsafe {
|
|
||||||
list.write_node_at(
|
|
||||||
free as usize,
|
|
||||||
Node {
|
|
||||||
val: MaybeUninit::uninit(),
|
|
||||||
next: LinkedIndex::none(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
list
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push unchecked
|
|
||||||
///
|
|
||||||
/// Complexity is O(N).
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Assumes that the list is not full.
|
|
||||||
pub unsafe fn push_unchecked(&mut self, value: T) {
|
|
||||||
let new = self.free.0;
|
|
||||||
// Store the data and update the next free spot
|
|
||||||
self.write_data_in_node_at(new as usize, value);
|
|
||||||
self.free = self.node_at(new as usize).next;
|
|
||||||
|
|
||||||
if let Some(head) = self.head.option() {
|
|
||||||
// Check if we need to replace head
|
|
||||||
if self
|
|
||||||
.read_data_in_node_at(head as usize)
|
|
||||||
.partial_cmp(self.read_data_in_node_at(new as usize))
|
|
||||||
!= Kind::ordering()
|
|
||||||
{
|
|
||||||
self.node_at_mut(new as usize).next = self.head;
|
|
||||||
self.head = LinkedIndex::new_unchecked(new);
|
|
||||||
} else {
|
|
||||||
// It's not head, search the list for the correct placement
|
|
||||||
let mut current = head;
|
|
||||||
|
|
||||||
while let Some(next) = self.node_at(current as usize).next.option() {
|
|
||||||
if self
|
|
||||||
.read_data_in_node_at(next as usize)
|
|
||||||
.partial_cmp(self.read_data_in_node_at(new as usize))
|
|
||||||
!= Kind::ordering()
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.node_at_mut(new as usize).next = self.node_at(current as usize).next;
|
|
||||||
self.node_at_mut(current as usize).next = LinkedIndex::new_unchecked(new);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.node_at_mut(new as usize).next = self.head;
|
|
||||||
self.head = LinkedIndex::new_unchecked(new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes an element to the linked list and sorts it into place.
|
|
||||||
///
|
|
||||||
/// Complexity is O(N).
|
|
||||||
pub fn push(&mut self, value: T) -> Result<(), T> {
|
|
||||||
if !self.is_full() {
|
|
||||||
Ok(unsafe { self.push_unchecked(value) })
|
|
||||||
} else {
|
|
||||||
Err(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over the sorted list.
|
|
||||||
pub fn iter(&self) -> Iter<'_, T, Kind, N> {
|
|
||||||
Iter {
|
|
||||||
list: self,
|
|
||||||
index: self.head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find an element in the list.
|
|
||||||
pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, T, Kind, N>>
|
|
||||||
where
|
|
||||||
F: FnMut(&T) -> bool,
|
|
||||||
{
|
|
||||||
let head = self.head.option()?;
|
|
||||||
|
|
||||||
// Special-case, first element
|
|
||||||
if f(self.read_data_in_node_at(head as usize)) {
|
|
||||||
return Some(FindMut {
|
|
||||||
is_head: true,
|
|
||||||
prev_index: LinkedIndex::none(),
|
|
||||||
index: self.head,
|
|
||||||
list: self,
|
|
||||||
maybe_changed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut current = head;
|
|
||||||
|
|
||||||
while let Some(next) = self.node_at(current as usize).next.option() {
|
|
||||||
if f(self.read_data_in_node_at(next as usize)) {
|
|
||||||
return Some(FindMut {
|
|
||||||
is_head: false,
|
|
||||||
prev_index: unsafe { LinkedIndex::new_unchecked(current) },
|
|
||||||
index: unsafe { LinkedIndex::new_unchecked(next) },
|
|
||||||
list: self,
|
|
||||||
maybe_changed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Peek at the first element.
|
|
||||||
pub fn peek(&self) -> Option<&T> {
|
|
||||||
self.head
|
|
||||||
.option()
|
|
||||||
.map(|head| self.read_data_in_node_at(head as usize))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pop unchecked
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Assumes that the list is not empty.
|
|
||||||
pub unsafe fn pop_unchecked(&mut self) -> T {
|
|
||||||
let head = self.head.0;
|
|
||||||
let current = head;
|
|
||||||
self.head = self.node_at(head as usize).next;
|
|
||||||
self.node_at_mut(current as usize).next = self.free;
|
|
||||||
self.free = LinkedIndex::new_unchecked(current);
|
|
||||||
|
|
||||||
self.extract_data_in_node_at(current as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pops the first element in the list.
|
|
||||||
///
|
|
||||||
/// Complexity is O(1).
|
|
||||||
pub fn pop(&mut self) -> Result<T, ()> {
|
|
||||||
if !self.is_empty() {
|
|
||||||
Ok(unsafe { self.pop_unchecked() })
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the linked list is full.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_full(&self) -> bool {
|
|
||||||
self.free.option().is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the linked list is empty.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.head.option().is_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> Drop for LinkedList<T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut index = self.head;
|
|
||||||
|
|
||||||
while let Some(i) = index.option() {
|
|
||||||
let node = self.node_at_mut(i as usize);
|
|
||||||
index = node.next;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ptr::drop_in_place(node.val.as_mut_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, Kind, const N: usize> fmt::Debug for LinkedList<T, Kind, N>
|
|
||||||
where
|
|
||||||
T: PartialEq + PartialOrd + core::fmt::Debug,
|
|
||||||
Kind: kind::Kind,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_list().entries(self.iter()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Min sorted linked list.
|
|
||||||
pub struct Min;
|
|
||||||
|
|
||||||
/// Max sorted linked list.
|
|
||||||
pub struct Max;
|
|
||||||
|
|
||||||
/// Sealed traits and implementations for `linked_list`
|
|
||||||
pub mod kind {
|
|
||||||
use super::{Max, Min};
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
|
|
||||||
/// The linked list kind: min first or max first
|
|
||||||
pub unsafe trait Kind {
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn ordering() -> Option<Ordering>;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Kind for Min {
|
|
||||||
#[inline]
|
|
||||||
fn ordering() -> Option<Ordering> {
|
|
||||||
Some(Ordering::Less)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Kind for Max {
|
|
||||||
#[inline]
|
|
||||||
fn ordering() -> Option<Ordering> {
|
|
||||||
Some(Ordering::Greater)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
// Note this useful idiom: importing names from outer (for mod tests) scope.
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_peek() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &2);
|
|
||||||
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &3);
|
|
||||||
|
|
||||||
let mut ll: LinkedList<u32, Min, 3> = LinkedList::new();
|
|
||||||
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &2);
|
|
||||||
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_full() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
|
|
||||||
assert!(ll.is_full())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty() {
|
|
||||||
let ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
|
|
||||||
assert!(ll.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_zero_size() {
|
|
||||||
let ll: LinkedList<u32, Max, 0> = LinkedList::new();
|
|
||||||
|
|
||||||
assert!(ll.is_empty());
|
|
||||||
assert!(ll.is_full());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rejected_push() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
|
|
||||||
// This won't fit
|
|
||||||
let r = ll.push(4);
|
|
||||||
|
|
||||||
assert_eq!(r, Err(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_updating() {
|
|
||||||
let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
|
|
||||||
ll.push(1).unwrap();
|
|
||||||
ll.push(2).unwrap();
|
|
||||||
ll.push(3).unwrap();
|
|
||||||
|
|
||||||
let mut find = ll.find_mut(|v| *v == 2).unwrap();
|
|
||||||
|
|
||||||
*find += 1000;
|
|
||||||
find.finish();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1002);
|
|
||||||
|
|
||||||
let mut find = ll.find_mut(|v| *v == 3).unwrap();
|
|
||||||
|
|
||||||
*find += 1000;
|
|
||||||
find.finish();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1003);
|
|
||||||
|
|
||||||
// Remove largest element
|
|
||||||
ll.find_mut(|v| *v == 1003).unwrap().pop();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1002);
|
|
||||||
}
|
|
||||||
}
|
|
10
src/tq.rs
10
src/tq.rs
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
linked_list::{LinkedList, Min},
|
|
||||||
time::{Clock, Instant},
|
time::{Clock, Instant},
|
||||||
Monotonic,
|
Monotonic,
|
||||||
};
|
};
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList};
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unwrapper<T, E>(val: Result<T, E>) -> T {
|
fn unwrapper<T, E>(val: Result<T, E>) -> T {
|
||||||
|
@ -14,7 +14,9 @@ fn unwrapper<T, E>(val: Result<T, E>) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TimerQueue<Mono, Task, const N: usize>(pub LinkedList<NotReady<Mono, Task>, Min, N>)
|
pub struct TimerQueue<Mono, Task, const N: usize>(
|
||||||
|
pub SortedLinkedList<NotReady<Mono, Task>, LinkedIndexU16, Min, N>,
|
||||||
|
)
|
||||||
where
|
where
|
||||||
Mono: Monotonic,
|
Mono: Monotonic,
|
||||||
Task: Copy;
|
Task: Copy;
|
||||||
|
@ -24,10 +26,6 @@ where
|
||||||
Mono: Monotonic,
|
Mono: Monotonic,
|
||||||
Task: Copy,
|
Task: Copy,
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
|
||||||
TimerQueue(LinkedList::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Writing to memory with a transmute in order to enable
|
/// Writing to memory with a transmute in order to enable
|
||||||
|
|
Loading…
Reference in a new issue