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
use std::{ops::Deref, ptr, slice};
use netcorehost::{
pdcstr,
hostfxr::AssemblyDelegateLoader,
};
/// A sized array for passing heap-allocated memory across the FFI memory boundary to receive
/// buffers from .NET. Dereferences a `&[T]` otherwise.
///
/// # Safety
/// This type makes heavy use of `unsafe` operations for manual memory management. Take care
/// when using it as more than an opaque buffer. When it is dropped the memory is freed.
#[repr(C)]
pub struct RawVec<T> {
pub(crate) data: *mut T,
pub(crate) len: usize,
pub(crate) capacity: usize,
}
unsafe impl<T> Send for RawVec<T> {}
unsafe impl<T> Sync for RawVec<T> {}
impl<T> RawVec<T> {
/// Releases the owned memory and puts this struct into an unusable, empty
/// state. This method is called on `drop`, but it is safe to call repeatedly.
pub fn free(&mut self) {
if !self.data.is_null() {
let owned = unsafe { Box::from_raw(self.data) };
drop(owned);
self.capacity = 0;
self.len = 0;
self.data = ptr::null_mut();
}
}
}
/// Pretend to by a `&[T]`, a read-only view of the memory
impl<T> Deref for RawVec<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts_mut(self.data, self.len) }
}
}
impl<T> Drop for RawVec<T> {
fn drop(&mut self) {
self.free()
}
}
/// A .NET memory allocator that receives a memory address for a [`RawVec`] and
/// heap-allocates `size` that is "owned" by Rust's memory allocator and gives
/// it to .NET via the passed pointer.
pub(crate) extern "system" fn rust_allocate_memory(size: usize, vec: *mut RawVec<u8>) {
let buf = vec![0; size];
let capacity = buf.capacity();
let len = buf.len();
let data = buf.leak();
unsafe {
*vec = RawVec {
data: data.as_mut_ptr(),
len,
capacity,
}
};
}
/// Configure the `dotnet` runtime to allow it to allocate unmanaged memory from Rust for
/// specific purposes. Directly depends upon the bundled `dotnet` library.
pub fn configure_allocator(delegate_loader: &AssemblyDelegateLoader) {
let set_rust_allocate_memory = delegate_loader
.get_function_with_unmanaged_callers_only::<fn(extern "system" fn(usize, *mut RawVec<u8>))>(
pdcstr!("librawfilereader.Exports, librawfilereader"),
pdcstr!("SetRustAllocateMemory"),
)
.unwrap();
set_rust_allocate_memory(rust_allocate_memory);
}