From e60faf7d6bcefbe0e3aa3a2f585f8b7b29fefa55 Mon Sep 17 00:00:00 2001 From: zhangyuang Date: Tue, 17 Dec 2024 13:44:13 +0800 Subject: [PATCH] feat: support struct array --- .github/workflows/CI.yml | 6 +- .vscode/settings.json | 8 ++ Cargo.toml | 12 ++- cpp/sum.cpp | 99 ++++++++++++++----- scripts/types.d.ts | 1 + src/datatype/create_struct.rs | 67 +++++++++---- src/datatype/function.rs | 3 + src/datatype/pointer.rs | 86 +++++++++++++++-- src/datatype/restore_struct.rs | 45 ++++++++- src/define.rs | 20 ++-- src/lib.rs | 56 +---------- src/utils/array.rs | 24 +++++ src/utils/dataprocess.rs | 167 +++++++++++++++++++++++---------- src/utils/mod.rs | 4 +- src/utils/object_utils.rs | 11 ++- src/utils/pointer.rs | 56 +++++++++++ tests/index.ts | 4 +- tests/struct.ts | 62 +++++++++++- 18 files changed, 561 insertions(+), 170 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/utils/array.rs create mode 100644 src/utils/pointer.rs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d98dbfd..40988d5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -48,7 +48,7 @@ jobs: build: |- set -e && target=x86_64-unknown-linux-gnu yarn build && - strip *.node && + strip *.node && yarn test && ls - host: macos-latest target: aarch64-apple-darwin @@ -72,7 +72,7 @@ jobs: ls # - host: ubuntu-latest # target: arm-unknown-linux-gnueabihf - + - host: ubuntu-latest target: arm-unknown-linux-gnueabihf docker: ghcr.io/catthehacker/ubuntu:custom-20.04-dev @@ -86,7 +86,7 @@ jobs: ls - host: ubuntu-latest target: x86_64-unknown-linux-musl - docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2023-09-17-alpine + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine build: | set -e && uname -a && apk update && apk add linux-headers && diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a87f5f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.formatOnSave": true, + "[rust]": { + "editor.formatOnSave": true, + }, + "files.trimTrailingWhitespace": true + +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 285bb25..d6a58fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,25 +9,27 @@ crate-type = ["cdylib"] [dependencies] # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix -napi = { version = "2.16.13", default-features = false, features = ["napi4", "napi6"] } +napi = { version = "2.16.13", default-features = false, features = [ + "napi4", + "napi6", +] } napi-derive = "2.13.0" napi-derive-backend = "1.0.52" dlopen = "0.1.8" libffi = { version = "3.2.0" } libffi-sys = { version = "^2.3.0" } libc = "0.2" -indexmap = "2.0" +indexmap = "2.7.0" widestring = "1.1.0" strum = "0.26" strum_macros = "0.26" [build-dependencies] -napi-build = "2.0.1" -bindgen = "^0.66.0" +napi-build = "=2.0.1" [profile.release] lto = true [lints.rust] -static-mut-refs="allow" +static-mut-refs = "allow" diff --git a/cpp/sum.cpp b/cpp/sum.cpp index 3777b2d..7080e8d 100644 --- a/cpp/sum.cpp +++ b/cpp/sum.cpp @@ -11,23 +11,27 @@ extern "C" double doubleSum(double a, double b) { return a + b; } extern "C" double floatSum(float a, float b) { return a + b; } -extern "C" int64_t testbigint(int64_t a) { - return a; +extern "C" int64_t testbigint(int64_t a) +{ + return a; } -extern "C" const char *concatenateStrings(const char *str1, const char *str2) { +extern "C" const char *concatenateStrings(const char *str1, const char *str2) +{ std::string result = std::string(str1) + std::string(str2); char *cstr = new char[result.length() + 1]; strcpy(cstr, result.c_str()); return cstr; } -extern "C" void* returnNullPointer() { - return NULL; +extern "C" void *returnNullPointer() +{ + return NULL; } extern "C" const wchar_t *concatenateWideStrings(const wchar_t *str1, - const wchar_t *str2) { + const wchar_t *str2) +{ std::wstring result = std::wstring(str1) + std::wstring(str2); wchar_t *wcstr = new wchar_t[result.length() + 1]; wcscpy(wcstr, result.c_str()); @@ -40,48 +44,59 @@ extern "C" int getValueFromDoublePointer(int **ptr) { return *ptr[0]; }; extern "C" void noRet() { printf("%s", "hello world\n"); } -extern "C" int *createArrayi32(const int *arr, int size) { +extern "C" int *createArrayi32(const int *arr, int size) +{ int *vec = (int *)malloc((size) * sizeof(int)); - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) + { vec[i] = arr[i]; } return vec; } -extern "C" double *createArrayDouble(const double *arr, int size) { +extern "C" double *createArrayDouble(const double *arr, int size) +{ double *vec = (double *)malloc((size) * sizeof(double)); - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) + { vec[i] = arr[i]; } return vec; } -extern "C" double *createArrayFloat(const float *arr, int size) { +extern "C" double *createArrayFloat(const float *arr, int size) +{ double *vec = (double *)malloc((size) * sizeof(double)); - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) + { vec[i] = arr[i]; } return vec; } -extern "C" char **createArrayString(char **arr, int size) { +extern "C" char **createArrayString(char **arr, int size) +{ char **vec = (char **)malloc(size * sizeof(char *)); - if (vec == NULL) { + if (vec == NULL) + { return NULL; } - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) + { vec[i] = strdup(arr[i]); } return vec; } extern "C" bool return_opposite(bool input) { return !input; } -typedef struct stackStruct { +typedef struct stackStruct +{ int age; } stackStruct; -typedef struct Person { +typedef struct Person +{ int age; double *doubleArray; Person *parent; @@ -100,7 +115,8 @@ typedef struct Person { extern "C" Person *getStruct(Person *person) { return person; } -extern "C" Person *createPerson() { +extern "C" Person *createPerson() +{ Person *person = (Person *)malloc(sizeof(Person)); // Allocate and initialize doubleArray @@ -175,7 +191,8 @@ extern "C" Person *createPerson() { typedef const int (*FunctionPointer)(int a, bool b, char *c, double d, char **e, int *f, Person *g); -extern "C" void callFunction(FunctionPointer func) { +extern "C" void callFunction(FunctionPointer func) +{ int a = 100; bool b = false; double d = 100.11; @@ -199,28 +216,64 @@ extern "C" void callFunction(FunctionPointer func) { } // 定义 C++ 类 -class MyClass { +class MyClass +{ public: std::string name; int age; MyClass(std::string n, int a) : name(n), age(a) {} - void print() { + void print() + { std::cout << "Name: " << name << ", Age: " << age << std::endl; } }; -MyClass *createMyClass(std::string name, int age) { +MyClass *createMyClass(std::string name, int age) +{ return new MyClass(name, age); } -extern "C" MyClass *createMyClassFromC(const char *name, int age) { +extern "C" MyClass *createMyClassFromC(const char *name, int age) +{ return createMyClass(std::string(name), age); } extern "C" void printMyClass(MyClass *instance) { instance->print(); } +typedef struct +{ + short x; + short y; + short dir; + unsigned char kind; +} MINUTIA; + +typedef struct +{ + short nNumber; + MINUTIA item[3]; +} MINUTIAE; + +extern "C" MINUTIAE *printAndReturnMinutiae(MINUTIAE *minutiae) +{ + + MINUTIAE *result = new MINUTIAE; + result->nNumber = minutiae->nNumber; + printf("nNumber=%d\n", minutiae->nNumber); + + for (int i = 0; i < result->nNumber; i++) + { + result->item[i] = minutiae->item[i]; + printf("Result item[%d]: x=%d, y=%d, dir=%d, kind=%d\n", + i, result->item[i].x, result->item[i].y, + result->item[i].dir, (int)result->item[i].kind); + } + + return result; +} + // typedef void (*CallbackType)(const char *); // extern "C" void call_callback_async() { // dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/scripts/types.d.ts b/scripts/types.d.ts index e61318f..945f3da 100644 --- a/scripts/types.d.ts +++ b/scripts/types.d.ts @@ -48,6 +48,7 @@ export const enum DataType { */ BigInt = 16, I16 = 17, + StructArray = 18, StackStruct = 999, // reserve keyword StackArray = 996, Function = 998, diff --git a/src/datatype/create_struct.rs b/src/datatype/create_struct.rs index 4ca2e3f..3be7271 100644 --- a/src/datatype/create_struct.rs +++ b/src/datatype/create_struct.rs @@ -136,11 +136,13 @@ pub unsafe fn generate_c_struct( } RsArgsValue::Object(mut obj_value) => { if let FFITypeTag::Array | FFITypeTag::StackArray = get_ffi_tag(&obj_value) { - let array_desc = get_array_desc(&obj_value); + let array_desc = get_array_desc(&mut obj_value); let array_value = get_array_value(&mut obj_value).unwrap(); let FFIARRARYDESC { array_type, array_len, + struct_item_type, + .. } = array_desc; let field_size = match array_type { RefDataType::U8Array => { @@ -264,35 +266,67 @@ pub unsafe fn generate_c_struct( return Err(FFIError::Panic(format!("error array type {:?}", array_type)).into()); } } + RefDataType::StructArray => { + let is_stack_struct = + get_ffi_tag(struct_item_type.as_ref().unwrap()) == FFITypeTag::StackStruct; + if let RsArgsValue::StructArray(arr) = array_value { + if is_stack_struct { + let (size, align) = calculate_struct_size(struct_item_type.as_ref().unwrap()); + let field_size = size * array_len; + for i in 0..array_len { + let padding = (align - (offset % align)) % align; + field_ptr = field_ptr.offset(padding as isize); + generate_c_struct( + env, + struct_item_type.as_ref().unwrap(), + arr[i].clone(), + Some(field_ptr), + ) + .unwrap(); + field_ptr = field_ptr.offset(size as isize); + offset += size; + } + field_size + } else { + panic!("!struct array not supported"); + } + } else { + return Err(FFIError::Panic(format!("error array type {:?}", array_type)).into()); + } + } }; field_size } else { - let is_stack_struct = - if let Some(RsArgsValue::Object(field_type)) = struct_type.get(&field) { + let is_stack_struct = match struct_type.get(&field) { + Some(RsArgsValue::Object(field_type)) => { get_ffi_tag(field_type) == FFITypeTag::StackStruct - } else { - false - }; + } + _ => get_ffi_tag(struct_type) == FFITypeTag::StackStruct, + }; // struct if is_stack_struct { // stack struct - if let RsArgsValue::Object(val_type) = struct_type.get(&field).unwrap() { - let (size, align) = calculate_struct_size(val_type); - let padding = (align - (offset % align)) % align; - field_ptr = field_ptr.offset(padding as isize); - generate_c_struct(env, val_type, obj_value, Some(field_ptr))?; - offset += size + padding; - size + let target_type = if let Some(RsArgsValue::Object(val_type)) = struct_type.get(&field) { + val_type + } else if get_ffi_tag(struct_type) == FFITypeTag::StackStruct { + struct_type } else { return Err(FFIError::Panic(format!("unknown field type {:?}", struct_type)).into()); - } + }; + + let (size, align) = calculate_struct_size(target_type); + let padding = (align - (offset % align)) % align; + field_ptr = field_ptr.offset(padding as isize); + generate_c_struct(env, target_type, obj_value, Some(field_ptr))?; + offset += size + padding; + size } else { let (size, align) = get_size_align::<*mut c_void>(); let padding = (align - (offset % align)) % align; field_ptr = field_ptr.offset(padding as isize); if let RsArgsValue::Object(val_type) = struct_type.get(&field).unwrap() { - let obj_ptr = generate_c_struct(env, val_type, obj_value, None)?; - (field_ptr as *mut *const c_void).write(obj_ptr); + let start_ptr = generate_c_struct(env, val_type, obj_value, None)?; + (field_ptr as *mut *const c_void).write(start_ptr); } offset += size + padding; size @@ -304,6 +338,7 @@ pub unsafe fn generate_c_struct( | RsArgsValue::FloatArray(_) | RsArgsValue::I32Array(_) | RsArgsValue::DoubleArray(_) + | RsArgsValue::StructArray(_) | RsArgsValue::U8Array(_, _) => { return Err( FFIError::Panic(format!( diff --git a/src/datatype/function.rs b/src/datatype/function.rs index 7d074cb..f93c2ab 100644 --- a/src/datatype/function.rs +++ b/src/datatype/function.rs @@ -70,6 +70,9 @@ pub unsafe fn get_rs_value_from_pointer( let arr = create_array_from_pointer(*(pointer as *mut *mut c_float), array_len); RsArgsValue::FloatArray(arr) } + RefDataType::StructArray => { + panic!("struct array not supported"); + } } } else { // function | raw object diff --git a/src/datatype/pointer.rs b/src/datatype/pointer.rs index fede255..9ffd697 100644 --- a/src/datatype/pointer.rs +++ b/src/datatype/pointer.rs @@ -3,6 +3,7 @@ use crate::utils::{ }; use indexmap::IndexMap; use libc::{c_double, c_float, c_int, c_short, c_void, free}; +use std::alloc::{dealloc, Layout}; use std::ffi::{c_char, c_longlong, c_uchar, c_ulonglong, CStr, CString}; use widestring::{WideCString, WideChar}; @@ -167,6 +168,8 @@ unsafe fn free_struct_memory( let FFIARRARYDESC { array_type, array_len, + struct_item_type, + .. } = array_desc; let dynamic_array = get_ffi_tag(&obj) == FFITypeTag::Array; match array_type { @@ -225,6 +228,27 @@ unsafe fn free_struct_memory( offset += size + padding; field_size = size } + RefDataType::StructArray => { + let (size, align) = if dynamic_array { + get_size_align::<*const c_void>() + } else { + let (size, align) = calculate_struct_size(&struct_item_type.as_ref().unwrap()); + (size * array_len, align) + }; + let padding = (align - (offset % align)) % align; + field_ptr = field_ptr.offset(padding as isize); + if dynamic_array { + // need to review + let (size, _) = calculate_struct_size(&struct_item_type.as_ref().unwrap()); + let mut target_ptr = *(field_ptr as *mut *mut c_void); + (0..array_len).for_each(|_| { + free_struct_memory(target_ptr, struct_item_type.as_ref().unwrap(), ptr_type); + target_ptr = target_ptr.offset(size as isize); + }); + } + offset += size + padding; + field_size = size + } RefDataType::U8Array => { let (size, align) = if dynamic_array { get_size_align::<*const c_void>() @@ -320,6 +344,8 @@ pub unsafe fn free_rs_pointer_memory( let FFIARRARYDESC { array_type, array_len, + struct_item_type, + .. } = array_desc; match array_type { RefDataType::U8Array => {} @@ -327,6 +353,26 @@ pub unsafe fn free_rs_pointer_memory( RefDataType::DoubleArray => free_dynamic_double_array(ptr, array_len), RefDataType::FloatArray => free_dynamic_float_array(ptr, array_len), RefDataType::StringArray => free_dynamic_string_array(ptr, array_len), + RefDataType::StructArray => { + let is_stack_struct = + get_ffi_tag(struct_item_type.as_ref().unwrap()) == FFITypeTag::StackStruct; + if !is_stack_struct { + let (size, align) = calculate_struct_size(&struct_item_type.as_ref().unwrap()); + if size > 0 { + let layout = Layout::from_size_align(size, align).unwrap(); + let mut start_ptr = ptr; + (0..array_len).for_each(|_| { + free_struct_memory( + *(start_ptr as *mut *mut c_void), + struct_item_type.as_ref().unwrap(), + PointerType::RsPointer, + ); + start_ptr = start_ptr.offset(size as isize); + }); + dealloc(*(ptr as *mut *mut u8), layout); + } + } + } } } else if let FFITypeTag::Function = ffi_tag { let func_desc = get_func_desc(&obj); @@ -334,15 +380,17 @@ pub unsafe fn free_rs_pointer_memory( free_closure(ptr) } } else { - use std::alloc::{dealloc, Layout}; + let is_stack_struct = get_ffi_tag(&obj) == FFITypeTag::StackStruct; let (size, align) = calculate_struct_size(&obj); - let layout = if size > 0 { - Layout::from_size_align(size, align).unwrap() - } else { - Layout::new::() - }; - free_struct_memory(*(ptr as *mut *mut c_void), obj, PointerType::RsPointer); - dealloc(*(ptr as *mut *mut u8), layout); + if size > 0 { + let layout = Layout::from_size_align(size, align).unwrap(); + if !is_stack_struct { + free_struct_memory(*(ptr as *mut *mut c_void), obj, PointerType::RsPointer); + dealloc(*(ptr as *mut *mut u8), layout); + } else { + free_struct_memory(ptr, obj, PointerType::RsPointer); + } + } } } _ => panic!("free rust pointer memory error"), @@ -384,6 +432,8 @@ pub unsafe fn free_c_pointer_memory( let FFIARRARYDESC { array_type, array_len, + struct_item_type, + .. } = array_desc; match array_type { RefDataType::U8Array => free_dynamic_u8_array(ptr, array_len), @@ -391,6 +441,18 @@ pub unsafe fn free_c_pointer_memory( RefDataType::DoubleArray => free_dynamic_double_array(ptr, array_len), RefDataType::FloatArray => free_dynamic_float_array(ptr, array_len), RefDataType::StringArray => free_dynamic_string_array(ptr, array_len), + RefDataType::StructArray => { + let mut target_ptr = *(ptr as *mut *mut c_void); + let (size, _) = calculate_struct_size(struct_item_type.as_ref().unwrap()); + (0..array_len).for_each(|_| { + free_struct_memory( + target_ptr, + struct_item_type.as_ref().unwrap(), + PointerType::CPointer, + ); + target_ptr = target_ptr.offset(size as isize); + }); + } } } else if let FFITypeTag::Function = ffi_tag { let func_desc = get_func_desc(&obj); @@ -399,7 +461,13 @@ pub unsafe fn free_c_pointer_memory( } } else { // struct - free_struct_memory(*(ptr as *mut *mut c_void), obj, PointerType::CPointer); + let is_stack_struct = get_ffi_tag(&obj) == FFITypeTag::StackStruct; + let target_ptr = if is_stack_struct { + ptr + } else { + *(ptr as *mut *mut c_void) + }; + free_struct_memory(target_ptr, obj, PointerType::CPointer); free(*(ptr as *mut *mut c_void)) } } diff --git a/src/datatype/restore_struct.rs b/src/datatype/restore_struct.rs index 7bc794b..b4e5db6 100644 --- a/src/datatype/restore_struct.rs +++ b/src/datatype/restore_struct.rs @@ -149,8 +149,7 @@ pub unsafe fn create_rs_struct_from_pointer( field_size = size } }; - } - if let RsArgsValue::Object(sub_obj_type) = val { + } else if let RsArgsValue::Object(sub_obj_type) = val { let field = field.clone(); if let FFITypeTag::Array | FFITypeTag::StackArray = get_ffi_tag(sub_obj_type) { let array_desc = get_array_desc(sub_obj_type); @@ -158,6 +157,7 @@ pub unsafe fn create_rs_struct_from_pointer( let FFIARRARYDESC { array_type, array_len, + struct_item_type, .. } = &array_desc; let dynamic_array = get_ffi_tag(sub_obj_type) == FFITypeTag::Array; @@ -232,6 +232,37 @@ pub unsafe fn create_rs_struct_from_pointer( offset += size + padding; field_size = size } + RefDataType::StructArray => { + let is_stack_struct = + get_ffi_tag(struct_item_type.as_ref().unwrap()) == FFITypeTag::StackStruct; + let (size, align) = if is_stack_struct { + calculate_struct_size(struct_item_type.as_ref().unwrap()) + } else { + let (size, align) = get_size_align::<*const c_void>(); + (size, align) + }; + let arr = (0..*array_len) + .map(|_| { + let padding = (align - (offset % align)) % align; + field_ptr = field_ptr.offset(padding as isize); + let rs_struct = create_rs_struct_from_pointer( + env, + field_ptr, + struct_item_type.as_ref().unwrap(), + need_thread_safe, + ); + if is_stack_struct { + field_ptr = field_ptr.offset(size as isize); + } else { + field_ptr = field_ptr.offset(1); + } + offset += size; + rs_struct + }) + .collect::>(); + field_size = size * (*array_len); + rs_struct.insert(field, RsArgsValue::StructArray(arr)); + } RefDataType::U8Array => { let (size, align) = if dynamic_array { get_size_align::<*const c_void>() @@ -330,6 +361,16 @@ pub fn rs_value_to_js_unknown(env: &Env, data: RsArgsValue) -> Result RsArgsValue::I32Array(val) => val.to_js_array(env)?.into_unknown(), RsArgsValue::StringArray(val) => val.to_js_array(env)?.into_unknown(), RsArgsValue::DoubleArray(val) => val.to_js_array(env)?.into_unknown(), + RsArgsValue::StructArray(val) => { + let mut js_array = env.create_array_with_length(val.len())?; + for (index, item) in val.into_iter().enumerate() { + js_array.set_element( + index as u32, + rs_value_to_js_unknown(env, RsArgsValue::Object(item))?, + )?; + } + js_array.into_unknown() + } RsArgsValue::Object(obj) => create_js_object_from_rs_map(env, obj)?.into_unknown(), RsArgsValue::External(val) => val.into_unknown(), RsArgsValue::Void(_) => env.get_undefined()?.into_unknown(), diff --git a/src/define.rs b/src/define.rs index 998286e..1559317 100644 --- a/src/define.rs +++ b/src/define.rs @@ -9,7 +9,7 @@ use napi::{Env, JsExternal, JsObject, JsUnknown}; use std::collections::HashMap; use std::hash::Hash; use std::rc::Rc; -use strum_macros::{EnumIter, FromRepr}; +use strum_macros::FromRepr; type StandardResult = std::result::Result; @@ -91,6 +91,7 @@ where pub struct FFIARRARYDESC { pub array_type: RefDataType, pub array_len: usize, + pub struct_item_type: Option>, } pub struct FFIFUNCDESC { @@ -118,6 +119,7 @@ pub enum DataType { WString = 15, BigInt = 16, I16 = 17, + StructArray = 18, } #[derive(Debug, FromRepr)] pub enum BasicDataType { @@ -143,6 +145,7 @@ pub enum RefDataType { DoubleArray = 5, U8Array = 10, FloatArray = 13, + StructArray = 18, } impl TryFrom for DataType { @@ -224,6 +227,7 @@ pub enum RsArgsValue { StringArray(Vec), DoubleArray(Vec), FloatArray(Vec), + StructArray(Vec>), Object(IndexMap), Boolean(bool), Void(()), @@ -247,6 +251,7 @@ impl Clone for RsArgsValue { RsArgsValue::StringArray(vec) => RsArgsValue::StringArray(vec.clone()), RsArgsValue::DoubleArray(vec) => RsArgsValue::DoubleArray(vec.clone()), RsArgsValue::FloatArray(vec) => RsArgsValue::FloatArray(vec.clone()), + RsArgsValue::StructArray(vec) => RsArgsValue::StructArray(vec.clone()), RsArgsValue::Object(map) => RsArgsValue::Object(map.clone()), RsArgsValue::Boolean(b) => RsArgsValue::Boolean(*b), RsArgsValue::Void(()) => RsArgsValue::Void(()), @@ -303,16 +308,18 @@ impl std::fmt::Debug for RsArgsValue { RsArgsValue::Float(d) => write!(f, "Float({})", d), RsArgsValue::Double(d) => write!(f, "Double({})", d), RsArgsValue::U8Array(buffer, v) => { - if buffer.is_some() { - write!(f, "U8Array({:?})", buffer.as_ref().unwrap().as_ref()) - } else { - write!(f, "U8Array({:?})", v.as_ref().unwrap()) - } + write!( + f, + "U8Array({:?}, {:?})", + buffer.as_ref().unwrap().as_ref(), + v + ) } RsArgsValue::I32Array(arr) => write!(f, "I32Array({:?})", arr), RsArgsValue::StringArray(arr) => write!(f, "StringArray({:?})", arr), RsArgsValue::DoubleArray(arr) => write!(f, "DoubleArray({:?})", arr), RsArgsValue::FloatArray(arr) => write!(f, "FloatArray({:?})", arr), + RsArgsValue::StructArray(arr) => write!(f, "StructArray({:?})", arr), RsArgsValue::Object(obj) => write!(f, "Object({:?})", obj), RsArgsValue::Boolean(b) => write!(f, "Boolean({})", b), RsArgsValue::Void(_) => write!(f, "Void"), @@ -419,6 +426,7 @@ impl Drop for FFITypeCleanup { pub const ARRAY_LENGTH_TAG: &str = "length"; pub const ARRAY_TYPE_TAG: &str = "type"; +pub const ARRAY_STRUCT_ITEM_TYPE_TAG: &str = "structItemType"; pub const ARRAY_VALUE_TAG: &str = "value"; pub const FFI_TAG_FIELD: &str = "ffiTypeTag"; diff --git a/src/lib.rs b/src/lib.rs index 5535600..6c36e5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,11 +7,7 @@ mod utils; use datatype::pointer::{free_c_pointer_memory, free_rs_pointer_memory}; use define::*; use dlopen::symbor::{Library, Symbol}; -use libffi_sys::{ - ffi_abi_FFI_DEFAULT_ABI, ffi_call, ffi_cif, ffi_prep_cif, ffi_type, ffi_type_double, - ffi_type_enum_STRUCT, ffi_type_float, ffi_type_pointer, ffi_type_sint16, ffi_type_sint32, - ffi_type_sint64, ffi_type_uint64, ffi_type_uint8, ffi_type_void, -}; +use libffi_sys::{ffi_abi_FFI_DEFAULT_ABI, ffi_call, ffi_cif, ffi_prep_cif}; use napi::{Env, JsExternal, JsUnknown, Result}; use std::collections::HashMap; use std::ffi::c_void; @@ -20,6 +16,7 @@ use utils::dataprocess::{ get_arg_types_values, get_ffi_tag, get_js_external_wrap_data, get_js_unknown_from_pointer, get_value_pointer, type_define_to_rs_args, }; +use utils::pointer::get_ffi_type; static mut LIBRARY_MAP: Option< HashMap< @@ -219,60 +216,11 @@ unsafe fn load(env: Env, params: FFIParams) -> napi::Result { .map(|param| type_define_to_rs_args(&env, param).unwrap()) .collect(), ); - let (arg_types, arg_values) = get_arg_types_values(Rc::clone(¶ms_type_rs), params_value)?; let mut arg_values_c_void = get_value_pointer(&env, Rc::clone(¶ms_type_rs), arg_values)?; let ret_type_rs = type_define_to_rs_args(&env, ret_type)?; let mut ffi_type_cleanup = FFITypeCleanup::new(); - unsafe fn get_ffi_type( - ret_type_rs: &RsArgsValue, - ffi_type_cleanup: &mut FFITypeCleanup, - ) -> *mut ffi_type { - match ret_type_rs { - RsArgsValue::I32(number) => { - let ret_data_type = (*number).try_into().unwrap(); - match ret_data_type { - BasicDataType::U8 => &mut ffi_type_uint8 as *mut ffi_type, - BasicDataType::I32 => &mut ffi_type_sint32 as *mut ffi_type, - BasicDataType::I16 => &mut ffi_type_sint16 as *mut ffi_type, - BasicDataType::I64 | BasicDataType::BigInt => &mut ffi_type_sint64 as *mut ffi_type, - BasicDataType::U64 => &mut ffi_type_uint64 as *mut ffi_type, - BasicDataType::String | BasicDataType::WString => &mut ffi_type_pointer as *mut ffi_type, - BasicDataType::Void => &mut ffi_type_void as *mut ffi_type, - BasicDataType::Float => &mut ffi_type_float as *mut ffi_type, - BasicDataType::Double => &mut ffi_type_double as *mut ffi_type, - BasicDataType::Boolean => &mut ffi_type_uint8 as *mut ffi_type, - BasicDataType::External => &mut ffi_type_pointer as *mut ffi_type, - } - } - RsArgsValue::Object(struct_type) => { - if get_ffi_tag(struct_type) == FFITypeTag::StackStruct { - let mut elements: Vec<*mut ffi_type> = struct_type - .iter() - .filter(|(field_name, _)| field_name != &FFI_TAG_FIELD) - .map(|(_, field_type)| get_ffi_type(field_type, ffi_type_cleanup)) - .collect(); - elements.push(std::ptr::null_mut()); - let struct_type_box = ffi_type { - size: 0, - alignment: 0, - type_: ffi_type_enum_STRUCT as u16, - elements: elements.as_mut_ptr(), - }; - let elements_ptr = Box::into_raw(Box::new(elements)); - let struct_type_ptr = Box::into_raw(Box::new(struct_type_box)); - ffi_type_cleanup.elements_box = Some(elements_ptr); - ffi_type_cleanup.struct_type_box = Some(struct_type_ptr); - struct_type_ptr - } else { - &mut ffi_type_pointer as *mut ffi_type - } - } - _ => &mut ffi_type_void as *mut ffi_type, - } - } - let r_type = get_ffi_type(&ret_type_rs, &mut ffi_type_cleanup); ffi_type_cleanup.r_type = Some(r_type); ffi_type_cleanup.arg_types = arg_types; diff --git a/src/utils/array.rs b/src/utils/array.rs new file mode 100644 index 0000000..eb6a45f --- /dev/null +++ b/src/utils/array.rs @@ -0,0 +1,24 @@ +use crate::define::{ + RsArgsValue, ARRAY_LENGTH_TAG, ARRAY_STRUCT_ITEM_TYPE_TAG, ARRAY_TYPE_TAG, FFIARRARYDESC, +}; +use indexmap::IndexMap; + +pub fn get_array_desc(obj: &IndexMap) -> FFIARRARYDESC { + let (mut array_len, mut array_type, mut struct_item_type) = (0, 0, None); + if let RsArgsValue::I32(number) = obj.get(ARRAY_LENGTH_TAG).unwrap() { + array_len = *number as usize + } + if let RsArgsValue::I32(number) = obj.get(ARRAY_TYPE_TAG).unwrap() { + array_type = *number + } + if let Some(RsArgsValue::Object(item_type)) = obj.get(ARRAY_STRUCT_ITEM_TYPE_TAG) { + struct_item_type = Some(item_type.clone()); + } + + let array_type = array_type.try_into().unwrap(); + FFIARRARYDESC { + array_len, + array_type, + struct_item_type, + } +} diff --git a/src/utils/dataprocess.rs b/src/utils/dataprocess.rs index 78dbb1e..045ce86 100644 --- a/src/utils/dataprocess.rs +++ b/src/utils/dataprocess.rs @@ -1,4 +1,7 @@ +use super::get_array_desc; use super::js_value::create_js_value_unchecked; +use super::object_utils::calculate_struct_size; +use super::pointer::get_ffi_type; use crate::datatype::array::ToRsArray; use crate::datatype::buffer::get_safe_buffer; use crate::datatype::create_struct::generate_c_struct; @@ -59,21 +62,6 @@ pub fn get_ffi_tag(obj: &IndexMap) -> FFITypeTag { FFITypeTag::Unknown } } -pub fn get_array_desc(obj: &IndexMap) -> FFIARRARYDESC { - let (mut array_len, mut array_type) = (0, 0); - if let RsArgsValue::I32(number) = obj.get(ARRAY_LENGTH_TAG).unwrap() { - array_len = *number as usize - } - if let RsArgsValue::I32(number) = obj.get(ARRAY_TYPE_TAG).unwrap() { - array_type = *number - } - let array_type = array_type.try_into().unwrap(); - - FFIARRARYDESC { - array_len, - array_type, - } -} pub fn get_array_value(obj: &mut IndexMap) -> Option { obj.remove(ARRAY_VALUE_TAG) @@ -180,7 +168,11 @@ pub unsafe fn get_arg_types_values( let arg_type = &mut ffi_type_pointer as *mut ffi_type; if let FFITypeTag::Array | FFITypeTag::StackArray = get_ffi_tag(¶ms_type_object_rs) { let array_desc = get_array_desc(¶ms_type_object_rs); - let FFIARRARYDESC { array_type, .. } = array_desc; + let FFIARRARYDESC { + array_type, + struct_item_type, + .. + } = array_desc; match array_type { RefDataType::U8Array => { let arg_type = &mut ffi_type_pointer as *mut ffi_type; @@ -237,6 +229,21 @@ pub unsafe fn get_arg_types_values( let arg_val = js_object.to_rs_array()?; (arg_type, RsArgsValue::StringArray(arg_val)) } + RefDataType::StructArray => { + let arg_type = &mut ffi_type_pointer as *mut ffi_type; + let js_object = create_js_value_unchecked::(value)?; + let arg_values = vec![0; js_object.get_array_length()? as usize] + .iter() + .enumerate() + .map(|(index, _)| { + let js_element: JsObject = js_object.get_element(index as u32).unwrap(); + let struct_item_type = struct_item_type.as_ref().unwrap(); + let index_map = get_params_value_rs_struct(struct_item_type, &js_element); + index_map.unwrap() + }) + .collect(); + (arg_type, RsArgsValue::StructArray(arg_values)) + } } } else if let FFITypeTag::Function = get_ffi_tag(¶ms_type_object_rs) { let params_val_function: JsFunction = value.try_into()?; @@ -246,6 +253,17 @@ pub unsafe fn get_arg_types_values( RsArgsValue::Function(params_type_object_rs.clone(), params_val_function), ) } else { + // struct + let is_stack_struct = get_ffi_tag(¶ms_type_object_rs) == FFITypeTag::StackStruct; + let mut ffi_type_cleanup = FFITypeCleanup::new(); + let arg_type = if is_stack_struct { + get_ffi_type( + &RsArgsValue::Object(params_type_object_rs.clone()), + &mut ffi_type_cleanup, + ) + } else { + &mut ffi_type_pointer as *mut ffi_type + }; let params_value_object = create_js_value_unchecked::(value)?; let index_map = get_params_value_rs_struct(params_type_object_rs, ¶ms_value_object); (arg_type, RsArgsValue::Object(index_map.unwrap())) @@ -365,6 +383,41 @@ pub unsafe fn get_value_pointer( std::mem::forget(c_char_vec); Ok(Box::into_raw(Box::new(ptr)) as *mut c_void) } + RsArgsValue::StructArray(val) => { + if let RsArgsValue::Object(arg_type) = arg_type { + let array_desc = get_array_desc(arg_type); + let struct_item_type = array_desc.struct_item_type.as_ref() + .ok_or_else(|| FFIError::Panic("Missing struct item type".to_string()))?; + + let is_stack_struct = get_ffi_tag(struct_item_type) == FFITypeTag::StackStruct; + + if is_stack_struct { + let (struct_size, _) = calculate_struct_size(struct_item_type); + let mut ptr = None; + let mut next_ptr = None; + + for item in val { + let struct_ptr = generate_c_struct(&env, struct_item_type, item, next_ptr)?; + if ptr.is_none() { + ptr = Some(struct_ptr); + } + next_ptr = Some(struct_ptr.offset(struct_size as isize)); + } + + Ok(Box::into_raw(Box::new(ptr.unwrap())) as *mut c_void) + } else { + let struct_ptrs: Vec<_> = val.into_iter() + .map(|item| generate_c_struct(&env, struct_item_type, item, None)) + .collect::>>()?; + let ptr = struct_ptrs.as_ptr(); + std::mem::forget(struct_ptrs); + + Ok(Box::into_raw(Box::new(ptr as *mut c_void)) as *mut c_void) + } + } else { + Err(FFIError::Panic(format!("uncorrect params type {:?}", arg_type)).into()) + } + } RsArgsValue::Boolean(val) => { let c_bool = Box::new(val); Ok(Box::into_raw(c_bool) as *mut c_void) @@ -372,9 +425,14 @@ pub unsafe fn get_value_pointer( RsArgsValue::Void(_) => Ok(Box::into_raw(Box::new(std::ptr::null_mut() as *mut c_void)) as *mut c_void), RsArgsValue::Object(val) => { if let RsArgsValue::Object(arg_type_rs) = arg_type { + let is_stack_struct = get_ffi_tag(arg_type_rs) == FFITypeTag::StackStruct; Ok( - Box::into_raw(Box::new(generate_c_struct(&env, &arg_type_rs, val, None)?)) - as *mut c_void, + if is_stack_struct { + generate_c_struct(&env, &arg_type_rs, val, None)? + } else { + Box::into_raw(Box::new(generate_c_struct(&env, &arg_type_rs, val, None)?)) + as *mut c_void + } ) } else { Err(FFIError::Panic(format!("uncorrect params type {:?}", arg_type)).into()) @@ -584,39 +642,12 @@ pub unsafe fn get_params_value_rs_struct( let val: f64 = val.try_into()?; RsArgsValue::Double(val) } - DataType::StringArray => { - let js_array: JsObject = params_value_object.get_named_property(&field)?; - let arg_val = js_array.to_rs_array()?; - RsArgsValue::StringArray(arg_val) - } - DataType::DoubleArray => { - let js_array: JsObject = params_value_object.get_named_property(&field)?; - let arg_val: Vec = js_array.to_rs_array()?; - RsArgsValue::DoubleArray(arg_val) - } - DataType::FloatArray => { - let js_array: JsObject = params_value_object.get_named_property(&field)?; - let arg_val: Vec = js_array - .to_rs_array()? - .into_iter() - .map(|item: f64| item as f32) - .collect(); - RsArgsValue::FloatArray(arg_val) - } - DataType::I32Array => { - let js_array: JsObject = params_value_object.get_named_property(&field)?; - let arg_val = js_array.to_rs_array()?; - RsArgsValue::I32Array(arg_val) - } - DataType::U8Array => { - let js_buffer: JsBuffer = params_value_object.get_named_property(&field)?; - RsArgsValue::U8Array(Some(js_buffer.into_value()?), None) - } DataType::External => { let val: JsExternal = params_value_object.get_named_property(&field)?; RsArgsValue::External(val) } DataType::Void => RsArgsValue::Void(()), + _ => panic!("unsupport data type: {:?}", data_type), }; index_map.insert(field, val); } @@ -625,7 +656,11 @@ pub unsafe fn get_params_value_rs_struct( let params_value: JsObject = params_value_object.get_named_property(&field)?; if let FFITypeTag::Array | FFITypeTag::StackArray = get_ffi_tag(¶ms_type_rs_value) { let array_desc = get_array_desc(¶ms_type_rs_value); - let FFIARRARYDESC { array_type, .. } = array_desc; + let FFIARRARYDESC { + array_type, + struct_item_type, + .. + } = array_desc; let array_value = match array_type { RefDataType::U8Array => { let js_buffer: JsBuffer = params_value_object.get_named_property(&field)?; @@ -655,6 +690,20 @@ pub unsafe fn get_params_value_rs_struct( let arg_val = js_array.to_rs_array()?; RsArgsValue::StringArray(arg_val) } + RefDataType::StructArray => { + let js_array: JsObject = params_value_object.get_named_property(&field)?; + let arg_val = vec![0; js_array.get_array_length()? as usize] + .iter() + .enumerate() + .map(|(index, _)| { + let js_element: JsObject = js_array.get_element(index as u32).unwrap(); + let struct_item_type = struct_item_type.as_ref().unwrap(); + let index_map = get_params_value_rs_struct(struct_item_type, &js_element); + index_map.unwrap() + }) + .collect(); + RsArgsValue::StructArray(arg_val) + } }; params_type_rs_value.insert(ARRAY_VALUE_TAG.to_string(), array_value); index_map.insert(field, RsArgsValue::Object(params_type_rs_value)); @@ -801,6 +850,7 @@ pub unsafe fn get_js_unknown_from_pointer( let FFIARRARYDESC { array_type, array_len, + struct_item_type, .. } = array_desc; match array_type { @@ -824,6 +874,29 @@ pub unsafe fn get_js_unknown_from_pointer( let arr = create_array_from_pointer(*(ptr as *mut *mut *mut c_char), array_len); rs_value_to_js_unknown(env, RsArgsValue::StringArray(arr)) } + RefDataType::StructArray => { + let mut safe_ptr = std::ptr::read(ptr as *const *mut c_void); + let is_stack_struct = + get_ffi_tag(struct_item_type.as_ref().unwrap()) == FFITypeTag::StackStruct; + let v = (0..array_len) + .map(|_| { + let rs_struct = create_rs_struct_from_pointer( + env, + safe_ptr, + struct_item_type.as_ref().unwrap(), + false, + ); + let (struct_size, _) = calculate_struct_size(&struct_item_type.as_ref().unwrap()); + if is_stack_struct { + safe_ptr = safe_ptr.offset(struct_size as isize); + } else { + safe_ptr = safe_ptr.offset(1); + } + rs_struct + }) + .collect(); + rs_value_to_js_unknown(env, RsArgsValue::StructArray(v)) + } } } else { // raw object diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c0a5388..f5bcdbf 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,6 +1,8 @@ +pub mod array; pub mod dataprocess; pub mod js_value; pub mod object_utils; - +pub mod pointer; +pub use array::*; pub use dataprocess::*; pub use object_utils::*; diff --git a/src/utils/object_utils.rs b/src/utils/object_utils.rs index ecb490a..7f4e85d 100644 --- a/src/utils/object_utils.rs +++ b/src/utils/object_utils.rs @@ -1,4 +1,6 @@ -use super::dataprocess::{get_array_desc, get_ffi_tag}; +use super::dataprocess::get_ffi_tag; +use super::get_array_desc; + use crate::define::*; use crate::{RefDataType, RsArgsValue, FFIARRARYDESC}; use indexmap::IndexMap; @@ -62,6 +64,8 @@ pub fn calculate_struct_size(struct_type: &IndexMap) -> (us let FFIARRARYDESC { array_type, array_len, + struct_item_type, + .. } = array_desc; let (mut type_size, type_align) = match array_type { RefDataType::U8Array => get_size_align::(), @@ -69,6 +73,7 @@ pub fn calculate_struct_size(struct_type: &IndexMap) -> (us RefDataType::FloatArray => get_size_align::(), RefDataType::StringArray => get_size_align::<*const c_char>(), RefDataType::DoubleArray => get_size_align::(), + RefDataType::StructArray => calculate_struct_size(struct_item_type.as_ref().unwrap()), }; type_size = type_size * array_len; let align = align.max(type_align); @@ -109,6 +114,7 @@ pub unsafe fn create_static_array_from_pointer( let FFIARRARYDESC { array_type, array_len, + .. } = array_desc; match array_type { RefDataType::U8Array => { @@ -131,6 +137,9 @@ pub unsafe fn create_static_array_from_pointer( let arr = (0..*array_len).map(|n| *(ptr.offset(n as isize))).collect(); RsArgsValue::FloatArray(arr) } + RefDataType::StructArray => { + panic!("struct array is not supported for static array"); + } RefDataType::StringArray => { panic!("string array is not supported for static array"); // let ptr = ptr as *mut *const c_char; diff --git a/src/utils/pointer.rs b/src/utils/pointer.rs new file mode 100644 index 0000000..6873f71 --- /dev/null +++ b/src/utils/pointer.rs @@ -0,0 +1,56 @@ +use super::dataprocess::get_ffi_tag; +use super::object_utils::calculate_struct_size; +use crate::define::*; +use libffi_sys::{ + ffi_type, ffi_type_double, ffi_type_enum_STRUCT, ffi_type_float, ffi_type_pointer, + ffi_type_sint16, ffi_type_sint32, ffi_type_sint64, ffi_type_uint64, ffi_type_uint8, + ffi_type_void, +}; +pub unsafe fn get_ffi_type( + ret_type_rs: &RsArgsValue, + ffi_type_cleanup: &mut FFITypeCleanup, +) -> *mut ffi_type { + match ret_type_rs { + RsArgsValue::I32(number) => { + let ret_data_type = (*number).try_into().unwrap(); + match ret_data_type { + BasicDataType::U8 => &mut ffi_type_uint8 as *mut ffi_type, + BasicDataType::I32 => &mut ffi_type_sint32 as *mut ffi_type, + BasicDataType::I16 => &mut ffi_type_sint16 as *mut ffi_type, + BasicDataType::I64 | BasicDataType::BigInt => &mut ffi_type_sint64 as *mut ffi_type, + BasicDataType::U64 => &mut ffi_type_uint64 as *mut ffi_type, + BasicDataType::String | BasicDataType::WString => &mut ffi_type_pointer as *mut ffi_type, + BasicDataType::Void => &mut ffi_type_void as *mut ffi_type, + BasicDataType::Float => &mut ffi_type_float as *mut ffi_type, + BasicDataType::Double => &mut ffi_type_double as *mut ffi_type, + BasicDataType::Boolean => &mut ffi_type_uint8 as *mut ffi_type, + BasicDataType::External => &mut ffi_type_pointer as *mut ffi_type, + } + } + RsArgsValue::Object(struct_type) => { + if get_ffi_tag(struct_type) == FFITypeTag::StackStruct { + let mut elements: Vec<*mut ffi_type> = struct_type + .iter() + .filter(|(field_name, _)| field_name != &FFI_TAG_FIELD) + .map(|(_, field_type)| get_ffi_type(field_type, ffi_type_cleanup)) + .collect(); + elements.push(std::ptr::null_mut()); + let (size, align) = calculate_struct_size(struct_type); + let struct_type_box = ffi_type { + size, + alignment: align as u16, + type_: ffi_type_enum_STRUCT as u16, + elements: elements.as_mut_ptr(), + }; + let elements_ptr = Box::into_raw(Box::new(elements)); + let struct_type_ptr = Box::into_raw(Box::new(struct_type_box)); + ffi_type_cleanup.elements_box = Some(elements_ptr); + ffi_type_cleanup.struct_type_box = Some(struct_type_ptr); + struct_type_ptr + } else { + &mut ffi_type_pointer as *mut ffi_type + } + } + _ => &mut ffi_type_void as *mut ffi_type, + } +} diff --git a/tests/index.ts b/tests/index.ts index 9f5acc1..ad17ab6 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -419,11 +419,11 @@ const unitTest = () => { testFunction() testCpp() logGreen('test cpp succeed') - testObject() - logGreen('test object succeed') testPointer() logGreen('test createPointer succeed') testRunInNewThread() + testObject() + logGreen('test object succeed') }; unitTest(); diff --git a/tests/struct.ts b/tests/struct.ts index dc57312..4634cdb 100644 --- a/tests/struct.ts +++ b/tests/struct.ts @@ -2,10 +2,10 @@ import { deepStrictEqual } from "assert" import { load, DataType, - arrayConstructor, createPointer, restorePointer, unwrapPointer, + arrayConstructor } from "../index" import { logGreen } from "./utils" import { person, personType } from "./types" @@ -57,4 +57,64 @@ export const testObject = () => { retType: [personType] }) deepStrictEqual(person, restorePersonObjByPointer[0]) + const structArray = [{ + x: 1, + y: 2, + dir: 3, + kind: 4, + }, + { + x: 5, + y: 6, + dir: 7, + kind: 8, + }, + { + x: 9, + y: 10, + dir: 11, + kind: 12, + }] + const res = load({ + library: "libsum", + funcName: "printAndReturnMinutiae", + retType: { + nNumber: DataType.I16, + item: arrayConstructor({ + type: DataType.StructArray, + ffiTypeTag: DataType.StackArray, + structItemType: { + x: DataType.I16, + y: DataType.I16, + dir: DataType.I16, + kind: DataType.U8, + ffiTypeTag: DataType.StackStruct + }, + length: 3 + }), + }, + paramsType: [{ + nNumber: DataType.I16, + item: arrayConstructor({ + type: DataType.StructArray, + ffiTypeTag: DataType.StackArray, + structItemType: { + x: DataType.I16, + y: DataType.I16, + dir: DataType.I16, + kind: DataType.U8, + ffiTypeTag: DataType.StackStruct + }, + length: 3 + }), + }], + paramsValue: [{ + nNumber: 3, + item: structArray, + }] + }) + deepStrictEqual(res, { + nNumber: 3, + item: structArray, + }) } \ No newline at end of file