numpy/npyffi/
objects.rs

1//! Low-Lebel binding for NumPy C API C-objects
2//!
3//! <https://numpy.org/doc/stable/reference/c-api/types-and-structures.html>
4#![allow(non_camel_case_types)]
5
6use libc::FILE;
7use pyo3::ffi::*;
8use std::ffi::*;
9
10use super::types::*;
11use crate::npyffi::*;
12
13pub const NPY_NTYPES_ABI_COMPATIBLE: usize = 21;
14pub const NPY_MAXDIMS_LEGACY_ITERS: usize = 32;
15
16#[repr(C)]
17pub struct PyArrayObject {
18    pub ob_base: PyObject,
19    pub data: *mut c_char,
20    pub nd: c_int,
21    pub dimensions: *mut npy_intp,
22    pub strides: *mut npy_intp,
23    pub base: *mut PyObject,
24    pub descr: *mut PyArray_Descr,
25    pub flags: c_int,
26    pub weakreflist: *mut PyObject,
27}
28
29#[repr(C)]
30pub struct PyArray_Descr {
31    pub ob_base: PyObject,
32    pub typeobj: *mut PyTypeObject,
33    pub kind: c_char,
34    pub type_: c_char,
35    pub byteorder: c_char,
36    pub _former_flags: c_char,
37    pub type_num: c_int,
38}
39
40#[repr(C)]
41pub struct PyArray_DescrProto {
42    pub ob_base: PyObject,
43    pub typeobj: *mut PyTypeObject,
44    pub kind: c_char,
45    pub type_: c_char,
46    pub byteorder: c_char,
47    pub flags: c_char,
48    pub type_num: c_int,
49    pub elsize: c_int,
50    pub alignment: c_int,
51    pub subarray: *mut PyArray_ArrayDescr,
52    pub fields: *mut PyObject,
53    pub names: *mut PyObject,
54    pub f: *mut PyArray_ArrFuncs,
55    pub metadata: *mut PyObject,
56    pub c_metadata: *mut NpyAuxData,
57    pub hash: npy_hash_t,
58}
59
60#[repr(C)]
61pub struct _PyArray_DescrNumPy2 {
62    pub ob_base: PyObject,
63    pub typeobj: *mut PyTypeObject,
64    pub kind: c_char,
65    pub type_: c_char,
66    pub byteorder: c_char,
67    pub _former_flags: c_char,
68    pub type_num: c_int,
69    pub flags: npy_uint64,
70    pub elsize: npy_intp,
71    pub alignment: npy_intp,
72    pub metadata: *mut PyObject,
73    pub hash: npy_hash_t,
74    pub reserved_null: [*mut std::ffi::c_void; 2],
75}
76
77#[repr(C)]
78struct _PyArray_LegacyDescr {
79    pub ob_base: PyObject,
80    pub typeobj: *mut PyTypeObject,
81    pub kind: c_char,
82    pub type_: c_char,
83    pub byteorder: c_char,
84    pub _former_flags: c_char,
85    pub type_num: c_int,
86    pub flags: npy_uint64,
87    pub elsize: npy_intp,
88    pub alignment: npy_intp,
89    pub metadata: *mut PyObject,
90    pub hash: npy_hash_t,
91    pub reserved_null: [*mut std::ffi::c_void; 2],
92    pub subarray: *mut PyArray_ArrayDescr,
93    pub fields: *mut PyObject,
94    pub names: *mut PyObject,
95    pub c_metadata: *mut NpyAuxData,
96}
97
98#[allow(non_snake_case)]
99#[inline(always)]
100pub unsafe fn PyDataType_ISLEGACY(dtype: *const PyArray_Descr) -> bool {
101    (*dtype).type_num < NPY_TYPES::NPY_VSTRING as _ && (*dtype).type_num >= 0
102}
103
104#[allow(non_snake_case)]
105#[inline(always)]
106pub unsafe fn PyDataType_SET_ELSIZE<'py>(
107    py: Python<'py>,
108    dtype: *mut PyArray_Descr,
109    size: npy_intp,
110) {
111    if is_numpy_2(py) {
112        unsafe {
113            (*(dtype as *mut _PyArray_DescrNumPy2)).elsize = size;
114        }
115    } else {
116        unsafe {
117            (*(dtype as *mut PyArray_DescrProto)).elsize = size as c_int;
118        }
119    }
120}
121
122#[allow(non_snake_case)]
123#[inline(always)]
124pub unsafe fn PyDataType_FLAGS<'py>(py: Python<'py>, dtype: *const PyArray_Descr) -> npy_uint64 {
125    if is_numpy_2(py) {
126        unsafe { (*(dtype as *mut _PyArray_DescrNumPy2)).flags }
127    } else {
128        unsafe { (*(dtype as *mut PyArray_DescrProto)).flags as c_uchar as npy_uint64 }
129    }
130}
131
132macro_rules! define_descr_accessor {
133    ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $default:expr) => {
134        #[allow(non_snake_case)]
135        #[inline(always)]
136        pub unsafe fn $name<'py>(py: Python<'py>, dtype: *const PyArray_Descr) -> $type {
137            if $legacy_only && !PyDataType_ISLEGACY(dtype) {
138                $default
139            } else {
140                if is_numpy_2(py) {
141                    unsafe { (*(dtype as *const _PyArray_LegacyDescr)).$property }
142                } else {
143                    unsafe { (*(dtype as *mut PyArray_DescrProto)).$property as $type }
144                }
145            }
146        }
147    };
148}
149
150define_descr_accessor!(PyDataType_ELSIZE, elsize, npy_intp, false, 0);
151define_descr_accessor!(PyDataType_ALIGNMENT, alignment, npy_intp, false, 0);
152define_descr_accessor!(
153    PyDataType_METADATA,
154    metadata,
155    *mut PyObject,
156    true,
157    std::ptr::null_mut()
158);
159define_descr_accessor!(
160    PyDataType_SUBARRAY,
161    subarray,
162    *mut PyArray_ArrayDescr,
163    true,
164    std::ptr::null_mut()
165);
166define_descr_accessor!(
167    PyDataType_NAMES,
168    names,
169    *mut PyObject,
170    true,
171    std::ptr::null_mut()
172);
173define_descr_accessor!(
174    PyDataType_FIELDS,
175    fields,
176    *mut PyObject,
177    true,
178    std::ptr::null_mut()
179);
180define_descr_accessor!(
181    PyDataType_C_METADATA,
182    c_metadata,
183    *mut NpyAuxData,
184    true,
185    std::ptr::null_mut()
186);
187
188#[repr(C)]
189#[derive(Copy, Clone)]
190pub struct PyArray_ArrayDescr {
191    pub base: *mut PyArray_Descr,
192    pub shape: *mut PyObject,
193}
194
195#[repr(C)]
196#[derive(Copy, Clone)]
197pub struct PyArray_ArrFuncs {
198    pub cast: [PyArray_VectorUnaryFunc; NPY_NTYPES_ABI_COMPATIBLE],
199    pub getitem: PyArray_GetItemFunc,
200    pub setitem: PyArray_SetItemFunc,
201    pub copyswapn: PyArray_CopySwapNFunc,
202    pub copyswap: PyArray_CopySwapFunc,
203    pub compare: PyArray_CompareFunc,
204    pub argmax: PyArray_ArgFunc,
205    pub dotfunc: PyArray_DotFunc,
206    pub scanfunc: PyArray_ScanFunc,
207    pub fromstr: PyArray_FromStrFunc,
208    pub nonzero: PyArray_NonzeroFunc,
209    pub fill: PyArray_FillFunc,
210    pub fillwithscalar: PyArray_FillWithScalarFunc,
211    pub sort: [PyArray_SortFunc; NPY_NSORTS],
212    pub argsort: [PyArray_ArgSortFunc; NPY_NSORTS],
213    pub castdict: *mut PyObject,
214    pub scalarkind: PyArray_ScalarKindFunc,
215    pub cancastscalarkindto: *mut *mut c_int,
216    pub cancastto: *mut c_int,
217    pub _unused1: *mut c_void,
218    pub _unused2: *mut c_void,
219    pub _unused3: *mut c_void,
220    pub argmin: PyArray_ArgFunc,
221}
222
223pub type PyArray_GetItemFunc =
224    Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> *mut PyObject>;
225pub type PyArray_SetItemFunc =
226    Option<unsafe extern "C" fn(*mut PyObject, *mut c_void, *mut c_void) -> c_int>;
227pub type PyArray_CopySwapNFunc = Option<
228    unsafe extern "C" fn(
229        *mut c_void,
230        npy_intp,
231        *mut c_void,
232        npy_intp,
233        npy_intp,
234        c_int,
235        *mut c_void,
236    ),
237>;
238pub type PyArray_CopySwapFunc =
239    Option<unsafe extern "C" fn(*mut c_void, *mut c_void, c_int, *mut c_void)>;
240pub type PyArray_NonzeroFunc = Option<unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_uchar>;
241pub type PyArray_CompareFunc =
242    Option<unsafe extern "C" fn(*const c_void, *const c_void, *mut c_void) -> c_int>;
243pub type PyArray_ArgFunc =
244    Option<unsafe extern "C" fn(*mut c_void, npy_intp, *mut npy_intp, *mut c_void) -> c_int>;
245pub type PyArray_DotFunc = Option<
246    unsafe extern "C" fn(
247        *mut c_void,
248        npy_intp,
249        *mut c_void,
250        npy_intp,
251        *mut c_void,
252        npy_intp,
253        *mut c_void,
254    ),
255>;
256pub type PyArray_VectorUnaryFunc =
257    Option<unsafe extern "C" fn(*mut c_void, *mut c_void, npy_intp, *mut c_void, *mut c_void)>;
258pub type PyArray_ScanFunc =
259    Option<unsafe extern "C" fn(*mut FILE, *mut c_void, *mut c_char, *mut PyArray_Descr) -> c_int>;
260pub type PyArray_FromStrFunc = Option<
261    unsafe extern "C" fn(*mut c_char, *mut c_void, *mut *mut c_char, *mut PyArray_Descr) -> c_int,
262>;
263pub type PyArray_FillFunc =
264    Option<unsafe extern "C" fn(*mut c_void, npy_intp, *mut c_void) -> c_int>;
265pub type PyArray_SortFunc =
266    Option<unsafe extern "C" fn(*mut c_void, npy_intp, *mut c_void) -> c_int>;
267pub type PyArray_ArgSortFunc =
268    Option<unsafe extern "C" fn(*mut c_void, *mut npy_intp, npy_intp, *mut c_void) -> c_int>;
269pub type PyArray_FillWithScalarFunc =
270    Option<unsafe extern "C" fn(*mut c_void, npy_intp, *mut c_void, *mut c_void) -> c_int>;
271pub type PyArray_ScalarKindFunc = Option<unsafe extern "C" fn(*mut c_void) -> c_int>;
272
273#[repr(C)]
274#[derive(Clone, Copy)]
275pub struct PyArray_Dims {
276    pub ptr: *mut npy_intp,
277    pub len: c_int,
278}
279
280#[repr(C)]
281pub struct PyArray_Chunk {
282    pub ob_base: PyObject,
283    pub base: *mut PyObject,
284    pub ptr: *mut c_void,
285    pub len: npy_intp,
286    pub flags: c_int,
287}
288
289#[repr(C)]
290#[derive(Clone, Copy)]
291pub struct PyArrayInterface {
292    pub two: c_int,
293    pub nd: c_int,
294    pub typekind: c_char,
295    pub itemsize: c_int,
296    pub flags: c_int,
297    pub shape: *mut npy_intp,
298    pub strides: *mut npy_intp,
299    pub data: *mut c_void,
300    pub descr: *mut PyObject,
301}
302
303#[repr(C)]
304pub struct PyUFuncObject {
305    pub ob_base: PyObject,
306    pub nin: c_int,
307    pub nout: c_int,
308    pub nargs: c_int,
309    pub identity: c_int,
310    pub functions: *mut PyUFuncGenericFunction,
311    pub data: *mut *mut c_void,
312    pub ntypes: c_int,
313    pub reserved1: c_int,
314    pub name: *const c_char,
315    pub types: *mut c_char,
316    pub doc: *const c_char,
317    pub ptr: *mut c_void,
318    pub obj: *mut PyObject,
319    pub userloops: *mut PyObject,
320    pub core_enabled: c_int,
321    pub core_num_dim_ix: c_int,
322    pub core_num_dims: *mut c_int,
323    pub core_dim_ixs: *mut c_int,
324    pub core_offsets: *mut c_int,
325    pub core_signature: *mut c_char,
326    pub type_resolver: PyUFunc_TypeResolutionFunc,
327    pub reserved2: *mut c_void, // Was the legacy loop resolver
328    #[cfg(all(Py_3_8, not(Py_LIMITED_API)))]
329    pub vectorcall: Option<vectorcallfunc>,
330    #[cfg(not(all(Py_3_8, not(Py_LIMITED_API))))]
331    pub vectorcall: *mut c_void,
332    pub reserved3: *mut c_void, // Was previously the `PyUFunc_MaskedInnerLoopSelectionFunc`
333    pub op_flags: *mut npy_uint32,
334    pub iter_flags: npy_uint32,
335}
336
337pub type PyUFuncGenericFunction =
338    Option<unsafe extern "C" fn(*mut *mut c_char, *mut npy_intp, *mut npy_intp, *mut c_void)>;
339pub type PyUFunc_MaskedStridedInnerLoopFunc = Option<
340    unsafe extern "C" fn(
341        *mut *mut c_char,
342        *mut npy_intp,
343        *mut c_char,
344        npy_intp,
345        npy_intp,
346        *mut NpyAuxData,
347    ),
348>;
349pub type PyUFunc_TypeResolutionFunc = Option<
350    unsafe extern "C" fn(
351        *mut PyUFuncObject,
352        NPY_CASTING,
353        *mut *mut PyArrayObject,
354        *mut PyObject,
355        *mut *mut PyArray_Descr,
356    ) -> c_int,
357>;
358
359#[repr(C)]
360#[derive(Debug, Copy, Clone)]
361pub struct NpyIter([u8; 0]);
362
363#[repr(C)]
364pub struct PyArrayIterObject {
365    pub ob_base: PyObject,
366    pub nd_m1: c_int,
367    pub index: npy_intp,
368    pub size: npy_intp,
369    pub coordinates: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
370    pub dims_m1: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
371    pub strides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
372    pub backstrides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
373    pub factors: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
374    pub ao: *mut PyArrayObject,
375    pub dataptr: *mut c_char,
376    pub contiguous: npy_bool,
377    pub bounds: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS],
378    pub limits: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS],
379    pub limits_sizes: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
380    pub translate: npy_iter_get_dataptr_t,
381}
382
383#[repr(C)]
384pub struct PyArrayMultiIterObject {
385    pub ob_base: PyObject,
386    pub numiter: c_int,
387    pub size: npy_intp,
388    pub index: npy_intp,
389    pub nd: c_int,
390    pub dimensions: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
391    pub iters: [*mut PyArrayIterObject; 32usize],
392}
393
394#[repr(C)]
395pub struct PyArrayNeighborhoodIterObject {
396    pub ob_base: PyObject,
397    pub nd_m1: c_int,
398    pub index: npy_intp,
399    pub size: npy_intp,
400    pub coordinates: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
401    pub dims_m1: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
402    pub strides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
403    pub backstrides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
404    pub factors: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
405    pub ao: *mut PyArrayObject,
406    pub dataptr: *mut c_char,
407    pub contiguous: npy_bool,
408    pub bounds: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS],
409    pub limits: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS],
410    pub limits_sizes: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
411    pub translate: npy_iter_get_dataptr_t,
412    pub nd: npy_intp,
413    pub dimensions: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS],
414    pub _internal_iter: *mut PyArrayIterObject,
415    pub constant: *mut c_char,
416    pub mode: c_int,
417}
418
419pub type NpyIter_IterNextFunc = Option<unsafe extern "C" fn(*mut NpyIter) -> c_int>;
420pub type NpyIter_GetMultiIndexFunc = Option<unsafe extern "C" fn(*mut NpyIter, *mut npy_intp)>;
421pub type PyDataMem_EventHookFunc =
422    Option<unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *mut c_void)>;
423pub type npy_iter_get_dataptr_t =
424    Option<unsafe extern "C" fn(*mut PyArrayIterObject, *mut npy_intp) -> *mut c_char>;
425
426#[repr(C)]
427#[derive(Copy, Clone)]
428pub struct NpyAuxData {
429    pub free: NpyAuxData_FreeFunc,
430    pub clone: NpyAuxData_CloneFunc,
431    pub reserved: [*mut c_void; 2usize],
432}
433
434pub type NpyAuxData_FreeFunc = Option<unsafe extern "C" fn(*mut NpyAuxData)>;
435pub type NpyAuxData_CloneFunc = Option<unsafe extern "C" fn(*mut NpyAuxData) -> *mut NpyAuxData>;
436
437#[repr(C)]
438#[derive(Clone, Copy)]
439pub struct PyArray_DatetimeMetaData {
440    pub base: NPY_DATETIMEUNIT,
441    pub num: c_int,
442}
443
444#[repr(C)]
445#[derive(Clone, Copy)]
446pub struct PyArray_DatetimeDTypeMetaData {
447    pub base: NpyAuxData,
448    pub meta: PyArray_DatetimeMetaData,
449}
450
451// npy_packed_static_string and npy_string_allocator are opaque pointers.
452// FIXME(adamreichold): Consider extern types when they are stabilized.
453// https://github.com/rust-lang/rust/issues/43467
454#[repr(C)]
455pub struct npy_packed_static_string([u8; 0]);
456
457#[repr(C)]
458pub struct npy_string_allocator([u8; 0]);
459
460#[cfg(not(Py_LIMITED_API))]
461#[repr(C)]
462pub struct PyArray_DTypeMeta {
463    pub superclass: PyHeapTypeObject,
464    pub singleton: *mut PyArray_Descr,
465    pub type_num: c_int,
466    pub scalar_type: *mut PyTypeObject,
467    pub flags: npy_uint64,
468    pub dt_slots: *mut c_void,
469    pub reserved: [*mut c_void; 3],
470}
471
472#[cfg(Py_LIMITED_API)]
473pub type PyArray_DTypeMeta = PyTypeObject;
474
475#[repr(C)]
476#[derive(Clone, Copy)]
477pub struct npy_static_string {
478    pub size: usize,
479    pub buf: *const c_char,
480}
481
482#[repr(C)]
483pub struct PyArray_StringDTypeObject {
484    pub base: PyArray_Descr,
485    pub na_object: *mut PyObject,
486    pub coerce: c_char,
487    pub has_nan_na: c_char,
488    pub has_string_na: c_char,
489    pub array_owned: c_char,
490    pub default_string: npy_static_string,
491    pub na_name: npy_static_string,
492    pub allocator: *mut npy_string_allocator,
493}
494
495#[repr(C)]
496#[derive(Clone, Copy)]
497pub struct PyArrayMethod_Spec {
498    pub name: *const c_char,
499    pub nin: c_int,
500    pub nout: c_int,
501    pub casting: NPY_CASTING,
502    pub flags: NPY_ARRAYMETHOD_FLAGS,
503    pub dtypes: *mut *mut PyArray_DTypeMeta,
504    pub slots: *mut PyType_Slot,
505}
506
507#[repr(C)]
508#[derive(Clone, Copy)]
509pub struct PyArrayDTypeMeta_Spec {
510    pub typeobj: *mut PyTypeObject,
511    pub flags: c_int,
512    pub casts: *mut *mut PyArrayMethod_Spec,
513    pub slots: *mut PyType_Slot,
514    pub baseclass: *mut PyTypeObject,
515}