Skip to content

Commit

Permalink
feat: support struct field type i32Array
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangyuang committed Oct 20, 2023
1 parent 48d9749 commit ee0f055
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 71 deletions.
42 changes: 22 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Then can use `ffi-rs` invoke the dynamic library file contains functions.

```js
const { equal } = require('assert')
const { load, DataType, open, close } = require('ffi-rs')
const { load, DataType, open, close, arrayConstructor } = require('ffi-rs')
const a = 1
const b = 100
const dynamicLib = platform === 'win32' ? './sum.dll' : "./libsum.so"
Expand Down Expand Up @@ -188,36 +188,32 @@ equal(1.1 + 2.2, load({
paramsType: [DataType.Double, DataType.Double],
paramsValue: [1.1, 2.2]
}))

let bigArr = new Array(100).fill(100)
equal(bigArr[0], load({
deepStrictEqual(bigArr, load({
library: 'libsum',
funcName: 'createArrayi32',
retType: DataType.I32Array,
retType: arrayConstructor({ type: DataType.I32Array, length: bigArr.length }),
paramsType: [DataType.I32Array, DataType.I32],
paramsValue: [bigArr, bigArr.length],
retTypeLen: bigArr.length
})[0])
}))

let bigDoubleArr = new Array(100).fill(1.1)
equal(bigDoubleArr[0], load({
let bigDoubleArr = new Array(5).fill(1.1)
deepStrictEqual(bigDoubleArr, load({
library: 'libsum',
funcName: 'createArrayDouble',
retType: DataType.DoubleArray,
retType: arrayConstructor({ type: DataType.DoubleArray, length: bigDoubleArr.length }),
paramsType: [DataType.DoubleArray, DataType.I32],
paramsValue: [bigDoubleArr, bigDoubleArr.length],
retTypeLen: bigDoubleArr.length
})[0])
}))
let stringArr = [c, c.repeat(20)]

let stringArr = [c, c.repeat(200)]
equal(stringArr[0], load({
deepStrictEqual(stringArr, load({
library: 'libsum',
funcName: 'createArrayString',
retType: DataType.StringArray,
retType: arrayConstructor({ type: DataType.StringArray, length: stringArr.length }),
paramsType: [DataType.StringArray, DataType.I32],
paramsValue: [stringArr, stringArr.length],
retTypeLen: stringArr.length
})[0])
}))
const bool_val = true
equal(!bool_val, load({
library: 'libsum',
Expand All @@ -226,11 +222,13 @@ equal(!bool_val, load({
paramsType: [DataType.Boolean],
paramsValue: [bool_val],
}))

const person = {
name: 'tom',
age: 23,
doubleProps: 1.1,
stringArray: ["foo", "bar"],
doubleArray: [1.1, 2.2, 3.3],
i32Array: [1, 2, 3, 4]
}
const personObj = load({
library: 'libsum',
Expand All @@ -239,16 +237,20 @@ const personObj = load({
name: DataType.String,
age: DataType.I32,
doubleProps: DataType.Double,
stringArray: arrayConstructor({ type: DataType.StringArray, length: person.stringArray.length }),
doubleArray: arrayConstructor({ type: DataType.DoubleArray, length: person.doubleArray.length }),
i32Array: arrayConstructor({ type: DataType.I32Array, length: person.i32Array.length }),
},
paramsType: [{
name: DataType.String,
age: DataType.I32,
doubleProps: DataType.Double,
stringArray: DataType.StringArray,
doubleArray: DataType.DoubleArray,
i32Array: DataType.I32Array,
}],
paramsValue: [person]
})
equal(person.name, personObj.name)
equal(person.age, personObj.age)
equal(person.doubleProps, personObj.doubleProps)
deepStrictEqual(person, personObj)

```
43 changes: 23 additions & 20 deletions README_Zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ $ g++ -shared -o sum.dll cpp/sum.cpp # win

```js
const { equal } = require('assert')
const { load, DataType, open } = require('ffi-rs')
const { load, DataType, open, close, arrayConstructor } = require('ffi-rs')
const a = 1
const b = 100
const dynamicLib = platform === 'win32' ? './sum.dll' : "./libsum.so"
Expand Down Expand Up @@ -185,36 +185,32 @@ equal(1.1 + 2.2, load({
paramsType: [DataType.Double, DataType.Double],
paramsValue: [1.1, 2.2]
}))

let bigArr = new Array(100).fill(100)
equal(bigArr[0], load({
deepStrictEqual(bigArr, load({
library: 'libsum',
funcName: 'createArrayi32',
retType: DataType.I32Array,
retType: arrayConstructor({ type: DataType.I32Array, length: bigArr.length }),
paramsType: [DataType.I32Array, DataType.I32],
paramsValue: [bigArr, bigArr.length],
retTypeLen: bigArr.length
})[0])
}))

let bigDoubleArr = new Array(100).fill(1.1)
equal(bigDoubleArr[0], load({
let bigDoubleArr = new Array(5).fill(1.1)
deepStrictEqual(bigDoubleArr, load({
library: 'libsum',
funcName: 'createArrayDouble',
retType: DataType.DoubleArray,
retType: arrayConstructor({ type: DataType.DoubleArray, length: bigDoubleArr.length }),
paramsType: [DataType.DoubleArray, DataType.I32],
paramsValue: [bigDoubleArr, bigDoubleArr.length],
retTypeLen: bigDoubleArr.length
})[0])
}))
let stringArr = [c, c.repeat(20)]

let stringArr = [c, c.repeat(200)]
equal(stringArr[0], load({
deepStrictEqual(stringArr, load({
library: 'libsum',
funcName: 'createArrayString',
retType: DataType.StringArray,
retType: arrayConstructor({ type: DataType.StringArray, length: stringArr.length }),
paramsType: [DataType.StringArray, DataType.I32],
paramsValue: [stringArr, stringArr.length],
retTypeLen: stringArr.length
})[0])
}))
const bool_val = true
equal(!bool_val, load({
library: 'libsum',
Expand All @@ -223,11 +219,13 @@ equal(!bool_val, load({
paramsType: [DataType.Boolean],
paramsValue: [bool_val],
}))

const person = {
name: 'tom',
age: 23,
doubleProps: 1.1,
stringArray: ["foo", "bar"],
doubleArray: [1.1, 2.2, 3.3],
i32Array: [1, 2, 3, 4]
}
const personObj = load({
library: 'libsum',
Expand All @@ -236,15 +234,20 @@ const personObj = load({
name: DataType.String,
age: DataType.I32,
doubleProps: DataType.Double,
stringArray: arrayConstructor({ type: DataType.StringArray, length: person.stringArray.length }),
doubleArray: arrayConstructor({ type: DataType.DoubleArray, length: person.doubleArray.length }),
i32Array: arrayConstructor({ type: DataType.I32Array, length: person.i32Array.length }),
},
paramsType: [{
name: DataType.String,
age: DataType.I32,
doubleProps: DataType.Double,
stringArray: DataType.StringArray,
doubleArray: DataType.DoubleArray,
i32Array: DataType.I32Array,
}],
paramsValue: [person]
})
equal(person.name, personObj.name)
equal(person.age, personObj.age)
equal(person.doubleProps, personObj.doubleProps)
deepStrictEqual(person, personObj)

```
12 changes: 6 additions & 6 deletions bench/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ async function run() {
libm.sum(1, 2);
libm.concatenateStrings("foo", "bar");
}),
// b.add('koffi', () => {
// const sum = koffilib.func('int sum(int a, int b)');
// const concatenateStrings = koffilib.func('const char *concatenateStrings(const char *str1, const char *str2)');
// sum(1, 2)
// concatenateStrings("foo", "bar")
// }),
b.add('koffi', () => {
const sum = koffilib.func('int sum(int a, int b)');
const concatenateStrings = koffilib.func('const char *concatenateStrings(const char *str1, const char *str2)');
sum(1, 2)
concatenateStrings("foo", "bar")
}),
b.add('ffi-rs', () => {
load({
library: 'libsum',
Expand Down
2 changes: 2 additions & 0 deletions cpp/sum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef struct Person {
double doubleProps;
char **stringArray;
double *doubleArray;
int *i32Array;
// Parent parent;
} Person;

Expand All @@ -65,6 +66,7 @@ extern "C" const Person *getStruct(const Person *person) {
printf("stringArray: %s\n", person->stringArray[1]);
printf("doubleArray: %f\n", person->doubleArray[0]);
printf("doubleArray: %f\n", person->doubleArray[1]);
printf("i32Array: %d\n", person->i32Array[0]);
// printf("Parent Age: %d\n", person->parent.age);
// printf("Parent Name: %s\n", person->parent.name);
return person;
Expand Down
56 changes: 42 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use define::{number_to_data_type, DataType, FFIParams, OpenParams, RsArgsValue};
use napi::bindgen_prelude::*;
use utils::{
align_ptr, calculate_layout, create_array_from_pointer, get_data_type_size_align,
get_js_function_call_value, js_array_to_double_array, js_array_to_string_array,
get_js_function_call_value, js_array_to_number_array, js_array_to_string_array,
js_nunmber_to_i32, js_string_to_string, js_unknown_to_data_type, rs_array_to_js_array,
ArrayPointerType, ArrayType,
};
Expand Down Expand Up @@ -192,9 +192,15 @@ fn load(
DataType::DoubleArray => {
let js_array: JsObject =
params_value_object.get_named_property(&field).unwrap();
let arg_val = js_array_to_double_array(js_array);
let arg_val = js_array_to_number_array(js_array);
RsArgsValue::DoubleArray(arg_val)
}
DataType::I32Array => {
let js_array: JsObject =
params_value_object.get_named_property(&field).unwrap();
let arg_val = js_array_to_number_array(js_array);
RsArgsValue::I32Array(arg_val)
}
// DataType::Object => {
// let val: JsObject = js_object.get_named_property(&field).unwrap();
// let index_map = jsobject_to_rs_struct(val);
Expand Down Expand Up @@ -361,6 +367,13 @@ fn load(
as *mut c_void;
field_ptr = align_ptr(field_ptr, align);
}
RsArgsValue::I32Array(arr) => {
(field_ptr as *mut *const c_int).write(arr.as_ptr());
std::mem::forget(arr);
field_ptr =
field_ptr.offset(std::mem::size_of::<*const c_int>() as isize) as *mut c_void;
field_ptr = align_ptr(field_ptr, align);
}
RsArgsValue::Object(val) => {
let (size, _) = calculate_layout(&val);
write_data(val, field_ptr, align);
Expand Down Expand Up @@ -597,6 +610,16 @@ fn load(
.for_each(|field| {
let val: JsUnknown = ret_object.get_named_property(&field).unwrap();
let data_type = js_unknown_to_data_type(val);
let array_constructor: JsObject = ret_object.get_named_property(&field).unwrap();
let array_len: usize = if array_constructor.has_named_property("length").unwrap() {
js_nunmber_to_i32(
array_constructor
.get_named_property::<JsNumber>("length")
.unwrap(),
) as usize
} else {
0
};
match data_type {
DataType::I32 => {
let type_field_ptr = field_ptr as *mut i32;
Expand Down Expand Up @@ -636,12 +659,6 @@ fn load(
field_ptr = align_ptr(field_ptr, align);
}
DataType::StringArray => {
let array_constructor: JsObject = ret_object.get_named_property(&field).unwrap();
let array_len: usize = js_nunmber_to_i32(
array_constructor
.get_named_property::<JsNumber>("length")
.unwrap(),
) as usize;
let type_field_ptr = field_ptr as *mut *mut *mut c_char;
let arr =
create_array_from_pointer(ArrayPointerType::String(*type_field_ptr), array_len);
Expand All @@ -659,12 +676,6 @@ fn load(
field_ptr = align_ptr(field_ptr, align);
}
DataType::DoubleArray => {
let array_constructor: JsObject = ret_object.get_named_property(&field).unwrap();
let array_len: usize = js_nunmber_to_i32(
array_constructor
.get_named_property::<JsNumber>("length")
.unwrap(),
) as usize;
let type_field_ptr = field_ptr as *mut *mut c_double;
let arr =
create_array_from_pointer(ArrayPointerType::Double(*type_field_ptr), array_len);
Expand All @@ -681,6 +692,23 @@ fn load(
as *mut c_void;
field_ptr = align_ptr(field_ptr, align);
}
DataType::I32Array => {
let type_field_ptr = field_ptr as *mut *mut c_int;
let arr =
create_array_from_pointer(ArrayPointerType::I32(*type_field_ptr), array_len);
match arr {
ArrayType::I32(arr) => {
let js_array = rs_array_to_js_array(env, ArrayType::I32(arr));
js_object
.set_property(env.create_string(&field).unwrap(), js_array)
.unwrap();
}
_ => panic!("some error"),
}
field_ptr =
field_ptr.offset(std::mem::size_of::<*const c_int>() as isize) as *mut c_void;
field_ptr = align_ptr(field_ptr, align);
}

_ => panic!(
"{:?} is not available as a field type at this time",
Expand Down
26 changes: 24 additions & 2 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,19 @@ pub fn js_array_to_string_array(js_array: JsObject) -> Vec<String> {
.collect::<Vec<String>>()
}

pub fn js_array_to_double_array(js_array: JsObject) -> Vec<f64> {
pub fn js_array_to_number_array<T>(js_array: JsObject) -> Vec<T>
where
T: TryFrom<JsNumber>,
<T as TryFrom<JsNumber>>::Error: std::fmt::Debug,
{
vec![0; js_array.get_array_length().unwrap() as usize]
.iter()
.enumerate()
.map(|(index, _)| {
let js_element: JsNumber = js_array.get_element(index as u32).unwrap();
return js_element.try_into().unwrap();
})
.collect::<Vec<f64>>()
.collect::<Vec<T>>()
}

pub fn align_ptr(ptr: *mut c_void, align: usize) -> *mut c_void {
Expand Down Expand Up @@ -110,6 +114,11 @@ pub fn calculate_layout(map: &IndexMap<String, RsArgsValue>) -> (usize, usize) {
let size = size + std::mem::size_of::<*const c_double>();
(size, align)
}
RsArgsValue::I32Array(_) => {
let align = align.max(std::mem::align_of::<*const c_int>());
let size = size + std::mem::size_of::<*const c_int>();
(size, align)
}
_ => panic!("calculate_layout"),
});
(size, align)
Expand All @@ -134,6 +143,10 @@ pub fn get_data_type_size_align(data_type: DataType) -> (usize, usize) {
std::mem::size_of::<*const c_double>(),
std::mem::align_of::<*const c_double>(),
),
DataType::I32Array => (
std::mem::size_of::<*const c_int>(),
std::mem::align_of::<*const c_int>(),
),
_ => {
panic!("{:?} Not available as a field type at this time", data_type)
}
Expand Down Expand Up @@ -238,6 +251,15 @@ pub fn rs_array_to_js_array(env: Env, val: ArrayType) -> JsObject {
});
js_array
}
ArrayType::I32(arr) => {
let mut js_array = env.create_array_with_length(arr.len()).unwrap();
arr.into_iter().enumerate().for_each(|(index, item)| {
js_array
.set_element(index as u32, env.create_int32(item).unwrap())
.unwrap();
});
js_array
}
_ => panic!("some error"),
}
}
Loading

0 comments on commit ee0f055

Please sign in to comment.