#![allow(unsafe_code)]
#![allow(dead_code)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
const MAX_ARENAS: usize = 128;
struct Region {
base: AtomicUsize,
end: AtomicUsize,
}
#[allow(clippy::declare_interior_mutable_const)] const ZERO_REGION: Region = Region {
base: AtomicUsize::new(0),
end: AtomicUsize::new(0),
};
static REGION_SLOTS: AtomicUsize = AtomicUsize::new(0);
static REGIONS: [Region; MAX_ARENAS] = [ZERO_REGION; MAX_ARENAS];
fn register_region(base: usize, end: usize) -> bool {
let idx = REGION_SLOTS.fetch_add(1, Ordering::Relaxed);
if idx >= MAX_ARENAS {
return false;
}
REGIONS[idx].base.store(base, Ordering::Relaxed);
REGIONS[idx].end.store(end, Ordering::Relaxed);
true
}
#[inline]
fn in_any_arena(p: usize) -> bool {
let n = REGION_SLOTS.load(Ordering::Relaxed).min(MAX_ARENAS);
for r in ®IONS[..n] {
let base = r.base.load(Ordering::Relaxed);
if base != 0 && p >= base && p < r.end.load(Ordering::Relaxed) {
return true;
}
}
false
}
fn bump_compute(cur: usize, align: usize, size: usize, end: usize) -> Option<(usize, usize)> {
let aligned = cur.checked_add(align - 1)? & !(align - 1);
let next = aligned.checked_add(size)?;
(next <= end).then_some((aligned, next))
}
#[cfg(target_arch = "wasm32")]
const fn parse_mb(s: &str) -> usize {
let bytes = s.as_bytes();
let mut n = 0usize;
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
assert!(
b >= b'0' && b <= b'9',
"SASSO_WASM_ARENA_MB must be decimal digits"
);
n = n * 10 + (b - b'0') as usize;
i += 1;
}
n
}
#[cfg(target_arch = "wasm32")]
const WASM_DEFAULT_ARENA_SIZE: usize = match option_env!("SASSO_WASM_ARENA_MB") {
Some(s) => parse_mb(s) * 1024 * 1024,
None => 32 * 1024 * 1024,
};
static ARENA_CONFIG: AtomicUsize = AtomicUsize::new(0);
pub fn set_arena_bytes(bytes: usize) {
ARENA_CONFIG.store(if bytes == 0 { usize::MAX } else { bytes }, Ordering::Relaxed);
}
#[cfg(target_arch = "wasm32")]
#[inline]
fn effective_arena_size() -> usize {
match ARENA_CONFIG.load(Ordering::Relaxed) {
0 => WASM_DEFAULT_ARENA_SIZE,
usize::MAX => 0,
n => n,
}
}
#[cfg(not(target_arch = "wasm32"))]
#[inline]
fn effective_arena_size() -> usize {
2 * 1024 * 1024 * 1024 }
struct ThreadState {
base: Cell<*mut u8>,
end: Cell<usize>,
cursor: Cell<usize>,
depth: Cell<u32>,
reserve_failed: Cell<bool>,
}
impl ThreadState {
const fn new() -> ThreadState {
ThreadState {
base: Cell::new(std::ptr::null_mut()),
end: Cell::new(0),
cursor: Cell::new(0),
depth: Cell::new(0),
reserve_failed: Cell::new(false),
}
}
#[cold]
fn reserve(&self) -> bool {
let size = effective_arena_size();
if size == 0 {
return false; }
let Ok(layout) = Layout::from_size_align(size, 4096) else {
return false;
};
let p = unsafe { System.alloc(layout) };
if p.is_null() {
return false;
}
if !register_region(p as usize, p as usize + size) {
unsafe { System.dealloc(p, layout) };
return false;
}
self.base.set(p);
self.end.set(p as usize + size);
self.cursor.set(p as usize);
true
}
}
thread_local! {
static TL: ThreadState = const { ThreadState::new() };
}
pub struct ScopedAlloc;
unsafe impl GlobalAlloc for ScopedAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
TL.with(|tl| {
if tl.depth.get() == 0 {
return unsafe { System.alloc(layout) };
}
if tl.base.get().is_null() {
if tl.reserve_failed.get() {
return unsafe { System.alloc(layout) };
}
if !tl.reserve() {
tl.reserve_failed.set(true);
return unsafe { System.alloc(layout) };
}
}
match bump_compute(tl.cursor.get(), layout.align(), layout.size(), tl.end.get()) {
Some((aligned, next)) => {
tl.cursor.set(next);
let base = tl.base.get();
unsafe { base.add(aligned - base as usize) }
}
None => unsafe { System.alloc(layout) },
}
})
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if !in_any_arena(ptr as usize) {
unsafe { System.dealloc(ptr, layout) };
}
}
}
pub(crate) struct Scope;
impl Scope {
pub(crate) fn enter() -> Scope {
TL.with(|tl| tl.depth.set(tl.depth.get() + 1));
Scope
}
}
impl Drop for Scope {
fn drop(&mut self) {
if leave_no_reset() {
reset();
}
}
}
pub(crate) fn leave_no_reset() -> bool {
TL.with(|tl| {
let d = tl.depth.get().saturating_sub(1);
tl.depth.set(d);
d == 0
})
}
pub(crate) fn reset() {
TL.with(|tl| {
if tl.depth.get() == 0 {
tl.cursor.set(tl.base.get() as usize);
}
});
}
pub(crate) fn pause() -> u32 {
TL.with(|tl| {
let d = tl.depth.get();
tl.depth.set(0);
d
})
}
pub(crate) fn resume(saved: u32) {
TL.with(|tl| tl.depth.set(saved));
}
#[cfg(test)]
struct Arena {
base: *mut u8,
size: usize,
end: usize,
cursor: Cell<usize>,
}
#[cfg(test)]
impl Arena {
fn with_system_backing(size: usize) -> Option<Arena> {
let layout = Layout::from_size_align(size, 4096).ok()?;
let base = unsafe { System.alloc(layout) };
if base.is_null() {
return None;
}
Some(Arena {
base,
size,
end: base as usize + size,
cursor: Cell::new(base as usize),
})
}
fn alloc(&self, layout: Layout) -> Option<*mut u8> {
let (aligned, next) = bump_compute(self.cursor.get(), layout.align(), layout.size(), self.end)?;
self.cursor.set(next);
Some(unsafe { self.base.add(aligned - self.base as usize) })
}
fn reset(&self) {
self.cursor.set(self.base as usize);
}
fn used(&self) -> usize {
self.cursor.get() - self.base as usize
}
fn contains(&self, ptr: *mut u8) -> bool {
let p = ptr as usize;
p >= self.base as usize && p < self.end
}
}
#[cfg(test)]
impl Drop for Arena {
fn drop(&mut self) {
if let Ok(layout) = Layout::from_size_align(self.size, 4096) {
unsafe { System.dealloc(self.base, layout) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_aligns_up() {
assert_eq!(bump_compute(10, 8, 4, 1000), Some((16, 20)));
assert_eq!(bump_compute(16, 8, 8, 1000), Some((16, 24)));
assert_eq!(bump_compute(7, 1, 3, 1000), Some((7, 10)));
}
#[test]
fn compute_every_power_of_two_alignment() {
for align in [1usize, 2, 4, 8, 16, 32, 64, 128, 256, 4096] {
let (aligned, next) = bump_compute(1, align, 64, usize::MAX).unwrap();
assert_eq!(aligned % align, 0, "align {align}");
assert_eq!(next, aligned + 64);
}
}
#[test]
fn compute_zero_size() {
assert_eq!(bump_compute(8, 8, 0, 100), Some((8, 8)));
}
#[test]
fn compute_boundary() {
assert_eq!(bump_compute(0, 1, 100, 100), Some((0, 100)));
assert_eq!(bump_compute(0, 1, 101, 100), None);
assert_eq!(bump_compute(90, 8, 20, 100), None);
}
#[test]
fn compute_overflow_is_none() {
assert_eq!(bump_compute(usize::MAX, 8, 0, usize::MAX), None);
assert_eq!(bump_compute(usize::MAX - 3, 1, 10, usize::MAX), None);
}
fn layout(size: usize, align: usize) -> Layout {
Layout::from_size_align(size, align).unwrap()
}
#[test]
fn arena_alloc_is_aligned_writable_and_in_bounds() {
let a = Arena::with_system_backing(64 * 1024).unwrap();
for align in [1usize, 2, 4, 8, 16, 64, 256] {
let p = a.alloc(layout(128, align)).unwrap();
assert_eq!(p as usize % align, 0, "align {align}");
assert!(a.contains(p));
unsafe {
std::ptr::write_bytes(p, 0xAB, 128);
assert_eq!(*p, 0xAB);
assert_eq!(*p.add(127), 0xAB);
}
}
}
#[test]
fn arena_allocations_do_not_overlap() {
let a = Arena::with_system_backing(64 * 1024).unwrap();
let p1 = a.alloc(layout(64, 8)).unwrap() as usize;
let p2 = a.alloc(layout(64, 8)).unwrap() as usize;
assert!(p2 >= p1 + 64);
}
#[test]
fn arena_full_returns_none() {
let a = Arena::with_system_backing(4096).unwrap();
assert!(a.alloc(layout(8192, 8)).is_none());
assert!(a.alloc(layout(2048, 8)).is_some());
assert!(a.alloc(layout(2048, 8)).is_some());
assert!(a.alloc(layout(1, 1)).is_none());
}
#[test]
fn arena_reset_reuses_region() {
let a = Arena::with_system_backing(64 * 1024).unwrap();
let p1 = a.alloc(layout(1000, 8)).unwrap();
assert_eq!(a.used(), 1000);
a.reset();
assert_eq!(a.used(), 0);
let p2 = a.alloc(layout(1000, 8)).unwrap();
assert_eq!(p1, p2);
unsafe { std::ptr::write_bytes(p2, 0xCD, 1000) };
}
#[test]
#[cfg_attr(miri, ignore)]
fn scoped_routes_to_system_when_inactive() {
let l = layout(64, 8);
let p = unsafe { ScopedAlloc.alloc(l) };
assert!(!p.is_null());
assert!(!TL.with(|tl| {
let b = tl.base.get() as usize;
(p as usize) >= b && (p as usize) < tl.end.get() && b != 0
}));
unsafe { ScopedAlloc.dealloc(p, l) };
}
#[test]
#[cfg_attr(miri, ignore)]
fn scoped_bumps_inside_scope_and_resets() {
let l = layout(128, 16);
let scope = Scope::enter();
let p1 = unsafe { ScopedAlloc.alloc(l) };
let p2 = unsafe { ScopedAlloc.alloc(l) };
let in_arena = |p: *mut u8| {
TL.with(|tl| {
let b = tl.base.get() as usize;
b != 0 && (p as usize) >= b && (p as usize) < tl.end.get()
})
};
assert!(
in_arena(p1) && in_arena(p2),
"in-scope allocs come from the arena"
);
assert!(p2 as usize >= p1 as usize + 128, "no overlap");
assert_eq!(p1 as usize % 16, 0);
unsafe { ScopedAlloc.dealloc(p1, l) };
let outer = leave_no_reset();
assert!(outer);
reset();
let scope2 = Scope::enter();
let p3 = unsafe { ScopedAlloc.alloc(l) };
assert_eq!(p3, p1, "reset hands back the same region");
let _ = leave_no_reset();
reset();
drop(scope2);
std::mem::forget(scope);
}
#[test]
#[cfg_attr(miri, ignore)]
fn pause_routes_to_system_then_resumes() {
let l = layout(64, 8);
let scope = Scope::enter();
let saved = pause(); let p_sys = unsafe { ScopedAlloc.alloc(l) }; let in_arena = |p: *mut u8| {
TL.with(|tl| {
let b = tl.base.get() as usize;
b != 0 && (p as usize) >= b && (p as usize) < tl.end.get()
})
};
assert!(!in_arena(p_sys), "paused scope routes to System");
unsafe { ScopedAlloc.dealloc(p_sys, l) };
resume(saved); let p_arena = unsafe { ScopedAlloc.alloc(l) };
assert!(in_arena(p_arena), "resumed scope bumps from the arena again");
let _ = leave_no_reset();
reset();
std::mem::forget(scope);
}
}