diff --git a/crates/lune-std-ffi/src/carr.rs b/crates/lune-std-ffi/src/carr.rs index fbee29b1..ad3d0492 100644 --- a/crates/lune-std-ffi/src/carr.rs +++ b/crates/lune-std-ffi/src/carr.rs @@ -3,8 +3,11 @@ use mlua::prelude::*; use crate::association::{get_association, set_association}; use crate::association_names::CARR_INNER; -use crate::chelper::{get_ensured_size, stringify_userdata, type_from_userdata}; +use crate::chelper::{ + get_ensured_size, name_from_userdata, stringify_userdata, type_from_userdata, +}; use crate::cptr::CPtr; +use crate::ctype::CType; // This is a series of some type. // It provides the final size and the offset of the index, @@ -60,15 +63,26 @@ impl CArr { 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!( - " {} ; {} ", - stringify_userdata(inner)?, - carr.length - )) + + if inner.is::() { + 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.")) } diff --git a/crates/lune-std-ffi/src/ccast.rs b/crates/lune-std-ffi/src/ccast.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/crates/lune-std-ffi/src/ccast.rs @@ -0,0 +1 @@ + diff --git a/crates/lune-std-ffi/src/ctype.rs b/crates/lune-std-ffi/src/ctype.rs index 6e99eed4..9e621900 100644 --- a/crates/lune-std-ffi/src/ctype.rs +++ b/crates/lune-std-ffi/src/ctype.rs @@ -1,11 +1,18 @@ #![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 crate::carr::CArr; use crate::chelper::get_ensured_size; use crate::cptr::CPtr; +use crate::ffihelper::get_ptr_from_userdata; +use crate::platform::CHAR_IS_SIGNED; // use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw}; pub struct CType { @@ -13,10 +20,21 @@ pub struct CType { libffi_type: Type, size: usize, name: Option, + + // 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, } impl CType { - pub fn new(libffi_type: Type, name: Option) -> LuaResult { + pub fn new( + libffi_type: Type, + name: Option, + luavalue_into_ptr: fn(value: LuaValue, ptr: *mut c_void) -> LuaResult<()>, + ptr_into_luavalue: fn(lua: &Lua, ptr: *mut c_void) -> LuaResult, + ) -> LuaResult { let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void()); let size = get_ensured_size(libffi_type.as_raw_ptr())?; Ok(Self { @@ -24,6 +42,8 @@ impl CType { libffi_type, size, name, + luavalue_into_ptr, + ptr_into_luavalue, }) } @@ -37,6 +57,30 @@ impl CType { 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, + ) -> LuaResult> { + 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, + ) -> LuaResult<()> { + let ptr = unsafe { get_ptr_from_userdata(&userdata, offset)? }; + (self.luavalue_into_ptr)(luavalue, ptr)?; + Ok(()) + } } impl LuaUserData for CType { @@ -49,6 +93,20 @@ impl LuaUserData for CType { let pointer = CPtr::from_lua_userdata(lua, &this)?; Ok(pointer) }); + methods.add_method( + "from", + |lua, ctype, (userdata, offset): (LuaAnyUserData, Option)| { + let value = unsafe { ctype.read_ptr(lua, userdata, offset)? }; + Ok(value) + }, + ); + methods.add_method( + "into", + |_, ctype, (value, userdata, offset): (LuaValue, LuaAnyUserData, Option)| { + 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) @@ -64,48 +122,62 @@ impl LuaUserData for CType { pub fn create_all_types(lua: &Lua) -> LuaResult> { 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)?, + "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::()) = value; + } + Ok(()) + }, + |lua: &Lua, ptr: *mut c_void| { + let value = unsafe { (*ptr.cast::()).into_lua(lua)? }; + Ok(value) + }, + )? + .into_lua(lua)?, ), ( - "void", - CType::new(Type::void(), Some(String::from("void")))?.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::()) = value; + } + Ok(()) + }, + |lua: &Lua, ptr: *mut c_void| { + let value = unsafe { (*ptr.cast::()).into_lua(lua)? }; + Ok(value) + }, + )? + .into_lua(lua)?, ), ]) } diff --git a/crates/lune-std-ffi/src/ffibox.rs b/crates/lune-std-ffi/src/ffibox.rs index b9d0df8e..98073be0 100644 --- a/crates/lune-std-ffi/src/ffibox.rs +++ b/crates/lune-std-ffi/src/ffibox.rs @@ -15,15 +15,25 @@ use core::ffi::c_void; use mlua::prelude::*; use crate::association::set_association; +use crate::association_names::BOX_REF_INNER; +use crate::ffiref::FfiRange; use crate::ffiref::FfiRef; -const BOX_REF_INNER: &str = "__box_ref"; - pub struct FfiBox(Box<[u8]>); impl FfiBox { + // For efficiency, it is initialized non-zeroed. pub fn new(size: usize) -> Self { - Self(vec![0u8; size].into_boxed_slice()) + // Create new vector to allocate heap memory. sized with 'size' + let mut vec_heap = Vec::::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 { @@ -36,14 +46,62 @@ impl FfiBox { 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, ) -> LuaResult> { let target = this.borrow::()?; - - let luaref = lua.create_userdata(FfiRef::new(target.get_ptr()))?; + 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())?; @@ -61,27 +119,19 @@ impl LuaUserData for FfiBox { } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_method_mut("zero", |_, this, ()| { - this.zero(); - Ok(()) + methods.add_function_mut("zero", |_, this: LuaAnyUserData| { + this.borrow_mut::()?.zero(); + Ok(this) }); - 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) + methods.add_function( + "ref", + |lua, (this, offset): (LuaAnyUserData, Option)| { + let luaref = FfiBox::luaref(lua, this, offset)?; + Ok(luaref) + }, + ); + methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { + Ok(this.binary_print()) }); } } diff --git a/crates/lune-std-ffi/src/ffihelper.rs b/crates/lune-std-ffi/src/ffihelper.rs new file mode 100644 index 00000000..26bf3bb6 --- /dev/null +++ b/crates/lune-std-ffi/src/ffihelper.rs @@ -0,0 +1,27 @@ +use std::ffi::c_void; + +use mlua::prelude::*; + +use crate::ffibox::FfiBox; +use crate::ffiref::FfiRef; + +pub unsafe fn get_ptr_from_userdata( + userdata: &LuaAnyUserData, + offset: Option, +) -> LuaResult<*mut c_void> { + let ptr = if userdata.is::() { + userdata.borrow::()?.get_ptr() + } else if userdata.is::() { + userdata.borrow::()?.get_ptr() + } else { + return Err(LuaError::external("asdf")); + }; + + let ptr = if let Some(t) = offset { + ptr.offset(t) + } else { + ptr + }; + + Ok(ptr) +} diff --git a/crates/lune-std-ffi/src/ffilib.rs b/crates/lune-std-ffi/src/ffilib.rs index 23958682..1cc1caf8 100644 --- a/crates/lune-std-ffi/src/ffilib.rs +++ b/crates/lune-std-ffi/src/ffilib.rs @@ -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())?; diff --git a/crates/lune-std-ffi/src/ffiraw.rs b/crates/lune-std-ffi/src/ffiraw.rs index 6d64e58b..6a82cd97 100644 --- a/crates/lune-std-ffi/src/ffiraw.rs +++ b/crates/lune-std-ffi/src/ffiraw.rs @@ -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 diff --git a/crates/lune-std-ffi/src/ffiref.rs b/crates/lune-std-ffi/src/ffiref.rs index 79473fa2..f3135f3f 100644 --- a/crates/lune-std-ffi/src/ffiref.rs +++ b/crates/lune-std-ffi/src/ffiref.rs @@ -4,6 +4,7 @@ use std::ptr; use mlua::prelude::*; use crate::association::set_association; +use crate::association_names::REF_INNER; // 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 @@ -11,13 +12,19 @@ use crate::association::set_association; // 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); +pub struct FfiRange { + pub(crate) high: isize, + pub(crate) low: isize, +} -const REF_INNER: &str = "__ref_inner"; +pub struct FfiRef { + ptr: *mut c_void, + range: Option, +} impl FfiRef { - pub fn new(target: *mut c_void) -> Self { - Self(target) + pub fn new(ptr: *mut c_void, range: Option) -> Self { + Self { ptr, range } } // bad naming. i have no idea what should i use @@ -27,19 +34,45 @@ impl FfiRef { ) -> LuaResult> { let target = this.borrow::()?; - let luaref = lua.create_userdata(FfiRef::new(ptr::from_ref(&target.0) as *mut c_void))?; + let luaref = lua.create_userdata(FfiRef::new( + ptr::from_ref(&target.ptr) as *mut c_void, + Some(FfiRange { + low: 0, + high: size_of::() 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.0.cast::<*mut c_void>()) + Self::new(*self.ptr.cast::<*mut c_void>(), None) } - pub unsafe fn offset(&self, offset: isize) -> Self { - Self::new(self.0.offset(offset)) + pub unsafe fn offset(&self, offset: isize) -> LuaResult { + 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)) } } @@ -50,7 +83,7 @@ impl LuaUserData for FfiRef { Ok(ffiref) }); methods.add_method("offset", |_, this, offset: isize| { - let ffiref = unsafe { this.offset(offset) }; + let ffiref = unsafe { this.offset(offset)? }; Ok(ffiref) }); methods.add_function("ref", |lua, this: LuaAnyUserData| { diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs index 1cbc0b8c..a096503f 100644 --- a/crates/lune-std-ffi/src/lib.rs +++ b/crates/lune-std-ffi/src/lib.rs @@ -12,9 +12,11 @@ mod cstring; mod cstruct; mod ctype; mod ffibox; +mod ffihelper; mod ffilib; mod ffiraw; mod ffiref; +mod platform; use crate::association::get_table; use crate::cfn::CFn; @@ -22,6 +24,7 @@ use crate::cstruct::CStruct; use crate::ctype::create_all_types; use crate::ffibox::FfiBox; use crate::ffilib::FfiLib; +use crate::platform::get_platform_value; // Converts ffi status into &str pub const FFI_STATUS_NAMES: [&str; 4] = [ @@ -31,10 +34,13 @@ pub const FFI_STATUS_NAMES: [&str; 4] = [ "ffi_status_FFI_BAD_ARGTYPE", ]; +// 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"; + pub const BOX_REF_INNER: &str = "__box_ref"; + pub const REF_INNER: &str = "__ref_inner"; } /** @@ -48,6 +54,7 @@ pub fn module(lua: &Lua) -> LuaResult { 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| { diff --git a/crates/lune-std-ffi/src/platform.rs b/crates/lune-std-ffi/src/platform.rs new file mode 100644 index 00000000..dda71537 --- /dev/null +++ b/crates/lune-std-ffi/src/platform.rs @@ -0,0 +1,18 @@ +use core::ffi::c_char; +use std::env::consts; +use std::vec::Vec; + +pub const CHAR_IS_SIGNED: bool = c_char::MIN as u8 != 0; +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" }), + ] +} diff --git a/crates/lune-std-ffi/todo.md b/crates/lune-std-ffi/todo.md index 307b29ad..9eed0774 100644 --- a/crates/lune-std-ffi/todo.md +++ b/crates/lune-std-ffi/todo.md @@ -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`