extern crate alloc;
use core::ptr;
const MAX_ALIGN: usize = 16;
const HDR_BASE: usize = 0;
const HDR_SIZE: usize = 8;
fn total_for(size: usize) -> Option<usize> {
size.checked_add(MAX_ALIGN)
}
unsafe fn set_header(payload: *mut u8, base: *mut u8, alloc_size: usize) {
unsafe {
let hdr = payload.sub(MAX_ALIGN) as *mut usize;
*hdr.add(HDR_BASE / 8) = base as usize;
*hdr.add(HDR_SIZE / 8) = alloc_size;
}
}
unsafe fn read_header(ptr: *mut u8) -> (*mut u8, usize) {
unsafe {
let hdr = ptr.sub(MAX_ALIGN) as *mut usize;
(*hdr.add(HDR_BASE / 8) as *mut u8, *hdr.add(HDR_SIZE / 8))
}
}
unsafe fn alloc_with_header(alloc_size: usize) -> *mut u8 {
let layout = alloc::alloc::Layout::from_size_align(alloc_size, MAX_ALIGN).unwrap();
let base = unsafe { alloc::alloc::alloc(layout) };
if base.is_null() {
return base;
}
let payload = unsafe { base.add(MAX_ALIGN) };
unsafe {
set_header(payload, base, alloc_size);
}
payload
}
pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
let Some(total) = total_for(size) else {
return ptr::null_mut();
};
unsafe { alloc_with_header(total) }
}
pub unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut u8 {
let Some(total_size) = nmemb.checked_mul(size) else {
return ptr::null_mut();
};
let Some(total) = total_for(total_size) else {
return ptr::null_mut();
};
let layout = alloc::alloc::Layout::from_size_align(total, MAX_ALIGN).unwrap();
let base = unsafe { alloc::alloc::alloc_zeroed(layout) };
if base.is_null() {
return base;
}
let payload = unsafe { base.add(MAX_ALIGN) };
unsafe {
set_header(payload, base, total);
}
payload
}
pub unsafe extern "C" fn realloc(ptr: *mut u8, size: usize) -> *mut u8 {
unsafe {
if ptr.is_null() {
return malloc(size);
}
let (base, old_total) = read_header(ptr);
let layout = alloc::alloc::Layout::from_size_align(old_total, MAX_ALIGN).unwrap();
let Some(new_total) = total_for(size) else {
return ptr::null_mut();
};
let new_base = alloc::alloc::realloc(base, layout, new_total);
if new_base.is_null() {
return new_base;
}
let payload = new_base.add(MAX_ALIGN);
set_header(payload, new_base, new_total);
payload
}
}
pub unsafe extern "C" fn free(ptr: *mut u8) {
if ptr.is_null() {
return;
}
unsafe {
let (base, alloc_size) = read_header(ptr);
let layout = alloc::alloc::Layout::from_size_align(alloc_size, MAX_ALIGN).unwrap();
alloc::alloc::dealloc(base, layout);
}
}
pub unsafe extern "C" fn aligned_alloc(alignment: usize, size: usize) -> *mut u8 {
if alignment == 0 || !alignment.is_power_of_two() {
return ptr::null_mut();
}
let Some(rounded) = size.checked_add(alignment - 1) else {
return ptr::null_mut();
};
let payload_size = rounded & !(alignment - 1);
let Some(alloc_size) = payload_size
.checked_add(MAX_ALIGN)
.and_then(|v| v.checked_add(alignment))
else {
return ptr::null_mut();
};
let layout = alloc::alloc::Layout::from_size_align(alloc_size, MAX_ALIGN).unwrap();
let base = unsafe { alloc::alloc::alloc(layout) };
if base.is_null() {
return base;
}
let raw = base as usize + MAX_ALIGN;
let aligned = (raw + alignment - 1) & !(alignment - 1);
let payload = unsafe { base.add(MAX_ALIGN + (aligned - raw)) };
unsafe {
set_header(payload, base, alloc_size);
}
payload
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_malloc() {
let ptr = unsafe { malloc(10) };
assert!(!ptr.is_null());
unsafe {
let (base, alloc_size) = read_header(ptr);
assert_eq!(base, ptr.sub(MAX_ALIGN));
assert_eq!(alloc_size, 10 + MAX_ALIGN);
(0..10).for_each(|i| {
*ptr.add(i) = i as u8;
});
(0..10).for_each(|i| {
assert_eq!(*ptr.add(i), i as u8);
});
}
unsafe { free(ptr) };
}
#[test]
fn test_calloc() {
let ptr = unsafe { calloc(10, 10) };
assert!(!ptr.is_null());
unsafe {
let (base, alloc_size) = read_header(ptr);
assert_eq!(base, ptr.sub(MAX_ALIGN));
assert_eq!(alloc_size, 100 + MAX_ALIGN);
(0..100).for_each(|i| {
assert_eq!(*ptr.add(i), 0);
});
}
unsafe { free(ptr) };
}
#[test]
fn test_realloc() {
let ptr = unsafe { malloc(10) };
assert!(!ptr.is_null());
unsafe {
(0..10).for_each(|i| {
*ptr.add(i) = i as u8;
});
}
let ptr = unsafe { realloc(ptr, 20) };
assert!(!ptr.is_null());
unsafe {
let (base, alloc_size) = read_header(ptr);
assert_eq!(base, ptr.sub(MAX_ALIGN));
assert_eq!(alloc_size, 20 + MAX_ALIGN);
(0..10).for_each(|i| {
assert_eq!(*ptr.add(i), i as u8);
});
}
unsafe { free(ptr) };
}
#[test]
fn test_aligned_alloc() {
for alignment in [1usize, 2, 4, 8, 16, 32, 64, 256, 4096] {
let ptr = unsafe { aligned_alloc(alignment, 7) };
assert!(!ptr.is_null(), "aligned_alloc({alignment}) returned null");
assert_eq!(
ptr as usize % alignment,
0,
"payload not {alignment}-aligned: {ptr:p}"
);
unsafe {
(0..7).for_each(|i| {
*ptr.add(i) = i as u8;
});
}
unsafe { free(ptr) };
}
let ptr = unsafe { aligned_alloc(3, 8) };
assert!(ptr.is_null());
}
#[test]
fn test_size_overflow_returns_null() {
let ptr = unsafe { malloc(usize::MAX) };
assert!(ptr.is_null());
let ptr = unsafe { calloc(usize::MAX, 2) };
assert!(ptr.is_null());
let ptr = unsafe { calloc(usize::MAX, usize::MAX) };
assert!(ptr.is_null());
let ptr = unsafe { aligned_alloc(16, usize::MAX) };
assert!(ptr.is_null());
let ptr = unsafe { aligned_alloc(usize::MAX, 1) };
assert!(ptr.is_null());
let ptr = unsafe { malloc(10) };
assert!(!ptr.is_null());
unsafe {
(0..10).for_each(|i| {
*ptr.add(i) = i as u8;
});
}
let new_ptr = unsafe { realloc(ptr, usize::MAX) };
assert!(new_ptr.is_null());
unsafe {
let (base, alloc_size) = read_header(ptr);
assert_eq!(base, ptr.sub(MAX_ALIGN));
assert_eq!(alloc_size, 10 + MAX_ALIGN);
(0..10).for_each(|i| {
assert_eq!(*ptr.add(i), i as u8);
});
}
unsafe { free(ptr) };
}
}