mirror of
https://github.com/lune-org/lune.git
synced 2025-04-06 19:40:54 +01:00
219 lines
7.2 KiB
Rust
219 lines
7.2 KiB
Rust
use std::cell::Ref;
|
|
|
|
use libffi::middle::Type;
|
|
use mlua::prelude::*;
|
|
|
|
use super::{
|
|
association_names::CARR_INNER,
|
|
c_helper::{get_conv, get_ensured_size, libffi_type_from_userdata, pretty_format_userdata},
|
|
CPtr,
|
|
};
|
|
use crate::ffi::{
|
|
ffi_association::{get_association, set_association},
|
|
FfiBox, GetNativeData, NativeConvert, NativeData, NativeSize,
|
|
};
|
|
|
|
// 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.
|
|
// However, multidimensional arrays are not impossible to implement
|
|
// because they are a series of transcribed one-dimensional arrays.
|
|
|
|
// 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.
|
|
|
|
pub struct CArr {
|
|
// element_type: Type,
|
|
struct_type: Type,
|
|
length: usize,
|
|
field_size: usize,
|
|
size: usize,
|
|
conv: *const dyn NativeConvert,
|
|
}
|
|
|
|
impl CArr {
|
|
pub fn new(
|
|
element_type: Type,
|
|
length: usize,
|
|
conv: *const dyn NativeConvert,
|
|
) -> LuaResult<Self> {
|
|
let field_size = get_ensured_size(element_type.as_raw_ptr())?;
|
|
let struct_type = Type::structure(vec![element_type.clone(); length]);
|
|
|
|
Ok(Self {
|
|
// element_type,
|
|
struct_type,
|
|
length,
|
|
field_size,
|
|
size: field_size * length,
|
|
conv,
|
|
})
|
|
}
|
|
|
|
pub fn new_from_lua_userdata<'lua>(
|
|
lua: &'lua Lua,
|
|
luatype: &LuaAnyUserData<'lua>,
|
|
length: usize,
|
|
) -> LuaResult<LuaAnyUserData<'lua>> {
|
|
let fields = libffi_type_from_userdata(lua, luatype)?;
|
|
let conv = unsafe { get_conv(luatype)? };
|
|
let carr = lua.create_userdata(Self::new(fields, length, conv)?)?;
|
|
|
|
set_association(lua, CARR_INNER, &carr, luatype)?;
|
|
Ok(carr)
|
|
}
|
|
|
|
pub fn get_length(&self) -> usize {
|
|
self.length
|
|
}
|
|
|
|
pub fn get_type(&self) -> &Type {
|
|
&self.struct_type
|
|
}
|
|
|
|
// pub fn get_element_type(&self) -> &Type {
|
|
// &self.element_type
|
|
// }
|
|
|
|
// Stringify cstruct for pretty printing something like:
|
|
// <CStruct( u8, i32, size = 8 )>
|
|
pub fn stringify(lua: &Lua, 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!(
|
|
"{}*{}",
|
|
pretty_format_userdata(lua, inner)?,
|
|
carr.length,
|
|
))
|
|
} else {
|
|
Err(LuaError::external("failed to get inner type userdata."))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl NativeSize for CArr {
|
|
fn get_size(&self) -> usize {
|
|
self.size
|
|
}
|
|
}
|
|
impl NativeConvert for CArr {
|
|
// FIXME: FfiBox, FfiRef support required
|
|
unsafe fn luavalue_into<'lua>(
|
|
&self,
|
|
lua: &'lua Lua,
|
|
offset: isize,
|
|
data_handle: &Ref<dyn NativeData>,
|
|
value: LuaValue<'lua>,
|
|
) -> LuaResult<()> {
|
|
let LuaValue::Table(ref table) = value else {
|
|
return Err(LuaError::external("Value is not a table"));
|
|
};
|
|
for i in 0..self.length {
|
|
let field_offset = (i * self.field_size) as isize;
|
|
let data: LuaValue = table.get(i + 1)?;
|
|
|
|
self.conv.as_ref().unwrap().luavalue_into(
|
|
lua,
|
|
field_offset + offset,
|
|
data_handle,
|
|
data,
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
unsafe fn luavalue_from<'lua>(
|
|
&self,
|
|
lua: &'lua Lua,
|
|
offset: isize,
|
|
data_handle: &Ref<dyn NativeData>,
|
|
) -> LuaResult<LuaValue<'lua>> {
|
|
let table = lua.create_table_with_capacity(self.length, 0)?;
|
|
for i in 0..self.length {
|
|
let field_offset = (i * self.field_size) as isize;
|
|
table.set(
|
|
i + 1,
|
|
self.conv.as_ref().unwrap().luavalue_from(
|
|
lua,
|
|
field_offset + offset,
|
|
data_handle,
|
|
)?,
|
|
)?;
|
|
}
|
|
Ok(LuaValue::Table(table))
|
|
}
|
|
}
|
|
|
|
impl LuaUserData for CArr {
|
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
|
fields.add_field_method_get("size", |_, this| Ok(this.get_size()));
|
|
fields.add_field_method_get("length", |_, this| Ok(this.get_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_method("box", |lua, this, table: LuaValue| {
|
|
let result = lua.create_userdata(FfiBox::new(this.get_size()))?;
|
|
|
|
unsafe { this.luavalue_into(lua, 0, &result.get_data_handle()?, table)? };
|
|
Ok(result)
|
|
});
|
|
methods.add_method(
|
|
"from",
|
|
|lua, this, (userdata, offset): (LuaAnyUserData, Option<isize>)| {
|
|
let offset = offset.unwrap_or(0);
|
|
|
|
let data_handle = &userdata.get_data_handle()?;
|
|
if !data_handle.check_boundary(offset, this.get_size()) {
|
|
return Err(LuaError::external("Out of bounds"));
|
|
}
|
|
|
|
unsafe { this.luavalue_from(lua, offset, data_handle) }
|
|
},
|
|
);
|
|
methods.add_method(
|
|
"into",
|
|
|lua, this, (userdata, value, offset): (LuaAnyUserData, LuaValue, Option<isize>)| {
|
|
let offset = offset.unwrap_or(0);
|
|
|
|
let data_handle = &userdata.get_data_handle()?;
|
|
if !data_handle.check_boundary(offset, this.size) {
|
|
return Err(LuaError::external("Out of bounds"));
|
|
}
|
|
if !data_handle.is_writable() {
|
|
return Err(LuaError::external("Unwritable data handle"));
|
|
}
|
|
|
|
unsafe { this.luavalue_into(lua, offset, data_handle, value) }
|
|
},
|
|
);
|
|
methods.add_function("ptr", |lua, this: LuaAnyUserData| {
|
|
let pointer = CPtr::new_from_lua_userdata(lua, &this)?;
|
|
Ok(pointer)
|
|
});
|
|
methods.add_meta_function(LuaMetaMethod::ToString, |lua, this: LuaAnyUserData| {
|
|
let result = CArr::stringify(lua, &this)?;
|
|
Ok(result)
|
|
});
|
|
}
|
|
}
|