-
Notifications
You must be signed in to change notification settings - Fork 642
Description
The current implementation of the BackingBuffer which relies on a NonNull and unsafe operations introduces more unsafety into the GapBuffer than I believe is strictly necessary.
Here is the relevant code:
enum BackingBuffer {
VirtualMemory(NonNull<u8>, usize),
Vec(Vec<u8>),
}
impl Drop for BackingBuffer {
fn drop(&mut self) {
unsafe {
if let Self::VirtualMemory(ptr, reserve) = *self {
sys::virtual_release(ptr, reserve);
}
}
}
}After going through the codebase, my understanding is this is to allow for BackingBuffer::VirtualMemory to be obtained via Windows' VirtualAlloc and unix's memmap function as used in sys::unix::virtual_reserve and sys::windows::virtual_reserve.
I believe this unsafety could be avoided by making BackingBuffer::VirtualMemory wrap a vector Vec<u8, VirtualAllocator> which relies on a VirtualAllocator struct which implements Allocator.
This would allow the GapBuffer methods which currently rely on unsafe pointer arithmetic such as move_gap and delete_text to switch to using safe vector/slice operations such as copy_within.
Below I included and example of what a "VirtualAllocator" might look like:
#[derive(Debug, Clone, Copy,)]
struct VirtualAllocator;
unsafe impl Allocator for VirtualAllocator {
/// When defining the `layout` its size should be `sizeof::<T> * num` not
/// `sizeof::<T>`.
fn allocate(&self, layout: Layout,) -> Result<NonNull<[u8],>, AllocError,> {
let base = ptr::null_mut();
let len = layout.size();
unsafe {
let c_ptr = Memory::VirtualAlloc(base, len, Memory::MEM_RESERVE, Memory::PAGE_READWRITE,);
match NonNull::new(ptr::from_raw_parts_mut(c_ptr, len,),) {
Some(ptr,) => Ok(ptr,),
None => todo!(),
}
}
}
unsafe fn deallocate(&self, ptr: NonNull<u8,>, layout: std::alloc::Layout,) {
todo!()
}
}
static ALLOCATOR: VirtualAllocator = VirtualAllocator;
enum BackingBuffer {
VirtualMemory(Vec<u8, VirtualAllocator>,),
Vec(Vec<u8>),
}