|
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net> |
|
2 // |
|
3 // This software may be used and distributed according to the terms of the |
|
4 // GNU General Public License version 2 or any later version. |
|
5 |
|
6 //! Minimal `RevlogIndex`, readable from standard Mercurial file format |
|
7 use hg::*; |
|
8 use memmap::*; |
|
9 use std::fs::File; |
|
10 use std::ops::Deref; |
|
11 use std::path::Path; |
|
12 use std::slice; |
|
13 |
|
14 pub struct Index { |
|
15 data: Box<dyn Deref<Target = [IndexEntry]> + Send>, |
|
16 } |
|
17 |
|
18 /// A fixed sized index entry. All numbers are big endian |
|
19 #[repr(C)] |
|
20 pub struct IndexEntry { |
|
21 not_used_yet: [u8; 24], |
|
22 p1: Revision, |
|
23 p2: Revision, |
|
24 node: Node, |
|
25 unused_node: [u8; 12], |
|
26 } |
|
27 |
|
28 pub const INDEX_ENTRY_SIZE: usize = 64; |
|
29 |
|
30 impl IndexEntry { |
|
31 fn parents(&self) -> [Revision; 2] { |
|
32 [Revision::from_be(self.p1), Revision::from_be(self.p1)] |
|
33 } |
|
34 } |
|
35 |
|
36 impl RevlogIndex for Index { |
|
37 fn len(&self) -> usize { |
|
38 self.data.len() |
|
39 } |
|
40 |
|
41 fn node(&self, rev: Revision) -> Option<&Node> { |
|
42 if rev == NULL_REVISION { |
|
43 return None; |
|
44 } |
|
45 let i = rev as usize; |
|
46 if i >= self.len() { |
|
47 None |
|
48 } else { |
|
49 Some(&self.data[i].node) |
|
50 } |
|
51 } |
|
52 } |
|
53 |
|
54 impl Graph for &Index { |
|
55 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { |
|
56 let [p1, p2] = (*self).data[rev as usize].parents(); |
|
57 let len = (*self).len(); |
|
58 if p1 < NULL_REVISION |
|
59 || p2 < NULL_REVISION |
|
60 || p1 as usize >= len |
|
61 || p2 as usize >= len |
|
62 { |
|
63 return Err(GraphError::ParentOutOfRange(rev)); |
|
64 } |
|
65 Ok([p1, p2]) |
|
66 } |
|
67 } |
|
68 |
|
69 struct IndexMmap(Mmap); |
|
70 |
|
71 impl Deref for IndexMmap { |
|
72 type Target = [IndexEntry]; |
|
73 |
|
74 fn deref(&self) -> &[IndexEntry] { |
|
75 let ptr = self.0.as_ptr() as *const IndexEntry; |
|
76 // Any misaligned data will be ignored. |
|
77 debug_assert_eq!( |
|
78 self.0.len() % std::mem::align_of::<IndexEntry>(), |
|
79 0, |
|
80 "Misaligned data in mmap" |
|
81 ); |
|
82 unsafe { slice::from_raw_parts(ptr, self.0.len() / INDEX_ENTRY_SIZE) } |
|
83 } |
|
84 } |
|
85 |
|
86 impl Index { |
|
87 pub fn load_mmap(path: impl AsRef<Path>) -> Self { |
|
88 let file = File::open(path).unwrap(); |
|
89 let msg = "Index file is missing, or missing permission"; |
|
90 let mmap = unsafe { MmapOptions::new().map(&file) }.expect(msg); |
|
91 Self { |
|
92 data: Box::new(IndexMmap(mmap)), |
|
93 } |
|
94 } |
|
95 } |