From d513aea7d21f00b2132b1f7d548900e774381eb8 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Thu, 21 Jul 2022 06:27:21 +0900
Subject: [PATCH] MutObjectSequenceOp find using Iteartor trait

to isolate control flow code from next() operation
---
 vm/src/builtins/list.rs      |   4 +-
 vm/src/sequence.rs           | 113 +++++++++++++++++++++--------------
 vm/src/stdlib/collections.rs |   4 +-
 3 files changed, 72 insertions(+), 49 deletions(-)

diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs
index f2944942ee..d03d51b161 100644
--- a/vm/src/builtins/list.rs
+++ b/vm/src/builtins/list.rs
@@ -271,7 +271,7 @@ impl PyList {
     ) -> PyResult<usize> {
         let (start, stop) = range.saturate(self.len(), vm)?;
         let index = self.mut_index_range(vm, &needle, start..stop)?;
-        if let Some(index) = index.into() {
+        if let Some(index) = index {
             Ok(index)
         } else {
             Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
@@ -298,7 +298,7 @@ impl PyList {
     fn remove(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
         let index = self.mut_index(vm, &needle)?;
 
-        if let Some(index) = index.into() {
+        if let Some(index) = index {
             // defer delete out of borrow
             let is_inside_range = index < self.borrow_vec().len();
             Ok(is_inside_range.then(|| self.borrow_vec_mut().remove(index)))
diff --git a/vm/src/sequence.rs b/vm/src/sequence.rs
index 2ed07b62c7..4297d1ffed 100644
--- a/vm/src/sequence.rs
+++ b/vm/src/sequence.rs
@@ -5,17 +5,18 @@ use crate::{
 use optional::Optioned;
 use std::ops::Range;
 
-pub trait MutObjectSequenceOp<'a> {
-    type Guard;
+pub trait MutObjectSequenceOp<'a>: Sized {
+    type Guard: Sized;
 
     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<usize> {
+        let mut find_iter = self._mut_find(vm, needle, 0..isize::MAX as usize);
         let mut count = 0;
-        self._mut_iter_equal_skeleton::<_, false>(vm, needle, 0..isize::MAX as usize, || {
-            count += 1
-        })?;
+        while (find_iter.next().transpose()?).is_some() {
+            count += 1;
+        }
         Ok(count)
     }
 
@@ -24,11 +25,11 @@ pub trait MutObjectSequenceOp<'a> {
         vm: &VirtualMachine,
         needle: &PyObject,
         range: Range<usize>,
-    ) -> PyResult<Optioned<usize>> {
-        self._mut_iter_equal_skeleton::<_, true>(vm, needle, range, || {})
+    ) -> PyResult<Option<usize>> {
+        self._mut_find(vm, needle, range).next().transpose()
     }
 
-    fn mut_index(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult<Optioned<usize>> {
+    fn mut_index(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult<Option<usize>> {
         self.mut_index_range(vm, needle, 0..isize::MAX as usize)
     }
 
@@ -36,56 +37,78 @@ pub trait MutObjectSequenceOp<'a> {
         self.mut_index(vm, needle).map(|x| x.is_some())
     }
 
-    fn _mut_iter_equal_skeleton<F, const SHORT: bool>(
+    fn _mut_find<'b, 'vm>(
         &'a self,
-        vm: &VirtualMachine,
-        needle: &PyObject,
+        vm: &'vm VirtualMachine,
+        needle: &'b PyObject,
         range: Range<usize>,
-        mut f: F,
-    ) -> PyResult<Optioned<usize>>
-    where
-        F: FnMut(),
-    {
-        let mut borrower = None;
-        let mut i = range.start;
-
-        let index = loop {
-            if i >= range.end {
-                break Optioned::<usize>::none();
-            }
-            let guard = if let Some(x) = borrower.take() {
-                x
-            } else {
-                self.do_lock()
-            };
+    ) -> MutObjectSequenceFindIter<'a, 'b, 'vm, Self> {
+        MutObjectSequenceFindIter {
+            seq: self,
+            needle,
+            pos: range.start,
+            end: range.end,
+            guard: None,
+            vm,
+        }
+    }
+}
+
+pub struct MutObjectSequenceFindIter<'a, 'b, 'vm, S: MutObjectSequenceOp<'a>> {
+    // mutable fields
+    pos: usize,
+    guard: Option<S::Guard>,
+    // immutable fields
+    seq: &'a S,
+    needle: &'b PyObject,
+    end: usize,
+    vm: &'vm VirtualMachine,
+}
 
-            let elem = if let Some(x) = Self::do_get(i, &guard) {
+impl<'a, 'b, 'vm, S: MutObjectSequenceOp<'a>> MutObjectSequenceFindIter<'a, 'b, 'vm, S> {
+    #[inline]
+    fn next_impl(&mut self) -> PyResult<Optioned<usize>> {
+        loop {
+            if self.pos >= self.end {
+                return Ok(Optioned::none());
+            }
+            let guard = self.guard.take().unwrap_or_else(|| self.seq.do_lock());
+            let elem = if let Some(x) = S::do_get(self.pos, &guard) {
                 x
             } else {
-                break Optioned::<usize>::none();
+                return Ok(Optioned::none());
             };
 
-            if elem.is(needle) {
-                f();
-                if SHORT {
-                    break Optioned::<usize>::some(i);
-                }
-                borrower = Some(guard);
+            let is_equal = if elem.is(self.needle) {
+                self.guard = Some(guard);
+                true
             } else {
                 let elem = elem.clone();
                 drop(guard);
+                elem.rich_compare_bool(self.needle, PyComparisonOp::Eq, self.vm)?
+            };
 
-                if elem.rich_compare_bool(needle, PyComparisonOp::Eq, vm)? {
-                    f();
-                    if SHORT {
-                        break Optioned::<usize>::some(i);
-                    }
-                }
+            if is_equal {
+                break;
             }
-            i += 1;
-        };
 
-        Ok(index)
+            self.pos += 1;
+        }
+
+        let i = self.pos;
+        self.pos += 1;
+        Ok(Optioned::some(i))
+    }
+}
+
+impl<'a, 'b, 'vm, S: MutObjectSequenceOp<'a>> Iterator
+    for MutObjectSequenceFindIter<'a, 'b, 'vm, S>
+{
+    type Item = PyResult<usize>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.next_impl().map(Into::into).transpose()
     }
 }
 
diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs
index a3cbdab996..32abb10e5a 100644
--- a/vm/src/stdlib/collections.rs
+++ b/vm/src/stdlib/collections.rs
@@ -167,7 +167,7 @@ mod _collections {
             let index = self.mut_index_range(vm, &needle, start..stop)?;
             if start_state != self.state.load() {
                 Err(vm.new_runtime_error("deque mutated during iteration".to_owned()))
-            } else if let Some(index) = index.into() {
+            } else if let Some(index) = index {
                 Ok(index)
             } else {
                 Err(vm.new_value_error(
@@ -228,7 +228,7 @@ mod _collections {
 
             if start_state != self.state.load() {
                 Err(vm.new_index_error("deque mutated during remove().".to_owned()))
-            } else if let Some(index) = index.into() {
+            } else if let Some(index) = index {
                 let mut deque = self.borrow_deque_mut();
                 self.state.fetch_add(1);
                 Ok(deque.remove(index).unwrap())