Skip to content

Commit 5a92708

Browse files
committed
Support for arbitrary arrays
1 parent 4a05f27 commit 5a92708

File tree

5 files changed

+107
-0
lines changed

5 files changed

+107
-0
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ rustversion = "1.0"
3535
[features]
3636
default = ["macros"]
3737
macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"]
38+
39+
# Supports arrays of arbitrary size
40+
const-generics = []
41+
3842
# Optimizes PyObject to Vec conversion and so on.
3943
nightly = []
4044

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg_attr(feature = "const-generics", feature(min_const_generics))]
2+
#![cfg_attr(feature = "nightly", allow(incomplete_features))]
13
#![cfg_attr(feature = "nightly", feature(specialization))]
24
#![allow(clippy::missing_safety_doc)] // FIXME (#698)
35

src/types/list.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,17 @@ where
178178
}
179179
}
180180

181+
#[cfg(feature = "const-generics")]
182+
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
183+
where
184+
T: ToPyObject,
185+
{
186+
fn into_py(self, py: Python) -> PyObject {
187+
self.as_ref().to_object(py)
188+
}
189+
}
190+
191+
#[cfg(not(feature = "const-generics"))]
181192
macro_rules! array_impls {
182193
($($N:expr),+) => {
183194
$(
@@ -193,6 +204,7 @@ macro_rules! array_impls {
193204
}
194205
}
195206

207+
#[cfg(not(feature = "const-generics"))]
196208
array_impls!(
197209
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
198210
26, 27, 28, 29, 30, 31, 32

src/types/mod.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,40 @@ mod slice;
235235
mod string;
236236
mod tuple;
237237
mod typeobject;
238+
239+
#[cfg(feature = "const-generics")]
240+
struct ArrayGuard<T, const N: usize> {
241+
dst: *mut T,
242+
initialized: usize,
243+
}
244+
245+
#[cfg(feature = "const-generics")]
246+
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
247+
fn drop(&mut self) {
248+
debug_assert!(self.initialized <= N);
249+
let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
250+
unsafe {
251+
core::ptr::drop_in_place(initialized_part);
252+
}
253+
}
254+
}
255+
256+
#[cfg(feature = "const-generics")]
257+
fn try_create_array<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
258+
where
259+
F: FnMut(usize) -> Result<T, E>,
260+
{
261+
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
262+
let mut guard: ArrayGuard<T, N> = ArrayGuard {
263+
dst: array.as_mut_ptr() as _,
264+
initialized: 0,
265+
};
266+
unsafe {
267+
for (idx, value_ptr) in (&mut *array.as_mut_ptr()).iter_mut().enumerate() {
268+
core::ptr::write(value_ptr, cb(idx)?);
269+
guard.initialized += 1;
270+
}
271+
core::mem::forget(guard);
272+
Ok(array.assume_init())
273+
}
274+
}

src/types/sequence.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,41 @@ impl PySequence {
257257
}
258258
}
259259

260+
#[cfg(feature = "const-generics")]
261+
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
262+
where
263+
T: FromPyObject<'a>,
264+
{
265+
#[cfg(not(feature = "nightly"))]
266+
fn extract(obj: &'a PyAny) -> PyResult<Self> {
267+
create_array_from_obj(obj)
268+
}
269+
270+
#[cfg(feature = "nightly")]
271+
default fn extract(obj: &'a PyAny) -> PyResult<Self> {
272+
create_array_from_obj(obj)
273+
}
274+
}
275+
276+
#[cfg(all(feature = "const-generics", feature = "nightly"))]
277+
impl<'source, T, const N: usize> FromPyObject<'source> for [T; N]
278+
where
279+
for<'a> T: FromPyObject<'a> + crate::buffer::Element,
280+
{
281+
fn extract(obj: &'source PyAny) -> PyResult<Self> {
282+
let mut array = create_array_from_obj(obj)?;
283+
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
284+
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
285+
buf.release(obj.py());
286+
return Ok(array);
287+
}
288+
buf.release(obj.py());
289+
}
290+
Ok(array)
291+
}
292+
}
293+
294+
#[cfg(not(feature = "const-generics"))]
260295
macro_rules! array_impls {
261296
($($N:expr),+) => {
262297
$(
@@ -303,6 +338,7 @@ macro_rules! array_impls {
303338
}
304339
}
305340

341+
#[cfg(not(feature = "const-generics"))]
306342
array_impls!(
307343
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
308344
26, 27, 28, 29, 30, 31, 32
@@ -343,6 +379,21 @@ where
343379
}
344380
}
345381

382+
#[cfg(feature = "const-generics")]
383+
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
384+
where
385+
T: FromPyObject<'s>,
386+
{
387+
let seq = <PySequence as PyTryFrom>::try_from(obj)?;
388+
crate::types::try_create_array(|idx| {
389+
seq.get_item(idx as isize)
390+
.map_err(|_| {
391+
exceptions::PyBufferError::py_err("Slice length does not match buffer length.")
392+
})?
393+
.extract::<T>()
394+
})
395+
}
396+
346397
fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult<Vec<T>>
347398
where
348399
T: FromPyObject<'s>,
@@ -355,6 +406,7 @@ where
355406
Ok(v)
356407
}
357408

409+
#[cfg(not(feature = "const-generics"))]
358410
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
359411
where
360412
T: FromPyObject<'s>,

0 commit comments

Comments
 (0)