Created
May 15, 2023 19:35
-
-
Save leddoo/6582bb1b62dd3b32dd0044e6bfe62299 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use core::ptr::NonNull; | |
use core::alloc::Layout; | |
pub trait Alloc { | |
/// allocates a block of memory. | |
/// | |
/// - if the call succeeds: | |
/// - the returned pointer refers to a live allocation. | |
/// - `layout` is the active layout of the returned memory block. | |
/// | |
#[inline(always)] | |
fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> { | |
if layout.size() != 0 { | |
// layout.size() > 0. | |
let result = unsafe { self.alloc_nonzero(layout) }; | |
if let Some(result) = result { | |
debug_assert!(result.as_ptr() as usize % layout.align() == 0); | |
} | |
return result; | |
} | |
else { | |
return Some(dangling(layout)); | |
} | |
} | |
/// allocates a block of memory. | |
/// | |
/// - if the call succeeds: | |
/// - the returned pointer refers to a live allocation. | |
/// - `layout` is the active layout of the returned memory block. | |
/// | |
/// # safety: | |
/// - `layout.size() > 0` | |
/// | |
unsafe fn alloc_nonzero(&self, layout: Layout) -> Option<NonNull<u8>>; | |
/// frees an allocation. | |
/// | |
/// - after the call, `ptr` is no longer a live allocation. | |
/// | |
/// # safety: | |
/// - if `layout.size() > 0`: | |
/// - `ptr` must be a live allocation, allocated from this allocator. | |
/// - `layout` must be the active layout of the memory block. | |
/// | |
#[inline(always)] | |
unsafe fn free(&self, ptr: NonNull<u8>, layout: Layout) { | |
if layout.size() != 0 { | |
// invariants upheld by the caller, because `layout.size() > 0`. | |
unsafe { self.free_nonzero(ptr, layout) } | |
} | |
} | |
/// frees an allocation. | |
/// | |
/// - after the call, `ptr` is no longer a live allocation. | |
/// | |
/// # safety: | |
/// - `ptr` must be a live allocation, allocated from this allocator. | |
/// - `layout` must be the active layout of the memory block. | |
/// - `layout.size() > 0`. | |
/// | |
unsafe fn free_nonzero(&self, ptr: NonNull<u8>, layout: Layout); | |
/// attempts to resize an allocation. | |
/// | |
/// - returns whether resizing succeeded. | |
/// - the allocation referenced by `ptr` always remains live. | |
/// - if the call succeeds, `new_layout` is the active layout, | |
/// otherwise, `old_layout` remains the active layout. | |
/// | |
/// # safety | |
/// - if old_layout.size() > 0: | |
/// - `ptr` must be a live allocation, allocated from this allocator. | |
/// - `old_layout` must be the active layout of the memory block. | |
/// - `old_layout.align() == new_layout.align()`. | |
/// | |
#[inline(always)] | |
unsafe fn try_realloc(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<(), ()> { | |
if old_layout.size() == 0 || new_layout.size() == 0 { | |
return Err(()) | |
} | |
self.try_realloc_nonzero(ptr, old_layout, new_layout) | |
} | |
/// attempts to resize an allocation. | |
/// | |
/// - returns whether resizing succeeded. | |
/// - the allocation referenced by `ptr` always remains live. | |
/// - if the call succeeds, `new_layout` is the active layout, | |
/// otherwise, `old_layout` remains the active layout. | |
/// | |
/// # safety | |
/// - `ptr` must be a live allocation, allocated from this allocator. | |
/// - `old_layout` must be the active layout of the memory block. | |
/// - `old_layout.align() == new_layout.align()`. | |
/// - `old_layout.size() > 0`. | |
/// - `new_layout.size() > 0`. | |
/// | |
#[inline(always)] | |
unsafe fn try_realloc_nonzero(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<(), ()> { | |
let _ = (ptr, old_layout, new_layout); | |
Err(()) | |
} | |
/// resize an allocation. | |
/// | |
/// - if the call succeeds: | |
/// - the returned pointer must be used for subsequent `Alloc` method calls. | |
/// - the active layout is `new_layout`. | |
/// - the new memory block's bytes in `0..min(old_layout.size(), new_layout.size())` | |
/// have the old memory block's values, other bytes are undefined. | |
/// - if the call fails: | |
/// - `ptr` remains live. | |
/// - `old_layout` remains the active layout. | |
/// - the memory block referenced by `ptr` remains unchanged. | |
/// | |
/// # safety | |
/// - if old_layout.size() > 0: | |
/// - `ptr` must be a live allocation, allocated from this allocator. | |
/// - `old_layout` must be the active layout of the memory block. | |
/// - `old_layout.align() == new_layout.align()`. | |
/// | |
unsafe fn realloc(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Option<NonNull<u8>> { | |
debug_assert_eq!(old_layout.align(), new_layout.align()); | |
// invariants upheld by the caller. | |
if unsafe { self.try_realloc(ptr, old_layout, new_layout).is_ok() } { | |
return Some(ptr); | |
} | |
else { | |
let new_ptr = self.alloc(new_layout); | |
if let Some(new_ptr) = new_ptr { | |
unsafe { | |
let min_size = old_layout.size().min(new_layout.size()); | |
if min_size > 0 { | |
// both allocations are live and at least `min_size` large. | |
// live allocations can't overlap. | |
core::ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), min_size); | |
} | |
// invariants upheld by the caller. | |
self.free(ptr, old_layout); | |
} | |
} | |
// else: failed. keep old allocation live. | |
return new_ptr; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment