use core::{ffi::{CStr, c_char}, marker::PhantomData, mem::MaybeUninit, ops::AddAssign, pin::Pin, slice};
use crate::{AmountReturned, LuaState, ffi::{self, luaL_Buffer}};
#[repr(transparent)]
#[derive(Debug)]
pub struct StringBuffer<'a>(luaL_Buffer, PhantomData<&'a mut LuaState>);
impl StringBuffer<'_> {
#[inline]
#[must_use]
pub const fn uninit() -> MaybeUninit<Self> {
MaybeUninit::uninit()
}
#[inline]
#[must_use]
pub fn as_mut_ptr(self: &mut Pin<&mut Self>) -> *mut luaL_Buffer {
(&raw const self.0).cast_mut()
}
#[inline]
#[allow(clippy::cast_possible_wrap)]
pub fn add_byte(self: &mut Pin<&mut Self>, byte: u8) {
unsafe { ffi::luaL_addchar(self.as_mut_ptr(), byte as c_char); }
}
#[inline]
pub fn add_char(self: &mut Pin<&mut Self>, ch: char) {
if ch.is_ascii() {
self.add_byte(ch as u8);
} else {
let mut bytes = [0u8; 4];
self.add_string(ch.encode_utf8(&mut bytes));
}
}
#[inline]
pub fn add_string(self: &mut Pin<&mut Self>, string: impl AsRef<[u8]>) {
self.add_byte_string(string.as_ref());
}
#[inline]
pub fn add_byte_string(self: &mut Pin<&mut Self>, byte_string: &[u8]) {
unsafe { ffi::luaL_addlstring(self.as_mut_ptr(), byte_string.as_ptr().cast(), byte_string.len()); }
}
#[inline]
pub fn add_cstring(self: &mut Pin<&mut Self>, cstring: &CStr) {
unsafe { ffi::luaL_addstring(self.as_mut_ptr(), cstring.as_ptr()); }
}
#[inline]
pub fn add_value(self: &mut Pin<&mut Self>, f: impl FnOnce(&mut LuaState)) {
let mut lua = unsafe { LuaState::from_ptr(self.0.L) };
let top = lua.get_top();
f(&mut lua);
assert!(lua.get_top() - top == 1, "tried to append a value to a buffer by pushing {} elements instead of single element", lua.get_top() - top);
unsafe { ffi::luaL_addvalue(self.as_mut_ptr()); }
}
pub fn add_size<'a>(self: &'a mut Pin<&mut Self>, amount: usize, fill: u8) -> &'a mut [u8] {
assert!(
amount <= StringBuffer::BUFFER_AREA_SIZE,
"tried to append the next {amount} bytes in the buffer area to a buffer, but the size of the buffer area is only {} bytes",
StringBuffer::BUFFER_AREA_SIZE
);
unsafe {
let ptr = ffi::luaL_prepbuffer(self.as_mut_ptr()).cast::<u8>();
ptr.write_bytes(fill, amount);
ffi::luaL_addsize(self.as_mut_ptr(), amount);
slice::from_raw_parts_mut(ptr, amount)
}
}
#[inline]
pub unsafe fn add_size_unchecked(self: &mut Pin<&mut Self>, amount: usize) {
unsafe { ffi::luaL_addsize(self.as_mut_ptr(), amount); }
}
pub const BUFFER_AREA_SIZE: usize = ffi::LUAL_BUFFERSIZE;
#[inline]
pub fn flush_buffer<'a>(self: &'a mut Pin<&mut Self>) -> &'a mut [MaybeUninit<u8>; StringBuffer::BUFFER_AREA_SIZE] {
unsafe { &mut *ffi::luaL_prepbuffer(self.as_mut_ptr()).cast() }
}
#[inline]
pub fn push_result(self: &mut Pin<&mut Self>) {
unsafe { ffi::luaL_pushresult(self.as_mut_ptr()); }
}
#[inline]
pub unsafe fn return_result(mut self: Pin<&mut Self>) -> AmountReturned {
self.push_result();
unsafe { AmountReturned::unchecked(1) }
}
#[inline]
pub fn lua<T>(self: &mut Pin<&mut Self>, f: impl FnOnce(&mut LuaState) -> T) -> T {
let mut lua = unsafe { LuaState::from_ptr(self.0.L) };
let top = lua.get_top();
let result = f(&mut lua);
assert!(top == lua.get_top(), "temporarily accessed the Lua state as the stack held {top} elements, but left it with {} elements instead", lua.get_top());
result
}
}
impl<T: AsRef<[u8]>> AddAssign<T> for Pin<&mut StringBuffer<'_>> {
#[inline]
fn add_assign(&mut self, rhs: T) {
self.add_string(rhs);
}
}
impl<T: AsRef<[u8]>> Extend<T> for Pin<&mut StringBuffer<'_>> {
#[inline]
fn extend<U: IntoIterator<Item = T>>(&mut self, iter: U) {
for string in iter {
self.add_string(string);
}
}
}
impl LuaState {
#[inline]
pub fn init_buffer<'a>(&'a mut self, buffer: Pin<&'a mut MaybeUninit<StringBuffer<'a>>>) -> Pin<&'a mut StringBuffer<'a>> {
unsafe {
ffi::luaL_buffinit(self.0, buffer.as_ptr().cast_mut().cast());
buffer.map_unchecked_mut(|buffer| buffer.assume_init_mut())
}
}
}