Skip to main content

baracuda_cufft_sys/
lib.rs

1//! Raw FFI + dynamic loader for NVIDIA cuFFT.
2//!
3//! `baracuda-cufft` wraps this with a safe, typed API. Use this crate
4//! directly only if you need a function that the safe layer hasn't
5//! wrapped yet (in which case please file a bug).
6
7#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
8#![warn(missing_debug_implementations)]
9
10use core::ffi::c_int;
11use std::sync::OnceLock;
12
13use baracuda_core::{platform, Library, LoaderError};
14use baracuda_cuda_sys::runtime::cudaStream_t;
15use baracuda_types::CudaStatus;
16
17/// A cuFFT plan handle. Historically typedef'd as `int`.
18pub type cufftHandle = c_int;
19
20/// cuFFT transform direction.
21pub const CUFFT_FORWARD: c_int = -1;
22/// Inverse-transform direction flag for `cufftExec*` calls.
23pub const CUFFT_INVERSE: c_int = 1;
24
25/// cuFFT transform type.
26#[repr(i32)]
27#[derive(Copy, Clone, Debug, Eq, PartialEq)]
28pub enum cufftType {
29    /// real-to-complex (single-precision) transform.
30    R2C = 0x2A,
31    /// complex-to-real (single-precision) transform.
32    C2R = 0x2C,
33    /// complex-to-complex (single-precision) transform.
34    C2C = 0x29,
35    /// real-to-complex (double-precision) transform.
36    D2Z = 0x6A,
37    /// complex-to-real (double-precision) transform.
38    Z2D = 0x6C,
39    /// complex-to-complex (double-precision) transform.
40    Z2Z = 0x69,
41}
42
43/// cuFFT complex (single precision) — layout-compatible with
44/// `baracuda_types::Complex32`.
45#[repr(C)]
46#[derive(Copy, Clone, Debug, Default, PartialEq)]
47pub struct cufftComplex {
48    /// `x` component.
49    pub x: f32,
50    /// `y` component.
51    pub y: f32,
52}
53
54/// cuFFT complex (double precision) — layout-compatible with `baracuda_types::Complex64`.
55#[repr(C)]
56#[derive(Copy, Clone, Debug, Default, PartialEq)]
57pub struct cufftDoubleComplex {
58    /// `x` component.
59    pub x: f64,
60    /// `y` component.
61    pub y: f64,
62}
63
64/// Return code from a cuFFT call.
65#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
66#[repr(transparent)]
67pub struct cufftResult(pub i32);
68
69impl cufftResult {
70    /// `CUFFT_SUCCESS` — operation succeeded.
71    pub const SUCCESS: Self = Self(0);
72    /// `CUFFT_INVALID_PLAN` — the plan handle is invalid.
73    pub const INVALID_PLAN: Self = Self(1);
74    /// `CUFFT_ALLOC_FAILED` — an allocation failed.
75    pub const ALLOC_FAILED: Self = Self(2);
76    /// `CUFFT_INVALID_TYPE` — the requested transform type is invalid.
77    pub const INVALID_TYPE: Self = Self(3);
78    /// `CUFFT_INVALID_VALUE` — an argument has an invalid value.
79    pub const INVALID_VALUE: Self = Self(4);
80    /// `CUFFT_INTERNAL_ERROR` — an internal cuFFT error occurred.
81    pub const INTERNAL_ERROR: Self = Self(5);
82    /// `CUFFT_EXEC_FAILED` — transform execution failed.
83    pub const EXEC_FAILED: Self = Self(6);
84    /// `CUFFT_SETUP_FAILED` — library setup failed.
85    pub const SETUP_FAILED: Self = Self(7);
86    /// `CUFFT_INVALID_SIZE` — the requested transform size is invalid.
87    pub const INVALID_SIZE: Self = Self(8);
88    /// `CUFFT_UNALIGNED_DATA` — input/output data is misaligned.
89    pub const UNALIGNED_DATA: Self = Self(9);
90    /// `CUFFT_INVALID_DEVICE` — the active CUDA device is invalid for cuFFT.
91    pub const INVALID_DEVICE: Self = Self(11);
92    /// `CUFFT_NOT_IMPLEMENTED` — the requested feature is not implemented.
93    pub const NOT_IMPLEMENTED: Self = Self(14);
94    /// `CUFFT_NOT_SUPPORTED` — the requested feature is not supported on this device.
95    pub const NOT_SUPPORTED: Self = Self(16);
96
97    /// Return `true` if the status code denotes success.
98    pub const fn is_success(self) -> bool {
99        self.0 == 0
100    }
101}
102
103impl CudaStatus for cufftResult {
104    fn code(self) -> i32 {
105        self.0
106    }
107    fn name(self) -> &'static str {
108        match self.0 {
109            0 => "CUFFT_SUCCESS",
110            1 => "CUFFT_INVALID_PLAN",
111            2 => "CUFFT_ALLOC_FAILED",
112            3 => "CUFFT_INVALID_TYPE",
113            4 => "CUFFT_INVALID_VALUE",
114            5 => "CUFFT_INTERNAL_ERROR",
115            6 => "CUFFT_EXEC_FAILED",
116            7 => "CUFFT_SETUP_FAILED",
117            8 => "CUFFT_INVALID_SIZE",
118            9 => "CUFFT_UNALIGNED_DATA",
119            11 => "CUFFT_INVALID_DEVICE",
120            14 => "CUFFT_NOT_IMPLEMENTED",
121            16 => "CUFFT_NOT_SUPPORTED",
122            _ => "CUFFT_ERROR_UNRECOGNIZED",
123        }
124    }
125    fn description(self) -> &'static str {
126        match self.0 {
127            0 => "success",
128            1 => "invalid plan handle",
129            2 => "allocation failed",
130            4 => "invalid argument",
131            6 => "transform execution failed",
132            8 => "invalid transform size",
133            9 => "misaligned data",
134            14 => "feature not implemented in this version",
135            16 => "feature not supported on the target device",
136            _ => "unrecognized cuFFT status code",
137        }
138    }
139    fn is_success(self) -> bool {
140        cufftResult::is_success(self)
141    }
142    fn library(self) -> &'static str {
143        "cufft"
144    }
145}
146
147// ---- function-pointer types ----
148
149/// Function-pointer type for `cufftCreate` (create cuFFT plan handle). See <https://docs.nvidia.com/cuda/cufft/index.html>.
150pub type PFN_cufftCreate = unsafe extern "C" fn(plan: *mut cufftHandle) -> cufftResult;
151/// Function-pointer type for `cufftDestroy` (destroy cuFFT plan handle). See <https://docs.nvidia.com/cuda/cufft/index.html>.
152pub type PFN_cufftDestroy = unsafe extern "C" fn(plan: cufftHandle) -> cufftResult;
153/// Function-pointer type for `cufftPlan1d` (create 1D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
154pub type PFN_cufftPlan1d = unsafe extern "C" fn(
155    plan: *mut cufftHandle,
156    nx: c_int,
157    ty: cufftType,
158    batch: c_int,
159) -> cufftResult;
160/// Function-pointer type for `cufftPlan2d` (create 2D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
161pub type PFN_cufftPlan2d = unsafe extern "C" fn(
162    plan: *mut cufftHandle,
163    nx: c_int,
164    ny: c_int,
165    ty: cufftType,
166) -> cufftResult;
167/// Function-pointer type for `cufftPlan3d` (create 3D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
168pub type PFN_cufftPlan3d = unsafe extern "C" fn(
169    plan: *mut cufftHandle,
170    nx: c_int,
171    ny: c_int,
172    nz: c_int,
173    ty: cufftType,
174) -> cufftResult;
175/// Function-pointer type for `cufftSetStream` (bind a CUDA stream to a cuFFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
176pub type PFN_cufftSetStream =
177    unsafe extern "C" fn(plan: cufftHandle, stream: cudaStream_t) -> cufftResult;
178/// Function-pointer type for `cufftGetVersion` (query cuFFT library version). See <https://docs.nvidia.com/cuda/cufft/index.html>.
179pub type PFN_cufftGetVersion = unsafe extern "C" fn(version: *mut c_int) -> cufftResult;
180
181/// Function-pointer type for `cufftExecR2C` (execute real-to-complex (single-precision) FFT). See <https://docs.nvidia.com/cuda/cufft/index.html>.
182pub type PFN_cufftExecR2C = unsafe extern "C" fn(
183    plan: cufftHandle,
184    input: *mut f32,
185    output: *mut cufftComplex,
186) -> cufftResult;
187/// Function-pointer type for `cufftExecC2R` (execute complex-to-real (single-precision) FFT). See <https://docs.nvidia.com/cuda/cufft/index.html>.
188pub type PFN_cufftExecC2R = unsafe extern "C" fn(
189    plan: cufftHandle,
190    input: *mut cufftComplex,
191    output: *mut f32,
192) -> cufftResult;
193/// Function-pointer type for `cufftExecC2C` (execute complex-to-complex (single-precision) FFT). See <https://docs.nvidia.com/cuda/cufft/index.html>.
194pub type PFN_cufftExecC2C = unsafe extern "C" fn(
195    plan: cufftHandle,
196    input: *mut cufftComplex,
197    output: *mut cufftComplex,
198    direction: c_int,
199) -> cufftResult;
200
201// ---- Double-precision exec paths ----
202
203/// Function-pointer type for `cufftExecD2Z` (execute real-to-complex (double-precision) FFT). See <https://docs.nvidia.com/cuda/cufft/index.html>.
204pub type PFN_cufftExecD2Z = unsafe extern "C" fn(
205    plan: cufftHandle,
206    input: *mut f64,
207    output: *mut cufftDoubleComplex,
208) -> cufftResult;
209
210/// Function-pointer type for `cufftExecZ2D` (execute complex-to-real (double-precision) FFT). See <https://docs.nvidia.com/cuda/cufft/index.html>.
211pub type PFN_cufftExecZ2D = unsafe extern "C" fn(
212    plan: cufftHandle,
213    input: *mut cufftDoubleComplex,
214    output: *mut f64,
215) -> cufftResult;
216
217/// Function-pointer type for `cufftExecZ2Z` (execute complex-to-complex (double-precision) FFT). See <https://docs.nvidia.com/cuda/cufft/index.html>.
218pub type PFN_cufftExecZ2Z = unsafe extern "C" fn(
219    plan: cufftHandle,
220    input: *mut cufftDoubleComplex,
221    output: *mut cufftDoubleComplex,
222    direction: c_int,
223) -> cufftResult;
224
225// ---- Batched / many plans ----
226
227/// Function-pointer type for `cufftPlanMany` (create batched / strided FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
228pub type PFN_cufftPlanMany = unsafe extern "C" fn(
229    plan: *mut cufftHandle,
230    rank: c_int,
231    n: *mut c_int,
232    inembed: *mut c_int,
233    istride: c_int,
234    idist: c_int,
235    onembed: *mut c_int,
236    ostride: c_int,
237    odist: c_int,
238    ty: cufftType,
239    batch: c_int,
240) -> cufftResult;
241
242/// Function-pointer type for `cufftMakePlan1d` (configure an existing handle as a 1D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
243pub type PFN_cufftMakePlan1d = unsafe extern "C" fn(
244    plan: cufftHandle,
245    nx: c_int,
246    ty: cufftType,
247    batch: c_int,
248    work_size: *mut usize,
249) -> cufftResult;
250
251/// Function-pointer type for `cufftMakePlan2d` (configure an existing handle as a 2D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
252pub type PFN_cufftMakePlan2d = unsafe extern "C" fn(
253    plan: cufftHandle,
254    nx: c_int,
255    ny: c_int,
256    ty: cufftType,
257    work_size: *mut usize,
258) -> cufftResult;
259
260/// Function-pointer type for `cufftMakePlan3d` (configure an existing handle as a 3D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
261pub type PFN_cufftMakePlan3d = unsafe extern "C" fn(
262    plan: cufftHandle,
263    nx: c_int,
264    ny: c_int,
265    nz: c_int,
266    ty: cufftType,
267    work_size: *mut usize,
268) -> cufftResult;
269
270/// Function-pointer type for `cufftMakePlanMany` (configure an existing handle as a batched/strided FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
271pub type PFN_cufftMakePlanMany = unsafe extern "C" fn(
272    plan: cufftHandle,
273    rank: c_int,
274    n: *mut c_int,
275    inembed: *mut c_int,
276    istride: c_int,
277    idist: c_int,
278    onembed: *mut c_int,
279    ostride: c_int,
280    odist: c_int,
281    ty: cufftType,
282    batch: c_int,
283    work_size: *mut usize,
284) -> cufftResult;
285
286/// Function-pointer type for `cufftMakePlanMany64` (configure an existing handle as a 64-bit batched/strided FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
287pub type PFN_cufftMakePlanMany64 = unsafe extern "C" fn(
288    plan: cufftHandle,
289    rank: c_int,
290    n: *mut i64,
291    inembed: *mut i64,
292    istride: i64,
293    idist: i64,
294    onembed: *mut i64,
295    ostride: i64,
296    odist: i64,
297    ty: cufftType,
298    batch: i64,
299    work_size: *mut usize,
300) -> cufftResult;
301
302/// Function-pointer type for `cufftEstimate1d` (estimate workspace size for a 1D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
303pub type PFN_cufftEstimate1d = unsafe extern "C" fn(
304    nx: c_int,
305    ty: cufftType,
306    batch: c_int,
307    work_size: *mut usize,
308) -> cufftResult;
309
310/// Function-pointer type for `cufftEstimate2d` (estimate workspace size for a 2D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
311pub type PFN_cufftEstimate2d = unsafe extern "C" fn(
312    nx: c_int,
313    ny: c_int,
314    ty: cufftType,
315    work_size: *mut usize,
316) -> cufftResult;
317
318/// Function-pointer type for `cufftEstimate3d` (estimate workspace size for a 3D FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
319pub type PFN_cufftEstimate3d = unsafe extern "C" fn(
320    nx: c_int,
321    ny: c_int,
322    nz: c_int,
323    ty: cufftType,
324    work_size: *mut usize,
325) -> cufftResult;
326
327/// Function-pointer type for `cufftEstimateMany` (estimate workspace size for a batched/strided FFT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
328pub type PFN_cufftEstimateMany = unsafe extern "C" fn(
329    rank: c_int,
330    n: *mut c_int,
331    inembed: *mut c_int,
332    istride: c_int,
333    idist: c_int,
334    onembed: *mut c_int,
335    ostride: c_int,
336    odist: c_int,
337    ty: cufftType,
338    batch: c_int,
339    work_size: *mut usize,
340) -> cufftResult;
341
342/// Function-pointer type for `cufftGetSize1d` (query exact workspace size for a 1D plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
343pub type PFN_cufftGetSize1d = unsafe extern "C" fn(
344    plan: cufftHandle,
345    nx: c_int,
346    ty: cufftType,
347    batch: c_int,
348    work_size: *mut usize,
349) -> cufftResult;
350
351/// Function-pointer type for `cufftGetSize` (query exact workspace size for a configured plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
352pub type PFN_cufftGetSize = unsafe extern "C" fn(plan: cufftHandle, work_size: *mut usize) -> cufftResult;
353
354// ---- Work-area management ----
355
356/// Function-pointer type for `cufftSetWorkArea` (supply caller-managed work area for a plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
357pub type PFN_cufftSetWorkArea = unsafe extern "C" fn(
358    plan: cufftHandle,
359    work_area: *mut core::ffi::c_void,
360) -> cufftResult;
361
362/// Function-pointer type for `cufftSetAutoAllocation` (toggle automatic workspace allocation). See <https://docs.nvidia.com/cuda/cufft/index.html>.
363pub type PFN_cufftSetAutoAllocation =
364    unsafe extern "C" fn(plan: cufftHandle, auto_allocate: c_int) -> cufftResult;
365
366// ---- Properties ----
367
368/// Function-pointer type for `cufftGetProperty` (query a cuFFT property value). See <https://docs.nvidia.com/cuda/cufft/index.html>.
369pub type PFN_cufftGetProperty =
370    unsafe extern "C" fn(prop: c_int, value_out: *mut c_int) -> cufftResult;
371
372// ---- Multi-GPU (XT) ----
373
374/// Function-pointer type for `cufftXtSetGPUs` (select GPUs for multi-GPU XT execution). See <https://docs.nvidia.com/cuda/cufft/index.html>.
375pub type PFN_cufftXtSetGPUs =
376    unsafe extern "C" fn(plan: cufftHandle, n: c_int, which_gpus: *mut c_int) -> cufftResult;
377
378/// Function-pointer type for `cufftXtMakePlanMany` (configure a multi-GPU XT batched/strided plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
379pub type PFN_cufftXtMakePlanMany = unsafe extern "C" fn(
380    plan: cufftHandle,
381    rank: c_int,
382    n: *mut i64,
383    inembed: *mut i64,
384    istride: i64,
385    idist: i64,
386    input_type: cudaDataType,
387    onembed: *mut i64,
388    ostride: i64,
389    odist: i64,
390    output_type: cudaDataType,
391    batch: i64,
392    work_size: *mut usize,
393    execution_type: cudaDataType,
394) -> cufftResult;
395
396/// Function-pointer type for `cufftXtMalloc` (allocate multi-GPU XT descriptor buffer). See <https://docs.nvidia.com/cuda/cufft/index.html>.
397pub type PFN_cufftXtMalloc = unsafe extern "C" fn(
398    plan: cufftHandle,
399    desc_out: *mut *mut core::ffi::c_void, // cudaLibXtDesc**
400    subformat: c_int,
401) -> cufftResult;
402
403/// Function-pointer type for `cufftXtFree` (free a multi-GPU XT descriptor buffer). See <https://docs.nvidia.com/cuda/cufft/index.html>.
404pub type PFN_cufftXtFree = unsafe extern "C" fn(desc: *mut core::ffi::c_void) -> cufftResult;
405
406/// Function-pointer type for `cufftXtMemcpy` (copy data to/from a multi-GPU XT descriptor). See <https://docs.nvidia.com/cuda/cufft/index.html>.
407pub type PFN_cufftXtMemcpy = unsafe extern "C" fn(
408    plan: cufftHandle,
409    dst: *mut core::ffi::c_void,
410    src: *mut core::ffi::c_void,
411    ty: c_int, // cufftXtCopyType
412) -> cufftResult;
413
414/// Function-pointer type for `cufftXtExec` (execute multi-GPU XT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
415pub type PFN_cufftXtExec = unsafe extern "C" fn(
416    plan: cufftHandle,
417    input: *mut core::ffi::c_void,
418    output: *mut core::ffi::c_void,
419    direction: c_int,
420) -> cufftResult;
421
422/// Function-pointer type for `cufftXtExecDescriptor` (execute multi-GPU XT plan over descriptor buffers). See <https://docs.nvidia.com/cuda/cufft/index.html>.
423pub type PFN_cufftXtExecDescriptor = unsafe extern "C" fn(
424    plan: cufftHandle,
425    input: *mut core::ffi::c_void,
426    output: *mut core::ffi::c_void,
427    direction: c_int,
428) -> cufftResult;
429
430/// Function-pointer type for `cufftXtQueryPlan` (query multi-GPU XT plan properties). See <https://docs.nvidia.com/cuda/cufft/index.html>.
431pub type PFN_cufftXtQueryPlan = unsafe extern "C" fn(
432    plan: cufftHandle,
433    query_struct: *mut core::ffi::c_void,
434    query_type: c_int,
435) -> cufftResult;
436
437/// Function-pointer type for `cufftXtSetCallback` (register a load/store callback on an XT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
438pub type PFN_cufftXtSetCallback = unsafe extern "C" fn(
439    plan: cufftHandle,
440    callback_routine: *mut *mut core::ffi::c_void,
441    cb_type: c_int,
442    caller_info: *mut *mut core::ffi::c_void,
443) -> cufftResult;
444
445/// Function-pointer type for `cufftXtClearCallback` (clear a load/store callback on an XT plan). See <https://docs.nvidia.com/cuda/cufft/index.html>.
446pub type PFN_cufftXtClearCallback =
447    unsafe extern "C" fn(plan: cufftHandle, cb_type: c_int) -> cufftResult;
448
449/// Function-pointer type for `cufftXtSetCallbackSharedSize` (set shared-memory size for a callback). See <https://docs.nvidia.com/cuda/cufft/index.html>.
450pub type PFN_cufftXtSetCallbackSharedSize = unsafe extern "C" fn(
451    plan: cufftHandle,
452    cb_type: c_int,
453    shared_size: usize,
454) -> cufftResult;
455
456/// `cudaDataType` forward-declared for cuFFT signatures. Matches
457/// `baracuda_cuda_sys::runtime::types::cudaDataType` but kept local to
458/// avoid a heavier dep.
459pub type cudaDataType = c_int;
460
461// ---- loader ----
462
463fn cufft_candidates() -> Vec<String> {
464    platform::versioned_library_candidates("cufft", &["13", "12", "11"])
465}
466
467macro_rules! cufft_fns {
468    ($($name:ident as $sym:literal : $pfn:ty);* $(;)?) => {
469        /// Lazily-resolved cuFFT function-pointer table.
470        pub struct Cufft {
471            lib: Library,
472            $($name: OnceLock<$pfn>,)*
473        }
474        impl core::fmt::Debug for Cufft {
475            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
476                f.debug_struct("Cufft").field("lib", &self.lib).finish_non_exhaustive()
477            }
478        }
479        impl Cufft {
480            $(
481                /// `func` (func).
482                pub fn $name(&self) -> Result<$pfn, LoaderError> {
483                    if let Some(&p) = self.$name.get() { return Ok(p); }
484                    let raw: *mut () = unsafe { self.lib.raw_symbol($sym)? };
485                    let p: $pfn = unsafe { core::mem::transmute_copy::<*mut (), $pfn>(&raw) };
486                    let _ = self.$name.set(p);
487                    Ok(p)
488                }
489            )*
490            fn empty(lib: Library) -> Self {
491                Self { lib, $($name: OnceLock::new(),)* }
492            }
493        }
494    };
495}
496
497cufft_fns! {
498    // Lifecycle + plan construction
499    cufft_create as "cufftCreate": PFN_cufftCreate;
500    cufft_destroy as "cufftDestroy": PFN_cufftDestroy;
501    cufft_plan_1d as "cufftPlan1d": PFN_cufftPlan1d;
502    cufft_plan_2d as "cufftPlan2d": PFN_cufftPlan2d;
503    cufft_plan_3d as "cufftPlan3d": PFN_cufftPlan3d;
504    cufft_plan_many as "cufftPlanMany": PFN_cufftPlanMany;
505    cufft_make_plan_1d as "cufftMakePlan1d": PFN_cufftMakePlan1d;
506    cufft_make_plan_2d as "cufftMakePlan2d": PFN_cufftMakePlan2d;
507    cufft_make_plan_3d as "cufftMakePlan3d": PFN_cufftMakePlan3d;
508    cufft_make_plan_many as "cufftMakePlanMany": PFN_cufftMakePlanMany;
509    cufft_make_plan_many64 as "cufftMakePlanMany64": PFN_cufftMakePlanMany64;
510
511    // Sizing
512    cufft_estimate_1d as "cufftEstimate1d": PFN_cufftEstimate1d;
513    cufft_estimate_2d as "cufftEstimate2d": PFN_cufftEstimate2d;
514    cufft_estimate_3d as "cufftEstimate3d": PFN_cufftEstimate3d;
515    cufft_estimate_many as "cufftEstimateMany": PFN_cufftEstimateMany;
516    cufft_get_size_1d as "cufftGetSize1d": PFN_cufftGetSize1d;
517    cufft_get_size as "cufftGetSize": PFN_cufftGetSize;
518
519    // Work-area
520    cufft_set_work_area as "cufftSetWorkArea": PFN_cufftSetWorkArea;
521    cufft_set_auto_allocation as "cufftSetAutoAllocation": PFN_cufftSetAutoAllocation;
522
523    // Stream
524    cufft_set_stream as "cufftSetStream": PFN_cufftSetStream;
525
526    // Version / property
527    cufft_get_version as "cufftGetVersion": PFN_cufftGetVersion;
528    cufft_get_property as "cufftGetProperty": PFN_cufftGetProperty;
529
530    // Exec single-precision
531    cufft_exec_r2c as "cufftExecR2C": PFN_cufftExecR2C;
532    cufft_exec_c2r as "cufftExecC2R": PFN_cufftExecC2R;
533    cufft_exec_c2c as "cufftExecC2C": PFN_cufftExecC2C;
534
535    // Exec double-precision
536    cufft_exec_d2z as "cufftExecD2Z": PFN_cufftExecD2Z;
537    cufft_exec_z2d as "cufftExecZ2D": PFN_cufftExecZ2D;
538    cufft_exec_z2z as "cufftExecZ2Z": PFN_cufftExecZ2Z;
539
540    // Multi-GPU (XT)
541    cufft_xt_set_gpus as "cufftXtSetGPUs": PFN_cufftXtSetGPUs;
542    cufft_xt_make_plan_many as "cufftXtMakePlanMany": PFN_cufftXtMakePlanMany;
543    cufft_xt_malloc as "cufftXtMalloc": PFN_cufftXtMalloc;
544    cufft_xt_free as "cufftXtFree": PFN_cufftXtFree;
545    cufft_xt_memcpy as "cufftXtMemcpy": PFN_cufftXtMemcpy;
546    cufft_xt_exec as "cufftXtExec": PFN_cufftXtExec;
547    cufft_xt_exec_descriptor as "cufftXtExecDescriptor": PFN_cufftXtExecDescriptor;
548    cufft_xt_query_plan as "cufftXtQueryPlan": PFN_cufftXtQueryPlan;
549
550    // Callbacks
551    cufft_xt_set_callback as "cufftXtSetCallback": PFN_cufftXtSetCallback;
552    cufft_xt_clear_callback as "cufftXtClearCallback": PFN_cufftXtClearCallback;
553    cufft_xt_set_callback_shared_size as "cufftXtSetCallbackSharedSize":
554        PFN_cufftXtSetCallbackSharedSize;
555}
556
557/// Return the lazily-loaded cuFFT library accessor.
558pub fn cufft() -> Result<&'static Cufft, LoaderError> {
559    static CUFFT: OnceLock<Cufft> = OnceLock::new();
560    if let Some(c) = CUFFT.get() {
561        return Ok(c);
562    }
563    let candidates: Vec<&'static str> = cufft_candidates()
564        .into_iter()
565        .map(|s| Box::leak(s.into_boxed_str()) as &'static str)
566        .collect();
567    let candidates_leaked: &'static [&'static str] = Box::leak(candidates.into_boxed_slice());
568    let lib = Library::open("cufft", candidates_leaked)?;
569    let c = Cufft::empty(lib);
570    let _ = CUFFT.set(c);
571    Ok(CUFFT.get().expect("OnceLock set or lost race"))
572}