#javascript #node.js #c #rust #ffi
#javascript #node.js #c #Ржавчина #ffi
Вопрос:
Я пытаюсь вернуть структуру из функции Rust в Node.js , структура является вложенной и содержит массив, поэтому она достаточно сложна, поэтому я хочу выполнить выделение в Rust и иметь Node.JS получает полный объект, вот краткий код Rust:
State
Возвращается A, содержащий число Config
s, сообщение (например, сообщение об ошибке или какое-либо предупреждение) и несколько конфигураций.
use std::ffi::CStr;
use std::os::raw::c_char;
#[derive(Debug)]
#[repr(C)]
pub struct Config {
pub some_fields_here: String,
// pub ...
}
#[derive(Debug)]
#[repr(C)]
pub struct State {
pub message: String, // Even changing this to *mut c_char doesn't help the immediate problem
pub configs: *mut Config,
pub num_configs: usize,
}
#[no_mangle]
pub extern "C" fn config_from_file(_not_really_used_in_example: *const c_char) -> *mut Configuration {
let mut configs: Vec<Config> = vec![];
configs.push(Config {
some_fields_here: String::from("hello world"), // should maybe be CString::from(...).into_raw()
});
Box::into_raw(Box::new(State {
message: String::from("a message here"),
configs: Box::into_raw(configs.into_boxed_slice()) as *mut Config,
num_configs: 1,
}))
}
Со стороны узла все примеры и документы, которые мы нашли, используют только StructType
для подготовки чего-либо для передачи в FFI или для прозрачной передачи, даже не допрашивая его.
Что мы хотели бы сделать, так это:
var ffi = require("ffi-napi");
var ref = require("ref-napi");
var StructType = require("ref-struct-di")(ref);
var ArrayType = require("ref-array-di")(ref);
const Config = StructType({
some_fields_here: ref.types.CString,
});
const State = StructType({
message: ref.types.CString,
configs: ref.refType(ArrayType(Config)),
num_configs: ref.types.size_t,
});
const StatePtr = ref.refType(StatePtr);
var ourlib = ffi.Library("./target/debug/ourlib", {
config_from_file: [StatePtr, [ref.types.CString]],
});
const ffiResult = ourlib.config_from_file("a file path, in the real code");
console.log(ffiResult)
// => <Buffer@0x47ce560 20 e2 83 04 00 00 00 00 57 00 00 00 00 00 00 00 57 00 00 00 00 00 00 00,
type: { [Function: StructType] defineProperty: [Function: defineProperty], toString: [Function:
toString], fields: { message: [Object], configs: [Object], num_configs: [Object] }, size: 24,
alignment: 8, indirection: 1, isPacked: false, get: [Function: get], set: [Function: set] }>
Здесь нам нужно получить объект Javscript, похожий на описанную выше структуру, доступ
к любым полям или увидеть его как объект. Мы не нашли подходящих примеров в тестах или документации https://tootallnate.github.io/ref , ни в https://github.com/node-ffi-napi/ref-struct-di
Я не ожидаю, что у меня будет чистый переход от объектов C, поддерживаемых Buffer()
в JavaScript, конечно, мы должны декодировать и перемещать вещи, но я не могу найти никакого способа получить доступ к информации, содержащейся в этой структуре, из JavaScript. Кажется, что никакая комбинация каких-либо deref()
или toObject()
или получение отдельных полей вообще не помогает.
Я ожидал, что что-то подобное может это сделать, но это либо / или ошибки seg, либо выводит мусор в зависимости от некоторых незначительных настроек, которые я делаю так или иначе:
console.log(ffiResult);
console.log(ref.deref().readCString(ffiResult, 0));
Я знаю, что в некоторой степени выделение в библиотеке и распаковка этих структур на языке хоста является однотипным, но из-за формы данных и того, что мы не можем изменить код библиотеки, у нас нет особого выбора.
Комментарии:
1. Вы смотрели на neon ?
2. Действительно, мы решили эту проблему, и ваше приглашение напоминает мне вернуться и ответить на мой собственный вопрос. Короче говоря, нет, мы этого не сделали, потому что мы специально ориентируемся на FFI, потому что наша вещь должна работать с Ruby, Node.js и Python (и, другие). Neon — это генератор привязки NAPI только между Node и Rust, поэтому он не соответствует нашим потребностям.
Ответ №1:
и содержит массив, поэтому он достаточно сложный, поэтому я хочу выполнить выделение в Rust и иметь Node.JS получает полный объект,
Содержание массива было сложной / сложной частью. Массивы в C (следовательно, FFI), как правило, заканчиваются нулем, но нет никакой гарантии, что все работает определенным образом для массивов структур.
Вот что нам нужно было сделать:
- В Rust предоставьте структуру с элементом длины и указателем на один элемент.
- В Rust выделите вектор наших вещей и используйте
into_boxed_slice()
, чтобы забыть его, это дает указатель на первый. - Заполните свойство length в нашей структуре
Все, как в примере, который мы опубликовали первым.
В node.js однако это немного отличается, рабочий код был почти идентичен тому, что у нас было, вот типы:
const Config = StructType({
some_fields_here: ref.types.CString,
});
const State = StructType({
message: ref.types.CString,
configs: ArrayType(Config), // we had refType(ArrayType(Config))
num_configs: ref.types.size_t,
});
const StatePtr = ref.refType(StatePtr);
var ourlib = ffi.Library("./target/debug/ourlib", {
config_f...
С этим внесенным изменением единственное, что массив непригоден для использования, пока не будет инициализирована длина, поэтому позже нам просто пришлось это сделать:
const ffiResult = ourlib.config_from_file("a file path, in the real code");
const ffiState = ffiResult.deref(); // it's a pointer, remember.
ffiState.configs.length = ffiState.num_configs; // see comment below
После установки длины ArrayType
then его можно использовать более или менее точно так же, как обычный массив.
Кикером был этот commit ( dc0d7296
) примерно десятилетней давности!.