use std::os::raw::c_void;
use libduckdb_sys::{
duckdb_function_get_init_data, duckdb_function_get_local_init_data, duckdb_function_info,
duckdb_init_info, duckdb_init_set_init_data,
};
pub struct FfiInitData<T: 'static> {
_marker: std::marker::PhantomData<T>,
}
impl<T: 'static> FfiInitData<T> {
pub unsafe fn set(info: duckdb_init_info, data: T) {
let raw = Box::into_raw(Box::new(data)).cast::<c_void>();
unsafe {
duckdb_init_set_init_data(info, raw, Some(Self::destroy));
}
}
pub unsafe fn get<'a>(info: duckdb_function_info) -> Option<&'a T> {
let raw = unsafe { duckdb_function_get_init_data(info) };
if raw.is_null() {
return None;
}
Some(unsafe { &*raw.cast::<T>() })
}
pub unsafe fn get_mut<'a>(info: duckdb_function_info) -> Option<&'a mut T> {
let raw = unsafe { duckdb_function_get_init_data(info) };
if raw.is_null() {
return None;
}
Some(unsafe { &mut *raw.cast::<T>() })
}
pub unsafe extern "C" fn destroy(ptr: *mut c_void) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr.cast::<T>())) };
}
}
}
pub struct FfiLocalInitData<T: 'static> {
_marker: std::marker::PhantomData<T>,
}
impl<T: 'static> FfiLocalInitData<T> {
pub unsafe fn set(info: duckdb_init_info, data: T) {
let raw = Box::into_raw(Box::new(data)).cast::<c_void>();
unsafe {
duckdb_init_set_init_data(info, raw, Some(Self::destroy));
}
}
pub unsafe fn get<'a>(info: duckdb_function_info) -> Option<&'a T> {
let raw = unsafe { duckdb_function_get_local_init_data(info) };
if raw.is_null() {
return None;
}
Some(unsafe { &*raw.cast::<T>() })
}
pub unsafe fn get_mut<'a>(info: duckdb_function_info) -> Option<&'a mut T> {
let raw = unsafe { duckdb_function_get_local_init_data(info) };
if raw.is_null() {
return None;
}
Some(unsafe { &mut *raw.cast::<T>() })
}
pub unsafe extern "C" fn destroy(ptr: *mut c_void) {
if !ptr.is_null() {
unsafe { drop(Box::from_raw(ptr.cast::<T>())) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
struct MyState {
counter: u64,
}
#[test]
fn destroy_null_is_noop() {
unsafe { FfiInitData::<MyState>::destroy(std::ptr::null_mut()) };
unsafe { FfiLocalInitData::<MyState>::destroy(std::ptr::null_mut()) };
}
#[test]
fn destroy_allocated_drops() {
let raw = Box::into_raw(Box::new(MyState { counter: 7 })).cast::<c_void>();
unsafe { FfiInitData::<MyState>::destroy(raw) };
let raw2 = Box::into_raw(Box::new(MyState { counter: 3 })).cast::<c_void>();
unsafe { FfiLocalInitData::<MyState>::destroy(raw2) };
}
}