Implement IntoFfi for Strings
This commit is contained in:
parent
45db380466
commit
f8e7b8ae62
@ -1,11 +1,14 @@
|
||||
use crate::__internal::{disp, display, export, write_sep};
|
||||
use crate::{
|
||||
__internal::{disp, display, export, write_sep},
|
||||
string::{DROP_BUFFER_FN, IS_UTF8_FN, lua_buffer},
|
||||
};
|
||||
pub use luaffi_impl::*;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ffi::{c_double, c_float, c_void},
|
||||
fmt::{self, Display, Formatter, Write},
|
||||
marker::PhantomData,
|
||||
mem, slice,
|
||||
mem,
|
||||
};
|
||||
|
||||
pub mod future;
|
||||
@ -14,24 +17,18 @@ pub mod string;
|
||||
#[doc(hidden)]
|
||||
#[path = "./internal.rs"]
|
||||
pub mod __internal;
|
||||
|
||||
const KEEP_FN: &str = "luaffi_keep";
|
||||
const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||
pub mod result;
|
||||
|
||||
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
||||
// garbage-collected until the end of the function. This shall exist until LuaJIT one day implements
|
||||
// something like `ffi.keep(obj)`.
|
||||
// garbage-collected until the end of the function (used in string.rs when string marshalling is
|
||||
// going through the slow-path). This shall exist until LuaJIT one day implements something like
|
||||
// `ffi.keep(obj)`.
|
||||
//
|
||||
// https://github.com/LuaJIT/LuaJIT/issues/1167
|
||||
pub const KEEP_FN: &str = "luaffi_keep";
|
||||
#[unsafe(export_name = "luaffi_keep")]
|
||||
extern "C" fn __keep(_ptr: *const c_void) {}
|
||||
|
||||
#[unsafe(export_name = "luaffi_is_utf8")]
|
||||
unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
|
||||
simdutf8::basic::from_utf8(unsafe { slice::from_raw_parts(ptr, len) }).is_ok()
|
||||
}
|
||||
|
||||
export![__keep, __is_utf8];
|
||||
export![__keep];
|
||||
|
||||
const CACHE_LIBS: &[(&str, &str)] = &[
|
||||
("table", "table"),
|
||||
@ -86,15 +83,15 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||
("__sgsub", "string.gsub"),
|
||||
("__sgmatch", "string.gmatch"),
|
||||
("__sdump", "string.dump"),
|
||||
// math
|
||||
// math (used in luaify! macro)
|
||||
("__fmod", "math.fmod"),
|
||||
// coroutine
|
||||
// coroutine (used in future.rs)
|
||||
("__yield", "coroutine.yield"),
|
||||
// package
|
||||
("__preload", "package.preload"),
|
||||
// debug
|
||||
("__traceback", "debug.traceback"),
|
||||
("__registry", "debug.getregistry()"),
|
||||
("__registry", "debug.getregistry()"), // (used in lib.lua)
|
||||
// ffi
|
||||
("__C", "ffi.C"),
|
||||
("__ct", "{}"),
|
||||
@ -107,8 +104,8 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||
("__gc", "ffi.gc"),
|
||||
("__sizeof", "ffi.sizeof"),
|
||||
("__alignof", "ffi.alignof"),
|
||||
("__intern", "ffi.string"),
|
||||
// bit
|
||||
("__intern", "ffi.string"), // (used in string.rs)
|
||||
// bit (used in luaify! macro)
|
||||
("__bnot", "bit.bnot"),
|
||||
("__band", "bit.band"),
|
||||
("__bor", "bit.bor"),
|
||||
@ -142,6 +139,7 @@ impl Registry {
|
||||
let mut s = Self::default();
|
||||
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
|
||||
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
|
||||
s.declare::<UnsafeExternCFn<(*mut lua_buffer,), ()>>(DROP_BUFFER_FN);
|
||||
s
|
||||
}
|
||||
|
||||
@ -162,10 +160,10 @@ impl Registry {
|
||||
|
||||
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||
self.include::<T>();
|
||||
let ct = T::name();
|
||||
writeln!(
|
||||
self.lua,
|
||||
r#"__preload["{name}"] = function(...) return __ct.{}(...); end;"#,
|
||||
T::name()
|
||||
r#"__preload["{name}"] = function(...) return __ct.{ct}(...); end;"#,
|
||||
)
|
||||
.unwrap();
|
||||
self
|
||||
@ -308,7 +306,7 @@ pub unsafe trait Metatype {
|
||||
#[derive(Debug)]
|
||||
pub struct MetatypeBuilder<'r> {
|
||||
registry: &'r mut Registry,
|
||||
name: String,
|
||||
ct: String,
|
||||
cdef: String,
|
||||
lua: String,
|
||||
}
|
||||
@ -317,7 +315,7 @@ impl<'r> MetatypeBuilder<'r> {
|
||||
fn new<T: Metatype>(registry: &'r mut Registry) -> Self {
|
||||
Self {
|
||||
registry,
|
||||
name: T::Target::name().to_string(),
|
||||
ct: T::Target::name().to_string(),
|
||||
cdef: String::new(),
|
||||
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
|
||||
}
|
||||
@ -365,7 +363,7 @@ impl<'r> Drop for MetatypeBuilder<'r> {
|
||||
fn drop(&mut self) {
|
||||
let Self {
|
||||
registry,
|
||||
name,
|
||||
ct,
|
||||
cdef,
|
||||
lua,
|
||||
..
|
||||
@ -373,7 +371,7 @@ impl<'r> Drop for MetatypeBuilder<'r> {
|
||||
|
||||
registry.cdef.push_str(cdef);
|
||||
registry.lua.push_str(lua);
|
||||
writeln!(registry.lua, r#"__metatype(__ct.{name}, __mt); end;"#).unwrap();
|
||||
writeln!(registry.lua, r#"__metatype(__ct.{ct}, __mt); end;"#).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,28 @@
|
||||
use crate::{
|
||||
__internal::{disp, display},
|
||||
FfiReturnConvention, FromFfi, IS_UTF8_FN, IntoFfi, Type,
|
||||
__internal::{disp, display, export},
|
||||
FfiReturnConvention, FromFfi, IntoFfi,
|
||||
};
|
||||
use bstr::BStr;
|
||||
use bstr::{BStr, BString};
|
||||
use luaffi_impl::{cdef, metatype};
|
||||
use std::{fmt::Display, slice};
|
||||
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
|
||||
|
||||
pub const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||
pub const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
||||
|
||||
#[unsafe(export_name = "luaffi_is_utf8")]
|
||||
unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
|
||||
debug_assert!(!ptr.is_null());
|
||||
simdutf8::basic::from_utf8(unsafe { slice::from_raw_parts(ptr, len) }).is_ok()
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "luaffi_drop_buffer")]
|
||||
unsafe extern "C" fn __drop_buffer(buf: *mut lua_buffer) {
|
||||
debug_assert!(!buf.is_null());
|
||||
debug_assert!(!unsafe { (*buf).__ptr.is_null() });
|
||||
drop(unsafe { Vec::from_raw_parts((*buf).__ptr, (*buf).__len, (*buf).__cap) })
|
||||
}
|
||||
|
||||
export![__is_utf8, __drop_buffer];
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cdef]
|
||||
@ -14,20 +32,15 @@ pub struct lua_buf {
|
||||
}
|
||||
|
||||
#[metatype]
|
||||
impl lua_buf {}
|
||||
|
||||
// not implemented yet
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cdef]
|
||||
pub struct lua_buffer {
|
||||
__ptr: *const u8,
|
||||
__len: usize,
|
||||
__cap: usize,
|
||||
impl lua_buf {
|
||||
fn null() -> Self {
|
||||
Self {
|
||||
__ptr: ptr::null(),
|
||||
__len: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[metatype]
|
||||
impl lua_buffer {}
|
||||
|
||||
unsafe impl<'s> FromFfi for &'s [u8] {
|
||||
type From = Option<&'s lua_buf>;
|
||||
|
||||
@ -38,12 +51,11 @@ unsafe impl<'s> FromFfi for &'s [u8] {
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
||||
disp(move |f| {
|
||||
let ct = Self::From::name();
|
||||
write!(
|
||||
f,
|
||||
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
||||
)?;
|
||||
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); end; ")
|
||||
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); end; ")
|
||||
})
|
||||
}
|
||||
|
||||
@ -60,15 +72,15 @@ unsafe impl<'s> FromFfi for &'s BStr {
|
||||
type From = <&'s [u8] as FromFfi>::From;
|
||||
|
||||
fn require_keepalive() -> bool {
|
||||
<&'s [u8] as FromFfi>::require_keepalive()
|
||||
<&[u8] as FromFfi>::require_keepalive()
|
||||
}
|
||||
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
<&'s [u8] as FromFfi>::prelude(arg)
|
||||
<&[u8] as FromFfi>::prelude(arg)
|
||||
}
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
<&'s [u8] as FromFfi>::convert(from).into()
|
||||
<&[u8] as FromFfi>::convert(from).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +95,6 @@ unsafe impl<'s> FromFfi for &'s str {
|
||||
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
||||
// and ensures that the string is valid utf8
|
||||
disp(move |f| {
|
||||
let ct = Self::From::name();
|
||||
write!(
|
||||
f,
|
||||
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
||||
@ -92,7 +103,7 @@ unsafe impl<'s> FromFfi for &'s str {
|
||||
f,
|
||||
r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "#
|
||||
)?;
|
||||
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); ")
|
||||
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); ")
|
||||
})
|
||||
}
|
||||
|
||||
@ -110,6 +121,34 @@ unsafe impl<'s> FromFfi for &'s str {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_optional_ref_from {
|
||||
($ty:ty) => {
|
||||
unsafe impl<'s> FromFfi for Option<$ty> {
|
||||
type From = <$ty as FromFfi>::From;
|
||||
|
||||
fn require_keepalive() -> bool {
|
||||
<$ty as FromFfi>::require_keepalive()
|
||||
}
|
||||
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
// avoid constructing a `lua_buf` at all for nil arguments
|
||||
display!(
|
||||
"if {arg} ~= nil then {}end; ",
|
||||
<$ty as FromFfi>::prelude(arg)
|
||||
)
|
||||
}
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_optional_ref_from!(&'s [u8]);
|
||||
impl_optional_ref_from!(&'s BStr);
|
||||
impl_optional_ref_from!(&'s str);
|
||||
|
||||
unsafe impl IntoFfi for &'static [u8] {
|
||||
type To = lua_buf;
|
||||
|
||||
@ -129,11 +168,11 @@ unsafe impl IntoFfi for &'static BStr {
|
||||
type To = <&'static [u8] as IntoFfi>::To;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
<&'static [u8] as IntoFfi>::convert(self.as_ref())
|
||||
<&[u8] as IntoFfi>::convert(self.as_ref())
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||
<&'static [u8] as IntoFfi>::postlude(ret, conv)
|
||||
<&[u8] as IntoFfi>::postlude(ret, conv)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,10 +180,117 @@ unsafe impl IntoFfi for &'static str {
|
||||
type To = <&'static [u8] as IntoFfi>::To;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
<&'static [u8] as IntoFfi>::convert(self.as_bytes())
|
||||
<&[u8] as IntoFfi>::convert(self.as_bytes())
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||
<&'static [u8] as IntoFfi>::postlude(ret, conv)
|
||||
<&[u8] as IntoFfi>::postlude(ret, conv)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_optional_ref_into {
|
||||
($ty:ty) => {
|
||||
unsafe impl IntoFfi for Option<$ty> {
|
||||
type To = <$ty as IntoFfi>::To;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
self.map_or(lua_buf::null(), <$ty as IntoFfi>::convert)
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||
display!(
|
||||
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
||||
<$ty as IntoFfi>::postlude(ret, conv)
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_optional_ref_into!(&'static [u8]);
|
||||
impl_optional_ref_into!(&'static BStr);
|
||||
impl_optional_ref_into!(&'static str);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cdef]
|
||||
pub struct lua_buffer {
|
||||
__ptr: *mut u8,
|
||||
__len: usize,
|
||||
__cap: usize,
|
||||
}
|
||||
|
||||
#[metatype]
|
||||
impl lua_buffer {
|
||||
fn null() -> Self {
|
||||
Self {
|
||||
__ptr: ptr::null_mut(),
|
||||
__len: 0,
|
||||
__cap: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl IntoFfi for Vec<u8> {
|
||||
type To = lua_buffer;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
lua_buffer {
|
||||
__cap: self.capacity(),
|
||||
__len: self.len(),
|
||||
__ptr: ManuallyDrop::new(self).as_mut_ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||
display!(
|
||||
"do local __{ret} = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}(__{ret}); end; "
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl IntoFfi for BString {
|
||||
type To = <Vec<u8> as IntoFfi>::To;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
<Vec<u8> as IntoFfi>::convert(self.into())
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||
<Vec<u8> as IntoFfi>::postlude(ret, conv)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl IntoFfi for String {
|
||||
type To = <Vec<u8> as IntoFfi>::To;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
<Vec<u8> as IntoFfi>::convert(self.into_bytes())
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||
<Vec<u8> as IntoFfi>::postlude(ret, conv)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_optional_into {
|
||||
($ty:ty) => {
|
||||
unsafe impl IntoFfi for Option<$ty> {
|
||||
type To = <$ty as IntoFfi>::To;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
self.map_or(lua_buffer::null(), <$ty as IntoFfi>::convert)
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||
display!(
|
||||
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
||||
<$ty as IntoFfi>::postlude(ret, conv)
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_optional_into!(Vec<u8>);
|
||||
impl_optional_into!(BString);
|
||||
impl_optional_into!(String);
|
||||
|
Loading…
x
Reference in New Issue
Block a user