changeset 44297:cf1f8660e568

rust-dirstatemap: add `NonNormalEntries` class This fix introduces the same encapsulation as the `copymap`. There is no easy way of doing this any better for now. `hg up -r null && time HGRCPATH= HGMODULEPOLICY=rust+c hg up tip` on Mozilla Central, (not super recent, but it doesn't matter): Before: 7:44,08 total After: 1:03,23 total Pretty brutal regression! Differential Revision: https://phab.mercurial-scm.org/D8049
author Raphaël Gomès <rgomes@octobus.net>
date Mon, 10 Feb 2020 21:54:12 +0100
parents 5830efce522b
children 0e8b28fb751b
files mercurial/dirstate.py rust/hg-cpython/src/dirstate.rs rust/hg-cpython/src/dirstate/dirstate_map.rs rust/hg-cpython/src/dirstate/non_normal_entries.rs
diffstat 4 files changed, 128 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/dirstate.py	Sun Feb 09 16:18:26 2020 -0500
+++ b/mercurial/dirstate.py	Mon Feb 10 21:54:12 2020 +0100
@@ -1846,12 +1846,12 @@
 
         @property
         def nonnormalset(self):
-            nonnorm, otherparents = self._rustmap.nonnormalentries()
+            nonnorm = self._rustmap.non_normal_entries()
             return nonnorm
 
         @propertycache
         def otherparentset(self):
-            nonnorm, otherparents = self._rustmap.nonnormalentries()
+            otherparents = self._rustmap.other_parent_entries()
             return otherparents
 
         @propertycache
--- a/rust/hg-cpython/src/dirstate.rs	Sun Feb 09 16:18:26 2020 -0500
+++ b/rust/hg-cpython/src/dirstate.rs	Mon Feb 10 21:54:12 2020 +0100
@@ -12,6 +12,7 @@
 mod copymap;
 mod dirs_multiset;
 mod dirstate_map;
+mod non_normal_entries;
 mod status;
 use crate::dirstate::{
     dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs	Sun Feb 09 16:18:26 2020 -0500
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs	Mon Feb 10 21:54:12 2020 +0100
@@ -13,12 +13,14 @@
 use std::time::Duration;
 
 use cpython::{
-    exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
-    PyResult, PyTuple, Python, PythonObject, ToPyObject, UnsafePyLeaked,
+    exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
+    PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
+    UnsafePyLeaked,
 };
 
 use crate::{
     dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
+    dirstate::non_normal_entries::NonNormalEntries,
     dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
 };
 use hg::{
@@ -164,32 +166,86 @@
         Ok(py.None())
     }
 
-    // TODO share the reference
-    def nonnormalentries(&self) -> PyResult<PyObject> {
-        let (non_normal, other_parent) =
-            self.inner(py).borrow().non_normal_other_parent_entries();
+    def other_parent_entries(&self) -> PyResult<PyObject> {
+        let mut inner_shared = self.inner(py).borrow_mut();
+        let (_, other_parent) =
+            inner_shared.get_non_normal_other_parent_entries();
 
         let locals = PyDict::new(py);
         locals.set_item(
             py,
-            "non_normal",
-            non_normal
-                .iter()
-                .map(|v| PyBytes::new(py, v.as_ref()))
-                .collect::<Vec<PyBytes>>()
-                .to_py_object(py),
-        )?;
-        locals.set_item(
-            py,
             "other_parent",
-            other_parent
+            other_parent.as_ref()
+                .unwrap()
                 .iter()
                 .map(|v| PyBytes::new(py, v.as_ref()))
                 .collect::<Vec<PyBytes>>()
                 .to_py_object(py),
         )?;
 
-        py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
+        py.eval("set(other_parent)", None, Some(&locals))
+    }
+
+    def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
+        NonNormalEntries::from_inner(py, self.clone_ref(py))
+    }
+
+    def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
+        let key = key.extract::<PyBytes>(py)?;
+        Ok(self
+            .inner(py)
+            .borrow_mut()
+            .get_non_normal_other_parent_entries().0
+            .as_ref()
+            .unwrap()
+            .contains(HgPath::new(key.data(py))))
+    }
+
+    def non_normal_entries_display(&self) -> PyResult<PyString> {
+        Ok(
+            PyString::new(
+                py,
+                &format!(
+                    "NonNormalEntries: {:?}",
+                    self
+                        .inner(py)
+                        .borrow_mut()
+                        .get_non_normal_other_parent_entries().0
+                        .as_ref()
+                        .unwrap().iter().map(|o| o))
+                )
+            )
+    }
+
+    def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
+        let key = key.extract::<PyBytes>(py)?;
+        self
+            .inner(py)
+            .borrow_mut()
+            .non_normal_entries_remove(HgPath::new(key.data(py)));
+        Ok(py.None())
+    }
+
+    def non_normal_entries_union(&self, other: PyObject) -> PyResult<PyList> {
+        let other: PyResult<_> = other.iter(py)?
+                    .map(|f| {
+                        Ok(HgPathBuf::from_bytes(
+                            f?.extract::<PyBytes>(py)?.data(py),
+                        ))
+                    })
+                    .collect();
+
+        let res = self
+            .inner(py)
+            .borrow_mut()
+            .non_normal_entries_union(other?);
+
+        let ret = PyList::new(py, &[]);
+        for filename in res.iter() {
+            let as_pystring = PyBytes::new(py, filename.as_bytes());
+            ret.append(py, as_pystring.into_object());
+        }
+        Ok(ret)
     }
 
     def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/dirstate/non_normal_entries.rs	Mon Feb 10 21:54:12 2020 +0100
@@ -0,0 +1,52 @@
+// non_normal_other_parent_entries.rs
+//
+// Copyright 2020 Raphaël Gomès <rgomes@octobus.net>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+use cpython::{
+    exc::NotImplementedError, CompareOp, ObjectProtocol, PyErr, PyList,
+    PyObject, PyResult, PyString, Python, PythonObject, ToPyObject,
+};
+
+use crate::dirstate::DirstateMap;
+
+py_class!(pub class NonNormalEntries |py| {
+    data dmap: DirstateMap;
+
+    def __contains__(&self, key: PyObject) -> PyResult<bool> {
+        self.dmap(py).non_normal_entries_contains(py, key)
+    }
+    def remove(&self, key: PyObject) -> PyResult<PyObject> {
+        self.dmap(py).non_normal_entries_remove(py, key)
+    }
+    def union(&self, other: PyObject) -> PyResult<PyList> {
+        self.dmap(py).non_normal_entries_union(py, other)
+    }
+    def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> {
+        match op {
+            CompareOp::Eq => self.is_equal_to(py, other),
+            CompareOp::Ne => Ok(!self.is_equal_to(py, other)?),
+            _ => Err(PyErr::new::<NotImplementedError, _>(py, ""))
+        }
+    }
+    def __repr__(&self) -> PyResult<PyString> {
+        self.dmap(py).non_normal_entries_display(py)
+    }
+});
+
+impl NonNormalEntries {
+    pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
+        Self::create_instance(py, dm)
+    }
+
+    fn is_equal_to(&self, py: Python, other: PyObject) -> PyResult<bool> {
+        for item in other.iter(py)? {
+            if !self.dmap(py).non_normal_entries_contains(py, item?)? {
+                return Ok(false);
+            }
+        }
+        Ok(true)
+    }
+}