blob: c06aa098c451e9f7b0181ea74c9745c3de0cb451 [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#![no_std]
#![cfg_attr(test, no_main)]
use core::cmp::Ordering;
use core::marker::PhantomData;
use core::ptr::NonNull;
use foreign_box::ForeignBox;
pub mod unsafe_list;
pub type Iter<'a, T, A> = unsafe_list::Iter<'a, T, A>;
pub use unsafe_list::{Adapter, Link, UnsafeList};
pub struct ForeignList<T, A: Adapter<T>> {
list: UnsafeList<T, A>,
}
// Safety:
// A given node can only be in a single [`ForeignList`] at a time. This list
// logically owns the node for the duration of its membership in the list. All
// mutation of the node's `Link` pointers are done while the node is in the
// list. There is no API to get a mutable reference to a node while it is in the
// list nor directly manipulate its membership or position in the list.
unsafe impl<T: Send, A: Adapter<T>> Send for ForeignList<T, A> {}
unsafe impl<T: Sync, A: Adapter<T>> Sync for ForeignList<T, A> {}
impl<T, A: Adapter<T>> Default for ForeignList<T, A> {
fn default() -> Self {
Self::new()
}
}
impl<T, A: Adapter<T>> ForeignList<T, A> {
#[must_use]
pub const fn new() -> Self {
Self {
list: UnsafeList::new(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
unsafe { self.list.is_empty() }
}
pub fn push_front(&mut self, element: ForeignBox<T>) {
let element = element.consume();
unsafe { self.list.push_front_unchecked(element) }
}
pub fn push_back(&mut self, element: ForeignBox<T>) {
let element = element.consume();
unsafe { self.list.push_back_unchecked(element) }
}
pub fn pop_head(&mut self) -> Option<ForeignBox<T>> {
unsafe { self.list.pop_head().map(|element| ForeignBox::new(element)) }
}
pub fn for_each<E, F: FnMut(&T) -> Result<(), E>>(&self, callback: F) -> Result<(), E> {
unsafe { self.list.for_each(callback) }
}
#[must_use]
pub fn iter(&self) -> Iter<'_, T, A> {
self.list.iter()
}
// Note: `iter_mut()` is intentionally not provided. Providing `&mut T` would
// expose the internal `Link` field, allowing callers to corrupt the list's
// structure or violate Rust's sharing rules (since intrusive elements are
// often shared via raw pointers).
/// # Safety
/// Caller ensures the element is a valid pointer to an instance of T.
pub unsafe fn remove_element(&mut self, element: NonNull<T>) -> Option<ForeignBox<T>> {
unsafe {
self.list
.unlink_element(element)
.map(|element| ForeignBox::new(element))
}
}
}
impl<T, A: Adapter<T>> Drop for ForeignList<T, A> {
fn drop(&mut self) {
if !self.is_empty() {
pw_assert::panic!("ForeignList dropped while non-empty");
}
}
}
impl<T, A: Adapter<T>> ForeignList<T, A> {
pub fn sorted_insert_by_key<F: FnMut(&T) -> K, K: Ord>(
&mut self,
element: ForeignBox<T>,
mut f: F,
) {
self.sorted_insert_by(element, move |a, b| f(a).cmp(&f(b)))
}
pub fn sorted_insert_by<F: FnMut(&T, &T) -> Ordering>(
&mut self,
element: ForeignBox<T>,
compare: F,
) {
let element = element.consume();
unsafe { self.list.sorted_insert_by_unchecked(element, compare) }
}
}
/// A key used to remove an element from a [`RandomAccessForeignList`]
pub struct RandomAccessKey<T, A: Adapter<T>> {
// `new()` ensure that this is a valid, well-aligned, pointer to a `T`.
ptr: NonNull<T>,
_phantom: PhantomData<A>,
}
impl<T, A: Adapter<T>> RandomAccessKey<T, A> {
/// # Safety
/// Caller ensures the element is a valid pointer to an instance of T.
unsafe fn new(element: &mut ForeignBox<T>) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(element.as_mut_ptr()) },
_phantom: PhantomData,
}
}
}
/// A variant of [`ForeignList`] that allows safely removal of arbitrary nodes
/// in exchange no pop operations.
///
/// When a node is added to a [`RandomAccessForeignList`], a [`RandomAccessKey`]
/// is returned. The node may only be removed from this list by passing that
/// key to [`RandomAccessForeignList::remove_element()`].
///
/// # Example
/// ```
/// use core::ptr::NonNull;
///
/// use foreign_box::ForeignBox;
/// use list::{Link, RandomAccessForeignList, define_adapter};
///
/// struct Element {
/// value: u32,
/// link: Link,
/// }
///
/// define_adapter!(Adapter => Element::link);
///
/// let mut element1 = Element {
/// value: 1,
/// link: Link::new(),
/// };
/// let mut element2 = Element {
/// value: 2,
/// link: Link::new(),
/// };
/// let mut element3 = Element {
/// value: 3,
/// link: Link::new(),
/// };
///
/// let mut list = RandomAccessForeignList::<Element, Adapter>::new();
/// let key3 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
/// let key2 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
/// let key1 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
///
/// let removed_element = list.remove_element(key2);
/// assert_eq!(removed_element.consume(), NonNull::new(&raw mut element2).unwrap());
///
/// let _ = list.remove_element(key1).consume();
/// let _ = list.remove_element(key3).consume();
/// ```
///
pub struct RandomAccessForeignList<T, A: Adapter<T>> {
list: ForeignList<T, A>,
}
impl<T, A: Adapter<T>> RandomAccessForeignList<T, A> {
#[must_use]
pub const fn new() -> Self {
Self {
list: ForeignList::new(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
#[must_use]
pub fn push_front(&mut self, mut element: ForeignBox<T>) -> RandomAccessKey<T, A> {
// SAFETY: Caller ensures the element is a valid pointer to an instance of T.
let key = unsafe { RandomAccessKey::new(&mut element) };
self.list.push_front(element);
key
}
#[must_use]
pub fn push_back(&mut self, mut element: ForeignBox<T>) -> RandomAccessKey<T, A> {
// SAFETY: Caller ensures the element is a valid pointer to an instance of T.
let key = unsafe { RandomAccessKey::new(&mut element) };
self.list.push_back(element);
key
}
pub fn for_each<E, F: FnMut(&T) -> Result<(), E>>(&self, callback: F) -> Result<(), E> {
self.list.for_each(callback)
}
#[must_use]
pub fn iter(&self) -> Iter<'_, T, A> {
self.list.iter()
}
// Note: `iter_mut()` is intentionally not provided. Providing `&mut T` would
// expose the internal `Link` field, allowing callers to corrupt the list's
// structure or violate Rust's sharing rules (since intrusive elements are
// often shared via raw pointers).
#[must_use]
pub fn remove_element(&mut self, key: RandomAccessKey<T, A>) -> ForeignBox<T> {
// SAFETY: Caller ensures the element is a valid pointer to an instance of T.
unsafe { self.list.remove_element(key.ptr).unwrap_unchecked() }
}
}
#[cfg(test)]
mod tests {
use core::ptr::NonNull;
use foreign_box::ForeignBox;
use unittest::test;
use super::*;
// `#[repr(C)]` is used to ensure that `link` is at a non-zero offset.
// Previously, without this, the compiler was putting link at the beginning of
// the struct causing `LINK_OFFSET` in the adapter to be zero which obfuscated
// some pointer math bugs.
#[repr(C)]
struct TestMember {
value: u32,
link: Link,
}
impl TestMember {
fn get_key(&self) -> u32 {
self.value
}
}
define_adapter!(TestAdapter => TestMember::link);
fn validate_list(
list: &ForeignList<TestMember, TestAdapter>,
expected_values: &[u32],
) -> unittest::Result<()> {
let mut index = 0;
list.for_each(|element| {
unittest::assert_eq!(element.value, expected_values[index]);
index += 1;
Ok(())
})?;
unittest::assert_eq!(index, expected_values.len());
Ok(())
}
fn drain_list(list: &mut ForeignList<TestMember, TestAdapter>) {
while let Some(element) = list.pop_head() {
element.consume();
}
}
#[test]
fn new_list_is_empty() -> unittest::Result<()> {
let list = ForeignList::<TestMember, TestAdapter>::new();
unittest::assert_true!(list.is_empty());
Ok(())
}
#[test]
fn iter_traverses_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_back(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
list.push_back(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
let mut index = 0;
let expected = [1, 2];
for element in list.iter() {
unittest::assert_eq!(element.value, expected[index]);
index += 1;
}
unittest::assert_eq!(index, 2);
drain_list(&mut list);
Ok(())
}
#[test]
fn push_front_adds_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
unittest::assert_false!(list.is_empty());
validate_list(&list, &[1, 2])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn push_back_adds_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_back(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
list.push_back(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
unittest::assert_false!(list.is_empty());
validate_list(&list, &[2, 1])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn pop_head_removes_correctly() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
let e = list.pop_head();
unittest::assert_true!(e.is_some());
let e = e.unwrap();
unittest::assert_eq!(e.value, 3);
unittest::assert_true!(e.link.is_unlinked());
e.consume();
let e = list.pop_head();
unittest::assert_true!(e.is_some());
let e = e.unwrap();
unittest::assert_eq!(e.value, 2);
unittest::assert_true!(e.link.is_unlinked());
e.consume();
let e = list.pop_head();
unittest::assert_true!(e.is_some());
let e = e.unwrap();
unittest::assert_eq!(e.value, 1);
unittest::assert_true!(e.link.is_unlinked());
e.consume();
validate_list(&list, &[])
}
#[test]
fn remove_element_can_remove_head() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
let removed_element =
unsafe { list.remove_element(NonNull::new(&raw mut element1).unwrap()) };
unittest::assert_true!(removed_element.is_some());
unittest::assert_eq!(
removed_element.unwrap().consume(),
NonNull::new(&raw mut element1).unwrap()
);
validate_list(&list, &[2, 3])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn remove_element_can_remove_middle() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
let removed_element =
unsafe { list.remove_element(NonNull::new(&raw mut element2).unwrap()) };
unittest::assert_true!(removed_element.is_some());
unittest::assert_eq!(
removed_element.unwrap().consume(),
NonNull::new(&raw mut element2).unwrap()
);
validate_list(&list, &[1, 3])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn remove_element_can_remove_tail() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
let removed_element =
unsafe { list.remove_element(NonNull::new(&raw mut element3).unwrap()) };
unittest::assert_true!(removed_element.is_some());
unittest::assert_eq!(
removed_element.unwrap().consume(),
NonNull::new(&raw mut element3).unwrap()
);
validate_list(&list, &[1, 2])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn sorted_insert_inserts_sorted_items_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element3) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element2) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element1) },
TestMember::get_key,
);
validate_list(&list, &[1, 2, 3])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn sorted_insert_inserts_reverse_sorted_items_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element1) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element2) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element3) },
TestMember::get_key,
);
validate_list(&list, &[1, 2, 3])?;
drain_list(&mut list);
Ok(())
}
#[test]
fn sorted_insert_inserts_unsorted_items_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element2_2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = ForeignList::<TestMember, TestAdapter>::new();
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element2) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element1) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element3) },
TestMember::get_key,
);
list.sorted_insert_by_key(
unsafe { ForeignBox::new_from_ptr(&raw mut element2_2) },
TestMember::get_key,
);
validate_list(&list, &[1, 2, 2, 3])?;
drain_list(&mut list);
Ok(())
}
fn validate_random_access_list(
list: &RandomAccessForeignList<TestMember, TestAdapter>,
expected_values: &[u32],
) -> unittest::Result<()> {
let mut index = 0;
list.for_each(|element| {
unittest::assert_eq!(element.value, expected_values[index]);
index += 1;
Ok(())
})?;
unittest::assert_eq!(index, expected_values.len());
Ok(())
}
#[test]
fn new_random_access_list_is_empty() -> unittest::Result<()> {
let list = RandomAccessForeignList::<TestMember, TestAdapter>::new();
unittest::assert_true!(list.is_empty());
Ok(())
}
#[test]
fn random_access_push_front_adds_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut list = RandomAccessForeignList::<TestMember, TestAdapter>::new();
let key2 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
let key1 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
unittest::assert_false!(list.is_empty());
validate_random_access_list(&list, &[1, 2])?;
for key in [key1, key2] {
let _ = list.remove_element(key).consume();
}
Ok(())
}
#[test]
fn random_access_push_back_adds_in_correct_order() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut list = RandomAccessForeignList::<TestMember, TestAdapter>::new();
let key2 = list.push_back(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
let key1 = list.push_back(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
unittest::assert_false!(list.is_empty());
validate_random_access_list(&list, &[2, 1])?;
for key in [key1, key2] {
let _ = list.remove_element(key).consume();
}
Ok(())
}
#[test]
fn random_access_remove_element_can_remove_head() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = RandomAccessForeignList::<TestMember, TestAdapter>::new();
let key3 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
let key2 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
let key1 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
let removed_element = list.remove_element(key1);
unittest::assert_eq!(
removed_element.consume(),
NonNull::new(&raw mut element1).unwrap()
);
validate_random_access_list(&list, &[2, 3])?;
for key in [key2, key3] {
let _ = list.remove_element(key).consume();
}
Ok(())
}
#[test]
fn random_access_remove_element_can_remove_middle() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = RandomAccessForeignList::<TestMember, TestAdapter>::new();
let key3 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
let key2 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
let key1 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
let removed_element = list.remove_element(key2);
unittest::assert_eq!(
removed_element.consume(),
NonNull::new(&raw mut element2).unwrap()
);
validate_random_access_list(&list, &[1, 3])?;
for key in [key1, key3] {
let _ = list.remove_element(key).consume();
}
Ok(())
}
#[test]
fn random_access_remove_element_can_remove_tail() -> unittest::Result<()> {
let mut element1 = TestMember {
value: 1,
link: Link::new(),
};
let mut element2 = TestMember {
value: 2,
link: Link::new(),
};
let mut element3 = TestMember {
value: 3,
link: Link::new(),
};
let mut list = RandomAccessForeignList::<TestMember, TestAdapter>::new();
let key3 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element3) });
let key2 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element2) });
let key1 = list.push_front(unsafe { ForeignBox::new_from_ptr(&raw mut element1) });
let removed_element = list.remove_element(key3);
unittest::assert_eq!(
removed_element.consume(),
NonNull::new(&raw mut element3).unwrap()
);
validate_random_access_list(&list, &[1, 2])?;
for key in [key1, key2] {
let _ = list.remove_element(key).consume();
}
Ok(())
}
}