#[cfg(feature = "runtime")]
mod heap {
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
const FREE_SLOTS: usize = 64;
const PAGE: usize = 65_536;
#[derive(Clone, Copy)]
struct Block {
start: usize,
len: usize,
}
struct Heap {
next: usize,
end: usize,
free: [Block; FREE_SLOTS],
count: usize,
}
impl Heap {
fn take_free(&mut self, layout: Layout) -> Option<usize> {
for index in 0..self.count {
let block = self.free[index];
let start = (block.start + layout.align() - 1) & !(layout.align() - 1);
let stop = start + layout.size();
if stop > block.start + block.len {
continue;
}
self.remove(index);
self.release(Block {
start: block.start,
len: start - block.start,
});
self.release(Block {
start: stop,
len: block.start + block.len - stop,
});
return Some(start);
}
None
}
fn bump(&mut self, layout: Layout) -> Option<usize> {
if self.end == 0 {
let previous = core::arch::wasm32::memory_grow(0, 1);
if previous == usize::MAX {
return None;
}
self.next = previous * PAGE;
self.end = self.next + PAGE;
}
let start = (self.next + layout.align() - 1) & !(layout.align() - 1);
let stop = start + layout.size();
if stop > self.end {
let pages = (stop - self.end).div_ceil(PAGE);
if core::arch::wasm32::memory_grow(0, pages) == usize::MAX {
return None;
}
self.end += pages * PAGE;
}
let gap = Block {
start: self.next,
len: start - self.next,
};
self.next = stop;
self.release(gap);
Some(start)
}
fn remove(&mut self, index: usize) {
self.count -= 1;
self.free[index] = self.free[self.count];
}
fn release(&mut self, mut block: Block) {
if block.len == 0 {
return;
}
let mut index = 0;
while index < self.count {
let other = self.free[index];
if other.start + other.len == block.start {
block = Block {
start: other.start,
len: other.len + block.len,
};
self.remove(index);
index = 0;
} else if block.start + block.len == other.start {
block.len += other.len;
self.remove(index);
index = 0;
} else {
index += 1;
}
}
if block.start + block.len == self.next {
self.next = block.start;
return;
}
if self.count == FREE_SLOTS {
let smallest = (0..self.count)
.min_by_key(|index| self.free[*index].len)
.unwrap_or(0);
if self.free[smallest].len >= block.len {
return;
}
self.remove(smallest);
}
self.free[self.count] = block;
self.count += 1;
}
}
struct Allocator {
heap: UnsafeCell<Heap>,
}
unsafe impl Sync for Allocator {}
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let heap = unsafe { &mut *self.heap.get() };
heap.take_free(layout)
.or_else(|| heap.bump(layout))
.map_or(core::ptr::null_mut(), |start| start as *mut u8)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let heap = unsafe { &mut *self.heap.get() };
heap.release(Block {
start: ptr as usize,
len: layout.size(),
});
}
}
#[global_allocator]
static ALLOCATOR: Allocator = Allocator {
heap: UnsafeCell::new(Heap {
next: 0,
end: 0,
free: [Block { start: 0, len: 0 }; FREE_SLOTS],
count: 0,
}),
};
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo<'_>) -> ! {
core::arch::wasm32::unreachable()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cabi_realloc(
old_ptr: *mut u8,
old_len: usize,
align: usize,
new_len: usize,
) -> *mut u8 {
use alloc::alloc::{Layout, alloc, dealloc};
let Ok(layout) = Layout::from_size_align(new_len.max(1), align.max(1)) else {
core::arch::wasm32::unreachable()
};
unsafe {
let new_ptr = alloc(layout);
if new_ptr.is_null() {
core::arch::wasm32::unreachable()
}
if !old_ptr.is_null() && old_len > 0 {
core::ptr::copy_nonoverlapping(old_ptr, new_ptr, old_len.min(new_len));
dealloc(
old_ptr,
Layout::from_size_align_unchecked(old_len, align.max(1)),
);
}
new_ptr
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn memcmp(
left: *const core::ffi::c_void,
right: *const core::ffi::c_void,
len: usize,
) -> i32 {
let left = left.cast::<u8>();
let right = right.cast::<u8>();
let mut index = 0;
while index < len {
let (a, b) = unsafe { (*left.add(index), *right.add(index)) };
if a != b {
return i32::from(a) - i32::from(b);
}
index += 1;
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn bcmp(
left: *const core::ffi::c_void,
right: *const core::ffi::c_void,
len: usize,
) -> i32 {
unsafe { memcmp(left, right, len) }
}
#[unsafe(no_mangle)]
pub extern "C" fn fmodf(x: f32, y: f32) -> f32 {
libm::fmodf(x, y)
}
#[unsafe(no_mangle)]
pub extern "C" fn fmod(x: f64, y: f64) -> f64 {
libm::fmod(x, y)
}