From dd6a3861e541aa89b54a9a3ddbb15ea24a19f22e Mon Sep 17 00:00:00 2001 From: qwreey Date: Fri, 30 Aug 2024 04:04:24 +0000 Subject: [PATCH] Improve conversion performance by caching dyn handle on subtype (#243) --- crates/lune-std-ffi/src/c/c_arr.rs | 138 ++++++++++++-- crates/lune-std-ffi/src/c/c_helper.rs | 7 +- crates/lune-std-ffi/src/c/c_type.rs | 2 +- crates/lune-std-ffi/src/c/mod.rs | 2 +- crates/lune-std-ffi/src/c/types/mod.rs | 204 ++++++++++----------- crates/lune-std-ffi/src/ffi/ffi_box.rs | 10 +- crates/lune-std-ffi/src/ffi/ffi_func.rs | 0 crates/lune-std-ffi/src/ffi/ffi_helper.rs | 29 --- crates/lune-std-ffi/src/ffi/ffi_lib.rs | 9 +- crates/lune-std-ffi/src/ffi/ffi_raw.rs | 2 +- crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs | 16 +- crates/lune-std-ffi/src/ffi/mod.rs | 1 + 12 files changed, 244 insertions(+), 176 deletions(-) create mode 100644 crates/lune-std-ffi/src/ffi/ffi_func.rs delete mode 100644 crates/lune-std-ffi/src/ffi/ffi_helper.rs diff --git a/crates/lune-std-ffi/src/c/c_arr.rs b/crates/lune-std-ffi/src/c/c_arr.rs index e1deff7..9302a3d 100644 --- a/crates/lune-std-ffi/src/c/c_arr.rs +++ b/crates/lune-std-ffi/src/c/c_arr.rs @@ -1,10 +1,17 @@ +use std::cell::Ref; + use libffi::middle::Type; use mlua::prelude::*; -use super::association_names::CARR_INNER; -use super::c_helper::{get_ensured_size, libffi_type_from_userdata, pretty_format_userdata}; -use super::c_ptr::CPtr; -use crate::ffi::ffi_association::{get_association, set_association}; +use super::{ + association_names::CARR_INNER, + c_helper::{get_conv, get_ensured_size, libffi_type_from_userdata, pretty_format_userdata}, + CPtr, +}; +use crate::ffi::{ + ffi_association::{get_association, set_association}, + FfiBox, GetNativeDataHandle, NativeConvert, NativeDataHandle, NativeSignedness, NativeSize, +}; // This is a series of some type. // It provides the final size and the offset of the index, @@ -18,24 +25,30 @@ use crate::ffi::ffi_association::{get_association, set_association}; // There is no problem even if you create a struct with n fields of a single type within the struct. Array adheres to the condition that there is no additional padding between each element. Padding to a struct is padding inside the struct. Simply think of the padding byte as a trailing unnamed field. pub struct CArr { - element_type: Type, + // element_type: Type, struct_type: Type, length: usize, field_size: usize, size: usize, + conv: *const dyn NativeConvert, } impl CArr { - pub fn new(element_type: Type, length: usize) -> LuaResult { - let struct_type = Type::structure(vec![element_type.clone(); length]); + pub fn new( + element_type: Type, + length: usize, + conv: *const dyn NativeConvert, + ) -> LuaResult { let field_size = get_ensured_size(element_type.as_raw_ptr())?; + let struct_type = Type::structure(vec![element_type.clone(); length]); Ok(Self { - element_type, + // element_type, struct_type, length, field_size, size: field_size * length, + conv, }) } @@ -45,16 +58,13 @@ impl CArr { length: usize, ) -> LuaResult> { let fields = libffi_type_from_userdata(lua, luatype)?; - let carr = lua.create_userdata(Self::new(fields, length)?)?; + let conv = unsafe { get_conv(luatype)? }; + let carr = lua.create_userdata(Self::new(fields, length, conv)?)?; set_association(lua, CARR_INNER, &carr, luatype)?; Ok(carr) } - pub fn get_size(&self) -> usize { - self.size - } - pub fn get_length(&self) -> usize { self.length } @@ -63,9 +73,9 @@ impl CArr { &self.struct_type } - pub fn get_element_type(&self) -> &Type { - &self.element_type - } + // pub fn get_element_type(&self) -> &Type { + // &self.element_type + // } // Stringify cstruct for pretty printing something like: // @@ -89,6 +99,64 @@ impl CArr { } } +impl NativeSize for CArr { + fn get_size(&self) -> usize { + self.size + } +} +impl NativeSignedness for CArr { + fn get_signedness(&self) -> bool { + false + } +} +impl NativeConvert for CArr { + // FIXME: FfiBox, FfiRef support required + unsafe fn luavalue_into<'lua>( + &self, + lua: &'lua Lua, + offset: isize, + data_handle: &Ref, + value: LuaValue<'lua>, + ) -> LuaResult<()> { + let LuaValue::Table(ref table) = value else { + return Err(LuaError::external("Value is not a table")); + }; + for i in 0..self.length { + let field_offset = (i * self.field_size) as isize; + let data: LuaValue = table.get(i + 1)?; + + self.conv.as_ref().unwrap().luavalue_into( + lua, + field_offset + offset, + data_handle, + data, + )?; + } + Ok(()) + } + + unsafe fn luavalue_from<'lua>( + &self, + lua: &'lua Lua, + offset: isize, + data_handle: &Ref, + ) -> LuaResult> { + let table = lua.create_table_with_capacity(self.length, 0)?; + for i in 0..self.length { + let field_offset = (i * self.field_size) as isize; + table.set( + i + 1, + self.conv.as_ref().unwrap().luavalue_from( + lua, + field_offset + offset, + data_handle, + )?, + )?; + } + Ok(LuaValue::Table(table)) + } +} + impl LuaUserData for CArr { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("size", |_, this| Ok(this.get_size())); @@ -109,6 +177,44 @@ impl LuaUserData for CArr { Err(LuaError::external("Out of index")) } }); + methods.add_method("box", |lua, this, table: LuaValue| { + let result = lua.create_userdata(FfiBox::new(this.get_size()))?; + + unsafe { this.luavalue_into(lua, 0, &result.get_data_handle()?, table)? }; + Ok(result) + }); + methods.add_method( + "from", + |lua, this, (userdata, offset): (LuaAnyUserData, Option)| { + let offset = offset.unwrap_or(0); + + let data_handle = &userdata.get_data_handle()?; + if !data_handle.check_boundary(offset, this.get_size()) { + return Err(LuaError::external("Out of bounds")); + } + if !data_handle.check_readable(&userdata, offset, this.get_size()) { + return Err(LuaError::external("Unreadable data handle")); + } + + unsafe { this.luavalue_from(lua, offset, data_handle) } + }, + ); + methods.add_method( + "into", + |lua, this, (userdata, value, offset): (LuaAnyUserData, LuaValue, Option)| { + let offset = offset.unwrap_or(0); + + let data_handle = &userdata.get_data_handle()?; + if !data_handle.check_boundary(offset, this.size) { + return Err(LuaError::external("Out of bounds")); + } + if !data_handle.checek_writable(&userdata, offset, this.size) { + return Err(LuaError::external("Unwritable data handle")); + } + + unsafe { this.luavalue_into(lua, offset, data_handle, value) } + }, + ); methods.add_function("ptr", |lua, this: LuaAnyUserData| { let pointer = CPtr::new_from_lua_userdata(lua, &this)?; Ok(pointer) diff --git a/crates/lune-std-ffi/src/c/c_helper.rs b/crates/lune-std-ffi/src/c/c_helper.rs index 6d0ac97..d072bd4 100644 --- a/crates/lune-std-ffi/src/c/c_helper.rs +++ b/crates/lune-std-ffi/src/c/c_helper.rs @@ -6,10 +6,9 @@ use libffi::{low, middle::Type, raw}; use lune_utils::fmt::{pretty_format_value, ValueFormatConfig}; use mlua::prelude::*; -use super::association_names::CTYPE_STATIC; -use super::c_type::CTypeStatic; -use super::types::get_ctype_conv; -use super::{CArr, CPtr, CStruct}; +use super::{ + association_names::CTYPE_STATIC, types::get_ctype_conv, CArr, CPtr, CStruct, CTypeStatic, +}; use crate::ffi::{ffi_association::get_association, NativeConvert, FFI_STATUS_NAMES}; // Get the NativeConvert handle from the type UserData diff --git a/crates/lune-std-ffi/src/c/c_type.rs b/crates/lune-std-ffi/src/c/c_type.rs index 6cc763a..4ab4cd8 100644 --- a/crates/lune-std-ffi/src/c/c_type.rs +++ b/crates/lune-std-ffi/src/c/c_type.rs @@ -88,7 +88,7 @@ impl NativeSize for CType { } } -pub struct CType { +pub struct CType { // for ffi_ptrarray_to_raw? // libffi_cif: Cif, libffi_type: Type, diff --git a/crates/lune-std-ffi/src/c/mod.rs b/crates/lune-std-ffi/src/c/mod.rs index 34c8044..2d7d618 100644 --- a/crates/lune-std-ffi/src/c/mod.rs +++ b/crates/lune-std-ffi/src/c/mod.rs @@ -12,7 +12,7 @@ pub use self::{ c_fn::CFn, c_ptr::CPtr, c_struct::CStruct, - c_type::{CType, CTypeCast}, + c_type::{CType, CTypeCast, CTypeStatic}, }; pub use types::create_all_c_types; diff --git a/crates/lune-std-ffi/src/c/types/mod.rs b/crates/lune-std-ffi/src/c/types/mod.rs index 5b8a700..9863e75 100644 --- a/crates/lune-std-ffi/src/c/types/mod.rs +++ b/crates/lune-std-ffi/src/c/types/mod.rs @@ -1,15 +1,14 @@ #![allow(clippy::inline_always)] use core::ffi::*; -use std::cell::Ref; -use std::{any::TypeId, ops::Deref}; +use std::{any::TypeId, cell::Ref}; use libffi::middle::Type; use mlua::prelude::*; use num::cast::AsPrimitive; use super::{CType, CTypeCast}; -use crate::ffi::{NativeConvert, NativeDataHandle, NativeSignedness}; +use crate::ffi::{NativeConvert, NativeDataHandle}; pub mod f32; pub mod f64; @@ -129,120 +128,107 @@ pub fn create_all_types(lua: &Lua) -> LuaResult { - if $t.is::>() { - Ok(size_of::<$f>()) - }$( else if $t.is::>() { - Ok(size_of::<$c>()) - })* else { - Err(LuaError::external("Unexpected type")) - } - }; -} -#[inline(always)] -pub fn ctype_size_from_userdata(this: &LuaAnyUserData) -> LuaResult { - define_ctype_size_from_userdata!( - this, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64 - ) -} - -macro_rules! define_ctype_luavalue_into_ptr { - ($lua:ident, $this:ident, $offset:ident, $data_handle:ident, $value:ident, $f:ty, $( $c:ty ),*) => { - if $this.is::>() { - let ctype = $this.borrow::>()?; - ctype.luavalue_into($lua, $offset, $data_handle, $value) - }$( else if $this.is::>() { - let ctype = $this.borrow::>()?; - ctype.luavalue_into($lua, $offset, $data_handle, $value) - })* else { - Err(LuaError::external("Unexpected type")) - } - }; -} -#[inline(always)] -pub unsafe fn ctype_luavalue_into_ptr<'lua>( - lua: &'lua Lua, - this: &LuaAnyUserData<'lua>, - offset: isize, - data_handle: &Ref, - value: LuaValue<'lua>, -) -> LuaResult<()> { - define_ctype_luavalue_into_ptr!( - lua, - this, - offset, - data_handle, - value, - u8, - u16, - u32, - u64, - u128, - i8, - i16, - i32, - i64, - i128, - f32, - f64 - ) -} - -macro_rules! define_ctype_luavalue_from_ptr { - ($lua:ident, $this:ident, $offset:ident, $data_handle:ident, $f:ty, $( $c:ty ),*) => { - if $this.is::>() { - $this.borrow::>()?.luavalue_from($lua, $offset, $data_handle) - }$( else if $this.is::>() { - $this.borrow::>()?.luavalue_from($lua, $offset, $data_handle) - })* else { - Err(LuaError::external("Unexpected type")) - } - }; -} -#[inline(always)] -pub unsafe fn ctype_luavalue_from_ptr<'lua>( - lua: &'lua Lua, - this: &LuaAnyUserData<'lua>, - offset: isize, - data_handle: &Ref, -) -> LuaResult> { - define_ctype_luavalue_from_ptr!( - lua, - this, - offset, - data_handle, - u8, - u16, - u32, - u64, - u128, - i8, - i16, - i32, - i64, - i128, - f32, - f64 - ) -} - -// struct CastCache<'a> { -// conv: &'a [for<'lua> fn(lua: &'lua Lua)], -// ud: Box<[*const dyn NativeConvert]>, +// macro_rules! define_ctype_size_from_userdata { +// ($t:ident, $f:ty, $( $c:ty ),*) => { +// if $t.is::>() { +// Ok(size_of::<$f>()) +// }$( else if $t.is::>() { +// Ok(size_of::<$c>()) +// })* else { +// Err(LuaError::external("Unexpected type")) +// } +// }; +// } +// #[inline(always)] +// pub fn ctype_size_from_userdata(this: &LuaAnyUserData) -> LuaResult { +// define_ctype_size_from_userdata!( +// this, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64 +// ) // } -// fn test<'a>(ud: &'a LuaAnyUserData) -> LuaResult>> { -// Box::new([(ud.to_pointer() as *const CType) as *const dyn NativeConvert]) -// let ff: for<'lua> unsafe fn( +// macro_rules! define_ctype_luavalue_into_ptr { +// ($lua:ident, $this:ident, $offset:ident, $data_handle:ident, $value:ident, $f:ty, $( $c:ty ),*) => { +// if $this.is::>() { +// let ctype = $this.borrow::>()?; +// ctype.luavalue_into($lua, $offset, $data_handle, $value) +// }$( else if $this.is::>() { +// let ctype = $this.borrow::>()?; +// ctype.luavalue_into($lua, $offset, $data_handle, $value) +// })* else { +// Err(LuaError::external("Unexpected type")) +// } +// }; +// } +// #[inline(always)] +// pub unsafe fn ctype_luavalue_into_ptr<'lua>( // lua: &'lua Lua, -// type_userdata: &LuaAnyUserData<'lua>, +// this: &LuaAnyUserData<'lua>, // offset: isize, // data_handle: &Ref, // value: LuaValue<'lua>, -// ) -> LuaResult<()> = || CType::::luavalue_into; +// ) -> LuaResult<()> { +// define_ctype_luavalue_into_ptr!( +// lua, +// this, +// offset, +// data_handle, +// value, +// u8, +// u16, +// u32, +// u64, +// u128, +// i8, +// i16, +// i32, +// i64, +// i128, +// f32, +// f64 +// ) // } +// macro_rules! define_ctype_luavalue_from_ptr { +// ($lua:ident, $this:ident, $offset:ident, $data_handle:ident, $f:ty, $( $c:ty ),*) => { +// if $this.is::>() { +// $this.borrow::>()?.luavalue_from($lua, $offset, $data_handle) +// }$( else if $this.is::>() { +// $this.borrow::>()?.luavalue_from($lua, $offset, $data_handle) +// })* else { +// Err(LuaError::external("Unexpected type")) +// } +// }; +// } +// #[inline(always)] +// pub unsafe fn ctype_luavalue_from_ptr<'lua>( +// lua: &'lua Lua, +// this: &LuaAnyUserData<'lua>, +// offset: isize, +// data_handle: &Ref, +// ) -> LuaResult> { +// define_ctype_luavalue_from_ptr!( +// lua, +// this, +// offset, +// data_handle, +// u8, +// u16, +// u32, +// u64, +// u128, +// i8, +// i16, +// i32, +// i64, +// i128, +// f32, +// f64 +// ) +// } + +// Use UB method, but safe. because we use ffi_association to ensure children alive +// Much faster then get NativeConvert handle everytime from lua table +// it's spam of table.get(), if ud.is::() { ud.borrow::()? ... } macro_rules! define_get_ctype_conv { ($userdata:ident, $f:ty, $( $c:ty ),*) => { if $userdata.is::>() { diff --git a/crates/lune-std-ffi/src/ffi/ffi_box.rs b/crates/lune-std-ffi/src/ffi/ffi_box.rs index 7035ff4..63c1f1e 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_box.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_box.rs @@ -3,10 +3,12 @@ use std::sync::LazyLock; use mlua::prelude::*; -use super::association_names::REF_INNER; -use super::ffi_association::set_association; -use super::ffi_native::NativeDataHandle; -use super::ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag, FfiRefFlagList}; +use super::{ + association_names::REF_INNER, + ffi_association::set_association, + ffi_ref::{FfiRef, FfiRefBounds, FfiRefFlag, FfiRefFlagList}, + NativeDataHandle, +}; static BOX_REF_FLAGS: LazyLock = LazyLock::new(|| { FfiRefFlagList::new(&[ diff --git a/crates/lune-std-ffi/src/ffi/ffi_func.rs b/crates/lune-std-ffi/src/ffi/ffi_func.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/lune-std-ffi/src/ffi/ffi_helper.rs b/crates/lune-std-ffi/src/ffi/ffi_helper.rs deleted file mode 100644 index 3288ed5..0000000 --- a/crates/lune-std-ffi/src/ffi/ffi_helper.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![allow(clippy::inline_always)] - -// Converts ffi status into &str -pub const FFI_STATUS_NAMES: [&str; 4] = [ - "ffi_status_FFI_OK", - "ffi_status_FFI_BAD_TYPEDEF", - "ffi_status_FFI_BAD_ABI", - "ffi_status_FFI_BAD_ARGTYPE", -]; - -#[allow(unused)] -pub mod bit_mask { - pub const U8_MASK1: u8 = 1; - pub const U8_MASK2: u8 = 2; - pub const U8_MASK3: u8 = 4; - pub const U8_MASK4: u8 = 8; - pub const U8_MASK5: u8 = 16; - pub const U8_MASK6: u8 = 32; - pub const U8_MASK7: u8 = 64; - pub const U8_MASK8: u8 = 128; - - macro_rules! U8_TEST { - ($val:expr, $mask:ident) => { - ($val & $mask != 0) - }; - } - - pub(crate) use U8_TEST; -} diff --git a/crates/lune-std-ffi/src/ffi/ffi_lib.rs b/crates/lune-std-ffi/src/ffi/ffi_lib.rs index 6e393da..14d2cec 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_lib.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_lib.rs @@ -4,8 +4,11 @@ use std::sync::LazyLock; use dlopen2::symbor::Library; use mlua::prelude::*; -use super::ffi_association::set_association; -use super::ffi_ref::{FfiRef, FfiRefFlag, FfiRefFlagList, UNSIZED_BOUNDS}; +use super::{ + association_names::SYM_INNER, + ffi_association::set_association, + ffi_ref::{FfiRef, FfiRefFlag, FfiRefFlagList, UNSIZED_BOUNDS}, +}; static LIB_REF_FLAGS: LazyLock = LazyLock::new(|| { FfiRefFlagList::new(&[ @@ -18,8 +21,6 @@ static LIB_REF_FLAGS: LazyLock = LazyLock::new(|| { pub struct FfiLib(Library); -const SYM_INNER: &str = "__syn_inner"; - // COMMENT HERE // For convenience, it would be nice to provide a way to get // symbols from a table with type and field names specified. diff --git a/crates/lune-std-ffi/src/ffi/ffi_raw.rs b/crates/lune-std-ffi/src/ffi/ffi_raw.rs index 8348b3d..2827ffc 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_raw.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_raw.rs @@ -10,4 +10,4 @@ // relatively insecure operations, and help ensure that as little // data copy as possible occurs, while allowing you to do little restrictions. -pub struct FfiRaw(); +pub struct FfiRaw(*const ()); diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs b/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs index 1c3cdcb..7971644 100644 --- a/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs +++ b/crates/lune-std-ffi/src/ffi/ffi_ref/mod.rs @@ -2,15 +2,19 @@ use std::ptr; use mlua::prelude::*; -use super::association_names::REF_INNER; -use super::ffi_association::{get_association, set_association}; -use super::ffi_native::NativeDataHandle; +use super::{ + association_names::REF_INNER, + ffi_association::{get_association, set_association}, + NativeDataHandle, +}; mod bounds; mod flags; -pub use self::bounds::{FfiRefBounds, UNSIZED_BOUNDS}; -pub use self::flags::{FfiRefFlag, FfiRefFlagList}; +pub use self::{ + bounds::{FfiRefBounds, UNSIZED_BOUNDS}, + flags::{FfiRefFlag, FfiRefFlagList}, +}; // A referenced space. It is possible to read and write through types. // This operation is not safe. This may cause a memory error in Lua @@ -18,8 +22,6 @@ pub use self::flags::{FfiRefFlag, FfiRefFlagList}; // If it references an area managed by Lua, // the box will remain as long as this reference is alive. -// Todo : how to impl ref == nullptr - pub struct FfiRef { ptr: *mut (), pub flags: FfiRefFlagList, diff --git a/crates/lune-std-ffi/src/ffi/mod.rs b/crates/lune-std-ffi/src/ffi/mod.rs index 5f57be8..3eb02f9 100644 --- a/crates/lune-std-ffi/src/ffi/mod.rs +++ b/crates/lune-std-ffi/src/ffi/mod.rs @@ -18,6 +18,7 @@ pub use self::{ // Named registry table names mod association_names { pub const REF_INNER: &str = "__ref_inner"; + pub const SYM_INNER: &str = "__syn_inner"; } // Converts ffi status into &str