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    #[track_caller]
41    pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
42        let lua = self.0.lua.lock();
43        let data = self.as_slice(&lua);
44        let mut bytes = [0u8; N];
45        bytes.copy_from_slice(&data[offset..offset + N]);
46        bytes
47    }
48
49    /// Writes given bytes to the buffer at the given offset.
50    ///
51    /// Offset is 0-based.
52    #[track_caller]
53    pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
54        let lua = self.0.lua.lock();
55        let data = self.as_slice_mut(&lua);
56        data[offset..offset + bytes.len()].copy_from_slice(bytes);
57    }
58
59    /// Returns an adaptor implementing [`io::Read`], [`io::Write`] and [`io::Seek`] over the
60    /// buffer.
61    ///
62    /// Buffer operations are infallible, none of the read/write functions will return an Err.
63    pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
64        BufferCursor(self, 0)
65    }
66
67    pub(crate) fn as_slice(&self, lua: &RawLua) -> &[u8] {
68        unsafe {
69            let (buf, size) = self.as_raw_parts(lua);
70            std::slice::from_raw_parts(buf, size)
71        }
72    }
73
74    #[allow(clippy::mut_from_ref)]
75    fn as_slice_mut(&self, lua: &RawLua) -> &mut [u8] {
76        unsafe {
77            let (buf, size) = self.as_raw_parts(lua);
78            std::slice::from_raw_parts_mut(buf, size)
79        }
80    }
81
82    #[cfg(feature = "luau")]
83    unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
84        let mut size = 0usize;
85        let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
86        mlua_assert!(!buf.is_null(), "invalid Luau buffer");
87        (buf as *mut u8, size)
88    }
89
90    #[cfg(not(feature = "luau"))]
91    unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
92        unreachable!()
93    }
94}
95
96struct BufferCursor(Buffer, usize);
97
98impl io::Read for BufferCursor {
99    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
100        let lua = self.0.0.lua.lock();
101        let data = self.0.as_slice(&lua);
102        if self.1 == data.len() {
103            return Ok(0);
104        }
105        let len = buf.len().min(data.len() - self.1);
106        buf[..len].copy_from_slice(&data[self.1..self.1 + len]);
107        self.1 += len;
108        Ok(len)
109    }
110}
111
112impl io::Write for BufferCursor {
113    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
114        let lua = self.0.0.lua.lock();
115        let data = self.0.as_slice_mut(&lua);
116        if self.1 == data.len() {
117            return Ok(0);
118        }
119        let len = buf.len().min(data.len() - self.1);
120        data[self.1..self.1 + len].copy_from_slice(&buf[..len]);
121        self.1 += len;
122        Ok(len)
123    }
124
125    fn flush(&mut self) -> io::Result<()> {
126        Ok(())
127    }
128}
129
130impl io::Seek for BufferCursor {
131    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
132        let lua = self.0.0.lua.lock();
133        let data = self.0.as_slice(&lua);
134        let new_offset = match pos {
135            io::SeekFrom::Start(offset) => offset as i64,
136            io::SeekFrom::End(offset) => data.len() as i64 + offset,
137            io::SeekFrom::Current(offset) => self.1 as i64 + offset,
138        };
139        if new_offset < 0 {
140            return Err(io::Error::new(
141                io::ErrorKind::InvalidInput,
142                "invalid seek to a negative position",
143            ));
144        }
145        if new_offset as usize > data.len() {
146            return Err(io::Error::new(
147                io::ErrorKind::InvalidInput,
148                "invalid seek to a position beyond the end of the buffer",
149            ));
150        }
151        self.1 = new_offset as usize;
152        Ok(self.1 as u64)
153    }
154}
155
156#[cfg(feature = "serde")]
157impl Serialize for Buffer {
158    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
159        let lua = self.0.lua.lock();
160        serializer.serialize_bytes(self.as_slice(&lua))
161    }
162}
163
164#[cfg(feature = "luau")]
165impl crate::types::LuaType for Buffer {
166    const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
167}