use core::ptr::NonNull;
use crate::VmErrorResult;
use crate::gc::GcObject;
use crate::handle::{RawHandle, sealed::Sealed};
use crate::layout::Align8Byte;
use crate::memory::{LuaPage, MemoryRuntime};
use crate::thread::Thread;
use crate::types::LUA_TBUFFER;
#[repr(C)]
pub struct RawBuffer {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
pub len: u32,
pub data: [Align8Byte; 1],
}
pub const MAX_BUFFER_SIZE: usize = 1 << 30;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Buffer {
raw: NonNull<RawBuffer>,
}
#[allow(
clippy::missing_safety_doc,
reason = "all methods share the capability-level safety contract"
)]
pub trait BufferRuntime: Sealed {
unsafe fn new_buffer_internal(&self, size: usize) -> VmErrorResult<Buffer>;
unsafe fn free_buffer(&self, buffer: Buffer, page: LuaPage);
}
impl Buffer {
pub const unsafe fn from_raw(raw: NonNull<RawBuffer>) -> Self {
Self { raw }
}
pub const fn size_buffer(len: usize) -> usize {
core::mem::offset_of!(RawBuffer, data) + if len > 8 { len } else { 8 }
}
pub fn len(&self) -> usize {
unsafe { (*self.as_ptr()).len as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub const fn data_ptr(&self) -> *const u8 {
unsafe { (&raw const (*self.raw.as_ptr()).data).cast::<u8>() }
}
pub unsafe fn data_mut_ptr(&self) -> *mut u8 {
unsafe { (&raw mut (*self.raw.as_ptr()).data).cast::<u8>() }
}
}
impl Sealed for Buffer {}
impl RawHandle for Buffer {
type Raw = RawBuffer;
fn as_ptr(&self) -> *mut Self::Raw {
self.raw.as_ptr()
}
}
impl AsRef<Buffer> for Buffer {
fn as_ref(&self) -> &Buffer {
self
}
}
impl BufferRuntime for Thread {
unsafe fn new_buffer_internal(&self, size: usize) -> VmErrorResult<Buffer> {
if size > MAX_BUFFER_SIZE {
return unsafe { self.too_big() };
}
unsafe {
let buffer = self.new_gco::<Buffer>(
Buffer::size_buffer(size),
self.as_ptr().as_ref().unwrap_unchecked().active_memcat,
)?;
GcObject::from(buffer).init_header(self, LUA_TBUFFER as u8);
buffer.as_ptr().as_mut().unwrap_unchecked().len = size as u32;
core::ptr::write_bytes(buffer.data_mut_ptr(), 0, size);
Ok(buffer)
}
}
unsafe fn free_buffer(&self, buffer: Buffer, page: LuaPage) {
unsafe {
let raw = buffer.as_ptr().as_ref().unwrap_unchecked();
self.free_gco(
buffer.into(),
Buffer::size_buffer(raw.len as usize),
raw.memcat,
page,
);
}
}
}