Hi rustaceans!
I wanted to build an arena allocator as a little exercise for myself and I came up with this:

use std::{alloc::{Layout, alloc, dealloc, handle_alloc_error}, ptr::NonNull}; use std::cell::Cell; pub struct Arena { ptr: Cell<NonNull<u8>>, ptr_pos: Cell<usize>, capacity: usize, } impl Arena { pub fn new(capacity: usize) -> Self { if capacity > isize::MAX as usize { panic!("cannot alloc more than isize::MAX bytes"); } if capacity == 0 { panic!("capacity cannot be 0"); } let layout = Layout::array::<u8>(capacity).expect("we checked if the capacity was less than isize::MAX byes"); // SAFETY: we checked if the capacity was 0 let ptr = unsafe { alloc(layout) }; if ptr.is_null() { handle_alloc_error(layout); } let ptr = Cell::new(NonNull::new(ptr).expect("we checked if ptr was null")); Self { ptr, ptr_pos: Cell::new(0), capacity, } } pub fn alloc<T>(&self, data: T) -> Option<&mut T> { self.ptr_pos.update(|pos| pos + size_of::<T>()); if self.ptr_pos.get() > self.capacity - 1 { return None; } let ptr = self.ptr.get().as_ptr() as *mut T; unsafe { ptr.write(data); } self.ptr.update(|ptr| unsafe { ptr.add(size_of::<T>()) }); // SAFETY: the pointer is valid non-null unsafe { Some(ptr.as_mut().expect("the pointer isn't null")) } } } impl Drop for Arena { fn drop(&mut self) { self.ptr.update(|ptr| unsafe {ptr.sub(self.ptr_pos.get())}); let layout = Layout::array::<u8>(self.capacity).expect("capacity isn't 0"); unsafe { dealloc(self.ptr.get().as_ptr(), layout); } } } 

but the problem with this Arena is that it doesn't align items properly and reading/writing from unaligned pointers is UB, so my question is: how can I make sure that my pointer is properly aligned for a type while still allowing many different types within one Arena.

5 posts - 2 participants

Read full topic