Add Lua 5.5 external strings support

This commit is contained in:
Alex Orlenko
2026-01-18 00:03:43 +00:00
parent b1f99aa852
commit 1be9e6ce2d
5 changed files with 81 additions and 1 deletions
+1 -1
View File
@@ -204,7 +204,7 @@ unsafe extern "C-unwind" {
L: *mut lua_State,
s: *const c_char,
len: usize,
falloc: lua_Alloc,
falloc: Option<lua_Alloc>,
ud: *mut c_void,
) -> *const c_char;
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;
+20
View File
@@ -500,11 +500,21 @@ impl FromLua for crate::Buffer {
impl IntoLua for StdString {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
#[cfg(feature = "lua55")]
if true {
return Ok(Value::String(lua.create_external_string(self)?));
}
Ok(Value::String(lua.create_string(self)?))
}
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
#[cfg(feature = "lua55")]
if lua.unlikely_memory_error() {
return crate::util::push_external_string(lua.state(), self.into(), false);
}
push_bytes_into_stack(self, lua)
}
}
@@ -591,6 +601,11 @@ impl FromLua for Box<str> {
impl IntoLua for CString {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
#[cfg(feature = "lua55")]
if true {
return Ok(Value::String(lua.create_external_string(self)?));
}
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
@@ -635,6 +650,11 @@ impl IntoLua for Cow<'_, CStr> {
impl IntoLua for BString {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
#[cfg(feature = "lua55")]
if true {
return Ok(Value::String(lua.create_external_string(self)?));
}
Ok(Value::String(lua.create_string(self)?))
}
}
+11
View File
@@ -1219,6 +1219,17 @@ impl Lua {
unsafe { self.lock().create_string(s.as_ref()) }
}
/// Creates and returns an external Lua string.
///
/// External string is a string where the memory is managed by Rust code, and Lua only holds a
/// reference to it. This can be used to avoid copying large strings into Lua memory.
#[cfg(feature = "lua55")]
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
#[inline]
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<String> {
unsafe { self.lock().create_external_string(s.into()) }
}
/// Creates and returns a Luau [buffer] object from a byte slice of data.
///
/// [buffer]: https://luau.org/library#buffer-library
+17
View File
@@ -529,6 +529,23 @@ impl RawLua {
Ok(String(self.pop_ref()))
}
/// Creates an external string, that is, a string that uses memory not managed by Lua.
///
/// Modifies the input data to add `\0` terminator.
#[cfg(feature = "lua55")]
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<String> {
let state = self.state();
if self.unlikely_memory_error() {
crate::util::push_external_string(state, bytes, false)?;
return Ok(String(self.pop_ref()));
}
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
crate::util::push_external_string(state, bytes, true)?;
Ok(String(self.pop_ref()))
}
#[cfg(feature = "luau")]
pub(crate) unsafe fn create_buffer_with_capacity(&self, size: usize) -> Result<(*mut u8, crate::Buffer)> {
let state = self.state();
+32
View File
@@ -99,6 +99,38 @@ pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect:
}
}
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
#[cfg(feature = "lua55")]
pub(crate) unsafe fn push_external_string(
state: *mut ffi::lua_State,
mut bytes: Vec<u8>,
protect: bool,
) -> Result<()> {
bytes.push(0);
let s_len = bytes.len() - 1; // exclude null terminator
let s_ptr = bytes.as_ptr() as *const c_char;
let bytes_ud = Box::into_raw(Box::new(bytes));
unsafe extern "C" fn dealloc(ud: *mut c_void, _: *mut c_void, _: usize, _: usize) -> *mut c_void {
drop(Box::from_raw(ud as *mut Vec<u8>));
ptr::null_mut()
}
if protect {
let res = protect_lua!(state, 0, 1, move |state| {
ffi::lua_pushexternalstring(state, s_ptr, s_len, Some(dealloc), bytes_ud as *mut _);
});
if res.is_err() {
// Deallocate on error
drop(Box::from_raw(bytes_ud));
return res;
}
} else {
ffi::lua_pushexternalstring(state, s_ptr, s_len, Some(dealloc), bytes_ud as *mut _);
}
Ok(())
}
// Uses 3 stack spaces (when protect), does not call checkstack.
#[cfg(feature = "luau")]
#[inline(always)]