summaryrefslogtreecommitdiff
path: root/src/allocator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/allocator.rs')
-rw-r--r--src/allocator.rs204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/allocator.rs b/src/allocator.rs
new file mode 100644
index 0000000..eaaba5f
--- /dev/null
+++ b/src/allocator.rs
@@ -0,0 +1,204 @@
+use core::{
+ alloc::{GlobalAlloc, Layout},
+ sync::atomic::{AtomicUsize, Ordering::Relaxed},
+};
+
+pub struct TalcAllocator(
+ talc::cell::TalcSyncCell<talc::wasm::WasmGrowAndExtend, talc::wasm::WasmBinning>,
+);
+
+impl TalcAllocator {
+ pub const fn new() -> Self {
+ Self(talc::cell::TalcSyncCell::new_wasm(
+ talc::wasm::WasmGrowAndExtend::new(),
+ ))
+ }
+}
+
+impl Default for TalcAllocator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+unsafe impl GlobalAlloc for TalcAllocator {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ unsafe { self.0.alloc(layout) }
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ unsafe {
+ self.0.dealloc(ptr, layout);
+ }
+ }
+
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ unsafe { self.0.alloc_zeroed(layout) }
+ }
+
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ unsafe { self.0.realloc(ptr, layout, new_size) }
+ }
+}
+
+const CHUNK_SIZE: usize = 64;
+const PAGE_SIZE: usize = 64 * 1024;
+
+unsafe extern "C" {
+ safe static __heap_base: usize;
+}
+
+pub struct BumpAllocator {
+ offset: AtomicUsize,
+ capacity: AtomicUsize,
+ free_8: AtomicUsize,
+ free_16: AtomicUsize,
+ free_32: AtomicUsize,
+ free_64: AtomicUsize,
+ free_128: AtomicUsize,
+ free_256: AtomicUsize,
+ free_512: AtomicUsize,
+ free_1024: AtomicUsize,
+ free_2048: AtomicUsize,
+ free_4096: AtomicUsize,
+ free_8192: AtomicUsize,
+ free_16384: AtomicUsize,
+ free_32768: AtomicUsize,
+}
+
+fn space_for(layout: Layout) -> usize {
+ usize::div_ceil(layout.size(), CHUNK_SIZE) * CHUNK_SIZE
+}
+
+fn pop_free_list(list: &AtomicUsize) -> Option<*mut u8> {
+ let offset = list.swap(0, Relaxed);
+ if offset != 0 {
+ // safety: offset is a valid pointer for these operations when non-zero
+ unsafe {
+ let ptr = offset as *mut u8;
+ let next = *ptr.cast::<usize>();
+ list.swap(next, Relaxed);
+ Some(ptr)
+ }
+ } else {
+ None
+ }
+}
+
+unsafe fn push_free_list(list: &AtomicUsize, ptr: *mut u8) {
+ // safety: all of the pointers here are valid
+ unsafe {
+ let new_offset = ptr.addr();
+ let current_head = list.swap(new_offset, Relaxed);
+ *ptr.cast::<usize>() = current_head;
+ }
+}
+
+impl BumpAllocator {
+ pub const fn new() -> Self {
+ Self {
+ offset: AtomicUsize::new(8),
+ capacity: AtomicUsize::new(PAGE_SIZE - 8),
+ free_8: AtomicUsize::new(0),
+ free_16: AtomicUsize::new(0),
+ free_32: AtomicUsize::new(0),
+ free_64: AtomicUsize::new(0),
+ free_128: AtomicUsize::new(0),
+ free_256: AtomicUsize::new(0),
+ free_512: AtomicUsize::new(0),
+ free_1024: AtomicUsize::new(0),
+ free_2048: AtomicUsize::new(0),
+ free_4096: AtomicUsize::new(0),
+ free_8192: AtomicUsize::new(0),
+ free_16384: AtomicUsize::new(0),
+ free_32768: AtomicUsize::new(0),
+ }
+ }
+}
+
+impl Default for BumpAllocator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+unsafe impl GlobalAlloc for BumpAllocator {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ self.offset.fetch_max(__heap_base, Relaxed);
+ let space_needed = space_for(layout);
+ let ptr = match space_needed {
+ 8 => pop_free_list(&self.free_8),
+ 16 => pop_free_list(&self.free_16),
+ 32 => pop_free_list(&self.free_32),
+ 64 => pop_free_list(&self.free_64),
+ 128 => pop_free_list(&self.free_128),
+ 256 => pop_free_list(&self.free_256),
+ 512 => pop_free_list(&self.free_512),
+ 1024 => pop_free_list(&self.free_1024),
+ 2048 => pop_free_list(&self.free_2048),
+ 4096 => pop_free_list(&self.free_4096),
+ 8192 => pop_free_list(&self.free_8192),
+ 16384 => pop_free_list(&self.free_16384),
+ 32768 => pop_free_list(&self.free_32768),
+ _ => None,
+ };
+ if let Some(ptr) = ptr {
+ return ptr;
+ }
+
+ let offset = self.offset.fetch_add(space_needed, Relaxed);
+ let pointer = offset as *mut u8;
+ let available_space = self.capacity.load(Relaxed) - offset;
+ if available_space < layout.size() {
+ let pages = layout.size().div_ceil(PAGE_SIZE);
+ core::arch::wasm32::memory_grow(0, pages);
+ self.capacity.fetch_add(pages * PAGE_SIZE, Relaxed);
+ }
+
+ pointer
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ let space_used = space_for(layout);
+ let ptr_offset = ptr.addr();
+ if self
+ .offset
+ .compare_exchange(ptr_offset + space_used, ptr_offset, Relaxed, Relaxed)
+ .is_ok()
+ {
+ return;
+ }
+
+ // safety: ptr must be valid and on the heap
+ unsafe {
+ match space_used {
+ 8 => push_free_list(&self.free_8, ptr),
+ 16 => push_free_list(&self.free_16, ptr),
+ 32 => push_free_list(&self.free_32, ptr),
+ 64 => push_free_list(&self.free_64, ptr),
+ 128 => push_free_list(&self.free_128, ptr),
+ 256 => push_free_list(&self.free_256, ptr),
+ 512 => push_free_list(&self.free_512, ptr),
+ 1024 => push_free_list(&self.free_1024, ptr),
+ 2048 => push_free_list(&self.free_2048, ptr),
+ 4096 => push_free_list(&self.free_4096, ptr),
+ 8192 => push_free_list(&self.free_8192, ptr),
+ 16384 => push_free_list(&self.free_16384, ptr),
+ 32768 => push_free_list(&self.free_32768, ptr),
+ _ => {}
+ };
+ }
+ }
+
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ let space_used = space_for(layout);
+ if new_size <= space_used {
+ return ptr;
+ }
+
+ unsafe {
+ self.dealloc(ptr, layout);
+ self.alloc(Layout::from_size_align(new_size, layout.align()).unwrap_unchecked())
+ }
+ }
+}