use super::*;
use core::fmt::Debug;
struct VTable {
print: fn(NonNull<()>),
drop: fn(NonNull<()>),
}
fn print<T: Debug>(this: NonNull<()>) {
let this: NonNull<OwnedEntryNode<T>> = this.cast();
let this: &OwnedEntryNode<T> = unsafe { this.as_ref() };
println!("PRINT: {:?}", this.item);
}
fn droppa<T>(this: NonNull<()>) {
let this: NonNull<OwnedEntryNode<T>> = this.cast();
unsafe {
drop(Box::from_raw(this.as_ptr()));
}
}
impl VTable {
pub const fn vtable_for<T: Debug>() -> Self {
Self {
print: print::<T>,
drop: droppa::<T>,
}
}
}
#[pin_project::pin_project]
struct OwnedEntryHeader {
#[pin]
links: Links<OwnedEntryHeader>,
vtable: &'static VTable,
}
#[repr(C)]
struct OwnedEntryNode<T> {
hdr: OwnedEntryHeader,
item: T,
}
struct NodeRef(NonNull<OwnedEntryHeader>);
impl<T> From<Pin<Box<OwnedEntryNode<T>>>> for NodeRef {
fn from(value: Pin<Box<OwnedEntryNode<T>>>) -> Self {
let ptr: NonNull<OwnedEntryNode<T>> =
unsafe { NonNull::from(Box::leak(Pin::into_inner_unchecked(value))) };
let ptr: NonNull<OwnedEntryHeader> = ptr.cast();
NodeRef(ptr)
}
}
impl Drop for NodeRef {
fn drop(&mut self) {
let ptr: NonNull<OwnedEntryHeader> = self.0;
let vtable: &'static VTable = unsafe { ptr.as_ref().vtable };
let ptr: NonNull<()> = ptr.cast();
(vtable.drop)(ptr);
}
}
unsafe impl Linked<Links<Self>> for OwnedEntryHeader {
type Handle = NodeRef;
fn into_ptr(handle: NodeRef) -> NonNull<Self> {
let ptr = handle.0;
core::mem::forget(handle);
ptr
}
unsafe fn from_ptr(ptr: NonNull<Self>) -> Self::Handle {
NodeRef(ptr)
}
unsafe fn links(target: NonNull<Self>) -> NonNull<Links<Self>> {
let links = ptr::addr_of_mut!((*target.as_ptr()).links);
NonNull::new_unchecked(links)
}
}
fn owned_entry<T: Debug>(val: T) -> Pin<Box<OwnedEntryNode<T>>> {
Box::pin(OwnedEntryNode {
hdr: OwnedEntryHeader {
links: Links::new(),
vtable: const { &VTable::vtable_for::<T>() },
},
item: val,
})
}
#[test]
fn smoke() {
let mut list = List::<OwnedEntryHeader>::new();
let a = owned_entry(42i32);
let b = owned_entry("bar");
let c = owned_entry(Some(123u64));
list.push_front(a.into());
list.push_front(b.into());
list.push_front(c.into());
for node in list.iter_raw() {
let vtable: &'static VTable = unsafe { node.as_ref().vtable };
let node: NonNull<()> = node.cast();
(vtable.print)(node);
}
drop(list);
}