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()