use crate::local_alloc::ThreadAllocator;
use core::marker::PhantomData;
use core::ptr::NonNull;
use mnemosyne_arena::HasSegmentPool;
use mnemosyne_core::types::Page;
use super::access::refresh_page_pointer;
type PageListBrand<'id, B> = fn(&'id mut ThreadAllocator<B>) -> &'id mut ThreadAllocator<B>;
pub(crate) struct PageListToken<'id, B: HasSegmentPool> {
_brand: PhantomData<PageListBrand<'id, B>>,
}
impl<'id, B: HasSegmentPool> PageListToken<'id, B> {
#[inline(always)]
fn new() -> Self {
Self {
_brand: PhantomData,
}
}
#[inline(always)]
pub(crate) unsafe fn page(&mut self, page_ptr: NonNull<Page>) -> BrandedPage<'id> {
BrandedPage {
ptr: refresh_page_pointer(page_ptr),
_brand: PhantomData,
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct BrandedPage<'id> {
ptr: NonNull<Page>,
_brand: PhantomData<fn(&'id mut Page) -> &'id mut Page>,
}
impl BrandedPage<'_> {
#[inline(always)]
fn ptr(self) -> NonNull<Page> {
self.ptr
}
}
#[inline(always)]
pub(crate) fn with_page_list_token<B: HasSegmentPool, R>(
f: impl for<'id> FnOnce(PageListToken<'id, B>) -> R,
) -> R {
f(PageListToken::new())
}
#[inline(always)]
pub(crate) unsafe fn push_page_front<'id, B: HasSegmentPool>(
token: &mut PageListToken<'id, B>,
head_slot: &mut Option<NonNull<Page>>,
page_ptr: BrandedPage<'id>,
list_state: u8,
) {
let raw_page = page_ptr.ptr();
unsafe {
(*raw_page.as_ptr()).next_page = *head_slot;
(*raw_page.as_ptr()).prev_page = None;
}
if let Some(head) = *head_slot {
unsafe {
let head = token.page(head).ptr();
(*head.as_ptr()).prev_page = Some(raw_page);
}
}
*head_slot = Some(raw_page);
unsafe { (*raw_page.as_ptr()).list_state = list_state };
let page_index = unsafe { (*raw_page.as_ptr()).page_index };
if page_index > 0 {
let (segment, _) =
unsafe { mnemosyne_core::types::locate_segment(raw_page.as_ptr().cast()) };
unsafe {
(*segment).page_linked_mask |= 1 << page_index;
}
}
}
#[inline(always)]
pub(crate) unsafe fn unlink_page_from_list<'id, B: HasSegmentPool>(
token: &mut PageListToken<'id, B>,
head_slot: &mut Option<NonNull<Page>>,
page_ptr: BrandedPage<'id>,
) {
let raw_page = page_ptr.ptr();
let next = unsafe { (*raw_page.as_ptr()).next_page };
let prev = unsafe { (*raw_page.as_ptr()).prev_page };
if let Some(prev_ptr) = prev {
unsafe {
let prev_ptr = token.page(prev_ptr).ptr();
(*prev_ptr.as_ptr()).next_page = next;
}
} else {
*head_slot = next;
}
if let Some(next_ptr) = next {
unsafe {
let next_ptr = token.page(next_ptr).ptr();
(*next_ptr.as_ptr()).prev_page = prev;
}
}
unsafe {
(*raw_page.as_ptr()).next_page = None;
(*raw_page.as_ptr()).prev_page = None;
(*raw_page.as_ptr()).list_state = 0;
}
let page_index = unsafe { (*raw_page.as_ptr()).page_index };
if page_index > 0 {
let (segment, _) =
unsafe { mnemosyne_core::types::locate_segment(raw_page.as_ptr().cast()) };
unsafe {
(*segment).page_linked_mask &= !(1 << page_index);
}
}
}
#[inline(always)]
pub(crate) unsafe fn move_page_between_lists_branded<'id, B: HasSegmentPool>(
token: &mut PageListToken<'id, B>,
from_head_slot: &mut Option<NonNull<Page>>,
to_head_slot: &mut Option<NonNull<Page>>,
page_ptr: BrandedPage<'id>,
new_state: u8,
) {
let raw_page = page_ptr.ptr();
let next = unsafe { (*raw_page.as_ptr()).next_page };
let prev = unsafe { (*raw_page.as_ptr()).prev_page };
if let Some(prev_ptr) = prev {
unsafe {
let prev_ptr = token.page(prev_ptr).ptr();
(*prev_ptr.as_ptr()).next_page = next;
}
} else {
*from_head_slot = next;
}
if let Some(next_ptr) = next {
unsafe {
let next_ptr = token.page(next_ptr).ptr();
(*next_ptr.as_ptr()).prev_page = prev;
}
}
let head = *to_head_slot;
unsafe {
(*raw_page.as_ptr()).next_page = head;
(*raw_page.as_ptr()).prev_page = None;
}
if let Some(head_ptr) = head {
unsafe {
let head_ptr = token.page(head_ptr).ptr();
(*head_ptr.as_ptr()).prev_page = Some(raw_page);
}
}
*to_head_slot = Some(raw_page);
unsafe { (*raw_page.as_ptr()).list_state = new_state };
}