diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 36fef168f..0597863e7 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -187,6 +187,11 @@ impl PyByteArrayRef { self.inner.borrow_mut().setitem(needle, value, vm) } + #[pymethod(name = "__delitem__")] + fn delitem(self, needle: Either, vm: &VirtualMachine) -> PyResult<()> { + self.inner.borrow_mut().delitem(needle, vm) + } + #[pymethod(name = "isalnum")] fn isalnum(self, vm: &VirtualMachine) -> bool { self.inner.borrow().isalnum(vm) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 4fcf491ca..09db11ed5 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -1,9 +1,9 @@ use std::convert::TryFrom; use std::ops::Range; -use num_bigint::BigInt; +use num_bigint::{BigInt, ToBigInt}; use num_integer::Integer; -use num_traits::ToPrimitive; +use num_traits::{One, Signed, ToPrimitive, Zero}; use super::objbytearray::{PyByteArray, PyByteArrayRef}; use super::objbytes::{PyBytes, PyBytesRef}; @@ -472,6 +472,140 @@ impl PyByteInner { } } + pub fn delitem( + &mut self, + needle: Either, + vm: &VirtualMachine, + ) -> PyResult<()> { + match needle { + Either::A(int) => { + if let Some(idx) = self.elements.get_pos(int) { + self.elements.remove(idx); + Ok(()) + } else { + Err(vm.new_index_error("index out of range".to_string())) + } + } + Either::B(slice) => self.delslice(slice, vm), + } + } + + // TODO: deduplicate this with the code in objlist + fn delslice(&mut self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult<()> { + let start = slice.start_index(vm)?; + let stop = slice.stop_index(vm)?; + let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one); + + if step.is_zero() { + Err(vm.new_value_error("slice step cannot be zero".to_string())) + } else if step.is_positive() { + let range = self.elements.get_slice_range(&start, &stop); + if range.start < range.end { + #[allow(clippy::range_plus_one)] + match step.to_i32() { + Some(1) => { + self._del_slice(range); + Ok(()) + } + Some(num) => { + self._del_stepped_slice(range, num as usize); + Ok(()) + } + None => { + self._del_slice(range.start..range.start + 1); + Ok(()) + } + } + } else { + // no del to do + Ok(()) + } + } else { + // calculate the range for the reverse slice, first the bounds needs to be made + // exclusive around stop, the lower number + let start = start.as_ref().map(|x| { + if *x == (-1).to_bigint().unwrap() { + self.elements.len() + BigInt::one() //.to_bigint().unwrap() + } else { + x + 1 + } + }); + let stop = stop.as_ref().map(|x| { + if *x == (-1).to_bigint().unwrap() { + self.elements.len().to_bigint().unwrap() + } else { + x + 1 + } + }); + let range = self.elements.get_slice_range(&stop, &start); + if range.start < range.end { + match (-step).to_i32() { + Some(1) => { + self._del_slice(range); + Ok(()) + } + Some(num) => { + self._del_stepped_slice_reverse(range, num as usize); + Ok(()) + } + None => { + self._del_slice(range.end - 1..range.end); + Ok(()) + } + } + } else { + // no del to do + Ok(()) + } + } + } + + fn _del_slice(&mut self, range: Range) { + self.elements.drain(range); + } + + fn _del_stepped_slice(&mut self, range: Range, step: usize) { + // no easy way to delete stepped indexes so here is what we'll do + let mut deleted = 0; + let elements = &mut self.elements; + let mut indexes = range.clone().step_by(step).peekable(); + + for i in range.clone() { + // is this an index to delete? + if indexes.peek() == Some(&i) { + // record and move on + indexes.next(); + deleted += 1; + } else { + // swap towards front + elements.swap(i - deleted, i); + } + } + // then drain (the values to delete should now be contiguous at the end of the range) + elements.drain((range.end - deleted)..range.end); + } + + fn _del_stepped_slice_reverse(&mut self, range: Range, step: usize) { + // no easy way to delete stepped indexes so here is what we'll do + let mut deleted = 0; + let elements = &mut self.elements; + let mut indexes = range.clone().rev().step_by(step).peekable(); + + for i in range.clone().rev() { + // is this an index to delete? + if indexes.peek() == Some(&i) { + // record and move on + indexes.next(); + deleted += 1; + } else { + // swap towards back + elements.swap(i + deleted, i); + } + } + // then drain (the values to delete should now be contiguous at teh start of the range) + elements.drain(range.start..(range.start + deleted)); + } + pub fn isalnum(&self, _vm: &VirtualMachine) -> bool { !self.elements.is_empty() && self diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 2c50a035f..5e88d55cb 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -584,23 +584,23 @@ impl PyListRef { } } - fn delitem(self, subscript: SequenceIndex, vm: &VirtualMachine) -> PyResult { + fn delitem(self, subscript: SequenceIndex, vm: &VirtualMachine) -> PyResult<()> { match subscript { SequenceIndex::Int(index) => self.delindex(index, vm), SequenceIndex::Slice(slice) => self.delslice(slice, vm), } } - fn delindex(self, index: i32, vm: &VirtualMachine) -> PyResult { + fn delindex(self, index: i32, vm: &VirtualMachine) -> PyResult<()> { if let Some(pos_index) = self.get_pos(index) { self.elements.borrow_mut().remove(pos_index); - Ok(vm.get_none()) + Ok(()) } else { Err(vm.new_index_error("Index out of bounds!".to_string())) } } - fn delslice(self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult { + fn delslice(self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult<()> { let start = slice.start_index(vm)?; let stop = slice.stop_index(vm)?; let step = slice.step_index(vm)?.unwrap_or_else(BigInt::one); @@ -614,20 +614,20 @@ impl PyListRef { match step.to_i32() { Some(1) => { self._del_slice(range); - Ok(vm.get_none()) + Ok(()) } Some(num) => { self._del_stepped_slice(range, num as usize); - Ok(vm.get_none()) + Ok(()) } None => { self._del_slice(range.start..range.start + 1); - Ok(vm.get_none()) + Ok(()) } } } else { // no del to do - Ok(vm.get_none()) + Ok(()) } } else { // calculate the range for the reverse slice, first the bounds needs to be made @@ -651,20 +651,20 @@ impl PyListRef { match (-step).to_i32() { Some(1) => { self._del_slice(range); - Ok(vm.get_none()) + Ok(()) } Some(num) => { self._del_stepped_slice_reverse(range, num as usize); - Ok(vm.get_none()) + Ok(()) } None => { self._del_slice(range.end - 1..range.end); - Ok(vm.get_none()) + Ok(()) } } } else { // no del to do - Ok(vm.get_none()) + Ok(()) } } }