Confused with Layout allocation

I have a dynamically sized type which I'm currently allocating like the code below, but frame.payload.len() returns 32 when I call alloc(16). I'm probably doing something wrong, and I assume it's when casting the pointer to my struct, though I can't tell exactly what.

I understand it results in a fat pointer of size 32, if I call frame.payload.as_ptr() it returns a memory address that's 16 bytes longer than the frame pointer so I assumed len() would be the size of the remaining bytes.

Sorry if it's a dumb question, I'm not very familiar with manual memory allocation and pointer arithmetic.

#[repr(C)]
pub(crate) struct FrameHeader {
	correlation_id: u64,
	message_type: u16,
	payload_length: u16,
	protocol_version: u8,
	message_version: u8,
}

#[repr(C)]
pub(crate) struct Frame {
	header: FrameHeader,
	payload: [u8],
}

impl Frame {
	fn alloc(payload_length: u16) -> Box<Self> {
		let header_layout = Layout::new::<FrameHeader>();
		let payload_layout = Layout::array::<u8>(payload_length as usize).unwrap();
		let (frame_layout, _offset) = header_layout.extend(payload_layout).unwrap();
		let final_layout = frame_layout.pad_to_align();

		let mem_region_ptr = unsafe { alloc::alloc_zeroed(final_layout) };
		if mem_region_ptr.is_null() {
			alloc::handle_alloc_error(final_layout);
		}

		let len = size_of::<FrameHeader>() + payload_length as usize;
		let frame_ptr = ptr::slice_from_raw_parts_mut(mem_region_ptr, len) as *mut Frame;
		unsafe { Box::from_raw(frame_ptr) }
	}
}

you are using the wrong metadata for the fat pointer. you should use payload_length as usize directly instead, DO NOT add the size of the header.

to quote the documentation about pointer metadata:

[...]

  • For structs whose last field is a DST, metadata is the metadata for the last field
  • ...
  • For slice types like [T], metadata is the length in items as usize

[...]

2 Likes

also, check out the fambox crate. for example, with the help of fambox, you can rewrite the alloc function in safe rust like this:

#[repr(C)]
struct FrameHeader {
    payload_length: u16,
    //...
}

unsafe impl FamHeader for FrameHeader {
    type Element = u8;
    fn fam_len(&self) -> usize {
        self.payload_length as usize
    }
}

type BoxedFrame = FamBoxOwned<FrameHeader>;

fn alloc_zeroed_frame(payload_length: u16) -> BoxedFrame {
    // initialize the header
    let header = FrameHeader {
        payload_length,
        //...
    };
    // construct the FamBox, initialize the payload with zeros
    BoxedFrame::from_fn(header, |_idx| 0)
}

That's not safe Rust.

You can do it in safe Rust using the zerocopy crate.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.