##// END OF EJS Templates
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer … that has the contents of the `.hg/dirstate` file. This only applies to the tree-based flavor of `DirstateMap`. For now only the entire `&[u8]` slice is stored, so this is not useful yet. Adding a lifetime parameter to the `DirstateMap` struct (in hg-core) makes Python bindings non-trivial because we keep that struct in a Python object that has a dynamic lifetime tied to Python’s reference-counting and GC. As long as we keep the `PyBytes` that owns the borrowed bytes buffer next to the borrowing struct, the buffer will live long enough for the borrows to stay valid. However this relationship cannot be expressed in safe Rust code in a way that would statisfy they borrow-checker. We use `unsafe` code to erase that lifetime parameter, and encapsulate it in a safe abstraction similar to the owning-ref crate: https://docs.rs/owning_ref/ Differential Revision: https://phab.mercurial-scm.org/D10557

File last commit:

r47893:d8ac6237 default
r47893:d8ac6237 default
Show More
dirstate_map.rs
408 lines | 11.9 KiB | application/rls-services+xml | RustLexer
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 // dirstate_map.rs
//
// Copyright 2019 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.
Simon Sapin
dirstate-tree: Add clear_ambiguous_times in the new DirstateMap...
r47875 use crate::dirstate::parsers::clear_ambiguous_mtime;
Simon Sapin
rust: Add a Timestamp struct instead of abusing Duration...
r47871 use crate::dirstate::parsers::Timestamp;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 use crate::{
Simon Sapin
rust: Remove handling of `parents` in `DirstateMap`...
r47891 dirstate::EntryState,
Raphaël Gomès
rust-utils: add normalize_case util to mirror Python one...
r43108 pack_dirstate, parse_dirstate,
Simon Sapin
rust: Remove DirstateMap::file_fold_map...
r47879 utils::hg_path::{HgPath, HgPathBuf},
Raphaël Gomès
rust: format with rustfmt...
r46162 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
Simon Sapin
rust: Remove DirstateMap::file_fold_map...
r47879 DirstateParents, StateMap,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 };
Raphaël Gomès
rust-dirstatemap: add #[timed] to dirstatemap read for comparison...
r46135 use micro_timer::timed;
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 use std::collections::HashSet;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 use std::iter::FromIterator;
use std::ops::Deref;
#[derive(Default)]
pub struct DirstateMap {
state_map: StateMap,
pub copy_map: CopyMap,
pub dirs: Option<DirsMultiset>,
pub all_dirs: Option<DirsMultiset>,
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 non_normal_set: Option<HashSet<HgPathBuf>>,
other_parent_set: Option<HashSet<HgPathBuf>>,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
/// Should only really be used in python interface code, for clarity
impl Deref for DirstateMap {
type Target = StateMap;
fn deref(&self) -> &Self::Target {
&self.state_map
}
}
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
Raphaël Gomès
rust: format with rustfmt...
r46162 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
iter: I,
) -> Self {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 Self {
state_map: iter.into_iter().collect(),
..Self::default()
}
}
}
impl DirstateMap {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
Raphaël Gomès
rust: start plugging the dirstate tree behind a feature gate...
r46185 self.state_map = StateMap::default();
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 self.copy_map.clear();
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.non_normal_set = None;
self.other_parent_set = None;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
/// Add a tracked file to the dirstate
pub fn add_file(
&mut self,
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 filename: &HgPath,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 old_state: EntryState,
entry: DirstateEntry,
Raphaël Gomès
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
r44227 ) -> Result<(), DirstateMapError> {
Raphaël Gomès
rust: format with rustfmt...
r46162 if old_state == EntryState::Unknown || old_state == EntryState::Removed
{
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 if let Some(ref mut dirs) = self.dirs {
Raphaël Gomès
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
r44227 dirs.add_path(filename)?;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
}
if old_state == EntryState::Unknown {
if let Some(ref mut all_dirs) = self.all_dirs {
Raphaël Gomès
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
r44227 all_dirs.add_path(filename)?;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
}
self.state_map.insert(filename.to_owned(), entry.to_owned());
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 if entry.is_non_normal() {
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.get_non_normal_other_parent_entries()
.0
.insert(filename.to_owned());
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 if entry.is_from_other_parent() {
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.get_non_normal_other_parent_entries()
.1
.insert(filename.to_owned());
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
r44227 Ok(())
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
/// Mark a file as removed in the dirstate.
///
/// The `size` parameter is used to store sentinel values that indicate
/// the file's previous state. In the future, we should refactor this
/// to be more explicit about what that state is.
pub fn remove_file(
&mut self,
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 filename: &HgPath,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 old_state: EntryState,
size: i32,
) -> Result<(), DirstateMapError> {
Raphaël Gomès
rust: format with rustfmt...
r46162 if old_state != EntryState::Unknown && old_state != EntryState::Removed
{
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 if let Some(ref mut dirs) = self.dirs {
dirs.delete_path(filename)?;
}
}
if old_state == EntryState::Unknown {
if let Some(ref mut all_dirs) = self.all_dirs {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 all_dirs.add_path(filename)?;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
}
self.state_map.insert(
filename.to_owned(),
DirstateEntry {
state: EntryState::Removed,
mode: 0,
size,
mtime: 0,
},
);
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.get_non_normal_other_parent_entries()
.0
.insert(filename.to_owned());
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 Ok(())
}
/// Remove a file from the dirstate.
/// Returns `true` if the file was previously recorded.
pub fn drop_file(
&mut self,
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 filename: &HgPath,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 old_state: EntryState,
) -> Result<bool, DirstateMapError> {
Yuya Nishihara
rust-dirstate: remove too abstracted way of getting &[u8]
r43063 let exists = self.state_map.remove(filename).is_some();
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998
if exists {
if old_state != EntryState::Removed {
if let Some(ref mut dirs) = self.dirs {
dirs.delete_path(filename)?;
}
}
if let Some(ref mut all_dirs) = self.all_dirs {
all_dirs.delete_path(filename)?;
}
}
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.get_non_normal_other_parent_entries()
.0
.remove(filename);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998
Ok(exists)
}
Raphaël Gomès
rust: format with rustfmt...
r46162 pub fn clear_ambiguous_times(
&mut self,
filenames: Vec<HgPathBuf>,
now: i32,
) {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 for filename in filenames {
Raphaël Gomès
rust: start plugging the dirstate tree behind a feature gate...
r46185 if let Some(entry) = self.state_map.get_mut(&filename) {
Simon Sapin
dirstate-tree: Add clear_ambiguous_times in the new DirstateMap...
r47875 if clear_ambiguous_mtime(entry, now) {
self.get_non_normal_other_parent_entries()
.0
.insert(filename.to_owned());
Raphaël Gomès
rust: start plugging the dirstate tree behind a feature gate...
r46185 }
}
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
}
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 pub fn non_normal_entries_remove(&mut self, key: impl AsRef<HgPath>) {
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.get_non_normal_other_parent_entries()
.0
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 .remove(key.as_ref());
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 }
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878
Raphaël Gomès
rust: format with rustfmt...
r46162 pub fn non_normal_entries_union(
&mut self,
other: HashSet<HgPathBuf>,
) -> Vec<HgPathBuf> {
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.get_non_normal_other_parent_entries()
.0
.union(&other)
Raphaël Gomès
rust: do a clippy pass...
r45500 .map(ToOwned::to_owned)
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 .collect()
}
pub fn get_non_normal_other_parent_entries(
&mut self,
Raphaël Gomès
rust-dirstatemap: directly return `non_normal` and `other_entries`...
r44842 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.set_non_normal_other_parent_entries(false);
Raphaël Gomès
rust-dirstatemap: directly return `non_normal` and `other_entries`...
r44842 (
self.non_normal_set.as_mut().unwrap(),
self.other_parent_set.as_mut().unwrap(),
)
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 }
Raphaël Gomès
rust-cpython: make `NonNormalEntires` iterable to fix `fsmonitor` (issue6276)...
r44903 /// Useful to get immutable references to those sets in contexts where
/// you only have an immutable reference to the `DirstateMap`, like when
/// sharing references with Python.
///
/// TODO, get rid of this along with the other "setter/getter" stuff when
/// a nice typestate plan is defined.
///
/// # Panics
///
/// Will panic if either set is `None`.
pub fn get_non_normal_other_parent_entries_panic(
&self,
) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
(
self.non_normal_set.as_ref().unwrap(),
self.other_parent_set.as_ref().unwrap(),
)
}
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
Raphaël Gomès
rust: format with rustfmt...
r46162 if !force
&& self.non_normal_set.is_some()
&& self.other_parent_set.is_some()
{
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 return;
}
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 let mut non_normal = HashSet::new();
let mut other_parent = HashSet::new();
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 for (filename, entry) in self.state_map.iter() {
if entry.is_non_normal() {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 non_normal.insert(filename.to_owned());
}
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 if entry.is_from_other_parent() {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 other_parent.insert(filename.to_owned());
}
}
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.non_normal_set = Some(non_normal);
self.other_parent_set = Some(other_parent);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
/// Both of these setters and their uses appear to be the simplest way to
/// emulate a Python lazy property, but it is ugly and unidiomatic.
/// TODO One day, rewriting this struct using the typestate might be a
/// good idea.
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 if self.all_dirs.is_none() {
Simon Sapin
dirstate-tree: Make Rust DirstateMap bindings go through a trait object...
r47863 self.all_dirs = Some(DirsMultiset::from_dirstate(
self.state_map.iter(),
None,
)?);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 Ok(())
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 if self.dirs.is_none() {
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 self.dirs = Some(DirsMultiset::from_dirstate(
&self.state_map,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 Some(EntryState::Removed),
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 )?);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 Ok(())
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust: format with rustfmt...
r46162 pub fn has_tracked_dir(
&mut self,
directory: &HgPath,
) -> Result<bool, DirstateMapError> {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 self.set_dirs()?;
Ok(self.dirs.as_ref().unwrap().contains(directory))
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust: format with rustfmt...
r46162 pub fn has_dir(
&mut self,
directory: &HgPath,
) -> Result<bool, DirstateMapError> {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 self.set_all_dirs()?;
Ok(self.all_dirs.as_ref().unwrap().contains(directory))
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
Raphaël Gomès
rust-dirstatemap: add #[timed] to dirstatemap read for comparison...
r46135 #[timed]
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 pub fn read(
Raphaël Gomès
rust: format with rustfmt...
r46162 &mut self,
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 file_contents: &[u8],
) -> Result<Option<DirstateParents>, DirstateError> {
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 if file_contents.is_empty() {
return Ok(None);
}
Antoine Cezar
hg-core: make parse_dirstate return references rather than update hashmaps...
r45916 let (parents, entries, copies) = parse_dirstate(file_contents)?;
self.state_map.extend(
entries
.into_iter()
.map(|(path, entry)| (path.to_owned(), entry)),
);
self.copy_map.extend(
copies
.into_iter()
.map(|(path, copy)| (path.to_owned(), copy.to_owned())),
);
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 Ok(Some(parents.clone()))
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
pub fn pack(
&mut self,
parents: DirstateParents,
Simon Sapin
rust: Add a Timestamp struct instead of abusing Duration...
r47871 now: Timestamp,
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 ) -> Result<Vec<u8>, DirstateError> {
Raphaël Gomès
rust: format with rustfmt...
r46162 let packed =
pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 self.set_non_normal_other_parent_entries(true);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 Ok(packed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dirs_multiset() {
let mut map = DirstateMap::new();
assert!(map.dirs.is_none());
assert!(map.all_dirs.is_none());
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 assert!(map.all_dirs.is_some());
assert!(map.dirs.is_none());
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 assert!(map.dirs.is_some());
}
#[test]
fn test_add_file() {
let mut map = DirstateMap::new();
assert_eq!(0, map.len());
map.add_file(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 HgPath::new(b"meh"),
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 EntryState::Normal,
DirstateEntry {
state: EntryState::Normal,
mode: 1337,
mtime: 1337,
size: 1337,
},
Raphaël Gomès
rust-warnings: fix warnings in tests...
r44342 )
.unwrap();
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998
assert_eq!(1, map.len());
Raphaël Gomès
rust-dirstatemap: directly return `non_normal` and `other_entries`...
r44842 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
#[test]
fn test_non_normal_other_parent_entries() {
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 let mut map: DirstateMap = [
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
(b"f2", (EntryState::Normal, 1337, 1337, -1)),
(b"f3", (EntryState::Normal, 1337, 1337, 1337)),
(b"f4", (EntryState::Normal, 1337, -2, 1337)),
(b"f5", (EntryState::Added, 1337, 1337, 1337)),
(b"f6", (EntryState::Added, 1337, 1337, -1)),
(b"f7", (EntryState::Merged, 1337, 1337, -1)),
(b"f8", (EntryState::Merged, 1337, 1337, 1337)),
(b"f9", (EntryState::Merged, 1337, -2, 1337)),
(b"fa", (EntryState::Added, 1337, -2, 1337)),
(b"fb", (EntryState::Removed, 1337, -2, 1337)),
]
.iter()
.map(|(fname, (state, mode, size, mtime))| {
(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 HgPathBuf::from_bytes(fname.as_ref()),
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 DirstateEntry {
state: *state,
mode: *mode,
size: *size,
mtime: *mtime,
},
)
})
.collect();
Raphaël Gomès
rust-dirstatemap: directly return `non_normal` and `other_entries`...
r44842 let mut non_normal = [
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
]
.iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 .collect();
let mut other_parent = HashSet::new();
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
Raphaël Gomès
rust-dirstatemap: cache non normal and other parent set...
r44775 let entries = map.get_non_normal_other_parent_entries();
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998
Raphaël Gomès
rust: format with rustfmt...
r46162 assert_eq!(
(&mut non_normal, &mut other_parent),
(entries.0, entries.1)
);
Raphaël Gomès
rust-dirstate: rust implementation of dirstatemap...
r42998 }
}