After sketching a few designs and digging into Rust Atomics and Locks: Low-Level Concurrency in Practice by Mara Bos, I’ve finally locked down the Node component of the allocator.
The Node represents the smallest memory unit the allocator operates on. It holds the pointer to the memory chunk to be handed out.
I’ve benchmarked this at around 200 nanoseconds, compared to 2000+ nanoseconds for Windows’ HeapAlloc. By bypassing a context switch on my machine, the Node allocation logic gives a 10× speed-up, which is huge.
This is the only finalized part so far, but I’ll update benchmarks as other components come together. There’s still more testing to do and more structs to define for broader allocator functionality.
For now, I’m sharing a full screenshot of this section of code. The project is proprietary (not something I intend to police) if someone replicates it, but because of that, I won’t be publishing the complete codebase. Future examples will lean illustrative rather than literal.
That being said this isn’t the code verbatim either but it’s a lot of it and gives the whole mechanics of how a node allocates data and tracks meta. Hope this helps others ❤️
#[derive(Debug)]
pub struct Node<const HEADER_LEN: usize, const DATA_LEN: usize, const BUFFER_LEN: usize> {
header: [u8; HEADER_LEN],
data: [u8; DATA_LEN],
}
impl<const HEADER_LEN: usize, const DATA_LEN: usize, const BUFFER_LEN: usize>
Node<HEADER_LEN, DATA_LEN, BUFFER_LEN>
{
#[inline]
pub fn write_header_info_ptr<P>(
&mut self,
position_begin_as_usize: usize,
type_as_ptr: *const P,
) -> usize {
let position_end_as_usize = position_begin_as_usize + SYS_POINTER_WIDTH;
let type_as_usize = type_as_ptr as usize;
let type_as_byte_array = type_as_usize.to_ne_bytes();
let type_as_byte_slice = type_as_byte_array.as_slice();
let writable_header_section =
&mut self.header[position_begin_as_usize..position_end_as_usize];
writable_header_section.copy_from_slice(type_as_byte_slice);
position_end_as_usize
}
#[inline(always)]
pub fn read_header_info_ptr<P>(&self, position_begin_as_usize: usize) -> (usize, NonNull<P>) {
let position_end_as_usize = position_begin_as_usize + SYS_POINTER_WIDTH;
let readable_header_section = &self.header[position_begin_as_usize..position_end_as_usize];
let mut result_buf = [0u8; SYS_POINTER_WIDTH];
result_buf.copy_from_slice(readable_header_section);
let addr = usize::from_ne_bytes(result_buf);
let type_as_ptr = addr as *const P;
(position_end_as_usize, unsafe {
NonNull::new_unchecked(type_as_ptr as *mut P)
})
}
pub fn aligned_addr(&mut self, align: usize) -> Option<NonNull<u8>> {
let data_as_mut_ptr = self.data.as_mut_ptr();
let word_offset_as_mut_ptr = unsafe { data_as_mut_ptr.add(SYS_POINTER_WIDTH) };
let align_offset = word_offset_as_mut_ptr.align_offset(align);
let align_offset_as_mut_ptr = unsafe { word_offset_as_mut_ptr.add(align_offset) };
unsafe {
let header_as_ptr = &self.header as *const [u8; HEADER_LEN];
let header_as_usize = header_as_ptr as usize;
let header_as_byte_array = header_as_usize.to_ne_bytes();
let header_as_byte_slice = header_as_byte_array.as_slice();
let write_header = align_offset_as_mut_ptr.sub(SYS_POINTER_WIDTH) as *mut u8;
let write_buf = slice::from_raw_parts_mut(write_header, SYS_POINTER_WIDTH);
write_buf.copy_from_slice(header_as_byte_slice);
}
NonNull::new(align_offset_as_mut_ptr)
}
pub fn resolve_addr(
position_begin_as_usize: usize,
ptr: *mut u8,
head: *mut Self,
) -> NonNull<Self> {
let position_end = SYS_POINTER_WIDTH + position_begin_as_usize;
let header_as_ptr = unsafe { ptr.sub(SYS_POINTER_WIDTH) };
let header_as_slice = unsafe { slice::from_raw_parts(header_as_ptr, SYS_POINTER_WIDTH) };
let mut header_as_array = [0u8; SYS_POINTER_WIDTH];
header_as_array.copy_from_slice(header_as_slice);
let header_as_usize = usize::from_ne_bytes(header_as_array);
let header_as_mut_ptr = header_as_usize as *mut [u8; HEADER_LEN];
let header_as_slice = unsafe { &*header_as_mut_ptr };
let node_as_slice = &header_as_slice[position_begin_as_usize..position_end];
let mut node_buf_array = [0u8; SYS_POINTER_WIDTH];
node_buf_array.copy_from_slice(node_as_slice);
let node_as_usize = usize::from_ne_bytes(node_buf_array);
let node_as_ptr = node_as_usize as *const Self;
unsafe {
let head_as_usize = head as usize;
let head_as_byte_array = head_as_usize.to_ne_bytes();
let head_as_byte_slice = head_as_byte_array.as_slice();
let header_as_mut_ref = &mut *header_as_mut_ptr;
let header_first_word_as_mut_slice =
&mut header_as_mut_ref[SYS_POINTER_WIDTH * 7..SYS_POINTER_WIDTH * 8];
header_first_word_as_mut_slice.copy_from_slice(head_as_byte_slice);
}
unsafe { NonNull::new_unchecked(node_as_ptr as *mut _) }
}
}