use std::marker::PhantomData;
#[repr(C)]
pub struct Dllink<T> {
pub next: *mut Dllink<T>,
pub prev: *mut Dllink<T>,
pub data: T,
_marker: PhantomData<*mut T>,
}
impl<T> Dllink<T> {
#[inline]
pub const fn new(data: T) -> Self {
Dllink {
next: std::ptr::null_mut(),
prev: std::ptr::null_mut(),
data,
_marker: PhantomData,
}
}
#[inline]
pub fn new_linked(data: T) -> Self {
Dllink {
next: std::ptr::null_mut(),
prev: std::ptr::null_mut(),
data,
_marker: PhantomData,
}
}
#[inline]
pub fn lock(&mut self) {
self.next = std::ptr::null_mut();
self.prev = std::ptr::null_mut();
}
#[inline]
pub fn is_locked(&self) -> bool {
self.next.is_null() && self.prev.is_null()
}
#[inline]
pub fn detach(&mut self) {
assert!(!self.is_locked(), "cannot detach a locked node");
let n = self.next;
let p = self.prev;
unsafe {
(*p).next = n;
(*n).prev = p;
}
self.lock();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_locked() {
let node = Dllink::new_linked(42);
assert!(node.is_locked());
assert_eq!(node.data, 42);
}
#[test]
fn test_lock_unlock() {
let mut n1 = Dllink::new_linked(1);
let mut n2 = Dllink::new_linked(2);
n1.next = &mut n2;
n2.prev = &mut n1;
assert!(!n1.is_locked());
assert!(!n2.is_locked());
n1.lock();
assert!(n1.is_locked());
}
#[test]
fn test_detach() {
let mut n1 = Dllink::new_linked(1);
let mut n2 = Dllink::new_linked(2);
let mut n3 = Dllink::new_linked(3);
let p1: *mut Dllink<i32> = &mut n1;
let p2: *mut Dllink<i32> = &mut n2;
let p3: *mut Dllink<i32> = &mut n3;
n1.next = p2;
n1.prev = p3;
n2.next = p3;
n2.prev = p1;
n3.next = p1;
n3.prev = p2;
n2.detach();
assert!(n2.is_locked());
assert_eq!(unsafe { (*n1.next).data }, 3);
assert_eq!(unsafe { (*n3.prev).data }, 1);
}
#[test]
#[should_panic(expected = "cannot detach a locked node")]
fn test_detach_locked_panics() {
let mut node = Dllink::new_linked(99);
node.detach();
}
}