From 202cacd76416022690714ec2cf6659b7dc413ed0 Mon Sep 17 00:00:00 2001 From: janis Date: Sat, 8 Aug 2026 22:04:59 +0200 Subject: [PATCH] foundation: linked list + DormantMutRef --- .../foundation/src/collections/linked_list.rs | 182 ++++++++++++++++-- crates/foundation/src/lib.rs | 73 +++++++ 2 files changed, 237 insertions(+), 18 deletions(-) diff --git a/crates/foundation/src/collections/linked_list.rs b/crates/foundation/src/collections/linked_list.rs index c1a3cbf..4c47550 100644 --- a/crates/foundation/src/collections/linked_list.rs +++ b/crates/foundation/src/collections/linked_list.rs @@ -1,19 +1,104 @@ +use core::alloc::{Allocator, Layout}; use core::ptr::NonNull; +use crate::mem::DormantMutRef; + pub unsafe trait LinkedListNode { fn next(this: NonNull) -> Option>; fn set_next(this: NonNull, next: Option>); } +#[derive(Debug)] pub struct LinkedList { head: Option>, } +pub struct NextOf<'a, T: LinkedListNode> { + parent: NonNull, + node: NonNull, + _marker: core::marker::PhantomData<&'a mut T>, +} +pub struct Head<'a, T: LinkedListNode> { + list: DormantMutRef<'a, LinkedList>, + node: NonNull, +} + +pub enum LinkedListEntry<'a, T: LinkedListNode> { + NextOf(NextOf<'a, T>), + Head(Head<'a, T>), +} + +impl<'a, T: LinkedListNode> LinkedListEntry<'a, T> { + unsafe fn new_head(list: &'a mut LinkedList, head: NonNull) -> Self { + Self::Head(Head { + list: DormantMutRef::new(list).1, + node: head, + }) + } + pub fn get(&self) -> &T { + match self { + LinkedListEntry::NextOf(NextOf { node, .. }) => unsafe { node.as_ref() }, + LinkedListEntry::Head(Head { node, .. }) => unsafe { node.as_ref() }, + } + } + + pub fn get_mut(&mut self) -> &mut T { + match self { + LinkedListEntry::NextOf(NextOf { node, .. }) => unsafe { node.as_mut() }, + LinkedListEntry::Head(Head { node, .. }) => unsafe { node.as_mut() }, + } + } + + pub fn remove(self) -> NonNull { + match self { + LinkedListEntry::NextOf(NextOf { parent, node, .. }) => { + let next = T::next(node); + T::set_next(parent, next); + node + } + LinkedListEntry::Head(Head { list, node }) => { + let next = T::next(node); + unsafe { list.awaken() }.head = next; + node + } + } + } + + pub fn inner(&self) -> NonNull { + match self { + LinkedListEntry::NextOf(NextOf { node, .. }) => *node, + LinkedListEntry::Head(Head { node, .. }) => *node, + } + } + pub fn next(&self) -> Option> { + match self { + LinkedListEntry::NextOf(NextOf { node, .. }) => T::next(*node), + LinkedListEntry::Head(Head { node, .. }) => T::next(*node), + } + } +} + impl LinkedList { pub const fn new() -> Self { Self { head: None } } + pub fn get_or_insert_front_with(&mut self, f: F) -> LinkedListEntry<'_, T> + where + F: FnOnce() -> NonNull, + { + let head = if let Some(head) = self.head { + head + } else { + let node = f(); + self.push_front(node); + node + }; + + // SAFETY: head is the head of the list + unsafe { LinkedListEntry::new_head(self, head) } + } + pub fn push_front(&mut self, node: NonNull) { T::set_next(node, self.head); self.head = Some(node); @@ -25,29 +110,31 @@ impl LinkedList { Some(node) } - pub fn remove_if(&mut self, mut predicate: impl FnMut(NonNull) -> bool) { - let mut current = self.head; - let mut prev: Option> = None; - - while let Some(node) = current { - if predicate(node) { - let next = T::next(node); - if let Some(prev_node) = prev { - T::set_next(prev_node, next); - } else { - self.head = next; - } - current = next; - } else { - prev = current; - current = T::next(node); - } - } + pub fn remove_if(&mut self, predicate: F) -> RemoveIf<'_, T, F> + where + F: FnMut(NonNull) -> bool, + { + RemoveIf::new(self, predicate) } pub fn iter(&self) -> LinkedListIter { LinkedListIter { current: self.head } } + pub fn into_iter(self) -> LinkedListIter { + LinkedListIter { current: self.head } + } + pub fn entries(&mut self) -> LinkedListEntries<'_, T> { + LinkedListEntries::new_head(self) + } + + pub unsafe fn drop_in(mut self, alloc: &A) { + let iter = LinkedListIter { + current: self.head.take(), + }; + for node in iter { + unsafe { alloc.deallocate(node.cast(), Layout::new::()) } + } + } } impl Default for LinkedList { @@ -56,6 +143,65 @@ impl Default for LinkedList { } } +pub struct RemoveIf<'a, T: LinkedListNode, F> { + entries: LinkedListEntries<'a, T>, + pred: F, +} + +impl<'a, T: LinkedListNode, F> RemoveIf<'a, T, F> { + pub fn new(list: &'a mut LinkedList, pred: F) -> Self { + Self { + entries: LinkedListEntries::new_head(list), + pred, + } + } +} + +impl<'a, T: LinkedListNode, F> Iterator for RemoveIf<'a, T, F> +where + F: FnMut(NonNull) -> bool, +{ + type Item = NonNull; + + fn next(&mut self) -> Option { + while let Some(entry) = self.entries.next() { + let node = entry.inner(); + if (self.pred)(node) { + return Some(entry.remove()); + } + } + None + } +} + +pub struct LinkedListEntries<'a, T: LinkedListNode> { + current: Option>, +} + +impl<'a, T: LinkedListNode> LinkedListEntries<'a, T> { + fn new_head(list: &'a mut LinkedList) -> Self { + let current = list + .head + .map(|head| unsafe { LinkedListEntry::new_head(list, head) }); + Self { current } + } + pub fn next(&mut self) -> Option> { + if let Some(current) = self.current.take() { + self.current = match current.next() { + Some(next_node) => Some(LinkedListEntry::NextOf(NextOf { + parent: current.inner(), + node: next_node, + _marker: core::marker::PhantomData, + })), + None => None, + }; + Some(current) + } else { + None + } + } +} + pub struct LinkedListIter { current: Option>, } diff --git a/crates/foundation/src/lib.rs b/crates/foundation/src/lib.rs index 9ff0893..cb26317 100644 --- a/crates/foundation/src/lib.rs +++ b/crates/foundation/src/lib.rs @@ -54,6 +54,79 @@ pub mod mem { } } } + + use core::marker::PhantomData; + use core::ptr::NonNull; + + /// Models a reborrow of some unique reference, when you know that the reborrow + /// and all its descendants (i.e., all pointers and references derived from it) + /// will not be used any more at some point, after which you want to use the + /// original unique reference again. + /// + /// The borrow checker usually handles this stacking of borrows for you, but + /// some control flows that accomplish this stacking are too complicated for + /// the compiler to follow. A `DormantMutRef` allows you to check borrowing + /// yourself, while still expressing its stacked nature, and encapsulating + /// the raw pointer code needed to do this without undefined behavior. + pub(super) struct DormantMutRef<'a, T> { + ptr: NonNull, + _marker: PhantomData<&'a mut T>, + } + + unsafe impl<'a, T> Sync for DormantMutRef<'a, T> where &'a mut T: Sync {} + unsafe impl<'a, T> Send for DormantMutRef<'a, T> where &'a mut T: Send {} + + impl<'a, T> DormantMutRef<'a, T> { + /// Capture a unique borrow, and immediately reborrow it. For the compiler, + /// the lifetime of the new reference is the same as the lifetime of the + /// original reference, but you promise to use it for a shorter period. + pub(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { + let ptr = NonNull::from(t); + // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose + // only this reference, so it is unique. + let new_ref = unsafe { &mut *ptr.as_ptr() }; + ( + new_ref, + Self { + ptr, + _marker: PhantomData, + }, + ) + } + + /// Revert to the unique borrow initially captured. + /// + /// # Safety + /// + /// The reborrow must have ended, i.e., the reference returned by `new` and + /// all pointers and references derived from it, must not be used anymore. + pub(super) unsafe fn awaken(self) -> &'a mut T { + // SAFETY: our own safety conditions imply this reference is again unique. + unsafe { &mut *self.ptr.as_ptr() } + } + + /// Borrows a new mutable reference from the unique borrow initially captured. + /// + /// # Safety + /// + /// The reborrow must have ended, i.e., the reference returned by `new` and + /// all pointers and references derived from it, must not be used anymore. + pub(super) unsafe fn reborrow(&mut self) -> &'a mut T { + // SAFETY: our own safety conditions imply this reference is again unique. + unsafe { &mut *self.ptr.as_ptr() } + } + + /// Borrows a new shared reference from the unique borrow initially captured. + /// + /// # Safety + /// + /// The reborrow must have ended, i.e., the reference returned by `new` and + /// all pointers and references derived from it, must not be used anymore. + pub(super) unsafe fn reborrow_shared(&self) -> &'a T { + // SAFETY: our own safety conditions imply this reference is again unique. + unsafe { &*self.ptr.as_ptr() } + } + } } pub struct DropGuard(::core::mem::ManuallyDrop);