comparison rust/hg-core/src/revlog/revlog.rs @ 46090:9eb07ab3f2d4

rhg: use persistent nodemap when available … for node ID → revision number lookups, instead on linear scan in a revlog. Differential Revision: https://phab.mercurial-scm.org/D9520
author Simon Sapin <simon-commits@exyr.org>
date Fri, 04 Dec 2020 17:27:10 +0100
parents 88e741bf2d93
children 8a4914397d02
comparison
equal deleted inserted replaced
46089:8ff2d8359d0f 46090:9eb07ab3f2d4
12 use micro_timer::timed; 12 use micro_timer::timed;
13 use zstd; 13 use zstd;
14 14
15 use super::index::Index; 15 use super::index::Index;
16 use super::node::{NodePrefixRef, NODE_BYTES_LENGTH, NULL_NODE}; 16 use super::node::{NodePrefixRef, NODE_BYTES_LENGTH, NULL_NODE};
17 use super::nodemap;
18 use super::nodemap::NodeMap;
19 use super::nodemap_docket::NodeMapDocket;
17 use super::patch; 20 use super::patch;
18 use crate::revlog::Revision; 21 use crate::revlog::Revision;
19 22
20 pub enum RevlogError { 23 pub enum RevlogError {
21 IoError(std::io::Error), 24 IoError(std::io::Error),
25 AmbiguousPrefix, 28 AmbiguousPrefix,
26 Corrupted, 29 Corrupted,
27 UnknowDataFormat(u8), 30 UnknowDataFormat(u8),
28 } 31 }
29 32
30 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> { 33 pub(super) fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
31 let file = File::open(path)?; 34 let file = File::open(path)?;
32 let mmap = unsafe { MmapOptions::new().map(&file) }?; 35 let mmap = unsafe { MmapOptions::new().map(&file) }?;
33 Ok(mmap) 36 Ok(mmap)
34 } 37 }
35 38
39 /// When index and data are interleaved: bytes of the revlog index and 42 /// When index and data are interleaved: bytes of the revlog index and
40 /// data. 43 /// data.
41 index: Index, 44 index: Index,
42 /// When index and data are not interleaved: bytes of the revlog data 45 /// When index and data are not interleaved: bytes of the revlog data
43 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>, 46 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
47 /// When present on disk: the persistent nodemap for this revlog
48 nodemap: Option<nodemap::NodeTree>,
44 } 49 }
45 50
46 impl Revlog { 51 impl Revlog {
47 /// Open a revlog index file. 52 /// Open a revlog index file.
48 /// 53 ///
75 let data_mmap = 80 let data_mmap =
76 mmap_open(data_path).map_err(RevlogError::IoError)?; 81 mmap_open(data_path).map_err(RevlogError::IoError)?;
77 Some(Box::new(data_mmap)) 82 Some(Box::new(data_mmap))
78 }; 83 };
79 84
80 Ok(Revlog { index, data_bytes }) 85 let nodemap = NodeMapDocket::read_from_file(index_path)?.map(
86 |(docket, data)| {
87 nodemap::NodeTree::load_bytes(
88 Box::new(data),
89 docket.data_length,
90 )
91 },
92 );
93
94 Ok(Revlog {
95 index,
96 data_bytes,
97 nodemap,
98 })
81 } 99 }
82 100
83 /// Return number of entries of the `Revlog`. 101 /// Return number of entries of the `Revlog`.
84 pub fn len(&self) -> usize { 102 pub fn len(&self) -> usize {
85 self.index.len() 103 self.index.len()
94 #[timed] 112 #[timed]
95 pub fn get_node_rev( 113 pub fn get_node_rev(
96 &self, 114 &self,
97 node: NodePrefixRef, 115 node: NodePrefixRef,
98 ) -> Result<Revision, RevlogError> { 116 ) -> Result<Revision, RevlogError> {
99 // This is brute force. But it is fast enough for now. 117 if let Some(nodemap) = &self.nodemap {
100 // Optimization will come later. 118 return nodemap
119 .find_bin(&self.index, node)
120 // TODO: propagate details of this error:
121 .map_err(|_| RevlogError::Corrupted)?
122 .ok_or(RevlogError::InvalidRevision);
123 }
124
125 // Fallback to linear scan when a persistent nodemap is not present.
126 // This happens when the persistent-nodemap experimental feature is not
127 // enabled, or for small revlogs.
128 //
129 // TODO: consider building a non-persistent nodemap in memory to
130 // optimize these cases.
101 let mut found_by_prefix = None; 131 let mut found_by_prefix = None;
102 for rev in (0..self.len() as Revision).rev() { 132 for rev in (0..self.len() as Revision).rev() {
103 let index_entry = 133 let index_entry =
104 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?; 134 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
105 if node == *index_entry.hash() { 135 if node == *index_entry.hash() {