use std::io; use std::path::Path; use std::ffi::c_void; use libloading::{Library, Symbol}; use static_assertions::assert_eq_size; use dd_utils::error::*; pub const CORE_API_VERSION: i32 = 1; pub struct ComputeCore { #[used] library: Library, // init: unsafe extern "C" fn(i32) -> i32, run_job: unsafe extern "C" fn(u64, *mut c_void, *mut u64, *mut *mut c_void) -> i32, free_outdata: unsafe extern "C" fn(u64, *mut c_void), } impl ComputeCore { pub fn load>(lib_path: P) -> io::Result { let lib_path = lib_path.as_ref(); let lib_path = lib_path.canonicalize().map_err(|e| { format!("Library '{}' does not exist", lib_path.display()).perror(e) })?; let library = libloading::Library::new(&lib_path).map_err(|e| { format!("Cannot load library at '{}'", lib_path.display()).perror(e) })?; macro_rules! load_symbol { ($name:expr, $symname:expr, $typ:ty) => {{ let sym: io::Result> = library.get($symname).map_err(|e| { format!("Failed to load symbol '{}' from library '{}'", $name, lib_path.display()).perror(e) }); sym.map(|s| std::mem::transmute::<*mut c_void, $typ>(s.into_raw().into_raw())) }} } let init = unsafe { load_symbol!("worker_init", b"worker_init\0", unsafe extern "C" fn(i32) -> i32)? }; let run_job = unsafe { load_symbol!("worker_run_job",b"worker_run_job\0", unsafe extern "C" fn(u64, *mut c_void, *mut u64, *mut *mut c_void) -> i32)? }; let free_outdata = unsafe { load_symbol!("worker_free_outdata", b"worker_free_outdata\0", unsafe extern "C" fn(u64, *mut c_void))? }; let ret = unsafe { init(CORE_API_VERSION) }; if ret != 0 { return Err(format!("Library refused initialisation at version {}: '{}'", CORE_API_VERSION, lib_path.display()).ioerr()); } Ok(ComputeCore { library, /*init,*/ run_job, free_outdata }) } /// Will block on this thread, so if you don't want to block, spawn a new thread. /// This does not syntactically need mutability, but since one can only run one job at a time /// on a core, semantic mutability seems warranted. pub fn run_job(&mut self, input_data: &mut [u8]) -> (i32, Vec) { let mut outsize = 0u64; let mut outdataptr: *mut c_void = std::ptr::null_mut(); assert_eq_size!(u64, usize); let ret = unsafe { (self.run_job)(input_data.len() as u64, input_data.as_mut_ptr() as *mut c_void, &mut outsize, &mut outdataptr) }; let mut result = (ret, Vec::new()); if outsize != 0 && outdataptr != std::ptr::null_mut() { result.1.resize(outsize as usize, 0u8); unsafe { std::ptr::copy(outdataptr as *const u8, result.1.as_mut_ptr(), outsize as usize); } } unsafe { (self.free_outdata)(outsize, outdataptr); } result } }