alloc_cat 1.1.1

a simple allocator for small-to-tiny Wasm projects in rust
Documentation
// wasm should be no_std
// #![cfg_attr(all(target_arch = "wasm32", not(test)), no_std)]

#![warn(clippy::std_instead_of_alloc, clippy::std_instead_of_core)]


use cfg_if::cfg_if;
use core::alloc::{GlobalAlloc, Layout};
use core::fmt;

mod allocator;
mod allocator_stats;
mod metrics;
mod simple_allocator;
mod wrapped_allocator;

pub use allocator::Allocator;
pub use allocator_stats::{AllocatorStats, SimpleAllocatorStats, Statsable};
pub use simple_allocator::SimpleAllocator;
pub use wrapped_allocator::WrappedAllocator;


// if you're not in a wasm VM, and not using the system heap, we need to
// pre-allocate an mmap heap. specify the maximum size of that heap with env
// var ALLOC_CAT_MMAP_HEAP_SIZE (default: 2MB)
#[allow(dead_code)]
const fn config_mmap_heap_size() -> usize {
    const DEFAULT_HEAP_SIZE: usize = 2097152;
    const HEAP_SIZE_ARG: Option<&str> = option_env!("ALLOC_CAT_MMAP_HEAP_SIZE");
    // kind of irritating that parse() and unwrap_or() are not const!
    match HEAP_SIZE_ARG {
        None => DEFAULT_HEAP_SIZE,
        Some(s) => {
            let mut acc: usize = 0;
            let mut i = 0;
            while i < s.len() {
                acc = acc * 10 + (s.as_bytes()[i] - b'0') as usize;
                i += 1;
            }
            acc
        }
    }
}

cfg_if! {
    if #[cfg(any(test, not(feature = "system_allocator")))] {
        mod mmap_heap;
        pub use mmap_heap::MmapHeap;
    }
}

cfg_if! {
    if #[cfg(target_arch = "wasm32")] {
        mod wasm_heap;
        pub use wasm_heap::WasmHeap;

        // low-level access to pages of the wasm "heap":
        const HEAP: WasmHeap = WasmHeap {};

        pub type AllocCat = Allocator<'static>;
        pub static ALLOCATOR: AllocCat = Allocator::new(&HEAP);
    } else if #[cfg(not(feature = "system_allocator"))] {
        mod thread_safe_allocator;
        pub use thread_safe_allocator::ThreadSafeAllocator;

        // if you want to use alloc_cat in a non-wasm environment (for
        // example, to do some gnarly testing), we need to map some anonymous
        // memory to treat as a "heap", and grab a lock around each
        // allocation. this is very slow. 💀
        static HEAP: MmapHeap = MmapHeap::new(config_mmap_heap_size());

        pub type AllocCat = ThreadSafeAllocator<Allocator<'static>>;
        static RAW_ALLOC: Allocator<'static> = Allocator::new(&HEAP);
        pub static ALLOCATOR: AllocCat = ThreadSafeAllocator::new(&RAW_ALLOC);
    } else {
        pub type AllocCat = WrappedAllocator<std::alloc::System>;
        pub static ALLOCATOR: AllocCat = WrappedAllocator::new(&std::alloc::System);
    }
}


pub const PAGE_SIZE: usize = 64 * 1024;

/// interface for something that behaves similar to a wasm heap
pub trait Heap: fmt::Debug + fmt::Display {
    fn heap_start(&self) -> usize;
    fn heap_end(&self) -> usize;
    fn heap_grow(&self, pages: usize) -> Option<*mut u8>;
}


// this is really weird, but rust can't figure out that `&AllocCat` implements
// the same trait as `AllocCat`.
unsafe impl GlobalAlloc for &AllocCat {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        unsafe { (*self).alloc(layout) }
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        unsafe { (*self).dealloc(ptr, layout) }
    }

    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
        unsafe { (*self).alloc_zeroed(layout) }
    }

    unsafe fn realloc(&self, old_pointer: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
        unsafe { (*self).realloc(old_pointer, layout, new_size) }
    }
}