@@ -4,19 +4,21 @@ use crate::common::cell::{
4
4
} ;
5
5
use crate :: function:: OptionalArg ;
6
6
use crate :: obj:: objbytes:: PyBytesRef ;
7
- use crate :: obj:: objsequence:: PySliceableSequence ;
7
+ use crate :: obj:: objsequence:: { PySliceableSequence , get_slice_range } ;
8
8
use crate :: obj:: objslice:: PySliceRef ;
9
9
use crate :: obj:: objstr:: PyStringRef ;
10
10
use crate :: obj:: objtype:: PyClassRef ;
11
11
use crate :: obj:: { objbool, objiter} ;
12
12
use crate :: pyobject:: {
13
13
BorrowValue , Either , IntoPyObject , PyArithmaticValue , PyClassImpl , PyComparisonValue ,
14
- PyIterable , PyObject , PyObjectRef , PyRef , PyResult , PyValue , TryFromObject ,
14
+ PyIterable , PyObject , PyObjectRef , PyRef , PyResult , PyValue , TryFromObject , TypeProtocol
15
15
} ;
16
16
use crate :: VirtualMachine ;
17
17
use crossbeam_utils:: atomic:: AtomicCell ;
18
18
use itertools:: Itertools ;
19
19
use std:: fmt;
20
+ use num_bigint:: BigInt ;
21
+ use num_traits:: { Zero , One , Signed , ToPrimitive } ;
20
22
21
23
struct ArrayTypeSpecifierError {
22
24
_priv : ( ) ,
@@ -33,7 +35,7 @@ impl fmt::Display for ArrayTypeSpecifierError {
33
35
34
36
macro_rules! def_array_enum {
35
37
( $( ( $n: ident, $t: ident, $c: literal) ) ,* $( , ) ?) => {
36
- #[ derive( Debug ) ]
38
+ #[ derive( Debug , Clone ) ]
37
39
enum ArrayContentType {
38
40
$( $n( Vec <$t>) , ) *
39
41
}
@@ -223,19 +225,115 @@ macro_rules! def_array_enum {
223
225
}
224
226
}
225
227
226
- fn setitem( & mut self , needle: Either <isize , PySliceRef >, value: PyObjectRef , vm: & VirtualMachine ) -> PyResult <( ) > {
227
- match needle {
228
- Either :: A ( i) => {
229
- let i = self . idx( i, "array assignment" , vm) ?;
230
- match self {
231
- $( ArrayContentType :: $n( v) => { v[ i] = TryFromObject :: try_from_object( vm, value) ? } , ) *
228
+ fn setitem_by_slice( & mut self , slice: PySliceRef , items: ArrayContentType , vm: & VirtualMachine ) -> PyResult <( ) > {
229
+ let start = slice. start_index( vm) ?;
230
+ let stop = slice. stop_index( vm) ?;
231
+ let step = slice. step_index( vm) ?. unwrap_or_else( BigInt :: one) ;
232
+
233
+ if step. is_zero( ) {
234
+ return Err ( vm. new_value_error( "slice step cannot be zero" . to_owned( ) ) ) ;
235
+ }
236
+
237
+ match self {
238
+ $( ArrayContentType :: $n( elements) => if let ArrayContentType :: $n( items) = items {
239
+ if step == BigInt :: one( ) {
240
+ let range = get_slice_range( & start, & stop, elements. len( ) ) ;
241
+ let range = if range. end < range. start {
242
+ range. start..range. start
243
+ } else {
244
+ range
245
+ } ;
246
+ elements. splice( range, items) ;
247
+ return Ok ( ( ) ) ;
232
248
}
233
- Ok ( ( ) )
234
- }
235
- Either :: B ( _slice) => Err ( vm. new_not_implemented_error( "array slice is not implemented" . to_owned( ) ) ) ,
249
+
250
+ let ( start, stop, step, is_negative_step) = if step. is_negative( ) {
251
+ (
252
+ stop. map( |x| if x == -BigInt :: one( ) {
253
+ elements. len( ) + BigInt :: one( )
254
+ } else {
255
+ x + 1
256
+ } ) ,
257
+ start. map( |x| if x == -BigInt :: one( ) {
258
+ BigInt :: from( elements. len( ) )
259
+ } else {
260
+ x + 1
261
+ } ) ,
262
+ -step,
263
+ true
264
+ )
265
+ } else {
266
+ ( start, stop, step, false )
267
+ } ;
268
+
269
+ let range = get_slice_range( & start, & stop, elements. len( ) ) ;
270
+ let range = if range. end < range. start {
271
+ range. start..range. start
272
+ } else {
273
+ range
274
+ } ;
275
+
276
+ // step is not negative here
277
+ if let Some ( step) = step. to_usize( ) {
278
+ let slicelen = if range. end > range. start {
279
+ ( range. end - range. start - 1 ) / step + 1
280
+ } else {
281
+ 0
282
+ } ;
283
+
284
+ if slicelen == items. len( ) {
285
+ if is_negative_step {
286
+ for ( i, item) in range. rev( ) . step_by( step) . zip( items) {
287
+ elements[ i] = item;
288
+ }
289
+ } else {
290
+ for ( i, item) in range. step_by( step) . zip( items) {
291
+ elements[ i] = item;
292
+ }
293
+ }
294
+ Ok ( ( ) )
295
+ } else {
296
+ Err ( vm. new_value_error( format!(
297
+ "attempt to assign sequence of size {} to extended slice of size {}" ,
298
+ items. len( ) , slicelen
299
+ ) ) )
300
+ }
301
+ } else {
302
+ // edge case, step is too big for usize
303
+ // same behaviour as CPython
304
+ let slicelen = if range. start < range. end { 1 } else { 0 } ;
305
+ if match items. len( ) {
306
+ 0 => slicelen == 0 ,
307
+ 1 => {
308
+ elements[
309
+ if is_negative_step { range. end - 1 } else { range. start }
310
+ ] = items[ 0 ] ;
311
+ true
312
+ } ,
313
+ _ => false ,
314
+ } {
315
+ Ok ( ( ) )
316
+ } else {
317
+ Err ( vm. new_value_error( format!(
318
+ "attempt to assign sequence of size {} to extended slice of size {}" ,
319
+ items. len( ) , slicelen
320
+ ) ) )
321
+ }
322
+ }
323
+ } else {
324
+ Err ( vm. new_type_error( "bad argument type for built-in operation" . to_owned( ) ) )
325
+ } , ) *
236
326
}
237
327
}
238
328
329
+ fn setitem_by_idx( & mut self , i: isize , value: PyObjectRef , vm: & VirtualMachine ) -> PyResult <( ) > {
330
+ let i = self . idx( i, "array assignment" , vm) ?;
331
+ match self {
332
+ $( ArrayContentType :: $n( v) => { v[ i] = TryFromObject :: try_from_object( vm, value) ? } , ) *
333
+ }
334
+ Ok ( ( ) )
335
+ }
336
+
239
337
fn iter<' a>( & ' a self , vm: & ' a VirtualMachine ) -> impl Iterator <Item = PyObjectRef > + ' a {
240
338
let mut i = 0 ;
241
339
std:: iter:: from_fn( move || {
@@ -440,7 +538,23 @@ impl PyArray {
440
538
obj : PyObjectRef ,
441
539
vm : & VirtualMachine ,
442
540
) -> PyResult < ( ) > {
443
- self . borrow_value_mut ( ) . setitem ( needle, obj, vm)
541
+ match needle {
542
+ Either :: A ( i) => self . borrow_value_mut ( ) . setitem_by_idx ( i, obj, vm) ,
543
+ Either :: B ( slice) => {
544
+ let items = match obj. payload :: < PyArray > ( ) {
545
+ // TODO: is there a way to check ref equality between [self] and [obj]
546
+ // so we can do clone() only when they are referring the same PyObject
547
+ Some ( array) => array. borrow_value ( ) . clone ( ) ,
548
+ None => {
549
+ return Err ( vm. new_type_error ( format ! (
550
+ "can only assign array (not \" {}\" ) to array slice" ,
551
+ obj. class( ) . name
552
+ ) ) ) ;
553
+ }
554
+ } ;
555
+ self . borrow_value_mut ( ) . setitem_by_slice ( slice, items, vm)
556
+ }
557
+ }
444
558
}
445
559
446
560
#[ pymethod( name = "__eq__" ) ]
0 commit comments