feat: utils: add linked_list

This commit is contained in:
Paul Pan 2024-05-04 23:28:20 +08:00
parent 93df10c5a2
commit c24264f469
3 changed files with 220 additions and 1 deletions

View File

@ -3,6 +3,20 @@
#[macro_export]
macro_rules! container_of {
($ptr:expr, $type:ty, $field:ident) => {
($ptr as *const u8).sub(core::mem::offset_of!($type, $field) as usize) as *const $type
($ptr as *const _ as *const u8).sub(core::mem::offset_of!($type, $field) as usize) as *const $type
};
}
#[macro_export]
macro_rules! container_of_offset {
($ptr:expr, $type:ty, $offset:expr) => {
($ptr as *const _ as *const u8).sub($offset) as *const $type
};
}
#[macro_export]
macro_rules! to_field_offset {
($ptr:expr, $type:ty, $offset:expr) => {
($ptr as *const _ as *const u8).add($offset) as *const $type
};
}

View File

@ -7,5 +7,10 @@ pub mod bin;
pub mod container_of;
pub mod extern_addr;
pub mod function_name;
pub mod linked_list;
pub mod size;
pub mod then;
#[cfg(test)]
#[macro_use]
extern crate std;

View File

@ -0,0 +1,200 @@
use crate::{container_of_offset, to_field_offset};
use core::{cell::Cell, ptr::NonNull};
#[macro_export]
macro_rules! LinkHelperImpl {
($type:ty : $field:ident) => {
impl LinkHelper for $type {
const LINK_OFFSET: usize = core::mem::offset_of!($type, $field) as usize;
}
};
}
pub trait LinkHelper: Clone {
const LINK_OFFSET: usize;
/// # Safety
/// LINK_OFFSET must be a valid field offset of Self
unsafe fn get_link(&self) -> &Link<Self> {
&*(to_field_offset!(self, Link<Self>, Self::LINK_OFFSET))
}
}
#[derive(Clone)]
pub struct Link<T: LinkHelper> {
pub prev: Cell<Option<NonNull<T>>>,
pub next: Cell<Option<NonNull<T>>>,
}
impl<T: LinkHelper> Default for Link<T> {
fn default() -> Self {
Self {
prev: Cell::new(None),
next: Cell::new(None),
}
}
}
impl<T: LinkHelper> Link<T> {
/// # Safety
/// LINK_OFFSET must be a valid field offset of T
pub unsafe fn object(&self) -> &T {
&*(container_of_offset!(self, T, T::LINK_OFFSET))
}
pub fn prev(&self) -> Option<NonNull<T>> {
self.prev.get()
}
pub fn next(&self) -> Option<NonNull<T>> {
self.next.get()
}
pub fn set_prev(&self, prev: Option<NonNull<T>>) {
self.prev.set(prev);
}
pub fn set_next(&self, next: Option<NonNull<T>>) {
self.next.set(next);
}
pub fn prepend(&self, new: &T) {
unsafe {
// setup new's link
new.get_link().set_prev(self.prev());
new.get_link().set_next(Some(NonNull::from(self.object())));
// setup prev's link
if let Some(prev) = self.prev() {
prev.as_ref().get_link().set_next(Some(NonNull::from(new)))
}
// setup self's link
self.set_prev(Some(NonNull::from(new)));
}
}
pub fn append(&self, new: &T) {
unsafe {
// setup new's link
new.get_link().set_prev(Some(NonNull::from(self.object())));
new.get_link().set_next(self.next());
// setup next's link
if let Some(next) = self.next() {
next.as_ref().get_link().set_prev(Some(NonNull::from(new)))
}
// setup self's link
self.set_next(Some(NonNull::from(new)));
}
}
pub fn detach(&self) {
unsafe {
// setup prev's link
if let Some(prev) = self.prev() {
prev.as_ref().get_link().set_next(self.next())
}
// setup next's link
if let Some(next) = self.next() {
next.as_ref().get_link().set_prev(self.prev())
}
// setup self's link
self.set_prev(None);
self.set_next(None);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone)]
struct Node {
link: Link<Node>,
value: i32,
}
LinkHelperImpl!(Node: link);
macro_rules! as_mut_ptr {
($ref:expr => $type:ty) => {
$ref as *const _ as *mut $type
};
($ref:expr) => {
$ref as *const _ as *mut _
};
}
#[test]
fn test_linked_list() {
let node1 = Node {
link: Link::default(),
value: 1,
};
let node2 = Node {
link: Link::default(),
value: 2,
};
let node3 = Node {
link: Link::default(),
value: 3,
};
node1.link.append(&node2);
node2.link.append(&node3);
unsafe {
// check object
assert_eq!(as_mut_ptr!(node1.link.object() => Node), as_mut_ptr!(&node1 => Node));
assert_eq!(node1.link.object().value, 1);
assert_eq!(as_mut_ptr!(node2.link.object() => Node), as_mut_ptr!(&node2 => Node));
assert_eq!(node2.link.object().value, 2);
assert_eq!(as_mut_ptr!(node3.link.object() => Node), as_mut_ptr!(&node3 => Node));
assert_eq!(node3.link.object().value, 3);
}
unsafe {
// check get_link
assert_eq!(as_mut_ptr!(node1.get_link() => Link<Node>), as_mut_ptr!(&node1.link));
assert_eq!(as_mut_ptr!(node2.get_link() => Link<Node>), as_mut_ptr!(&node2.link));
assert_eq!(as_mut_ptr!(node3.get_link() => Link<Node>), as_mut_ptr!(&node3.link));
}
{
// check next link
assert!(node1.link.next().is_some());
assert_eq!(node1.link.next().unwrap().as_ptr(), as_mut_ptr!(&node2));
assert!(node2.link.next().is_some());
assert_eq!(node2.link.next().unwrap().as_ptr(), as_mut_ptr!(&node3));
assert!(node3.link.next().is_none());
}
{
// check prev link
assert!(node1.link.prev().is_none());
assert!(node2.link.prev().is_some());
assert_eq!(node2.link.prev().unwrap().as_ptr(), as_mut_ptr!(&node1));
assert!(node3.link.prev().is_some());
assert_eq!(node3.link.prev().unwrap().as_ptr(), as_mut_ptr!(&node2));
}
{
// check detach
node2.link.detach();
assert!(node2.link.next().is_none());
assert!(node2.link.prev().is_none());
assert!(node1.link.next().is_some());
assert_eq!(node1.link.next().unwrap().as_ptr(), as_mut_ptr!(&node3));
assert!(node3.link.prev().is_some());
assert_eq!(node3.link.prev().unwrap().as_ptr(), as_mut_ptr!(&node1));
}
}
}