Skip to main content

mlua/
buffer.rs

1use std::io;
2
3#[cfg(feature = "serde")]
4use serde::ser::{Serialize, Serializer};
5
6use crate::state::RawLua;
7use crate::types::ValueRef;
8
9/// A Luau buffer type.
10///
11/// See the buffer [documentation] for more information.
12///
13/// [documentation]: https://luau.org/library#buffer-library
14#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
15#[derive(Clone, Debug, PartialEq)]
16pub struct Buffer(pub(crate) ValueRef);
17
18#[cfg_attr(not(feature = "luau"), allow(unused))]
19impl Buffer {
20    /// Copies the buffer data into a new `Vec<u8>`.
21    pub fn to_vec(&self) -> Vec<u8> {
22        let lua = self.0.lua.lock();
23        self.as_slice(&lua).to_vec()
24    }
25
26    /// Returns the length of the buffer.
27    pub fn len(&self) -> usize {
28        let lua = self.0.lua.lock();
29        self.as_slice(&lua).len()
30    }
31
32    /// Returns `true` if the buffer is empty.
33    pub fn is_empty(&self) -> bool {
34        self.len() == 0
35    }
36
37    /// Reads given number of bytes from the buffer at the given offset.
38    ///
39    /// Offset is 0-based.
40    ///
41    /// # Panics
42    ///
43    /// Panics if `offset + N` is greater than the buffer length.
44    #[track_caller]
45    pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
46        let lua = self.0.lua.lock();
47        let data = self.as_slice(&lua);
48        let mut bytes = [0u8; N];
49        bytes.copy_from_slice(&data[offset..offset + N]);
50        bytes
51    }
52
53    /// Writes given bytes to the buffer at the given offset.
54    ///
55    /// Offset is 0-based.
56    ///
57    /// # Panics
58    ///
59    /// Panics if `offset + bytes.len()` is greater than the buffer length.
60    #[track_caller]
61    pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
62        let lua = self.0.lua.lock();
63        let data = self.as_slice_mut(&lua);
64        data[offset..offset + bytes.len()].copy_from_slice(bytes);
65    }
66
67    /// Returns an adaptor implementing [`io::Read`], [`io::Write`] and [`io::Seek`] over the
68    /// buffer.
69    ///
70    /// Buffer operations are infallible, none of the read/write functions will return an Err.
71    pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
72        BufferCursor(self, 0)
73    }
74
75    pub(crate) fn as_slice(&self, lua: &RawLua) -> &[u8] {
76        unsafe {
77            let (buf, size) = self.as_raw_parts(lua);
78            std::slice::from_raw_parts(buf, size)
79        }
80    }
81
82    #[allow(clippy::mut_from_ref)]
83    fn as_slice_mut(&self, lua: &RawLua) -> &mut [u8] {
84        unsafe {
85            let (buf, size) = self.as_raw_parts(lua);
86            std::slice::from_raw_parts_mut(buf, size)
87        }
88    }
89
90    #[cfg(feature = "luau")]
91    unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
92        let mut size = 0usize;
93        let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
94        mlua_assert!(!buf.is_null(), "invalid Luau buffer");
95        (buf as *mut u8, size)
96    }
97
98    #[cfg(not(feature = "luau"))]
99    unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
100        unreachable!()
101    }
102}
103
104struct BufferCursor(Buffer, usize);
105
106impl io::Read for BufferCursor {
107    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
108        let lua = self.0.0.lua.lock();
109        let data = self.0.as_slice(&lua);
110        if self.1 == data.len() {
111            return Ok(0);
112        }
113        let len = buf.len().min(data.len() - self.1);
114        buf[..len].copy_from_slice(&data[self.1..self.1 + len]);
115        self.1 += len;
116        Ok(len)
117    }
118}
119
120impl io::Write for BufferCursor {
121    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
122        let lua = self.0.0.lua.lock();
123        let data = self.0.as_slice_mut(&lua);
124        if self.1 == data.len() {
125            return Ok(0);
126        }
127        let len = buf.len().min(data.len() - self.1);
128        data[self.1..self.1 + len].copy_from_slice(&buf[..len]);
129        self.1 += len;
130        Ok(len)
131    }
132
133    fn flush(&mut self) -> io::Result<()> {
134        Ok(())
135    }
136}
137
138impl io::Seek for BufferCursor {
139    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
140        let lua = self.0.0.lua.lock();
141        let data = self.0.as_slice(&lua);
142        let new_offset = match pos {
143            io::SeekFrom::Start(offset) => offset as i64,
144            io::SeekFrom::End(offset) => data.len() as i64 + offset,
145            io::SeekFrom::Current(offset) => self.1 as i64 + offset,
146        };
147        if new_offset < 0 {
148            return Err(io::Error::new(
149                io::ErrorKind::InvalidInput,
150                "invalid seek to a negative position",
151            ));
152        }
153        if new_offset as usize > data.len() {
154            return Err(io::Error::new(
155                io::ErrorKind::InvalidInput,
156                "invalid seek to a position beyond the end of the buffer",
157            ));
158        }
159        self.1 = new_offset as usize;
160        Ok(self.1 as u64)
161    }
162}
163
164#[cfg(feature = "serde")]
165impl Serialize for Buffer {
166    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
167        let lua = self.0.lua.lock();
168        serializer.serialize_bytes(self.as_slice(&lua))
169    }
170}
171
172#[cfg(feature = "luau")]
173impl crate::types::LuaType for Buffer {
174    const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
175}