Skip to content

Commit 1be9e6c

Browse files
committed
Add Lua 5.5 external strings support
1 parent b1f99aa commit 1be9e6c

5 files changed

Lines changed: 81 additions & 1 deletion

File tree

mlua-sys/src/lua55/lua.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ unsafe extern "C-unwind" {
204204
L: *mut lua_State,
205205
s: *const c_char,
206206
len: usize,
207-
falloc: lua_Alloc,
207+
falloc: Option<lua_Alloc>,
208208
ud: *mut c_void,
209209
) -> *const c_char;
210210
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char;

src/conversion.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,21 @@ impl FromLua for crate::Buffer {
500500
impl IntoLua for StdString {
501501
#[inline]
502502
fn into_lua(self, lua: &Lua) -> Result<Value> {
503+
#[cfg(feature = "lua55")]
504+
if true {
505+
return Ok(Value::String(lua.create_external_string(self)?));
506+
}
507+
503508
Ok(Value::String(lua.create_string(self)?))
504509
}
505510

506511
#[inline]
507512
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
513+
#[cfg(feature = "lua55")]
514+
if lua.unlikely_memory_error() {
515+
return crate::util::push_external_string(lua.state(), self.into(), false);
516+
}
517+
508518
push_bytes_into_stack(self, lua)
509519
}
510520
}
@@ -591,6 +601,11 @@ impl FromLua for Box<str> {
591601
impl IntoLua for CString {
592602
#[inline]
593603
fn into_lua(self, lua: &Lua) -> Result<Value> {
604+
#[cfg(feature = "lua55")]
605+
if true {
606+
return Ok(Value::String(lua.create_external_string(self)?));
607+
}
608+
594609
Ok(Value::String(lua.create_string(self.as_bytes())?))
595610
}
596611
}
@@ -635,6 +650,11 @@ impl IntoLua for Cow<'_, CStr> {
635650
impl IntoLua for BString {
636651
#[inline]
637652
fn into_lua(self, lua: &Lua) -> Result<Value> {
653+
#[cfg(feature = "lua55")]
654+
if true {
655+
return Ok(Value::String(lua.create_external_string(self)?));
656+
}
657+
638658
Ok(Value::String(lua.create_string(self)?))
639659
}
640660
}

src/state.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,17 @@ impl Lua {
12191219
unsafe { self.lock().create_string(s.as_ref()) }
12201220
}
12211221

1222+
/// Creates and returns an external Lua string.
1223+
///
1224+
/// External string is a string where the memory is managed by Rust code, and Lua only holds a
1225+
/// reference to it. This can be used to avoid copying large strings into Lua memory.
1226+
#[cfg(feature = "lua55")]
1227+
#[cfg_attr(docsrs, doc(cfg(feature = "lua55")))]
1228+
#[inline]
1229+
pub fn create_external_string(&self, s: impl Into<Vec<u8>>) -> Result<String> {
1230+
unsafe { self.lock().create_external_string(s.into()) }
1231+
}
1232+
12221233
/// Creates and returns a Luau [buffer] object from a byte slice of data.
12231234
///
12241235
/// [buffer]: https://luau.org/library#buffer-library

src/state/raw.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,23 @@ impl RawLua {
529529
Ok(String(self.pop_ref()))
530530
}
531531

532+
/// Creates an external string, that is, a string that uses memory not managed by Lua.
533+
///
534+
/// Modifies the input data to add `\0` terminator.
535+
#[cfg(feature = "lua55")]
536+
pub(crate) unsafe fn create_external_string(&self, bytes: Vec<u8>) -> Result<String> {
537+
let state = self.state();
538+
if self.unlikely_memory_error() {
539+
crate::util::push_external_string(state, bytes, false)?;
540+
return Ok(String(self.pop_ref()));
541+
}
542+
543+
let _sg = StackGuard::new(state);
544+
check_stack(state, 3)?;
545+
crate::util::push_external_string(state, bytes, true)?;
546+
Ok(String(self.pop_ref()))
547+
}
548+
532549
#[cfg(feature = "luau")]
533550
pub(crate) unsafe fn create_buffer_with_capacity(&self, size: usize) -> Result<(*mut u8, crate::Buffer)> {
534551
let state = self.state();

src/util/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,38 @@ pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect:
9999
}
100100
}
101101

102+
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
103+
#[cfg(feature = "lua55")]
104+
pub(crate) unsafe fn push_external_string(
105+
state: *mut ffi::lua_State,
106+
mut bytes: Vec<u8>,
107+
protect: bool,
108+
) -> Result<()> {
109+
bytes.push(0);
110+
let s_len = bytes.len() - 1; // exclude null terminator
111+
let s_ptr = bytes.as_ptr() as *const c_char;
112+
let bytes_ud = Box::into_raw(Box::new(bytes));
113+
114+
unsafe extern "C" fn dealloc(ud: *mut c_void, _: *mut c_void, _: usize, _: usize) -> *mut c_void {
115+
drop(Box::from_raw(ud as *mut Vec<u8>));
116+
ptr::null_mut()
117+
}
118+
119+
if protect {
120+
let res = protect_lua!(state, 0, 1, move |state| {
121+
ffi::lua_pushexternalstring(state, s_ptr, s_len, Some(dealloc), bytes_ud as *mut _);
122+
});
123+
if res.is_err() {
124+
// Deallocate on error
125+
drop(Box::from_raw(bytes_ud));
126+
return res;
127+
}
128+
} else {
129+
ffi::lua_pushexternalstring(state, s_ptr, s_len, Some(dealloc), bytes_ud as *mut _);
130+
}
131+
Ok(())
132+
}
133+
102134
// Uses 3 stack spaces (when protect), does not call checkstack.
103135
#[cfg(feature = "luau")]
104136
#[inline(always)]

0 commit comments

Comments
 (0)