use std::ffi::c_void;
use crate::config::FFI_ConfigOptions;
use abi_stable::StableAbi;
use datafusion_common::config::ConfigOptions;
use datafusion_common::error::{DataFusionError, Result};
use datafusion_execution::config::SessionConfig;
#[repr(C)]
#[derive(Debug, StableAbi)]
pub struct FFI_SessionConfig {
pub config_options: FFI_ConfigOptions,
pub clone: unsafe extern "C" fn(plan: &Self) -> Self,
pub release: unsafe extern "C" fn(arg: &mut Self),
pub private_data: *mut c_void,
pub library_marker_id: extern "C" fn() -> usize,
}
unsafe impl Send for FFI_SessionConfig {}
unsafe impl Sync for FFI_SessionConfig {}
impl FFI_SessionConfig {
fn inner(&self) -> &SessionConfig {
let private_data = self.private_data as *mut SessionConfigPrivateData;
unsafe { &(*private_data).config }
}
}
unsafe extern "C" fn release_fn_wrapper(config: &mut FFI_SessionConfig) {
unsafe {
debug_assert!(!config.private_data.is_null());
let private_data =
Box::from_raw(config.private_data as *mut SessionConfigPrivateData);
drop(private_data);
config.private_data = std::ptr::null_mut();
}
}
unsafe extern "C" fn clone_fn_wrapper(config: &FFI_SessionConfig) -> FFI_SessionConfig {
unsafe {
let old_private_data = config.private_data as *mut SessionConfigPrivateData;
let old_config = (*old_private_data).config.clone();
let private_data = Box::new(SessionConfigPrivateData { config: old_config });
FFI_SessionConfig {
config_options: config.config_options.clone(),
private_data: Box::into_raw(private_data) as *mut c_void,
clone: clone_fn_wrapper,
release: release_fn_wrapper,
library_marker_id: crate::get_library_marker_id,
}
}
}
struct SessionConfigPrivateData {
pub config: SessionConfig,
}
impl From<&SessionConfig> for FFI_SessionConfig {
fn from(session: &SessionConfig) -> Self {
let private_data = Box::new(SessionConfigPrivateData {
config: session.clone(),
});
let config_options = FFI_ConfigOptions::from(session.options().as_ref());
Self {
config_options,
private_data: Box::into_raw(private_data) as *mut c_void,
clone: clone_fn_wrapper,
release: release_fn_wrapper,
library_marker_id: crate::get_library_marker_id,
}
}
}
impl Clone for FFI_SessionConfig {
fn clone(&self) -> Self {
unsafe { (self.clone)(self) }
}
}
impl Drop for FFI_SessionConfig {
fn drop(&mut self) {
unsafe { (self.release)(self) };
}
}
impl TryFrom<&FFI_SessionConfig> for SessionConfig {
type Error = DataFusionError;
fn try_from(config: &FFI_SessionConfig) -> Result<Self, Self::Error> {
if (config.library_marker_id)() == crate::get_library_marker_id() {
return Ok(config.inner().clone());
}
let config_options = ConfigOptions::try_from(config.config_options.clone())?;
Ok(SessionConfig::from(config_options))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_round_trip_ffi_session_config() -> Result<()> {
let session_config = SessionConfig::new();
let original_options = session_config.options().entries();
let mut ffi_config: FFI_SessionConfig = (&session_config).into();
let _ = ffi_config.clone();
ffi_config.library_marker_id = crate::mock_foreign_marker_id;
let foreign_config: SessionConfig = (&ffi_config).try_into()?;
let returned_options = foreign_config.options().entries();
assert_eq!(original_options.len(), returned_options.len());
Ok(())
}
}