Show More
@@ -9,6 +9,75 b' use crate::revlog::{Revision, NULL_REVIS' | |||||
9 |
|
9 | |||
10 | pub const INDEX_ENTRY_SIZE: usize = 64; |
|
10 | pub const INDEX_ENTRY_SIZE: usize = 64; | |
11 |
|
11 | |||
|
12 | pub struct IndexHeader { | |||
|
13 | header_bytes: [u8; 4], | |||
|
14 | } | |||
|
15 | ||||
|
16 | #[derive(Copy, Clone)] | |||
|
17 | pub struct IndexHeaderFlags { | |||
|
18 | flags: u16, | |||
|
19 | } | |||
|
20 | ||||
|
21 | /// Corresponds to the high bits of `_format_flags` in python | |||
|
22 | impl IndexHeaderFlags { | |||
|
23 | /// Corresponds to FLAG_INLINE_DATA in python | |||
|
24 | pub fn is_inline(self) -> bool { | |||
|
25 | return self.flags & 1 != 0; | |||
|
26 | } | |||
|
27 | /// Corresponds to FLAG_GENERALDELTA in python | |||
|
28 | pub fn uses_generaldelta(self) -> bool { | |||
|
29 | return self.flags & 2 != 0; | |||
|
30 | } | |||
|
31 | } | |||
|
32 | ||||
|
33 | /// Corresponds to the INDEX_HEADER structure, | |||
|
34 | /// which is parsed as a `header` variable in `_loadindex` in `revlog.py` | |||
|
35 | impl IndexHeader { | |||
|
36 | fn format_flags(&self) -> IndexHeaderFlags { | |||
|
37 | // No "unknown flags" check here, unlike in python. Maybe there should | |||
|
38 | // be. | |||
|
39 | return IndexHeaderFlags { | |||
|
40 | flags: BigEndian::read_u16(&self.header_bytes[0..2]), | |||
|
41 | }; | |||
|
42 | } | |||
|
43 | ||||
|
44 | /// The only revlog version currently supported by rhg. | |||
|
45 | const REVLOGV1: u16 = 1; | |||
|
46 | ||||
|
47 | /// Corresponds to `_format_version` in Python. | |||
|
48 | fn format_version(&self) -> u16 { | |||
|
49 | return BigEndian::read_u16(&self.header_bytes[2..4]); | |||
|
50 | } | |||
|
51 | ||||
|
52 | const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader { | |||
|
53 | // We treat an empty file as a valid index with no entries. | |||
|
54 | // Here we make an arbitrary choice of what we assume the format of the | |||
|
55 | // index to be (V1, using generaldelta). | |||
|
56 | // This doesn't matter too much, since we're only doing read-only | |||
|
57 | // access. but the value corresponds to the `new_header` variable in | |||
|
58 | // `revlog.py`, `_loadindex` | |||
|
59 | header_bytes: [0, 3, 0, 1], | |||
|
60 | }; | |||
|
61 | ||||
|
62 | fn parse(index_bytes: &[u8]) -> Result<IndexHeader, HgError> { | |||
|
63 | if index_bytes.len() == 0 { | |||
|
64 | return Ok(IndexHeader::EMPTY_INDEX_HEADER); | |||
|
65 | } | |||
|
66 | if index_bytes.len() < 4 { | |||
|
67 | return Err(HgError::corrupted( | |||
|
68 | "corrupted revlog: can't read the index format header", | |||
|
69 | )); | |||
|
70 | } | |||
|
71 | return Ok(IndexHeader { | |||
|
72 | header_bytes: { | |||
|
73 | let bytes: [u8; 4] = | |||
|
74 | index_bytes[0..4].try_into().expect("impossible"); | |||
|
75 | bytes | |||
|
76 | }, | |||
|
77 | }); | |||
|
78 | } | |||
|
79 | } | |||
|
80 | ||||
12 | /// A Revlog index |
|
81 | /// A Revlog index | |
13 | pub struct Index { |
|
82 | pub struct Index { | |
14 | bytes: Box<dyn Deref<Target = [u8]> + Send>, |
|
83 | bytes: Box<dyn Deref<Target = [u8]> + Send>, | |
@@ -23,7 +92,15 b' impl Index {' | |||||
23 | pub fn new( |
|
92 | pub fn new( | |
24 | bytes: Box<dyn Deref<Target = [u8]> + Send>, |
|
93 | bytes: Box<dyn Deref<Target = [u8]> + Send>, | |
25 | ) -> Result<Self, HgError> { |
|
94 | ) -> Result<Self, HgError> { | |
26 | if is_inline(&bytes) { |
|
95 | let header = IndexHeader::parse(bytes.as_ref())?; | |
|
96 | ||||
|
97 | if header.format_version() != IndexHeader::REVLOGV1 { | |||
|
98 | // A proper new version should have had a repo/store | |||
|
99 | // requirement. | |||
|
100 | return Err(HgError::corrupted("unsupported revlog version")); | |||
|
101 | } | |||
|
102 | ||||
|
103 | if header.format_flags().is_inline() { | |||
27 | let mut offset: usize = 0; |
|
104 | let mut offset: usize = 0; | |
28 | let mut offsets = Vec::new(); |
|
105 | let mut offsets = Vec::new(); | |
29 |
|
106 | |||
@@ -206,17 +283,6 b" impl<'a> IndexEntry<'a> {" | |||||
206 | } |
|
283 | } | |
207 | } |
|
284 | } | |
208 |
|
285 | |||
209 | /// Value of the inline flag. |
|
|||
210 | pub fn is_inline(index_bytes: &[u8]) -> bool { |
|
|||
211 | if index_bytes.len() < 4 { |
|
|||
212 | return true; |
|
|||
213 | } |
|
|||
214 | match &index_bytes[0..=1] { |
|
|||
215 | [0, 0] | [0, 2] => false, |
|
|||
216 | _ => true, |
|
|||
217 | } |
|
|||
218 | } |
|
|||
219 |
|
||||
220 | #[cfg(test)] |
|
286 | #[cfg(test)] | |
221 | mod tests { |
|
287 | mod tests { | |
222 | use super::*; |
|
288 | use super::*; | |
@@ -313,37 +379,60 b' mod tests {' | |||||
313 | } |
|
379 | } | |
314 | } |
|
380 | } | |
315 |
|
381 | |||
|
382 | pub fn is_inline(index_bytes: &[u8]) -> bool { | |||
|
383 | IndexHeader::parse(index_bytes) | |||
|
384 | .expect("too short") | |||
|
385 | .format_flags() | |||
|
386 | .is_inline() | |||
|
387 | } | |||
|
388 | ||||
|
389 | pub fn uses_generaldelta(index_bytes: &[u8]) -> bool { | |||
|
390 | IndexHeader::parse(index_bytes) | |||
|
391 | .expect("too short") | |||
|
392 | .format_flags() | |||
|
393 | .uses_generaldelta() | |||
|
394 | } | |||
|
395 | ||||
|
396 | pub fn get_version(index_bytes: &[u8]) -> u16 { | |||
|
397 | IndexHeader::parse(index_bytes) | |||
|
398 | .expect("too short") | |||
|
399 | .format_version() | |||
|
400 | } | |||
|
401 | ||||
316 | #[test] |
|
402 | #[test] | |
317 |
fn |
|
403 | fn flags_when_no_inline_flag_test() { | |
318 | let bytes = IndexEntryBuilder::new() |
|
404 | let bytes = IndexEntryBuilder::new() | |
319 | .is_first(true) |
|
405 | .is_first(true) | |
320 | .with_general_delta(false) |
|
406 | .with_general_delta(false) | |
321 | .with_inline(false) |
|
407 | .with_inline(false) | |
322 | .build(); |
|
408 | .build(); | |
323 |
|
409 | |||
324 | assert_eq!(is_inline(&bytes), false) |
|
410 | assert_eq!(is_inline(&bytes), false); | |
|
411 | assert_eq!(uses_generaldelta(&bytes), false); | |||
325 | } |
|
412 | } | |
326 |
|
413 | |||
327 | #[test] |
|
414 | #[test] | |
328 |
fn |
|
415 | fn flags_when_inline_flag_test() { | |
329 | let bytes = IndexEntryBuilder::new() |
|
416 | let bytes = IndexEntryBuilder::new() | |
330 | .is_first(true) |
|
417 | .is_first(true) | |
331 | .with_general_delta(false) |
|
418 | .with_general_delta(false) | |
332 | .with_inline(true) |
|
419 | .with_inline(true) | |
333 | .build(); |
|
420 | .build(); | |
334 |
|
421 | |||
335 | assert_eq!(is_inline(&bytes), true) |
|
422 | assert_eq!(is_inline(&bytes), true); | |
|
423 | assert_eq!(uses_generaldelta(&bytes), false); | |||
336 | } |
|
424 | } | |
337 |
|
425 | |||
338 | #[test] |
|
426 | #[test] | |
339 |
fn |
|
427 | fn flags_when_inline_and_generaldelta_flags_test() { | |
340 | let bytes = IndexEntryBuilder::new() |
|
428 | let bytes = IndexEntryBuilder::new() | |
341 | .is_first(true) |
|
429 | .is_first(true) | |
342 | .with_general_delta(true) |
|
430 | .with_general_delta(true) | |
343 | .with_inline(true) |
|
431 | .with_inline(true) | |
344 | .build(); |
|
432 | .build(); | |
345 |
|
433 | |||
346 | assert_eq!(is_inline(&bytes), true) |
|
434 | assert_eq!(is_inline(&bytes), true); | |
|
435 | assert_eq!(uses_generaldelta(&bytes), true); | |||
347 | } |
|
436 | } | |
348 |
|
437 | |||
349 | #[test] |
|
438 | #[test] | |
@@ -400,6 +489,16 b' mod tests {' | |||||
400 |
|
489 | |||
401 | assert_eq!(entry.base_revision(), 1) |
|
490 | assert_eq!(entry.base_revision(), 1) | |
402 | } |
|
491 | } | |
|
492 | ||||
|
493 | #[test] | |||
|
494 | fn version_test() { | |||
|
495 | let bytes = IndexEntryBuilder::new() | |||
|
496 | .is_first(true) | |||
|
497 | .with_version(1) | |||
|
498 | .build(); | |||
|
499 | ||||
|
500 | assert_eq!(get_version(&bytes), 1) | |||
|
501 | } | |||
403 | } |
|
502 | } | |
404 |
|
503 | |||
405 | #[cfg(test)] |
|
504 | #[cfg(test)] |
@@ -3,7 +3,6 b' 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 byteorder::{BigEndian, ByteOrder}; |
|
|||
7 | use flate2::read::ZlibDecoder; |
|
6 | use flate2::read::ZlibDecoder; | |
8 | use micro_timer::timed; |
|
7 | use micro_timer::timed; | |
9 | use sha1::{Digest, Sha1}; |
|
8 | use sha1::{Digest, Sha1}; | |
@@ -74,13 +73,6 b' impl Revlog {' | |||||
74 | match repo.store_vfs().mmap_open_opt(&index_path)? { |
|
73 | match repo.store_vfs().mmap_open_opt(&index_path)? { | |
75 | None => Index::new(Box::new(vec![])), |
|
74 | None => Index::new(Box::new(vec![])), | |
76 | Some(index_mmap) => { |
|
75 | Some(index_mmap) => { | |
77 | let version = get_version(&index_mmap)?; |
|
|||
78 | if version != 1 { |
|
|||
79 | // A proper new version should have had a repo/store |
|
|||
80 | // requirement. |
|
|||
81 | return Err(HgError::corrupted("corrupted revlog")); |
|
|||
82 | } |
|
|||
83 |
|
||||
84 | let index = Index::new(Box::new(index_mmap))?; |
|
76 | let index = Index::new(Box::new(index_mmap))?; | |
85 | Ok(index) |
|
77 | Ok(index) | |
86 | } |
|
78 | } | |
@@ -387,19 +379,6 b" impl<'a> RevlogEntry<'a> {" | |||||
387 | } |
|
379 | } | |
388 | } |
|
380 | } | |
389 |
|
381 | |||
390 | /// Format version of the revlog. |
|
|||
391 | pub fn get_version(index_bytes: &[u8]) -> Result<u16, HgError> { |
|
|||
392 | if index_bytes.len() == 0 { |
|
|||
393 | return Ok(1); |
|
|||
394 | }; |
|
|||
395 | if index_bytes.len() < 4 { |
|
|||
396 | return Err(HgError::corrupted( |
|
|||
397 | "corrupted revlog: can't read the index format header", |
|
|||
398 | )); |
|
|||
399 | }; |
|
|||
400 | Ok(BigEndian::read_u16(&index_bytes[2..=3])) |
|
|||
401 | } |
|
|||
402 |
|
||||
403 | /// Calculate the hash of a revision given its data and its parents. |
|
382 | /// Calculate the hash of a revision given its data and its parents. | |
404 | fn hash( |
|
383 | fn hash( | |
405 | data: &[u8], |
|
384 | data: &[u8], | |
@@ -418,20 +397,3 b' fn hash(' | |||||
418 | hasher.update(data); |
|
397 | hasher.update(data); | |
419 | *hasher.finalize().as_ref() |
|
398 | *hasher.finalize().as_ref() | |
420 | } |
|
399 | } | |
421 |
|
||||
422 | #[cfg(test)] |
|
|||
423 | mod tests { |
|
|||
424 | use super::*; |
|
|||
425 |
|
||||
426 | use super::super::index::IndexEntryBuilder; |
|
|||
427 |
|
||||
428 | #[test] |
|
|||
429 | fn version_test() { |
|
|||
430 | let bytes = IndexEntryBuilder::new() |
|
|||
431 | .is_first(true) |
|
|||
432 | .with_version(1) |
|
|||
433 | .build(); |
|
|||
434 |
|
||||
435 | assert_eq!(get_version(&bytes).map_err(|_err| ()), Ok(1)) |
|
|||
436 | } |
|
|||
437 | } |
|
General Comments 0
You need to be logged in to leave comments.
Login now