1use std::alloc::{self, Layout};
2use std::os::raw::c_void;
3use std::ptr;
4
5pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
6
7#[repr(C)]
8#[derive(Default)]
9pub(crate) struct MemoryState {
10 used_memory: isize,
11 memory_limit: isize,
12 ignore_limit: bool,
15 #[cfg(feature = "luau")]
17 limit_reached: bool,
18}
19
20impl MemoryState {
21 #[cfg(feature = "luau")]
22 #[inline]
23 pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
24 let mut mem_state = ptr::null_mut();
25 ffi::lua_getallocf(state, &mut mem_state);
26 mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
27 mem_state as *mut MemoryState
28 }
29
30 #[cfg(not(feature = "luau"))]
31 #[rustversion::since(1.85)]
32 #[inline]
33 #[allow(clippy::incompatible_msrv)]
34 pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
35 let mut mem_state = ptr::null_mut();
36 if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
37 mem_state = ptr::null_mut();
38 }
39 mem_state as *mut MemoryState
40 }
41
42 #[cfg(not(feature = "luau"))]
43 #[rustversion::before(1.85)]
44 #[inline]
45 pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
46 let mut mem_state = ptr::null_mut();
47 if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
48 mem_state = ptr::null_mut();
49 }
50 mem_state as *mut MemoryState
51 }
52
53 #[inline]
54 pub(crate) fn used_memory(&self) -> usize {
55 self.used_memory as usize
56 }
57
58 #[inline]
59 pub(crate) fn memory_limit(&self) -> usize {
60 self.memory_limit as usize
61 }
62
63 #[inline]
64 pub(crate) fn set_memory_limit(&mut self, limit: usize) -> usize {
65 let prev_limit = self.memory_limit;
66 self.memory_limit = limit as isize;
67 prev_limit as usize
68 }
69
70 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
73 #[inline]
74 pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
75 let mem_state = Self::get(state);
76 if !mem_state.is_null() {
77 (*mem_state).ignore_limit = true;
78 f();
79 (*mem_state).ignore_limit = false;
80 } else {
81 f();
82 }
83 }
84
85 #[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
87 #[inline]
88 pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
89 f();
90 }
91
92 #[cfg(feature = "luau")]
94 #[inline]
95 pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
96 (*Self::get(state)).limit_reached
97 }
98}
99
100unsafe extern "C-unwind" fn allocator(
101 extra: *mut c_void,
102 ptr: *mut c_void,
103 osize: usize,
104 nsize: usize,
105) -> *mut c_void {
106 let mem_state = &mut *(extra as *mut MemoryState);
107 #[cfg(feature = "luau")]
108 {
109 mem_state.limit_reached = false;
111 }
112
113 if nsize == 0 {
114 if !ptr.is_null() {
116 let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
117 alloc::dealloc(ptr as *mut u8, layout);
118 mem_state.used_memory -= osize as isize;
119 }
120 return ptr::null_mut();
121 }
122
123 if nsize > isize::MAX as usize {
125 return ptr::null_mut();
126 }
127
128 let mut mem_diff = nsize as isize;
130 if !ptr.is_null() {
131 mem_diff -= osize as isize;
132 }
133 let mem_limit = mem_state.memory_limit;
134 let new_used_memory = mem_state.used_memory + mem_diff;
135 if mem_limit > 0 && new_used_memory > mem_limit && !mem_state.ignore_limit {
136 #[cfg(feature = "luau")]
137 {
138 mem_state.limit_reached = true;
139 }
140 return ptr::null_mut();
141 }
142 mem_state.used_memory += mem_diff;
143
144 if ptr.is_null() {
145 let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
147 Ok(layout) => layout,
148 Err(_) => return ptr::null_mut(),
149 };
150 let new_ptr = alloc::alloc(new_layout) as *mut c_void;
151 if new_ptr.is_null() {
152 alloc::handle_alloc_error(new_layout);
153 }
154 return new_ptr;
155 }
156
157 let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
159 let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
160 if new_ptr.is_null() {
161 alloc::handle_alloc_error(old_layout);
162 }
163 new_ptr
164}