259 /// Offsets of starts of index blocks. |
259 /// Offsets of starts of index blocks. |
260 /// Only needed when the index is interleaved with data. |
260 /// Only needed when the index is interleaved with data. |
261 offsets: RwLock<Option<Vec<usize>>>, |
261 offsets: RwLock<Option<Vec<usize>>>, |
262 uses_generaldelta: bool, |
262 uses_generaldelta: bool, |
263 is_inline: bool, |
263 is_inline: bool, |
264 /// Cache of the head revisions in this index, kept in sync. Should |
264 /// Cache of (head_revisions, filtered_revisions) |
|
265 /// |
|
266 /// The head revisions in this index, kept in sync. Should |
265 /// be accessed via the [`Self::head_revs`] method. |
267 /// be accessed via the [`Self::head_revs`] method. |
266 head_revs: Vec<Revision>, |
268 /// The last filtered revisions in this index, used to make sure |
267 /// Cache of the last filtered revisions in this index, used to make sure |
|
268 /// we haven't changed filters when returning the cached `head_revs`. |
269 /// we haven't changed filters when returning the cached `head_revs`. |
269 filtered_revs: HashSet<Revision>, |
270 head_revs: RwLock<(Vec<Revision>, HashSet<Revision>)>, |
270 } |
271 } |
271 |
272 |
272 impl Debug for Index { |
273 impl Debug for Index { |
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
274 f.debug_struct("Index") |
275 f.debug_struct("Index") |
364 Ok(Self { |
365 Ok(Self { |
365 bytes: IndexData::new(bytes), |
366 bytes: IndexData::new(bytes), |
366 offsets: RwLock::new(Some(offsets)), |
367 offsets: RwLock::new(Some(offsets)), |
367 uses_generaldelta, |
368 uses_generaldelta, |
368 is_inline: true, |
369 is_inline: true, |
369 head_revs: vec![], |
370 head_revs: RwLock::new((vec![], HashSet::new())), |
370 filtered_revs: HashSet::new(), |
|
371 }) |
371 }) |
372 } else { |
372 } else { |
373 Err(HgError::corrupted("unexpected inline revlog length")) |
373 Err(HgError::corrupted("unexpected inline revlog length")) |
374 } |
374 } |
375 } else { |
375 } else { |
376 Ok(Self { |
376 Ok(Self { |
377 bytes: IndexData::new(bytes), |
377 bytes: IndexData::new(bytes), |
378 offsets: RwLock::new(None), |
378 offsets: RwLock::new(None), |
379 uses_generaldelta, |
379 uses_generaldelta, |
380 is_inline: false, |
380 is_inline: false, |
381 head_revs: vec![], |
381 head_revs: RwLock::new((vec![], HashSet::new())), |
382 filtered_revs: HashSet::new(), |
|
383 }) |
382 }) |
384 } |
383 } |
385 } |
384 } |
386 |
385 |
387 pub fn uses_generaldelta(&self) -> bool { |
386 pub fn uses_generaldelta(&self) -> bool { |
530 offset_override: Some(0), |
529 offset_override: Some(0), |
531 } |
530 } |
532 } |
531 } |
533 |
532 |
534 /// Return the head revisions of this index |
533 /// Return the head revisions of this index |
535 pub fn head_revs(&mut self) -> Result<Vec<Revision>, GraphError> { |
534 pub fn head_revs(&self) -> Result<Vec<Revision>, GraphError> { |
536 self.head_revs_filtered(&HashSet::new()) |
535 self.head_revs_filtered(&HashSet::new()) |
537 } |
536 } |
538 |
537 |
539 /// Return the head revisions of this index |
538 /// Return the head revisions of this index |
540 pub fn head_revs_filtered( |
539 pub fn head_revs_filtered( |
541 &mut self, |
540 &self, |
542 filtered_revs: &HashSet<Revision>, |
541 filtered_revs: &HashSet<Revision>, |
543 ) -> Result<Vec<Revision>, GraphError> { |
542 ) -> Result<Vec<Revision>, GraphError> { |
544 if !self.head_revs.is_empty() && filtered_revs == &self.filtered_revs { |
543 { |
545 return Ok(self.head_revs.to_owned()); |
544 let guard = self |
|
545 .head_revs |
|
546 .read() |
|
547 .expect("RwLock on Index.head_revs should not be poisoned"); |
|
548 let self_head_revs = &guard.0; |
|
549 let self_filtered_revs = &guard.1; |
|
550 if !self_head_revs.is_empty() |
|
551 && filtered_revs == self_filtered_revs |
|
552 { |
|
553 return Ok(self_head_revs.to_owned()); |
|
554 } |
546 } |
555 } |
547 let mut revs: HashSet<Revision, RandomState> = |
556 let mut revs: HashSet<Revision, RandomState> = |
548 if filtered_revs.is_empty() { |
557 if filtered_revs.is_empty() { |
549 (0..self.len()) |
558 (0..self.len()) |
550 .into_iter() |
559 .into_iter() |
568 revs.insert(NULL_REVISION); |
577 revs.insert(NULL_REVISION); |
569 } |
578 } |
570 let mut as_vec: Vec<Revision> = |
579 let mut as_vec: Vec<Revision> = |
571 revs.into_iter().map(Into::into).collect(); |
580 revs.into_iter().map(Into::into).collect(); |
572 as_vec.sort_unstable(); |
581 as_vec.sort_unstable(); |
573 self.head_revs = as_vec.to_owned(); |
582 *self |
574 self.filtered_revs = filtered_revs.to_owned(); |
583 .head_revs |
|
584 .write() |
|
585 .expect("RwLock on Index.head_revs should not be poisoned") = |
|
586 (as_vec.to_owned(), filtered_revs.to_owned()); |
575 Ok(as_vec) |
587 Ok(as_vec) |
576 } |
588 } |
577 |
589 |
578 /// Obtain the delta chain for a revision. |
590 /// Obtain the delta chain for a revision. |
579 /// |
591 /// |
649 cache.insert_for(base.0, rev)?; |
661 cache.insert_for(base.0, rev)?; |
650 } |
662 } |
651 Ok(()) |
663 Ok(()) |
652 } |
664 } |
653 |
665 |
|
666 fn clear_head_revs(&self) { |
|
667 self.head_revs |
|
668 .write() |
|
669 .expect("RwLock on Index.head_revs should not be poisoined") |
|
670 .0 |
|
671 .clear() |
|
672 } |
|
673 |
654 /// TODO move this to the trait probably, along with other things |
674 /// TODO move this to the trait probably, along with other things |
655 pub fn append( |
675 pub fn append( |
656 &mut self, |
676 &mut self, |
657 revision_data: RevisionDataParams, |
677 revision_data: RevisionDataParams, |
658 ) -> Result<(), RevlogError> { |
678 ) -> Result<(), RevlogError> { |
660 let new_offset = self.bytes.len(); |
680 let new_offset = self.bytes.len(); |
661 if let Some(offsets) = &mut *self.get_offsets_mut() { |
681 if let Some(offsets) = &mut *self.get_offsets_mut() { |
662 offsets.push(new_offset) |
682 offsets.push(new_offset) |
663 } |
683 } |
664 self.bytes.added.extend(revision_data.into_v1().as_bytes()); |
684 self.bytes.added.extend(revision_data.into_v1().as_bytes()); |
665 self.head_revs.clear(); |
685 self.clear_head_revs(); |
666 Ok(()) |
686 Ok(()) |
667 } |
687 } |
668 |
688 |
669 pub fn pack_header(&self, header: i32) -> [u8; 4] { |
689 pub fn pack_header(&self, header: i32) -> [u8; 4] { |
670 header.to_be_bytes() |
690 header.to_be_bytes() |
674 let offsets = self.get_offsets().clone(); |
694 let offsets = self.get_offsets().clone(); |
675 self.bytes.remove(rev, offsets.as_deref())?; |
695 self.bytes.remove(rev, offsets.as_deref())?; |
676 if let Some(offsets) = &mut *self.get_offsets_mut() { |
696 if let Some(offsets) = &mut *self.get_offsets_mut() { |
677 offsets.truncate(rev.0 as usize) |
697 offsets.truncate(rev.0 as usize) |
678 } |
698 } |
679 self.head_revs.clear(); |
699 self.clear_head_revs(); |
680 Ok(()) |
700 Ok(()) |
681 } |
701 } |
682 |
702 |
683 pub fn clear_caches(&mut self) { |
703 pub fn clear_caches(&self) { |
684 // We need to get the 'inline' value from Python at init and use this |
704 // We need to get the 'inline' value from Python at init and use this |
685 // instead of offsets to determine whether we're inline since we might |
705 // instead of offsets to determine whether we're inline since we might |
686 // clear caches. This implies re-populating the offsets on-demand. |
706 // clear caches. This implies re-populating the offsets on-demand. |
687 self.offsets = RwLock::new(None); |
707 *self |
688 self.head_revs.clear(); |
708 .offsets |
|
709 .write() |
|
710 .expect("RwLock on Index.offsets should not be poisoed") = None; |
|
711 self.clear_head_revs(); |
689 } |
712 } |
690 |
713 |
691 /// Unchecked version of `is_snapshot`. |
714 /// Unchecked version of `is_snapshot`. |
692 /// Assumes the caller checked that `rev` is within a valid revision range. |
715 /// Assumes the caller checked that `rev` is within a valid revision range. |
693 pub fn is_snapshot_unchecked( |
716 pub fn is_snapshot_unchecked( |