Implement chelper, ffiref, ffibox (#243)

This commit is contained in:
qwreey 2024-08-24 08:21:28 +00:00
parent b36948cf1b
commit 6f131e9512
No known key found for this signature in database
GPG key ID: D28DB79297A214BD
22 changed files with 743 additions and 450 deletions

View file

@ -1,11 +1,13 @@
use libffi::middle::Type; use libffi::middle::Type;
use mlua::prelude::*; use mlua::prelude::*;
use crate::association::{get_association, set_association}; use super::association_names::CARR_INNER;
use crate::cptr::CPtr; use super::c_helper::{
use crate::ctype::{ get_ensured_size, name_from_userdata, stringify_userdata, type_from_userdata,
libffi_type_ensured_size, libffi_type_from_userdata, type_userdata_stringify, CType,
}; };
use super::c_ptr::CPtr;
use super::c_type::CType;
use crate::ffi::ffi_association::{get_association, set_association};
// This is a series of some type. // This is a series of some type.
// It provides the final size and the offset of the index, // It provides the final size and the offset of the index,
@ -18,8 +20,6 @@ use crate::ctype::{
// Padding after each field inside the struct is set to next field can follow the alignment. // Padding after each field inside the struct is set to next field can follow the alignment.
// 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. // 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.
const CARR_INNER: &str = "__carr_inner";
pub struct CArr { pub struct CArr {
libffi_type: Type, libffi_type: Type,
struct_type: Type, struct_type: Type,
@ -31,7 +31,7 @@ pub struct CArr {
impl CArr { impl CArr {
pub fn new(libffi_type: Type, length: usize) -> LuaResult<Self> { pub fn new(libffi_type: Type, length: usize) -> LuaResult<Self> {
let struct_type = Type::structure(vec![libffi_type.clone(); length]); let struct_type = Type::structure(vec![libffi_type.clone(); length]);
let field_size = libffi_type_ensured_size(libffi_type.as_raw_ptr())?; let field_size = get_ensured_size(libffi_type.as_raw_ptr())?;
Ok(Self { Ok(Self {
libffi_type, libffi_type,
@ -47,7 +47,7 @@ impl CArr {
luatype: &LuaAnyUserData<'lua>, luatype: &LuaAnyUserData<'lua>,
length: usize, length: usize,
) -> LuaResult<LuaAnyUserData<'lua>> { ) -> LuaResult<LuaAnyUserData<'lua>> {
let fields = libffi_type_from_userdata(luatype)?; let fields = type_from_userdata(luatype)?;
let carr = lua.create_userdata(Self::new(fields, length)?)?; let carr = lua.create_userdata(Self::new(fields, length)?)?;
set_association(lua, CARR_INNER, carr.clone(), luatype)?; set_association(lua, CARR_INNER, carr.clone(), luatype)?;
@ -63,15 +63,26 @@ impl CArr {
pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult<String> { pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult<String> {
let inner: LuaValue = userdata.get("inner")?; let inner: LuaValue = userdata.get("inner")?;
let carr = userdata.borrow::<CArr>()?; let carr = userdata.borrow::<CArr>()?;
if inner.is_userdata() { if inner.is_userdata() {
let inner = inner let inner = inner
.as_userdata() .as_userdata()
.ok_or(LuaError::external("failed to get inner type userdata."))?; .ok_or(LuaError::external("failed to get inner type userdata."))?;
Ok(format!(
" {} ; {} ", if inner.is::<CType>() {
type_userdata_stringify(inner)?, Ok(format!(
carr.length " {} ; {} ",
)) stringify_userdata(inner)?,
carr.length
))
} else {
Ok(format!(
" <{}({})> ; {} ",
name_from_userdata(inner),
stringify_userdata(inner)?,
carr.length
))
}
} else { } else {
Err(LuaError::external("failed to get inner type userdata.")) Err(LuaError::external("failed to get inner type userdata."))
} }

View file

@ -0,0 +1 @@

View file

@ -1,7 +1,7 @@
use libffi::middle::{Cif, Type}; use libffi::middle::{Cif, Type};
use mlua::prelude::*; use mlua::prelude::*;
use crate::ctype::{libffi_type_from_userdata, libffi_types_from_table}; use super::c_helper::{type_from_userdata, type_list_from_table};
// cfn is a type declaration for a function. // cfn is a type declaration for a function.
// Basically, when calling an external function, this type declaration // Basically, when calling an external function, this type declaration
@ -36,8 +36,8 @@ impl CFn {
} }
pub fn from_lua_table(args: LuaTable, ret: LuaAnyUserData) -> LuaResult<Self> { pub fn from_lua_table(args: LuaTable, ret: LuaAnyUserData) -> LuaResult<Self> {
let args = libffi_types_from_table(&args)?; let args = type_list_from_table(&args)?;
let ret = libffi_type_from_userdata(&ret)?; let ret = type_from_userdata(&ret)?;
Ok(Self::new(args, ret)) Ok(Self::new(args, ret))
} }
} }

View file

@ -0,0 +1,115 @@
use std::ptr::{self, null_mut};
use libffi::{low, middle::Type, raw};
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
use mlua::prelude::*;
use super::c_arr::CArr;
use super::c_ptr::CPtr;
use super::c_struct::CStruct;
use super::c_type::CType;
use crate::ffi::ffi_helper::FFI_STATUS_NAMES;
// get Vec<libffi_type> from table(array) of c-types userdata
pub fn type_list_from_table(table: &LuaTable) -> LuaResult<Vec<Type>> {
let len: usize = table.raw_len();
let mut fields = Vec::with_capacity(len);
for i in 0..len {
// Test required
let value = table.raw_get(i + 1)?;
match value {
LuaValue::UserData(field_type) => {
fields.push(type_from_userdata(&field_type)?);
}
_ => {
return Err(LuaError::external(format!(
"Unexpected field. CStruct, CType or CArr is required for element but got {}",
pretty_format_value(&value, &ValueFormatConfig::new())
)));
}
}
}
Ok(fields)
}
// get libffi_type from any c-type userdata
pub fn type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult<Type> {
if userdata.is::<CStruct>() {
Ok(userdata.borrow::<CStruct>()?.get_type())
} else if userdata.is::<CType>() {
Ok(userdata.borrow::<CType>()?.get_type())
} else if userdata.is::<CArr>() {
Ok(userdata.borrow::<CArr>()?.get_type())
} else if userdata.is::<CPtr>() {
Ok(CPtr::get_type())
} else {
Err(LuaError::external(format!(
"Unexpected field. CStruct, CType, CString or CArr is required for element but got {}",
pretty_format_value(
// Since the data is in the Lua location,
// there is no problem with the clone.
&LuaValue::UserData(userdata.to_owned()),
&ValueFormatConfig::new()
)
)))
}
}
// stringify any c-type userdata (for recursive)
pub fn stringify_userdata(userdata: &LuaAnyUserData) -> LuaResult<String> {
if userdata.is::<CType>() {
let name = userdata.borrow::<CType>()?.stringify();
Ok(name)
} else if userdata.is::<CStruct>() {
let name = CStruct::stringify(userdata)?;
Ok(name)
} else if userdata.is::<CArr>() {
let name = CArr::stringify(userdata)?;
Ok(name)
} else if userdata.is::<CPtr>() {
let name: String = CPtr::stringify(userdata)?;
Ok(name)
} else {
Ok(String::from("unnamed"))
}
}
// get name tag for any c-type userdata
pub fn name_from_userdata(userdata: &LuaAnyUserData) -> String {
if userdata.is::<CStruct>() {
String::from("CStruct")
} else if userdata.is::<CType>() {
String::from("CType")
} else if userdata.is::<CArr>() {
String::from("CArr")
} else if userdata.is::<CPtr>() {
String::from("CPtr")
} else {
String::from("unnamed")
}
}
// Ensure sizeof c-type (raw::libffi_type)
// See: http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html
pub fn get_ensured_size(ffi_type: *mut raw::ffi_type) -> LuaResult<usize> {
let mut cif = low::ffi_cif::default();
let result = unsafe {
raw::ffi_prep_cif(
ptr::from_mut(&mut cif),
raw::ffi_abi_FFI_DEFAULT_ABI,
0,
ffi_type,
null_mut(),
)
};
if result != raw::ffi_status_FFI_OK {
return Err(LuaError::external(format!(
"ffi_get_struct_offsets failed. expected result {}, got {}",
FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[result as usize]
)));
}
unsafe { Ok((*ffi_type).size) }
}

View file

@ -5,11 +5,10 @@ use std::borrow::Borrow;
use libffi::middle::Type; use libffi::middle::Type;
use mlua::prelude::*; use mlua::prelude::*;
use crate::association::{get_association, set_association}; use super::association_names::CPTR_INNER;
use crate::carr::CArr; use super::c_arr::CArr;
use crate::ctype::{type_name_from_userdata, type_userdata_stringify}; use super::c_helper::{name_from_userdata, stringify_userdata};
use crate::ffi::ffi_association::{get_association, set_association};
const POINTER_INNER: &str = "__pointer_inner";
pub struct CPtr(); pub struct CPtr();
@ -22,7 +21,7 @@ impl CPtr {
) -> LuaResult<LuaValue<'lua>> { ) -> LuaResult<LuaValue<'lua>> {
let value = Self().into_lua(lua)?; let value = Self().into_lua(lua)?;
set_association(lua, POINTER_INNER, value.borrow(), inner)?; set_association(lua, CPTR_INNER, value.borrow(), inner)?;
Ok(value) Ok(value)
} }
@ -37,8 +36,8 @@ impl CPtr {
.ok_or(LuaError::external("failed to get inner type userdata."))?; .ok_or(LuaError::external("failed to get inner type userdata."))?;
Ok(format!( Ok(format!(
" <{}({})> ", " <{}({})> ",
type_name_from_userdata(inner), name_from_userdata(inner),
type_userdata_stringify(inner)?, stringify_userdata(inner)?,
)) ))
} else { } else {
Err(LuaError::external("failed to get inner type userdata.")) Err(LuaError::external("failed to get inner type userdata."))
@ -55,7 +54,7 @@ impl LuaUserData for CPtr {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("size", |_, _| Ok(size_of::<usize>())); fields.add_field_method_get("size", |_, _| Ok(size_of::<usize>()));
fields.add_field_function_get("inner", |lua, this| { fields.add_field_function_get("inner", |lua, this| {
let inner = get_association(lua, POINTER_INNER, this)? let inner = get_association(lua, CPTR_INNER, this)?
.ok_or(LuaError::external("inner type not found"))?; .ok_or(LuaError::external("inner type not found"))?;
Ok(inner) Ok(inner)
}); });

View file

@ -9,13 +9,13 @@ use libffi::{
}; };
use mlua::prelude::*; use mlua::prelude::*;
use crate::ctype::{libffi_types_from_table, type_userdata_stringify, CType}; use super::association_names::CSTRUCT_INNER;
use crate::FFI_STATUS_NAMES; use super::c_arr::CArr;
use crate::{ use super::c_helper::{name_from_userdata, stringify_userdata, type_list_from_table};
association::{get_association, set_association}, use super::c_ptr::CPtr;
ctype::type_name_from_userdata, use super::c_type::CType;
}; use crate::ffi::ffi_association::{get_association, set_association};
use crate::{carr::CArr, cptr::CPtr}; use crate::ffi::ffi_helper::FFI_STATUS_NAMES;
pub struct CStruct { pub struct CStruct {
libffi_cif: Cif, libffi_cif: Cif,
@ -25,8 +25,6 @@ pub struct CStruct {
size: usize, size: usize,
} }
const CSTRUCT_INNER: &str = "__cstruct_inner";
impl CStruct { impl CStruct {
pub fn new(fields: Vec<Type>) -> LuaResult<Self> { pub fn new(fields: Vec<Type>) -> LuaResult<Self> {
let libffi_type = Type::structure(fields.clone()); let libffi_type = Type::structure(fields.clone());
@ -68,7 +66,7 @@ impl CStruct {
lua: &'lua Lua, lua: &'lua Lua,
table: LuaTable<'lua>, table: LuaTable<'lua>,
) -> LuaResult<LuaAnyUserData<'lua>> { ) -> LuaResult<LuaAnyUserData<'lua>> {
let fields = libffi_types_from_table(&table)?; let fields = type_list_from_table(&table)?;
let cstruct = lua.create_userdata(Self::new(fields)?)?; let cstruct = lua.create_userdata(Self::new(fields)?)?;
table.set_readonly(true); table.set_readonly(true);
set_association(lua, CSTRUCT_INNER, cstruct.clone(), table)?; set_association(lua, CSTRUCT_INNER, cstruct.clone(), table)?;
@ -88,13 +86,13 @@ impl CStruct {
for i in 0..table.raw_len() { for i in 0..table.raw_len() {
let child: LuaAnyUserData = table.raw_get(i + 1)?; let child: LuaAnyUserData = table.raw_get(i + 1)?;
if child.is::<CType>() { if child.is::<CType>() {
result.push_str(format!("{}, ", type_userdata_stringify(&child)?).as_str()); result.push_str(format!("{}, ", stringify_userdata(&child)?).as_str());
} else { } else {
result.push_str( result.push_str(
format!( format!(
"<{}({})>, ", "<{}({})>, ",
type_name_from_userdata(&child), name_from_userdata(&child),
type_userdata_stringify(&child)? stringify_userdata(&child)?
) )
.as_str(), .as_str(),
); );

View file

@ -0,0 +1,238 @@
#![allow(clippy::cargo_common_metadata)]
use core::ffi::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
c_ulong, c_ulonglong, c_ushort, c_void,
};
use libffi::middle::{Cif, Type};
use mlua::prelude::*;
use super::c_arr::CArr;
use super::c_helper::get_ensured_size;
use super::c_ptr::CPtr;
use crate::ffi::ffi_helper::get_ptr_from_userdata;
use crate::ffi::ffi_platform::CHAR_IS_SIGNED;
// use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
pub struct CType {
libffi_cif: Cif,
libffi_type: Type,
size: usize,
name: Option<String>,
// Write converted data from luavalue into some ptr
pub luavalue_into_ptr: fn(value: LuaValue, ptr: *mut c_void) -> LuaResult<()>,
// Read luavalue from some ptr
pub ptr_into_luavalue: fn(lua: &Lua, ptr: *mut c_void) -> LuaResult<LuaValue>,
}
impl CType {
pub fn new(
libffi_type: Type,
name: Option<String>,
luavalue_into_ptr: fn(value: LuaValue, ptr: *mut c_void) -> LuaResult<()>,
ptr_into_luavalue: fn(lua: &Lua, ptr: *mut c_void) -> LuaResult<LuaValue>,
) -> LuaResult<Self> {
let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
let size = get_ensured_size(libffi_type.as_raw_ptr())?;
Ok(Self {
libffi_cif: libffi_cfi,
libffi_type,
size,
name,
luavalue_into_ptr,
ptr_into_luavalue,
})
}
pub fn get_type(&self) -> Type {
self.libffi_type.clone()
}
pub fn stringify(&self) -> String {
match &self.name {
Some(t) => t.to_owned(),
None => String::from("unnamed"),
}
}
// Read data from ptr and convert it into luavalue
pub unsafe fn read_ptr<'lua>(
&self,
lua: &'lua Lua,
userdata: LuaAnyUserData<'lua>,
offset: Option<isize>,
) -> LuaResult<LuaValue<'lua>> {
let ptr = unsafe { get_ptr_from_userdata(&userdata, offset)? };
let value = (self.ptr_into_luavalue)(lua, ptr)?;
Ok(value)
}
// Write converted data from luavalue into ptr
pub unsafe fn write_ptr<'lua>(
&self,
luavalue: LuaValue<'lua>,
userdata: LuaAnyUserData<'lua>,
offset: Option<isize>,
) -> LuaResult<()> {
let ptr = unsafe { get_ptr_from_userdata(&userdata, offset)? };
(self.luavalue_into_ptr)(luavalue, ptr)?;
Ok(())
}
}
impl LuaUserData for CType {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("size", |_, this| Ok(this.size));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
let pointer = CPtr::from_lua_userdata(lua, &this)?;
Ok(pointer)
});
methods.add_method(
"from",
|lua, ctype, (userdata, offset): (LuaAnyUserData, Option<isize>)| {
let value = unsafe { ctype.read_ptr(lua, userdata, offset)? };
Ok(value)
},
);
methods.add_method(
"into",
|_, ctype, (value, userdata, offset): (LuaValue, LuaAnyUserData, Option<isize>)| {
unsafe { ctype.write_ptr(value, userdata, offset)? };
Ok(())
},
);
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
let carr = CArr::from_lua_userdata(lua, &this, length)?;
Ok(carr)
});
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
let name = this.stringify();
Ok(name)
});
}
}
// export all default c-types
#[allow(clippy::too_many_lines)]
pub fn create_all_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
Ok(vec![
(
"int",
CType::new(
Type::c_int(),
Some(String::from("int")),
|data, ptr| {
let value = match data {
LuaValue::Integer(t) => t,
_ => {
return Err(LuaError::external(format!(
"Integer expected, got {}",
data.type_name()
)))
}
} as c_int;
unsafe {
*(ptr.cast::<c_int>()) = value;
}
Ok(())
},
|lua: &Lua, ptr: *mut c_void| {
let value = unsafe { (*ptr.cast::<c_int>()).into_lua(lua)? };
Ok(value)
},
)?
.into_lua(lua)?,
),
(
"long",
CType::new(
Type::c_long(),
Some(String::from("long")),
|data, ptr| {
let value = match data {
LuaValue::Integer(t) => t,
_ => {
return Err(LuaError::external(format!(
"Integer expected, got {}",
data.type_name()
)))
}
} as c_long;
unsafe {
*(ptr.cast::<c_long>()) = value;
}
Ok(())
},
|lua: &Lua, ptr: *mut c_void| {
let value = unsafe { (*ptr.cast::<c_long>()).into_lua(lua)? };
Ok(value)
},
)?
.into_lua(lua)?,
),
(
"longlong",
CType::new(
Type::c_longlong(),
Some(String::from("longlong")),
|data, ptr| {
let value = match data {
LuaValue::Integer(t) => t,
_ => {
return Err(LuaError::external(format!(
"Integer expected, got {}",
data.type_name()
)))
}
} as c_longlong;
unsafe {
*(ptr.cast::<c_longlong>()) = value;
}
Ok(())
},
|lua: &Lua, ptr: *mut c_void| {
let value = unsafe { (*ptr.cast::<c_longlong>()).into_lua(lua)? };
Ok(value)
},
)?
.into_lua(lua)?,
),
(
"char",
CType::new(
if CHAR_IS_SIGNED {
Type::c_schar()
} else {
Type::c_uchar()
},
Some(String::from("char")),
|data, ptr| {
let value = match data {
LuaValue::Integer(t) => t,
_ => {
return Err(LuaError::external(format!(
"Integer expected, got {}",
data.type_name()
)))
}
} as c_char;
unsafe {
*(ptr.cast::<c_char>()) = value;
}
Ok(())
},
|lua: &Lua, ptr: *mut c_void| {
let value = unsafe { (*ptr.cast::<c_char>()).into_lua(lua)? };
Ok(value)
},
)?
.into_lua(lua)?,
),
])
}

View file

@ -0,0 +1,14 @@
pub(super) mod c_arr;
pub(super) mod c_fn;
pub(super) mod c_helper;
pub(super) mod c_ptr;
pub(super) mod c_string;
pub(super) mod c_struct;
pub(super) mod c_type;
// Named registry table names
mod association_names {
pub const CPTR_INNER: &str = "__cptr_inner";
pub const CARR_INNER: &str = "__carr_inner";
pub const CSTRUCT_INNER: &str = "__cstruct_inner";
}

View file

@ -1,227 +0,0 @@
#![allow(clippy::cargo_common_metadata)]
use std::borrow::Borrow;
use std::ptr::{self, null_mut};
use libffi::{
low,
middle::{Cif, Type},
raw,
};
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
use mlua::prelude::*;
use crate::association::{get_association, set_association};
use crate::carr::CArr;
use crate::cptr::CPtr;
use crate::cstruct::CStruct;
use crate::FFI_STATUS_NAMES;
// use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
const POINTER_INNER: &str = "__pointer_inner";
pub struct CType {
libffi_cif: Cif,
libffi_type: Type,
size: usize,
name: Option<String>,
}
impl CType {
pub fn new(libffi_type: Type, name: Option<String>) -> LuaResult<Self> {
let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
let size = libffi_type_ensured_size(libffi_type.as_raw_ptr())?;
Ok(Self {
libffi_cif: libffi_cfi,
libffi_type,
size,
name,
})
}
pub fn get_type(&self) -> Type {
self.libffi_type.clone()
}
pub fn stringify(&self) -> String {
match &self.name {
Some(t) => t.to_owned(),
None => String::from("unnamed"),
}
}
}
impl LuaUserData for CType {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("size", |_, this| Ok(this.size));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
let pointer = CPtr::from_lua_userdata(lua, &this)?;
Ok(pointer)
});
methods.add_function("arr", |lua, (this, length): (LuaAnyUserData, usize)| {
let carr = CArr::from_lua_userdata(lua, &this, length)?;
Ok(carr)
});
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
let name = this.stringify();
Ok(name)
});
}
}
// export all default c-types
pub fn create_all_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
Ok(vec![
(
"u8",
CType::new(Type::u8(), Some(String::from("u8")))?.into_lua(lua)?,
),
(
"u16",
CType::new(Type::u16(), Some(String::from("u16")))?.into_lua(lua)?,
),
(
"u32",
CType::new(Type::u32(), Some(String::from("u32")))?.into_lua(lua)?,
),
(
"u64",
CType::new(Type::u64(), Some(String::from("u64")))?.into_lua(lua)?,
),
(
"i8",
CType::new(Type::i8(), Some(String::from("i8")))?.into_lua(lua)?,
),
(
"i16",
CType::new(Type::i16(), Some(String::from("i16")))?.into_lua(lua)?,
),
(
"i32",
CType::new(Type::i32(), Some(String::from("i32")))?.into_lua(lua)?,
),
(
"i64",
CType::new(Type::i64(), Some(String::from("i64")))?.into_lua(lua)?,
),
(
"f32",
CType::new(Type::f32(), Some(String::from("f32")))?.into_lua(lua)?,
),
(
"f64",
CType::new(Type::f64(), Some(String::from("f64")))?.into_lua(lua)?,
),
(
"void",
CType::new(Type::void(), Some(String::from("void")))?.into_lua(lua)?,
),
])
}
// get Vec<libffi_type> from table(array) of c-types userdata
pub fn libffi_types_from_table(table: &LuaTable) -> LuaResult<Vec<Type>> {
let len: usize = table.raw_len();
let mut fields = Vec::with_capacity(len);
for i in 0..len {
// Test required
let value = table.raw_get(i + 1)?;
match value {
LuaValue::UserData(field_type) => {
fields.push(libffi_type_from_userdata(&field_type)?);
}
_ => {
return Err(LuaError::external(format!(
"Unexpected field. CStruct, CType or CArr is required for element but got {}",
pretty_format_value(&value, &ValueFormatConfig::new())
)));
}
}
}
Ok(fields)
}
// get libffi_type from any c-type userdata
pub fn libffi_type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult<Type> {
if userdata.is::<CStruct>() {
Ok(userdata.borrow::<CStruct>()?.get_type())
} else if userdata.is::<CType>() {
Ok(userdata.borrow::<CType>()?.get_type())
} else if userdata.is::<CArr>() {
Ok(userdata.borrow::<CArr>()?.get_type())
} else if userdata.is::<CPtr>() {
Ok(CPtr::get_type())
} else {
Err(LuaError::external(format!(
"Unexpected field. CStruct, CType, CString or CArr is required for element but got {}",
pretty_format_value(
// Since the data is in the Lua location,
// there is no problem with the clone.
&LuaValue::UserData(userdata.to_owned()),
&ValueFormatConfig::new()
)
)))
}
}
// stringify any c-type userdata (for recursive)
pub fn type_userdata_stringify(userdata: &LuaAnyUserData) -> LuaResult<String> {
if userdata.is::<CType>() {
let name = userdata.borrow::<CType>()?.stringify();
Ok(name)
} else if userdata.is::<CStruct>() {
let name = CStruct::stringify(userdata)?;
Ok(name)
} else if userdata.is::<CArr>() {
let name = CArr::stringify(userdata)?;
Ok(name)
} else if userdata.is::<CPtr>() {
let name: String = CPtr::stringify(userdata)?;
Ok(name)
} else {
Ok(String::from("unnamed"))
}
}
// get name tag for any c-type userdata
pub fn type_name_from_userdata(userdata: &LuaAnyUserData) -> String {
if userdata.is::<CStruct>() {
String::from("CStruct")
} else if userdata.is::<CType>() {
String::from("CType")
} else if userdata.is::<CArr>() {
String::from("CArr")
} else if userdata.is::<CPtr>() {
String::from("CPtr")
} else {
String::from("unnamed")
}
}
// Ensure sizeof c-type (raw::libffi_type)
// See: http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html
pub fn libffi_type_ensured_size(ffi_type: *mut raw::ffi_type) -> LuaResult<usize> {
let mut cif: low::ffi_cif = Default::default();
let result = unsafe {
raw::ffi_prep_cif(
ptr::from_mut(&mut cif),
raw::ffi_abi_FFI_DEFAULT_ABI,
0,
ffi_type,
null_mut(),
)
};
if result != raw::ffi_status_FFI_OK {
return Err(LuaError::external(format!(
"ffi_get_struct_offsets failed. expected result {}, got {}",
FFI_STATUS_NAMES[0], FFI_STATUS_NAMES[result as usize]
)));
}
unsafe { Ok((*ffi_type).size) }
}

View file

@ -0,0 +1,137 @@
#![allow(clippy::cargo_common_metadata)]
// It is an untyped, sized memory area that Lua can manage.
// This area is safe within Lua. Operations have their boundaries checked.
// It is basically intended to implement passing a pointed space to the outside.
// It also helps you handle data that Lua cannot handle.
// Depending on the type, operations such as sum, mul, and mod may be implemented.
// There is no need to enclose all data in a box;
// rather, it creates more heap space, so it should be used appropriately
// where necessary.
use std::boxed::Box;
use core::ffi::c_void;
use mlua::prelude::*;
use super::association_names::BOX_REF_INNER;
use super::ffi_association::set_association;
use super::ffi_ref::FfiRange;
use super::ffi_ref::FfiRef;
pub struct FfiBox(Box<[u8]>);
impl FfiBox {
// For efficiency, it is initialized non-zeroed.
pub fn new(size: usize) -> Self {
// Create new vector to allocate heap memory. sized with 'size'
let mut vec_heap = Vec::<u8>::with_capacity(size);
// It is safe to have a length equal to the capacity
#[allow(clippy::uninit_vec)]
unsafe {
vec_heap.set_len(size);
}
Self(vec_heap.into_boxed_slice())
}
pub fn size(&self) -> usize {
self.0.len()
}
// pub fn copy(&self, target: &mut FfiBox) {}
pub fn get_ptr(&self) -> *mut c_void {
self.0.as_ptr() as *mut c_void
}
pub fn stringify(&self) -> String {
let mut buff = String::from(" ");
for i in &self.0 {
buff.push_str(i.to_string().as_str());
buff.push_str(", ");
}
buff.pop();
buff.pop();
buff.push(' ');
buff
}
pub fn binary_print(&self) -> String {
let mut buff: String = String::with_capacity(self.size() * 10 - 2);
for (pos, value) in self.0.iter().enumerate() {
for i in 0..8 {
if (value & (1 << i)) == 0 {
buff.push('0');
} else {
buff.push('1');
}
}
if pos < self.size() - 1 {
buff.push_str(", ");
}
}
buff
}
// bad naming. i have no idea what should i use
pub fn luaref<'lua>(
lua: &'lua Lua,
this: LuaAnyUserData<'lua>,
offset: Option<isize>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let target = this.borrow::<FfiBox>()?;
let ptr = if let Some(t) = offset {
if t < 0 || t >= (target.size() as isize) {
return Err(LuaError::external(format!(
"Offset is out of bounds. box.size: {}. offset got {}",
target.size(),
t
)));
}
unsafe { target.get_ptr().offset(t) }
} else {
target.get_ptr()
};
let luaref = lua.create_userdata(FfiRef::new(
ptr,
Some(FfiRange {
low: 0,
high: target.size() as isize,
}),
))?;
set_association(lua, BOX_REF_INNER, luaref.clone(), this.clone())?;
Ok(luaref)
}
pub fn zero(&mut self) {
self.0.fill(0u8);
}
}
impl LuaUserData for FfiBox {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("size", |_, this| Ok(this.size()));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function_mut("zero", |_, this: LuaAnyUserData| {
this.borrow_mut::<FfiBox>()?.zero();
Ok(this)
});
methods.add_function(
"ref",
|lua, (this, offset): (LuaAnyUserData, Option<isize>)| {
let luaref = FfiBox::luaref(lua, this, offset)?;
Ok(luaref)
},
);
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
Ok(this.binary_print())
});
}
}

View file

@ -0,0 +1,35 @@
use std::ffi::c_void;
use mlua::prelude::*;
use super::ffi_box::FfiBox;
use super::ffi_ref::FfiRef;
// 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",
];
pub unsafe fn get_ptr_from_userdata(
userdata: &LuaAnyUserData,
offset: Option<isize>,
) -> LuaResult<*mut c_void> {
let ptr = if userdata.is::<FfiBox>() {
userdata.borrow::<FfiBox>()?.get_ptr()
} else if userdata.is::<FfiRef>() {
userdata.borrow::<FfiRef>()?.get_ptr()
} else {
return Err(LuaError::external("asdf"));
};
let ptr = if let Some(t) = offset {
ptr.offset(t)
} else {
ptr
};
Ok(ptr)
}

View file

@ -3,8 +3,8 @@ use std::ffi::c_void;
use dlopen2::symbor::Library; use dlopen2::symbor::Library;
use mlua::prelude::*; use mlua::prelude::*;
use crate::association::set_association; use super::ffi_association::set_association;
use crate::ffiref::FfiRef; use super::ffi_ref::FfiRef;
pub struct FfiLib(Library); pub struct FfiLib(Library);
@ -39,7 +39,7 @@ impl FfiLib {
.map_err(|err| LuaError::external(format!("{err}")))? .map_err(|err| LuaError::external(format!("{err}")))?
}; };
let luasym = lua.create_userdata(FfiRef::new(*sym))?; let luasym = lua.create_userdata(FfiRef::new(*sym, None))?;
set_association(lua, SYM_INNER, luasym.clone(), this.clone())?; set_association(lua, SYM_INNER, luasym.clone(), this.clone())?;

View file

@ -0,0 +1,22 @@
use core::ffi::c_char;
use std::env::consts;
use std::vec::Vec;
pub const CHAR_IS_SIGNED: bool = c_char::MIN as u8 != u8::MIN;
pub const IS_LITTLE_ENDIAN: bool = cfg!(target_endian = "little");
pub fn get_platform_value() -> Vec<(&'static str, &'static str)> {
vec![
// https://doc.rust-lang.org/std/env/consts/constant.ARCH.html
("arch", consts::ARCH),
// https://doc.rust-lang.org/std/env/consts/constant.OS.html
("os", consts::OS),
// https://doc.rust-lang.org/std/env/consts/constant.FAMILY.html
("family", consts::FAMILY),
("endian", if IS_LITTLE_ENDIAN { "little" } else { "big" }),
(
"char_variant",
if CHAR_IS_SIGNED { "schar" } else { "uchar" },
),
]
}

View file

@ -1,3 +1,6 @@
use core::ffi::c_void;
use std::{convert, mem::transmute, ptr};
// This is raw data coming from outside. // This is raw data coming from outside.
// Users must convert it to a Lua value, reference, or box to use it. // Users must convert it to a Lua value, reference, or box to use it.
// The biggest reason for providing this is to allow the user to // The biggest reason for providing this is to allow the user to

View file

@ -0,0 +1,94 @@
use core::ffi::c_void;
use std::ptr;
use mlua::prelude::*;
use super::association_names::REF_INNER;
use super::ffi_association::set_association;
// 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
// if use it incorrectly.
// If it references an area managed by Lua,
// the box will remain as long as this reference is alive.
pub struct FfiRange {
pub(crate) high: isize,
pub(crate) low: isize,
}
pub struct FfiRef {
ptr: *mut c_void,
range: Option<FfiRange>,
}
impl FfiRef {
pub fn new(ptr: *mut c_void, range: Option<FfiRange>) -> Self {
Self { ptr, range }
}
// bad naming. i have no idea what should i use
pub fn luaref<'lua>(
lua: &'lua Lua,
this: LuaAnyUserData<'lua>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let target = this.borrow::<FfiRef>()?;
let luaref = lua.create_userdata(FfiRef::new(
ptr::from_ref(&target.ptr) as *mut c_void,
Some(FfiRange {
low: 0,
high: size_of::<usize>() as isize,
}),
))?;
set_association(lua, REF_INNER, luaref.clone(), this.clone())?;
Ok(luaref)
}
pub fn get_ptr(&self) -> *mut c_void {
self.ptr
}
pub unsafe fn deref(&self) -> Self {
Self::new(*self.ptr.cast::<*mut c_void>(), None)
}
pub unsafe fn offset(&self, offset: isize) -> LuaResult<Self> {
let range = if let Some(ref t) = self.range {
let high = t.high - offset;
let low = t.low - offset;
if low > 0 || high < 0 {
return Err(LuaError::external(format!(
"Offset is out of bounds. low: {}, high: {}. offset got {}",
t.low, t.high, offset
)));
}
Some(FfiRange { high, low })
} else {
None
};
Ok(Self::new(self.ptr.offset(offset), range))
}
}
impl LuaUserData for FfiRef {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("deref", |_, this, ()| {
let ffiref = unsafe { this.deref() };
Ok(ffiref)
});
methods.add_method("offset", |_, this, offset: isize| {
let ffiref = unsafe { this.offset(offset)? };
Ok(ffiref)
});
methods.add_function("ref", |lua, this: LuaAnyUserData| {
let ffiref = FfiRef::luaref(lua, this)?;
Ok(ffiref)
});
}
}

View file

@ -0,0 +1,13 @@
pub(super) mod ffi_association;
pub(super) mod ffi_box;
pub(super) mod ffi_helper;
pub(super) mod ffi_lib;
pub(super) mod ffi_platform;
pub(super) mod ffi_raw;
pub(super) mod ffi_ref;
// Named registry table names
mod association_names {
pub const BOX_REF_INNER: &str = "__box_ref";
pub const REF_INNER: &str = "__ref_inner";
}

View file

@ -1,87 +0,0 @@
#![allow(clippy::cargo_common_metadata)]
// It is an untyped, sized memory area that Lua can manage.
// This area is safe within Lua. Operations have their boundaries checked.
// It is basically intended to implement passing a pointed space to the outside.
// It also helps you handle data that Lua cannot handle.
// Depending on the type, operations such as sum, mul, and mod may be implemented.
// There is no need to enclose all data in a box;
// rather, it creates more heap space, so it should be used appropriately
// where necessary.
use std::boxed::Box;
use core::ffi::c_void;
use mlua::prelude::*;
use crate::association::set_association;
use crate::ffiref::FfiRef;
const BOX_REF_INNER: &str = "__box_ref";
pub struct FfiBox(Box<[u8]>);
impl FfiBox {
pub fn new(size: usize) -> Self {
Self(vec![0u8; size].into_boxed_slice())
}
pub fn size(&self) -> usize {
self.0.len()
}
// pub fn copy(&self, target: &mut FfiBox) {}
pub fn get_ptr(&self) -> *mut c_void {
self.0.as_ptr() as *mut c_void
}
// bad naming. i have no idea what should i use
pub fn luaref<'lua>(
lua: &'lua Lua,
this: LuaAnyUserData<'lua>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let target = this.borrow::<FfiBox>()?;
let luaref = lua.create_userdata(FfiRef::new(target.get_ptr()))?;
set_association(lua, BOX_REF_INNER, luaref.clone(), this.clone())?;
Ok(luaref)
}
pub fn zero(&mut self) {
self.0.fill(0u8);
}
}
impl LuaUserData for FfiBox {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("size", |_, this| Ok(this.size()));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method_mut("zero", |_, this, ()| {
this.zero();
Ok(())
});
methods.add_function("ref", |lua, this: LuaAnyUserData| {
let luaref = FfiBox::luaref(lua, this)?;
Ok(luaref)
});
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.size()));
methods.add_meta_method(LuaMetaMethod::ToString, |lua, this, ()| {
dbg!(&this.0.len());
let mut buff = String::from("[ ");
for i in &this.0 {
buff.push_str(i.to_owned().to_string().as_str());
buff.push_str(", ");
}
buff.pop();
buff.pop();
buff.push_str(" ]");
let luastr = lua.create_string(buff.as_bytes())?;
Ok(luastr)
});
}
}

View file

@ -1,61 +0,0 @@
use core::ffi::c_void;
use std::ptr;
use mlua::prelude::*;
use crate::association::set_association;
// 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
// if use it incorrectly.
// If it references an area managed by Lua,
// the box will remain as long as this reference is alive.
pub struct FfiRef(*mut c_void);
const REF_INNER: &str = "__ref_inner";
impl FfiRef {
pub fn new(target: *mut c_void) -> Self {
Self(target)
}
// bad naming. i have no idea what should i use
pub fn luaref<'lua>(
lua: &'lua Lua,
this: LuaAnyUserData<'lua>,
) -> LuaResult<LuaAnyUserData<'lua>> {
let target = this.borrow::<FfiRef>()?;
let luaref = lua.create_userdata(FfiRef::new(ptr::from_ref(&target.0) as *mut c_void))?;
set_association(lua, REF_INNER, luaref.clone(), this.clone())?;
Ok(luaref)
}
pub unsafe fn deref(&self) -> Self {
Self::new(*self.0.cast::<*mut c_void>())
}
pub unsafe fn offset(&self, offset: isize) -> Self {
Self::new(self.0.offset(offset))
}
}
impl LuaUserData for FfiRef {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("deref", |_, this, ()| {
let ffiref = unsafe { this.deref() };
Ok(ffiref)
});
methods.add_method("offset", |_, this, offset: isize| {
let ffiref = unsafe { this.offset(offset) };
Ok(ffiref)
});
methods.add_function("ref", |lua, this: LuaAnyUserData| {
let ffiref = FfiRef::luaref(lua, this)?;
Ok(ffiref)
});
}
}

View file

@ -3,31 +3,16 @@
use lune_utils::TableBuilder; use lune_utils::TableBuilder;
use mlua::prelude::*; use mlua::prelude::*;
mod association; mod c;
mod carr; mod ffi;
mod cfn;
mod cptr;
mod cstring;
mod cstruct;
mod ctype;
mod ffibox;
mod ffilib;
mod ffiraw;
mod ffiref;
use crate::association::get_table; use crate::c::c_fn::CFn;
use crate::cfn::CFn; use crate::c::c_struct::CStruct;
use crate::cstruct::CStruct; use crate::c::c_type::create_all_types;
use crate::ctype::create_all_types; use crate::ffi::ffi_association::get_table;
use crate::ffibox::FfiBox; use crate::ffi::ffi_box::FfiBox;
use crate::ffilib::FfiLib; use crate::ffi::ffi_lib::FfiLib;
use crate::ffi::ffi_platform::get_platform_value;
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",
];
/** /**
Creates the `ffi` standard library module. Creates the `ffi` standard library module.
@ -40,6 +25,7 @@ pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
let ctypes = create_all_types(lua)?; let ctypes = create_all_types(lua)?;
let result = TableBuilder::new(lua)? let result = TableBuilder::new(lua)?
.with_values(ctypes)? .with_values(ctypes)?
.with_values(get_platform_value())?
.with_function("box", |_, size: usize| Ok(FfiBox::new(size)))? .with_function("box", |_, size: usize| Ok(FfiBox::new(size)))?
// TODO: discuss about function name. matching with io.open is better? // TODO: discuss about function name. matching with io.open is better?
.with_function("dlopen", |_, name: String| { .with_function("dlopen", |_, name: String| {

View file

@ -1,10 +1,9 @@
use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw}; use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
// pub fn ffi_get_struct_offsets( // pub fn ffi_get_struct_offsets(
// abi: ffi_abi, // abi: ffi_abi,
// struct_type: *mut ffi_type, // struct_type: *mut ffi_type,
// offsets: *mut usize, // offsets: *mut usize,
// ) -> ffi_status; // ) -> ffi_status;
- last thing to do - last thing to do
@ -14,10 +13,10 @@ use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
# Raw # Raw
- [ ] Raw:toRef() - [ ] Raw:toRef()
- [ ] Raw:toBox() - [ ] Raw:toBox()
- [ ] Raw:intoBox() - [ ] Raw:intoBox()
- [ ] Raw:intoRef() - [ ] Raw:intoRef()
# Box # Box
@ -31,6 +30,9 @@ use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
# Ref (Unsafe) # Ref (Unsafe)
- [ ] high, low Boundaries
- [ ] iter
- [x] ref:deref() -> ref - [x] ref:deref() -> ref
- [x] ref:offset(bytes) -> ref - [x] ref:offset(bytes) -> ref
- [x] ref:ref() -> ref - [x] ref:ref() -> ref
@ -70,12 +72,11 @@ from(box|ref|raw, offset) is better idea i think.
- [ ] :sub - [ ] :sub
## subtype ## subtype
- [x] :ptr() -> Ptr - [x] :ptr() -> Ptr
- [~] :arr(len) -> Arr - [~] :arr(len) -> Arr
- [x] .size - [x] .size
# Ptr # Ptr
- [x] .inner - [x] .inner
@ -92,6 +93,7 @@ from(box|ref|raw, offset) is better idea i think.
Zero sized type. Zero sized type.
## Fn ## Fn
Prototype type of some function. converts lua function into native function pointer or native function pointer into lua function. Prototype type of some function. converts lua function into native function pointer or native function pointer into lua function.
`ffi.fn({ type }, type) -> fn` `ffi.fn({ type }, type) -> fn`