use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr;
use core::ptr::NonNull;
use std::alloc::{Layout, alloc, dealloc, handle_alloc_error};
use crate::map::{EMPTY, GROUP_WIDTH, KevyMap, MIN_CAP, table_layout};
const THP_BACKED_THRESHOLD: usize = 1024 * 1024;
impl<K, V> KevyMap<K, V> {
pub(crate) fn alloc_table(cap: usize) -> Self {
debug_assert!(cap.is_power_of_two());
debug_assert!(cap >= MIN_CAP);
let (layout, meta_offset) = table_layout::<(K, V)>(cap);
let (base, mmap_backed) = if layout.size() >= THP_BACKED_THRESHOLD {
if let Some(p) = kevy_madvise::mmap_anon_aligned_2mb(layout.size()) {
(p.as_ptr(), true)
} else {
(fallback_alloc(layout), false)
}
} else {
(fallback_alloc(layout), false)
};
let meta_byte_ptr = unsafe { base.add(meta_offset) };
unsafe { ptr::write_bytes(meta_byte_ptr, EMPTY, cap + GROUP_WIDTH) };
let slots_ptr = base.cast::<MaybeUninit<(K, V)>>();
let metadata_ptr = meta_byte_ptr;
if !mmap_backed {
kevy_madvise::advise_hugepage(base.cast_const(), layout.size());
}
Self {
slots_ptr: unsafe { NonNull::new_unchecked(slots_ptr) },
metadata_ptr: unsafe { NonNull::new_unchecked(metadata_ptr) },
cap,
mask: cap - 1,
occupied: 0,
deleted: 0,
mmap_backed,
_marker: PhantomData,
}
}
}
fn fallback_alloc(layout: Layout) -> *mut u8 {
let p = unsafe { alloc(layout) };
if p.is_null() {
handle_alloc_error(layout);
}
p
}
impl<K, V> Drop for KevyMap<K, V> {
fn drop(&mut self) {
if self.cap == 0 {
return;
}
if std::mem::needs_drop::<(K, V)>() {
for i in 0..self.cap {
let meta = unsafe { *self.metadata_ptr.as_ptr().add(i) };
if meta & 0x80 == 0 {
unsafe {
ptr::drop_in_place(self.slots_ptr.as_ptr().add(i).cast::<(K, V)>());
}
}
}
}
let (layout, _) = table_layout::<(K, V)>(self.cap);
if self.mmap_backed {
unsafe {
kevy_madvise::munmap_2mb(self.slots_ptr.cast(), layout.size());
}
} else {
unsafe {
dealloc(self.slots_ptr.as_ptr().cast::<u8>(), layout);
}
}
}
}