@@ -5,18 +5,21 @@ use crate::common::cell::{
5
5
use crate :: function:: OptionalArg ;
6
6
use crate :: obj:: objbytes:: PyBytesRef ;
7
7
use crate :: obj:: objfloat:: try_float;
8
- use crate :: obj:: objsequence:: PySliceableSequence ;
8
+ use crate :: obj:: objsequence:: { get_slice_range , PySliceableSequence } ;
9
9
use crate :: obj:: objslice:: PySliceRef ;
10
10
use crate :: obj:: objstr:: PyStringRef ;
11
11
use crate :: obj:: objtype:: PyClassRef ;
12
12
use crate :: obj:: { objbool, objiter} ;
13
13
use crate :: pyobject:: {
14
- BorrowValue , Either , IntoPyObject , PyArithmaticValue , PyClassImpl , PyComparisonValue ,
15
- PyIterable , PyObject , PyObjectRef , PyRef , PyResult , PyValue , TryFromObject , TypeProtocol ,
14
+ BorrowValue , Either , IdProtocol , IntoPyObject , PyArithmaticValue , PyClassImpl ,
15
+ PyComparisonValue , PyIterable , PyObject , PyObjectRef , PyRef , PyResult , PyValue , TryFromObject ,
16
+ TypeProtocol ,
16
17
} ;
17
18
use crate :: VirtualMachine ;
18
19
use crossbeam_utils:: atomic:: AtomicCell ;
19
20
use itertools:: Itertools ;
21
+ use num_bigint:: BigInt ;
22
+ use num_traits:: { One , Signed , ToPrimitive , Zero } ;
20
23
use std:: fmt;
21
24
22
25
struct ArrayTypeSpecifierError {
@@ -34,7 +37,7 @@ impl fmt::Display for ArrayTypeSpecifierError {
34
37
35
38
macro_rules! def_array_enum {
36
39
( $( ( $n: ident, $t: ident, $c: literal) ) ,* $( , ) ?) => {
37
- #[ derive( Debug ) ]
40
+ #[ derive( Debug , Clone ) ]
38
41
enum ArrayContentType {
39
42
$( $n( Vec <$t>) , ) *
40
43
}
@@ -230,20 +233,130 @@ macro_rules! def_array_enum {
230
233
Either :: B ( slice) => self . getitem_by_slice( slice, vm) ,
231
234
}
232
235
}
236
+
237
+ fn setitem_by_slice( & mut self , slice: PySliceRef , items: & ArrayContentType , vm: & VirtualMachine ) -> PyResult <( ) > {
238
+ let start = slice. start_index( vm) ?;
239
+ let stop = slice. stop_index( vm) ?;
240
+ let step = slice. step_index( vm) ?. unwrap_or_else( BigInt :: one) ;
241
+
242
+ if step. is_zero( ) {
243
+ return Err ( vm. new_value_error( "slice step cannot be zero" . to_owned( ) ) ) ;
244
+ }
233
245
234
- fn setitem( & mut self , needle: Either <isize , PySliceRef >, value: PyObjectRef , vm: & VirtualMachine ) -> PyResult <( ) > {
235
- match needle {
236
- Either :: A ( i) => {
237
- let i = self . idx( i, "array assignment" , vm) ?;
238
- match self {
239
- $( ArrayContentType :: $n( v) => { v[ i] = $t:: try_into_from_object( vm, value) ? } , ) *
246
+ match self {
247
+ $( ArrayContentType :: $n( elements) => if let ArrayContentType :: $n( items) = items {
248
+ if step == BigInt :: one( ) {
249
+ let range = get_slice_range( & start, & stop, elements. len( ) ) ;
250
+ let range = if range. end < range. start {
251
+ range. start..range. start
252
+ } else {
253
+ range
254
+ } ;
255
+ elements. splice( range, items. iter( ) . cloned( ) ) ;
256
+ return Ok ( ( ) ) ;
240
257
}
241
- Ok ( ( ) )
242
- }
243
- Either :: B ( _slice) => Err ( vm. new_not_implemented_error( "array slice is not implemented" . to_owned( ) ) ) ,
258
+
259
+ let ( start, stop, step, is_negative_step) = if step. is_negative( ) {
260
+ (
261
+ stop. map( |x| if x == -BigInt :: one( ) {
262
+ elements. len( ) + BigInt :: one( )
263
+ } else {
264
+ x + 1
265
+ } ) ,
266
+ start. map( |x| if x == -BigInt :: one( ) {
267
+ BigInt :: from( elements. len( ) )
268
+ } else {
269
+ x + 1
270
+ } ) ,
271
+ -step,
272
+ true
273
+ )
274
+ } else {
275
+ ( start, stop, step, false )
276
+ } ;
277
+
278
+ let range = get_slice_range( & start, & stop, elements. len( ) ) ;
279
+ let range = if range. end < range. start {
280
+ range. start..range. start
281
+ } else {
282
+ range
283
+ } ;
284
+
285
+ // step is not negative here
286
+ if let Some ( step) = step. to_usize( ) {
287
+ let slicelen = if range. end > range. start {
288
+ ( range. end - range. start - 1 ) / step + 1
289
+ } else {
290
+ 0
291
+ } ;
292
+
293
+ if slicelen == items. len( ) {
294
+ if is_negative_step {
295
+ for ( i, & item) in range. rev( ) . step_by( step) . zip( items) {
296
+ elements[ i] = item;
297
+ }
298
+ } else {
299
+ for ( i, & item) in range. step_by( step) . zip( items) {
300
+ elements[ i] = item;
301
+ }
302
+ }
303
+ Ok ( ( ) )
304
+ } else {
305
+ Err ( vm. new_value_error( format!(
306
+ "attempt to assign sequence of size {} to extended slice of size {}" ,
307
+ items. len( ) , slicelen
308
+ ) ) )
309
+ }
310
+ } else {
311
+ // edge case, step is too big for usize
312
+ // same behaviour as CPython
313
+ let slicelen = if range. start < range. end { 1 } else { 0 } ;
314
+ if match items. len( ) {
315
+ 0 => slicelen == 0 ,
316
+ 1 => {
317
+ elements[
318
+ if is_negative_step { range. end - 1 } else { range. start }
319
+ ] = items[ 0 ] ;
320
+ true
321
+ } ,
322
+ _ => false ,
323
+ } {
324
+ Ok ( ( ) )
325
+ } else {
326
+ Err ( vm. new_value_error( format!(
327
+ "attempt to assign sequence of size {} to extended slice of size {}" ,
328
+ items. len( ) , slicelen
329
+ ) ) )
330
+ }
331
+ }
332
+ } else {
333
+ Err ( vm. new_type_error( "bad argument type for built-in operation" . to_owned( ) ) )
334
+ } , ) *
244
335
}
245
336
}
246
337
338
+ fn setitem_by_idx( & mut self , i: isize , value: PyObjectRef , vm: & VirtualMachine ) -> PyResult <( ) > {
339
+ let i = self . idx( i, "array assignment" , vm) ?;
340
+ match self {
341
+ $( ArrayContentType :: $n( v) => { v[ i] = TryFromObject :: try_from_object( vm, value) ? } , ) *
342
+ }
343
+ Ok ( ( ) )
344
+ }
345
+
346
+ fn repr( & self , _vm: & VirtualMachine ) -> PyResult <String > {
347
+ // we don't need ReprGuard here
348
+ let s = match self {
349
+ $( ArrayContentType :: $n( v) => {
350
+ if v. is_empty( ) {
351
+ format!( "array('{}')" , $c)
352
+ } else {
353
+ format!( "array('{}', [{}])" , $c, v. iter( ) . format( ", " ) )
354
+ }
355
+ } ) *
356
+ } ;
357
+ Ok ( s)
358
+ }
359
+
247
360
fn iter<' a>( & ' a self , vm: & ' a VirtualMachine ) -> impl Iterator <Item = PyObjectRef > + ' a {
248
361
let mut i = 0 ;
249
362
std:: iter:: from_fn( move || {
@@ -479,12 +592,41 @@ impl PyArray {
479
592
480
593
#[ pymethod( magic) ]
481
594
fn setitem (
482
- & self ,
595
+ zelf : PyRef < Self > ,
483
596
needle : Either < isize , PySliceRef > ,
484
597
obj : PyObjectRef ,
485
598
vm : & VirtualMachine ,
486
599
) -> PyResult < ( ) > {
487
- self . borrow_value_mut ( ) . setitem ( needle, obj, vm)
600
+ match needle {
601
+ Either :: A ( i) => zelf. borrow_value_mut ( ) . setitem_by_idx ( i, obj, vm) ,
602
+ Either :: B ( slice) => {
603
+ let cloned;
604
+ let guard;
605
+ let items = if zelf. is ( & obj) {
606
+ cloned = zelf. borrow_value ( ) . clone ( ) ;
607
+ & cloned
608
+ } else {
609
+ match obj. payload :: < PyArray > ( ) {
610
+ Some ( array) => {
611
+ guard = array. borrow_value ( ) ;
612
+ & * guard
613
+ }
614
+ None => {
615
+ return Err ( vm. new_type_error ( format ! (
616
+ "can only assign array (not \" {}\" ) to array slice" ,
617
+ obj. class( ) . name
618
+ ) ) ) ;
619
+ }
620
+ }
621
+ } ;
622
+ zelf. borrow_value_mut ( ) . setitem_by_slice ( slice, items, vm)
623
+ }
624
+ }
625
+ }
626
+
627
+ #[ pymethod( name = "__repr__" ) ]
628
+ fn repr ( zelf : PyRef < Self > , vm : & VirtualMachine ) -> PyResult < String > {
629
+ zelf. borrow_value ( ) . repr ( vm)
488
630
}
489
631
490
632
#[ pymethod( name = "__eq__" ) ]
0 commit comments