use core::mem::size_of;
pub(crate) const MAX_SEGMENTS: usize = 1024;
pub(crate) const REGISTRY_FOOTPRINT: usize = MAX_SEGMENTS * size_of::<*mut u8>();
pub(crate) const HASH_CAPACITY: usize = 2 * MAX_SEGMENTS;
pub(crate) const HASH_FOOTPRINT: usize = HASH_CAPACITY * size_of::<*mut u8>();
pub(crate) const SEGMENT_SHIFT: usize = 22;
const TOMBSTONE: *mut u8 = core::ptr::without_provenance_mut::<u8>(1);
pub(crate) struct SegmentTable {
slots: *mut *mut u8,
count: u32,
hash_slots: *mut *mut u8,
}
impl SegmentTable {
pub(crate) fn from_primordial(
slots: *mut *mut u8,
count: u32,
hash_slots: *mut *mut u8,
) -> Self {
Self {
slots,
count,
hash_slots,
}
}
pub(crate) fn register(&mut self, base: *mut u8) -> Option<u32> {
let count = self.count as usize;
for i in 0..count {
let slot = Self::slot_ptr(self.slots, i);
let current = super::node::Node::read_struct::<*mut u8>(slot);
if current.is_null() {
super::node::Node::write_struct::<*mut u8>(slot, base);
self.hash_insert(base);
return Some(i as u32);
}
}
let idx = count;
if idx >= MAX_SEGMENTS {
return None;
}
let slot = Self::slot_ptr(self.slots, idx);
super::node::Node::write_struct::<*mut u8>(slot, base);
self.count += 1;
self.hash_insert(base);
Some(idx as u32)
}
#[cfg(feature = "alloc-decommit")]
pub(crate) fn unregister(&mut self, base: *mut u8) {
let count = self.count as usize;
for i in 0..count {
let slot = Self::slot_ptr(self.slots, i);
let current = super::node::Node::read_struct::<*mut u8>(slot);
if current == base {
super::node::Node::write_struct::<*mut u8>(slot, core::ptr::null_mut());
self.hash_remove(base);
return;
}
}
}
#[cfg(feature = "alloc-decommit")]
pub(crate) fn recycle(&mut self, base: *mut u8) {
let hdr = super::segment_header::SegmentHeader::read_at(base);
let reservation = hdr.reservation;
let reservation_len = hdr.reservation_len;
let count = self.count as usize;
for i in 0..count {
let slot = Self::slot_ptr(self.slots, i);
let current = super::node::Node::read_struct::<*mut u8>(slot);
if current == base {
self.hash_remove(base);
super::os::release_segment(reservation, reservation_len);
super::node::Node::write_struct::<*mut u8>(slot, core::ptr::null_mut());
return;
}
}
super::os::release_segment(reservation, reservation_len);
}
#[allow(dead_code)] pub(crate) fn count(&self) -> u32 {
self.count
}
#[inline(always)]
pub(crate) fn contains_base(&self, base: *mut u8) -> bool {
self.hash_contains(base)
}
pub(crate) fn bases(&self) -> impl Iterator<Item = *mut u8> {
let slots = self.slots;
let n = self.count as usize;
(0..n)
.map(move |i| {
let slot = Self::slot_ptr(slots, i) as *const *mut u8;
super::node::Node::read_struct::<*mut u8>(slot)
})
.filter(|&p| !p.is_null())
}
#[inline]
fn slot_ptr(slots: *mut *mut u8, i: usize) -> *mut *mut u8 {
super::node::Node::offset(slots as *mut u8, i * core::mem::size_of::<*mut u8>())
as *mut *mut u8
}
#[inline(always)]
fn hash_index(base: *mut u8) -> usize {
(base as usize >> SEGMENT_SHIFT) & (HASH_CAPACITY - 1)
}
#[inline(always)]
fn hash_slot_ptr(&self, i: usize) -> *mut *mut u8 {
super::node::Node::offset(
self.hash_slots as *mut u8,
i * core::mem::size_of::<*mut u8>(),
) as *mut *mut u8
}
#[inline(always)]
fn hash_slot_read(&self, i: usize) -> *mut u8 {
super::node::Node::read_struct::<*mut u8>(self.hash_slot_ptr(i))
}
#[inline]
fn hash_slot_write(&mut self, i: usize, value: *mut u8) {
super::node::Node::write_struct::<*mut u8>(self.hash_slot_ptr(i), value);
}
fn hash_insert(&mut self, base: *mut u8) {
let start = Self::hash_index(base);
let mut i = start;
loop {
let entry = self.hash_slot_read(i);
if entry.is_null() || entry == TOMBSTONE {
self.hash_slot_write(i, base);
return;
}
i = (i + 1) & (HASH_CAPACITY - 1);
debug_assert!(i != start, "hash table full — load factor exceeded");
}
}
#[cfg_attr(not(feature = "alloc-decommit"), allow(dead_code))]
fn hash_remove(&mut self, base: *mut u8) {
let start = Self::hash_index(base);
let mut i = start;
loop {
let entry = self.hash_slot_read(i);
if entry.is_null() {
return;
}
if entry == base {
self.hash_slot_write(i, TOMBSTONE);
return;
}
i = (i + 1) & (HASH_CAPACITY - 1);
debug_assert!(i != start, "hash_remove looped without finding base");
}
}
#[inline(always)]
fn hash_contains(&self, base: *mut u8) -> bool {
let start = Self::hash_index(base);
let mut i = start;
loop {
let entry = self.hash_slot_read(i);
if entry.is_null() {
return false;
}
if entry == base {
return true;
}
i = (i + 1) & (HASH_CAPACITY - 1);
if i == start {
return false;
}
}
}
}