Skip to content

Commit

Permalink
Support Luau buffer type and and library.
Browse files Browse the repository at this point in the history
Buffer is an object that represents a fixed-size mutable block of memory and added to Luau 0.601.
See https://luau-lang.org/library#buffer-library for more details.
  • Loading branch information
khvzak committed Nov 16, 2023
1 parent b879abc commit 5043447
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 13 deletions.
2 changes: 1 addition & 1 deletion mlua-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ cfg-if = "1.0"
pkg-config = "0.3.17"
lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true }
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
luau0-src = { version = "0.7.7", optional = true }
luau0-src = { version = "0.7.8", optional = true }
47 changes: 46 additions & 1 deletion mlua-sys/src/luau/lauxlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ extern "C-unwind" {
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;

pub fn luaL_checkbuffer(L: *mut lua_State, narg: c_int, len: *mut usize) -> *mut c_void;

pub fn luaL_where(L: *mut lua_State, lvl: c_int);

#[link_name = "luaL_errorL"]
Expand Down Expand Up @@ -152,5 +154,48 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
}

//
// TODO: Generic Buffer Manipulation
// Generic Buffer Manipulation
//

/// Buffer size used for on-stack string operations. This limit depends on native stack size.
pub const LUA_BUFFERSIZE: usize = 512;

#[repr(C)]
pub struct luaL_Strbuf {
p: *mut c_char, // current position in buffer
end: *mut c_char, // end of the current buffer
L: *mut lua_State,
storage: *mut c_void, // TString
buffer: [c_char; LUA_BUFFERSIZE],
}

// For compatibility
pub type luaL_Buffer = luaL_Strbuf;

extern "C-unwind" {
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Strbuf);
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
pub fn luaL_prepbuffsize(B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
pub fn luaL_addlstring(B: *mut luaL_Strbuf, s: *const c_char, l: usize);
pub fn luaL_addvalue(B: *mut luaL_Strbuf);
pub fn luaL_addvalueany(B: *mut luaL_Strbuf, idx: c_int);
pub fn luaL_pushresult(B: *mut luaL_Strbuf);
pub fn luaL_pushresultsize(B: *mut luaL_Strbuf, size: usize);
}

pub unsafe fn luaL_addchar(B: *mut luaL_Strbuf, c: c_char) {
if (*B).p >= (*B).end {
luaL_prepbuffsize(B, 1);
}
*(*B).p = c;
(*B).p = (*B).p.add(1);
}

pub unsafe fn luaL_addstring(B: *mut luaL_Strbuf, s: *const c_char) {
// Calculate length of s
let mut len = 0;
while *s.add(len) != 0 {
len += 1;
}
luaL_addlstring(B, s, len);
}
9 changes: 9 additions & 0 deletions mlua-sys/src/luau/lua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub const LUA_TTABLE: c_int = 6;
pub const LUA_TFUNCTION: c_int = 7;
pub const LUA_TUSERDATA: c_int = 8;
pub const LUA_TTHREAD: c_int = 9;
pub const LUA_TBUFFER: c_int = 10;

/// Guaranteed number of Lua stack slots available to a C function.
pub const LUA_MINSTACK: c_int = 20;
Expand Down Expand Up @@ -147,6 +148,7 @@ extern "C-unwind" {
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_tobuffer(L: *mut lua_State, idx: c_int, len: *mut usize) -> *mut c_void;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;

//
Expand Down Expand Up @@ -181,6 +183,8 @@ extern "C-unwind" {
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;

pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;

//
// Get functions (Lua -> stack)
//
Expand Down Expand Up @@ -372,6 +376,11 @@ pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}

#[inline(always)]
pub unsafe fn lua_isbuffer(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBUFFER) as c_int
}

#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
Expand Down
2 changes: 2 additions & 0 deletions mlua-sys/src/luau/lualib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_BITLIBNAME: &str = "bit32";
pub const LUA_BUFFERLIBNAME: &str = "buffer";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_DBLIBNAME: &str = "debug";
Expand All @@ -20,6 +21,7 @@ extern "C-unwind" {
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
pub fn luaopen_buffer(L: *mut lua_State) -> c_int;
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
Expand Down
5 changes: 4 additions & 1 deletion src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
#[inline]
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0))))
Ok(Value::UserData(AnyUserData(
lua.adopt_owned_ref(self.0),
self.1,
)))
}
}

Expand Down
26 changes: 23 additions & 3 deletions src/lua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2419,12 +2419,18 @@ impl Lua {
ffi::lua_pop(state, 1);
Nil
}
_ => Value::UserData(AnyUserData(self.pop_ref())),
_ => Value::UserData(AnyUserData(self.pop_ref(), 0)),
}
}

ffi::LUA_TTHREAD => Value::Thread(Thread::new(self.pop_ref())),

#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
// Buffer is represented as a userdata type
Value::UserData(AnyUserData(self.pop_ref(), crate::types::BUFFER_SUBTYPE_ID))
}

#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
ffi::lua_pop(state, 1);
Expand Down Expand Up @@ -2514,7 +2520,7 @@ impl Lua {
}
_ => {
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread()))
Value::UserData(AnyUserData(self.pop_ref_thread(), 0))
}
}
}
Expand All @@ -2524,6 +2530,14 @@ impl Lua {
Value::Thread(Thread::new(self.pop_ref_thread()))
}

#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
// Buffer is represented as a userdata type
ffi::lua_xpush(state, self.ref_thread(), idx);
let subtype_id = crate::types::BUFFER_SUBTYPE_ID;
Value::UserData(AnyUserData(self.pop_ref_thread(), subtype_id))
}

#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
// TODO: Fix this in a next major release
Expand Down Expand Up @@ -3112,7 +3126,7 @@ impl Lua {
ffi::lua_setuservalue(state, -2);
}

Ok(AnyUserData(self.pop_ref()))
Ok(AnyUserData(self.pop_ref(), 0))
}

#[cfg(not(feature = "luau"))]
Expand Down Expand Up @@ -3497,6 +3511,12 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
}
}

#[cfg(feature = "luau")]
if libs.contains(StdLib::BUFFER) {
requiref(state, ffi::LUA_BUFFERLIBNAME, ffi::luaopen_buffer, 1)?;
ffi::lua_pop(state, 1);
}

if libs.contains(StdLib::MATH) {
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
ffi::lua_pop(state, 1);
Expand Down
2 changes: 1 addition & 1 deletion src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg(not(feature = "luau"))]
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
ffi::lua_setmetatable(state, -2);
let ud = AnyUserData(lua.pop_ref());
let ud = AnyUserData(lua.pop_ref(), 0);
lua.register_raw_userdata_metatable(mt_ptr, None);

#[cfg(any(feature = "lua51", feature = "luajit"))]
Expand Down
15 changes: 15 additions & 0 deletions src/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,46 @@ impl StdLib {
feature = "luau"
))]
pub const COROUTINE: StdLib = StdLib(1);

/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
pub const TABLE: StdLib = StdLib(1 << 1);

/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub const IO: StdLib = StdLib(1 << 2);

/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
pub const OS: StdLib = StdLib(1 << 3);

/// [`string`](https://www.lua.org/manual/5.4/manual.html#6.4) library
pub const STRING: StdLib = StdLib(1 << 4);

/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
///
/// Requires `feature = "lua54/lua53/luau"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
pub const UTF8: StdLib = StdLib(1 << 5);

/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
///
/// Requires `feature = "lua52/luajit/luau"`
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
pub const BIT: StdLib = StdLib(1 << 6);

/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
pub const MATH: StdLib = StdLib(1 << 7);

/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub const PACKAGE: StdLib = StdLib(1 << 8);

/// [`buffer`](https://luau-lang.org/library#buffer-library) library
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub const BUFFER: StdLib = StdLib(1 << 9);

/// [`jit`](http://luajit.org/ext_jit.html) library
///
/// Requires `feature = "luajit"`
Expand All @@ -55,6 +69,7 @@ impl StdLib {
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
pub const FFI: StdLib = StdLib(1 << 30);

/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
pub const DEBUG: StdLib = StdLib(1 << 31);

Expand Down
4 changes: 4 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
pub type Number = ffi::lua_Number;

// LUA_TBUFFER subtype
#[cfg(feature = "luau")]
pub(crate) const BUFFER_SUBTYPE_ID: u8 = 1;

/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
Expand Down
13 changes: 9 additions & 4 deletions src/userdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ impl<T> Deref for UserDataVariant<T> {
/// [`is`]: crate::AnyUserData::is
/// [`borrow`]: crate::AnyUserData::borrow
#[derive(Clone, Debug)]
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) u8);

/// Owned handle to an internal Lua userdata.
///
Expand All @@ -801,14 +801,14 @@ pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef);
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef, pub(crate) u8);

#[cfg(feature = "unstable")]
impl OwnedAnyUserData {
/// Get borrowed handle to the underlying Lua userdata.
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> AnyUserData {
AnyUserData(self.0.to_ref())
AnyUserData(self.0.to_ref(), self.1)
}
}

Expand Down Expand Up @@ -1101,7 +1101,7 @@ impl<'lua> AnyUserData<'lua> {
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedAnyUserData {
OwnedAnyUserData(self.0.into_owned())
OwnedAnyUserData(self.0.into_owned(), self.1)
}

#[cfg(feature = "async")]
Expand All @@ -1112,6 +1112,11 @@ impl<'lua> AnyUserData<'lua> {

/// Returns a type name of this `UserData` (from a metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
#[cfg(feature = "luau")]
if self.1 == crate::types::BUFFER_SUBTYPE_ID {
return Ok(Some("buffer".to_owned()));
}

let lua = self.0.lua;
let state = lua.state();
unsafe {
Expand Down
2 changes: 2 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,8 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)),
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)),
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
_ => "<unknown>".to_string(),
}
}
Expand Down
17 changes: 15 additions & 2 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ impl<'lua> Value<'lua> {
Value::Table(_) => "table",
Value::Function(_) => "function",
Value::Thread(_) => "thread",
#[cfg(feature = "luau")]
Value::UserData(AnyUserData(_, crate::types::BUFFER_SUBTYPE_ID)) => "buffer",
Value::UserData(_) => "userdata",
Value::Error(_) => "error",
}
Expand Down Expand Up @@ -126,7 +128,7 @@ impl<'lua> Value<'lua> {
| Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
_ => ptr::null(),
}
}
Expand All @@ -148,7 +150,7 @@ impl<'lua> Value<'lua> {
Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r)) => unsafe {
| Value::UserData(AnyUserData(r, ..)) => unsafe {
let state = r.lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
Expand Down Expand Up @@ -410,6 +412,17 @@ impl<'lua> Value<'lua> {
}
}

/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
#[inline]
pub fn is_buffer(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == crate::types::BUFFER_SUBTYPE_ID)
.unwrap_or_default()
}

/// Wrap reference to this Value into [`SerializableValue`].
///
/// This allows customizing serialization behavior using serde.
Expand Down
Loading

0 comments on commit 5043447

Please sign in to comment.