Better Luau buffer type support.

- Add `Lua::create_buffer()` function
- Support serializing buffer type as a byte slice
- Support accessing copy of underlying bytes using `BString`
This commit is contained in:
Alex Orlenko
2024-03-28 13:05:01 +00:00
parent b62f2ee0f7
commit fa217d3706
7 changed files with 162 additions and 5 deletions
+35 -5
View File
@@ -679,19 +679,49 @@ impl<'lua> IntoLua<'lua> for BString {
}
impl<'lua> FromLua<'lua> for BString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(BString::from(
lua.coerce_string(value)?
match value {
Value::String(s) => Ok(s.as_bytes().into()),
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let mut size = 0usize;
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
Ok(slice::from_raw_parts(buf as *const u8, size).into())
},
_ => Ok(lua
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "BString",
message: Some("expected string or number".to_string()),
})?
.as_bytes()
.to_vec(),
))
.into()),
}
}
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
let state = lua.state();
match ffi::lua_type(state, idx) {
ffi::LUA_TSTRING => {
let mut size = 0;
let data = ffi::lua_tolstring(state, idx, &mut size);
Ok(slice::from_raw_parts(data as *const u8, size).into())
}
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
let mut size = 0;
let buf = ffi::lua_tobuffer(state, idx, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
Ok(slice::from_raw_parts(buf as *const u8, size).into())
}
_ => {
// Fallback to default
Self::from_lua(lua.stack_value(idx), lua)
}
}
}
}
+21
View File
@@ -1373,6 +1373,27 @@ impl Lua {
}
}
/// Create and return a Luau [buffer] object from a byte slice of data.
///
/// Requires `feature = "luau"`
///
/// [buffer]: https://luau-lang.org/library#buffer-library
#[cfg(feature = "luau")]
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<AnyUserData> {
let state = self.state();
unsafe {
if self.unlikely_memory_error() {
crate::util::push_buffer(self.ref_thread(), buf.as_ref(), false)?;
return Ok(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer));
}
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
crate::util::push_buffer(state, buf.as_ref(), true)?;
Ok(AnyUserData(self.pop_ref(), SubtypeId::Buffer))
}
}
/// Creates and returns a new empty table.
pub fn create_table(&self) -> Result<Table> {
self.create_table_with_capacity(0, 0)
+8
View File
@@ -148,6 +148,14 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
Value::UserData(ud) if ud.is_serializable() => {
serde_userdata(ud, |value| value.deserialize_any(visitor))
}
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let mut size = 0usize;
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
let buf = std::slice::from_raw_parts(buf as *const u8, size);
visitor.visit_bytes(buf)
},
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
+13
View File
@@ -1340,6 +1340,19 @@ impl<'lua> Serialize for AnyUserData<'lua> {
S: Serializer,
{
let lua = self.0.lua;
// Special case for Luau buffer type
#[cfg(feature = "luau")]
if self.1 == SubtypeId::Buffer {
let buf = unsafe {
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
std::slice::from_raw_parts(buf as *const u8, size)
};
return serializer.serialize_bytes(buf);
}
let data = unsafe {
let _ = lua
.get_userdata_ref_type_id(&self.0)
+14
View File
@@ -253,6 +253,20 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
}
}
// Uses 3 stack spaces (when protect), does not call checkstack.
#[cfg(feature = "luau")]
#[inline(always)]
pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
let data = if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
} else {
ffi::lua_newbuffer(state, b.len())
};
let buf = slice::from_raw_parts_mut(data as *mut u8, b.len());
buf.copy_from_slice(b);
Ok(())
}
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(