use std::mem::ManuallyDrop;
use std::ptr::NonNull;
pub(crate) struct FreelistNode<T> {
pub prev: *mut Entry<T>,
pub next: *mut Entry<T>,
}
#[repr(align(8))]
pub union Entry<T> {
pub(crate) data: ManuallyDrop<T>,
pub(crate) freelist_node: ManuallyDrop<FreelistNode<T>>,
}
unsafe impl<T: Send> Send for Entry<T> {}
unsafe impl<T: Sync> Sync for Entry<T> {}
impl<T> Entry<T> {
#[inline(always)]
pub fn write(&mut self, val: T) -> &mut T {
self.data = ManuallyDrop::new(val);
unsafe { &mut self.data }
}
pub(crate) unsafe fn remove_free_node(&mut self) -> Option<NonNull<Entry<T>>> {
let next = self.freelist_node.next;
if next == self {
None
} else {
let prev = self.freelist_node.prev;
Entry::set_next(prev, next);
Entry::set_prev(next, prev);
Some(NonNull::new_unchecked(
if (next as usize + prev as usize) / 2 < self as *const Self as usize {
prev
} else {
next
},
))
}
}
pub(crate) unsafe fn init_free_node(this: *mut Self) {
Entry::set_next(this, this);
Entry::set_prev(this, this);
}
pub(crate) unsafe fn insert_free_node(mut this: *mut Self, freed_node: *mut Self) {
if freed_node < this {
if freed_node < Entry::prev(this) {
this = Entry::prev(this);
}
Entry::set_next(freed_node, this);
Entry::set_prev(freed_node, Entry::prev(this));
Entry::set_next(Entry::prev(this), freed_node);
Entry::set_prev(this, freed_node);
} else {
if freed_node > Entry::next(this) {
this = Entry::next(this);
}
Entry::set_prev(freed_node, this);
Entry::set_next(freed_node, Entry::next(this));
Entry::set_prev(Entry::next(this), freed_node);
Entry::set_next(this, freed_node);
}
}
#[inline(always)]
unsafe fn next(this: *mut Self) -> *mut Self {
(*(*this).freelist_node).next
}
#[inline(always)]
unsafe fn prev(this: *mut Self) -> *mut Self {
(*(*this).freelist_node).prev
}
#[inline(always)]
unsafe fn set_next(this: *mut Self, that: *mut Self) {
(*(*this).freelist_node).next = that;
}
#[inline(always)]
unsafe fn set_prev(this: *mut Self, that: *mut Self) {
(*(*this).freelist_node).prev = that;
}
}
#[test]
fn entry_layout() {
let e = Entry {
data: ManuallyDrop::new(String::from("Hello")),
};
assert_eq!(
(&e) as *const Entry<String> as usize,
(&e) as *const Entry<String> as usize
);
}