1
- use crate :: { FromPyObject , IntoPy , PyAny , PyObject , PyResult , PyTryFrom , Python , ToPyObject } ;
1
+ use crate :: {
2
+ exceptions, FromPyObject , IntoPy , PyAny , PyErr , PyObject , PyResult , PyTryFrom , Python ,
3
+ ToPyObject ,
4
+ } ;
2
5
3
6
#[ cfg( not( min_const_generics) ) ]
4
7
macro_rules! array_impls {
5
8
( $( $N: expr) ,+) => {
6
9
$(
10
+ impl <T > IntoPy <PyObject > for [ T ; $N]
11
+ where
12
+ T : ToPyObject
13
+ {
14
+ fn into_py( self , py: Python ) -> PyObject {
15
+ self . as_ref( ) . to_object( py)
16
+ }
17
+ }
18
+
7
19
impl <' a, T > FromPyObject <' a> for [ T ; $N]
8
20
where
9
21
T : Copy + Default + FromPyObject <' a>,
@@ -55,6 +67,16 @@ array_impls!(
55
67
26 , 27 , 28 , 29 , 30 , 31 , 32
56
68
) ;
57
69
70
+ #[ cfg( min_const_generics) ]
71
+ impl < T , const N : usize > IntoPy < PyObject > for [ T ; N ]
72
+ where
73
+ T : ToPyObject ,
74
+ {
75
+ fn into_py ( self , py : Python ) -> PyObject {
76
+ self . as_ref ( ) . to_object ( py)
77
+ }
78
+ }
79
+
58
80
#[ cfg( min_const_generics) ]
59
81
impl < ' a , T , const N : usize > FromPyObject < ' a > for [ T ; N ]
60
82
where
@@ -71,60 +93,27 @@ where
71
93
}
72
94
}
73
95
74
- #[ cfg( not( min_const_generics) ) ]
75
- macro_rules! array_impls {
76
- ( $( $N: expr) ,+) => {
77
- $(
78
- impl <T > IntoPy <PyObject > for [ T ; $N]
79
- where
80
- T : ToPyObject
81
- {
82
- fn into_py( self , py: Python ) -> PyObject {
83
- self . as_ref( ) . to_object( py)
84
- }
85
- }
86
- ) +
87
- }
88
- }
89
-
90
- #[ cfg( not( min_const_generics) ) ]
91
- array_impls ! (
92
- 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 ,
93
- 26 , 27 , 28 , 29 , 30 , 31 , 32
94
- ) ;
95
-
96
- #[ cfg( min_const_generics) ]
97
- impl < T , const N : usize > IntoPy < PyObject > for [ T ; N ]
98
- where
99
- T : ToPyObject ,
100
- {
101
- fn into_py ( self , py : Python ) -> PyObject {
102
- self . as_ref ( ) . to_object ( py)
103
- }
104
- }
105
-
106
96
#[ cfg( all( min_const_generics, feature = "nightly" ) ) ]
107
97
impl < ' source , T , const N : usize > FromPyObject < ' source > for [ T ; N ]
108
98
where
109
- for < ' a > T : FromPyObject < ' a > + crate :: buffer:: Element ,
99
+ for < ' a > T : Default + FromPyObject < ' a > + crate :: buffer:: Element ,
110
100
{
111
101
fn extract ( obj : & ' source PyAny ) -> PyResult < Self > {
112
- let mut array: core:: mem:: MaybeUninit < [ T ; N ] > = core:: mem:: MaybeUninit :: uninit ( ) ;
102
+ use crate :: { AsPyPointer , PyNativeType } ;
103
+ let mut array = [ T :: default ( ) ; N ] ;
113
104
// first try buffer protocol
114
105
if unsafe { crate :: ffi:: PyObject_CheckBuffer ( obj. as_ptr ( ) ) } == 1 {
115
106
if let Ok ( buf) = crate :: buffer:: PyBuffer :: get ( obj) {
116
107
if buf. dimensions ( ) == 1 && buf. copy_to_slice ( obj. py ( ) , & mut array) . is_ok ( ) {
117
108
buf. release ( obj. py ( ) ) ;
118
- // SAFETY: The array should be fully filled by `copy_to_slice`
119
- return Ok ( unsafe { array. assume_init ( ) } ) ;
109
+ return Ok ( array) ;
120
110
}
121
111
buf. release ( obj. py ( ) ) ;
122
112
}
123
113
}
124
114
// fall back to sequence protocol
125
115
_extract_sequence_into_slice ( obj, & mut array) ?;
126
- // SAFETY: The array should be fully filled by `_extract_sequence_into_slice`
127
- Ok ( unsafe { array. assume_init ( ) } )
116
+ Ok ( array)
128
117
}
129
118
}
130
119
@@ -135,102 +124,110 @@ where
135
124
{
136
125
let seq = <crate :: types:: PySequence as PyTryFrom >:: try_from ( obj) ?;
137
126
let expected_len = seq. len ( ) ? as usize ;
138
- let mut counter = 0 ;
139
- try_create_array ( & mut counter, |idx| {
127
+ array_try_from_fn ( |idx| {
140
128
seq. get_item ( idx as isize )
141
- . map_err ( |_| crate :: utils :: invalid_sequence_length ( expected_len, idx + 1 ) ) ?
129
+ . map_err ( |_| invalid_sequence_length ( expected_len, idx + 1 ) ) ?
142
130
. extract :: < T > ( )
143
131
} )
144
132
}
145
133
146
- fn _extract_sequence_into_slice < ' s , T > ( obj : & ' s PyAny , slice : & mut [ T ] ) -> PyResult < ( ) >
147
- where
148
- T : FromPyObject < ' s > ,
149
- {
150
- let seq = <crate :: types:: PySequence as PyTryFrom >:: try_from ( obj) ?;
151
- let expected_len = seq. len ( ) ? as usize ;
152
- if expected_len != slice. len ( ) {
153
- return Err ( crate :: utils:: invalid_sequence_length (
154
- expected_len,
155
- slice. len ( ) ,
156
- ) ) ;
157
- }
158
- for ( value, item) in slice. iter_mut ( ) . zip ( seq. iter ( ) ?) {
159
- * value = item?. extract :: < T > ( ) ?;
160
- }
161
- Ok ( ( ) )
162
- }
163
-
134
+ // TODO use std::array::try_from_fn, if that stabilises:
135
+ // (https://github.com/rust-lang/rust/pull/75644)
164
136
#[ cfg( min_const_generics) ]
165
- fn try_create_array < E , F , T , const N : usize > ( counter : & mut usize , mut cb : F ) -> Result < [ T ; N ] , E >
137
+ fn array_try_from_fn < E , F , T , const N : usize > ( mut cb : F ) -> Result < [ T ; N ] , E >
166
138
where
167
139
F : FnMut ( usize ) -> Result < T , E > ,
168
140
{
169
141
// Helper to safely create arrays since the standard library doesn't
170
142
// provide one yet. Shouldn't be necessary in the future.
171
- struct ArrayGuard < ' a , T , const N : usize > {
143
+ struct ArrayGuard < T , const N : usize > {
172
144
dst : * mut T ,
173
- initialized : & ' a mut usize ,
145
+ initialized : usize ,
174
146
}
175
147
176
- impl < T , const N : usize > Drop for ArrayGuard < ' _ , T , N > {
148
+ impl < T , const N : usize > Drop for ArrayGuard < T , N > {
177
149
fn drop ( & mut self ) {
178
- debug_assert ! ( * self . initialized <= N ) ;
179
- let initialized_part = core:: ptr:: slice_from_raw_parts_mut ( self . dst , * self . initialized ) ;
150
+ debug_assert ! ( self . initialized <= N ) ;
151
+ let initialized_part = core:: ptr:: slice_from_raw_parts_mut ( self . dst , self . initialized ) ;
180
152
unsafe {
181
153
core:: ptr:: drop_in_place ( initialized_part) ;
182
154
}
183
155
}
184
156
}
185
157
158
+ // [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
159
+ // APIs which would make this easier.
186
160
let mut array: core:: mem:: MaybeUninit < [ T ; N ] > = core:: mem:: MaybeUninit :: uninit ( ) ;
187
- let guard: ArrayGuard < T , N > = ArrayGuard {
161
+ let mut guard: ArrayGuard < T , N > = ArrayGuard {
188
162
dst : array. as_mut_ptr ( ) as _ ,
189
- initialized : counter ,
163
+ initialized : 0 ,
190
164
} ;
191
165
unsafe {
192
- for ( idx, value_ptr) in ( & mut * array. as_mut_ptr ( ) ) . iter_mut ( ) . enumerate ( ) {
193
- core:: ptr:: write ( value_ptr, cb ( idx) ?) ;
194
- * guard. initialized += 1 ;
166
+ let mut value_ptr = array. as_mut_ptr ( ) as * mut T ;
167
+ for i in 0 ..N {
168
+ core:: ptr:: write ( value_ptr, cb ( i) ?) ;
169
+ value_ptr = value_ptr. offset ( 1 ) ;
170
+ guard. initialized += 1 ;
195
171
}
196
172
core:: mem:: forget ( guard) ;
197
173
Ok ( array. assume_init ( ) )
198
174
}
199
175
}
200
176
177
+ fn _extract_sequence_into_slice < ' s , T > ( obj : & ' s PyAny , slice : & mut [ T ] ) -> PyResult < ( ) >
178
+ where
179
+ T : FromPyObject < ' s > ,
180
+ {
181
+ let seq = <crate :: types:: PySequence as PyTryFrom >:: try_from ( obj) ?;
182
+ let expected_len = seq. len ( ) ? as usize ;
183
+ if expected_len != slice. len ( ) {
184
+ return Err ( invalid_sequence_length ( expected_len, slice. len ( ) ) ) ;
185
+ }
186
+ for ( value, item) in slice. iter_mut ( ) . zip ( seq. iter ( ) ?) {
187
+ * value = item?. extract :: < T > ( ) ?;
188
+ }
189
+ Ok ( ( ) )
190
+ }
191
+
192
+ pub fn invalid_sequence_length ( expected : usize , actual : usize ) -> PyErr {
193
+ exceptions:: PyValueError :: new_err ( format ! (
194
+ "expected a sequence of length {} (got {})" ,
195
+ expected, actual
196
+ ) )
197
+ }
198
+
201
199
#[ cfg( test) ]
202
200
mod test {
203
201
use crate :: Python ;
204
202
#[ cfg( min_const_generics) ]
205
203
use std:: {
206
204
panic,
207
- sync:: { Arc , Mutex } ,
208
- thread:: sleep,
209
- time,
205
+ sync:: atomic:: { AtomicUsize , Ordering } ,
210
206
} ;
211
207
212
208
#[ cfg( min_const_generics) ]
213
209
#[ test]
214
- fn try_create_array ( ) {
215
- #[ allow( clippy:: mutex_atomic) ]
216
- let counter = Arc :: new ( Mutex :: new ( 0 ) ) ;
217
- let counter_unwind = Arc :: clone ( & counter) ;
210
+ fn array_try_from_fn ( ) {
211
+ static DROP_COUNTER : AtomicUsize = AtomicUsize :: new ( 0 ) ;
212
+ struct CountDrop ;
213
+ impl Drop for CountDrop {
214
+ fn drop ( & mut self ) {
215
+ DROP_COUNTER . fetch_add ( 1 , Ordering :: SeqCst ) ;
216
+ }
217
+ }
218
218
let _ = catch_unwind_silent ( move || {
219
- let mut locked = counter_unwind. lock ( ) . unwrap ( ) ;
220
- let _: Result < [ i32 ; 4 ] , _ > = super :: try_create_array ( & mut * locked, |idx| {
219
+ let _: Result < [ CountDrop ; 4 ] , ( ) > = super :: array_try_from_fn ( |idx| {
221
220
if idx == 2 {
222
221
panic ! ( "peek a boo" ) ;
223
222
}
224
- Ok :: < _ , ( ) > ( 1 )
223
+ Ok ( CountDrop )
225
224
} ) ;
226
225
} ) ;
227
- sleep ( time:: Duration :: from_secs ( 2 ) ) ;
228
- assert_eq ! ( * counter. lock( ) . unwrap_err( ) . into_inner( ) , 2 ) ;
226
+ assert_eq ! ( DROP_COUNTER . load( Ordering :: SeqCst ) , 2 ) ;
229
227
}
230
228
231
- #[ cfg( not( min_const_generics) ) ]
232
229
#[ test]
233
- fn test_extract_bytearray_to_array ( ) {
230
+ fn test_extract_small_bytearray_to_array ( ) {
234
231
let gil = Python :: acquire_gil ( ) ;
235
232
let py = gil. python ( ) ;
236
233
let v: [ u8 ; 3 ] = py
0 commit comments