From c24264f469548673592c1b73b94add1ce561a5a3 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Sat, 4 May 2024 23:28:20 +0800 Subject: [PATCH] feat: utils: add linked_list --- lib/utils/src/container_of.rs | 16 ++- lib/utils/src/lib.rs | 5 + lib/utils/src/linked_list.rs | 200 ++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 lib/utils/src/linked_list.rs diff --git a/lib/utils/src/container_of.rs b/lib/utils/src/container_of.rs index 0d8cd6a..32cb2c4 100644 --- a/lib/utils/src/container_of.rs +++ b/lib/utils/src/container_of.rs @@ -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 }; } diff --git a/lib/utils/src/lib.rs b/lib/utils/src/lib.rs index 3130303..8f3eb97 100644 --- a/lib/utils/src/lib.rs +++ b/lib/utils/src/lib.rs @@ -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; diff --git a/lib/utils/src/linked_list.rs b/lib/utils/src/linked_list.rs new file mode 100644 index 0000000..a91f750 --- /dev/null +++ b/lib/utils/src/linked_list.rs @@ -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 { + &*(to_field_offset!(self, Link, Self::LINK_OFFSET)) + } +} + +#[derive(Clone)] +pub struct Link { + pub prev: Cell>>, + pub next: Cell>>, +} + +impl Default for Link { + fn default() -> Self { + Self { + prev: Cell::new(None), + next: Cell::new(None), + } + } +} + +impl Link { + /// # 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> { + self.prev.get() + } + + pub fn next(&self) -> Option> { + self.next.get() + } + + pub fn set_prev(&self, prev: Option>) { + self.prev.set(prev); + } + + pub fn set_next(&self, next: Option>) { + 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, + 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), as_mut_ptr!(&node1.link)); + assert_eq!(as_mut_ptr!(node2.get_link() => Link), as_mut_ptr!(&node2.link)); + assert_eq!(as_mut_ptr!(node3.get_link() => Link), 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)); + } + } +}