Mercurial > public > mercurial-scm > hg
diff rust/hg-core/src/repo.rs @ 47956:81aedf1fc897
rust: Add Repo::dirstate_map and use it in `rhg status`
This moves low-level dirstate wrangling out of the status command and into
a more reusable location.
The open dirstate map is lazily initialized and kept on the Repo object,
for reuse by sub-sequent calls.
Differential Revision: https://phab.mercurial-scm.org/D11398
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Thu, 09 Sep 2021 21:04:55 +0200 |
parents | 9cd35c8c6044 |
children | fc208d6faed3 |
line wrap: on
line diff
--- a/rust/hg-core/src/repo.rs Fri Sep 10 09:53:09 2021 +0200 +++ b/rust/hg-core/src/repo.rs Thu Sep 09 21:04:55 2021 +0200 @@ -1,10 +1,16 @@ use crate::config::{Config, ConfigError, ConfigParseError}; +use crate::dirstate::DirstateParents; +use crate::dirstate_tree::dirstate_map::DirstateMap; +use crate::dirstate_tree::owning::OwningDirstateMap; use crate::errors::HgError; +use crate::errors::HgResultExt; use crate::exit_codes; use crate::requirements; use crate::utils::files::get_path_from_bytes; use crate::utils::SliceExt; use crate::vfs::{is_dir, is_file, Vfs}; +use crate::DirstateError; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::HashSet; use std::path::{Path, PathBuf}; @@ -15,6 +21,9 @@ store: PathBuf, requirements: HashSet<String>, config: Config, + // None means not known/initialized yet + dirstate_parents: Cell<Option<DirstateParents>>, + dirstate_map: RefCell<Option<OwningDirstateMap>>, } #[derive(Debug, derive_more::From)] @@ -186,6 +195,8 @@ store: store_path, dot_hg, config: repo_config, + dirstate_parents: Cell::new(None), + dirstate_map: RefCell::new(None), }; requirements::check(&repo)?; @@ -228,19 +239,101 @@ .contains(requirements::DIRSTATE_V2_REQUIREMENT) } - pub fn dirstate_parents( - &self, - ) -> Result<crate::dirstate::DirstateParents, HgError> { - let dirstate = self.hg_vfs().mmap_open("dirstate")?; - if dirstate.is_empty() { - return Ok(crate::dirstate::DirstateParents::NULL); + fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> { + Ok(self + .hg_vfs() + .read("dirstate") + .io_not_found_as_none()? + .unwrap_or(Vec::new())) + } + + pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> { + if let Some(parents) = self.dirstate_parents.get() { + return Ok(parents); } - let parents = if self.has_dirstate_v2() { + let dirstate = self.dirstate_file_contents()?; + let parents = if dirstate.is_empty() { + DirstateParents::NULL + } else if self.has_dirstate_v2() { crate::dirstate_tree::on_disk::read_docket(&dirstate)?.parents() } else { crate::dirstate::parsers::parse_dirstate_parents(&dirstate)? .clone() }; + self.dirstate_parents.set(Some(parents)); Ok(parents) } + + fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> { + let dirstate_file_contents = self.dirstate_file_contents()?; + if dirstate_file_contents.is_empty() { + self.dirstate_parents.set(Some(DirstateParents::NULL)); + Ok(OwningDirstateMap::new_empty(Vec::new())) + } else if self.has_dirstate_v2() { + let docket = crate::dirstate_tree::on_disk::read_docket( + &dirstate_file_contents, + )?; + self.dirstate_parents.set(Some(docket.parents())); + let data_size = docket.data_size(); + let metadata = docket.tree_metadata(); + let mut map = if let Some(data_mmap) = self + .hg_vfs() + .mmap_open(docket.data_filename()) + .io_not_found_as_none()? + { + OwningDirstateMap::new_empty(MmapWrapper(data_mmap)) + } else { + OwningDirstateMap::new_empty(Vec::new()) + }; + let (on_disk, placeholder) = map.get_mut_pair(); + *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?; + Ok(map) + } else { + let mut map = OwningDirstateMap::new_empty(dirstate_file_contents); + let (on_disk, placeholder) = map.get_mut_pair(); + let (inner, parents) = DirstateMap::new_v1(on_disk)?; + self.dirstate_parents + .set(Some(parents.unwrap_or(DirstateParents::NULL))); + *placeholder = inner; + Ok(map) + } + } + + pub fn dirstate_map( + &self, + ) -> Result<Ref<OwningDirstateMap>, DirstateError> { + let mut borrowed = self.dirstate_map.borrow(); + if borrowed.is_none() { + drop(borrowed); + // Only use `borrow_mut` if it is really needed to avoid panic in + // case there is another outstanding borrow but mutation is not + // needed. + *self.dirstate_map.borrow_mut() = Some(self.new_dirstate_map()?); + borrowed = self.dirstate_map.borrow() + } + Ok(Ref::map(borrowed, |option| option.as_ref().unwrap())) + } + + pub fn dirstate_map_mut( + &self, + ) -> Result<RefMut<OwningDirstateMap>, DirstateError> { + let mut borrowed = self.dirstate_map.borrow_mut(); + if borrowed.is_none() { + *borrowed = Some(self.new_dirstate_map()?); + } + Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap())) + } } + +// TODO: remove this when https://github.com/RazrFalcon/memmap2-rs/pull/22 is on crates.io +struct MmapWrapper(memmap2::Mmap); + +impl std::ops::Deref for MmapWrapper { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.0.deref() + } +} + +unsafe impl stable_deref_trait::StableDeref for MmapWrapper {}