How to work with __IncompleteArrayField<u8> from bindgen

So bindgen gives these bindings:

pub struct some_buffer {
    pub len: size_t,
    pub data: __IncompleteArrayField<u8>,
};

from this C struct:

struct some_buffer {
    size_t len;
    uint8_t data[];
};

How do I work with this __IncompleteArrayField<u8> type? Should I even work with it directly, or am I supposed to create my own Struct and then raw pointer/type cast it? The C struct/buffer is pretty straightfoward, but I guess there's a good reason uint8_t data[] can't simply be mapped to something like a Vec<u8>.

For example, I have a function that takes a raw pointer of this buffer type and I need to allocate and assign it some u8s:

extern "C" some_func(output_buffer: *mut *mut some_buffer, ...) {
    // set some_buffer to 1, 2, 3, 4
    let values = vec![1u8, 2, 3, 4];
    ...
}

Thanks!

It looks like there's no nice way to work with it:

https://github.com/rust-lang/rust-bindgen/issues/1680#issuecomment-554296347

Appreciate the link, thanks!

That shows allocating but, maybe I'm missing something obvious here, it's still missing how to write the data. Rewriting that code snippet to look somewhat like my example above:

extern "C" fn some_func(output_buffer: *mut *mut some_buffer) {
    let count = 4;
    let layout = alloc::from_size_align(
        mem::size_of::<usize>() + count * mem::size_of::<u8>(),
        cmp::max(mem::align_of::<usize>(), mem::align_of::<u8>())
    ).unwrap();
    let value = alloc(layout) as *mut some_buffer;
    // write length and data
    // How?
}

Edit: __IncompleteArrayField<T> has as_mut_slice, I can probably use that one to get a raw mut u8 pointer and write data that way.

This is what I came up with. It compiles, and hopefully works :grinning_face_with_smiling_eyes: :

extern "C" fn some_func(output_buffer: *mut *mut some_buffer) {
    let values_to_write = vec![1u8, 2, 3, 4];
    unsafe {
        let layout = Layout::from_size_align(
            mem::size_of::<usize>() + values_to_write.len() * mem::size_of::<u8>(),
            cmp::max(mem::align_of::<usize>(), mem::align_of::<u8>())
        ).unwrap();
        let buffer = alloc(layout) as *mut some_buffer;

        // Write len
        (*buffer).len = values_to_write.len().try_into().unwrap();

        // Write data
        let data_slice = (*buffer).data.as_mut_slice(values_to_write.len());
        for i in 0..values_to_write.len() {
            data_slice[i] = values_to_write[i];
        }
        *output_buffer = buffer;
    }
}

That looks like about the best way to do it.

Is there an alternative to as_mut_slice() which gives you a raw pointer to the first element? If as_mut_slice() returns a &mut [u8] then you will be violating the requirement that references must refer to initialized data. Instead I'd prefer to do pointer arithmetic with buffer.data.as_mut_ptr().add(i).write(values_to_write[i]).

You can also clean up the Layout calculation by doing something like:

let length_field = Layout::new::<usize>();
let data_field = Layout::array::<u8>(values_to_write.len());
let overall_layout = length_field.extend(data_field);

That way you aren't manually calculating alignment and sizes.

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.