Implement string parameter specialisation

This commit is contained in:
2025-06-25 18:42:09 +10:00
parent 681dd332ab
commit 98100d02fa
4 changed files with 207 additions and 83 deletions

View File

@@ -485,12 +485,17 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
self
}
pub fn param_str(&mut self, name: impl Display) -> &mut Self {
// fast-path for &str and &[u8]-like parameters
pub fn param_str(
&mut self,
name: impl Display,
allow_nil: bool,
check_utf8: bool,
) -> &mut Self {
// fast-path for &[u8] and &str-like parameters
//
// this passes one lua `string` argument as two C `const uint8_t *ptr` and `uintptr_t len`
// arguments, bypassing the slower generic `&[u8]: FromFfi` path which constructs a
// temporary cdata to pass the string and its length in one argument
// temporary cdata to pass the string and its length in one argument.
let Self {
lparams,
cparams,
@@ -499,22 +504,26 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
..
} = self;
let param_ptr = <*const u8>::cdecl("ptr");
let param_len = usize::cdecl("len");
let param_ptr = <*const u8>::cdecl(&name);
let param_len = usize::cdecl(format!("{name}_len"));
(!lparams.is_empty()).then(|| lparams.push_str(", "));
(!cparams.is_empty()).then(|| cparams.push_str(", "));
(!cargs.is_empty()).then(|| cargs.push_str(", "));
write!(lparams, "{name}").unwrap();
write!(cparams, "{param_ptr}, {param_len}",).unwrap();
write!(cparams, "{param_ptr}, {param_len}").unwrap();
write!(cargs, "{name}, __{name}_len").unwrap();
write!(prelude, "local __{name}_len = 0; ").unwrap();
write!(
prelude,
r#"if {name} ~= nil then assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); __{name}_len = #{name}; end; "#
)
.unwrap();
write!(prelude, "local __{name}_len = 0; if {name} ~= nil then ").unwrap();
write!(prelude, r#"assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); "#).unwrap();
write!(prelude, r#"__{name}_len = #{name}; "#).unwrap();
if check_utf8 {
write!(prelude, r#"assert(__C.{IS_UTF8_FN}({name}, __{name}_len), "argument '{name}' must be a valid utf-8 string"); "#).unwrap();
}
if !allow_nil {
write!(prelude, r#"else return error("string expected in argument '{name}', got " .. type({name})); "#).unwrap();
}
write!(prelude, r#"end; "#).unwrap();
self
}
@@ -549,21 +558,21 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
if T::Into::ty() == TypeType::Void {
write!(lua, "__C.{func}({cargs}); {postlude}end").unwrap();
} else {
let check = T::postlude("__res");
write!(lua, "local __res = __C.{func}({cargs}); ").unwrap();
write!(lua, "{check}{postlude}return __res; end").unwrap();
let check = T::postlude("__ret");
write!(lua, "local __ret = __C.{func}({cargs}); ").unwrap();
write!(lua, "{check}{postlude}return __ret; end").unwrap();
}
writeln!(cdef, "{};", T::Into::cdecl(display!("{func}({cparams})"))).unwrap();
}
FfiReturnConvention::ByOutParam => {
let ct = T::Into::name();
let check = T::postlude("__res");
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
let check = T::postlude("__out");
write!(lua, "local __out = __new(__ct.{ct}); __C.{func}(__out").unwrap();
if !cargs.is_empty() {
write!(lua, ", {cargs}").unwrap();
}
write!(lua, "); {check}{postlude}return __res; end").unwrap();
write!(lua, "); {check}{postlude}return __out; end").unwrap();
write!(cdef, "void {func}({}", <*mut T::Into>::cdecl("out")).unwrap();
if !cparams.is_empty() {
write!(cdef, ", {cparams}").unwrap();