use crate::{ function::{Either, PyComparisonValue}, types::{richcompare_wrapper, PyComparisonOp, RichCompareFunc}, vm::VirtualMachine, AsObject, PyObject, PyObjectRef, PyResult, }; use itertools::Itertools; use optional::Optioned; use std::{collections::VecDeque, ops::Range}; pub trait ObjectSequenceOp<'a> { type Iter: ExactSizeIterator; fn iter(&'a self) -> Self::Iter; fn eq(&'a self, vm: &VirtualMachine, other: &'a Self) -> PyResult { let lhs = self.iter(); let rhs = other.iter(); if lhs.len() != rhs.len() { return Ok(false); } for (a, b) in lhs.zip_eq(rhs) { if !vm.identical_or_equal(a, b)? { return Ok(false); } } Ok(true) } fn cmp(&'a self, vm: &VirtualMachine, other: &'a Self, op: PyComparisonOp) -> PyResult { let less = match op { PyComparisonOp::Eq => return self.eq(vm, other), PyComparisonOp::Ne => return self.eq(vm, other).map(|eq| !eq), PyComparisonOp::Lt | PyComparisonOp::Le => true, PyComparisonOp::Gt | PyComparisonOp::Ge => false, }; let lhs = self.iter(); let rhs = other.iter(); let lhs_len = lhs.len(); let rhs_len = rhs.len(); for (a, b) in lhs.zip(rhs) { let ret = if less { vm.bool_seq_lt(a, b)? } else { vm.bool_seq_gt(a, b)? }; if let Some(v) = ret { return Ok(v); } } Ok(op.eval_ord(lhs_len.cmp(&rhs_len))) } } impl<'a> ObjectSequenceOp<'a> for [PyObjectRef] { type Iter = core::slice::Iter<'a, PyObjectRef>; fn iter(&'a self) -> Self::Iter { self.iter() } } impl<'a> ObjectSequenceOp<'a> for VecDeque { type Iter = std::collections::vec_deque::Iter<'a, PyObjectRef>; fn iter(&'a self) -> Self::Iter { self.iter() } } pub trait MutObjectSequenceOp<'a> { type Guard; fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef>; fn do_lock(&'a self) -> Self::Guard; fn mut_count(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult { let mut count = 0; self._mut_iter_equal_skeleton::<_, false>(vm, needle, 0..isize::MAX as usize, || { count += 1 })?; Ok(count) } fn mut_index_range( &'a self, vm: &VirtualMachine, needle: &PyObject, range: Range, ) -> PyResult> { self._mut_iter_equal_skeleton::<_, true>(vm, needle, range, || {}) } fn mut_index(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult> { self.mut_index_range(vm, needle, 0..isize::MAX as usize) } fn mut_contains(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult { self.mut_index(vm, needle).map(|x| x.is_some()) } fn _mut_iter_equal_skeleton( &'a self, vm: &VirtualMachine, needle: &PyObject, range: Range, mut f: F, ) -> PyResult> where F: FnMut(), { let needle_cls = needle.class(); let needle_cmp = needle_cls .mro_find_map(|cls| cls.slots.richcompare.load()) .unwrap(); let mut borrower = None; let mut i = range.start; let index = loop { if i >= range.end { break Optioned::::none(); } let guard = if let Some(x) = borrower.take() { x } else { self.do_lock() }; let elem = if let Some(x) = Self::do_get(i, &guard) { x } else { break Optioned::::none(); }; if elem.is(needle) { f(); if SHORT { break Optioned::::some(i); } borrower = Some(guard); } else { let elem_cls = elem.class(); let reverse_first = !elem_cls.is(&needle_cls) && elem_cls.fast_issubclass(&needle_cls); let eq = if reverse_first { let elem_cmp = elem_cls .mro_find_map(|cls| cls.slots.richcompare.load()) .unwrap(); drop(elem_cls); fn cmp( elem: &PyObject, needle: &PyObject, elem_cmp: RichCompareFunc, needle_cmp: RichCompareFunc, vm: &VirtualMachine, ) -> PyResult { match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? { Either::B(PyComparisonValue::Implemented(value)) => Ok(value), Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { obj.try_to_bool(vm) } _ => match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? { Either::B(PyComparisonValue::Implemented(value)) => Ok(value), Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { obj.try_to_bool(vm) } _ => Ok(false), }, } } if elem_cmp as usize == richcompare_wrapper as usize { let elem = elem.clone(); drop(guard); cmp(&elem, needle, elem_cmp, needle_cmp, vm)? } else { let eq = cmp(elem, needle, elem_cmp, needle_cmp, vm)?; borrower = Some(guard); eq } } else { match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? { Either::B(PyComparisonValue::Implemented(value)) => { drop(elem_cls); borrower = Some(guard); value } Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { drop(elem_cls); borrower = Some(guard); obj.try_to_bool(vm)? } _ => { let elem_cmp = elem_cls .mro_find_map(|cls| cls.slots.richcompare.load()) .unwrap(); drop(elem_cls); fn cmp( elem: &PyObject, needle: &PyObject, elem_cmp: RichCompareFunc, vm: &VirtualMachine, ) -> PyResult { match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? { Either::B(PyComparisonValue::Implemented(value)) => Ok(value), Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { obj.try_to_bool(vm) } _ => Ok(false), } } if elem_cmp as usize == richcompare_wrapper as usize { let elem = elem.clone(); drop(guard); cmp(&elem, needle, elem_cmp, vm)? } else { let eq = cmp(elem, needle, elem_cmp, vm)?; borrower = Some(guard); eq } } } }; if eq { f(); if SHORT { break Optioned::::some(i); } } } i += 1; }; Ok(index) } } pub trait SequenceOp where Self: AsRef<[T]>, { fn mul(&self, vm: &VirtualMachine, n: isize) -> PyResult> { let n = vm.check_repeat_or_overflow_error(self.as_ref().len(), n)?; let mut v = Vec::with_capacity(n * self.as_ref().len()); for _ in 0..n { v.extend_from_slice(self.as_ref()); } Ok(v) } } impl SequenceOp for [T] {} pub trait SequenceMutOp where Self: AsRef<[T]>, { fn as_vec_mut(&mut self) -> &mut Vec; fn imul(&mut self, vm: &VirtualMachine, n: isize) -> PyResult<()> { let n = vm.check_repeat_or_overflow_error(self.as_ref().len(), n)?; if n == 0 { self.as_vec_mut().clear(); } else if n != 1 { let mut sample = self.as_vec_mut().clone(); if n != 2 { self.as_vec_mut().reserve(sample.len() * (n - 1)); for _ in 0..n - 2 { self.as_vec_mut().extend_from_slice(&sample); } } self.as_vec_mut().append(&mut sample); } Ok(()) } } impl SequenceMutOp for Vec { fn as_vec_mut(&mut self) -> &mut Vec { self } }