Передать 2D-массив байтов на C в Rust

Я написал функцию на C с этой подписью:

write_stuff(char **pages, uint32 page_count);

pages будет массивом из 128 элементов, где каждый элемент либо NULL, либо указывает на массив символов размером 8192 байта. Итак, массив массивов с фиксированными, известными размерами.

Мне нужно заполнить массивы в Rust и передать их функции C. Когда я генерирую подпись Rust с помощью bindgen, получается следующее:

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

Теперь, как мне объявить переменную в Rust, которую я могу заполнить и передать этой функции?

Я пробовал это:

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

но он не компилируется.

Это компилируется:

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

но я не понимаю, будет ли он хранить данные в правильном формате или как правильно их использовать:

let pages: *mut *mut ::std::os::raw::c_char = myarray.as_mut_ptr() as // what goes here?;
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я считаю, что тип Rust, который вы ищете, это &[Option<&[u8; 8192]>]. Версия C использует двойной указатель, поэтому вам нужно убедиться, что внутренний тип является ссылкой. Option<&T> показывает, что ссылка может принимать значение NULL, а оптимизация нулевого указателя гарантирует, что она имеет тот же размер, что и T.

Я проверил, что приведенный ниже код работает должным образом, но я не уверен на 100%, что это преобразование типа правильно поэтому я был бы осторожен, используя это в производственном коде.

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>");
        }
    }
}

Спасибо, это выдающееся. Вы сделали все возможное.

ccleve 19.03.2022 20:01

Однако это правильно: а) лучше использовать &[Option<&[u8; 8192]>; 128] (дает компилятору больше знаний, а значит, улучшает оптимизацию). б) Если код C изменяет ссылки, это UB (поскольку они созданы из общих ссылок), а если нет, то лучше изменить их на *const (или const * в C).

Chayim Friedman 20.03.2022 22:42

Другие вопросы по теме