#![no_std]
extern crate snmalloc_sys as ffi;
use core::{
alloc::{GlobalAlloc, Layout},
ptr::NonNull,
};
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct SnMalloc;
unsafe impl Send for SnMalloc {}
unsafe impl Sync for SnMalloc {}
impl SnMalloc {
#[inline(always)]
pub const fn new() -> Self {
Self
}
#[inline(always)]
pub fn usable_size(&self, ptr: *const u8) -> Option<usize> {
match ptr.is_null() {
true => None,
false => Some(unsafe { ffi::sn_rust_usable_size(ptr.cast()) })
}
}
#[inline(always)]
pub fn alloc_aligned(&self, layout: Layout) -> Option<NonNull<u8>> {
match layout.size() {
0 => NonNull::new(layout.align() as *mut u8),
size => NonNull::new(unsafe { ffi::sn_rust_alloc(layout.align(), size) }.cast())
}
}
}
unsafe impl GlobalAlloc for SnMalloc {
#[inline(always)]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
match layout.size() {
0 => layout.align() as *mut u8,
size => ffi::sn_rust_alloc(layout.align(), size).cast()
}
}
#[inline(always)]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.size() != 0 {
ffi::sn_rust_dealloc(ptr as _, layout.align(), layout.size());
}
}
#[inline(always)]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
match layout.size() {
0 => layout.align() as *mut u8,
size => ffi::sn_rust_alloc_zeroed(layout.align(), size).cast()
}
}
#[inline(always)]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
match new_size {
0 => {
self.dealloc(ptr, layout);
layout.align() as *mut u8
}
new_size if layout.size() == 0 => {
self.alloc(Layout::from_size_align_unchecked(new_size, layout.align()))
}
_ => ffi::sn_rust_realloc(ptr.cast(), layout.align(), layout.size(), new_size).cast()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allocation_lifecycle() {
let alloc = SnMalloc::new();
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let ptr = alloc.alloc(layout);
alloc.dealloc(ptr, layout);
let ptr = alloc.alloc_zeroed(layout);
alloc.dealloc(ptr, layout);
let ptr = alloc.alloc(layout);
let ptr = alloc.realloc(ptr, layout, 16);
alloc.dealloc(ptr, layout);
let large_layout = Layout::from_size_align(1 << 20, 32).unwrap();
let ptr = alloc.alloc(large_layout);
alloc.dealloc(ptr, large_layout);
}
}
#[test]
fn it_frees_allocated_memory() {
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let alloc = SnMalloc;
let ptr = alloc.alloc(layout);
alloc.dealloc(ptr, layout);
}
}
#[test]
fn it_frees_zero_allocated_memory() {
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let alloc = SnMalloc;
let ptr = alloc.alloc_zeroed(layout);
alloc.dealloc(ptr, layout);
}
}
#[test]
fn it_frees_reallocated_memory() {
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let alloc = SnMalloc;
let ptr = alloc.alloc(layout);
let ptr = alloc.realloc(ptr, layout, 16);
alloc.dealloc(ptr, layout);
}
}
#[test]
fn it_frees_large_alloc() {
unsafe {
let layout = Layout::from_size_align(1 << 20, 32).unwrap();
let alloc = SnMalloc;
let ptr = alloc.alloc(layout);
alloc.dealloc(ptr, layout);
}
}
#[test]
fn test_usable_size() {
let alloc = SnMalloc::new();
unsafe {
let layout = Layout::from_size_align(8, 8).unwrap();
let ptr = alloc.alloc(layout);
let usz = alloc.usable_size(ptr).expect("usable_size returned None");
alloc.dealloc(ptr, layout);
assert!(usz >= 8);
}
}
}