use core::ptr::NonNull;
use std::boxed::Box;
pub(crate) struct Node<T> {
pub(crate) next: NonNull<Node<T>>,
pub(crate) prev: NonNull<Node<T>>,
pub(crate) data: T,
}
impl<T> Node<T> {
pub(crate) fn new(data: T) -> NonNull<Self> {
let boxed = Box::new(Self {
data,
next: NonNull::dangling(),
prev: NonNull::dangling(),
});
let ptr =
NonNull::new(Box::into_raw(boxed)).expect("Box::into_raw should never return null");
unsafe {
(*ptr.as_ptr()).next = ptr;
(*ptr.as_ptr()).prev = ptr;
}
ptr
}
#[allow(dead_code)]
pub(crate) fn with_pointers(
data: T,
next: NonNull<Self>,
prev: NonNull<Self>,
) -> NonNull<Self> {
let boxed = Box::new(Self { data, next, prev });
NonNull::new(Box::into_raw(boxed)).expect("Box::into_raw should never return null")
}
pub(crate) unsafe fn data_mut<'a>(ptr: NonNull<Self>) -> &'a mut T {
unsafe { &mut (*ptr.as_ptr()).data }
}
pub(crate) unsafe fn data_ref<'a>(ptr: NonNull<Self>) -> &'a T {
unsafe { &(*ptr.as_ptr()).data }
}
pub(crate) unsafe fn dealloc(ptr: NonNull<Self>) -> T {
unsafe {
let boxed = Box::from_raw(ptr.as_ptr());
boxed.data
}
}
pub(crate) unsafe fn set_next(ptr: NonNull<Self>, next: NonNull<Self>) {
unsafe { (*ptr.as_ptr()).next = next }
}
pub(crate) unsafe fn set_prev(ptr: NonNull<Self>, prev: NonNull<Self>) {
unsafe { (*ptr.as_ptr()).prev = prev }
}
pub(crate) unsafe fn get_next(ptr: NonNull<Self>) -> NonNull<Self> {
unsafe { (*ptr.as_ptr()).next }
}
pub(crate) unsafe fn get_prev(ptr: NonNull<Self>) -> NonNull<Self> {
unsafe { (*ptr.as_ptr()).prev }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_creation_with_primitive() {
let ptr = Node::new(42);
unsafe {
assert_eq!(
*Node::data_ref(ptr),
42,
"Node data should match input value"
);
let recovered = Node::dealloc(ptr);
assert_eq!(
recovered, 42,
"Deallocated data should match original value"
);
}
}
#[test]
fn test_node_creation_with_string() {
let input = String::from("hello world");
let ptr = Node::new(input.clone());
unsafe {
assert_eq!(
*Node::data_ref(ptr),
"hello world",
"String data should be preserved"
);
let recovered = Node::dealloc(ptr);
assert_eq!(recovered, input, "Recovered string should match original");
}
}
#[test]
fn test_node_self_referential_pointers() {
let ptr = Node::new("test");
unsafe {
let next_ptr = Node::get_next(ptr);
let prev_ptr = Node::get_prev(ptr);
assert_eq!(
next_ptr.as_ptr(),
ptr.as_ptr(),
"Next pointer should point to self for standalone node"
);
assert_eq!(
prev_ptr.as_ptr(),
ptr.as_ptr(),
"Prev pointer should point to self for standalone node"
);
Node::dealloc(ptr);
}
}
#[test]
fn test_node_data_mutable_access() {
let ptr = Node::new(42);
unsafe {
let data_mut = Node::data_mut(ptr);
*data_mut = 100;
assert_eq!(
*Node::data_ref(ptr),
100,
"Modified value should be visible on subsequent read"
);
Node::dealloc(ptr);
}
}
#[test]
fn test_node_set_get_next_pointer() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::new(2);
Node::set_next(node1, node2);
let retrieved_next = Node::get_next(node1);
assert_eq!(
retrieved_next.as_ptr(),
node2.as_ptr(),
"Next pointer should match the set value"
);
let node2_next = Node::get_next(node2);
assert_eq!(
node2_next.as_ptr(),
node2.as_ptr(),
"Node2's next should still be self-referential"
);
Node::dealloc(node1);
Node::dealloc(node2);
}
}
#[test]
fn test_node_set_get_prev_pointer() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::new(2);
Node::set_prev(node2, node1);
let retrieved_prev = Node::get_prev(node2);
assert_eq!(
retrieved_prev.as_ptr(),
node1.as_ptr(),
"Prev pointer should match the set value"
);
let node1_prev = Node::get_prev(node1);
assert_eq!(
node1_prev.as_ptr(),
node1.as_ptr(),
"Node1's prev should still be self-referential"
);
Node::dealloc(node1);
Node::dealloc(node2);
}
}
#[test]
fn test_node_creation_with_explicit_pointers() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::new(2);
let node3 = Node::with_pointers(3, node2, node1);
assert_eq!(*Node::data_ref(node3), 3, "Data should match input");
let next = Node::get_next(node3);
assert_eq!(
next.as_ptr(),
node2.as_ptr(),
"Next pointer should match provided value"
);
let prev = Node::get_prev(node3);
assert_eq!(
prev.as_ptr(),
node1.as_ptr(),
"Prev pointer should match provided value"
);
Node::dealloc(node1);
Node::dealloc(node2);
Node::dealloc(node3);
}
}
#[test]
fn test_node_dealloc_returns_data() {
let original_value = 42;
let ptr = Node::new(original_value);
unsafe {
let recovered = Node::dealloc(ptr);
assert_eq!(
recovered, original_value,
"Dealloc should return the original data"
);
}
}
#[test]
fn test_node_with_vec_type() {
let input = vec![1, 2, 3, 4, 5];
let ptr = Node::new(input.clone());
unsafe {
assert_eq!(*Node::data_ref(ptr), input, "Vec data should be preserved");
let recovered = Node::dealloc(ptr);
assert_eq!(recovered, input, "Recovered Vec should match original");
}
}
#[test]
fn test_node_with_option_type() {
let ptr_some = Node::new(Some(42));
unsafe {
assert_eq!(*Node::data_ref(ptr_some), Some(42));
Node::dealloc(ptr_some);
}
let ptr_none: NonNull<Node<Option<i32>>> = Node::new(None);
unsafe {
assert_eq!(*Node::data_ref(ptr_none), None);
Node::dealloc(ptr_none);
}
}
#[test]
fn test_node_with_custom_struct() {
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
let input = Point { x: 10, y: 20 };
let ptr = Node::new(input.clone());
unsafe {
assert_eq!(
*Node::data_ref(ptr),
input,
"Custom struct should be preserved"
);
let recovered = Node::dealloc(ptr);
assert_eq!(recovered, input, "Recovered struct should match original");
}
}
#[test]
fn test_node_multiple_pointer_updates() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::new(2);
let node3 = Node::new(3);
assert_eq!(Node::get_next(node1).as_ptr(), node1.as_ptr());
Node::set_next(node1, node2);
assert_eq!(Node::get_next(node1).as_ptr(), node2.as_ptr());
Node::set_next(node1, node3);
assert_eq!(
Node::get_next(node1).as_ptr(),
node3.as_ptr(),
"Second update should overwrite first"
);
Node::dealloc(node1);
Node::dealloc(node2);
Node::dealloc(node3);
}
}
#[test]
fn test_node_with_bool_type() {
let ptr_true = Node::new(true);
let ptr_false = Node::new(false);
unsafe {
assert_eq!(*Node::data_ref(ptr_true), true);
assert_eq!(*Node::data_ref(ptr_false), false);
Node::dealloc(ptr_true);
Node::dealloc(ptr_false);
}
}
#[test]
fn test_node_with_float_type() {
let ptr = Node::new(3.14159f64);
unsafe {
let value = *Node::data_ref(ptr);
assert!(
(value - 3.14159).abs() < f64::EPSILON,
"Float value should be preserved"
);
Node::dealloc(ptr);
}
}
#[test]
fn test_node_with_tuple_type() {
let input = (42, "hello", true);
let ptr = Node::new(input);
unsafe {
let (num, text, flag) = *Node::data_ref(ptr);
assert_eq!(num, 42);
assert_eq!(text, "hello");
assert_eq!(flag, true);
Node::dealloc(ptr);
}
}
#[test]
fn test_node_pointer_alignment() {
let ptr = Node::new(42);
assert!(
ptr.as_ptr() as usize % core::mem::align_of::<Node<i32>>() == 0,
"Node pointer should be properly aligned"
);
unsafe {
Node::dealloc(ptr);
}
}
#[test]
fn test_node_creation_loop() {
const COUNT: usize = 100;
for i in 0..COUNT {
let ptr = Node::new(i);
unsafe {
assert_eq!(*Node::data_ref(ptr), i);
Node::dealloc(ptr);
}
}
}
#[test]
fn test_with_pointers_circular_chain() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::new(2);
let node3 = Node::new(3);
Node::set_next(node1, node2);
Node::set_prev(node1, node3);
Node::set_next(node2, node3);
Node::set_prev(node2, node1);
Node::set_next(node3, node1);
Node::set_prev(node3, node2);
assert_eq!(Node::get_next(node1).as_ptr(), node2.as_ptr());
assert_eq!(Node::get_prev(node1).as_ptr(), node3.as_ptr());
assert_eq!(Node::get_next(node2).as_ptr(), node3.as_ptr());
assert_eq!(Node::get_prev(node2).as_ptr(), node1.as_ptr());
assert_eq!(Node::get_next(node3).as_ptr(), node1.as_ptr());
assert_eq!(Node::get_prev(node3).as_ptr(), node2.as_ptr());
Node::dealloc(node1);
Node::dealloc(node2);
Node::dealloc(node3);
}
}
#[test]
fn test_with_pointers_same_pointers() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::with_pointers(2, node1, node1);
assert_eq!(Node::get_next(node2).as_ptr(), node1.as_ptr());
assert_eq!(Node::get_prev(node2).as_ptr(), node1.as_ptr());
Node::dealloc(node1);
Node::dealloc(node2);
}
}
#[test]
fn test_with_pointers_data_integrity() {
unsafe {
let node1 = Node::new("1".to_string());
let node2 = Node::new("1".to_string());
let node_str = Node::with_pointers(String::from("test"), node1, node2);
assert_eq!(*Node::data_ref(node_str), "test");
let _ = Node::dealloc(node_str);
Node::dealloc(node1);
Node::dealloc(node2);
}
}
#[test]
fn test_with_pointers_complex_type() {
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
unsafe {
let node1 = Node::new(Point { x: 0, y: 0 });
let node2 = Node::new(Point { x: 0, y: 0 });
let point_data = Point { x: 10, y: 20 };
let node3 = Node::with_pointers(point_data.clone(), node1, node2);
assert_eq!(*Node::data_ref(node3), point_data);
let recovered = Node::dealloc(node3);
assert_eq!(recovered, point_data);
Node::dealloc(node1);
Node::dealloc(node2);
}
}
#[test]
fn test_with_pointers_update_after_creation() {
unsafe {
let node1 = Node::new(1);
let node2 = Node::new(2);
let node3 = Node::new(3);
let node4 = Node::new(4);
let new_node = Node::with_pointers(100, node1, node2);
assert_eq!(Node::get_next(new_node).as_ptr(), node1.as_ptr());
assert_eq!(Node::get_prev(new_node).as_ptr(), node2.as_ptr());
Node::set_next(new_node, node3);
Node::set_prev(new_node, node4);
assert_eq!(Node::get_next(new_node).as_ptr(), node3.as_ptr());
assert_eq!(Node::get_prev(new_node).as_ptr(), node4.as_ptr());
Node::dealloc(node1);
Node::dealloc(node2);
Node::dealloc(node3);
Node::dealloc(node4);
Node::dealloc(new_node);
}
}
}