From ee0f0558947970043c09370153dae5cdeb2baae0 Mon Sep 17 00:00:00 2001 From: zhangyuang Date: Fri, 20 Oct 2023 13:06:10 +0800 Subject: [PATCH] feat: support struct field type i32Array --- README.md | 42 +++++++++++++++++++------------------ README_Zh.md | 43 ++++++++++++++++++++------------------ bench/bench.js | 12 +++++------ cpp/sum.cpp | 2 ++ src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++------------- src/utils.rs | 26 +++++++++++++++++++++-- test.ts | 15 ++++++-------- 7 files changed, 125 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index bbbf8c6..a68a8c1 100644 --- a/README.md +++ b/README.md @@ -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" @@ -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', @@ -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', @@ -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) ``` diff --git a/README_Zh.md b/README_Zh.md index d5536fe..061105a 100644 --- a/README_Zh.md +++ b/README_Zh.md @@ -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" @@ -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', @@ -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', @@ -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) + ``` diff --git a/bench/bench.js b/bench/bench.js index ac8d74b..8888009 100644 --- a/bench/bench.js +++ b/bench/bench.js @@ -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', diff --git a/cpp/sum.cpp b/cpp/sum.cpp index ba1d35c..f594d6b 100644 --- a/cpp/sum.cpp +++ b/cpp/sum.cpp @@ -54,6 +54,7 @@ typedef struct Person { double doubleProps; char **stringArray; double *doubleArray; + int *i32Array; // Parent parent; } Person; @@ -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; diff --git a/src/lib.rs b/src/lib.rs index 484b2c2..9d94430 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, }; @@ -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); @@ -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); @@ -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::("length") + .unwrap(), + ) as usize + } else { + 0 + }; match data_type { DataType::I32 => { let type_field_ptr = field_ptr as *mut i32; @@ -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::("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); @@ -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::("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); @@ -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", diff --git a/src/utils.rs b/src/utils.rs index 620d1cf..d2a6863 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -57,7 +57,11 @@ pub fn js_array_to_string_array(js_array: JsObject) -> Vec { .collect::>() } -pub fn js_array_to_double_array(js_array: JsObject) -> Vec { +pub fn js_array_to_number_array(js_array: JsObject) -> Vec +where + T: TryFrom, + >::Error: std::fmt::Debug, +{ vec![0; js_array.get_array_length().unwrap() as usize] .iter() .enumerate() @@ -65,7 +69,7 @@ pub fn js_array_to_double_array(js_array: JsObject) -> Vec { let js_element: JsNumber = js_array.get_element(index as u32).unwrap(); return js_element.try_into().unwrap(); }) - .collect::>() + .collect::>() } pub fn align_ptr(ptr: *mut c_void, align: usize) -> *mut c_void { @@ -110,6 +114,11 @@ pub fn calculate_layout(map: &IndexMap) -> (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) @@ -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) } @@ -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"), } } diff --git a/test.ts b/test.ts index 32938e2..8a58b54 100644 --- a/test.ts +++ b/test.ts @@ -55,13 +55,7 @@ const unitTest = () => { paramsType: [DataType.I32Array, DataType.I32], paramsValue: [bigArr, bigArr.length], })) - // let foo = load({ - // library: 'libsum', - // funcName: 'createArrayi32', - // retType: arrayConstructor({ type: DataType.I32Array, length: bigArr.length }), - // paramsType: [DataType.I32Array, DataType.I32], - // paramsValue: [bigArr, bigArr.length], - // }) + let bigDoubleArr = new Array(5).fill(1.1) deepStrictEqual(bigDoubleArr, load({ library: 'libsum', @@ -92,7 +86,8 @@ const unitTest = () => { age: 23, doubleProps: 1.1, stringArray: ["foo", "bar"], - doubleArray: [1.1, 2.2, 3.3] + doubleArray: [1.1, 2.2, 3.3], + i32Array: [1, 2, 3, 4] } const personObj = load({ library: 'libsum', @@ -102,7 +97,8 @@ const unitTest = () => { age: DataType.I32, doubleProps: DataType.Double, stringArray: arrayConstructor({ type: DataType.StringArray, length: person.stringArray.length }), - doubleArray: arrayConstructor({ type: DataType.DoubleArray, length: person.doubleArray.length }) + doubleArray: arrayConstructor({ type: DataType.DoubleArray, length: person.doubleArray.length }), + i32Array: arrayConstructor({ type: DataType.I32Array, length: person.i32Array.length }), }, paramsType: [{ name: DataType.String, @@ -110,6 +106,7 @@ const unitTest = () => { doubleProps: DataType.Double, stringArray: DataType.StringArray, doubleArray: DataType.DoubleArray, + i32Array: DataType.I32Array, }], paramsValue: [person] })