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 mlua::prelude::*;
use crate::association::{get_association, set_association};
use crate::cptr::CPtr;
use crate::ctype::{
libffi_type_ensured_size, libffi_type_from_userdata, type_userdata_stringify, CType,
use super::association_names::CARR_INNER;
use super::c_helper::{
get_ensured_size, name_from_userdata, stringify_userdata, type_from_userdata,
};
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.
// 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.
// 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 {
libffi_type: Type,
struct_type: Type,
@ -31,7 +31,7 @@ pub struct CArr {
impl CArr {
pub fn new(libffi_type: Type, length: usize) -> LuaResult<Self> {
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 {
libffi_type,
@ -47,7 +47,7 @@ impl CArr {
luatype: &LuaAnyUserData<'lua>,
length: usize,
) -> 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)?)?;
set_association(lua, CARR_INNER, carr.clone(), luatype)?;
@ -63,15 +63,26 @@ impl CArr {
pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult<String> {
let inner: LuaValue = userdata.get("inner")?;
let carr = userdata.borrow::<CArr>()?;
if inner.is_userdata() {
let inner = inner
.as_userdata()
.ok_or(LuaError::external("failed to get inner type userdata."))?;
Ok(format!(
" {} ; {} ",
type_userdata_stringify(inner)?,
carr.length
))
if inner.is::<CType>() {
Ok(format!(
" {} ; {} ",
stringify_userdata(inner)?,
carr.length
))
} else {
Ok(format!(
" <{}({})> ; {} ",
name_from_userdata(inner),
stringify_userdata(inner)?,
carr.length
))
}
} else {
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 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.
// 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> {
let args = libffi_types_from_table(&args)?;
let ret = libffi_type_from_userdata(&ret)?;
let args = type_list_from_table(&args)?;
let ret = type_from_userdata(&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 mlua::prelude::*;
use crate::association::{get_association, set_association};
use crate::carr::CArr;
use crate::ctype::{type_name_from_userdata, type_userdata_stringify};
const POINTER_INNER: &str = "__pointer_inner";
use super::association_names::CPTR_INNER;
use super::c_arr::CArr;
use super::c_helper::{name_from_userdata, stringify_userdata};
use crate::ffi::ffi_association::{get_association, set_association};
pub struct CPtr();
@ -22,7 +21,7 @@ impl CPtr {
) -> LuaResult<LuaValue<'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)
}
@ -37,8 +36,8 @@ impl CPtr {
.ok_or(LuaError::external("failed to get inner type userdata."))?;
Ok(format!(
" <{}({})> ",
type_name_from_userdata(inner),
type_userdata_stringify(inner)?,
name_from_userdata(inner),
stringify_userdata(inner)?,
))
} else {
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) {
fields.add_field_method_get("size", |_, _| Ok(size_of::<usize>()));
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(inner)
});

View file

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

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.
// 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

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

View file

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