Patchwork D10487: dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources

login
register
mail settings
Submitter phabricator
Date April 20, 2021, 2:17 p.m.
Message ID <differential-rev-PHID-DREV-fhuyymfwcr3sm4xdhvvy-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/48802/
State Superseded
Headers show

Comments

phabricator - April 20, 2021, 2:17 p.m.
SimonSapin created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This allows implementing __len__ for DirstateMap and CopyMap efficiently,
  without traversing the tree.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D10487

AFFECTED FILES
  rust/hg-core/src/dirstate_tree/dirstate_map.rs

CHANGE DETAILS




To: SimonSapin, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
@@ -30,6 +30,13 @@ 
     parents: Option<DirstateParents>,
     dirty_parents: bool,
     root: ChildNodes,
+
+    /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
+    nodes_with_entry_count: usize,
+
+    /// Number of nodes anywhere in the tree that have
+    /// `.copy_source.is_some()`.
+    nodes_with_copy_source_count: usize,
 }
 
 /// Using a plain `HgPathBuf` of the full path from the repository root as a
@@ -59,6 +66,8 @@ 
             parents: None,
             dirty_parents: false,
             root: ChildNodes::new(),
+            nodes_with_entry_count: 0,
+            nodes_with_copy_source_count: 0,
         }
     }
 
@@ -78,8 +87,13 @@ 
         }
     }
 
-    fn get_or_insert_node(&mut self, path: &HgPath) -> &mut Node {
-        let mut child_nodes = &mut self.root;
+    /// This takes `root` instead of `&mut self` so that callers can mutate
+    /// other fields while the returned borrow is still valid
+    fn get_or_insert_node<'tree>(
+        root: &'tree mut ChildNodes,
+        path: &HgPath,
+    ) -> &'tree mut Node {
+        let mut child_nodes = root;
         let mut inclusive_ancestor_paths =
             WithBasename::inclusive_ancestors_of(path);
         let mut ancestor_path = inclusive_ancestor_paths
@@ -106,6 +120,35 @@ 
         }
     }
 
+    /// The meaning of `new_copy_source` is:
+    ///
+    /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)`
+    /// * `Some(None)`: set `Node::copy_source` to `None`
+    /// * `None`: leave `Node::copy_source` unchanged
+    fn add_file_node(
+        &mut self,
+        path: &HgPath,
+        new_entry: DirstateEntry,
+        new_copy_source: Option<Option<HgPathBuf>>,
+    ) {
+        let node = Self::get_or_insert_node(&mut self.root, path);
+        if node.entry.is_none() {
+            self.nodes_with_entry_count += 1
+        }
+        if let Some(source) = &new_copy_source {
+            if node.copy_source.is_none() && source.is_some() {
+                self.nodes_with_copy_source_count += 1
+            }
+            if node.copy_source.is_some() && source.is_none() {
+                self.nodes_with_copy_source_count -= 1
+            }
+        }
+        node.entry = Some(new_entry);
+        if let Some(source) = new_copy_source {
+            node.copy_source = source
+        }
+    }
+
     fn iter_nodes<'a>(
         &'a self,
     ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
@@ -194,7 +237,9 @@ 
             p1: NULL_NODE,
             p2: NULL_NODE,
         });
-        self.root.clear()
+        self.root.clear();
+        self.nodes_with_entry_count = 0;
+        self.nodes_with_copy_source_count = 0;
     }
 
     fn add_file(
@@ -315,9 +360,11 @@ 
         let parents = parse_dirstate_entries(
             file_contents,
             |path, entry, copy_source| {
-                let node = self.get_or_insert_node(path);
-                node.entry = Some(*entry);
-                node.copy_source = copy_source.map(HgPath::to_owned);
+                self.add_file_node(
+                    path,
+                    *entry,
+                    Some(copy_source.map(HgPath::to_owned)),
+                )
             },
         )?;
 
@@ -393,7 +440,7 @@ 
     }
 
     fn copy_map_len(&self) -> usize {
-        todo!()
+        self.nodes_with_copy_source_count
     }
 
     fn copy_map_iter(&self) -> CopyMapIter<'_> {
@@ -429,7 +476,7 @@ 
     }
 
     fn len(&self) -> usize {
-        todo!()
+        self.nodes_with_entry_count
     }
 
     fn contains_key(&self, key: &HgPath) -> bool {