Pass 2D array of bytes to C in Rust

1k Views Asked by At

I've written a function in C with this signature:

write_stuff(char **pages, uint32 page_count);

pages will be an array of 128 elements, with each element either NULL or pointing to a char array of 8192 bytes. So, an array of arrays with fixed, known sizes.

I need to populate the arrays in Rust and pass them to the C function. When I generate a Rust signature using bindgen, it produces this:

extern "C" {
    pub fn write_stuff(pages: *mut *mut ::std::os::raw::c_char, page_count: u32,);
}

Now, how do I declare a variable in Rust that I can populate and pass to this function?

I tried this:

let mut pages = [Option <[u8; 8192]>; 128];

but it doesn't compile.

This does compile:

type MyPage = Option<[u8; 8192]>;
let myarray: [MyPage; 128] = [None; 128];

but I'm not clear on whether it would store the data in the right format, or how to cast it appropriately:

let pages: *mut *mut ::std::os::raw::c_char = myarray.as_mut_ptr() as // what goes here?;
1

There are 1 best solutions below

3
On BEST ANSWER

I believe the Rust type you're looking for is &[Option<&[u8; 8192]>]. The C version uses a double pointer, so you need to make sure the inner type is a reference . Option<&T> shows that the reference is nullable and the null pointer optimization guarantees that it has the same size as T.

I've tested that the code below works as expected, but I'm not 100% sure this type conversion is sound so I'd be careful using this in production code.

Rust main.rs

#[link(name = "mylib")]
extern "C" {
    pub fn write_stuff(pages: *mut *mut ::std::os::raw::c_char, page_count: u32);
}

pub fn write_stuff_rust(pages: &[Option<&[u8; 8192]>]) {
    unsafe { write_stuff(pages.as_ptr() as *mut _, pages.len() as u32) }
}

fn main() {
    let page1: [u8; 8192] = [b'a'; 8192];
    let page3: [u8; 8192] = [b'b'; 8192];
    let page4: [u8; 8192] = [b'c'; 8192];
    let page7: [u8; 8192] = [b'd'; 8192];

    let pages: Vec<Option<&[u8; 8192]>> = vec![
        Some(&page1),
        None,
        Some(&page3),
        Some(&page4),
        None,
        None,
        Some(&page7),
    ];

    write_stuff_rust(&pages);
}

C mylib.c

#include <stdio.h>
#include <stdint.h>

void write_stuff(char **pages, uint32_t page_count) {
    for (uint32_t i = 0; i < page_count; i++) {
        printf("%d=%p\n", i, pages[i]);
        if (pages[i]) {
            for (size_t j = 0; j < 8192; j++) {
                putchar(pages[i][j]);
            }
            putchar('\n');
        } else {
            puts("<empty>");
        }
    }
}