##// END OF EJS Templates
dirstate-tree: Use HashMap instead of BTreeMap...
dirstate-tree: Use HashMap instead of BTreeMap BTreeMap has the advantage of its "natural" iteration order being the one we need in the status algorithm. With HashMap however, iteration order is undefined so we need to allocate a Vec and sort it explicitly. Unfortunately many BTreeMap operations are slower than in HashMap, and skipping that extra allocation and sort is not enough to compensate. Switching to HashMap + sort makes `hg status` 17% faster in one test case, as measure with hyperfine: ``` Benchmark #1: ../hg2/hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1 Time (mean ± σ): 765.0 ms ± 8.8 ms [User: 1.352 s, System: 0.747 s] Range (min … max): 751.8 ms … 778.7 ms 10 runs Benchmark #2: ./hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1 Time (mean ± σ): 651.8 ms ± 9.9 ms [User: 1.251 s, System: 0.799 s] Range (min … max): 642.2 ms … 671.8 ms 10 runs Summary './hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1' ran 1.17 ± 0.02 times faster than '../hg2/hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1' ``` * ./hg is this revision * ../hg2/hg is its parent * $REPO is an old snapshot of mozilla-central Differential Revision: https://phab.mercurial-scm.org/D10553

File last commit:

r47889:15395fd8 default
r47889:15395fd8 default
Show More
dirstate_map.rs
653 lines | 20.4 KiB | application/rls-services+xml | RustLexer
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 use bytes_cast::BytesCast;
Simon Sapin
dirstate-tree: Add #[timed] attribute to `status` and `DirstateMap::read`...
r47888 use micro_timer::timed;
Simon Sapin
dirstate-tree: Use HashMap instead of BTreeMap...
r47889 use std::convert::TryInto;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 use std::path::PathBuf;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 use super::path_with_basename::WithBasename;
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 use crate::dirstate::parsers::clear_ambiguous_mtime;
use crate::dirstate::parsers::pack_entry;
use crate::dirstate::parsers::packed_entry_size;
Simon Sapin
dirstate-tree: Add parsing only dirstate parents from disk...
r47868 use crate::dirstate::parsers::parse_dirstate_entries;
use crate::dirstate::parsers::parse_dirstate_parents;
Simon Sapin
rust: Add a Timestamp struct instead of abusing Duration...
r47871 use crate::dirstate::parsers::Timestamp;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 use crate::matchers::Matcher;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 use crate::revlog::node::NULL_NODE;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 use crate::utils::hg_path::{HgPath, HgPathBuf};
use crate::CopyMapIter;
use crate::DirstateEntry;
use crate::DirstateError;
use crate::DirstateMapError;
use crate::DirstateParents;
use crate::DirstateStatus;
use crate::EntryState;
Simon Sapin
dirstate-tree: Use HashMap instead of BTreeMap...
r47889 use crate::FastHashMap;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 use crate::PatternFileWarning;
use crate::StateMapIter;
use crate::StatusError;
use crate::StatusOptions;
pub struct DirstateMap {
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 parents: Option<DirstateParents>,
dirty_parents: bool,
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) root: ChildNodes,
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873
/// 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,
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 }
/// Using a plain `HgPathBuf` of the full path from the repository root as a
/// map key would also work: all paths in a given map have the same parent
/// path, so comparing full paths gives the same result as comparing base
/// names. However `BTreeMap` would waste time always re-comparing the same
/// string prefix.
Simon Sapin
dirstate-tree: Use HashMap instead of BTreeMap...
r47889 pub(super) type ChildNodes = FastHashMap<WithBasename<HgPathBuf>, Node>;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 /// Represents a file or a directory
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 #[derive(Default)]
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) struct Node {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 /// `None` for directories
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) entry: Option<DirstateEntry>,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) copy_source: Option<HgPathBuf>,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) children: ChildNodes,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876
/// How many (non-inclusive) descendants of this node are tracked files
tracked_descendants_count: usize,
}
impl Node {
/// Whether this node has a `DirstateEntry` with `.state.is_tracked()`
fn is_tracked_file(&self) -> bool {
if let Some(entry) = &self.entry {
entry.state.is_tracked()
} else {
false
}
}
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883
pub(super) fn state(&self) -> Option<EntryState> {
self.entry.as_ref().map(|entry| entry.state)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 /// `(full_path, entry, copy_source)`
type NodeDataMut<'a> = (
&'a WithBasename<HgPathBuf>,
&'a mut Option<DirstateEntry>,
&'a mut Option<HgPathBuf>,
);
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 impl DirstateMap {
pub fn new() -> Self {
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 Self {
parents: None,
dirty_parents: false,
Simon Sapin
dirstate-tree: Use HashMap instead of BTreeMap...
r47889 root: ChildNodes::default(),
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 nodes_with_entry_count: 0,
nodes_with_copy_source_count: 0,
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 }
}
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn get_node(&self, path: &HgPath) -> Option<&Node> {
let mut children = &self.root;
let mut components = path.components();
let mut component =
components.next().expect("expected at least one components");
loop {
let child = children.get(component)?;
if let Some(next_component) = components.next() {
component = next_component;
children = &child.children;
} else {
return Some(child);
}
}
}
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 /// Returns a mutable reference to the node at `path` if it exists
///
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 /// This takes `root` instead of `&mut self` so that callers can mutate
/// other fields while the returned borrow is still valid
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 fn get_node_mut<'tree>(
root: &'tree mut ChildNodes,
path: &HgPath,
) -> Option<&'tree mut Node> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 Self::each_and_get(root, path, |_| {})
}
/// Call `each` for each ancestor node of the one at `path` (not including
/// that node itself), starting from nearest the root.
///
/// Panics (possibly after some calls to `each`) if there is no node at
/// `path`.
fn for_each_ancestor_node<'tree>(
&mut self,
path: &HgPath,
each: impl FnMut(&mut Node),
) {
let parent = path.parent();
if !parent.is_empty() {
Self::each_and_get(&mut self.root, parent, each)
.expect("missing dirstate node");
}
}
/// Common implementation detail of `get_node_mut` and
/// `for_each_ancestor_node`
fn each_and_get<'tree>(
root: &'tree mut ChildNodes,
path: &HgPath,
mut each: impl FnMut(&mut Node),
) -> Option<&'tree mut Node> {
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 let mut children = root;
let mut components = path.components();
let mut component =
components.next().expect("expected at least one components");
loop {
let child = children.get_mut(component)?;
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 each(child);
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 if let Some(next_component) = components.next() {
component = next_component;
children = &mut child.children;
} else {
return Some(child);
}
}
}
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 fn get_or_insert_node<'tree>(
root: &'tree mut ChildNodes,
path: &HgPath,
) -> &'tree mut Node {
let mut child_nodes = root;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 let mut inclusive_ancestor_paths =
WithBasename::inclusive_ancestors_of(path);
let mut ancestor_path = inclusive_ancestor_paths
.next()
.expect("expected at least one inclusive ancestor");
loop {
Simon Sapin
dirstate-tree: Avoid BTreeMap double-lookup when inserting a dirstate entry...
r47886 // TODO: can we avoid allocating an owned key in cases where the
// map already contains that key, without introducing double
// lookup?
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 let child_node =
Simon Sapin
dirstate-tree: Avoid BTreeMap double-lookup when inserting a dirstate entry...
r47886 child_nodes.entry(ancestor_path.to_owned()).or_default();
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 if let Some(next) = inclusive_ancestor_paths.next() {
ancestor_path = next;
child_nodes = &mut child_node.children;
} else {
return child_node;
}
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 /// 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
}
}
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 let tracked_count_increment =
match (node.is_tracked_file(), new_entry.state.is_tracked()) {
(false, true) => 1,
(true, false) => -1,
_ => 0,
};
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 node.entry = Some(new_entry);
if let Some(source) = new_copy_source {
node.copy_source = source
}
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 // Borrow of `self.root` through `node` ends here
match tracked_count_increment {
1 => self.for_each_ancestor_node(path, |node| {
node.tracked_descendants_count += 1
}),
// We can’t use `+= -1` because the counter is unsigned
-1 => self.for_each_ancestor_node(path, |node| {
node.tracked_descendants_count -= 1
}),
_ => {}
}
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 }
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 fn iter_nodes<'a>(
&'a self,
) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
{
// Depth first tree traversal.
//
// If we could afford internal iteration and recursion,
// this would look like:
//
// ```
// fn traverse_children(
// children: &ChildNodes,
// each: &mut impl FnMut(&Node),
// ) {
// for child in children.values() {
// traverse_children(&child.children, each);
// each(child);
// }
// }
// ```
//
// However we want an external iterator and therefore can’t use the
// call stack. Use an explicit stack instead:
let mut stack = Vec::new();
let mut iter = self.root.iter();
std::iter::from_fn(move || {
while let Some((key, child_node)) = iter.next() {
// Pseudo-recursion
let new_iter = child_node.children.iter();
let old_iter = std::mem::replace(&mut iter, new_iter);
stack.push((key, child_node, old_iter));
}
// Found the end of a `children.iter()` iterator.
if let Some((key, child_node, next_iter)) = stack.pop() {
// "Return" from pseudo-recursion by restoring state from the
// explicit stack
iter = next_iter;
Some((key, child_node))
} else {
// Reached the bottom of the stack, we’re done
None
}
})
}
/// Mutable iterator for the `(entry, copy source)` of each node.
///
/// It would not be safe to yield mutable references to nodes themeselves
/// with `-> impl Iterator<Item = &mut Node>` since child nodes are
/// reachable from their ancestor nodes, potentially creating multiple
/// `&mut` references to a given node.
fn iter_node_data_mut<'a>(
&'a mut self,
) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
// Explict stack for pseudo-recursion, see `iter_nodes` above.
let mut stack = Vec::new();
let mut iter = self.root.iter_mut();
std::iter::from_fn(move || {
while let Some((key, child_node)) = iter.next() {
// Pseudo-recursion
let data =
(key, &mut child_node.entry, &mut child_node.copy_source);
let new_iter = child_node.children.iter_mut();
let old_iter = std::mem::replace(&mut iter, new_iter);
stack.push((data, old_iter));
}
// Found the end of a `children.values_mut()` iterator.
if let Some((data, next_iter)) = stack.pop() {
// "Return" from pseudo-recursion by restoring state from the
// explicit stack
iter = next_iter;
Some(data)
} else {
// Reached the bottom of the stack, we’re done
None
}
})
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
impl super::dispatch::DirstateMapMethods for DirstateMap {
fn clear(&mut self) {
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 self.set_parents(&DirstateParents {
p1: NULL_NODE,
p2: NULL_NODE,
});
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 self.root.clear();
self.nodes_with_entry_count = 0;
self.nodes_with_copy_source_count = 0;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn add_file(
&mut self,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 filename: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 _old_state: EntryState,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 entry: DirstateEntry,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 self.add_file_node(filename, entry, None);
Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn remove_file(
&mut self,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 filename: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 _old_state: EntryState,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 size: i32,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 let entry = DirstateEntry {
state: EntryState::Removed,
mode: 0,
size,
mtime: 0,
};
self.add_file_node(filename, entry, None);
Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn drop_file(
&mut self,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 filename: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 _old_state: EntryState,
) -> Result<bool, DirstateMapError> {
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 if let Some(node) = Self::get_node_mut(&mut self.root, filename) {
let was_tracked = node.is_tracked_file();
let had_entry = node.entry.is_some();
let had_copy_source = node.copy_source.is_some();
// TODO: this leaves in the tree a "non-file" node. Should we
// remove the node instead, together with ancestor nodes for
// directories that become empty?
node.entry = None;
node.copy_source = None;
if had_entry {
self.nodes_with_entry_count -= 1
}
if had_copy_source {
self.nodes_with_copy_source_count -= 1
}
if was_tracked {
self.for_each_ancestor_node(filename, |node| {
node.tracked_descendants_count -= 1
})
}
Ok(had_entry)
} else {
Ok(false)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add clear_ambiguous_times in the new DirstateMap...
r47875 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
for filename in filenames {
if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
if let Some(entry) = node.entry.as_mut() {
clear_ambiguous_mtime(entry, now);
}
}
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
self.get_node(key)
.and_then(|node| node.entry.as_ref())
.map_or(false, DirstateEntry::is_non_normal)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 fn non_normal_entries_remove(&mut self, _key: &HgPath) {
// Do nothing, this `DirstateMap` does not have a separate "non normal
// entries" set that need to be kept up to date
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn non_normal_or_other_parent_paths(
&mut self,
) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry
.as_ref()
.filter(|entry| {
entry.is_non_normal() || entry.is_from_other_parent()
})
.map(|_| path.full_path())
}))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 // Do nothing, this `DirstateMap` does not have a separate "non normal
// entries" and "from other parent" sets that need to be recomputed
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter_non_normal_paths(
&mut self,
) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 self.iter_non_normal_paths_panic()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter_non_normal_paths_panic(
&self,
) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry
.as_ref()
.filter(|entry| entry.is_non_normal())
.map(|_| path.full_path())
}))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter_other_parent_paths(
&mut self,
) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry
.as_ref()
.filter(|entry| entry.is_from_other_parent())
.map(|_| path.full_path())
}))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn has_tracked_dir(
&mut self,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 directory: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<bool, DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 if let Some(node) = self.get_node(directory) {
// A node without a `DirstateEntry` was created to hold child
// nodes, and is therefore a directory.
Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
} else {
Ok(false)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn has_dir(
&mut self,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 directory: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<bool, DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 if let Some(node) = self.get_node(directory) {
// A node without a `DirstateEntry` was created to hold child
// nodes, and is therefore a directory.
Ok(node.entry.is_none())
} else {
Ok(false)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn parents(
&mut self,
Simon Sapin
dirstate-tree: Add parsing only dirstate parents from disk...
r47868 file_contents: &[u8],
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<&DirstateParents, DirstateError> {
Simon Sapin
dirstate-tree: Add parsing only dirstate parents from disk...
r47868 if self.parents.is_none() {
let parents = if !file_contents.is_empty() {
parse_dirstate_parents(file_contents)?.clone()
} else {
DirstateParents {
p1: NULL_NODE,
p2: NULL_NODE,
}
};
self.parents = Some(parents);
}
Ok(self.parents.as_ref().unwrap())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 fn set_parents(&mut self, parents: &DirstateParents) {
self.parents = Some(parents.clone());
self.dirty_parents = true;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add #[timed] attribute to `status` and `DirstateMap::read`...
r47888 #[timed]
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 fn read<'a>(
&mut self,
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 file_contents: &'a [u8],
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 if file_contents.is_empty() {
return Ok(None);
}
let parents = parse_dirstate_entries(
file_contents,
|path, entry, copy_source| {
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 self.add_file_node(
path,
*entry,
Some(copy_source.map(HgPath::to_owned)),
)
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 },
)?;
if !self.dirty_parents {
self.set_parents(parents);
}
Ok(Some(parents))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn pack(
&mut self,
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 parents: DirstateParents,
now: Timestamp,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<Vec<u8>, DirstateError> {
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
// reallocations
let mut size = parents.as_bytes().len();
for (path, node) in self.iter_nodes() {
if node.entry.is_some() {
size += packed_entry_size(
path.full_path(),
node.copy_source.as_ref(),
)
}
}
let mut packed = Vec::with_capacity(size);
packed.extend(parents.as_bytes());
let now: i32 = now.0.try_into().expect("time overflow");
for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
if let Some(entry) = opt_entry {
clear_ambiguous_mtime(entry, now);
pack_entry(
path.full_path(),
entry,
copy_source.as_ref(),
&mut packed,
);
}
}
self.dirty_parents = false;
Ok(packed)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
// needs to be recomputed
Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
// to be recomputed
Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn status<'a>(
Simon Sapin
dirstate-tree: Give to `status()` mutable access to the `DirstateMap`...
r47882 &'a mut self,
matcher: &'a (dyn Matcher + Sync),
root_dir: PathBuf,
ignore_files: Vec<PathBuf>,
options: StatusOptions,
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
{
Simon Sapin
dirstate-tree: Give to `status()` mutable access to the `DirstateMap`...
r47882 super::status::status(self, matcher, root_dir, ignore_files, options)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn copy_map_len(&self) -> usize {
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 self.nodes_with_copy_source_count
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn copy_map_iter(&self) -> CopyMapIter<'_> {
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.copy_source
.as_ref()
.map(|copy_source| (path.full_path(), copy_source))
}))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
if let Some(node) = self.get_node(key) {
node.copy_source.is_some()
} else {
false
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
self.get_node(key)?.copy_source.as_ref()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
let count = &mut self.nodes_with_copy_source_count;
Self::get_node_mut(&mut self.root, key).and_then(|node| {
if node.copy_source.is_some() {
*count -= 1
}
node.copy_source.take()
})
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn copy_map_insert(
&mut self,
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 key: HgPathBuf,
value: HgPathBuf,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Option<HgPathBuf> {
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 let node = Self::get_or_insert_node(&mut self.root, &key);
if node.copy_source.is_none() {
self.nodes_with_copy_source_count += 1
}
node.copy_source.replace(value)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn len(&self) -> usize {
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 self.nodes_with_entry_count
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn contains_key(&self, key: &HgPath) -> bool {
self.get(key).is_some()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
self.get_node(key)?.entry.as_ref()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter(&self) -> StateMapIter<'_> {
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry.as_ref().map(|entry| (path.full_path(), entry))
}))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
}