rquickjs_core/allocator.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
//! Tools for using different allocators with QuickJS.
use crate::qjs;
mod rust;
pub use rust::RustAllocator;
/// The allocator interface
///
/// # Safety
/// Failure to implement this trait correctly will result in undefined behavior.
/// - `alloc` must return a either a null pointer or a pointer to an available region of memory
/// atleast `size` bytes and aligned to the size of `usize`.
/// - `realloc` must either return a null pointer or return a pointer to an available region of
/// memory atleast `new_size` bytes and aligned to the size of `usize`.
/// - `usable_size` must return the amount of available memory for any allocation allocated with
/// this allocator.
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "allocator")))]
pub unsafe trait Allocator {
/// Allocate new memory
///
///
fn alloc(&mut self, size: usize) -> *mut u8;
/// Allocates memory for an array of num objects of size and initializes all bytes in the allocated storage to zero.
///
///
fn calloc(&mut self, count: usize, size: usize) -> *mut u8;
/// De-allocate previously allocated memory
///
/// # Safety
/// Caller must ensure that the pointer that is being deallocated was allocated by the same
/// Allocator instance.
unsafe fn dealloc(&mut self, ptr: *mut u8);
/// Re-allocate previously allocated memory
///
/// # Safety
/// Caller must ensure that the pointer points to an allocation that was allocated by the same
/// Allocator instance.
unsafe fn realloc(&mut self, ptr: *mut u8, new_size: usize) -> *mut u8;
/// Get usable size of allocated memory region
///
/// # Safety
/// Caller must ensure that the pointer handed to this function points to an allocation
/// allocated by the same allocator instance.
unsafe fn usable_size(ptr: *mut u8) -> usize
where
Self: Sized;
}
type DynAllocator = Box<dyn Allocator>;
#[derive(Debug)]
pub(crate) struct AllocatorHolder(*mut DynAllocator);
impl Drop for AllocatorHolder {
fn drop(&mut self) {
let _ = unsafe { Box::from_raw(self.0) };
}
}
impl AllocatorHolder {
pub(crate) fn functions<A>() -> qjs::JSMallocFunctions
where
A: Allocator,
{
qjs::JSMallocFunctions {
js_calloc: Some(Self::calloc::<A>),
js_malloc: Some(Self::malloc::<A>),
js_free: Some(Self::free::<A>),
js_realloc: Some(Self::realloc::<A>),
js_malloc_usable_size: Some(Self::malloc_usable_size::<A>),
}
}
pub(crate) fn new<A>(allocator: A) -> Self
where
A: Allocator + 'static,
{
Self(Box::into_raw(Box::new(Box::new(allocator))))
}
pub(crate) fn opaque_ptr(&self) -> *mut DynAllocator {
self.0
}
unsafe extern "C" fn calloc<A>(
opaque: *mut qjs::c_void,
count: qjs::size_t,
size: qjs::size_t,
) -> *mut qjs::c_void
where
A: Allocator,
{
let allocator = &mut *(opaque as *mut DynAllocator);
let rust_size: usize = size.try_into().expect(qjs::SIZE_T_ERROR);
let rust_count: usize = count.try_into().expect(qjs::SIZE_T_ERROR);
allocator.calloc(rust_count, rust_size) as *mut qjs::c_void
}
unsafe extern "C" fn malloc<A>(opaque: *mut qjs::c_void, size: qjs::size_t) -> *mut qjs::c_void
where
A: Allocator,
{
let allocator = &mut *(opaque as *mut DynAllocator);
let rust_size: usize = size.try_into().expect(qjs::SIZE_T_ERROR);
allocator.alloc(rust_size) as *mut qjs::c_void
}
unsafe extern "C" fn free<A>(opaque: *mut qjs::c_void, ptr: *mut qjs::c_void)
where
A: Allocator,
{
// simulate the default behavior of libc::free
if ptr.is_null() {
// nothing to do
return;
}
let allocator = &mut *(opaque as *mut DynAllocator);
allocator.dealloc(ptr as _);
}
unsafe extern "C" fn realloc<A>(
opaque: *mut qjs::c_void,
ptr: *mut qjs::c_void,
size: qjs::size_t,
) -> *mut qjs::c_void
where
A: Allocator,
{
let rust_size: usize = size.try_into().expect(qjs::SIZE_T_ERROR);
let allocator = &mut *(opaque as *mut DynAllocator);
allocator.realloc(ptr as _, rust_size) as *mut qjs::c_void
}
unsafe extern "C" fn malloc_usable_size<A>(ptr: *const qjs::c_void) -> qjs::size_t
where
A: Allocator,
{
// simulate the default behavior of libc::malloc_usable_size
if ptr.is_null() {
return 0;
}
A::usable_size(ptr as _).try_into().unwrap()
}
}