numpy 0.29.0

PyO3-based Rust bindings of the NumPy C-API
Documentation
//! Low-Level binding for [UFunc API](https://numpy.org/doc/stable/reference/c-api/ufunc.html)

use std::ffi::*;

use pyo3::{ffi::PyObject, sync::PyOnceLock};

use crate::npyffi::*;

fn mod_name(py: Python<'_>) -> PyResult<&'static str> {
    static MOD_NAME: PyOnceLock<String> = PyOnceLock::new();
    MOD_NAME
        .get_or_try_init(py, || {
            let numpy_core = super::array::numpy_core_name(py)?;
            Ok(format!("{numpy_core}.umath"))
        })
        .map(String::as_str)
}

const CAPSULE_NAME: &str = "_UFUNC_API";

/// A global variable which stores a ['capsule'](https://docs.python.org/3/c-api/capsule.html)
/// pointer to [Numpy UFunc API](https://numpy.org/doc/stable/reference/c-api/ufunc.html).
pub static PY_UFUNC_API: PyUFuncAPI = PyUFuncAPI(PyOnceLock::new());

pub struct PyUFuncAPI(PyOnceLock<NonNull<*const c_void>>);

unsafe impl Send for PyUFuncAPI {}

unsafe impl Sync for PyUFuncAPI {}

impl PyUFuncAPI {
    pub(super) unsafe fn get<'py>(&self, py: Python<'py>, offset: isize) -> NonNull<*const c_void> {
        let api = self
            .0
            .get_or_try_init(py, || get_numpy_api(py, mod_name(py)?, CAPSULE_NAME))
            .expect("Failed to access NumPy ufunc API capsule");

        api.offset(offset)
    }
}

// See PyArrayAPI for a reference of NumPy API mapping.
impl PyUFuncAPI {
    impl_api![1; PyUFunc_FromFuncAndData(func: *mut PyUFuncGenericFunction, data: *mut *mut c_void, types: *mut c_char, ntypes: c_int, nin: c_int, nout: c_int, identity: c_int, name: *const c_char, doc: *const c_char, unused: c_int) -> *mut PyObject];
    impl_api![2; PyUFunc_RegisterLoopForType(ufunc: *mut PyUFuncObject, usertype: c_int, function: PyUFuncGenericFunction, arg_types: *mut c_int, data: *mut c_void) -> c_int];
    // Unused slot 3, was `PyUFunc_GenericFunction`
    impl_api![4; PyUFunc_f_f_As_d_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![5; PyUFunc_d_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![6; PyUFunc_f_f(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![7; PyUFunc_g_g(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![8; PyUFunc_F_F_As_D_D(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![9; PyUFunc_F_F(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![10; PyUFunc_D_D(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![11; PyUFunc_G_G(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![12; PyUFunc_O_O(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![13; PyUFunc_ff_f_As_dd_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![14; PyUFunc_ff_f(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![15; PyUFunc_dd_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![16; PyUFunc_gg_g(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![17; PyUFunc_FF_F_As_DD_D(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![18; PyUFunc_DD_D(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![19; PyUFunc_FF_F(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![20; PyUFunc_GG_G(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![21; PyUFunc_OO_O(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![22; PyUFunc_O_O_method(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![23; PyUFunc_OO_O_method(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![24; PyUFunc_On_Om(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    // Unused slot 25, was `PyUFunc_GetPyValues`
    // Unused slot 26, was `PyUFunc_checkfperr`
    impl_api![27; PyUFunc_clearfperr()];
    impl_api![28; PyUFunc_getfperr() -> c_int];
    // Unused slot 29, was `PyUFunc_handlefperr`
    impl_api![30; PyUFunc_ReplaceLoopBySignature(func: *mut PyUFuncObject, newfunc: PyUFuncGenericFunction, signature: *mut c_int, oldfunc: *mut PyUFuncGenericFunction) -> c_int];
    impl_api![31; PyUFunc_FromFuncAndDataAndSignature(func: *mut PyUFuncGenericFunction, data: *mut *mut c_void, types: *mut c_char, ntypes: c_int, nin: c_int, nout: c_int, identity: c_int, name: *const c_char, doc: *const c_char, unused: c_int, signature: *const c_char) -> *mut PyObject];
    impl_api![32; PyUFunc_SetUsesArraysAsData(data: *mut *mut c_void, i: usize) -> c_int];
    impl_api![33; PyUFunc_e_e(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![34; PyUFunc_e_e_As_f_f(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![35; PyUFunc_e_e_As_d_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![36; PyUFunc_ee_e(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![37; PyUFunc_ee_e_As_ff_f(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![38; PyUFunc_ee_e_As_dd_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)];
    impl_api![39; PyUFunc_DefaultTypeResolver(ufunc: *mut PyUFuncObject, casting: NPY_CASTING, operands: *mut *mut PyArrayObject, type_tup: *mut PyObject, out_dtypes: *mut *mut PyArray_Descr) -> c_int];
    impl_api![40; PyUFunc_ValidateCasting(ufunc: *mut PyUFuncObject, casting: NPY_CASTING, operands: *mut *mut PyArrayObject, dtypes: *mut *mut PyArray_Descr) -> c_int];
    impl_api![41; PyUFunc_RegisterLoopForDescr(ufunc: *mut PyUFuncObject, user_dtype: *mut PyArray_Descr, function: PyUFuncGenericFunction, arg_dtypes: *mut *mut PyArray_Descr, data: *mut c_void) -> c_int];
    // Min v1.16 impl_api![42; PyUFunc_FromFuncAndDataAndSignatureAndIdentity(
    //     ufunc: *mut PyUFuncObject,
    //     data: *mut *mut c_void,
    //     types: *mut c_char,
    //     ntypes: c_int,
    //     nin: c_int,
    //     nout: c_int,
    //     identity: c_int,
    //     name: *const c_char,
    //     doc: *const c_char,
    //     unused: c_int,
    //     signature: *const c_char,
    //     identity_value: *const c_char,
    // ) -> c_int];
    // Min v2.0 impl_api![43; PyUFunc_AddLoopFromSpec(ufunc: *mut PyObject, spec: *mut PyArrayMethod_Spec) -> c_int];
    // Min v2.0 impl_api![44; PyUFunc_AddPromoter(ufunc: *mut PyObject, dtype_tuple: *mut PyObject, promoter: *mut PyObject) -> c_int];
    // Min v2.0 impl_api![45; PyUFunc_AddWrappingLoop(
    //     ufunc_obj: *mut PyObject,
    //     new_dtypes: *mut *mut PyArray_DTypeMeta,
    //     wrapped_dtypes: *mut *mut PyArray_DTypeMeta,
    //     translate_given_descrs: *mut PyArrayMethod_TranslateGivenDescriptors,
    //     translate_loop_descrs: *mut PyArrayMethod_TranslateLoopDescriptors) -> c_int];
    // Min v2.0 impl_api![46; PyUFunc_GiveFloatingpointErrors(name: *mut c_char, fpe_errors: c_int) -> c_int];
}