Skip to main content

reifydb_sdk/transform/
wrapper.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{
5	ffi::c_void,
6	panic::{AssertUnwindSafe, catch_unwind},
7	process::abort,
8};
9
10use reifydb_abi::{context::context::ContextFFI, data::column::ColumnsFFI, transform::vtable::TransformVTableFFI};
11use tracing::error;
12
13use crate::{
14	operator::change::BorrowedColumns,
15	transform::{FFITransform, context::FFITransformContext},
16};
17
18pub struct TransformWrapper<T: FFITransform> {
19	transform: T,
20}
21
22impl<T: FFITransform> TransformWrapper<T> {
23	pub fn new(transform: T) -> Self {
24		Self {
25			transform,
26		}
27	}
28
29	pub fn from_ptr(ptr: *mut c_void) -> &'static mut Self {
30		unsafe { &mut *(ptr as *mut Self) }
31	}
32}
33
34/// # Safety
35///
36/// - `instance` must be a valid pointer to a `TransformWrapper<T>`.
37/// - `ctx` must point to a valid `ContextFFI`.
38/// - `input` must point to a valid `ColumnsFFI`.
39pub unsafe extern "C" fn ffi_transform<T: FFITransform>(
40	instance: *mut c_void,
41	ctx: *mut ContextFFI,
42	input: *const ColumnsFFI,
43) -> i32 {
44	let result = catch_unwind(AssertUnwindSafe(|| {
45		let wrapper = TransformWrapper::<T>::from_ptr(instance);
46
47		// Zero-copy: borrow input columns directly from the FFI struct.
48		let borrowed_input = unsafe { BorrowedColumns::from_ffi(input) };
49		let mut tctx = FFITransformContext::new(ctx);
50
51		match wrapper.transform.transform(&mut tctx, borrowed_input) {
52			Ok(()) => 0,
53			Err(e) => {
54				error!(?e, "Transform failed");
55				-2
56			}
57		}
58	}));
59
60	let code = result.unwrap_or_else(|e| {
61		error!(?e, "Panic in ffi_transform");
62		-99
63	});
64	if code < 0 {
65		error!(code, "ffi_transform failed - aborting");
66		abort();
67	}
68	code
69}
70
71/// # Safety
72///
73/// - `instance` must be a valid pointer to a `TransformWrapper<T>`, or null.
74pub unsafe extern "C" fn ffi_transform_destroy<T: FFITransform>(instance: *mut c_void) {
75	if instance.is_null() {
76		return;
77	}
78
79	let result = catch_unwind(AssertUnwindSafe(|| unsafe {
80		let _wrapper = Box::from_raw(instance as *mut TransformWrapper<T>);
81	}));
82
83	if let Err(e) = result {
84		error!(?e, "Panic in ffi_transform_destroy - aborting");
85		abort();
86	}
87}
88
89pub fn create_transform_vtable<T: FFITransform>() -> TransformVTableFFI {
90	TransformVTableFFI {
91		transform: ffi_transform::<T>,
92		destroy: ffi_transform_destroy::<T>,
93	}
94}