use core::ptr::NonNull;
use crate::class::{self, NCLASSES};
use crate::os;
use crate::outbound::Outbound;
use crate::partials::PartialRing;
use crate::segment::{
self, FIRST_DATA_SPAN, NO_CLASS, SEGMENT_BYTES, SPANS_PER_SEGMENT, Segment,
};
pub const PER_CLASS_CAP: u32 = 16_777_216;
pub const EMPTY_SPAN_HYSTERESIS: u16 = 4;
pub struct Heap {
id: usize,
pub(crate) segments: *mut Segment,
pub(crate) partial: [Option<(NonNull<Segment>, u8)>; NCLASSES],
pub(crate) spans_in_class: [u32; NCLASSES],
pub(crate) live_bytes: u64,
pub(crate) rounding_bytes: u64,
pub(crate) outbound: Outbound,
partials: [PartialRing; NCLASSES],
claims: [Option<Claim>; NCLASSES],
class_cap: u32,
}
impl Heap {
#[must_use]
pub const fn new(id: usize) -> Self {
Self::with_class_cap(id, PER_CLASS_CAP)
}
#[must_use]
pub const fn with_class_cap(id: usize, class_cap: u32) -> Self {
Self {
id,
segments: core::ptr::null_mut(),
partial: [None; NCLASSES],
spans_in_class: [0; NCLASSES],
live_bytes: 0,
rounding_bytes: 0,
outbound: Outbound::new(),
partials: [PartialRing::EMPTY; NCLASSES],
claims: [None; NCLASSES],
class_cap,
}
}
pub fn ensure_identity(&mut self) {
if self.id == 0 {
self.id = core::ptr::from_mut(self) as usize;
}
}
pub fn alloc(&mut self, size: usize, align: usize) -> Option<NonNull<u8>> {
match class::index_of(size, align) {
Some(c) => self.alloc_small(c, size),
None => self.alloc_large(size, align),
}
}
pub unsafe fn try_resize_in_place(
&mut self,
ptr: NonNull<u8>,
old_size: usize,
new_size: usize,
align: usize,
) -> bool {
let (Some(a), Some(b)) = (class::index_of(old_size, align), class::index_of(new_size, align))
else {
return false;
};
if a != b {
return false;
}
let seg = unsafe { segment::segment_of(ptr) };
if unsafe { seg.as_ref() }.owner != self.id {
return false;
}
let slot = class::size_of(a) as u64;
self.live_bytes = self.live_bytes - old_size as u64 + new_size as u64;
self.rounding_bytes = self.rounding_bytes - (slot - old_size as u64)
+ (slot - new_size as u64);
true
}
pub unsafe fn dealloc(&mut self, ptr: NonNull<u8>, size: usize, align: usize) {
match class::index_of(size, align) {
Some(c) => unsafe { self.dealloc_small(ptr, c, size) },
None => unsafe { self.dealloc_large(ptr, size) },
}
}
fn alloc_small(&mut self, c: usize, size: usize) -> Option<NonNull<u8>> {
let slot = self.pop_slot(c).or_else(|| self.slow_path(c))?;
self.live_bytes += size as u64;
self.rounding_bytes += (class::size_of(c) - size) as u64;
Some(slot)
}
fn slow_path(&mut self, c: usize) -> Option<NonNull<u8>> {
while let Some((seg, ix)) = self.partials[c].pop() {
let m = unsafe { &(*seg).spans[ix] };
if m.class as usize == c && u32::from(m.live) < m.capacity() {
self.partial[c] = Some((unsafe { NonNull::new_unchecked(seg) }, ix as u8));
if let Some(p) = self.pop_slot(c) {
return Some(p);
}
}
}
self.drain_foreign();
if self.adopt_partial(c)
&& let Some(p) = self.pop_slot(c)
{
return Some(p);
}
self.claim_span(c)?;
self.pop_slot(c)
}
fn adopt_partial(&mut self, c: usize) -> bool {
let mut seg = self.segments;
while !seg.is_null() {
let s = unsafe { &*seg };
for ix in FIRST_DATA_SPAN..SPANS_PER_SEGMENT {
let m = &s.spans[ix];
if m.class as usize == c && u32::from(m.live) < m.capacity() {
self.partial[c] = Some((unsafe { NonNull::new_unchecked(seg) }, ix as u8));
return true;
}
}
seg = s.next;
}
false
}
fn pop_slot(&mut self, c: usize) -> Option<NonNull<u8>> {
if let Some(p) = self.pop_claimed(c) {
return Some(p);
}
self.refill_claim(c)?;
self.pop_claimed(c)
}
fn claim_span(&mut self, c: usize) -> Option<()> {
if self.spans_in_class[c] >= self.class_cap {
return None;
}
let (seg, ix) = self.find_free_span().or_else(|| {
self.map_segment()?;
self.find_free_span()
})?;
let meta = unsafe { &mut (*seg.as_ptr()).spans[ix] };
meta.reset(c as u8);
self.spans_in_class[c] += 1;
self.partial[c] = Some((seg, ix as u8));
Some(())
}
fn find_free_span(&self) -> Option<(NonNull<Segment>, usize)> {
let mut seg = self.segments;
while !seg.is_null() {
let s = unsafe { &*seg };
for ix in FIRST_DATA_SPAN..SPANS_PER_SEGMENT {
if s.spans[ix].class == NO_CLASS {
return Some((unsafe { NonNull::new_unchecked(seg) }, ix));
}
}
seg = s.next;
}
None
}
fn map_segment(&mut self) -> Option<()> {
let base = os::map_aligned(SEGMENT_BYTES, SEGMENT_BYTES)?;
let seg = unsafe { Segment::init(base, self.id) };
unsafe { (*seg.as_ptr()).next = self.segments };
self.segments = seg.as_ptr();
Some(())
}
fn alloc_large(&mut self, size: usize, align: usize) -> Option<NonNull<u8>> {
crate::large::alloc(size, align)
}
unsafe fn dealloc_large(&mut self, ptr: NonNull<u8>, size: usize) {
unsafe { crate::large::dealloc(ptr, size) };
}
}
impl Drop for Heap {
fn drop(&mut self) {
self.flush_claims();
crate::large::pool_drain();
let mut seg = self.segments;
while !seg.is_null() {
let next = unsafe { (*seg).next };
unsafe {
os::unmap(NonNull::new_unchecked(seg.cast::<u8>()), SEGMENT_BYTES);
}
seg = next;
}
}
}
#[path = "heap_claims.rs"]
mod heap_claims;
#[path = "heap_free.rs"]
mod heap_free;
pub(crate) use heap_claims::Claim;