use std::{io, ptr::NonNull, slice};
mod os;
pub struct JitMemory {
ptr: NonNull<u8>,
len: usize,
capacity: usize,
}
impl JitMemory {
pub fn new(capacity: usize) -> io::Result<Self> {
let page_size = os::get_page_size();
let rounded_capacity = (capacity + page_size - 1) & !(page_size - 1);
let ptr = os::alloc_rw(rounded_capacity)?;
Ok(Self {
ptr,
len: 0,
capacity: rounded_capacity,
})
}
pub fn write(&mut self, code: &[u8]) -> io::Result<()> {
if self.len + code.len() > self.capacity {
return Err(io::Error::new(
io::ErrorKind::OutOfMemory,
"Not enough capacity in JIT buffer",
));
}
unsafe {
os::write_protect_scoped(self.ptr.as_ptr(), self.capacity, || {
std::ptr::copy_nonoverlapping(
code.as_ptr(),
self.ptr.as_ptr().add(self.len),
code.len(),
);
})?;
}
self.len += code.len();
Ok(())
}
pub fn make_executable(&self) -> io::Result<*const u8> {
unsafe {
os::make_executable(self.ptr.as_ptr(), self.capacity)?;
}
Ok(self.ptr.as_ptr())
}
pub fn make_writable(&self) -> io::Result<()> {
unsafe {
os::make_writable(self.ptr.as_ptr(), self.capacity)?;
}
Ok(())
}
pub fn len(&self) -> usize {
self.len
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
pub unsafe fn as_fn<S>(&self) -> S {
unsafe { std::mem::transmute_copy(&self.ptr.as_ptr()) }
}
}
impl Drop for JitMemory {
fn drop(&mut self) {
unsafe {
os::dealloc(self.ptr.as_ptr(), self.capacity);
}
}
}
unsafe impl Send for JitMemory {}
unsafe impl Sync for JitMemory {}