use core::ptr;
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
pub mod pflags {
pub const HAS_ALIGNED: u8 = 1 << 0;
pub const SINGLE_BLOCK: u8 = 1 << 1;
pub const IN_FULL: u8 = 1 << 2;
pub const HUGE_SEGMENT: u8 = 1 << 3;
pub const SLOW_FREE: u8 = HAS_ALIGNED | SINGLE_BLOCK | IN_FULL | HUGE_SEGMENT;
}
pub const XMASK: usize = 0b11;
pub const XFLAG_NORMAL: usize = 0;
pub const XFLAG_DELAYED: usize = 1;
pub const XFLAG_FREEING: usize = 2;
pub const XFLAG_NEVER: usize = 3;
#[repr(C)]
pub struct Block {
pub next: *mut Block,
}
#[inline]
pub(crate) const fn odd_mod_inverse(n: usize) -> usize {
debug_assert!(n % 2 == 1);
let mut x = n;
let mut i = 0;
while i < 5 {
x = x.wrapping_mul(2usize.wrapping_sub(n.wrapping_mul(x)));
i += 1;
}
x
}
#[cfg(feature = "blockmap")]
#[inline]
pub(crate) const fn bitmap_bytes(blocks: usize) -> usize {
blocks
.div_ceil(8)
.next_multiple_of(crate::types::MAX_ALIGN_SIZE)
}
#[cold]
#[inline(never)]
#[cfg(feature = "blockmap")]
pub(crate) fn blockmap_abort() -> ! {
crate::abort()
}
#[cfg(feature = "blockmap")]
#[inline]
unsafe fn blockmap_transition(page: *mut Page, b: *const u8, to_live: bool) {
unsafe {
let payload = (*page).payload;
if payload.is_null() {
return;
}
let bsize = (*page).block_size;
let reserved = (*page).reserved as usize;
let delta = b.addr().wrapping_sub(payload.addr());
let idx = (delta >> bsize.trailing_zeros()).wrapping_mul((*page).bs_inv);
if idx >= reserved {
blockmap_abort();
}
let byte = payload.add(reserved * bsize).add(idx >> 3);
let mask = 1u8 << (idx & 7);
if (byte.read() & mask != 0) == to_live {
blockmap_abort();
}
byte.write(byte.read() ^ mask);
}
}
#[inline]
#[doc(hidden)]
pub fn link_is_plausible(dec: usize, b_addr: usize) -> bool {
let (dec, b_addr) = if crate::REGION_STRIDES {
let base = crate::prim::fixed::stride_base();
(dec.wrapping_sub(base), b_addr.wrapping_sub(base))
} else {
(dec, b_addr)
};
dec.is_multiple_of(crate::types::MAX_ALIGN_SIZE.min(8))
&& (dec ^ b_addr) < crate::types::SEGMENT_SIZE
}
#[inline]
pub unsafe fn block_next(page: *const Page, b: *const Block) -> *mut Block {
unsafe {
#[cfg(not(feature = "secure"))]
{
let _ = page;
let n = (*b).next;
#[cfg(feature = "linkcheck")]
{
if !n.is_null() && !link_is_plausible(n.addr(), b.addr()) {
corrupt_free_list_abort();
}
}
n
}
#[cfg(feature = "secure")]
{
let enc = (*b).next as usize;
if enc == 0 {
return core::ptr::null_mut();
}
let keys = (*page).keys;
let dec = (enc ^ keys[0]).wrapping_sub(keys[1]);
if !link_is_plausible(dec, b.addr()) {
corrupt_free_list_abort();
}
crate::ptr_with_addr(b.cast_mut(), dec)
}
}
}
#[inline]
pub unsafe fn block_set_next(page: *const Page, b: *mut Block, next: *mut Block) {
unsafe {
#[cfg(not(feature = "secure"))]
{
let _ = page;
(*b).next = next;
}
#[cfg(feature = "secure")]
{
if next.is_null() {
(*b).next = core::ptr::null_mut();
} else {
let keys = (*page).keys;
let enc = (next.addr().wrapping_add(keys[1])) ^ keys[0];
(*b).next = crate::ptr_with_addr(b, enc);
}
}
}
}
pub struct DelayedList {
pub head: AtomicUsize,
}
impl DelayedList {
pub const fn new() -> DelayedList {
DelayedList {
head: AtomicUsize::new(0),
}
}
}
impl Default for DelayedList {
fn default() -> Self {
Self::new()
}
}
pub struct Page {
pub free: *mut Block,
pub local_free: *mut Block,
pub xthread_free: AtomicUsize,
pub xheap: AtomicUsize,
pub next: *mut Page,
pub prev: *mut Page,
pub used: u32,
pub capacity: u32,
pub reserved: u32,
pub block_size: usize,
pub area: *mut u8,
pub slice_count: u16,
pub slice_offset: u16,
pub bin: u8,
pub flags: AtomicU8,
pub free_is_zero: bool,
pub purged: bool,
pub heap_tag: i32,
#[cfg(feature = "secure")]
pub keys: [usize; 2],
#[cfg(feature = "blockmap")]
pub payload: *mut u8,
#[cfg(feature = "blockmap")]
pub bs_inv: usize,
}
#[inline]
pub unsafe fn debug_validate_page(page: *const Page, where_: &str) {
#[cfg(feature = "debug_checks")]
{
unsafe {
assert!(!page.is_null(), "{where_}: null page");
assert_eq!((*page).slice_offset, 0, "{where_}: not a span start");
assert!((*page).block_size > 0, "{where_}: dead page (block_size 0)");
assert!(
(*page).block_size.is_multiple_of(8),
"{where_}: block_size {} not word-aligned",
(*page).block_size
);
assert!((*page).slice_count > 0, "{where_}: zero slice_count");
assert!(
(*page).capacity <= (*page).reserved,
"{where_}: capacity {} > reserved {}",
(*page).capacity,
(*page).reserved
);
assert!(
(*page).used <= (*page).capacity,
"{where_}: used {} > capacity {}",
(*page).used,
(*page).capacity
);
assert!(
((*page).bin as usize) <= crate::types::BIN_FULL,
"{where_}: bin {} out of range",
(*page).bin
);
}
}
#[cfg(not(feature = "debug_checks"))]
{
let _ = (page, where_);
}
}
impl Page {
pub const fn empty_sentinel() -> Page {
Page {
free: ptr::null_mut(),
local_free: ptr::null_mut(),
xthread_free: AtomicUsize::new(0),
xheap: AtomicUsize::new(0),
next: ptr::null_mut(),
prev: ptr::null_mut(),
used: 0,
capacity: 0,
reserved: 0,
block_size: 8,
area: ptr::null_mut(),
slice_count: 1,
slice_offset: 0,
bin: 0,
flags: AtomicU8::new(0),
free_is_zero: false,
purged: false,
heap_tag: 0,
#[cfg(feature = "secure")]
keys: [0; 2],
#[cfg(feature = "blockmap")]
payload: ptr::null_mut(),
#[cfg(feature = "blockmap")]
bs_inv: 1,
}
}
}
#[repr(transparent)]
pub struct EmptyPage(Page);
unsafe impl Sync for EmptyPage {}
#[cfg_attr(
all(
ra_single_threaded,
not(miri),
not(windows),
not(unix),
not(target_arch = "wasm32")
),
unsafe(link_section = ".rodata.rusty_alloc_empty_page")
)]
pub static EMPTY_PAGE: EmptyPage = EmptyPage(Page::empty_sentinel());
#[inline]
pub const fn empty_page_ptr() -> *mut Page {
(&raw const EMPTY_PAGE.0).cast_mut()
}
#[inline]
pub unsafe fn page_pop(page: *mut Page) -> *mut u8 {
unsafe { debug_validate_page(page, "page_pop") };
let block = unsafe { (*page).free };
if block.is_null() {
return ptr::null_mut();
}
unsafe {
#[cfg(feature = "blockmap")]
blockmap_transition(page, block.cast(), true);
(*page).free = block_next(page, block);
(*page).used += 1;
}
block.cast()
}
#[inline(always)]
pub unsafe fn page_link_local(page: *mut Page, block: *mut Block) {
unsafe {
#[cfg(feature = "blockmap")]
blockmap_transition(page, block.cast(), false);
block_set_next(page, block, (*page).local_free);
(*page).local_free = block;
}
}
pub const USED_OFFSET: usize = core::mem::offset_of!(Page, used);
#[inline]
#[must_use = "a negative return is a double free the caller must abort on"]
pub unsafe fn page_push_local(page: *mut Page, block: *mut Block) -> u32 {
unsafe { debug_validate_page(page, "page_push_local") };
unsafe {
page_link_local(page, block);
let u = (*page).used.wrapping_sub(1);
(*page).used = u;
u
}
}
#[cold]
#[inline(never)]
pub(crate) fn double_free_abort() -> ! {
crate::abort()
}
#[cold]
#[inline(never)]
#[cfg(any(feature = "secure", feature = "linkcheck"))]
pub(crate) fn corrupt_free_list_abort() -> ! {
crate::abort()
}
pub unsafe fn remote_free(page: *mut Page, block: *mut Block) {
if crate::ONE_THREAD {
unreachable!("a cross-thread free on a build that asserted a single thread");
}
loop {
let x = unsafe { (*page).xthread_free.load(Ordering::Acquire) };
match x & XMASK {
XFLAG_DELAYED => {
let claimed = unsafe {
(*page)
.xthread_free
.compare_exchange_weak(
x,
(x & !XMASK) | XFLAG_FREEING,
Ordering::AcqRel,
Ordering::Relaxed,
)
.is_ok()
};
if claimed {
unsafe {
let dl = (*page).xheap.load(Ordering::Acquire) as *const DelayedList;
debug_assert!(!dl.is_null(), "DELAYED page without an owner heap");
loop {
let head = (*dl).head.load(Ordering::Acquire);
(*block).next = crate::ptr_with_addr(block, head);
if (*dl)
.head
.compare_exchange_weak(
head,
block as usize,
Ordering::AcqRel,
Ordering::Relaxed,
)
.is_ok()
{
break;
}
}
loop {
let y = (*page).xthread_free.load(Ordering::Acquire);
if (*page)
.xthread_free
.compare_exchange_weak(
y,
(y & !XMASK) | XFLAG_DELAYED,
Ordering::AcqRel,
Ordering::Relaxed,
)
.is_ok()
{
break;
}
}
}
return;
}
}
XFLAG_FREEING => core::hint::spin_loop(),
flag => {
unsafe {
block_set_next(page, block, crate::ptr_with_addr(block, x & !XMASK));
if (*page)
.xthread_free
.compare_exchange_weak(
x,
(block as usize) | flag,
Ordering::Release,
Ordering::Relaxed,
)
.is_ok()
{
return;
}
}
}
}
}
}
pub unsafe fn page_set_flag(page: *mut Page, flag: usize) {
loop {
let x = unsafe { (*page).xthread_free.load(Ordering::Acquire) };
if x & XMASK == XFLAG_FREEING {
core::hint::spin_loop();
continue;
}
let ok = unsafe {
(*page)
.xthread_free
.compare_exchange_weak(x, (x & !XMASK) | flag, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
};
if ok {
return;
}
}
}
pub unsafe fn page_collect(page: *mut Page) -> bool {
unsafe { page_collect_impl::<false>(page, 0) }
}
pub unsafe fn page_collect_and_set_flag(page: *mut Page, flag: usize) {
let _stole = unsafe { page_collect_impl::<true>(page, flag) };
}
#[inline]
unsafe fn page_collect_impl<const SET_FLAG: bool>(page: *mut Page, flag: usize) -> bool {
unsafe {
if (*page).free.is_null() {
(*page).free = (*page).local_free;
(*page).local_free = ptr::null_mut();
if !(*page).free.is_null() {
(*page).free_is_zero = false;
}
}
if !SET_FLAG && ((*page).xthread_free.load(Ordering::Acquire) & !XMASK) == 0 {
return false;
}
loop {
let x = (*page).xthread_free.load(Ordering::Acquire);
if SET_FLAG && x & XMASK == XFLAG_FREEING {
core::hint::spin_loop();
continue;
}
let head = (x & !XMASK) as *mut Block;
if head.is_null() {
if SET_FLAG
&& (*page)
.xthread_free
.compare_exchange_weak(x, flag, Ordering::AcqRel, Ordering::Relaxed)
.is_err()
{
continue;
}
return false;
}
let want = if SET_FLAG { flag } else { x & XMASK };
if (*page)
.xthread_free
.compare_exchange_weak(x, want, Ordering::AcqRel, Ordering::Relaxed)
.is_err()
{
continue;
}
(*page).free_is_zero = false;
let mut tail = head;
let mut n = 1u32;
loop {
#[cfg(feature = "blockmap")]
blockmap_transition(page, tail.cast(), false);
let nxt = block_next(page, tail);
if nxt.is_null() {
break;
}
tail = nxt;
n += 1;
}
block_set_next(page, tail, (*page).free);
(*page).free = head;
if n > (*page).used {
double_free_abort();
}
(*page).used -= n;
break;
}
true
}
}
const EXTEND_SHIFT_BASE: u32 = {
assert!(
crate::types::SEGMENT_SLICE_SIZE >= 4096,
"the extend bound assumes a slice of at least one 4 KiB page"
);
crate::types::SEGMENT_SLICE_SIZE.trailing_zeros() - 12
};
pub unsafe fn page_extend(page: *mut Page, area: *mut u8) {
unsafe { debug_validate_page(page, "page_extend") };
unsafe {
let bsize = (*page).block_size;
let capacity = (*page).capacity as usize;
let reserved = (*page).reserved as usize;
if capacity >= reserved {
return;
}
#[cfg(feature = "blockmap")]
if capacity == 0 {
(*page).payload = area;
core::ptr::write_bytes(area.add(reserved * bsize), 0, bitmap_bytes(reserved));
}
debug_assert!(
(*page).slice_count.is_power_of_two(),
"extend bound assumes a power-of-two span, got {}",
(*page).slice_count
);
let span_shift = EXTEND_SHIFT_BASE + (*page).slice_count.trailing_zeros();
let take = ((reserved >> span_shift).max(1)).min(reserved - capacity);
let start = area.add(capacity * bsize);
let mut i = take;
let mut head: *mut Block = (*page).free;
while i > 0 {
i -= 1;
let b: *mut Block = start.add(i * bsize).cast();
block_set_next(page, b, head);
head = b;
}
(*page).free = head;
(*page).capacity = (capacity + take) as u32;
}
}
#[inline]
pub unsafe fn page_all_free(page: *mut Page) -> bool {
unsafe { (*page).used == 0 }
}
#[cfg(all(test, feature = "blockmap"))]
mod blockmap_index_tests {
use super::*;
use crate::bins::bin_size;
#[test]
fn index_matches_real_division_for_every_bin() {
for bin in 1..=40usize {
let bs = bin_size(bin);
if bs == 0 {
continue;
}
let k = bs.trailing_zeros();
let odd = bs >> k;
let inv = odd_mod_inverse(odd);
let blocks = (64 * 1024 / bs).min(4096);
for idx in 0..blocks {
let delta = idx * bs;
let got = (delta >> k).wrapping_mul(inv);
assert_eq!(
got, idx,
"bin {bin} (block_size {bs}): delta {delta} gave index {got}, want {idx}"
);
}
}
}
#[test]
fn interior_pointers_map_to_the_containing_block_or_out_of_range() {
const CAP: usize = 4096;
let mut saw_containing = 0;
let mut saw_garbage = 0;
for bin in 2..=40usize {
let bs = bin_size(bin);
if bs <= 1 {
continue;
}
let k = bs.trailing_zeros();
let odd = bs >> k;
let inv = odd_mod_inverse(odd);
let base = 4usize;
for off in 1..bs.min(64) {
let delta = base * bs + off;
let got = (delta >> k).wrapping_mul(inv);
if got == base {
saw_containing += 1;
} else if got >= CAP {
saw_garbage += 1;
} else {
panic!(
"bin {bin} (block_size {bs}) offset {off}: interior pointer mapped to \
index {got} — neither the containing block ({base}) nor out of range"
);
}
}
}
assert!(saw_containing > 0, "no containing-block cases covered");
assert!(saw_garbage > 0, "no out-of-range cases covered");
}
}
#[cfg(test)]
mod layout_tests {
use super::*;
#[test]
fn used_offset_matches_the_field_the_asm_decrements() {
assert_eq!(
USED_OFFSET,
core::mem::offset_of!(Page, used),
"USED_OFFSET is out of step with Page::used"
);
}
const _WIDTH_CHECK: fn(&Page) -> u32 = |p| p.used;
}
#[cfg(test)]
mod link_tests {
use super::*;
use crate::types::SEGMENT_SIZE;
const BASE: usize = 0x0000_4000_0000_0000;
const OFF: usize = SEGMENT_SIZE / 8;
const B: usize = BASE + OFF;
#[test]
fn accepts_genuine_links_anywhere_in_the_same_segment() {
assert!(link_is_plausible(BASE, B), "segment's first block");
assert!(link_is_plausible(B, B), "self-link");
assert!(link_is_plausible(B + 16, B), "next block");
assert!(link_is_plausible(B - 16, B), "previous block");
assert!(link_is_plausible(B + 4096, B), "forward link");
assert!(link_is_plausible(B - 4096, B), "backward link");
assert!(
link_is_plausible(BASE + SEGMENT_SIZE - 16, B),
"last aligned slot in the segment"
);
}
#[test]
fn rejects_every_misalignment() {
for off in 1..8usize {
assert!(!link_is_plausible(B + off, B), "misaligned by {off}");
}
}
#[test]
fn rejects_aligned_targets_outside_the_segment() {
assert!(
!link_is_plausible(BASE + SEGMENT_SIZE, B),
"first byte of the NEXT segment"
);
assert!(
!link_is_plausible(BASE - 16, B),
"last slot of the PREVIOUS segment"
);
assert!(
!link_is_plausible(0x0000_7fff_ffff_e000, B),
"a stack-shaped address"
);
assert!(
!link_is_plausible(0x0000_0000_0040_1000, B),
"a GOT-shaped address"
);
}
#[test]
fn the_segment_bound_is_exact() {
assert!(link_is_plausible(BASE + SEGMENT_SIZE - 16, B));
assert!(!link_is_plausible(BASE + SEGMENT_SIZE, B));
assert!(link_is_plausible(BASE, B));
assert!(!link_is_plausible(BASE - 16, B));
}
#[test]
fn does_not_stop_intra_segment_redirection() {
assert!(
link_is_plausible(B + 16, B),
"the neighbouring block is reachable by design — see R-005"
);
assert!(
link_is_plausible(B + OFF, B),
"far away in bytes, still the same segment"
);
}
}