miniextendr_api/
allocator.rs1use crate::sys::{R_PreserveObject_unchecked, R_ReleaseObject_unchecked};
41use crate::worker::{has_worker_context, is_r_main_thread, with_r_thread};
42use crate::{SEXP, SEXPTYPE, SexpExt};
43use core::{
44 alloc::{GlobalAlloc, Layout},
45 mem, ptr,
46};
47
48type SendableDataPtr = crate::worker::Sendable<*mut u8>;
62
63#[inline]
64const fn sendable_data_ptr_new(ptr: *mut u8) -> SendableDataPtr {
65 crate::worker::Sendable(ptr)
66}
67
68#[inline]
69const fn sendable_data_ptr_get(ptr: SendableDataPtr) -> *mut u8 {
70 ptr.0
71}
72
73#[inline]
74const fn sendable_data_ptr_is_null(ptr: SendableDataPtr) -> bool {
75 ptr.0.is_null()
76}
77
78#[inline]
79const fn sendable_data_ptr_null() -> SendableDataPtr {
80 crate::worker::Sendable(ptr::null_mut())
81}
82#[inline]
97fn with_r_thread_or_inline<R: Send + 'static, F: FnOnce() -> R + Send + 'static>(f: F) -> R {
98 if is_r_main_thread() {
99 f()
100 } else if has_worker_context() {
101 with_r_thread(f)
102 } else {
103 {
::core::panicking::panic_fmt(format_args!("RAllocator: cannot allocate from non-main thread without worker context. Ensure miniextendr_runtime_init() was called and you\'re within run_on_worker()."));
}panic!(
104 "RAllocator: cannot allocate from non-main thread without worker context. \
105 Ensure miniextendr_runtime_init() was called and you're within run_on_worker()."
106 )
107 }
108}
109#[repr(C)]
115#[derive(#[automatically_derived]
impl ::core::marker::Copy for Header { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Header { }
#[automatically_derived]
impl ::core::clone::Clone for Header {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<SEXP>;
*self
}
}Clone)]
116struct Header {
117 sexp: SEXP,
119}
120
121const HEADER_SIZE: usize = mem::size_of::<Header>();
122const HEADER_ALIGN: usize = mem::align_of::<Header>();
123#[derive(#[automatically_derived]
impl ::core::fmt::Debug for RAllocator {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "RAllocator")
}
}Debug)]
144pub struct RAllocator;
145
146unsafe impl GlobalAlloc for RAllocator {
147 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
148 sendable_data_ptr_get(with_r_thread_or_inline(move || unsafe {
149 alloc_main_thread(layout)
150 }))
151 }
152
153 unsafe fn dealloc(&self, data: *mut u8, _layout: Layout) {
154 if data.is_null() {
155 return;
156 }
157 let ptr = sendable_data_ptr_new(data);
158 with_r_thread_or_inline(move || unsafe {
159 dealloc_main_thread(ptr);
160 });
161 }
162
163 unsafe fn realloc(&self, old: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
164 if old.is_null() {
166 let Ok(new_layout) = Layout::from_size_align(new_size, layout.align()) else {
167 return ptr::null_mut();
168 };
169 return unsafe { self.alloc(new_layout) };
170 }
171
172 if new_size == 0 {
174 unsafe { self.dealloc(old, layout) };
175 return ptr::null_mut();
176 }
177
178 let old_ptr = sendable_data_ptr_new(old);
179 let old_size = layout.size();
180 let align = layout.align();
181
182 let new_ptr = with_r_thread_or_inline(move || unsafe {
183 realloc_main_thread(old_ptr, old_size, align, new_size)
184 });
185 sendable_data_ptr_get(new_ptr)
186 }
187
188 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
189 let p = unsafe { self.alloc(layout) };
190 if !p.is_null() {
191 unsafe { ptr::write_bytes(p, 0, layout.size()) };
192 }
193 p
194 }
195}
196unsafe fn alloc_main_thread(layout: Layout) -> SendableDataPtr {
206 if layout.size() == 0 {
209 return sendable_data_ptr_null();
210 }
211
212 let align = layout.align().max(HEADER_ALIGN);
213
214 let total = {
216 let Some(align_minus_1) = align.checked_sub(1) else {
217 return sendable_data_ptr_null();
218 };
219 let Some(temp) = HEADER_SIZE.checked_add(align_minus_1) else {
220 return sendable_data_ptr_null();
221 };
222 let Some(total) = temp.checked_add(layout.size()) else {
223 return sendable_data_ptr_null();
224 };
225 total
226 };
227
228 let total_isize: isize = match total.try_into() {
229 Ok(n) => n,
230 Err(_) => return sendable_data_ptr_null(),
231 };
232
233 let sexp = unsafe { crate::sys::Rf_allocVector_unchecked(SEXPTYPE::RAWSXP, total_isize) };
241 if sexp.is_null_ptr() {
242 return sendable_data_ptr_null();
243 }
244
245 unsafe { R_PreserveObject_unchecked(sexp) };
248
249 let raw_base = unsafe { crate::sys::RAW_unchecked(sexp) }.cast::<u8>();
251
252 let after_header = unsafe { raw_base.add(HEADER_SIZE) };
254 let pad = after_header.align_offset(align);
255 if pad == usize::MAX {
256 unsafe { R_ReleaseObject_unchecked(sexp) };
258 return sendable_data_ptr_null();
259 }
260
261 let data = unsafe { after_header.add(pad) };
262 let header = unsafe { data.sub(HEADER_SIZE) }.cast::<Header>();
263
264 unsafe { header.write(Header { sexp }) };
265
266 if true {
{
match (&data.align_offset(layout.align()), &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(data.align_offset(layout.align()), 0);
267 sendable_data_ptr_new(data)
268}
269
270unsafe fn dealloc_main_thread(ptr: SendableDataPtr) {
277 let data = sendable_data_ptr_get(ptr);
278 let header = unsafe { data.sub(HEADER_SIZE) }.cast::<Header>();
279 let sexp = unsafe { (*header).sexp };
280 unsafe { R_ReleaseObject_unchecked(sexp) };
281}
282
283unsafe fn realloc_main_thread(
290 old_ptr: SendableDataPtr,
291 old_size: usize,
292 align: usize,
293 new_size: usize,
294) -> SendableDataPtr {
295 let old = sendable_data_ptr_get(old_ptr);
296
297 let header = unsafe { old.sub(HEADER_SIZE) }.cast::<Header>();
300 let sexp = unsafe { (*header).sexp };
301
302 let raw_base = unsafe { crate::sys::RAW_unchecked(sexp) }.cast::<u8>();
304 let cap: usize = match unsafe { sexp.xlength_unchecked() }.try_into() {
305 Ok(n) => n,
306 Err(_) => return sendable_data_ptr_null(),
307 };
308
309 let used = unsafe { old.cast_const().offset_from(raw_base.cast_const()) };
310 let Ok(used_usize) = usize::try_from(used) else {
311 return sendable_data_ptr_null();
313 };
314 let available = cap.saturating_sub(used_usize);
315
316 if new_size <= available {
317 return old_ptr; }
319
320 let Ok(new_layout) = Layout::from_size_align(new_size, align) else {
322 return sendable_data_ptr_null();
323 };
324
325 let new_ptr = unsafe { alloc_main_thread(new_layout) };
326 if sendable_data_ptr_is_null(new_ptr) {
327 return sendable_data_ptr_null();
329 }
330
331 unsafe {
332 ptr::copy_nonoverlapping(old, sendable_data_ptr_get(new_ptr), old_size.min(new_size))
333 };
334 unsafe { R_ReleaseObject_unchecked(sexp) }; new_ptr
337}
338