##// END OF EJS Templates
rust-clippy: factor an `if` with the same duplicated clause...
Raphaël Gomès -
r50812:4c71c215 default
parent child Browse files
Show More
@@ -1,643 +1,641 b''
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::io::Read;
2 use std::io::Read;
3 use std::ops::Deref;
3 use std::ops::Deref;
4 use std::path::Path;
4 use std::path::Path;
5
5
6 use flate2::read::ZlibDecoder;
6 use flate2::read::ZlibDecoder;
7 use sha1::{Digest, Sha1};
7 use sha1::{Digest, Sha1};
8 use zstd;
8 use zstd;
9
9
10 use super::index::Index;
10 use super::index::Index;
11 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
11 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
12 use super::nodemap;
12 use super::nodemap;
13 use super::nodemap::{NodeMap, NodeMapError};
13 use super::nodemap::{NodeMap, NodeMapError};
14 use super::nodemap_docket::NodeMapDocket;
14 use super::nodemap_docket::NodeMapDocket;
15 use super::patch;
15 use super::patch;
16 use crate::errors::HgError;
16 use crate::errors::HgError;
17 use crate::revlog::Revision;
17 use crate::revlog::Revision;
18 use crate::vfs::Vfs;
18 use crate::vfs::Vfs;
19 use crate::{Node, NULL_REVISION};
19 use crate::{Node, NULL_REVISION};
20
20
21 const REVISION_FLAG_CENSORED: u16 = 1 << 15;
21 const REVISION_FLAG_CENSORED: u16 = 1 << 15;
22 const REVISION_FLAG_ELLIPSIS: u16 = 1 << 14;
22 const REVISION_FLAG_ELLIPSIS: u16 = 1 << 14;
23 const REVISION_FLAG_EXTSTORED: u16 = 1 << 13;
23 const REVISION_FLAG_EXTSTORED: u16 = 1 << 13;
24 const REVISION_FLAG_HASCOPIESINFO: u16 = 1 << 12;
24 const REVISION_FLAG_HASCOPIESINFO: u16 = 1 << 12;
25
25
26 // Keep this in sync with REVIDX_KNOWN_FLAGS in
26 // Keep this in sync with REVIDX_KNOWN_FLAGS in
27 // mercurial/revlogutils/flagutil.py
27 // mercurial/revlogutils/flagutil.py
28 const REVIDX_KNOWN_FLAGS: u16 = REVISION_FLAG_CENSORED
28 const REVIDX_KNOWN_FLAGS: u16 = REVISION_FLAG_CENSORED
29 | REVISION_FLAG_ELLIPSIS
29 | REVISION_FLAG_ELLIPSIS
30 | REVISION_FLAG_EXTSTORED
30 | REVISION_FLAG_EXTSTORED
31 | REVISION_FLAG_HASCOPIESINFO;
31 | REVISION_FLAG_HASCOPIESINFO;
32
32
33 const NULL_REVLOG_ENTRY_FLAGS: u16 = 0;
33 const NULL_REVLOG_ENTRY_FLAGS: u16 = 0;
34
34
35 #[derive(Debug, derive_more::From)]
35 #[derive(Debug, derive_more::From)]
36 pub enum RevlogError {
36 pub enum RevlogError {
37 InvalidRevision,
37 InvalidRevision,
38 /// Working directory is not supported
38 /// Working directory is not supported
39 WDirUnsupported,
39 WDirUnsupported,
40 /// Found more than one entry whose ID match the requested prefix
40 /// Found more than one entry whose ID match the requested prefix
41 AmbiguousPrefix,
41 AmbiguousPrefix,
42 #[from]
42 #[from]
43 Other(HgError),
43 Other(HgError),
44 }
44 }
45
45
46 impl From<NodeMapError> for RevlogError {
46 impl From<NodeMapError> for RevlogError {
47 fn from(error: NodeMapError) -> Self {
47 fn from(error: NodeMapError) -> Self {
48 match error {
48 match error {
49 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
49 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
50 NodeMapError::RevisionNotInIndex(rev) => RevlogError::corrupted(
50 NodeMapError::RevisionNotInIndex(rev) => RevlogError::corrupted(
51 format!("nodemap point to revision {} not in index", rev),
51 format!("nodemap point to revision {} not in index", rev),
52 ),
52 ),
53 }
53 }
54 }
54 }
55 }
55 }
56
56
57 fn corrupted<S: AsRef<str>>(context: S) -> HgError {
57 fn corrupted<S: AsRef<str>>(context: S) -> HgError {
58 HgError::corrupted(format!("corrupted revlog, {}", context.as_ref()))
58 HgError::corrupted(format!("corrupted revlog, {}", context.as_ref()))
59 }
59 }
60
60
61 impl RevlogError {
61 impl RevlogError {
62 fn corrupted<S: AsRef<str>>(context: S) -> Self {
62 fn corrupted<S: AsRef<str>>(context: S) -> Self {
63 RevlogError::Other(corrupted(context))
63 RevlogError::Other(corrupted(context))
64 }
64 }
65 }
65 }
66
66
67 /// Read only implementation of revlog.
67 /// Read only implementation of revlog.
68 pub struct Revlog {
68 pub struct Revlog {
69 /// When index and data are not interleaved: bytes of the revlog index.
69 /// When index and data are not interleaved: bytes of the revlog index.
70 /// When index and data are interleaved: bytes of the revlog index and
70 /// When index and data are interleaved: bytes of the revlog index and
71 /// data.
71 /// data.
72 index: Index,
72 index: Index,
73 /// When index and data are not interleaved: bytes of the revlog data
73 /// When index and data are not interleaved: bytes of the revlog data
74 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
74 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
75 /// When present on disk: the persistent nodemap for this revlog
75 /// When present on disk: the persistent nodemap for this revlog
76 nodemap: Option<nodemap::NodeTree>,
76 nodemap: Option<nodemap::NodeTree>,
77 }
77 }
78
78
79 impl Revlog {
79 impl Revlog {
80 /// Open a revlog index file.
80 /// Open a revlog index file.
81 ///
81 ///
82 /// It will also open the associated data file if index and data are not
82 /// It will also open the associated data file if index and data are not
83 /// interleaved.
83 /// interleaved.
84 pub fn open(
84 pub fn open(
85 store_vfs: &Vfs,
85 store_vfs: &Vfs,
86 index_path: impl AsRef<Path>,
86 index_path: impl AsRef<Path>,
87 data_path: Option<&Path>,
87 data_path: Option<&Path>,
88 use_nodemap: bool,
88 use_nodemap: bool,
89 ) -> Result<Self, HgError> {
89 ) -> Result<Self, HgError> {
90 let index_path = index_path.as_ref();
90 let index_path = index_path.as_ref();
91 let index = {
91 let index = {
92 match store_vfs.mmap_open_opt(&index_path)? {
92 match store_vfs.mmap_open_opt(&index_path)? {
93 None => Index::new(Box::new(vec![])),
93 None => Index::new(Box::new(vec![])),
94 Some(index_mmap) => {
94 Some(index_mmap) => {
95 let index = Index::new(Box::new(index_mmap))?;
95 let index = Index::new(Box::new(index_mmap))?;
96 Ok(index)
96 Ok(index)
97 }
97 }
98 }
98 }
99 }?;
99 }?;
100
100
101 let default_data_path = index_path.with_extension("d");
101 let default_data_path = index_path.with_extension("d");
102
102
103 // type annotation required
103 // type annotation required
104 // won't recognize Mmap as Deref<Target = [u8]>
104 // won't recognize Mmap as Deref<Target = [u8]>
105 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
105 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
106 if index.is_inline() {
106 if index.is_inline() {
107 None
107 None
108 } else {
108 } else {
109 let data_path = data_path.unwrap_or(&default_data_path);
109 let data_path = data_path.unwrap_or(&default_data_path);
110 let data_mmap = store_vfs.mmap_open(data_path)?;
110 let data_mmap = store_vfs.mmap_open(data_path)?;
111 Some(Box::new(data_mmap))
111 Some(Box::new(data_mmap))
112 };
112 };
113
113
114 let nodemap = if index.is_inline() {
114 let nodemap = if index.is_inline() || !use_nodemap {
115 None
116 } else if !use_nodemap {
117 None
115 None
118 } else {
116 } else {
119 NodeMapDocket::read_from_file(store_vfs, index_path)?.map(
117 NodeMapDocket::read_from_file(store_vfs, index_path)?.map(
120 |(docket, data)| {
118 |(docket, data)| {
121 nodemap::NodeTree::load_bytes(
119 nodemap::NodeTree::load_bytes(
122 Box::new(data),
120 Box::new(data),
123 docket.data_length,
121 docket.data_length,
124 )
122 )
125 },
123 },
126 )
124 )
127 };
125 };
128
126
129 Ok(Revlog {
127 Ok(Revlog {
130 index,
128 index,
131 data_bytes,
129 data_bytes,
132 nodemap,
130 nodemap,
133 })
131 })
134 }
132 }
135
133
136 /// Return number of entries of the `Revlog`.
134 /// Return number of entries of the `Revlog`.
137 pub fn len(&self) -> usize {
135 pub fn len(&self) -> usize {
138 self.index.len()
136 self.index.len()
139 }
137 }
140
138
141 /// Returns `true` if the `Revlog` has zero `entries`.
139 /// Returns `true` if the `Revlog` has zero `entries`.
142 pub fn is_empty(&self) -> bool {
140 pub fn is_empty(&self) -> bool {
143 self.index.is_empty()
141 self.index.is_empty()
144 }
142 }
145
143
146 /// Returns the node ID for the given revision number, if it exists in this
144 /// Returns the node ID for the given revision number, if it exists in this
147 /// revlog
145 /// revlog
148 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
146 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
149 if rev == NULL_REVISION {
147 if rev == NULL_REVISION {
150 return Some(&NULL_NODE);
148 return Some(&NULL_NODE);
151 }
149 }
152 Some(self.index.get_entry(rev)?.hash())
150 Some(self.index.get_entry(rev)?.hash())
153 }
151 }
154
152
155 /// Return the revision number for the given node ID, if it exists in this
153 /// Return the revision number for the given node ID, if it exists in this
156 /// revlog
154 /// revlog
157 pub fn rev_from_node(
155 pub fn rev_from_node(
158 &self,
156 &self,
159 node: NodePrefix,
157 node: NodePrefix,
160 ) -> Result<Revision, RevlogError> {
158 ) -> Result<Revision, RevlogError> {
161 if node.is_prefix_of(&NULL_NODE) {
159 if node.is_prefix_of(&NULL_NODE) {
162 return Ok(NULL_REVISION);
160 return Ok(NULL_REVISION);
163 }
161 }
164
162
165 if let Some(nodemap) = &self.nodemap {
163 if let Some(nodemap) = &self.nodemap {
166 return nodemap
164 return nodemap
167 .find_bin(&self.index, node)?
165 .find_bin(&self.index, node)?
168 .ok_or(RevlogError::InvalidRevision);
166 .ok_or(RevlogError::InvalidRevision);
169 }
167 }
170
168
171 // Fallback to linear scan when a persistent nodemap is not present.
169 // Fallback to linear scan when a persistent nodemap is not present.
172 // This happens when the persistent-nodemap experimental feature is not
170 // This happens when the persistent-nodemap experimental feature is not
173 // enabled, or for small revlogs.
171 // enabled, or for small revlogs.
174 //
172 //
175 // TODO: consider building a non-persistent nodemap in memory to
173 // TODO: consider building a non-persistent nodemap in memory to
176 // optimize these cases.
174 // optimize these cases.
177 let mut found_by_prefix = None;
175 let mut found_by_prefix = None;
178 for rev in (0..self.len() as Revision).rev() {
176 for rev in (0..self.len() as Revision).rev() {
179 let index_entry =
177 let index_entry =
180 self.index.get_entry(rev).ok_or(HgError::corrupted(
178 self.index.get_entry(rev).ok_or(HgError::corrupted(
181 "revlog references a revision not in the index",
179 "revlog references a revision not in the index",
182 ))?;
180 ))?;
183 if node == *index_entry.hash() {
181 if node == *index_entry.hash() {
184 return Ok(rev);
182 return Ok(rev);
185 }
183 }
186 if node.is_prefix_of(index_entry.hash()) {
184 if node.is_prefix_of(index_entry.hash()) {
187 if found_by_prefix.is_some() {
185 if found_by_prefix.is_some() {
188 return Err(RevlogError::AmbiguousPrefix);
186 return Err(RevlogError::AmbiguousPrefix);
189 }
187 }
190 found_by_prefix = Some(rev)
188 found_by_prefix = Some(rev)
191 }
189 }
192 }
190 }
193 found_by_prefix.ok_or(RevlogError::InvalidRevision)
191 found_by_prefix.ok_or(RevlogError::InvalidRevision)
194 }
192 }
195
193
196 /// Returns whether the given revision exists in this revlog.
194 /// Returns whether the given revision exists in this revlog.
197 pub fn has_rev(&self, rev: Revision) -> bool {
195 pub fn has_rev(&self, rev: Revision) -> bool {
198 self.index.get_entry(rev).is_some()
196 self.index.get_entry(rev).is_some()
199 }
197 }
200
198
201 /// Return the full data associated to a revision.
199 /// Return the full data associated to a revision.
202 ///
200 ///
203 /// All entries required to build the final data out of deltas will be
201 /// All entries required to build the final data out of deltas will be
204 /// retrieved as needed, and the deltas will be applied to the inital
202 /// retrieved as needed, and the deltas will be applied to the inital
205 /// snapshot to rebuild the final data.
203 /// snapshot to rebuild the final data.
206 pub fn get_rev_data(
204 pub fn get_rev_data(
207 &self,
205 &self,
208 rev: Revision,
206 rev: Revision,
209 ) -> Result<Cow<[u8]>, RevlogError> {
207 ) -> Result<Cow<[u8]>, RevlogError> {
210 if rev == NULL_REVISION {
208 if rev == NULL_REVISION {
211 return Ok(Cow::Borrowed(&[]));
209 return Ok(Cow::Borrowed(&[]));
212 };
210 };
213 Ok(self.get_entry(rev)?.data()?)
211 Ok(self.get_entry(rev)?.data()?)
214 }
212 }
215
213
216 /// Check the hash of some given data against the recorded hash.
214 /// Check the hash of some given data against the recorded hash.
217 pub fn check_hash(
215 pub fn check_hash(
218 &self,
216 &self,
219 p1: Revision,
217 p1: Revision,
220 p2: Revision,
218 p2: Revision,
221 expected: &[u8],
219 expected: &[u8],
222 data: &[u8],
220 data: &[u8],
223 ) -> bool {
221 ) -> bool {
224 let e1 = self.index.get_entry(p1);
222 let e1 = self.index.get_entry(p1);
225 let h1 = match e1 {
223 let h1 = match e1 {
226 Some(ref entry) => entry.hash(),
224 Some(ref entry) => entry.hash(),
227 None => &NULL_NODE,
225 None => &NULL_NODE,
228 };
226 };
229 let e2 = self.index.get_entry(p2);
227 let e2 = self.index.get_entry(p2);
230 let h2 = match e2 {
228 let h2 = match e2 {
231 Some(ref entry) => entry.hash(),
229 Some(ref entry) => entry.hash(),
232 None => &NULL_NODE,
230 None => &NULL_NODE,
233 };
231 };
234
232
235 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
233 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
236 }
234 }
237
235
238 /// Build the full data of a revision out its snapshot
236 /// Build the full data of a revision out its snapshot
239 /// and its deltas.
237 /// and its deltas.
240 fn build_data_from_deltas(
238 fn build_data_from_deltas(
241 snapshot: RevlogEntry,
239 snapshot: RevlogEntry,
242 deltas: &[RevlogEntry],
240 deltas: &[RevlogEntry],
243 ) -> Result<Vec<u8>, HgError> {
241 ) -> Result<Vec<u8>, HgError> {
244 let snapshot = snapshot.data_chunk()?;
242 let snapshot = snapshot.data_chunk()?;
245 let deltas = deltas
243 let deltas = deltas
246 .iter()
244 .iter()
247 .rev()
245 .rev()
248 .map(RevlogEntry::data_chunk)
246 .map(RevlogEntry::data_chunk)
249 .collect::<Result<Vec<_>, _>>()?;
247 .collect::<Result<Vec<_>, _>>()?;
250 let patches: Vec<_> =
248 let patches: Vec<_> =
251 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
249 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
252 let patch = patch::fold_patch_lists(&patches);
250 let patch = patch::fold_patch_lists(&patches);
253 Ok(patch.apply(&snapshot))
251 Ok(patch.apply(&snapshot))
254 }
252 }
255
253
256 /// Return the revlog data.
254 /// Return the revlog data.
257 fn data(&self) -> &[u8] {
255 fn data(&self) -> &[u8] {
258 match self.data_bytes {
256 match self.data_bytes {
259 Some(ref data_bytes) => &data_bytes,
257 Some(ref data_bytes) => &data_bytes,
260 None => panic!(
258 None => panic!(
261 "forgot to load the data or trying to access inline data"
259 "forgot to load the data or trying to access inline data"
262 ),
260 ),
263 }
261 }
264 }
262 }
265
263
266 pub fn make_null_entry(&self) -> RevlogEntry {
264 pub fn make_null_entry(&self) -> RevlogEntry {
267 RevlogEntry {
265 RevlogEntry {
268 revlog: self,
266 revlog: self,
269 rev: NULL_REVISION,
267 rev: NULL_REVISION,
270 bytes: b"",
268 bytes: b"",
271 compressed_len: 0,
269 compressed_len: 0,
272 uncompressed_len: 0,
270 uncompressed_len: 0,
273 base_rev_or_base_of_delta_chain: None,
271 base_rev_or_base_of_delta_chain: None,
274 p1: NULL_REVISION,
272 p1: NULL_REVISION,
275 p2: NULL_REVISION,
273 p2: NULL_REVISION,
276 flags: NULL_REVLOG_ENTRY_FLAGS,
274 flags: NULL_REVLOG_ENTRY_FLAGS,
277 hash: NULL_NODE,
275 hash: NULL_NODE,
278 }
276 }
279 }
277 }
280
278
281 /// Get an entry of the revlog.
279 /// Get an entry of the revlog.
282 pub fn get_entry(
280 pub fn get_entry(
283 &self,
281 &self,
284 rev: Revision,
282 rev: Revision,
285 ) -> Result<RevlogEntry, RevlogError> {
283 ) -> Result<RevlogEntry, RevlogError> {
286 if rev == NULL_REVISION {
284 if rev == NULL_REVISION {
287 return Ok(self.make_null_entry());
285 return Ok(self.make_null_entry());
288 }
286 }
289 let index_entry = self
287 let index_entry = self
290 .index
288 .index
291 .get_entry(rev)
289 .get_entry(rev)
292 .ok_or(RevlogError::InvalidRevision)?;
290 .ok_or(RevlogError::InvalidRevision)?;
293 let start = index_entry.offset();
291 let start = index_entry.offset();
294 let end = start + index_entry.compressed_len() as usize;
292 let end = start + index_entry.compressed_len() as usize;
295 let data = if self.index.is_inline() {
293 let data = if self.index.is_inline() {
296 self.index.data(start, end)
294 self.index.data(start, end)
297 } else {
295 } else {
298 &self.data()[start..end]
296 &self.data()[start..end]
299 };
297 };
300 let entry = RevlogEntry {
298 let entry = RevlogEntry {
301 revlog: self,
299 revlog: self,
302 rev,
300 rev,
303 bytes: data,
301 bytes: data,
304 compressed_len: index_entry.compressed_len(),
302 compressed_len: index_entry.compressed_len(),
305 uncompressed_len: index_entry.uncompressed_len(),
303 uncompressed_len: index_entry.uncompressed_len(),
306 base_rev_or_base_of_delta_chain: if index_entry
304 base_rev_or_base_of_delta_chain: if index_entry
307 .base_revision_or_base_of_delta_chain()
305 .base_revision_or_base_of_delta_chain()
308 == rev
306 == rev
309 {
307 {
310 None
308 None
311 } else {
309 } else {
312 Some(index_entry.base_revision_or_base_of_delta_chain())
310 Some(index_entry.base_revision_or_base_of_delta_chain())
313 },
311 },
314 p1: index_entry.p1(),
312 p1: index_entry.p1(),
315 p2: index_entry.p2(),
313 p2: index_entry.p2(),
316 flags: index_entry.flags(),
314 flags: index_entry.flags(),
317 hash: *index_entry.hash(),
315 hash: *index_entry.hash(),
318 };
316 };
319 Ok(entry)
317 Ok(entry)
320 }
318 }
321
319
322 /// when resolving internal references within revlog, any errors
320 /// when resolving internal references within revlog, any errors
323 /// should be reported as corruption, instead of e.g. "invalid revision"
321 /// should be reported as corruption, instead of e.g. "invalid revision"
324 fn get_entry_internal(
322 fn get_entry_internal(
325 &self,
323 &self,
326 rev: Revision,
324 rev: Revision,
327 ) -> Result<RevlogEntry, HgError> {
325 ) -> Result<RevlogEntry, HgError> {
328 self.get_entry(rev)
326 self.get_entry(rev)
329 .map_err(|_| corrupted(format!("revision {} out of range", rev)))
327 .map_err(|_| corrupted(format!("revision {} out of range", rev)))
330 }
328 }
331 }
329 }
332
330
333 /// The revlog entry's bytes and the necessary informations to extract
331 /// The revlog entry's bytes and the necessary informations to extract
334 /// the entry's data.
332 /// the entry's data.
335 #[derive(Clone)]
333 #[derive(Clone)]
336 pub struct RevlogEntry<'a> {
334 pub struct RevlogEntry<'a> {
337 revlog: &'a Revlog,
335 revlog: &'a Revlog,
338 rev: Revision,
336 rev: Revision,
339 bytes: &'a [u8],
337 bytes: &'a [u8],
340 compressed_len: u32,
338 compressed_len: u32,
341 uncompressed_len: i32,
339 uncompressed_len: i32,
342 base_rev_or_base_of_delta_chain: Option<Revision>,
340 base_rev_or_base_of_delta_chain: Option<Revision>,
343 p1: Revision,
341 p1: Revision,
344 p2: Revision,
342 p2: Revision,
345 flags: u16,
343 flags: u16,
346 hash: Node,
344 hash: Node,
347 }
345 }
348
346
349 impl<'a> RevlogEntry<'a> {
347 impl<'a> RevlogEntry<'a> {
350 pub fn revision(&self) -> Revision {
348 pub fn revision(&self) -> Revision {
351 self.rev
349 self.rev
352 }
350 }
353
351
354 pub fn node(&self) -> &Node {
352 pub fn node(&self) -> &Node {
355 &self.hash
353 &self.hash
356 }
354 }
357
355
358 pub fn uncompressed_len(&self) -> Option<u32> {
356 pub fn uncompressed_len(&self) -> Option<u32> {
359 u32::try_from(self.uncompressed_len).ok()
357 u32::try_from(self.uncompressed_len).ok()
360 }
358 }
361
359
362 pub fn has_p1(&self) -> bool {
360 pub fn has_p1(&self) -> bool {
363 self.p1 != NULL_REVISION
361 self.p1 != NULL_REVISION
364 }
362 }
365
363
366 pub fn p1_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
364 pub fn p1_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
367 if self.p1 == NULL_REVISION {
365 if self.p1 == NULL_REVISION {
368 Ok(None)
366 Ok(None)
369 } else {
367 } else {
370 Ok(Some(self.revlog.get_entry(self.p1)?))
368 Ok(Some(self.revlog.get_entry(self.p1)?))
371 }
369 }
372 }
370 }
373
371
374 pub fn p2_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
372 pub fn p2_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
375 if self.p2 == NULL_REVISION {
373 if self.p2 == NULL_REVISION {
376 Ok(None)
374 Ok(None)
377 } else {
375 } else {
378 Ok(Some(self.revlog.get_entry(self.p2)?))
376 Ok(Some(self.revlog.get_entry(self.p2)?))
379 }
377 }
380 }
378 }
381
379
382 pub fn p1(&self) -> Option<Revision> {
380 pub fn p1(&self) -> Option<Revision> {
383 if self.p1 == NULL_REVISION {
381 if self.p1 == NULL_REVISION {
384 None
382 None
385 } else {
383 } else {
386 Some(self.p1)
384 Some(self.p1)
387 }
385 }
388 }
386 }
389
387
390 pub fn p2(&self) -> Option<Revision> {
388 pub fn p2(&self) -> Option<Revision> {
391 if self.p2 == NULL_REVISION {
389 if self.p2 == NULL_REVISION {
392 None
390 None
393 } else {
391 } else {
394 Some(self.p2)
392 Some(self.p2)
395 }
393 }
396 }
394 }
397
395
398 pub fn is_censored(&self) -> bool {
396 pub fn is_censored(&self) -> bool {
399 (self.flags & REVISION_FLAG_CENSORED) != 0
397 (self.flags & REVISION_FLAG_CENSORED) != 0
400 }
398 }
401
399
402 pub fn has_length_affecting_flag_processor(&self) -> bool {
400 pub fn has_length_affecting_flag_processor(&self) -> bool {
403 // Relevant Python code: revlog.size()
401 // Relevant Python code: revlog.size()
404 // note: ELLIPSIS is known to not change the content
402 // note: ELLIPSIS is known to not change the content
405 (self.flags & (REVIDX_KNOWN_FLAGS ^ REVISION_FLAG_ELLIPSIS)) != 0
403 (self.flags & (REVIDX_KNOWN_FLAGS ^ REVISION_FLAG_ELLIPSIS)) != 0
406 }
404 }
407
405
408 /// The data for this entry, after resolving deltas if any.
406 /// The data for this entry, after resolving deltas if any.
409 pub fn rawdata(&self) -> Result<Cow<'a, [u8]>, HgError> {
407 pub fn rawdata(&self) -> Result<Cow<'a, [u8]>, HgError> {
410 let mut entry = self.clone();
408 let mut entry = self.clone();
411 let mut delta_chain = vec![];
409 let mut delta_chain = vec![];
412
410
413 // The meaning of `base_rev_or_base_of_delta_chain` depends on
411 // The meaning of `base_rev_or_base_of_delta_chain` depends on
414 // generaldelta. See the doc on `ENTRY_DELTA_BASE` in
412 // generaldelta. See the doc on `ENTRY_DELTA_BASE` in
415 // `mercurial/revlogutils/constants.py` and the code in
413 // `mercurial/revlogutils/constants.py` and the code in
416 // [_chaininfo] and in [index_deltachain].
414 // [_chaininfo] and in [index_deltachain].
417 let uses_generaldelta = self.revlog.index.uses_generaldelta();
415 let uses_generaldelta = self.revlog.index.uses_generaldelta();
418 while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
416 while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
419 let base_rev = if uses_generaldelta {
417 let base_rev = if uses_generaldelta {
420 base_rev
418 base_rev
421 } else {
419 } else {
422 entry.rev - 1
420 entry.rev - 1
423 };
421 };
424 delta_chain.push(entry);
422 delta_chain.push(entry);
425 entry = self.revlog.get_entry_internal(base_rev)?;
423 entry = self.revlog.get_entry_internal(base_rev)?;
426 }
424 }
427
425
428 let data = if delta_chain.is_empty() {
426 let data = if delta_chain.is_empty() {
429 entry.data_chunk()?
427 entry.data_chunk()?
430 } else {
428 } else {
431 Revlog::build_data_from_deltas(entry, &delta_chain)?.into()
429 Revlog::build_data_from_deltas(entry, &delta_chain)?.into()
432 };
430 };
433
431
434 Ok(data)
432 Ok(data)
435 }
433 }
436
434
437 fn check_data(
435 fn check_data(
438 &self,
436 &self,
439 data: Cow<'a, [u8]>,
437 data: Cow<'a, [u8]>,
440 ) -> Result<Cow<'a, [u8]>, HgError> {
438 ) -> Result<Cow<'a, [u8]>, HgError> {
441 if self.revlog.check_hash(
439 if self.revlog.check_hash(
442 self.p1,
440 self.p1,
443 self.p2,
441 self.p2,
444 self.hash.as_bytes(),
442 self.hash.as_bytes(),
445 &data,
443 &data,
446 ) {
444 ) {
447 Ok(data)
445 Ok(data)
448 } else {
446 } else {
449 if (self.flags & REVISION_FLAG_ELLIPSIS) != 0 {
447 if (self.flags & REVISION_FLAG_ELLIPSIS) != 0 {
450 return Err(HgError::unsupported(
448 return Err(HgError::unsupported(
451 "ellipsis revisions are not supported by rhg",
449 "ellipsis revisions are not supported by rhg",
452 ));
450 ));
453 }
451 }
454 Err(corrupted(format!(
452 Err(corrupted(format!(
455 "hash check failed for revision {}",
453 "hash check failed for revision {}",
456 self.rev
454 self.rev
457 )))
455 )))
458 }
456 }
459 }
457 }
460
458
461 pub fn data(&self) -> Result<Cow<'a, [u8]>, HgError> {
459 pub fn data(&self) -> Result<Cow<'a, [u8]>, HgError> {
462 let data = self.rawdata()?;
460 let data = self.rawdata()?;
463 if self.is_censored() {
461 if self.is_censored() {
464 return Err(HgError::CensoredNodeError);
462 return Err(HgError::CensoredNodeError);
465 }
463 }
466 self.check_data(data)
464 self.check_data(data)
467 }
465 }
468
466
469 /// Extract the data contained in the entry.
467 /// Extract the data contained in the entry.
470 /// This may be a delta. (See `is_delta`.)
468 /// This may be a delta. (See `is_delta`.)
471 fn data_chunk(&self) -> Result<Cow<'a, [u8]>, HgError> {
469 fn data_chunk(&self) -> Result<Cow<'a, [u8]>, HgError> {
472 if self.bytes.is_empty() {
470 if self.bytes.is_empty() {
473 return Ok(Cow::Borrowed(&[]));
471 return Ok(Cow::Borrowed(&[]));
474 }
472 }
475 match self.bytes[0] {
473 match self.bytes[0] {
476 // Revision data is the entirety of the entry, including this
474 // Revision data is the entirety of the entry, including this
477 // header.
475 // header.
478 b'\0' => Ok(Cow::Borrowed(self.bytes)),
476 b'\0' => Ok(Cow::Borrowed(self.bytes)),
479 // Raw revision data follows.
477 // Raw revision data follows.
480 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
478 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
481 // zlib (RFC 1950) data.
479 // zlib (RFC 1950) data.
482 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
480 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
483 // zstd data.
481 // zstd data.
484 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
482 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
485 // A proper new format should have had a repo/store requirement.
483 // A proper new format should have had a repo/store requirement.
486 format_type => Err(corrupted(format!(
484 format_type => Err(corrupted(format!(
487 "unknown compression header '{}'",
485 "unknown compression header '{}'",
488 format_type
486 format_type
489 ))),
487 ))),
490 }
488 }
491 }
489 }
492
490
493 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, HgError> {
491 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, HgError> {
494 let mut decoder = ZlibDecoder::new(self.bytes);
492 let mut decoder = ZlibDecoder::new(self.bytes);
495 if self.is_delta() {
493 if self.is_delta() {
496 let mut buf = Vec::with_capacity(self.compressed_len as usize);
494 let mut buf = Vec::with_capacity(self.compressed_len as usize);
497 decoder
495 decoder
498 .read_to_end(&mut buf)
496 .read_to_end(&mut buf)
499 .map_err(|e| corrupted(e.to_string()))?;
497 .map_err(|e| corrupted(e.to_string()))?;
500 Ok(buf)
498 Ok(buf)
501 } else {
499 } else {
502 let cap = self.uncompressed_len.max(0) as usize;
500 let cap = self.uncompressed_len.max(0) as usize;
503 let mut buf = vec![0; cap];
501 let mut buf = vec![0; cap];
504 decoder
502 decoder
505 .read_exact(&mut buf)
503 .read_exact(&mut buf)
506 .map_err(|e| corrupted(e.to_string()))?;
504 .map_err(|e| corrupted(e.to_string()))?;
507 Ok(buf)
505 Ok(buf)
508 }
506 }
509 }
507 }
510
508
511 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, HgError> {
509 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, HgError> {
512 if self.is_delta() {
510 if self.is_delta() {
513 let mut buf = Vec::with_capacity(self.compressed_len as usize);
511 let mut buf = Vec::with_capacity(self.compressed_len as usize);
514 zstd::stream::copy_decode(self.bytes, &mut buf)
512 zstd::stream::copy_decode(self.bytes, &mut buf)
515 .map_err(|e| corrupted(e.to_string()))?;
513 .map_err(|e| corrupted(e.to_string()))?;
516 Ok(buf)
514 Ok(buf)
517 } else {
515 } else {
518 let cap = self.uncompressed_len.max(0) as usize;
516 let cap = self.uncompressed_len.max(0) as usize;
519 let mut buf = vec![0; cap];
517 let mut buf = vec![0; cap];
520 let len = zstd::bulk::decompress_to_buffer(self.bytes, &mut buf)
518 let len = zstd::bulk::decompress_to_buffer(self.bytes, &mut buf)
521 .map_err(|e| corrupted(e.to_string()))?;
519 .map_err(|e| corrupted(e.to_string()))?;
522 if len != self.uncompressed_len as usize {
520 if len != self.uncompressed_len as usize {
523 Err(corrupted("uncompressed length does not match"))
521 Err(corrupted("uncompressed length does not match"))
524 } else {
522 } else {
525 Ok(buf)
523 Ok(buf)
526 }
524 }
527 }
525 }
528 }
526 }
529
527
530 /// Tell if the entry is a snapshot or a delta
528 /// Tell if the entry is a snapshot or a delta
531 /// (influences on decompression).
529 /// (influences on decompression).
532 fn is_delta(&self) -> bool {
530 fn is_delta(&self) -> bool {
533 self.base_rev_or_base_of_delta_chain.is_some()
531 self.base_rev_or_base_of_delta_chain.is_some()
534 }
532 }
535 }
533 }
536
534
537 /// Calculate the hash of a revision given its data and its parents.
535 /// Calculate the hash of a revision given its data and its parents.
538 fn hash(
536 fn hash(
539 data: &[u8],
537 data: &[u8],
540 p1_hash: &[u8],
538 p1_hash: &[u8],
541 p2_hash: &[u8],
539 p2_hash: &[u8],
542 ) -> [u8; NODE_BYTES_LENGTH] {
540 ) -> [u8; NODE_BYTES_LENGTH] {
543 let mut hasher = Sha1::new();
541 let mut hasher = Sha1::new();
544 let (a, b) = (p1_hash, p2_hash);
542 let (a, b) = (p1_hash, p2_hash);
545 if a > b {
543 if a > b {
546 hasher.update(b);
544 hasher.update(b);
547 hasher.update(a);
545 hasher.update(a);
548 } else {
546 } else {
549 hasher.update(a);
547 hasher.update(a);
550 hasher.update(b);
548 hasher.update(b);
551 }
549 }
552 hasher.update(data);
550 hasher.update(data);
553 *hasher.finalize().as_ref()
551 *hasher.finalize().as_ref()
554 }
552 }
555
553
556 #[cfg(test)]
554 #[cfg(test)]
557 mod tests {
555 mod tests {
558 use super::*;
556 use super::*;
559 use crate::index::{IndexEntryBuilder, INDEX_ENTRY_SIZE};
557 use crate::index::{IndexEntryBuilder, INDEX_ENTRY_SIZE};
560 use itertools::Itertools;
558 use itertools::Itertools;
561
559
562 #[test]
560 #[test]
563 fn test_empty() {
561 fn test_empty() {
564 let temp = tempfile::tempdir().unwrap();
562 let temp = tempfile::tempdir().unwrap();
565 let vfs = Vfs { base: temp.path() };
563 let vfs = Vfs { base: temp.path() };
566 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
564 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
567 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
565 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
568 assert!(revlog.is_empty());
566 assert!(revlog.is_empty());
569 assert_eq!(revlog.len(), 0);
567 assert_eq!(revlog.len(), 0);
570 assert!(revlog.get_entry(0).is_err());
568 assert!(revlog.get_entry(0).is_err());
571 assert!(!revlog.has_rev(0));
569 assert!(!revlog.has_rev(0));
572 }
570 }
573
571
574 #[test]
572 #[test]
575 fn test_inline() {
573 fn test_inline() {
576 let temp = tempfile::tempdir().unwrap();
574 let temp = tempfile::tempdir().unwrap();
577 let vfs = Vfs { base: temp.path() };
575 let vfs = Vfs { base: temp.path() };
578 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
576 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
579 .unwrap();
577 .unwrap();
580 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
578 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
581 .unwrap();
579 .unwrap();
582 let node2 = Node::from_hex("dd6ad206e907be60927b5a3117b97dffb2590582")
580 let node2 = Node::from_hex("dd6ad206e907be60927b5a3117b97dffb2590582")
583 .unwrap();
581 .unwrap();
584 let entry0_bytes = IndexEntryBuilder::new()
582 let entry0_bytes = IndexEntryBuilder::new()
585 .is_first(true)
583 .is_first(true)
586 .with_version(1)
584 .with_version(1)
587 .with_inline(true)
585 .with_inline(true)
588 .with_offset(INDEX_ENTRY_SIZE)
586 .with_offset(INDEX_ENTRY_SIZE)
589 .with_node(node0)
587 .with_node(node0)
590 .build();
588 .build();
591 let entry1_bytes = IndexEntryBuilder::new()
589 let entry1_bytes = IndexEntryBuilder::new()
592 .with_offset(INDEX_ENTRY_SIZE)
590 .with_offset(INDEX_ENTRY_SIZE)
593 .with_node(node1)
591 .with_node(node1)
594 .build();
592 .build();
595 let entry2_bytes = IndexEntryBuilder::new()
593 let entry2_bytes = IndexEntryBuilder::new()
596 .with_offset(INDEX_ENTRY_SIZE)
594 .with_offset(INDEX_ENTRY_SIZE)
597 .with_p1(0)
595 .with_p1(0)
598 .with_p2(1)
596 .with_p2(1)
599 .with_node(node2)
597 .with_node(node2)
600 .build();
598 .build();
601 let contents = vec![entry0_bytes, entry1_bytes, entry2_bytes]
599 let contents = vec![entry0_bytes, entry1_bytes, entry2_bytes]
602 .into_iter()
600 .into_iter()
603 .flatten()
601 .flatten()
604 .collect_vec();
602 .collect_vec();
605 std::fs::write(temp.path().join("foo.i"), contents).unwrap();
603 std::fs::write(temp.path().join("foo.i"), contents).unwrap();
606 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
604 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
607
605
608 let entry0 = revlog.get_entry(0).ok().unwrap();
606 let entry0 = revlog.get_entry(0).ok().unwrap();
609 assert_eq!(entry0.revision(), 0);
607 assert_eq!(entry0.revision(), 0);
610 assert_eq!(*entry0.node(), node0);
608 assert_eq!(*entry0.node(), node0);
611 assert!(!entry0.has_p1());
609 assert!(!entry0.has_p1());
612 assert_eq!(entry0.p1(), None);
610 assert_eq!(entry0.p1(), None);
613 assert_eq!(entry0.p2(), None);
611 assert_eq!(entry0.p2(), None);
614 let p1_entry = entry0.p1_entry().unwrap();
612 let p1_entry = entry0.p1_entry().unwrap();
615 assert!(p1_entry.is_none());
613 assert!(p1_entry.is_none());
616 let p2_entry = entry0.p2_entry().unwrap();
614 let p2_entry = entry0.p2_entry().unwrap();
617 assert!(p2_entry.is_none());
615 assert!(p2_entry.is_none());
618
616
619 let entry1 = revlog.get_entry(1).ok().unwrap();
617 let entry1 = revlog.get_entry(1).ok().unwrap();
620 assert_eq!(entry1.revision(), 1);
618 assert_eq!(entry1.revision(), 1);
621 assert_eq!(*entry1.node(), node1);
619 assert_eq!(*entry1.node(), node1);
622 assert!(!entry1.has_p1());
620 assert!(!entry1.has_p1());
623 assert_eq!(entry1.p1(), None);
621 assert_eq!(entry1.p1(), None);
624 assert_eq!(entry1.p2(), None);
622 assert_eq!(entry1.p2(), None);
625 let p1_entry = entry1.p1_entry().unwrap();
623 let p1_entry = entry1.p1_entry().unwrap();
626 assert!(p1_entry.is_none());
624 assert!(p1_entry.is_none());
627 let p2_entry = entry1.p2_entry().unwrap();
625 let p2_entry = entry1.p2_entry().unwrap();
628 assert!(p2_entry.is_none());
626 assert!(p2_entry.is_none());
629
627
630 let entry2 = revlog.get_entry(2).ok().unwrap();
628 let entry2 = revlog.get_entry(2).ok().unwrap();
631 assert_eq!(entry2.revision(), 2);
629 assert_eq!(entry2.revision(), 2);
632 assert_eq!(*entry2.node(), node2);
630 assert_eq!(*entry2.node(), node2);
633 assert!(entry2.has_p1());
631 assert!(entry2.has_p1());
634 assert_eq!(entry2.p1(), Some(0));
632 assert_eq!(entry2.p1(), Some(0));
635 assert_eq!(entry2.p2(), Some(1));
633 assert_eq!(entry2.p2(), Some(1));
636 let p1_entry = entry2.p1_entry().unwrap();
634 let p1_entry = entry2.p1_entry().unwrap();
637 assert!(p1_entry.is_some());
635 assert!(p1_entry.is_some());
638 assert_eq!(p1_entry.unwrap().revision(), 0);
636 assert_eq!(p1_entry.unwrap().revision(), 0);
639 let p2_entry = entry2.p2_entry().unwrap();
637 let p2_entry = entry2.p2_entry().unwrap();
640 assert!(p2_entry.is_some());
638 assert!(p2_entry.is_some());
641 assert_eq!(p2_entry.unwrap().revision(), 1);
639 assert_eq!(p2_entry.unwrap().revision(), 1);
642 }
640 }
643 }
641 }
General Comments 0
You need to be logged in to leave comments. Login now