mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Add new Buffer type for Luau.
Previously it was represented as `AnyUserData` which is not always convenient.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// A Luau buffer type.
|
||||
///
|
||||
/// See the buffer [documentation] for more information.
|
||||
///
|
||||
/// [documentation]: https://luau.org/library#buffer-library
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Buffer(pub(crate) ValueRef);
|
||||
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
impl Buffer {
|
||||
/// Copies the buffer data into a new `Vec<u8>`.
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
unsafe { self.as_slice().to_vec() }
|
||||
}
|
||||
|
||||
/// Returns the length of the buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { self.as_slice().len() }
|
||||
}
|
||||
|
||||
/// Returns `true` if the buffer is empty.
|
||||
#[doc(hidden)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Reads given number of bytes from the buffer at the given offset.
|
||||
///
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
|
||||
let data = unsafe { self.as_slice() };
|
||||
let mut bytes = [0u8; N];
|
||||
bytes.copy_from_slice(&data[offset..offset + N]);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Writes given bytes to the buffer at the given offset.
|
||||
///
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
|
||||
let data = unsafe {
|
||||
let (buf, size) = self.as_raw_parts();
|
||||
std::slice::from_raw_parts_mut(buf, size)
|
||||
};
|
||||
data[offset..offset + bytes.len()].copy_from_slice(bytes);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
|
||||
let (buf, size) = self.as_raw_parts();
|
||||
std::slice::from_raw_parts(buf, size)
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
||||
let lua = self.0.lua.lock();
|
||||
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");
|
||||
(buf as *mut u8, size)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Buffer {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
serializer.serialize_bytes(unsafe { self.as_slice() })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl crate::types::LuaType for Buffer {
|
||||
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
|
||||
}
|
||||
+38
-7
@@ -367,6 +367,43 @@ impl FromLua for crate::types::Vector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoLua for crate::Buffer {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Buffer(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoLua for &crate::Buffer {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Buffer(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl FromLua for crate::Buffer {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Buffer(buf) => Ok(buf),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "buffer".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for StdString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
@@ -515,13 +552,7 @@ impl FromLua for BString {
|
||||
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 lua = ud.0.lua.lock();
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(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())
|
||||
},
|
||||
Value::Buffer(buf) => unsafe { Ok(buf.as_slice().into()) },
|
||||
_ => Ok((*lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
|
||||
+2
-1
@@ -78,6 +78,7 @@
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod error;
|
||||
@@ -130,7 +131,7 @@ pub use crate::hook::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{chunk::Compiler, function::CoverageInfo, types::Vector};
|
||||
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, types::Vector};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
|
||||
+2
-9
@@ -145,14 +145,7 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let lua = ud.0.lua.lock();
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(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::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
@@ -463,7 +456,7 @@ impl<'a> MapPairs<'a> {
|
||||
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
|
||||
if sort_keys {
|
||||
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
|
||||
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
|
||||
pairs.sort_by(|(a, _), (b, _)| b.sort_cmp(a)); // reverse order as we pop values from the end
|
||||
Ok(MapPairs::Vec(pairs))
|
||||
} else {
|
||||
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
|
||||
|
||||
+6
-7
@@ -31,7 +31,7 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil
|
||||
use crate::hook::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::chunk::Compiler;
|
||||
use crate::{buffer::Buffer, chunk::Compiler};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
@@ -996,22 +996,21 @@ impl Lua {
|
||||
/// 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> {
|
||||
use crate::types::SubtypeId;
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
if lua.unlikely_memory_error() {
|
||||
crate::util::push_buffer(lua.ref_thread(), buf.as_ref(), false)?;
|
||||
return Ok(AnyUserData(lua.pop_ref_thread(), SubtypeId::Buffer));
|
||||
return Ok(Buffer(lua.pop_ref_thread()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
crate::util::push_buffer(state, buf.as_ref(), true)?;
|
||||
Ok(AnyUserData(lua.pop_ref(), SubtypeId::Buffer))
|
||||
Ok(Buffer(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -551,6 +551,8 @@ impl RawLua {
|
||||
Value::Function(f) => self.push_ref(&f.0),
|
||||
Value::Thread(t) => self.push_ref(&t.0),
|
||||
Value::UserData(ud) => self.push_ref(&ud.0),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => self.push_ref(&buf.0),
|
||||
Value::Error(err) => {
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_internal_userdata(state, WrappedFailure::Error(*err.clone()), protect)?;
|
||||
@@ -652,9 +654,8 @@ impl RawLua {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => {
|
||||
// Buffer is represented as a userdata type
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer))
|
||||
Value::Buffer(crate::Buffer(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
|
||||
+1
-1
@@ -785,7 +785,7 @@ impl Table {
|
||||
// Collect key/value pairs into a vector so we can sort them
|
||||
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
// Sort keys
|
||||
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
|
||||
if pairs.is_empty() {
|
||||
return write!(fmt, "{{}}");
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@ pub type Number = ffi::lua_Number;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum SubtypeId {
|
||||
None,
|
||||
#[cfg(feature = "luau")]
|
||||
Buffer,
|
||||
#[cfg(feature = "luajit")]
|
||||
CData,
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ use super::LuaType;
|
||||
/// By default vectors are 3-dimensional, but can be 4-dimensional
|
||||
/// if the `luau-vector4` feature is enabled.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct Vector(pub(crate) [f32; Self::SIZE]);
|
||||
|
||||
impl fmt::Display for Vector {
|
||||
|
||||
@@ -937,8 +937,6 @@ impl AnyUserData {
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
match self.1 {
|
||||
SubtypeId::None => {}
|
||||
#[cfg(feature = "luau")]
|
||||
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
|
||||
#[cfg(feature = "luajit")]
|
||||
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
|
||||
}
|
||||
@@ -1111,19 +1109,6 @@ impl Serialize for AnyUserData {
|
||||
S: Serializer,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = lua
|
||||
.get_userdata_ref_type_id(&self.0)
|
||||
|
||||
+41
-10
@@ -48,7 +48,7 @@ pub enum Value {
|
||||
/// A Luau vector.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Vector(crate::types::Vector),
|
||||
Vector(crate::Vector),
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
@@ -60,8 +60,13 @@ pub enum Value {
|
||||
/// Reference to a Lua thread (or coroutine).
|
||||
Thread(Thread),
|
||||
/// Reference to a userdata object that holds a custom type which implements `UserData`.
|
||||
///
|
||||
/// Special builtin userdata types will be represented as other `Value` variants.
|
||||
UserData(AnyUserData),
|
||||
/// A Luau buffer.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Buffer(crate::Buffer),
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Box<Error>),
|
||||
}
|
||||
@@ -89,10 +94,10 @@ impl Value {
|
||||
Value::Function(_) => "function",
|
||||
Value::Thread(_) => "thread",
|
||||
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
|
||||
#[cfg(feature = "luajit")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(_) => "buffer",
|
||||
Value::Error(_) => "error",
|
||||
}
|
||||
}
|
||||
@@ -131,6 +136,8 @@ impl Value {
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(crate::Buffer(r)) => r.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
@@ -165,6 +172,8 @@ impl Value {
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
||||
},
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => StdString::from_utf8(buf.to_vec()).map_err(Error::external),
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -416,15 +425,25 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
|
||||
/// Cast the value to a `Buffer`.
|
||||
///
|
||||
/// If the value is `Buffer`, returns it or `None` otherwise.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[inline]
|
||||
pub fn as_buffer(&self) -> Option<&crate::Buffer> {
|
||||
match self {
|
||||
Value::Buffer(b) => Some(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a `Buffer`.
|
||||
#[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 == SubtypeId::Buffer)
|
||||
.unwrap_or_default()
|
||||
self.as_buffer().is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
|
||||
@@ -450,7 +469,7 @@ impl Value {
|
||||
|
||||
// Compares two values.
|
||||
// Used to sort values for Debug printing.
|
||||
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
|
||||
pub(crate) fn sort_cmp(&self, other: &Self) -> Ordering {
|
||||
fn cmp_num(a: Number, b: Number) -> Ordering {
|
||||
match (a, b) {
|
||||
_ if a < b => Ordering::Less,
|
||||
@@ -479,11 +498,14 @@ impl Value {
|
||||
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
|
||||
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
|
||||
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
|
||||
// Vector (Luau)
|
||||
#[cfg(feature = "luau")]
|
||||
(Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
|
||||
// String
|
||||
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
|
||||
(Value::String(_), _) => Ordering::Less,
|
||||
(_, Value::String(_)) => Ordering::Greater,
|
||||
// Other variants can be randomly ordered
|
||||
// Other variants can be ordered by their pointer
|
||||
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
|
||||
}
|
||||
}
|
||||
@@ -520,6 +542,8 @@ impl Value {
|
||||
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
|
||||
write!(fmt, "{s}")
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
Value::Error(_) => write!(fmt, "error"),
|
||||
}
|
||||
@@ -531,6 +555,7 @@ impl fmt::Debug for Value {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
|
||||
}
|
||||
|
||||
match self {
|
||||
Value::Nil => write!(fmt, "Nil"),
|
||||
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
|
||||
@@ -544,6 +569,8 @@ impl fmt::Debug for Value {
|
||||
Value::Function(f) => write!(fmt, "{f:?}"),
|
||||
Value::Thread(t) => write!(fmt, "{t:?}"),
|
||||
Value::UserData(ud) => write!(fmt, "{ud:?}"),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => write!(fmt, "{buf:?}"),
|
||||
Value::Error(e) => write!(fmt, "Error({e:?})"),
|
||||
}
|
||||
}
|
||||
@@ -566,6 +593,8 @@ impl PartialEq for Value {
|
||||
(Value::Function(a), Value::Function(b)) => a == b,
|
||||
(Value::Thread(a), Value::Thread(b)) => a == b,
|
||||
(Value::UserData(a), Value::UserData(b)) => a == b,
|
||||
#[cfg(feature = "luau")]
|
||||
(Value::Buffer(a), Value::Buffer(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -674,6 +703,8 @@ impl<'a> Serialize for SerializableValue<'a> {
|
||||
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => buf.serialize(serializer),
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#![cfg(feature = "luau")]
|
||||
|
||||
use mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf1 = lua
|
||||
.load(
|
||||
r#"
|
||||
local buf = buffer.fromstring("hello")
|
||||
assert(buffer.len(buf) == 5)
|
||||
return buf
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
assert!(buf1.is_buffer());
|
||||
assert_eq!(buf1.type_name(), "buffer");
|
||||
|
||||
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
|
||||
assert_ne!(buf1, buf2);
|
||||
|
||||
// Check that we can pass buffer type to Lua
|
||||
let buf1 = buf1.as_buffer().unwrap();
|
||||
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
|
||||
assert_eq!(func.call::<String>(buf1)?, "hello");
|
||||
|
||||
// Check buffer methods
|
||||
assert_eq!(buf1.len(), 5);
|
||||
assert_eq!(buf1.to_vec(), b"hello");
|
||||
assert_eq!(buf1.read_bytes::<3>(1), [b'e', b'l', b'l']);
|
||||
buf1.write_bytes(1, b"i");
|
||||
assert_eq!(buf1.to_vec(), b"hillo");
|
||||
|
||||
let buf3 = lua.create_buffer(b"")?;
|
||||
assert!(buf3.is_empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "range end index 14 out of range for slice of length 13")]
|
||||
fn test_buffer_out_of_bounds_read() {
|
||||
let lua = Lua::new();
|
||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||
_ = buf.read_bytes::<1>(13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "range end index 16 out of range for slice of length 13")]
|
||||
fn test_buffer_out_of_bounds_write() {
|
||||
let lua = Lua::new();
|
||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||
buf.write_bytes(14, b"!!");
|
||||
}
|
||||
+2
-2
@@ -384,8 +384,8 @@ fn test_bstring_from_lua() -> Result<()> {
|
||||
fn test_bstring_from_lua_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let b = lua.create_buffer("hello, world")?;
|
||||
let bstr = lua.unpack::<BString>(Value::UserData(b))?;
|
||||
let buf = lua.create_buffer("hello, world")?;
|
||||
let bstr = lua.convert::<BString>(buf)?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
// Test from stack
|
||||
|
||||
@@ -465,32 +465,6 @@ fn test_coverage() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf1 = lua
|
||||
.load(
|
||||
r#"
|
||||
local buf = buffer.fromstring("hello")
|
||||
assert(buffer.len(buf) == 5)
|
||||
return buf
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
assert!(buf1.is_userdata() && buf1.is_buffer());
|
||||
assert_eq!(buf1.type_name(), "buffer");
|
||||
|
||||
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
|
||||
assert_ne!(buf1, buf2);
|
||||
|
||||
// Check that we can pass buffer type to Lua
|
||||
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
|
||||
assert!(func.call::<String>(buf1)?.starts_with("buffer:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fflags() {
|
||||
// We cannot really on any particular feature flag to be present
|
||||
|
||||
+10
-8
@@ -717,27 +717,29 @@ fn test_arbitrary_precision() {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_buffer_serialize() {
|
||||
fn test_buffer_serialize() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4])?;
|
||||
let val = serde_value::to_value(&buf).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
|
||||
|
||||
// Try empty buffer
|
||||
let buf = lua.create_buffer(&[]).unwrap();
|
||||
let buf = lua.create_buffer(&[])?;
|
||||
let val = serde_value::to_value(&buf).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_buffer_from_value() {
|
||||
fn test_buffer_from_value() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
|
||||
let val = lua
|
||||
.from_value::<serde_value::Value>(Value::UserData(buf))
|
||||
.unwrap();
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4])?;
|
||||
let val = lua.from_value::<serde_value::Value>(Value::Buffer(buf)).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user