Skip to content

Commit 909d94e

Browse files
committed
impl and use SequenceIndex for list.__delitem__
- removal of del_item - all del function impl on PyListRef - stepped deletion now loops only over the range insted of the whole list
1 parent c3ecf0e commit 909d94e

File tree

3 files changed

+188
-144
lines changed

3 files changed

+188
-144
lines changed

tests/snippets/list.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ def f(x):
190190
del x[-5:]
191191
assert x == [1, 2, 3, 4, 5, 6, 7, 8, 10]
192192

193+
x = list(range(12))
194+
del x[10:2:-2]
195+
assert x == [0,1,2,3,5,7,9,11]
196+
193197
def bad_del_1():
194198
del ['a', 'b']['a']
195199
assert_raises(TypeError, bad_del_1)

vm/src/obj/objlist.rs

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::cell::{Cell, RefCell};
22
use std::fmt;
33

4-
use std::ops::DerefMut;
4+
use std::ops::Range;
55

6-
use num_traits::ToPrimitive;
6+
use num_bigint::BigInt;
7+
use num_traits::{One, Signed, ToPrimitive, Zero};
78

89
use crate::function::{OptionalArg, PyFuncArgs};
910
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol};
@@ -13,9 +14,10 @@ use super::objbool;
1314
use super::objint;
1415
use super::objiter;
1516
use super::objsequence::{
16-
del_item, get_elements, get_elements_cell, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt,
17-
seq_mul, PySliceableSequence,
17+
get_elements, get_elements_cell, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul,
18+
PySliceableSequence, SequenceIndex,
1819
};
20+
use super::objslice::PySliceRef;
1921
use super::objtype;
2022
use crate::obj::objtype::PyClassRef;
2123

@@ -46,6 +48,54 @@ impl PyValue for PyList {
4648
}
4749
}
4850

51+
impl PyList {
52+
pub fn get_len(&self) -> usize {
53+
self.elements.borrow().len()
54+
}
55+
56+
pub fn get_pos(&self, p: i32) -> Option<usize> {
57+
// convert a (potentially negative) positon into a real index
58+
if p < 0 {
59+
if -p as usize > self.get_len() {
60+
None
61+
} else {
62+
Some(self.get_len() - ((-p) as usize))
63+
}
64+
} else if p as usize >= self.get_len() {
65+
None
66+
} else {
67+
Some(p as usize)
68+
}
69+
}
70+
71+
pub fn get_slice_pos(&self, slice_pos: &BigInt) -> usize {
72+
if let Some(pos) = slice_pos.to_i32() {
73+
if let Some(index) = self.get_pos(pos) {
74+
// within bounds
75+
return index;
76+
}
77+
}
78+
79+
if slice_pos.is_negative() {
80+
// slice past start bound, round to start
81+
0
82+
} else {
83+
// slice past end bound, round to end
84+
self.get_len()
85+
}
86+
}
87+
88+
pub fn get_slice_range(&self, start: &Option<BigInt>, stop: &Option<BigInt>) -> Range<usize> {
89+
let start = start.as_ref().map(|x| self.get_slice_pos(x)).unwrap_or(0);
90+
let stop = stop
91+
.as_ref()
92+
.map(|x| self.get_slice_pos(x))
93+
.unwrap_or_else(|| self.get_len());
94+
95+
start..stop
96+
}
97+
}
98+
4999
pub type PyListRef = PyRef<PyList>;
50100

51101
impl PyListRef {
@@ -313,9 +363,123 @@ impl PyListRef {
313363
}
314364
}
315365

316-
fn delitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
366+
fn delitem(self, subscript: SequenceIndex, vm: &VirtualMachine) -> PyResult {
367+
match subscript {
368+
SequenceIndex::Int(index) => self.delindex(index, vm),
369+
SequenceIndex::Slice(slice) => self.delslice(slice, vm),
370+
}
371+
}
372+
373+
fn delindex(self, index: i32, vm: &VirtualMachine) -> PyResult {
374+
if let Some(pos_index) = self.get_pos(index) {
375+
self.elements.borrow_mut().remove(pos_index);
376+
Ok(vm.get_none())
377+
} else {
378+
Err(vm.new_index_error("Index out of bounds!".to_string()))
379+
}
380+
}
381+
382+
fn delslice(self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult {
383+
let start = &slice.start;
384+
let stop = &slice.stop;
385+
let step = slice.step.clone().unwrap_or_else(BigInt::one);
386+
387+
if step.is_zero() {
388+
Err(vm.new_value_error("slice step cannot be zero".to_string()))
389+
} else if step.is_positive() {
390+
let range = self.get_slice_range(&start, &stop);
391+
if range.start < range.end {
392+
#[allow(clippy::range_plus_one)]
393+
match step.to_i32() {
394+
Some(1) => {
395+
self._del_slice(range);
396+
Ok(vm.get_none())
397+
}
398+
Some(num) => {
399+
self._del_stepped_slice(range, num as usize);
400+
Ok(vm.get_none())
401+
}
402+
None => {
403+
self._del_slice(range.start..range.start + 1);
404+
Ok(vm.get_none())
405+
}
406+
}
407+
} else {
408+
// no del to do
409+
Ok(vm.get_none())
410+
}
411+
} else {
412+
// calculate the range for the reverse slice, first the bounds needs to be made
413+
// exclusive around stop, the lower number
414+
let start = start.as_ref().map(|x| x + 1);
415+
let stop = stop.as_ref().map(|x| x + 1);
416+
let range = self.get_slice_range(&stop, &start);
417+
if range.start < range.end {
418+
match (-step).to_i32() {
419+
Some(1) => {
420+
self._del_slice(range);
421+
Ok(vm.get_none())
422+
}
423+
Some(num) => {
424+
self._del_stepped_slice_reverse(range, num as usize);
425+
Ok(vm.get_none())
426+
}
427+
None => {
428+
self._del_slice(range.end - 1..range.end);
429+
Ok(vm.get_none())
430+
}
431+
}
432+
} else {
433+
// no del to do
434+
Ok(vm.get_none())
435+
}
436+
}
437+
}
438+
439+
fn _del_slice(self, range: Range<usize>) {
440+
self.elements.borrow_mut().drain(range);
441+
}
442+
443+
fn _del_stepped_slice(self, range: Range<usize>, step: usize) {
444+
// no easy way to delete stepped indexes so here is what we'll do
445+
let mut deleted = 0;
446+
let mut elements = self.elements.borrow_mut();
447+
let mut indexes = range.clone().step_by(step).peekable();
448+
449+
for i in range.clone() {
450+
// is this an index to delete?
451+
if indexes.peek() == Some(&i) {
452+
// record and move on
453+
indexes.next();
454+
deleted += 1;
455+
} else {
456+
// swap towards front
457+
elements.swap(i - deleted, i);
458+
}
459+
}
460+
// then drain (the values to delete should now be contiguous at the end of the range)
461+
elements.drain((range.end - deleted)..range.end);
462+
}
463+
464+
fn _del_stepped_slice_reverse(self, range: Range<usize>, step: usize) {
465+
// no easy way to delete stepped indexes so here is what we'll do
466+
let mut deleted = 0;
317467
let mut elements = self.elements.borrow_mut();
318-
del_item(vm, self.as_object(), elements.deref_mut(), key)
468+
let mut indexes = range.clone().rev().step_by(step).peekable();
469+
470+
for i in range.clone().rev() {
471+
// is this an index to delete?
472+
if indexes.peek() == Some(&i) {
473+
// record and move on
474+
indexes.next();
475+
deleted += 1;
476+
} else {
477+
// swap towards back
478+
elements.swap(i + deleted, i);
479+
}
480+
}
481+
// then drain (the values to delete should now be contiguous at teh start of the range)
482+
elements.drain(range.start..(range.start + deleted));
319483
}
320484
}
321485

vm/src/obj/objsequence.rs

Lines changed: 14 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut, Range};
55
use num_bigint::BigInt;
66
use num_traits::{One, Signed, ToPrimitive, Zero};
77

8-
use crate::pyobject::{IdProtocol, PyObject, PyObjectRef, PyResult, TypeProtocol};
8+
use crate::pyobject::{IdProtocol, PyObject, PyObjectRef, PyResult, TryFromObject, TypeProtocol};
99
use crate::vm::VirtualMachine;
1010

1111
use super::objbool;
@@ -137,104 +137,21 @@ impl<T: Clone> PySliceableSequence for Vec<T> {
137137
}
138138
}
139139

140-
pub trait PySliceableSequenceMut: PySliceableSequence {
141-
//fn set_slice<T: PySliceableSequence>(&self, range: Range<usize>, seq: T) -> Self;
142-
//fn set_slice_reverse<T: PySliceableSequence>(&self, range: Range<usize>, seq: T) -> Self;
143-
//fn set_stepped_slice<T: PySliceableSequence>(&self, range: Range<usize>, step: usize, seq: T) -> Self;
144-
//fn set_stepped_slice_reverse<T: PySliceableSequence>(&self, range: Range<usize>, step: usize, seq: T) -> Self;
145-
146-
fn del_slice(&mut self, range: Range<usize>);
147-
fn del_stepped_slice(&mut self, range: Range<usize>, step: usize);
148-
fn del_index(&mut self, index: usize);
149-
150-
fn del_slice_items(&mut self, vm: &VirtualMachine, slice: &PySliceRef) -> PyResult {
151-
152-
let start = &slice.start;
153-
let stop = &slice.stop;
154-
155-
let step = slice.step.clone().unwrap_or_else(BigInt::one);
156-
157-
if step.is_zero() {
158-
Err(vm.new_value_error("slice step cannot be zero".to_string()))
159-
} else if step.is_positive() {
160-
let range = self.get_slice_range(start, stop);
161-
if range.start < range.end {
162-
#[allow(clippy::range_plus_one)]
163-
match step.to_i32() {
164-
Some(1) => {
165-
self.del_slice(range);
166-
Ok(vm.get_none())
167-
}
168-
Some(num) => {
169-
self.del_stepped_slice(range, num as usize);
170-
Ok(vm.get_none())
171-
}
172-
None => {
173-
self.del_slice(range.start..range.start + 1);
174-
Ok(vm.get_none())
175-
}
176-
}
177-
} else {
178-
// no del to do
179-
Ok(vm.get_none())
180-
}
181-
} else {
182-
// calculate the range for the reverse slice, first the bounds needs to be made
183-
// exclusive around stop, the lower number
184-
let start = start.as_ref().map(|x| x + 1);
185-
let stop = stop.as_ref().map(|x| x + 1);
186-
let range = self.get_slice_range(&stop, &start);
187-
if range.start < range.end {
188-
match (-step).to_i32() {
189-
Some(1) => {
190-
self.del_slice(range);
191-
Ok(vm.get_none())
192-
}
193-
Some(num) => {
194-
self.del_stepped_slice(range, num as usize);
195-
Ok(vm.get_none())
196-
}
197-
None => {
198-
self.del_slice(range.end - 1..range.end);
199-
Ok(vm.get_none())
200-
}
201-
}
202-
} else {
203-
// no del to do
204-
Ok(vm.get_none())
205-
}
206-
}
207-
208-
}
140+
pub enum SequenceIndex {
141+
Int(i32),
142+
Slice(PySliceRef),
209143
}
210144

211-
impl<T: Clone> PySliceableSequenceMut for Vec<T> {
212-
fn del_slice(&mut self, range: Range<usize>) {
213-
self.drain(range);
214-
}
215-
216-
fn del_stepped_slice(&mut self, range: Range<usize>, step: usize) {
217-
// no easy way to delete steped indexes so heres what we'll do
218-
let len = self.len();
219-
let mut deleted = 0;
220-
let mut indexes = range.step_by(step).peekable();
221-
for i in 0..len {
222-
// is this an index to delete?
223-
if indexes.peek() == Some(&i) {
224-
// record and move on
225-
indexes.next();
226-
deleted += 1;
227-
} else {
228-
// swap towards front
229-
self.swap(i - deleted, i);
230-
}
231-
}
232-
// then truncate the vec (the values to delete should now be the last elements and thus choped off)
233-
self.truncate(len - deleted);
234-
}
235-
236-
fn del_index(&mut self, index: usize) {
237-
self.remove(index);
145+
impl TryFromObject for SequenceIndex {
146+
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
147+
match_class!(obj,
148+
i @ PyInt => Ok(SequenceIndex::Int(i32::try_from_object(vm, i.into_object())?)),
149+
s @ PySlice => Ok(SequenceIndex::Slice(s)),
150+
obj => Err(vm.new_type_error(format!(
151+
"sequence indices be integers or slices, not {}",
152+
obj.class(),
153+
)))
154+
)
238155
}
239156
}
240157

@@ -284,47 +201,6 @@ pub fn get_item(
284201
}
285202
}
286203

287-
pub fn del_item<T: PySliceableSequenceMut>(
288-
vm: &VirtualMachine,
289-
sequence: &PyObjectRef,
290-
elements: &mut T,
291-
subscript: PyObjectRef,
292-
) -> PyResult {
293-
if let Some(i) = subscript.payload::<PyInt>() {
294-
return match i.as_bigint().to_i32() {
295-
Some(value) => {
296-
if let Some(pos_index) = elements.get_pos(value) {
297-
elements.del_index(pos_index);
298-
Ok(vm.get_none())
299-
} else {
300-
Err(vm.new_index_error("Index out of bounds!".to_string()))
301-
}
302-
}
303-
None => {
304-
Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string()))
305-
}
306-
};
307-
}
308-
309-
if subscript.payload::<PySlice>().is_some() {
310-
if sequence.payload::<PyList>().is_some() {
311-
if let Ok(slice) = subscript.downcast::<PySlice>() {
312-
elements.del_slice_items(vm, &slice)
313-
} else {
314-
panic!("PySlice is not a slice? this should not be")
315-
}
316-
317-
} else {
318-
panic!("sequence del_item called for non-mutable-sequence")
319-
}
320-
} else {
321-
Err(vm.new_type_error(format!(
322-
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
323-
sequence, subscript
324-
)))
325-
}
326-
}
327-
328204
pub fn seq_equal(
329205
vm: &VirtualMachine,
330206
zelf: &[PyObjectRef],

0 commit comments

Comments
 (0)