Implement struct and arr (#243)

struct.size: Returns a non-zero actual size
rewrite use super:: => use crate::
This commit is contained in:
qwreey 2024-08-23 20:02:57 +09:00
parent af08c59e3b
commit 8c38aef32d
No known key found for this signature in database
GPG key ID: D28DB79297A214BD
7 changed files with 107 additions and 39 deletions

View file

@ -1,6 +1,8 @@
use libffi::middle::Type; use libffi::middle::Type;
use mlua::prelude::*; use mlua::prelude::*;
use crate::ctype::libffi_type_ensured_size;
// 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,
// but does not allow multidimensional arrays because of API complexity. // but does not allow multidimensional arrays because of API complexity.
@ -9,15 +11,29 @@ use mlua::prelude::*;
// See: https://stackoverflow.com/a/43525176 // 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 { struct CArr {
libffi_type: Type, libffi_type: Type,
struct_type: Type,
length: usize, length: usize,
field_size: usize,
size: usize, size: usize,
} }
impl CArr { impl CArr {
fn new(libffi_type: Type, length: usize) { fn new(libffi_type: Type, length: usize) -> LuaResult<Self> {
Self { libffi_type } 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,
})
} }
} }

View file

@ -1,16 +1,17 @@
#![allow(clippy::cargo_common_metadata)] #![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 std::vec::Vec;
use crate::association::{get_association, set_association}; use libffi::{
use crate::ctype::{libffi_types_from_table, type_name_from_userdata}; 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 { pub struct CStruct {
libffi_cif: Cif, libffi_cif: Cif,
@ -23,35 +24,48 @@ pub struct CStruct {
const CSTRUCT_INNER: &str = "__cstruct_inner"; const CSTRUCT_INNER: &str = "__cstruct_inner";
impl CStruct { impl CStruct {
pub fn new(fields: Vec<Type>) -> Self { pub fn new(fields: Vec<Type>) -> LuaResult<Self> {
let libffi_type = Type::structure(fields.clone()); let libffi_type = Type::structure(fields.clone());
let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); 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::<usize>::with_capacity(fields.len()); let mut offsets = Vec::<usize>::with_capacity(fields.len());
unsafe { unsafe {
ffi_get_struct_offsets( let offset_result: raw::ffi_status = raw::ffi_get_struct_offsets(
ffi_abi_FFI_DEFAULT_ABI, low::ffi_abi_FFI_DEFAULT_ABI,
libffi_type.as_raw_ptr(), libffi_type.as_raw_ptr(),
offsets.as_mut_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()); 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_cif: libffi_cfi,
libffi_type, libffi_type,
fields, fields,
offsets, offsets,
size, size,
} })
} }
// Create new CStruct UserData with LuaTable.
// Lock and hold table for .inner ref
pub fn from_lua_table<'lua>( pub fn from_lua_table<'lua>(
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 = 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); table.set_readonly(true);
set_association(lua, CSTRUCT_INNER, cstruct.clone(), table)?; set_association(lua, CSTRUCT_INNER, cstruct.clone(), table)?;
Ok(cstruct) Ok(cstruct)
@ -81,10 +95,6 @@ impl CStruct {
} }
} }
pub fn get_type(&self) -> Type {
self.libffi_type.clone()
}
// Get byte offset of nth field // Get byte offset of nth field
pub fn offset(&self, index: usize) -> LuaResult<usize> { pub fn offset(&self, index: usize) -> LuaResult<usize> {
let offset = self let offset = self
@ -94,6 +104,10 @@ impl CStruct {
.to_owned(); .to_owned();
Ok(offset) Ok(offset)
} }
pub fn get_type(&self) -> Type {
self.libffi_type.clone()
}
} }
impl LuaUserData for CStruct { impl LuaUserData for CStruct {

View file

@ -1,12 +1,19 @@
#![allow(clippy::cargo_common_metadata)] #![allow(clippy::cargo_common_metadata)]
use std::borrow::Borrow; use std::borrow::Borrow;
use std::ptr::{self, null_mut};
use super::association::{get_association, set_association}; use libffi::{
use super::cstruct::CStruct; low,
use libffi::middle::{Cif, Type}; middle::{Cif, Type},
raw,
};
use lune_utils::fmt::{pretty_format_value, ValueFormatConfig}; use lune_utils::fmt::{pretty_format_value, ValueFormatConfig};
use mlua::prelude::*; 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}; // use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
const POINTER_INNER: &str = "__pointer_inner"; const POINTER_INNER: &str = "__pointer_inner";
@ -18,9 +25,6 @@ pub struct CType {
name: Option<String>, name: Option<String>,
} }
// TODO: ARR
// TODO: convert
impl CType { impl CType {
pub fn new(libffi_type: Type, name: Option<String>) -> Self { pub fn new(libffi_type: Type, name: Option<String>) -> Self {
let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
@ -201,3 +205,26 @@ pub fn type_name_from_userdata(userdata: &LuaAnyUserData) -> LuaResult<String> {
Ok(String::from("unnamed")) 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<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

@ -9,11 +9,13 @@
// rather, it creates more heap space, so it should be used appropriately // rather, it creates more heap space, so it should be used appropriately
// where necessary. // where necessary.
use super::association::set_association; use std::boxed::Box;
use super::ffiref::FfiRef;
use core::ffi::c_void; use core::ffi::c_void;
use mlua::prelude::*; use mlua::prelude::*;
use std::boxed::Box;
use crate::association::set_association;
use crate::ffiref::FfiRef;
const BOX_REF_INNER: &str = "__box_ref"; const BOX_REF_INNER: &str = "__box_ref";

View file

@ -1,9 +1,9 @@
use std::ffi::c_void; use std::ffi::c_void;
use super::association::set_association;
use dlopen2::symbor::Library; use dlopen2::symbor::Library;
use mlua::prelude::*; use mlua::prelude::*;
use crate::association::set_association;
use crate::ffiref::FfiRef; use crate::ffiref::FfiRef;
pub struct FfiLib(Library); pub struct FfiLib(Library);

View file

@ -1,8 +1,10 @@
use super::association::set_association;
use core::ffi::c_void; use core::ffi::c_void;
use mlua::prelude::*;
use std::ptr; use std::ptr;
use mlua::prelude::*;
use crate::association::set_association;
// A referenced space. It is possible to read and write through types. // 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 // This operation is not safe. This may cause a memory error in Lua
// if use it incorrectly. // if use it incorrectly.

View file

@ -14,12 +14,19 @@ mod ffilib;
mod ffiraw; mod ffiraw;
mod ffiref; mod ffiref;
use self::association::get_table; use crate::association::get_table;
use self::cfn::CFn; use crate::cfn::CFn;
use self::cstruct::CStruct; use crate::cstruct::CStruct;
use self::ctype::create_all_types; use crate::ctype::create_all_types;
use self::ffibox::FfiBox; use crate::ffibox::FfiBox;
use self::ffilib::FfiLib; 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. Creates the `ffi` standard library module.