##// END OF EJS Templates
rhg: handle null changelog and manifest revisions...
Arseniy Alekseyev -
r49012:61ce70fd default
parent child Browse files
Show More
@@ -0,0 +1,23 b''
1 Create a repo such that the changelog entry refers to a null manifest node:
2
3 $ hg init a
4 $ cd a
5 $ hg log
6 $ touch x
7 $ hg add x
8 $ hg commit -m "init"
9 $ hg rm x
10 $ hg commit -q --amend
11
12 $ wc -c < .hg/store/00manifest.i
13 0
14
15 Make sure that the manifest can be read (and is empty):
16
17 $ hg --config rhg.on-unsupported=abort files -r .
18 [1]
19
20 Test a null changelog rev, too:
21
22 $ hg --config rhg.on-unsupported=abort files -r 0000000000000000000000000000000000000000
23 [1]
@@ -1,67 +1,67 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::revlog::node::NULL_NODE;
3 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::Revision;
5 use crate::revlog::Revision;
5 use crate::revlog::{Node, NodePrefix};
6 use crate::revlog::{Node, NodePrefix};
6
7
7 /// A specialized `Revlog` to work with `changelog` data format.
8 /// A specialized `Revlog` to work with `changelog` data format.
8 pub struct Changelog {
9 pub struct Changelog {
9 /// The generic `revlog` format.
10 /// The generic `revlog` format.
10 pub(crate) revlog: Revlog,
11 pub(crate) revlog: Revlog,
11 }
12 }
12
13
13 impl Changelog {
14 impl Changelog {
14 /// Open the `changelog` of a repository given by its root.
15 /// Open the `changelog` of a repository given by its root.
15 pub fn open(repo: &Repo) -> Result<Self, HgError> {
16 pub fn open(repo: &Repo) -> Result<Self, HgError> {
16 let revlog = Revlog::open(repo, "00changelog.i", None)?;
17 let revlog = Revlog::open(repo, "00changelog.i", None)?;
17 Ok(Self { revlog })
18 Ok(Self { revlog })
18 }
19 }
19
20
20 /// Return the `ChangelogEntry` for the given node ID.
21 /// Return the `ChangelogEntry` for the given node ID.
21 pub fn data_for_node(
22 pub fn data_for_node(
22 &self,
23 &self,
23 node: NodePrefix,
24 node: NodePrefix,
24 ) -> Result<ChangelogEntry, RevlogError> {
25 ) -> Result<ChangelogEntry, RevlogError> {
25 let rev = self.revlog.rev_from_node(node)?;
26 let rev = self.revlog.rev_from_node(node)?;
26 self.data_for_rev(rev)
27 self.data_for_rev(rev)
27 }
28 }
28
29
29 /// Return the `ChangelogEntry` of the given revision number.
30 /// Return the `ChangelogEntry` of the given revision number.
30 pub fn data_for_rev(
31 pub fn data_for_rev(
31 &self,
32 &self,
32 rev: Revision,
33 rev: Revision,
33 ) -> Result<ChangelogEntry, RevlogError> {
34 ) -> Result<ChangelogEntry, RevlogError> {
34 let bytes = self.revlog.get_rev_data(rev)?;
35 let bytes = self.revlog.get_rev_data(rev)?;
35 Ok(ChangelogEntry { bytes })
36 Ok(ChangelogEntry { bytes })
36 }
37 }
37
38
38 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
39 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
39 self.revlog.node_from_rev(rev)
40 self.revlog.node_from_rev(rev)
40 }
41 }
41 }
42 }
42
43
43 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
44 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
44 #[derive(Debug)]
45 #[derive(Debug)]
45 pub struct ChangelogEntry {
46 pub struct ChangelogEntry {
46 /// The data bytes of the `changelog` entry.
47 /// The data bytes of the `changelog` entry.
47 bytes: Vec<u8>,
48 bytes: Vec<u8>,
48 }
49 }
49
50
50 impl ChangelogEntry {
51 impl ChangelogEntry {
51 /// Return an iterator over the lines of the entry.
52 /// Return an iterator over the lines of the entry.
52 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
53 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
53 self.bytes
54 self.bytes
54 .split(|b| b == &b'\n')
55 .split(|b| b == &b'\n')
55 .filter(|line| !line.is_empty())
56 .filter(|line| !line.is_empty())
56 }
57 }
57
58
58 /// Return the node id of the `manifest` referenced by this `changelog`
59 /// Return the node id of the `manifest` referenced by this `changelog`
59 /// entry.
60 /// entry.
60 pub fn manifest_node(&self) -> Result<Node, HgError> {
61 pub fn manifest_node(&self) -> Result<Node, HgError> {
61 Node::from_hex_for_repo(
62 match self.lines().next() {
62 self.lines()
63 None => Ok(NULL_NODE),
63 .next()
64 Some(x) => Node::from_hex_for_repo(x),
64 .ok_or_else(|| HgError::corrupted("empty changelog entry"))?,
65 }
65 )
66 }
66 }
67 }
67 }
@@ -1,403 +1,406 b''
1 use std::convert::TryInto;
1 use std::convert::TryInto;
2 use std::ops::Deref;
2 use std::ops::Deref;
3
3
4 use byteorder::{BigEndian, ByteOrder};
4 use byteorder::{BigEndian, ByteOrder};
5
5
6 use crate::errors::HgError;
6 use crate::errors::HgError;
7 use crate::revlog::node::Node;
7 use crate::revlog::node::Node;
8 use crate::revlog::{Revision, NULL_REVISION};
8 use crate::revlog::{Revision, NULL_REVISION};
9
9
10 pub const INDEX_ENTRY_SIZE: usize = 64;
10 pub const INDEX_ENTRY_SIZE: usize = 64;
11
11
12 /// A Revlog index
12 /// A Revlog index
13 pub struct Index {
13 pub struct Index {
14 bytes: Box<dyn Deref<Target = [u8]> + Send>,
14 bytes: Box<dyn Deref<Target = [u8]> + Send>,
15 /// Offsets of starts of index blocks.
15 /// Offsets of starts of index blocks.
16 /// Only needed when the index is interleaved with data.
16 /// Only needed when the index is interleaved with data.
17 offsets: Option<Vec<usize>>,
17 offsets: Option<Vec<usize>>,
18 }
18 }
19
19
20 impl Index {
20 impl Index {
21 /// Create an index from bytes.
21 /// Create an index from bytes.
22 /// Calculate the start of each entry when is_inline is true.
22 /// Calculate the start of each entry when is_inline is true.
23 pub fn new(
23 pub fn new(
24 bytes: Box<dyn Deref<Target = [u8]> + Send>,
24 bytes: Box<dyn Deref<Target = [u8]> + Send>,
25 ) -> Result<Self, HgError> {
25 ) -> Result<Self, HgError> {
26 if is_inline(&bytes) {
26 if is_inline(&bytes) {
27 let mut offset: usize = 0;
27 let mut offset: usize = 0;
28 let mut offsets = Vec::new();
28 let mut offsets = Vec::new();
29
29
30 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
30 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
31 offsets.push(offset);
31 offsets.push(offset);
32 let end = offset + INDEX_ENTRY_SIZE;
32 let end = offset + INDEX_ENTRY_SIZE;
33 let entry = IndexEntry {
33 let entry = IndexEntry {
34 bytes: &bytes[offset..end],
34 bytes: &bytes[offset..end],
35 offset_override: None,
35 offset_override: None,
36 };
36 };
37
37
38 offset += INDEX_ENTRY_SIZE + entry.compressed_len();
38 offset += INDEX_ENTRY_SIZE + entry.compressed_len();
39 }
39 }
40
40
41 if offset == bytes.len() {
41 if offset == bytes.len() {
42 Ok(Self {
42 Ok(Self {
43 bytes,
43 bytes,
44 offsets: Some(offsets),
44 offsets: Some(offsets),
45 })
45 })
46 } else {
46 } else {
47 Err(HgError::corrupted("unexpected inline revlog length")
47 Err(HgError::corrupted("unexpected inline revlog length")
48 .into())
48 .into())
49 }
49 }
50 } else {
50 } else {
51 Ok(Self {
51 Ok(Self {
52 bytes,
52 bytes,
53 offsets: None,
53 offsets: None,
54 })
54 })
55 }
55 }
56 }
56 }
57
57
58 /// Value of the inline flag.
58 /// Value of the inline flag.
59 pub fn is_inline(&self) -> bool {
59 pub fn is_inline(&self) -> bool {
60 is_inline(&self.bytes)
60 is_inline(&self.bytes)
61 }
61 }
62
62
63 /// Return a slice of bytes if `revlog` is inline. Panic if not.
63 /// Return a slice of bytes if `revlog` is inline. Panic if not.
64 pub fn data(&self, start: usize, end: usize) -> &[u8] {
64 pub fn data(&self, start: usize, end: usize) -> &[u8] {
65 if !self.is_inline() {
65 if !self.is_inline() {
66 panic!("tried to access data in the index of a revlog that is not inline");
66 panic!("tried to access data in the index of a revlog that is not inline");
67 }
67 }
68 &self.bytes[start..end]
68 &self.bytes[start..end]
69 }
69 }
70
70
71 /// Return number of entries of the revlog index.
71 /// Return number of entries of the revlog index.
72 pub fn len(&self) -> usize {
72 pub fn len(&self) -> usize {
73 if let Some(offsets) = &self.offsets {
73 if let Some(offsets) = &self.offsets {
74 offsets.len()
74 offsets.len()
75 } else {
75 } else {
76 self.bytes.len() / INDEX_ENTRY_SIZE
76 self.bytes.len() / INDEX_ENTRY_SIZE
77 }
77 }
78 }
78 }
79
79
80 /// Returns `true` if the `Index` has zero `entries`.
80 /// Returns `true` if the `Index` has zero `entries`.
81 pub fn is_empty(&self) -> bool {
81 pub fn is_empty(&self) -> bool {
82 self.len() == 0
82 self.len() == 0
83 }
83 }
84
84
85 /// Return the index entry corresponding to the given revision if it
85 /// Return the index entry corresponding to the given revision if it
86 /// exists.
86 /// exists.
87 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
87 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
88 if rev == NULL_REVISION {
88 if rev == NULL_REVISION {
89 return None;
89 return None;
90 }
90 }
91 if let Some(offsets) = &self.offsets {
91 if let Some(offsets) = &self.offsets {
92 self.get_entry_inline(rev, offsets)
92 self.get_entry_inline(rev, offsets)
93 } else {
93 } else {
94 self.get_entry_separated(rev)
94 self.get_entry_separated(rev)
95 }
95 }
96 }
96 }
97
97
98 fn get_entry_inline(
98 fn get_entry_inline(
99 &self,
99 &self,
100 rev: Revision,
100 rev: Revision,
101 offsets: &[usize],
101 offsets: &[usize],
102 ) -> Option<IndexEntry> {
102 ) -> Option<IndexEntry> {
103 let start = *offsets.get(rev as usize)?;
103 let start = *offsets.get(rev as usize)?;
104 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
104 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
105 let bytes = &self.bytes[start..end];
105 let bytes = &self.bytes[start..end];
106
106
107 // See IndexEntry for an explanation of this override.
107 // See IndexEntry for an explanation of this override.
108 let offset_override = Some(end);
108 let offset_override = Some(end);
109
109
110 Some(IndexEntry {
110 Some(IndexEntry {
111 bytes,
111 bytes,
112 offset_override,
112 offset_override,
113 })
113 })
114 }
114 }
115
115
116 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
116 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
117 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
117 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
118 if rev as usize >= max_rev {
118 if rev as usize >= max_rev {
119 return None;
119 return None;
120 }
120 }
121 let start = rev as usize * INDEX_ENTRY_SIZE;
121 let start = rev as usize * INDEX_ENTRY_SIZE;
122 let end = start + INDEX_ENTRY_SIZE;
122 let end = start + INDEX_ENTRY_SIZE;
123 let bytes = &self.bytes[start..end];
123 let bytes = &self.bytes[start..end];
124
124
125 // Override the offset of the first revision as its bytes are used
125 // Override the offset of the first revision as its bytes are used
126 // for the index's metadata (saving space because it is always 0)
126 // for the index's metadata (saving space because it is always 0)
127 let offset_override = if rev == 0 { Some(0) } else { None };
127 let offset_override = if rev == 0 { Some(0) } else { None };
128
128
129 Some(IndexEntry {
129 Some(IndexEntry {
130 bytes,
130 bytes,
131 offset_override,
131 offset_override,
132 })
132 })
133 }
133 }
134 }
134 }
135
135
136 impl super::RevlogIndex for Index {
136 impl super::RevlogIndex for Index {
137 fn len(&self) -> usize {
137 fn len(&self) -> usize {
138 self.len()
138 self.len()
139 }
139 }
140
140
141 fn node(&self, rev: Revision) -> Option<&Node> {
141 fn node(&self, rev: Revision) -> Option<&Node> {
142 self.get_entry(rev).map(|entry| entry.hash())
142 self.get_entry(rev).map(|entry| entry.hash())
143 }
143 }
144 }
144 }
145
145
146 #[derive(Debug)]
146 #[derive(Debug)]
147 pub struct IndexEntry<'a> {
147 pub struct IndexEntry<'a> {
148 bytes: &'a [u8],
148 bytes: &'a [u8],
149 /// Allows to override the offset value of the entry.
149 /// Allows to override the offset value of the entry.
150 ///
150 ///
151 /// For interleaved index and data, the offset stored in the index
151 /// For interleaved index and data, the offset stored in the index
152 /// corresponds to the separated data offset.
152 /// corresponds to the separated data offset.
153 /// It has to be overridden with the actual offset in the interleaved
153 /// It has to be overridden with the actual offset in the interleaved
154 /// index which is just after the index block.
154 /// index which is just after the index block.
155 ///
155 ///
156 /// For separated index and data, the offset stored in the first index
156 /// For separated index and data, the offset stored in the first index
157 /// entry is mixed with the index headers.
157 /// entry is mixed with the index headers.
158 /// It has to be overridden with 0.
158 /// It has to be overridden with 0.
159 offset_override: Option<usize>,
159 offset_override: Option<usize>,
160 }
160 }
161
161
162 impl<'a> IndexEntry<'a> {
162 impl<'a> IndexEntry<'a> {
163 /// Return the offset of the data.
163 /// Return the offset of the data.
164 pub fn offset(&self) -> usize {
164 pub fn offset(&self) -> usize {
165 if let Some(offset_override) = self.offset_override {
165 if let Some(offset_override) = self.offset_override {
166 offset_override
166 offset_override
167 } else {
167 } else {
168 let mut bytes = [0; 8];
168 let mut bytes = [0; 8];
169 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
169 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
170 BigEndian::read_u64(&bytes[..]) as usize
170 BigEndian::read_u64(&bytes[..]) as usize
171 }
171 }
172 }
172 }
173
173
174 /// Return the compressed length of the data.
174 /// Return the compressed length of the data.
175 pub fn compressed_len(&self) -> usize {
175 pub fn compressed_len(&self) -> usize {
176 BigEndian::read_u32(&self.bytes[8..=11]) as usize
176 BigEndian::read_u32(&self.bytes[8..=11]) as usize
177 }
177 }
178
178
179 /// Return the uncompressed length of the data.
179 /// Return the uncompressed length of the data.
180 pub fn uncompressed_len(&self) -> usize {
180 pub fn uncompressed_len(&self) -> usize {
181 BigEndian::read_u32(&self.bytes[12..=15]) as usize
181 BigEndian::read_u32(&self.bytes[12..=15]) as usize
182 }
182 }
183
183
184 /// Return the revision upon which the data has been derived.
184 /// Return the revision upon which the data has been derived.
185 pub fn base_revision(&self) -> Revision {
185 pub fn base_revision(&self) -> Revision {
186 // TODO Maybe return an Option when base_revision == rev?
186 // TODO Maybe return an Option when base_revision == rev?
187 // Requires to add rev to IndexEntry
187 // Requires to add rev to IndexEntry
188
188
189 BigEndian::read_i32(&self.bytes[16..])
189 BigEndian::read_i32(&self.bytes[16..])
190 }
190 }
191
191
192 pub fn p1(&self) -> Revision {
192 pub fn p1(&self) -> Revision {
193 BigEndian::read_i32(&self.bytes[24..])
193 BigEndian::read_i32(&self.bytes[24..])
194 }
194 }
195
195
196 pub fn p2(&self) -> Revision {
196 pub fn p2(&self) -> Revision {
197 BigEndian::read_i32(&self.bytes[28..])
197 BigEndian::read_i32(&self.bytes[28..])
198 }
198 }
199
199
200 /// Return the hash of revision's full text.
200 /// Return the hash of revision's full text.
201 ///
201 ///
202 /// Currently, SHA-1 is used and only the first 20 bytes of this field
202 /// Currently, SHA-1 is used and only the first 20 bytes of this field
203 /// are used.
203 /// are used.
204 pub fn hash(&self) -> &'a Node {
204 pub fn hash(&self) -> &'a Node {
205 (&self.bytes[32..52]).try_into().unwrap()
205 (&self.bytes[32..52]).try_into().unwrap()
206 }
206 }
207 }
207 }
208
208
209 /// Value of the inline flag.
209 /// Value of the inline flag.
210 pub fn is_inline(index_bytes: &[u8]) -> bool {
210 pub fn is_inline(index_bytes: &[u8]) -> bool {
211 if index_bytes.len() < 4 {
212 return true;
213 }
211 match &index_bytes[0..=1] {
214 match &index_bytes[0..=1] {
212 [0, 0] | [0, 2] => false,
215 [0, 0] | [0, 2] => false,
213 _ => true,
216 _ => true,
214 }
217 }
215 }
218 }
216
219
217 #[cfg(test)]
220 #[cfg(test)]
218 mod tests {
221 mod tests {
219 use super::*;
222 use super::*;
220
223
221 #[cfg(test)]
224 #[cfg(test)]
222 #[derive(Debug, Copy, Clone)]
225 #[derive(Debug, Copy, Clone)]
223 pub struct IndexEntryBuilder {
226 pub struct IndexEntryBuilder {
224 is_first: bool,
227 is_first: bool,
225 is_inline: bool,
228 is_inline: bool,
226 is_general_delta: bool,
229 is_general_delta: bool,
227 version: u16,
230 version: u16,
228 offset: usize,
231 offset: usize,
229 compressed_len: usize,
232 compressed_len: usize,
230 uncompressed_len: usize,
233 uncompressed_len: usize,
231 base_revision: Revision,
234 base_revision: Revision,
232 }
235 }
233
236
234 #[cfg(test)]
237 #[cfg(test)]
235 impl IndexEntryBuilder {
238 impl IndexEntryBuilder {
236 pub fn new() -> Self {
239 pub fn new() -> Self {
237 Self {
240 Self {
238 is_first: false,
241 is_first: false,
239 is_inline: false,
242 is_inline: false,
240 is_general_delta: true,
243 is_general_delta: true,
241 version: 2,
244 version: 2,
242 offset: 0,
245 offset: 0,
243 compressed_len: 0,
246 compressed_len: 0,
244 uncompressed_len: 0,
247 uncompressed_len: 0,
245 base_revision: 0,
248 base_revision: 0,
246 }
249 }
247 }
250 }
248
251
249 pub fn is_first(&mut self, value: bool) -> &mut Self {
252 pub fn is_first(&mut self, value: bool) -> &mut Self {
250 self.is_first = value;
253 self.is_first = value;
251 self
254 self
252 }
255 }
253
256
254 pub fn with_inline(&mut self, value: bool) -> &mut Self {
257 pub fn with_inline(&mut self, value: bool) -> &mut Self {
255 self.is_inline = value;
258 self.is_inline = value;
256 self
259 self
257 }
260 }
258
261
259 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
262 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
260 self.is_general_delta = value;
263 self.is_general_delta = value;
261 self
264 self
262 }
265 }
263
266
264 pub fn with_version(&mut self, value: u16) -> &mut Self {
267 pub fn with_version(&mut self, value: u16) -> &mut Self {
265 self.version = value;
268 self.version = value;
266 self
269 self
267 }
270 }
268
271
269 pub fn with_offset(&mut self, value: usize) -> &mut Self {
272 pub fn with_offset(&mut self, value: usize) -> &mut Self {
270 self.offset = value;
273 self.offset = value;
271 self
274 self
272 }
275 }
273
276
274 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
277 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
275 self.compressed_len = value;
278 self.compressed_len = value;
276 self
279 self
277 }
280 }
278
281
279 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
282 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
280 self.uncompressed_len = value;
283 self.uncompressed_len = value;
281 self
284 self
282 }
285 }
283
286
284 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
287 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
285 self.base_revision = value;
288 self.base_revision = value;
286 self
289 self
287 }
290 }
288
291
289 pub fn build(&self) -> Vec<u8> {
292 pub fn build(&self) -> Vec<u8> {
290 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
293 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
291 if self.is_first {
294 if self.is_first {
292 bytes.extend(&match (self.is_general_delta, self.is_inline) {
295 bytes.extend(&match (self.is_general_delta, self.is_inline) {
293 (false, false) => [0u8, 0],
296 (false, false) => [0u8, 0],
294 (false, true) => [0u8, 1],
297 (false, true) => [0u8, 1],
295 (true, false) => [0u8, 2],
298 (true, false) => [0u8, 2],
296 (true, true) => [0u8, 3],
299 (true, true) => [0u8, 3],
297 });
300 });
298 bytes.extend(&self.version.to_be_bytes());
301 bytes.extend(&self.version.to_be_bytes());
299 // Remaining offset bytes.
302 // Remaining offset bytes.
300 bytes.extend(&[0u8; 2]);
303 bytes.extend(&[0u8; 2]);
301 } else {
304 } else {
302 // Offset stored on 48 bits (6 bytes)
305 // Offset stored on 48 bits (6 bytes)
303 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
306 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
304 }
307 }
305 bytes.extend(&[0u8; 2]); // Revision flags.
308 bytes.extend(&[0u8; 2]); // Revision flags.
306 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
309 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
307 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
310 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
308 bytes.extend(&self.base_revision.to_be_bytes());
311 bytes.extend(&self.base_revision.to_be_bytes());
309 bytes
312 bytes
310 }
313 }
311 }
314 }
312
315
313 #[test]
316 #[test]
314 fn is_not_inline_when_no_inline_flag_test() {
317 fn is_not_inline_when_no_inline_flag_test() {
315 let bytes = IndexEntryBuilder::new()
318 let bytes = IndexEntryBuilder::new()
316 .is_first(true)
319 .is_first(true)
317 .with_general_delta(false)
320 .with_general_delta(false)
318 .with_inline(false)
321 .with_inline(false)
319 .build();
322 .build();
320
323
321 assert_eq!(is_inline(&bytes), false)
324 assert_eq!(is_inline(&bytes), false)
322 }
325 }
323
326
324 #[test]
327 #[test]
325 fn is_inline_when_inline_flag_test() {
328 fn is_inline_when_inline_flag_test() {
326 let bytes = IndexEntryBuilder::new()
329 let bytes = IndexEntryBuilder::new()
327 .is_first(true)
330 .is_first(true)
328 .with_general_delta(false)
331 .with_general_delta(false)
329 .with_inline(true)
332 .with_inline(true)
330 .build();
333 .build();
331
334
332 assert_eq!(is_inline(&bytes), true)
335 assert_eq!(is_inline(&bytes), true)
333 }
336 }
334
337
335 #[test]
338 #[test]
336 fn is_inline_when_inline_and_generaldelta_flags_test() {
339 fn is_inline_when_inline_and_generaldelta_flags_test() {
337 let bytes = IndexEntryBuilder::new()
340 let bytes = IndexEntryBuilder::new()
338 .is_first(true)
341 .is_first(true)
339 .with_general_delta(true)
342 .with_general_delta(true)
340 .with_inline(true)
343 .with_inline(true)
341 .build();
344 .build();
342
345
343 assert_eq!(is_inline(&bytes), true)
346 assert_eq!(is_inline(&bytes), true)
344 }
347 }
345
348
346 #[test]
349 #[test]
347 fn test_offset() {
350 fn test_offset() {
348 let bytes = IndexEntryBuilder::new().with_offset(1).build();
351 let bytes = IndexEntryBuilder::new().with_offset(1).build();
349 let entry = IndexEntry {
352 let entry = IndexEntry {
350 bytes: &bytes,
353 bytes: &bytes,
351 offset_override: None,
354 offset_override: None,
352 };
355 };
353
356
354 assert_eq!(entry.offset(), 1)
357 assert_eq!(entry.offset(), 1)
355 }
358 }
356
359
357 #[test]
360 #[test]
358 fn test_with_overridden_offset() {
361 fn test_with_overridden_offset() {
359 let bytes = IndexEntryBuilder::new().with_offset(1).build();
362 let bytes = IndexEntryBuilder::new().with_offset(1).build();
360 let entry = IndexEntry {
363 let entry = IndexEntry {
361 bytes: &bytes,
364 bytes: &bytes,
362 offset_override: Some(2),
365 offset_override: Some(2),
363 };
366 };
364
367
365 assert_eq!(entry.offset(), 2)
368 assert_eq!(entry.offset(), 2)
366 }
369 }
367
370
368 #[test]
371 #[test]
369 fn test_compressed_len() {
372 fn test_compressed_len() {
370 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
373 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
371 let entry = IndexEntry {
374 let entry = IndexEntry {
372 bytes: &bytes,
375 bytes: &bytes,
373 offset_override: None,
376 offset_override: None,
374 };
377 };
375
378
376 assert_eq!(entry.compressed_len(), 1)
379 assert_eq!(entry.compressed_len(), 1)
377 }
380 }
378
381
379 #[test]
382 #[test]
380 fn test_uncompressed_len() {
383 fn test_uncompressed_len() {
381 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
384 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
382 let entry = IndexEntry {
385 let entry = IndexEntry {
383 bytes: &bytes,
386 bytes: &bytes,
384 offset_override: None,
387 offset_override: None,
385 };
388 };
386
389
387 assert_eq!(entry.uncompressed_len(), 1)
390 assert_eq!(entry.uncompressed_len(), 1)
388 }
391 }
389
392
390 #[test]
393 #[test]
391 fn test_base_revision() {
394 fn test_base_revision() {
392 let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
395 let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
393 let entry = IndexEntry {
396 let entry = IndexEntry {
394 bytes: &bytes,
397 bytes: &bytes,
395 offset_override: None,
398 offset_override: None,
396 };
399 };
397
400
398 assert_eq!(entry.base_revision(), 1)
401 assert_eq!(entry.base_revision(), 1)
399 }
402 }
400 }
403 }
401
404
402 #[cfg(test)]
405 #[cfg(test)]
403 pub use tests::IndexEntryBuilder;
406 pub use tests::IndexEntryBuilder;
@@ -1,412 +1,423 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 byteorder::{BigEndian, ByteOrder};
6 use byteorder::{BigEndian, ByteOrder};
7 use flate2::read::ZlibDecoder;
7 use flate2::read::ZlibDecoder;
8 use micro_timer::timed;
8 use micro_timer::timed;
9 use sha1::{Digest, Sha1};
9 use sha1::{Digest, Sha1};
10 use zstd;
10 use zstd;
11
11
12 use super::index::Index;
12 use super::index::Index;
13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
14 use super::nodemap;
14 use super::nodemap;
15 use super::nodemap::{NodeMap, NodeMapError};
15 use super::nodemap::{NodeMap, NodeMapError};
16 use super::nodemap_docket::NodeMapDocket;
16 use super::nodemap_docket::NodeMapDocket;
17 use super::patch;
17 use super::patch;
18 use crate::errors::HgError;
18 use crate::errors::HgError;
19 use crate::repo::Repo;
19 use crate::repo::Repo;
20 use crate::revlog::Revision;
20 use crate::revlog::Revision;
21 use crate::{Node, NULL_REVISION};
21 use crate::{Node, NULL_REVISION};
22
22
23 #[derive(derive_more::From)]
23 #[derive(derive_more::From)]
24 pub enum RevlogError {
24 pub enum RevlogError {
25 InvalidRevision,
25 InvalidRevision,
26 /// Working directory is not supported
26 /// Working directory is not supported
27 WDirUnsupported,
27 WDirUnsupported,
28 /// Found more than one entry whose ID match the requested prefix
28 /// Found more than one entry whose ID match the requested prefix
29 AmbiguousPrefix,
29 AmbiguousPrefix,
30 #[from]
30 #[from]
31 Other(HgError),
31 Other(HgError),
32 }
32 }
33
33
34 impl From<NodeMapError> for RevlogError {
34 impl From<NodeMapError> for RevlogError {
35 fn from(error: NodeMapError) -> Self {
35 fn from(error: NodeMapError) -> Self {
36 match error {
36 match error {
37 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
37 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
38 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
38 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
39 }
39 }
40 }
40 }
41 }
41 }
42
42
43 impl RevlogError {
43 impl RevlogError {
44 fn corrupted() -> Self {
44 fn corrupted() -> Self {
45 RevlogError::Other(HgError::corrupted("corrupted revlog"))
45 RevlogError::Other(HgError::corrupted("corrupted revlog"))
46 }
46 }
47 }
47 }
48
48
49 /// Read only implementation of revlog.
49 /// Read only implementation of revlog.
50 pub struct Revlog {
50 pub struct Revlog {
51 /// When index and data are not interleaved: bytes of the revlog index.
51 /// When index and data are not interleaved: bytes of the revlog index.
52 /// When index and data are interleaved: bytes of the revlog index and
52 /// When index and data are interleaved: bytes of the revlog index and
53 /// data.
53 /// data.
54 index: Index,
54 index: Index,
55 /// When index and data are not interleaved: bytes of the revlog data
55 /// When index and data are not interleaved: bytes of the revlog data
56 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
56 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
57 /// When present on disk: the persistent nodemap for this revlog
57 /// When present on disk: the persistent nodemap for this revlog
58 nodemap: Option<nodemap::NodeTree>,
58 nodemap: Option<nodemap::NodeTree>,
59 }
59 }
60
60
61 impl Revlog {
61 impl Revlog {
62 /// Open a revlog index file.
62 /// Open a revlog index file.
63 ///
63 ///
64 /// It will also open the associated data file if index and data are not
64 /// It will also open the associated data file if index and data are not
65 /// interleaved.
65 /// interleaved.
66 #[timed]
66 #[timed]
67 pub fn open(
67 pub fn open(
68 repo: &Repo,
68 repo: &Repo,
69 index_path: impl AsRef<Path>,
69 index_path: impl AsRef<Path>,
70 data_path: Option<&Path>,
70 data_path: Option<&Path>,
71 ) -> Result<Self, HgError> {
71 ) -> Result<Self, HgError> {
72 let index_path = index_path.as_ref();
72 let index_path = index_path.as_ref();
73 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
73 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
74
74
75 let version = get_version(&index_mmap);
75 let version = get_version(&index_mmap)?;
76 if version != 1 {
76 if version != 1 {
77 // A proper new version should have had a repo/store requirement.
77 // A proper new version should have had a repo/store requirement.
78 return Err(HgError::corrupted("corrupted revlog"));
78 return Err(HgError::corrupted("corrupted revlog"));
79 }
79 }
80
80
81 let index = Index::new(Box::new(index_mmap))?;
81 let index = Index::new(Box::new(index_mmap))?;
82
82
83 let default_data_path = index_path.with_extension("d");
83 let default_data_path = index_path.with_extension("d");
84
84
85 // type annotation required
85 // type annotation required
86 // won't recognize Mmap as Deref<Target = [u8]>
86 // won't recognize Mmap as Deref<Target = [u8]>
87 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
87 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
88 if index.is_inline() {
88 if index.is_inline() {
89 None
89 None
90 } else {
90 } else {
91 let data_path = data_path.unwrap_or(&default_data_path);
91 let data_path = data_path.unwrap_or(&default_data_path);
92 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
92 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
93 Some(Box::new(data_mmap))
93 Some(Box::new(data_mmap))
94 };
94 };
95
95
96 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
96 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
97 |(docket, data)| {
97 |(docket, data)| {
98 nodemap::NodeTree::load_bytes(
98 nodemap::NodeTree::load_bytes(
99 Box::new(data),
99 Box::new(data),
100 docket.data_length,
100 docket.data_length,
101 )
101 )
102 },
102 },
103 );
103 );
104
104
105 Ok(Revlog {
105 Ok(Revlog {
106 index,
106 index,
107 data_bytes,
107 data_bytes,
108 nodemap,
108 nodemap,
109 })
109 })
110 }
110 }
111
111
112 /// Return number of entries of the `Revlog`.
112 /// Return number of entries of the `Revlog`.
113 pub fn len(&self) -> usize {
113 pub fn len(&self) -> usize {
114 self.index.len()
114 self.index.len()
115 }
115 }
116
116
117 /// Returns `true` if the `Revlog` has zero `entries`.
117 /// Returns `true` if the `Revlog` has zero `entries`.
118 pub fn is_empty(&self) -> bool {
118 pub fn is_empty(&self) -> bool {
119 self.index.is_empty()
119 self.index.is_empty()
120 }
120 }
121
121
122 /// Returns the node ID for the given revision number, if it exists in this
122 /// Returns the node ID for the given revision number, if it exists in this
123 /// revlog
123 /// revlog
124 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
124 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
125 Some(self.index.get_entry(rev)?.hash())
125 Some(self.index.get_entry(rev)?.hash())
126 }
126 }
127
127
128 /// Return the revision number for the given node ID, if it exists in this
128 /// Return the revision number for the given node ID, if it exists in this
129 /// revlog
129 /// revlog
130 #[timed]
130 #[timed]
131 pub fn rev_from_node(
131 pub fn rev_from_node(
132 &self,
132 &self,
133 node: NodePrefix,
133 node: NodePrefix,
134 ) -> Result<Revision, RevlogError> {
134 ) -> Result<Revision, RevlogError> {
135 if node.is_prefix_of(&NULL_NODE) {
135 if node.is_prefix_of(&NULL_NODE) {
136 return Ok(NULL_REVISION);
136 return Ok(NULL_REVISION);
137 }
137 }
138
138
139 if let Some(nodemap) = &self.nodemap {
139 if let Some(nodemap) = &self.nodemap {
140 return nodemap
140 return nodemap
141 .find_bin(&self.index, node)?
141 .find_bin(&self.index, node)?
142 .ok_or(RevlogError::InvalidRevision);
142 .ok_or(RevlogError::InvalidRevision);
143 }
143 }
144
144
145 // Fallback to linear scan when a persistent nodemap is not present.
145 // Fallback to linear scan when a persistent nodemap is not present.
146 // This happens when the persistent-nodemap experimental feature is not
146 // This happens when the persistent-nodemap experimental feature is not
147 // enabled, or for small revlogs.
147 // enabled, or for small revlogs.
148 //
148 //
149 // TODO: consider building a non-persistent nodemap in memory to
149 // TODO: consider building a non-persistent nodemap in memory to
150 // optimize these cases.
150 // optimize these cases.
151 let mut found_by_prefix = None;
151 let mut found_by_prefix = None;
152 for rev in (0..self.len() as Revision).rev() {
152 for rev in (0..self.len() as Revision).rev() {
153 let index_entry =
153 let index_entry =
154 self.index.get_entry(rev).ok_or(HgError::corrupted(
154 self.index.get_entry(rev).ok_or(HgError::corrupted(
155 "revlog references a revision not in the index",
155 "revlog references a revision not in the index",
156 ))?;
156 ))?;
157 if node == *index_entry.hash() {
157 if node == *index_entry.hash() {
158 return Ok(rev);
158 return Ok(rev);
159 }
159 }
160 if node.is_prefix_of(index_entry.hash()) {
160 if node.is_prefix_of(index_entry.hash()) {
161 if found_by_prefix.is_some() {
161 if found_by_prefix.is_some() {
162 return Err(RevlogError::AmbiguousPrefix);
162 return Err(RevlogError::AmbiguousPrefix);
163 }
163 }
164 found_by_prefix = Some(rev)
164 found_by_prefix = Some(rev)
165 }
165 }
166 }
166 }
167 found_by_prefix.ok_or(RevlogError::InvalidRevision)
167 found_by_prefix.ok_or(RevlogError::InvalidRevision)
168 }
168 }
169
169
170 /// Returns whether the given revision exists in this revlog.
170 /// Returns whether the given revision exists in this revlog.
171 pub fn has_rev(&self, rev: Revision) -> bool {
171 pub fn has_rev(&self, rev: Revision) -> bool {
172 self.index.get_entry(rev).is_some()
172 self.index.get_entry(rev).is_some()
173 }
173 }
174
174
175 /// Return the full data associated to a revision.
175 /// Return the full data associated to a revision.
176 ///
176 ///
177 /// All entries required to build the final data out of deltas will be
177 /// All entries required to build the final data out of deltas will be
178 /// retrieved as needed, and the deltas will be applied to the inital
178 /// retrieved as needed, and the deltas will be applied to the inital
179 /// snapshot to rebuild the final data.
179 /// snapshot to rebuild the final data.
180 #[timed]
180 #[timed]
181 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
181 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
182 if rev == NULL_REVISION {
183 return Ok(vec![]);
184 };
182 // Todo return -> Cow
185 // Todo return -> Cow
183 let mut entry = self.get_entry(rev)?;
186 let mut entry = self.get_entry(rev)?;
184 let mut delta_chain = vec![];
187 let mut delta_chain = vec![];
185 while let Some(base_rev) = entry.base_rev {
188 while let Some(base_rev) = entry.base_rev {
186 delta_chain.push(entry);
189 delta_chain.push(entry);
187 entry = self
190 entry = self
188 .get_entry(base_rev)
191 .get_entry(base_rev)
189 .map_err(|_| RevlogError::corrupted())?;
192 .map_err(|_| RevlogError::corrupted())?;
190 }
193 }
191
194
192 // TODO do not look twice in the index
195 // TODO do not look twice in the index
193 let index_entry = self
196 let index_entry = self
194 .index
197 .index
195 .get_entry(rev)
198 .get_entry(rev)
196 .ok_or(RevlogError::InvalidRevision)?;
199 .ok_or(RevlogError::InvalidRevision)?;
197
200
198 let data: Vec<u8> = if delta_chain.is_empty() {
201 let data: Vec<u8> = if delta_chain.is_empty() {
199 entry.data()?.into()
202 entry.data()?.into()
200 } else {
203 } else {
201 Revlog::build_data_from_deltas(entry, &delta_chain)?
204 Revlog::build_data_from_deltas(entry, &delta_chain)?
202 };
205 };
203
206
204 if self.check_hash(
207 if self.check_hash(
205 index_entry.p1(),
208 index_entry.p1(),
206 index_entry.p2(),
209 index_entry.p2(),
207 index_entry.hash().as_bytes(),
210 index_entry.hash().as_bytes(),
208 &data,
211 &data,
209 ) {
212 ) {
210 Ok(data)
213 Ok(data)
211 } else {
214 } else {
212 Err(RevlogError::corrupted())
215 Err(RevlogError::corrupted())
213 }
216 }
214 }
217 }
215
218
216 /// Check the hash of some given data against the recorded hash.
219 /// Check the hash of some given data against the recorded hash.
217 pub fn check_hash(
220 pub fn check_hash(
218 &self,
221 &self,
219 p1: Revision,
222 p1: Revision,
220 p2: Revision,
223 p2: Revision,
221 expected: &[u8],
224 expected: &[u8],
222 data: &[u8],
225 data: &[u8],
223 ) -> bool {
226 ) -> bool {
224 let e1 = self.index.get_entry(p1);
227 let e1 = self.index.get_entry(p1);
225 let h1 = match e1 {
228 let h1 = match e1 {
226 Some(ref entry) => entry.hash(),
229 Some(ref entry) => entry.hash(),
227 None => &NULL_NODE,
230 None => &NULL_NODE,
228 };
231 };
229 let e2 = self.index.get_entry(p2);
232 let e2 = self.index.get_entry(p2);
230 let h2 = match e2 {
233 let h2 = match e2 {
231 Some(ref entry) => entry.hash(),
234 Some(ref entry) => entry.hash(),
232 None => &NULL_NODE,
235 None => &NULL_NODE,
233 };
236 };
234
237
235 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
238 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
236 }
239 }
237
240
238 /// Build the full data of a revision out its snapshot
241 /// Build the full data of a revision out its snapshot
239 /// and its deltas.
242 /// and its deltas.
240 #[timed]
243 #[timed]
241 fn build_data_from_deltas(
244 fn build_data_from_deltas(
242 snapshot: RevlogEntry,
245 snapshot: RevlogEntry,
243 deltas: &[RevlogEntry],
246 deltas: &[RevlogEntry],
244 ) -> Result<Vec<u8>, RevlogError> {
247 ) -> Result<Vec<u8>, RevlogError> {
245 let snapshot = snapshot.data()?;
248 let snapshot = snapshot.data()?;
246 let deltas = deltas
249 let deltas = deltas
247 .iter()
250 .iter()
248 .rev()
251 .rev()
249 .map(RevlogEntry::data)
252 .map(RevlogEntry::data)
250 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
253 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
251 let patches: Vec<_> =
254 let patches: Vec<_> =
252 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
255 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
253 let patch = patch::fold_patch_lists(&patches);
256 let patch = patch::fold_patch_lists(&patches);
254 Ok(patch.apply(&snapshot))
257 Ok(patch.apply(&snapshot))
255 }
258 }
256
259
257 /// Return the revlog data.
260 /// Return the revlog data.
258 fn data(&self) -> &[u8] {
261 fn data(&self) -> &[u8] {
259 match self.data_bytes {
262 match self.data_bytes {
260 Some(ref data_bytes) => &data_bytes,
263 Some(ref data_bytes) => &data_bytes,
261 None => panic!(
264 None => panic!(
262 "forgot to load the data or trying to access inline data"
265 "forgot to load the data or trying to access inline data"
263 ),
266 ),
264 }
267 }
265 }
268 }
266
269
267 /// Get an entry of the revlog.
270 /// Get an entry of the revlog.
268 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
271 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
269 let index_entry = self
272 let index_entry = self
270 .index
273 .index
271 .get_entry(rev)
274 .get_entry(rev)
272 .ok_or(RevlogError::InvalidRevision)?;
275 .ok_or(RevlogError::InvalidRevision)?;
273 let start = index_entry.offset();
276 let start = index_entry.offset();
274 let end = start + index_entry.compressed_len();
277 let end = start + index_entry.compressed_len();
275 let data = if self.index.is_inline() {
278 let data = if self.index.is_inline() {
276 self.index.data(start, end)
279 self.index.data(start, end)
277 } else {
280 } else {
278 &self.data()[start..end]
281 &self.data()[start..end]
279 };
282 };
280 let entry = RevlogEntry {
283 let entry = RevlogEntry {
281 rev,
284 rev,
282 bytes: data,
285 bytes: data,
283 compressed_len: index_entry.compressed_len(),
286 compressed_len: index_entry.compressed_len(),
284 uncompressed_len: index_entry.uncompressed_len(),
287 uncompressed_len: index_entry.uncompressed_len(),
285 base_rev: if index_entry.base_revision() == rev {
288 base_rev: if index_entry.base_revision() == rev {
286 None
289 None
287 } else {
290 } else {
288 Some(index_entry.base_revision())
291 Some(index_entry.base_revision())
289 },
292 },
290 };
293 };
291 Ok(entry)
294 Ok(entry)
292 }
295 }
293 }
296 }
294
297
295 /// The revlog entry's bytes and the necessary informations to extract
298 /// The revlog entry's bytes and the necessary informations to extract
296 /// the entry's data.
299 /// the entry's data.
297 #[derive(Debug)]
300 #[derive(Debug)]
298 pub struct RevlogEntry<'a> {
301 pub struct RevlogEntry<'a> {
299 rev: Revision,
302 rev: Revision,
300 bytes: &'a [u8],
303 bytes: &'a [u8],
301 compressed_len: usize,
304 compressed_len: usize,
302 uncompressed_len: usize,
305 uncompressed_len: usize,
303 base_rev: Option<Revision>,
306 base_rev: Option<Revision>,
304 }
307 }
305
308
306 impl<'a> RevlogEntry<'a> {
309 impl<'a> RevlogEntry<'a> {
307 pub fn revision(&self) -> Revision {
310 pub fn revision(&self) -> Revision {
308 self.rev
311 self.rev
309 }
312 }
310
313
311 /// Extract the data contained in the entry.
314 /// Extract the data contained in the entry.
312 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
315 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
313 if self.bytes.is_empty() {
316 if self.bytes.is_empty() {
314 return Ok(Cow::Borrowed(&[]));
317 return Ok(Cow::Borrowed(&[]));
315 }
318 }
316 match self.bytes[0] {
319 match self.bytes[0] {
317 // Revision data is the entirety of the entry, including this
320 // Revision data is the entirety of the entry, including this
318 // header.
321 // header.
319 b'\0' => Ok(Cow::Borrowed(self.bytes)),
322 b'\0' => Ok(Cow::Borrowed(self.bytes)),
320 // Raw revision data follows.
323 // Raw revision data follows.
321 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
324 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
322 // zlib (RFC 1950) data.
325 // zlib (RFC 1950) data.
323 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
326 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
324 // zstd data.
327 // zstd data.
325 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
328 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
326 // A proper new format should have had a repo/store requirement.
329 // A proper new format should have had a repo/store requirement.
327 _format_type => Err(RevlogError::corrupted()),
330 _format_type => Err(RevlogError::corrupted()),
328 }
331 }
329 }
332 }
330
333
331 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
334 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
332 let mut decoder = ZlibDecoder::new(self.bytes);
335 let mut decoder = ZlibDecoder::new(self.bytes);
333 if self.is_delta() {
336 if self.is_delta() {
334 let mut buf = Vec::with_capacity(self.compressed_len);
337 let mut buf = Vec::with_capacity(self.compressed_len);
335 decoder
338 decoder
336 .read_to_end(&mut buf)
339 .read_to_end(&mut buf)
337 .map_err(|_| RevlogError::corrupted())?;
340 .map_err(|_| RevlogError::corrupted())?;
338 Ok(buf)
341 Ok(buf)
339 } else {
342 } else {
340 let mut buf = vec![0; self.uncompressed_len];
343 let mut buf = vec![0; self.uncompressed_len];
341 decoder
344 decoder
342 .read_exact(&mut buf)
345 .read_exact(&mut buf)
343 .map_err(|_| RevlogError::corrupted())?;
346 .map_err(|_| RevlogError::corrupted())?;
344 Ok(buf)
347 Ok(buf)
345 }
348 }
346 }
349 }
347
350
348 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
351 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
349 if self.is_delta() {
352 if self.is_delta() {
350 let mut buf = Vec::with_capacity(self.compressed_len);
353 let mut buf = Vec::with_capacity(self.compressed_len);
351 zstd::stream::copy_decode(self.bytes, &mut buf)
354 zstd::stream::copy_decode(self.bytes, &mut buf)
352 .map_err(|_| RevlogError::corrupted())?;
355 .map_err(|_| RevlogError::corrupted())?;
353 Ok(buf)
356 Ok(buf)
354 } else {
357 } else {
355 let mut buf = vec![0; self.uncompressed_len];
358 let mut buf = vec![0; self.uncompressed_len];
356 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
359 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
357 .map_err(|_| RevlogError::corrupted())?;
360 .map_err(|_| RevlogError::corrupted())?;
358 if len != self.uncompressed_len {
361 if len != self.uncompressed_len {
359 Err(RevlogError::corrupted())
362 Err(RevlogError::corrupted())
360 } else {
363 } else {
361 Ok(buf)
364 Ok(buf)
362 }
365 }
363 }
366 }
364 }
367 }
365
368
366 /// Tell if the entry is a snapshot or a delta
369 /// Tell if the entry is a snapshot or a delta
367 /// (influences on decompression).
370 /// (influences on decompression).
368 fn is_delta(&self) -> bool {
371 fn is_delta(&self) -> bool {
369 self.base_rev.is_some()
372 self.base_rev.is_some()
370 }
373 }
371 }
374 }
372
375
373 /// Format version of the revlog.
376 /// Format version of the revlog.
374 pub fn get_version(index_bytes: &[u8]) -> u16 {
377 pub fn get_version(index_bytes: &[u8]) -> Result<u16, HgError> {
375 BigEndian::read_u16(&index_bytes[2..=3])
378 if index_bytes.len() == 0 {
379 return Ok(1);
380 };
381 if index_bytes.len() < 4 {
382 return Err(HgError::corrupted(
383 "corrupted revlog: can't read the index format header",
384 ));
385 };
386 Ok(BigEndian::read_u16(&index_bytes[2..=3]))
376 }
387 }
377
388
378 /// Calculate the hash of a revision given its data and its parents.
389 /// Calculate the hash of a revision given its data and its parents.
379 fn hash(
390 fn hash(
380 data: &[u8],
391 data: &[u8],
381 p1_hash: &[u8],
392 p1_hash: &[u8],
382 p2_hash: &[u8],
393 p2_hash: &[u8],
383 ) -> [u8; NODE_BYTES_LENGTH] {
394 ) -> [u8; NODE_BYTES_LENGTH] {
384 let mut hasher = Sha1::new();
395 let mut hasher = Sha1::new();
385 let (a, b) = (p1_hash, p2_hash);
396 let (a, b) = (p1_hash, p2_hash);
386 if a > b {
397 if a > b {
387 hasher.update(b);
398 hasher.update(b);
388 hasher.update(a);
399 hasher.update(a);
389 } else {
400 } else {
390 hasher.update(a);
401 hasher.update(a);
391 hasher.update(b);
402 hasher.update(b);
392 }
403 }
393 hasher.update(data);
404 hasher.update(data);
394 *hasher.finalize().as_ref()
405 *hasher.finalize().as_ref()
395 }
406 }
396
407
397 #[cfg(test)]
408 #[cfg(test)]
398 mod tests {
409 mod tests {
399 use super::*;
410 use super::*;
400
411
401 use super::super::index::IndexEntryBuilder;
412 use super::super::index::IndexEntryBuilder;
402
413
403 #[test]
414 #[test]
404 fn version_test() {
415 fn version_test() {
405 let bytes = IndexEntryBuilder::new()
416 let bytes = IndexEntryBuilder::new()
406 .is_first(true)
417 .is_first(true)
407 .with_version(1)
418 .with_version(1)
408 .build();
419 .build();
409
420
410 assert_eq!(get_version(&bytes), 1)
421 assert_eq!(get_version(&bytes), 1)
411 }
422 }
412 }
423 }
General Comments 0
You need to be logged in to leave comments. Login now