1use core::ptr::NonNull;
2
3use crate::VmErrorResult;
4use crate::gc::GcObject;
5use crate::handle::{RawHandle, sealed::Sealed};
6use crate::layout::Align8Byte;
7use crate::memory::{LuaPage, MemoryRuntime};
8use crate::thread::Thread;
9use crate::types::LUA_TBUFFER;
10
11#[repr(C)]
12pub struct RawBuffer {
13 pub tt: u8,
14 pub marked: u8,
15 pub memcat: u8,
16 pub len: u32,
17 pub data: [Align8Byte; 1],
18}
19
20pub const MAX_BUFFER_SIZE: usize = 1 << 30;
21
22#[derive(Clone, Copy)]
23#[repr(transparent)]
24pub struct Buffer {
25 raw: NonNull<RawBuffer>,
26}
27
28#[allow(
36 clippy::missing_safety_doc,
37 reason = "all methods share the capability-level safety contract"
38)]
39pub trait BufferRuntime: Sealed {
40 unsafe fn new_buffer_internal(&self, size: usize) -> VmErrorResult<Buffer>;
42
43 unsafe fn free_buffer(&self, buffer: Buffer, page: LuaPage);
45}
46
47impl Buffer {
48 pub const unsafe fn from_raw(raw: NonNull<RawBuffer>) -> Self {
55 Self { raw }
56 }
57
58 pub const fn size_buffer(len: usize) -> usize {
59 core::mem::offset_of!(RawBuffer, data) + if len > 8 { len } else { 8 }
60 }
61
62 pub fn len(&self) -> usize {
63 unsafe { (*self.as_ptr()).len as usize }
64 }
65
66 pub fn is_empty(&self) -> bool {
67 self.len() == 0
68 }
69
70 pub const fn data_ptr(&self) -> *const u8 {
71 unsafe { (&raw const (*self.raw.as_ptr()).data).cast::<u8>() }
72 }
73
74 pub unsafe fn data_mut_ptr(&self) -> *mut u8 {
81 unsafe { (&raw mut (*self.raw.as_ptr()).data).cast::<u8>() }
82 }
83}
84
85impl Sealed for Buffer {}
86
87impl RawHandle for Buffer {
88 type Raw = RawBuffer;
89
90 fn as_ptr(&self) -> *mut Self::Raw {
91 self.raw.as_ptr()
92 }
93}
94
95impl AsRef<Buffer> for Buffer {
96 fn as_ref(&self) -> &Buffer {
97 self
98 }
99}
100
101impl BufferRuntime for Thread {
102 unsafe fn new_buffer_internal(&self, size: usize) -> VmErrorResult<Buffer> {
104 if size > MAX_BUFFER_SIZE {
105 return unsafe { self.too_big() };
106 }
107
108 unsafe {
109 let buffer = self.new_gco::<Buffer>(
110 Buffer::size_buffer(size),
111 self.as_ptr().as_ref().unwrap_unchecked().active_memcat,
112 )?;
113 GcObject::from(buffer).init_header(self, LUA_TBUFFER as u8);
114 buffer.as_ptr().as_mut().unwrap_unchecked().len = size as u32;
115 core::ptr::write_bytes(buffer.data_mut_ptr(), 0, size);
116
117 Ok(buffer)
118 }
119 }
120
121 unsafe fn free_buffer(&self, buffer: Buffer, page: LuaPage) {
123 unsafe {
124 let raw = buffer.as_ptr().as_ref().unwrap_unchecked();
125 self.free_gco(
126 buffer.into(),
127 Buffer::size_buffer(raw.len as usize),
128 raw.memcat,
129 page,
130 );
131 }
132 }
133}