如何为NonNull<Opaque>类型指针添加或减去内存偏移量?
Fixing Pointer Offset for Allocator-API's NonNull
Got it, let's work through this issue with the updated allocator-api. The core problem is that NonNull<Opaque> can't use add() or sub() directly because Opaque is an unsized type. The workaround here is to convert the opaque pointer to a *mut u8 (a sized type that supports pointer arithmetic) to handle the offset logic, then convert back when needed.
Here's the updated implementation for both alloc and free functions, with safety explanations:
#![feature(allocator_api)] #![no_std] extern crate libc; use core::alloc::{Alloc, Layout, NonNull}; use libc::c_void; unsafe extern "system" fn alloc<A: Alloc>( size: usize, alignment: usize, ) -> *mut c_void { let requested_layout = Layout::from_size_align(size, alignment).unwrap(); // Calculate combined layout: Layout header + requested memory block let (combined_layout, padding) = Layout::new::<Layout>() .extend_packed(requested_layout) .unwrap(); // Allocate the total required memory let allocated_ptr = A::alloc(combined_layout).unwrap(); // Convert NonNull<Opaque> to *mut u8 to perform valid pointer arithmetic let ptr_as_u8 = allocated_ptr.as_ptr() as *mut u8; // Write the combined layout to the start of the block (for free to retrieve) (ptr_as_u8 as *mut Layout).write(combined_layout); // Offset past the Layout header + padding to get the user-facing pointer let user_ptr = ptr_as_u8.add(padding); // Convert to *mut c_void as required by the function signature user_ptr as *mut c_void } unsafe extern "system" fn free<A: Alloc>( memory: *mut c_void, ) { // Convert user's pointer back to *mut u8 for reverse arithmetic let user_ptr = memory as *mut u8; // Calculate the padding offset to get back to the start of the allocated block let header_layout = Layout::new::<Layout>(); // Dummy layout used here only to compute padding for the header let (_, padding) = header_layout.extend_packed(Layout::from_size_align(0, 1).unwrap()).unwrap(); // Move back past the padding to reach the stored Layout let header_ptr = user_ptr.sub(padding); // Read the combined layout we stored during allocation let stored_layout = (header_ptr as *const Layout).read(); // Convert back to NonNull<Opaque> for deallocation let dealloc_ptr = NonNull::new(header_ptr as *mut _).unwrap(); // Deallocate using the original combined layout A::dealloc(dealloc_ptr, stored_layout); }
Key Details & Safety Notes:
- Pointer Conversion: Casting
NonNull<Opaque>to*mut u8is safe here because we know the allocated memory is a contiguous block of bytes, andu8's pointer arithmetic works reliably for byte-level offsets. - Layout Storage: We store the full combined layout at the start of the allocation so
freecan retrieve the exact parameters needed forAlloc::dealloc. - Reverse Offset in Free: We undo the offset from
allocby subtracting the same padding value, ensuring we land exactly at the start of the allocated block where the layout is stored. - Invariant Requirement: This code assumes
freeis only called with pointers returned byalloc—passing arbitrary pointers will result in undefined behavior.
内容的提问来源于stack exchange,提问作者Tim Diekmann
相关产品推荐
相关产品推荐

