Mercurial > hg
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() { |