Skip to main content

pyo3_ffi/
slots.rs

1#[cfg(Py_3_15)]
2use crate::Py_ssize_t;
3#[cfg(Py_3_15)]
4use core::ffi::{c_int, c_void};
5
6#[cfg(Py_3_15)]
7pub type _Py_funcptr_t = unsafe extern "C" fn();
8
9#[derive(Copy, Clone)]
10#[repr(C)]
11#[cfg(Py_3_15)]
12pub union _anon_union_32b {
13    pub sl_reserved: u32,
14}
15
16#[derive(Copy, Clone)]
17#[repr(C)]
18#[cfg(Py_3_15)]
19pub union _anon_union_64b {
20    pub sl_ptr: *mut c_void,
21    pub sl_func: Option<_Py_funcptr_t>,
22    pub sl_size: Py_ssize_t,
23    pub sl_int64: i64,
24    pub sl_uint64: u64,
25}
26
27#[derive(Copy, Clone)]
28#[repr(C)]
29#[cfg(Py_3_15)]
30pub struct PySlot {
31    pub sl_id: u16,
32    pub sl_flags: u16,
33    pub anon1: _anon_union_32b,
34    pub anon2: _anon_union_64b,
35}
36
37#[cfg(Py_3_15)]
38pub const PySlot_OPTIONAL: u16 = 0x01;
39#[cfg(Py_3_15)]
40pub const PySlot_STATIC: u16 = 0x02;
41#[cfg(Py_3_15)]
42pub const PySlot_INTPTR: u16 = 0x04;
43#[cfg(Py_3_15)]
44pub const Py_slot_invalid: u16 = 0xffff;
45
46// The slot IDs are integer constants in the C headers and don't have
47// explicit types. Legacy slot IDs are c integers but PySlot IDs are
48// u16s. We use this to convert from c_int to u16 safely, without using
49// `as`. The panic should only be possible if there is a programming
50// error somewhere leading to a slot ID outside the range of u16. If
51// TryInto becomes possible in const contexts we could use that instead.
52#[cfg(Py_3_15)]
53const fn safe_cast_c_int_to_u16(val: c_int) -> u16 {
54    if val >= 0 && val <= u16::MAX as c_int {
55        val as u16
56    } else {
57        panic!("Slot ID out of range for u16");
58    }
59}
60
61#[cfg(Py_3_15)]
62pub const fn PySlot_DATA(NAME: c_int, VALUE: *mut c_void) -> PySlot {
63    PySlot {
64        sl_id: safe_cast_c_int_to_u16(NAME),
65        sl_flags: PySlot_INTPTR,
66        anon1: _anon_union_32b { sl_reserved: 0 },
67        anon2: _anon_union_64b { sl_ptr: VALUE },
68    }
69}
70
71#[expect(clippy::incompatible_msrv, reason = "guarded by cfg(const_is_null)")]
72#[cfg(Py_3_15)]
73pub const unsafe fn PySlot_FUNC(NAME: c_int, VALUE: *mut c_void) -> PySlot {
74    #[cfg(const_is_null)]
75    assert!(!VALUE.is_null(), "value may not be null");
76    PySlot {
77        sl_id: safe_cast_c_int_to_u16(NAME),
78        sl_flags: 0,
79        anon1: _anon_union_32b { sl_reserved: 0 },
80        anon2: _anon_union_64b {
81            // SAFETY: caller should have provided a valid pointer to function
82            sl_func: Some(unsafe { core::mem::transmute::<*mut c_void, _Py_funcptr_t>(VALUE) }),
83        },
84    }
85}
86
87#[cfg(Py_3_15)]
88pub const fn PySlot_SIZE(NAME: c_int, VALUE: Py_ssize_t) -> PySlot {
89    PySlot {
90        sl_id: safe_cast_c_int_to_u16(NAME),
91        sl_flags: 0,
92        anon1: _anon_union_32b { sl_reserved: 0 },
93        anon2: _anon_union_64b { sl_size: VALUE },
94    }
95}
96
97#[cfg(Py_3_15)]
98pub const fn PySlot_INT64(NAME: c_int, VALUE: i64) -> PySlot {
99    PySlot {
100        sl_id: safe_cast_c_int_to_u16(NAME),
101        sl_flags: 0,
102        anon1: _anon_union_32b { sl_reserved: 0 },
103        anon2: _anon_union_64b { sl_int64: VALUE },
104    }
105}
106
107#[cfg(Py_3_15)]
108pub const fn PySlot_UINT64(NAME: c_int, VALUE: u64) -> PySlot {
109    PySlot {
110        sl_id: safe_cast_c_int_to_u16(NAME),
111        sl_flags: 0,
112        anon1: _anon_union_32b { sl_reserved: 0 },
113        anon2: _anon_union_64b { sl_uint64: VALUE },
114    }
115}
116
117#[cfg(Py_3_15)]
118pub const fn PySlot_STATIC_DATA(NAME: c_int, VALUE: *mut c_void) -> PySlot {
119    PySlot {
120        sl_id: safe_cast_c_int_to_u16(NAME),
121        sl_flags: PySlot_STATIC,
122        anon1: _anon_union_32b { sl_reserved: 0 },
123        anon2: _anon_union_64b { sl_ptr: VALUE },
124    }
125}
126
127#[cfg(Py_3_15)]
128pub const fn PySlot_PTR(NAME: c_int, VALUE: *mut c_void) -> PySlot {
129    PySlot {
130        sl_id: safe_cast_c_int_to_u16(NAME),
131        sl_flags: PySlot_INTPTR,
132        anon1: _anon_union_32b { sl_reserved: 0 },
133        anon2: _anon_union_64b { sl_ptr: VALUE },
134    }
135}
136
137#[cfg(Py_3_15)]
138pub const fn PySlot_PTR_STATIC(NAME: c_int, VALUE: *mut c_void) -> PySlot {
139    PySlot {
140        sl_id: safe_cast_c_int_to_u16(NAME),
141        sl_flags: PySlot_INTPTR | PySlot_STATIC,
142        anon1: _anon_union_32b { sl_reserved: 0 },
143        anon2: _anon_union_64b { sl_ptr: VALUE },
144    }
145}
146
147#[cfg(Py_3_15)]
148pub const fn PySlot_END() -> PySlot {
149    // SAFETY: the interpreter expects a zeroed slot to mark the end of the slot array
150    unsafe { core::mem::zeroed() }
151}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here