#![no_std]
#![cfg_attr(target_family = "wasm", feature(linkage))]
#![deny(warnings)]
extern crate alloc;
use alloc::alloc::{GlobalAlloc, Layout};
use core::{
ptr::null_mut,
sync::atomic::{AtomicPtr, Ordering},
};
#[cfg(target_family = "wasm")]
const PAGE_SIZE: usize = 2usize.pow(16);
const MIN_ALIGN: usize = 16;
const HEAP_END: *mut u8 = u32::MAX as *mut u8;
pub struct BumpAlloc {
top: AtomicPtr<u8>,
}
impl Default for BumpAlloc {
fn default() -> Self {
Self::new()
}
}
impl BumpAlloc {
pub const fn new() -> Self {
Self {
top: AtomicPtr::new(null_mut()),
}
}
#[cfg(target_family = "wasm")]
fn maybe_init(&self) {
let top = self.top.load(Ordering::Relaxed);
if top.is_null() {
let base = unsafe { heap_base() };
let size = core::arch::wasm32::memory_size(0);
self.top.store(unsafe { base.byte_add(size * PAGE_SIZE) }, Ordering::Relaxed);
}
}
#[cfg(not(target_family = "wasm"))]
fn maybe_init(&self) {}
}
unsafe impl GlobalAlloc for BumpAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let layout = layout
.align_to(core::cmp::max(layout.align(), MIN_ALIGN))
.unwrap()
.pad_to_align();
let size = layout.size();
let align = layout.align();
self.maybe_init();
let top = self.top.load(Ordering::Relaxed);
let available = unsafe { HEAP_END.byte_offset_from(top) as usize };
if available >= size {
unsafe {
self.top.store(top.byte_add(size), Ordering::Relaxed);
top.byte_offset(align as isize)
}
} else {
null_mut()
}
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}
#[cfg(target_family = "wasm")]
unsafe extern "C" {
#[linkage = "extern_weak"]
#[link_name = "intrinsics::mem::heap_base"]
fn heap_base() -> *mut u8;
}