Mercurial > hg
comparison rust/hg-core/src/revlog/index.rs @ 48458:96ea4db4741b
rhg: fix a crash on non-generaldelta revlogs
Differential Revision: https://phab.mercurial-scm.org/D11882
author | Arseniy Alekseyev <aalekseyev@janestreet.com> |
---|---|
date | Tue, 07 Dec 2021 18:57:43 +0000 |
parents | 1fb3615dfce2 |
children | 0a4ac916673e |
comparison
equal
deleted
inserted
replaced
48457:1fb3615dfce2 | 48458:96ea4db4741b |
---|---|
82 pub struct Index { | 82 pub struct Index { |
83 bytes: Box<dyn Deref<Target = [u8]> + Send>, | 83 bytes: Box<dyn Deref<Target = [u8]> + Send>, |
84 /// Offsets of starts of index blocks. | 84 /// Offsets of starts of index blocks. |
85 /// Only needed when the index is interleaved with data. | 85 /// Only needed when the index is interleaved with data. |
86 offsets: Option<Vec<usize>>, | 86 offsets: Option<Vec<usize>>, |
87 uses_generaldelta: bool, | |
87 } | 88 } |
88 | 89 |
89 impl Index { | 90 impl Index { |
90 /// Create an index from bytes. | 91 /// Create an index from bytes. |
91 /// Calculate the start of each entry when is_inline is true. | 92 /// Calculate the start of each entry when is_inline is true. |
98 // A proper new version should have had a repo/store | 99 // A proper new version should have had a repo/store |
99 // requirement. | 100 // requirement. |
100 return Err(HgError::corrupted("unsupported revlog version")); | 101 return Err(HgError::corrupted("unsupported revlog version")); |
101 } | 102 } |
102 | 103 |
104 // This is only correct because we know version is REVLOGV1. | |
105 // In v2 we always use generaldelta, while in v0 we never use | |
106 // generaldelta. Similar for [is_inline] (it's only used in v1). | |
107 let uses_generaldelta = header.format_flags().uses_generaldelta(); | |
108 | |
103 if header.format_flags().is_inline() { | 109 if header.format_flags().is_inline() { |
104 let mut offset: usize = 0; | 110 let mut offset: usize = 0; |
105 let mut offsets = Vec::new(); | 111 let mut offsets = Vec::new(); |
106 | 112 |
107 while offset + INDEX_ENTRY_SIZE <= bytes.len() { | 113 while offset + INDEX_ENTRY_SIZE <= bytes.len() { |
117 | 123 |
118 if offset == bytes.len() { | 124 if offset == bytes.len() { |
119 Ok(Self { | 125 Ok(Self { |
120 bytes, | 126 bytes, |
121 offsets: Some(offsets), | 127 offsets: Some(offsets), |
128 uses_generaldelta, | |
122 }) | 129 }) |
123 } else { | 130 } else { |
124 Err(HgError::corrupted("unexpected inline revlog length") | 131 Err(HgError::corrupted("unexpected inline revlog length") |
125 .into()) | 132 .into()) |
126 } | 133 } |
127 } else { | 134 } else { |
128 Ok(Self { | 135 Ok(Self { |
129 bytes, | 136 bytes, |
130 offsets: None, | 137 offsets: None, |
138 uses_generaldelta, | |
131 }) | 139 }) |
132 } | 140 } |
141 } | |
142 | |
143 pub fn uses_generaldelta(&self) -> bool { | |
144 self.uses_generaldelta | |
133 } | 145 } |
134 | 146 |
135 /// Value of the inline flag. | 147 /// Value of the inline flag. |
136 pub fn is_inline(&self) -> bool { | 148 pub fn is_inline(&self) -> bool { |
137 self.offsets.is_some() | 149 self.offsets.is_some() |
257 pub fn uncompressed_len(&self) -> usize { | 269 pub fn uncompressed_len(&self) -> usize { |
258 BigEndian::read_u32(&self.bytes[12..=15]) as usize | 270 BigEndian::read_u32(&self.bytes[12..=15]) as usize |
259 } | 271 } |
260 | 272 |
261 /// Return the revision upon which the data has been derived. | 273 /// Return the revision upon which the data has been derived. |
262 pub fn base_revision(&self) -> Revision { | 274 pub fn base_revision_or_base_of_delta_chain(&self) -> Revision { |
263 // TODO Maybe return an Option when base_revision == rev? | 275 // TODO Maybe return an Option when base_revision == rev? |
264 // Requires to add rev to IndexEntry | 276 // Requires to add rev to IndexEntry |
265 | 277 |
266 BigEndian::read_i32(&self.bytes[16..]) | 278 BigEndian::read_i32(&self.bytes[16..]) |
267 } | 279 } |
295 is_general_delta: bool, | 307 is_general_delta: bool, |
296 version: u16, | 308 version: u16, |
297 offset: usize, | 309 offset: usize, |
298 compressed_len: usize, | 310 compressed_len: usize, |
299 uncompressed_len: usize, | 311 uncompressed_len: usize, |
300 base_revision: Revision, | 312 base_revision_or_base_of_delta_chain: Revision, |
301 } | 313 } |
302 | 314 |
303 #[cfg(test)] | 315 #[cfg(test)] |
304 impl IndexEntryBuilder { | 316 impl IndexEntryBuilder { |
305 pub fn new() -> Self { | 317 pub fn new() -> Self { |
309 is_general_delta: true, | 321 is_general_delta: true, |
310 version: 2, | 322 version: 2, |
311 offset: 0, | 323 offset: 0, |
312 compressed_len: 0, | 324 compressed_len: 0, |
313 uncompressed_len: 0, | 325 uncompressed_len: 0, |
314 base_revision: 0, | 326 base_revision_or_base_of_delta_chain: 0, |
315 } | 327 } |
316 } | 328 } |
317 | 329 |
318 pub fn is_first(&mut self, value: bool) -> &mut Self { | 330 pub fn is_first(&mut self, value: bool) -> &mut Self { |
319 self.is_first = value; | 331 self.is_first = value; |
348 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self { | 360 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self { |
349 self.uncompressed_len = value; | 361 self.uncompressed_len = value; |
350 self | 362 self |
351 } | 363 } |
352 | 364 |
353 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self { | 365 pub fn with_base_revision_or_base_of_delta_chain( |
354 self.base_revision = value; | 366 &mut self, |
367 value: Revision, | |
368 ) -> &mut Self { | |
369 self.base_revision_or_base_of_delta_chain = value; | |
355 self | 370 self |
356 } | 371 } |
357 | 372 |
358 pub fn build(&self) -> Vec<u8> { | 373 pub fn build(&self) -> Vec<u8> { |
359 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE); | 374 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE); |
372 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]); | 387 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]); |
373 } | 388 } |
374 bytes.extend(&[0u8; 2]); // Revision flags. | 389 bytes.extend(&[0u8; 2]); // Revision flags. |
375 bytes.extend(&(self.compressed_len as u32).to_be_bytes()); | 390 bytes.extend(&(self.compressed_len as u32).to_be_bytes()); |
376 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes()); | 391 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes()); |
377 bytes.extend(&self.base_revision.to_be_bytes()); | 392 bytes.extend( |
393 &self.base_revision_or_base_of_delta_chain.to_be_bytes(), | |
394 ); | |
378 bytes | 395 bytes |
379 } | 396 } |
380 } | 397 } |
381 | 398 |
382 pub fn is_inline(index_bytes: &[u8]) -> bool { | 399 pub fn is_inline(index_bytes: &[u8]) -> bool { |
478 | 495 |
479 assert_eq!(entry.uncompressed_len(), 1) | 496 assert_eq!(entry.uncompressed_len(), 1) |
480 } | 497 } |
481 | 498 |
482 #[test] | 499 #[test] |
483 fn test_base_revision() { | 500 fn test_base_revision_or_base_of_delta_chain() { |
484 let bytes = IndexEntryBuilder::new().with_base_revision(1).build(); | 501 let bytes = IndexEntryBuilder::new() |
502 .with_base_revision_or_base_of_delta_chain(1) | |
503 .build(); | |
485 let entry = IndexEntry { | 504 let entry = IndexEntry { |
486 bytes: &bytes, | 505 bytes: &bytes, |
487 offset_override: None, | 506 offset_override: None, |
488 }; | 507 }; |
489 | 508 |
490 assert_eq!(entry.base_revision(), 1) | 509 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1) |
491 } | 510 } |
492 | 511 |
493 #[test] | 512 #[test] |
494 fn version_test() { | 513 fn version_test() { |
495 let bytes = IndexEntryBuilder::new() | 514 let bytes = IndexEntryBuilder::new() |