use std::{
cell::RefCell,
ffi::c_void,
panic::{AssertUnwindSafe, catch_unwind},
process::abort,
};
use reifydb_abi::{data::column::ColumnsFFI, transform::vtable::TransformVTableFFI};
use tracing::error;
use crate::{ffi::arena::Arena, transform::FFITransform};
pub struct TransformWrapper<T: FFITransform> {
transform: T,
arena: RefCell<Arena>,
}
impl<T: FFITransform> TransformWrapper<T> {
pub fn new(transform: T) -> Self {
Self {
transform,
arena: RefCell::new(Arena::new()),
}
}
pub fn from_ptr(ptr: *mut c_void) -> &'static mut Self {
unsafe { &mut *(ptr as *mut Self) }
}
}
pub extern "C" fn ffi_transform<T: FFITransform>(
instance: *mut c_void,
input: *const ColumnsFFI,
output: *mut ColumnsFFI,
) -> i32 {
let result = catch_unwind(AssertUnwindSafe(|| {
let wrapper = TransformWrapper::<T>::from_ptr(instance);
let mut arena = wrapper.arena.borrow_mut();
arena.clear();
let input_columns = unsafe { arena.unmarshal_columns(&*input) };
let output_columns = match wrapper.transform.transform(input_columns) {
Ok(cols) => cols,
Err(e) => {
error!(?e, "Transform failed");
return -2;
}
};
unsafe {
*output = arena.marshal_columns(&output_columns);
}
0 }));
let code = result.unwrap_or_else(|e| {
error!(?e, "Panic in ffi_transform");
-99
});
if code < 0 {
error!(code, "ffi_transform failed - aborting");
abort();
}
code
}
pub extern "C" fn ffi_transform_destroy<T: FFITransform>(instance: *mut c_void) {
if instance.is_null() {
return;
}
let result = catch_unwind(AssertUnwindSafe(|| unsafe {
let _wrapper = Box::from_raw(instance as *mut TransformWrapper<T>);
}));
if let Err(e) = result {
error!(?e, "Panic in ffi_transform_destroy - aborting");
abort();
}
}
pub fn create_transform_vtable<T: FFITransform>() -> TransformVTableFFI {
TransformVTableFFI {
transform: ffi_transform::<T>,
destroy: ffi_transform_destroy::<T>,
}
}