use std::ffi::{c_void, OsStr, OsString};
use std::sync::Arc;
use libloading::Library;
use super::error::CudaFftError;
const DEFAULT_CUFFT_PATHS: &[&str] = &["libcufft.so.11", "libcufft.so.10", "libcufft.so"];
pub(crate) type CufftHandle = i32;
pub(crate) type CufftStatus = i32;
pub(crate) const CUFFT_SUCCESS: CufftStatus = 0;
#[cfg(test)]
pub(crate) const CUFFT_INVALID_PLAN: CufftStatus = 1;
#[cfg(test)]
pub(crate) const CUFFT_ALLOC_FAILED: CufftStatus = 2;
#[cfg(test)]
pub(crate) const CUFFT_INVALID_TYPE: CufftStatus = 3;
#[cfg(test)]
pub(crate) const CUFFT_INVALID_VALUE: CufftStatus = 4;
#[cfg(test)]
pub(crate) const CUFFT_INTERNAL_ERROR: CufftStatus = 5;
#[cfg(test)]
pub(crate) const CUFFT_EXEC_FAILED: CufftStatus = 6;
#[cfg(test)]
pub(crate) const CUFFT_SETUP_FAILED: CufftStatus = 7;
#[cfg(test)]
pub(crate) const CUFFT_INVALID_SIZE: CufftStatus = 8;
#[cfg(test)]
pub(crate) const CUFFT_UNALIGNED_DATA: CufftStatus = 9;
pub(crate) const CUFFT_FORWARD: i32 = -1;
pub(crate) const CUFFT_INVERSE: i32 = 1;
pub(crate) const CUFFT_R2C: i32 = 0x2a;
pub(crate) const CUFFT_C2R: i32 = 0x2c;
pub(crate) const CUFFT_C2C: i32 = 0x29;
pub(crate) const CUFFT_D2Z: i32 = 0x6a;
pub(crate) const CUFFT_Z2D: i32 = 0x6c;
pub(crate) const CUFFT_Z2Z: i32 = 0x69;
pub(crate) type CufftCreateFn = unsafe extern "C" fn(*mut CufftHandle) -> CufftStatus;
pub(crate) type CufftSetAutoAllocationFn = unsafe extern "C" fn(CufftHandle, i32) -> CufftStatus;
pub(crate) type CufftMakePlanMany64Fn = unsafe extern "C" fn(
CufftHandle,
i32,
*mut i64,
*mut i64,
i64,
i64,
*mut i64,
i64,
i64,
i32,
i64,
*mut usize,
) -> CufftStatus;
pub(crate) type CufftSetWorkAreaFn = unsafe extern "C" fn(CufftHandle, *mut c_void) -> CufftStatus;
pub(crate) type CufftSetStreamFn = unsafe extern "C" fn(CufftHandle, *mut c_void) -> CufftStatus;
pub(crate) type CufftExecC2cFn =
unsafe extern "C" fn(CufftHandle, *mut c_void, *mut c_void, i32) -> CufftStatus;
pub(crate) type CufftExecR2cFn =
unsafe extern "C" fn(CufftHandle, *mut c_void, *mut c_void) -> CufftStatus;
pub(crate) type CufftExecC2rFn =
unsafe extern "C" fn(CufftHandle, *mut c_void, *mut c_void) -> CufftStatus;
pub(crate) type CufftExecZ2zFn =
unsafe extern "C" fn(CufftHandle, *mut c_void, *mut c_void, i32) -> CufftStatus;
pub(crate) type CufftExecD2zFn =
unsafe extern "C" fn(CufftHandle, *mut c_void, *mut c_void) -> CufftStatus;
pub(crate) type CufftExecZ2dFn =
unsafe extern "C" fn(CufftHandle, *mut c_void, *mut c_void) -> CufftStatus;
pub(crate) type CufftDestroyFn = unsafe extern "C" fn(CufftHandle) -> CufftStatus;
#[derive(Clone, Copy)]
pub(crate) struct CufftApi {
pub(crate) create: CufftCreateFn,
pub(crate) set_auto_allocation: CufftSetAutoAllocationFn,
pub(crate) make_plan_many_64: CufftMakePlanMany64Fn,
pub(crate) set_work_area: CufftSetWorkAreaFn,
pub(crate) set_stream: CufftSetStreamFn,
pub(crate) exec_c2c: CufftExecC2cFn,
pub(crate) exec_r2c: CufftExecR2cFn,
pub(crate) exec_c2r: CufftExecC2rFn,
pub(crate) exec_z2z: CufftExecZ2zFn,
pub(crate) exec_d2z: CufftExecD2zFn,
pub(crate) exec_z2d: CufftExecZ2dFn,
pub(crate) destroy: CufftDestroyFn,
}
impl CufftApi {
fn load(library: &Library) -> Result<Self, CudaFftError> {
Ok(Self {
create: load_symbol(library, b"cufftCreate\0")?,
set_auto_allocation: load_symbol(library, b"cufftSetAutoAllocation\0")?,
make_plan_many_64: load_symbol(library, b"cufftMakePlanMany64\0")?,
set_work_area: load_symbol(library, b"cufftSetWorkArea\0")?,
set_stream: load_symbol(library, b"cufftSetStream\0")?,
exec_c2c: load_symbol(library, b"cufftExecC2C\0")?,
exec_r2c: load_symbol(library, b"cufftExecR2C\0")?,
exec_c2r: load_symbol(library, b"cufftExecC2R\0")?,
exec_z2z: load_symbol(library, b"cufftExecZ2Z\0")?,
exec_d2z: load_symbol(library, b"cufftExecD2Z\0")?,
exec_z2d: load_symbol(library, b"cufftExecZ2D\0")?,
destroy: load_symbol(library, b"cufftDestroy\0")?,
})
}
}
fn load_symbol<T: Copy>(library: &Library, name: &[u8]) -> Result<T, CudaFftError> {
let symbol_name = String::from_utf8_lossy(name)
.trim_end_matches('\0')
.to_owned();
let symbol = unsafe { library.get::<T>(name) }.map_err(|source| CudaFftError::SymbolLoad {
name: symbol_name,
source,
})?;
Ok(*symbol)
}
pub(crate) struct CufftLibrary {
_library: Option<Library>,
pub(crate) api: CufftApi,
}
unsafe impl Send for CufftLibrary {}
unsafe impl Sync for CufftLibrary {}
impl CufftLibrary {
pub(crate) fn load() -> Result<Arc<Self>, CudaFftError> {
Self::load_from_paths(cufft_library_candidates(
std::env::var_os("TENFERRO_CUFFT_PATH").as_deref(),
))
}
#[cfg(test)]
pub(crate) fn load_from_paths_for_tests(
paths: Vec<OsString>,
) -> Result<Arc<Self>, CudaFftError> {
Self::load_from_paths(paths)
}
fn load_from_paths(paths: Vec<OsString>) -> Result<Arc<Self>, CudaFftError> {
let mut attempts = Vec::new();
let mut last_source = None;
for path in &paths {
let library = match unsafe { Library::new(path) } {
Ok(library) => library,
Err(source) => {
attempts.push(format!("{}: {source}", path.to_string_lossy()));
last_source = Some(source);
continue;
}
};
let api = CufftApi::load(&library)?;
return Ok(Arc::new(Self {
_library: Some(library),
api,
}));
}
let paths_text = paths
.iter()
.map(|path| path.to_string_lossy())
.collect::<Vec<_>>()
.join(", ");
let attempts_text = attempts.join("; ");
let Some(source) = last_source else {
return Err(CudaFftError::NoLibraryCandidates);
};
Err(CudaFftError::LibraryLoad {
paths: paths_text,
attempts: attempts_text,
source,
})
}
#[cfg(test)]
pub(crate) fn from_api_for_tests(api: CufftApi) -> Arc<Self> {
Arc::new(Self {
_library: None,
api,
})
}
}
pub(crate) fn cufft_library_candidates(override_path: Option<&OsStr>) -> Vec<OsString> {
let mut candidates = override_path
.into_iter()
.flat_map(std::env::split_paths)
.filter(|path| !path.as_os_str().is_empty())
.map(|path| path.into_os_string())
.collect::<Vec<_>>();
candidates.extend(DEFAULT_CUFFT_PATHS.iter().map(OsString::from));
candidates
}
pub(crate) fn map_cufft_status(
function: &'static str,
status: CufftStatus,
) -> Result<(), CudaFftError> {
if status == CUFFT_SUCCESS {
Ok(())
} else {
Err(CudaFftError::CufftStatus { function, status })
}
}