##// END OF EJS Templates
rhg: centralize index header parsing...
Arseniy Alekseyev -
r49288:1fb3615d default
parent child Browse files
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 is_not_inline_when_no_inline_flag_test() {
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 is_inline_when_inline_flag_test() {
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 is_inline_when_inline_and_generaldelta_flags_test() {
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