From b36948cf1b524b798a6bbdac24953f1808605fd8 Mon Sep 17 00:00:00 2001 From: qwreey Date: Sat, 24 Aug 2024 06:33:29 +0000 Subject: [PATCH] Implement carr, cstruct, ctype and cptr (#243) --- crates/lune-std-ffi/src/carr.rs | 78 +++++++++++++++++++-- crates/lune-std-ffi/src/cptr.rs | 78 +++++++++++++++++++++ crates/lune-std-ffi/src/cstruct.rs | 33 +++++++-- crates/lune-std-ffi/src/ctype.rs | 105 ++++++++++++++--------------- crates/lune-std-ffi/src/lib.rs | 1 + 5 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 crates/lune-std-ffi/src/cptr.rs diff --git a/crates/lune-std-ffi/src/carr.rs b/crates/lune-std-ffi/src/carr.rs index fc38e8b..886a01f 100644 --- a/crates/lune-std-ffi/src/carr.rs +++ b/crates/lune-std-ffi/src/carr.rs @@ -1,7 +1,11 @@ use libffi::middle::Type; use mlua::prelude::*; -use crate::ctype::libffi_type_ensured_size; +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, +}; // This is a series of some type. // It provides the final size and the offset of the index, @@ -14,7 +18,9 @@ use crate::ctype::libffi_type_ensured_size; // 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. -struct CArr { +const CARR_INNER: &str = "__carr_inner"; + +pub struct CArr { libffi_type: Type, struct_type: Type, length: usize, @@ -23,7 +29,7 @@ struct CArr { } impl CArr { - fn new(libffi_type: Type, length: usize) -> LuaResult { + pub fn new(libffi_type: Type, length: usize) -> LuaResult { let struct_type = Type::structure(vec![libffi_type.clone(); length]); let field_size = libffi_type_ensured_size(libffi_type.as_raw_ptr())?; @@ -35,6 +41,70 @@ impl CArr { size: field_size * length, }) } + + pub fn from_lua_userdata<'lua>( + lua: &'lua Lua, + luatype: &LuaAnyUserData<'lua>, + length: usize, + ) -> LuaResult> { + let fields = libffi_type_from_userdata(luatype)?; + let carr = lua.create_userdata(Self::new(fields, length)?)?; + + set_association(lua, CARR_INNER, carr.clone(), luatype)?; + Ok(carr) + } + + pub fn get_type(&self) -> Type { + self.libffi_type.clone() + } + + // Stringify cstruct for pretty printing something like: + // + pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult { + let inner: LuaValue = userdata.get("inner")?; + let carr = userdata.borrow::()?; + 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 + )) + } else { + Err(LuaError::external("failed to get inner type userdata.")) + } + } } -impl LuaUserData for CArr {} +impl LuaUserData for CArr { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("size", |_, this| Ok(this.size)); + fields.add_field_method_get("length", |_, this| Ok(this.length)); + fields.add_field_function_get("inner", |lua, this: LuaAnyUserData| { + let inner: LuaValue = get_association(lua, CARR_INNER, this)? + // It shouldn't happen. + .ok_or(LuaError::external("inner field not found"))?; + Ok(inner) + }); + } + + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("offset", |_, this, offset: isize| { + if this.length > (offset as usize) && offset >= 0 { + Ok(this.field_size * (offset as usize)) + } else { + Err(LuaError::external("Out of index")) + } + }); + methods.add_function("ptr", |lua, this: LuaAnyUserData| { + let pointer = CPtr::from_lua_userdata(lua, &this)?; + Ok(pointer) + }); + methods.add_meta_function(LuaMetaMethod::ToString, |_, this: LuaAnyUserData| { + let result = CArr::stringify(&this)?; + Ok(result) + }); + } +} diff --git a/crates/lune-std-ffi/src/cptr.rs b/crates/lune-std-ffi/src/cptr.rs new file mode 100644 index 0000000..62324d1 --- /dev/null +++ b/crates/lune-std-ffi/src/cptr.rs @@ -0,0 +1,78 @@ +#![allow(clippy::cargo_common_metadata)] + +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"; + +pub struct CPtr(); + +impl CPtr { + // Create pointer type with '.inner' field + // inner can be CArr, CType or CStruct + pub fn from_lua_userdata<'lua>( + lua: &'lua Lua, + inner: &LuaAnyUserData, + ) -> LuaResult> { + let value = Self().into_lua(lua)?; + + set_association(lua, POINTER_INNER, value.borrow(), inner)?; + + Ok(value) + } + + // Stringify CPtr with inner ctype + pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult { + let inner: LuaValue = userdata.get("inner")?; + + if inner.is_userdata() { + let inner = inner + .as_userdata() + .ok_or(LuaError::external("failed to get inner type userdata."))?; + Ok(format!( + " <{}({})> ", + type_name_from_userdata(inner), + type_userdata_stringify(inner)?, + )) + } else { + Err(LuaError::external("failed to get inner type userdata.")) + } + } + + // Return void* + pub fn get_type() -> Type { + Type::pointer() + } +} + +impl LuaUserData for CPtr { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("size", |_, _| Ok(size_of::())); + fields.add_field_function_get("inner", |lua, this| { + let inner = get_association(lua, POINTER_INNER, this)? + .ok_or(LuaError::external("inner type not found"))?; + Ok(inner) + }); + } + + 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_function(LuaMetaMethod::ToString, |_, this: LuaAnyUserData| { + let name: Result = CPtr::stringify(&this); + Ok(name) + }); + } +} diff --git a/crates/lune-std-ffi/src/cstruct.rs b/crates/lune-std-ffi/src/cstruct.rs index f0152a6..9a554da 100644 --- a/crates/lune-std-ffi/src/cstruct.rs +++ b/crates/lune-std-ffi/src/cstruct.rs @@ -9,9 +9,13 @@ use libffi::{ }; use mlua::prelude::*; -use crate::association::{get_association, set_association}; -use crate::ctype::{libffi_types_from_table, type_name_from_userdata, CType}; +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}; pub struct CStruct { libffi_cif: Cif, @@ -78,20 +82,30 @@ impl CStruct { if field.is_table() { let table = field .as_table() - .ok_or(LuaError::external("failed to get inner table."))?; - + .ok_or(LuaError::external("failed to get inner type table."))?; // iterate for field let mut result = String::from(" "); for i in 0..table.raw_len() { let child: LuaAnyUserData = table.raw_get(i + 1)?; - result.push_str(format!("{}, ", type_name_from_userdata(&child)?).as_str()); + if child.is::() { + result.push_str(format!("{}, ", type_userdata_stringify(&child)?).as_str()); + } else { + result.push_str( + format!( + "<{}({})>, ", + type_name_from_userdata(&child), + type_userdata_stringify(&child)? + ) + .as_str(), + ); + } } // size of result.push_str(format!("size = {} ", userdata.borrow::()?.size).as_str()); Ok(result) } else { - Ok(String::from("unnamed")) + Err(LuaError::external("failed to get inner type table.")) } } @@ -124,15 +138,20 @@ impl LuaUserData for CStruct { Ok(table) }); } + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method("offset", |_, this, index: usize| { let offset = this.offset(index)?; Ok(offset) }); methods.add_function("ptr", |lua, this: LuaAnyUserData| { - let pointer = CType::pointer(lua, &this)?; + 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_function(LuaMetaMethod::ToString, |_, this: LuaAnyUserData| { let result = CStruct::stringify(&this)?; Ok(result) diff --git a/crates/lune-std-ffi/src/ctype.rs b/crates/lune-std-ffi/src/ctype.rs index 6531aba..7fe6b15 100644 --- a/crates/lune-std-ffi/src/ctype.rs +++ b/crates/lune-std-ffi/src/ctype.rs @@ -12,6 +12,8 @@ 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}; @@ -26,47 +28,21 @@ pub struct CType { } impl CType { - pub fn new(libffi_type: Type, name: Option) -> Self { + pub fn new(libffi_type: Type, name: Option) -> LuaResult { let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); - let size = unsafe { (*libffi_type.as_raw_ptr()).size }; - Self { + 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 pointer<'lua>(lua: &'lua Lua, inner: &LuaAnyUserData) -> LuaResult> { - let value = Self { - libffi_cif: Cif::new(vec![Type::pointer()], Type::void()), - libffi_type: Type::pointer(), - size: size_of::(), - name: Some(format!( - "Ptr<{}({})>", - { - if inner.is::() { - "CStruct" - } else if inner.is::() { - "CType" - } else { - "unnamed" - } - }, - type_name_from_userdata(inner)? - )), - } - .into_lua(lua)?; - - set_association(lua, POINTER_INNER, value.borrow(), inner)?; - - Ok(value) - } - pub fn stringify(&self) -> String { match &self.name { Some(t) => t.to_owned(), @@ -78,21 +54,17 @@ impl CType { 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)); - fields.add_field_function_get("inner", |lua, this| { - let inner = get_association(lua, POINTER_INNER, this)?; - match inner { - Some(t) => Ok(t), - None => Ok(LuaNil), - } - }); } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_function("ptr", |lua, this: LuaAnyUserData| { - let pointer = CType::pointer(lua, &this)?; + 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) @@ -105,47 +77,47 @@ pub fn create_all_types(lua: &Lua) -> LuaResult> { Ok(vec![ ( "u8", - CType::new(Type::u8(), Some(String::from("u8"))).into_lua(lua)?, + CType::new(Type::u8(), Some(String::from("u8")))?.into_lua(lua)?, ), ( "u16", - CType::new(Type::u16(), Some(String::from("u16"))).into_lua(lua)?, + CType::new(Type::u16(), Some(String::from("u16")))?.into_lua(lua)?, ), ( "u32", - CType::new(Type::u32(), Some(String::from("u32"))).into_lua(lua)?, + CType::new(Type::u32(), Some(String::from("u32")))?.into_lua(lua)?, ), ( "u64", - CType::new(Type::u64(), Some(String::from("u64"))).into_lua(lua)?, + CType::new(Type::u64(), Some(String::from("u64")))?.into_lua(lua)?, ), ( "i8", - CType::new(Type::i8(), Some(String::from("i8"))).into_lua(lua)?, + CType::new(Type::i8(), Some(String::from("i8")))?.into_lua(lua)?, ), ( "i16", - CType::new(Type::i16(), Some(String::from("i16"))).into_lua(lua)?, + CType::new(Type::i16(), Some(String::from("i16")))?.into_lua(lua)?, ), ( "i32", - CType::new(Type::i32(), Some(String::from("i32"))).into_lua(lua)?, + CType::new(Type::i32(), Some(String::from("i32")))?.into_lua(lua)?, ), ( "i64", - CType::new(Type::i64(), Some(String::from("i64"))).into_lua(lua)?, + CType::new(Type::i64(), Some(String::from("i64")))?.into_lua(lua)?, ), ( "f32", - CType::new(Type::f32(), Some(String::from("f32"))).into_lua(lua)?, + CType::new(Type::f32(), Some(String::from("f32")))?.into_lua(lua)?, ), ( "f64", - CType::new(Type::f64(), Some(String::from("f64"))).into_lua(lua)?, + CType::new(Type::f64(), Some(String::from("f64")))?.into_lua(lua)?, ), ( "void", - CType::new(Type::void(), Some(String::from("void"))).into_lua(lua)?, + CType::new(Type::void(), Some(String::from("void")))?.into_lua(lua)?, ), ]) } @@ -174,15 +146,19 @@ pub fn libffi_types_from_table(table: &LuaTable) -> LuaResult> { Ok(fields) } -// get libffi_type from any c-types userdata +// get libffi_type from any c-type userdata pub fn libffi_type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult { if userdata.is::() { Ok(userdata.borrow::()?.get_type()) } else if userdata.is::() { Ok(userdata.borrow::()?.get_type()) + } else if userdata.is::() { + Ok(userdata.borrow::()?.get_type()) + } else if userdata.is::() { + Ok(CPtr::get_type()) } else { Err(LuaError::external(format!( - "Unexpected field. CStruct, CType or CArr is required for element but got {}", + "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. @@ -193,19 +169,40 @@ pub fn libffi_type_from_userdata(userdata: &LuaAnyUserData) -> LuaResult { } } -// stringify any c-types userdata (for recursive) -pub fn type_name_from_userdata(userdata: &LuaAnyUserData) -> LuaResult { +// stringify any c-type userdata (for recursive) +pub fn type_userdata_stringify(userdata: &LuaAnyUserData) -> LuaResult { if userdata.is::() { let name = userdata.borrow::()?.stringify(); Ok(name) } else if userdata.is::() { let name = CStruct::stringify(userdata)?; Ok(name) + } else if userdata.is::() { + let name = CArr::stringify(userdata)?; + Ok(name) + } else if userdata.is::() { + 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::() { + String::from("CStruct") + } else if userdata.is::() { + String::from("CType") + } else if userdata.is::() { + String::from("CArr") + } else if userdata.is::() { + 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 { diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index d2fdd8e..a8a5adc 100644 --- a/crates/lune-std-ffi/src/lib.rs +++ b/crates/lune-std-ffi/src/lib.rs @@ -6,6 +6,7 @@ use mlua::prelude::*; mod association; mod carr; mod cfn; +mod cptr; mod cstring; mod cstruct; mod ctype;