luby/crates/luaffi/src/string.rs
2025-06-24 10:37:28 +10:00

166 lines
4.5 KiB
Rust

use crate::{
__internal::{disp, display},
FfiReturnConvention, FromFfi, IS_UTF8_FN, ToFfi, Type,
};
use bstr::BStr;
use luaffi_impl::{cdef, metatype};
use std::{fmt::Display, slice};
#[derive(Debug, Clone, Copy)]
#[cdef]
pub struct lua_buf {
__ptr: *const u8,
__len: usize,
}
#[metatype]
impl lua_buf {}
// not implemented yet
#[derive(Debug, Clone, Copy)]
#[cdef]
pub struct lua_buffer {
__ptr: *const u8,
__len: usize,
__cap: usize,
}
#[metatype]
impl lua_buffer {}
unsafe impl<'s> FromFfi for &'s [u8] {
type From = lua_buf;
type FromArg = Option<&'s Self::From>;
fn require_keepalive() -> bool {
true
}
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; ")
})
}
fn convert(from: Self::From) -> Self {
// SAFETY: we already checked that the string is nonnull from the lua side
debug_assert!(!from.__ptr.is_null());
unsafe { slice::from_raw_parts(from.__ptr, from.__len) }
}
fn convert_arg(from: Self::FromArg) -> Self {
// SAFETY: we already checked that the string is nonnull from the lua side
debug_assert!(from.is_some());
unsafe { FromFfi::convert(*from.unwrap_unchecked()) }
}
}
unsafe impl<'s> FromFfi for &'s BStr {
type From = <&'s [u8] as FromFfi>::From;
type FromArg = <&'s [u8] as FromFfi>::FromArg;
fn require_keepalive() -> bool {
<&'s [u8] as FromFfi>::require_keepalive()
}
fn prelude(arg: &str) -> impl Display {
<&'s [u8] as FromFfi>::prelude(arg)
}
fn convert(from: Self::From) -> Self {
<&'s [u8] as FromFfi>::convert(from).into()
}
fn convert_arg(from: Self::FromArg) -> Self {
<&'s [u8] as FromFfi>::convert_arg(from).into()
}
}
unsafe impl<'s> FromFfi for &'s str {
type From = lua_buf;
type FromArg = Option<&'s Self::From>;
fn require_keepalive() -> bool {
true
}
fn prelude(arg: &str) -> impl Display {
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,
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}); ")
})
}
fn convert(from: Self::From) -> Self {
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
debug_assert!(!from.__ptr.is_null());
let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
debug_assert!(
std::str::from_utf8(s).is_ok(),
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
);
unsafe { std::str::from_utf8_unchecked(s) }
}
fn convert_arg(from: Self::FromArg) -> Self {
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
debug_assert!(from.is_some());
unsafe { FromFfi::convert(*from.unwrap_unchecked()) }
}
}
unsafe impl ToFfi for &'static [u8] {
type To = lua_buf;
fn convert(self) -> Self::To {
lua_buf {
__ptr: self.as_ptr(),
__len: self.len(),
}
}
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
display!("{ret} = __intern({ret}.__ptr, {ret}.__len)")
}
}
unsafe impl ToFfi for &'static BStr {
type To = <&'static [u8] as ToFfi>::To;
fn convert(self) -> Self::To {
<&'static [u8] as ToFfi>::convert(self.as_ref())
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
<&'static [u8] as ToFfi>::postlude(ret, conv)
}
}
unsafe impl ToFfi for &'static str {
type To = <&'static [u8] as ToFfi>::To;
fn convert(self) -> Self::To {
<&'static [u8] as ToFfi>::convert(self.as_bytes())
}
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
<&'static [u8] as ToFfi>::postlude(ret, conv)
}
}