From 8c38aef32d86838d6e1e22c49f385e5894200d77 Mon Sep 17 00:00:00 2001 From: qwreey Date: Fri, 23 Aug 2024 20:02:57 +0900 Subject: [PATCH] Implement struct and arr (#243) struct.size: Returns a non-zero actual size rewrite use super:: => use crate:: --- crates/lune-std-ffi/src/carr.rs | 20 ++++++++++-- crates/lune-std-ffi/src/cstruct.rs | 52 +++++++++++++++++++----------- crates/lune-std-ffi/src/ctype.rs | 39 ++++++++++++++++++---- crates/lune-std-ffi/src/ffibox.rs | 8 +++-- crates/lune-std-ffi/src/ffilib.rs | 2 +- crates/lune-std-ffi/src/ffiref.rs | 6 ++-- crates/lune-std-ffi/src/lib.rs | 19 +++++++---- 7 files changed, 107 insertions(+), 39 deletions(-) diff --git a/crates/lune-std-ffi/src/carr.rs b/crates/lune-std-ffi/src/carr.rs index 9258e89..fc38e8b 100644 --- a/crates/lune-std-ffi/src/carr.rs +++ b/crates/lune-std-ffi/src/carr.rs @@ -1,6 +1,8 @@ use libffi::middle::Type; use mlua::prelude::*; +use crate::ctype::libffi_type_ensured_size; + // This is a series of some type. // It provides the final size and the offset of the index, // but does not allow multidimensional arrays because of API complexity. @@ -9,15 +11,29 @@ use mlua::prelude::*; // See: https://stackoverflow.com/a/43525176 +// 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 { libffi_type: Type, + struct_type: Type, length: usize, + field_size: usize, size: usize, } impl CArr { - fn new(libffi_type: Type, length: usize) { - Self { libffi_type } + 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())?; + + Ok(Self { + libffi_type, + struct_type, + length, + field_size, + size: field_size * length, + }) } } diff --git a/crates/lune-std-ffi/src/cstruct.rs b/crates/lune-std-ffi/src/cstruct.rs index b5aa47e..f0152a6 100644 --- a/crates/lune-std-ffi/src/cstruct.rs +++ b/crates/lune-std-ffi/src/cstruct.rs @@ -1,16 +1,17 @@ #![allow(clippy::cargo_common_metadata)] -use mlua::prelude::*; - -use libffi::low::ffi_abi_FFI_DEFAULT_ABI; -use libffi::middle::{Cif, Type}; -use libffi::raw::ffi_get_struct_offsets; use std::vec::Vec; -use crate::association::{get_association, set_association}; -use crate::ctype::{libffi_types_from_table, type_name_from_userdata}; +use libffi::{ + low, + middle::{Cif, Type}, + raw, +}; +use mlua::prelude::*; -use super::ctype::CType; +use crate::association::{get_association, set_association}; +use crate::ctype::{libffi_types_from_table, type_name_from_userdata, CType}; +use crate::FFI_STATUS_NAMES; pub struct CStruct { libffi_cif: Cif, @@ -23,35 +24,48 @@ pub struct CStruct { const CSTRUCT_INNER: &str = "__cstruct_inner"; impl CStruct { - pub fn new(fields: Vec) -> Self { + pub fn new(fields: Vec) -> LuaResult { let libffi_type = Type::structure(fields.clone()); let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); - let size = unsafe { (*libffi_type.as_raw_ptr()).size }; + + // Get field offsets with ffi_get_struct_offsets let mut offsets = Vec::::with_capacity(fields.len()); unsafe { - ffi_get_struct_offsets( - ffi_abi_FFI_DEFAULT_ABI, + let offset_result: raw::ffi_status = raw::ffi_get_struct_offsets( + low::ffi_abi_FFI_DEFAULT_ABI, libffi_type.as_raw_ptr(), offsets.as_mut_ptr(), ); + if offset_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[offset_result as usize] + ))); + } offsets.set_len(offsets.capacity()); } - Self { + // Get tailing padded size of struct + // See http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Size-and-Alignment.html + let size = unsafe { (*libffi_type.as_raw_ptr()).size }; + + Ok(Self { libffi_cif: libffi_cfi, libffi_type, fields, offsets, size, - } + }) } + // Create new CStruct UserData with LuaTable. + // Lock and hold table for .inner ref pub fn from_lua_table<'lua>( lua: &'lua Lua, table: LuaTable<'lua>, ) -> LuaResult> { let fields = libffi_types_from_table(&table)?; - let cstruct = lua.create_userdata(Self::new(fields))?; + let cstruct = lua.create_userdata(Self::new(fields)?)?; table.set_readonly(true); set_association(lua, CSTRUCT_INNER, cstruct.clone(), table)?; Ok(cstruct) @@ -81,10 +95,6 @@ impl CStruct { } } - pub fn get_type(&self) -> Type { - self.libffi_type.clone() - } - // Get byte offset of nth field pub fn offset(&self, index: usize) -> LuaResult { let offset = self @@ -94,6 +104,10 @@ impl CStruct { .to_owned(); Ok(offset) } + + pub fn get_type(&self) -> Type { + self.libffi_type.clone() + } } impl LuaUserData for CStruct { diff --git a/crates/lune-std-ffi/src/ctype.rs b/crates/lune-std-ffi/src/ctype.rs index fe223c5..6531aba 100644 --- a/crates/lune-std-ffi/src/ctype.rs +++ b/crates/lune-std-ffi/src/ctype.rs @@ -1,12 +1,19 @@ #![allow(clippy::cargo_common_metadata)] use std::borrow::Borrow; +use std::ptr::{self, null_mut}; -use super::association::{get_association, set_association}; -use super::cstruct::CStruct; -use libffi::middle::{Cif, Type}; +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::cstruct::CStruct; +use crate::FFI_STATUS_NAMES; // use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw}; const POINTER_INNER: &str = "__pointer_inner"; @@ -18,9 +25,6 @@ pub struct CType { name: Option, } -// TODO: ARR -// TODO: convert - impl CType { pub fn new(libffi_type: Type, name: Option) -> Self { let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); @@ -201,3 +205,26 @@ pub fn type_name_from_userdata(userdata: &LuaAnyUserData) -> LuaResult { Ok(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 { + 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) } +} diff --git a/crates/lune-std-ffi/src/ffibox.rs b/crates/lune-std-ffi/src/ffibox.rs index 94f1f99..b9d0df8 100644 --- a/crates/lune-std-ffi/src/ffibox.rs +++ b/crates/lune-std-ffi/src/ffibox.rs @@ -9,11 +9,13 @@ // rather, it creates more heap space, so it should be used appropriately // where necessary. -use super::association::set_association; -use super::ffiref::FfiRef; +use std::boxed::Box; + use core::ffi::c_void; use mlua::prelude::*; -use std::boxed::Box; + +use crate::association::set_association; +use crate::ffiref::FfiRef; const BOX_REF_INNER: &str = "__box_ref"; diff --git a/crates/lune-std-ffi/src/ffilib.rs b/crates/lune-std-ffi/src/ffilib.rs index 9dfbf3b..2395868 100644 --- a/crates/lune-std-ffi/src/ffilib.rs +++ b/crates/lune-std-ffi/src/ffilib.rs @@ -1,9 +1,9 @@ use std::ffi::c_void; -use super::association::set_association; use dlopen2::symbor::Library; use mlua::prelude::*; +use crate::association::set_association; use crate::ffiref::FfiRef; pub struct FfiLib(Library); diff --git a/crates/lune-std-ffi/src/ffiref.rs b/crates/lune-std-ffi/src/ffiref.rs index 5b17966..79473fa 100644 --- a/crates/lune-std-ffi/src/ffiref.rs +++ b/crates/lune-std-ffi/src/ffiref.rs @@ -1,8 +1,10 @@ -use super::association::set_association; use core::ffi::c_void; -use mlua::prelude::*; 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. diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index 5291616..d2fdd8e 100644 --- a/crates/lune-std-ffi/src/lib.rs +++ b/crates/lune-std-ffi/src/lib.rs @@ -14,12 +14,19 @@ mod ffilib; mod ffiraw; mod ffiref; -use self::association::get_table; -use self::cfn::CFn; -use self::cstruct::CStruct; -use self::ctype::create_all_types; -use self::ffibox::FfiBox; -use self::ffilib::FfiLib; +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", +]; /** Creates the `ffi` standard library module.