pub mod dummy;
pub mod external;
pub mod linalg;
use byte_slice_cast::*;
use get_size::GetSize;
use serde_derive::{Deserialize, Serialize};
use std::io::Read;
use std::mem::MaybeUninit;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::pin::Pin;
use std::sync::Arc;
use zip::read::ZipFile;
use crate::layout::{Layout, Struct};
use crate::Error;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct RawResourceMethod(
pub unsafe extern "C" fn(*const (), *const u8, u64, *mut u8, u64) -> *mut u8,
);
impl GetSize for RawResourceMethod {}
#[derive(Debug, Clone, PartialEq, GetSize)]
pub struct ResourceMethod {
pub(crate) fn_ptr: RawResourceMethod,
pub(crate) input_layout: Struct,
pub(crate) output_layout: Layout,
}
#[typetag::serde(tag = "type")]
pub trait ResourceType: std::fmt::Debug + Send + Sync + UnwindSafe + RefUnwindSafe {
#[allow(clippy::wrong_self_convention)]
fn from_bytes(&self, bytes: &[u8]) -> Result<Pin<Box<dyn Resource>>, Error>;
fn read(&self, mut f: ZipFile<'_>) -> Result<Pin<Box<dyn Resource>>, Error> {
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
self.from_bytes(&buffer)
}
}
pub trait Resource: 'static + std::fmt::Debug + Send + Sync + UnwindSafe + RefUnwindSafe {
fn r#type(&self) -> Arc<dyn ResourceType>;
fn dump(&self) -> Result<Vec<u8>, Error>;
fn size(&self) -> usize;
fn get_method(&self, method: &str) -> Option<ResourceMethod>;
fn get_raw_ptr(&self) -> *const () {
self as *const Self as *const ()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ResourceContainer {
resource_type: Arc<dyn ResourceType>,
#[serde(skip_serializing)]
#[serde(skip_deserializing)]
#[serde(default)]
resource: Option<Pin<Box<dyn Resource>>>,
}
impl GetSize for ResourceContainer {
fn get_heap_size(&self) -> usize {
if let Some(resource) = &self.resource {
resource.size()
} else {
0
}
}
}
impl ResourceContainer {
pub fn new<R: Resource>(resource: R) -> ResourceContainer {
ResourceContainer {
resource_type: resource.r#type(),
resource: Some(Box::pin(resource)),
}
}
pub fn new_boxed(resource: Pin<Box<dyn Resource>>) -> ResourceContainer {
ResourceContainer {
resource_type: resource.r#type(),
resource: Some(resource),
}
}
pub(crate) fn read(&self, f: ZipFile<'_>) -> Result<Self, Error> {
let resource = self.resource_type.read(f)?;
Ok(ResourceContainer {
resource_type: self.resource_type.clone(),
resource: Some(resource),
})
}
pub(crate) fn dump(&self) -> Result<Vec<u8>, Error> {
self.resource
.as_ref()
.expect("resource not initialized")
.dump()
}
pub fn is_initialized(&self) -> bool {
self.resource.is_some()
}
pub fn get_raw_ptr(&self) -> *const () {
self.resource
.as_ref()
.expect("resource not initialized")
.get_raw_ptr()
}
pub fn resource(&self) -> Pin<&dyn Resource> {
self.resource
.as_ref()
.expect("resource not initialized")
.as_ref()
}
pub fn get_method(&self, method: &str) -> Option<ResourceMethod> {
self.resource
.as_ref()
.expect("resource not initialized")
.get_method(method)
}
}
#[repr(transparent)]
pub struct Input<'a>(&'a [u64]);
impl<'a> Input<'a> {
pub unsafe fn new(input: *const u8, n_slots: usize) -> Self {
Self(std::slice::from_raw_parts(input as *const u64, n_slots))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get_f64(&self, idx: usize) -> f64 {
f64::from_ne_bytes(self.0[idx].to_ne_bytes())
}
pub fn get_u64(&self, idx: usize) -> u64 {
self.0[idx]
}
pub fn get_bool(&self, idx: usize) -> bool {
self.0[idx] == 1
}
pub fn as_f64_slice(&self) -> &[f64] {
self.0
.as_byte_slice()
.as_slice_of()
.expect("f64 and u64 have the same size")
}
pub fn as_u64_slice(&self) -> &[u64] {
self.0
}
}
pub struct OutputBuilder<'a> {
position: usize,
slice: &'a mut [MaybeUninit<u64>],
}
impl<'a> Drop for OutputBuilder<'a> {
fn drop(&mut self) {
while self.position < self.slice.len() {
self.push_u64(0)
}
}
}
impl<'a> OutputBuilder<'a> {
pub unsafe fn new(output: *mut u8, n_slots: usize) -> Self {
Self {
position: 0,
slice: std::slice::from_raw_parts_mut(output as *mut MaybeUninit<u64>, n_slots),
}
}
pub fn push_f64(&mut self, val: f64) {
self.slice[self.position].write(u64::from_ne_bytes(val.to_ne_bytes()));
self.position += 1;
}
pub fn push_u64(&mut self, val: u64) {
self.slice[self.position].write(val);
self.position += 1;
}
pub fn push_bool(&mut self, val: bool) {
self.slice[self.position].write(val as u64);
self.position += 1;
}
pub fn copy_from_f64(&mut self, src: &[f64]) {
for &val in src {
self.push_f64(val);
}
}
pub fn copy_from_u64(&mut self, src: &[u64]) {
for &val in src {
self.push_u64(val);
}
}
pub fn copy_from_bool(&mut self, src: &[bool]) {
for &val in src {
self.push_bool(val);
}
}
}
#[macro_export]
macro_rules! safe_method {
($safe_interface:ident) => {{
pub unsafe extern "C" fn safe_interface(
resource_ptr: *const (),
input_ptr: *const u8,
input_slots: u64,
output_ptr: *mut u8,
output_slots: u64,
) -> *mut u8 {
match std::panic::catch_unwind(|| {
unsafe {
let resource = &*(resource_ptr as *const _);
$safe_interface(
resource,
$crate::resource::Input::new(input_ptr, input_slots as usize),
$crate::resource::OutputBuilder::new(output_ptr, output_slots as usize),
)
}
}) {
Ok(Ok(())) => std::ptr::null_mut(),
Ok(Err(err)) => $crate::utils::make_safe_c_str(err).into_raw() as *mut u8,
Err(_) => $crate::utils::make_safe_c_str("method panicked. See stderr".to_string())
.into_raw() as *mut u8,
}
}
$crate::resource::RawResourceMethod(safe_interface)
}};
}