You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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 u8 is safe here because we know the allocated memory is a contiguous block of bytes, and u8's pointer arithmetic works reliably for byte-level offsets.
  • Layout Storage: We store the full combined layout at the start of the allocation so free can retrieve the exact parameters needed for Alloc::dealloc.
  • Reverse Offset in Free: We undo the offset from alloc by 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 free is only called with pointers returned by alloc—passing arbitrary pointers will result in undefined behavior.

内容的提问来源于stack exchange,提问作者Tim Diekmann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:07:17