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