use std::io;
use crate::error::Result;
use crate::state::{Lua, LuaRef};
use crate::sync::{NotSync, XRc, NOT_SYNC};
use crate::sys::*;
#[derive(Clone)]
pub struct Buffer {
pub(crate) reference: XRc<LuaRef>,
pub(crate) _not_sync: NotSync,
}
impl Buffer {
pub(crate) fn from_ref(reference: LuaRef) -> Buffer {
Buffer {
reference: XRc::new(reference),
_not_sync: NOT_SYNC,
}
}
pub(crate) unsafe fn push_to_stack(&self) {
self.reference.push();
}
pub(crate) fn lua(&self) -> Lua {
self.reference.lua()
}
pub fn to_vec(&self) -> Vec<u8> {
self.as_slice().to_vec()
}
pub fn len(&self) -> usize {
self.as_slice().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[track_caller]
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
let data = self.as_slice();
let mut bytes = [0u8; N];
bytes.copy_from_slice(&data[offset..offset + N]);
bytes
}
#[track_caller]
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
let data = self.as_slice_mut();
data[offset..offset + bytes.len()].copy_from_slice(bytes);
}
pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
BufferCursor(self, 0)
}
pub(crate) fn to_pointer(&self) -> *const std::ffi::c_void {
let state = self.reference.state();
unsafe {
self.reference.push();
let p = lua_topointer(state, -1);
lua_pop(state, 1);
p
}
}
pub(crate) fn as_slice(&self) -> &[u8] {
unsafe {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts(buf, size)
}
}
#[allow(clippy::mut_from_ref)]
pub(crate) fn as_slice_mut(&self) -> &mut [u8] {
unsafe {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts_mut(buf, size)
}
}
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
let state = self.reference.state();
unsafe {
self.reference.push();
let mut size = 0usize;
let buf = lua_tobuffer(state, -1, &mut size);
lua_pop(state, 1);
assert!(!buf.is_null(), "invalid Luau buffer");
(buf as *mut u8, size)
}
}
}
impl std::fmt::Debug for Buffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Buffer({:?})", self.as_slice())
}
}
impl PartialEq for Buffer {
fn eq(&self, other: &Self) -> bool {
self.to_pointer() == other.to_pointer()
}
}
struct BufferCursor(Buffer, usize);
impl io::Read for BufferCursor {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let data = self.0.as_slice();
if self.1 == data.len() {
return Ok(0);
}
let len = buf.len().min(data.len() - self.1);
buf[..len].copy_from_slice(&data[self.1..self.1 + len]);
self.1 += len;
Ok(len)
}
}
impl io::Write for BufferCursor {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let data = self.0.as_slice_mut();
if self.1 == data.len() {
return Ok(0);
}
let len = buf.len().min(data.len() - self.1);
data[self.1..self.1 + len].copy_from_slice(&buf[..len]);
self.1 += len;
Ok(len)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Seek for BufferCursor {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
let data = self.0.as_slice();
let new_offset = match pos {
io::SeekFrom::Start(offset) => offset as i64,
io::SeekFrom::End(offset) => data.len() as i64 + offset,
io::SeekFrom::Current(offset) => self.1 as i64 + offset,
};
if new_offset < 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid seek to a negative position",
));
}
if new_offset as usize > data.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid seek to a position beyond the end of the buffer",
));
}
self.1 = new_offset as usize;
Ok(self.1 as u64)
}
}
unsafe fn c_newbuffer(state: *mut lua_State) -> c_int {
unsafe {
let size = lua_tonumberx(state, 1, core::ptr::null_mut()) as usize;
lua_settop(state, 0);
lua_newbuffer(state, size);
1
}
}
pub(crate) fn create_buffer_with_capacity(lua: &Lua, size: usize) -> Result<Buffer> {
let state = lua.state();
unsafe {
lua_pushcclosurek(
state,
Some(c_newbuffer),
c"luaur-rt-newbuffer".as_ptr(),
0,
None,
);
lua_pushnumber(state, size as f64);
let status = lua_pcall(state, 1, 1, 0);
if status != 0 {
return Err(lua.pop_error(status));
}
Ok(Buffer::from_ref(lua.pop_ref()))
}
}