Improve conversion performance by caching dyn handle on subtype (#243)

This commit is contained in:
qwreey 2024-08-30 04:04:24 +00:00
parent b54ea519ba
commit dd6a3861e5
No known key found for this signature in database
GPG key ID: D28DB79297A214BD
12 changed files with 244 additions and 176 deletions

View file

@ -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<Self> {
let struct_type = Type::structure(vec![element_type.clone(); length]);
pub fn new(
element_type: Type,
length: usize,
conv: *const dyn NativeConvert,
) -> LuaResult<Self> {
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<LuaAnyUserData<'lua>> {
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:
// <CStruct( u8, i32, size = 8 )>
@ -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<dyn NativeDataHandle>,
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<dyn NativeDataHandle>,
) -> LuaResult<LuaValue<'lua>> {
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<isize>)| {
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<isize>)| {
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)

View file

@ -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

View file

@ -88,7 +88,7 @@ impl<T> NativeSize for CType<T> {
}
}
pub struct CType<T: ?Sized> {
pub struct CType<T> {
// for ffi_ptrarray_to_raw?
// libffi_cif: Cif,
libffi_type: Type,

View file

@ -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;

View file

@ -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<Vec<(&'static str, LuaAnyUserDat
])
}
macro_rules! define_ctype_size_from_userdata {
($t:ident, $f:ty, $( $c:ty ),*) => {
if $t.is::<CType<$f>>() {
Ok(size_of::<$f>())
}$( else if $t.is::<CType<$c>>() {
Ok(size_of::<$c>())
})* else {
Err(LuaError::external("Unexpected type"))
}
};
}
#[inline(always)]
pub fn ctype_size_from_userdata(this: &LuaAnyUserData) -> LuaResult<usize> {
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::<CType<$f>>() {
let ctype = $this.borrow::<CType<$f>>()?;
ctype.luavalue_into($lua, $offset, $data_handle, $value)
}$( else if $this.is::<CType<$c>>() {
let ctype = $this.borrow::<CType<$c>>()?;
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<dyn NativeDataHandle>,
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::<CType<$f>>() {
$this.borrow::<CType<$f>>()?.luavalue_from($lua, $offset, $data_handle)
}$( else if $this.is::<CType<$c>>() {
$this.borrow::<CType<$c>>()?.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<dyn NativeDataHandle>,
) -> LuaResult<LuaValue<'lua>> {
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::<CType<$f>>() {
// Ok(size_of::<$f>())
// }$( else if $t.is::<CType<$c>>() {
// Ok(size_of::<$c>())
// })* else {
// Err(LuaError::external("Unexpected type"))
// }
// };
// }
// #[inline(always)]
// pub fn ctype_size_from_userdata(this: &LuaAnyUserData) -> LuaResult<usize> {
// 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<CastCache<'a>>> {
// Box::new([(ud.to_pointer() as *const CType<u8>) 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::<CType<$f>>() {
// let ctype = $this.borrow::<CType<$f>>()?;
// ctype.luavalue_into($lua, $offset, $data_handle, $value)
// }$( else if $this.is::<CType<$c>>() {
// let ctype = $this.borrow::<CType<$c>>()?;
// 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<dyn NativeDataHandle>,
// value: LuaValue<'lua>,
// ) -> LuaResult<()> = || CType::<f32>::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::<CType<$f>>() {
// $this.borrow::<CType<$f>>()?.luavalue_from($lua, $offset, $data_handle)
// }$( else if $this.is::<CType<$c>>() {
// $this.borrow::<CType<$c>>()?.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<dyn NativeDataHandle>,
// ) -> LuaResult<LuaValue<'lua>> {
// 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::<T>() { ud.borrow::<T>()? ... }
macro_rules! define_get_ctype_conv {
($userdata:ident, $f:ty, $( $c:ty ),*) => {
if $userdata.is::<CType<$f>>() {

View file

@ -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<FfiRefFlagList> = LazyLock::new(|| {
FfiRefFlagList::new(&[

View file

View file

@ -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;
}

View file

@ -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<FfiRefFlagList> = LazyLock::new(|| {
FfiRefFlagList::new(&[
@ -18,8 +21,6 @@ static LIB_REF_FLAGS: LazyLock<FfiRefFlagList> = 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.

View file

@ -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 ());

View file

@ -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,

View file

@ -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