mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
feat: utils: add linked_list
This commit is contained in:
parent
93df10c5a2
commit
c24264f469
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
200
lib/utils/src/linked_list.rs
Normal file
200
lib/utils/src/linked_list.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user