diff --git a/lib/src/default_index/composite.rs b/lib/src/default_index/composite.rs index 13c3a2f21c..97551fee04 100644 --- a/lib/src/default_index/composite.rs +++ b/lib/src/default_index/composite.rs @@ -21,15 +21,48 @@ use std::sync::Arc; use itertools::Itertools; +use super::entry::{IndexEntry, IndexPosition, IndexPositionByGeneration, SmallIndexPositionsVec}; use super::readonly::ReadonlyIndexSegment; use super::rev_walk::RevWalk; -use super::{IndexEntry, IndexPosition, IndexPositionByGeneration, IndexSegment}; -use crate::backend::{CommitId, ObjectId}; +use crate::backend::{ChangeId, CommitId, ObjectId}; use crate::index::{HexPrefix, Index, PrefixResolution}; use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError}; use crate::store::Store; use crate::{backend, default_revset_engine}; +pub(super) trait IndexSegment: Send + Sync { + fn segment_num_parent_commits(&self) -> u32; + + fn segment_num_commits(&self) -> u32; + + fn segment_parent_file(&self) -> Option<&Arc>; + + fn segment_name(&self) -> Option; + + fn segment_commit_id_to_pos(&self, commit_id: &CommitId) -> Option; + + /// Suppose the given `commit_id` exists, returns the positions of the + /// previous and next commit ids in lexicographical order. + fn segment_commit_id_to_neighbor_positions( + &self, + commit_id: &CommitId, + ) -> (Option, Option); + + fn segment_resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution; + + fn segment_generation_number(&self, local_pos: u32) -> u32; + + fn segment_commit_id(&self, local_pos: u32) -> CommitId; + + fn segment_change_id(&self, local_pos: u32) -> ChangeId; + + fn segment_num_parents(&self, local_pos: u32) -> u32; + + fn segment_parent_positions(&self, local_pos: u32) -> SmallIndexPositionsVec; + + fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry; +} + #[derive(Clone, Copy)] pub struct CompositeIndex<'a>(&'a dyn IndexSegment); @@ -150,7 +183,7 @@ impl<'a> CompositeIndex<'a> { if descendant_pos == ancestor_pos { return true; } - if !visited.insert(descendant_entry.pos) { + if !visited.insert(descendant_entry.position()) { continue; } if descendant_entry.generation_number() <= ancestor_generation { @@ -182,7 +215,7 @@ impl<'a> CompositeIndex<'a> { let item1 = dedup_pop(&mut items1).unwrap(); let entry1 = self.entry_by_pos(item1.pos); for parent_entry in entry1.parents() { - assert!(parent_entry.pos < entry1.pos); + assert!(parent_entry.position() < entry1.position()); items1.push(IndexPositionByGeneration::from(&parent_entry)); } } @@ -190,7 +223,7 @@ impl<'a> CompositeIndex<'a> { let item2 = dedup_pop(&mut items2).unwrap(); let entry2 = self.entry_by_pos(item2.pos); for parent_entry in entry2.parents() { - assert!(parent_entry.pos < entry2.pos); + assert!(parent_entry.position() < entry2.position()); items2.push(IndexPositionByGeneration::from(&parent_entry)); } } @@ -238,7 +271,7 @@ impl<'a> CompositeIndex<'a> { candidate_positions.remove(&item.pos); let entry = self.entry_by_pos(item.pos); for parent_entry in entry.parents() { - assert!(parent_entry.pos < entry.pos); + assert!(parent_entry.position() < entry.position()); work.push(IndexPositionByGeneration::from(&parent_entry)); } } diff --git a/lib/src/default_index/entry.rs b/lib/src/default_index/entry.rs new file mode 100644 index 0000000000..cfe4c57ed3 --- /dev/null +++ b/lib/src/default_index/entry.rs @@ -0,0 +1,150 @@ +// Copyright 2023 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(missing_docs)] + +use std::cmp::Ordering; +use std::fmt::{Debug, Formatter}; +use std::hash::{Hash, Hasher}; + +use smallvec::SmallVec; + +use super::composite::{CompositeIndex, IndexSegment}; +use crate::backend::{ChangeId, CommitId, ObjectId}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub struct IndexPosition(pub(super) u32); + +impl IndexPosition { + pub const MAX: Self = IndexPosition(u32::MAX); +} + +// SmallVec reuses two pointer-size fields as inline area, which meas we can +// inline up to 16 bytes (on 64-bit platform) for free. +pub(super) type SmallIndexPositionsVec = SmallVec<[IndexPosition; 4]>; + +#[derive(Clone)] +pub struct IndexEntry<'a> { + source: &'a dyn IndexSegment, + pos: IndexPosition, + // Position within the source segment + local_pos: u32, +} + +impl Debug for IndexEntry<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("IndexEntry") + .field("pos", &self.pos) + .field("local_pos", &self.local_pos) + .field("commit_id", &self.commit_id().hex()) + .finish() + } +} + +impl PartialEq for IndexEntry<'_> { + fn eq(&self, other: &Self) -> bool { + self.pos == other.pos + } +} + +impl Eq for IndexEntry<'_> {} + +impl Hash for IndexEntry<'_> { + fn hash(&self, state: &mut H) { + self.pos.hash(state) + } +} + +impl<'a> IndexEntry<'a> { + pub(super) fn new(source: &'a dyn IndexSegment, pos: IndexPosition, local_pos: u32) -> Self { + IndexEntry { + source, + pos, + local_pos, + } + } + + pub fn position(&self) -> IndexPosition { + self.pos + } + + pub fn generation_number(&self) -> u32 { + self.source.segment_generation_number(self.local_pos) + } + + pub fn commit_id(&self) -> CommitId { + self.source.segment_commit_id(self.local_pos) + } + + pub fn change_id(&self) -> ChangeId { + self.source.segment_change_id(self.local_pos) + } + + pub fn num_parents(&self) -> u32 { + self.source.segment_num_parents(self.local_pos) + } + + pub fn parent_positions(&self) -> SmallIndexPositionsVec { + self.source.segment_parent_positions(self.local_pos) + } + + pub fn parents(&self) -> impl ExactSizeIterator> { + let composite = CompositeIndex::new(self.source); + self.parent_positions() + .into_iter() + .map(move |pos| composite.entry_by_pos(pos)) + } +} + +#[derive(Clone, Eq, PartialEq)] +pub struct IndexEntryByPosition<'a>(pub IndexEntry<'a>); + +impl Ord for IndexEntryByPosition<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.0.pos.cmp(&other.0.pos) + } +} + +impl PartialOrd for IndexEntryByPosition<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Wrapper to sort `IndexPosition` by its generation number. +/// +/// This is similar to `IndexEntry` newtypes, but optimized for size and cache +/// locality. The original `IndexEntry` will have to be looked up when needed. +#[derive(Clone, Copy, Debug, Ord, PartialOrd)] +pub(super) struct IndexPositionByGeneration { + pub generation: u32, // order by generation number + pub pos: IndexPosition, // tie breaker +} + +impl Eq for IndexPositionByGeneration {} + +impl PartialEq for IndexPositionByGeneration { + fn eq(&self, other: &Self) -> bool { + self.pos == other.pos + } +} + +impl From<&IndexEntry<'_>> for IndexPositionByGeneration { + fn from(entry: &IndexEntry<'_>) -> Self { + IndexPositionByGeneration { + generation: entry.generation_number(), + pos: entry.position(), + } + } +} diff --git a/lib/src/default_index/mod.rs b/lib/src/default_index/mod.rs index f8a4de6ae2..ea3a0f41ac 100644 --- a/lib/src/default_index/mod.rs +++ b/lib/src/default_index/mod.rs @@ -15,192 +15,36 @@ #![allow(missing_docs)] mod composite; +mod entry; mod mutable; mod readonly; mod rev_walk; mod store; -use std::cmp::Ordering; -use std::fmt::{Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::sync::Arc; - -use smallvec::SmallVec; - pub use self::composite::{CompositeIndex, IndexLevelStats, IndexStats}; +pub use self::entry::{IndexEntry, IndexEntryByPosition, IndexPosition}; pub use self::mutable::DefaultMutableIndex; pub use self::readonly::DefaultReadonlyIndex; -use self::readonly::ReadonlyIndexSegment; pub use self::rev_walk::{ RevWalk, RevWalkDescendants, RevWalkDescendantsGenerationRange, RevWalkGenerationRange, }; pub use self::store::{DefaultIndexStore, DefaultIndexStoreError, IndexLoadError}; -use crate::backend::{ChangeId, CommitId, ObjectId}; -use crate::index::{HexPrefix, PrefixResolution}; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] -pub struct IndexPosition(u32); - -impl IndexPosition { - pub const MAX: Self = IndexPosition(u32::MAX); -} - -// SmallVec reuses two pointer-size fields as inline area, which meas we can -// inline up to 16 bytes (on 64-bit platform) for free. -type SmallIndexPositionsVec = SmallVec<[IndexPosition; 4]>; - -trait IndexSegment: Send + Sync { - fn segment_num_parent_commits(&self) -> u32; - - fn segment_num_commits(&self) -> u32; - - fn segment_parent_file(&self) -> Option<&Arc>; - - fn segment_name(&self) -> Option; - - fn segment_commit_id_to_pos(&self, commit_id: &CommitId) -> Option; - - /// Suppose the given `commit_id` exists, returns the positions of the - /// previous and next commit ids in lexicographical order. - fn segment_commit_id_to_neighbor_positions( - &self, - commit_id: &CommitId, - ) -> (Option, Option); - - fn segment_resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution; - - fn segment_generation_number(&self, local_pos: u32) -> u32; - - fn segment_commit_id(&self, local_pos: u32) -> CommitId; - - fn segment_change_id(&self, local_pos: u32) -> ChangeId; - - fn segment_num_parents(&self, local_pos: u32) -> u32; - - fn segment_parent_positions(&self, local_pos: u32) -> SmallIndexPositionsVec; - - fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry; -} - -#[derive(Clone, Eq, PartialEq)] -pub struct IndexEntryByPosition<'a>(pub IndexEntry<'a>); - -impl Ord for IndexEntryByPosition<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.0.pos.cmp(&other.0.pos) - } -} - -impl PartialOrd for IndexEntryByPosition<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// Wrapper to sort `IndexPosition` by its generation number. -/// -/// This is similar to `IndexEntry` newtypes, but optimized for size and cache -/// locality. The original `IndexEntry` will have to be looked up when needed. -#[derive(Clone, Copy, Debug, Ord, PartialOrd)] -struct IndexPositionByGeneration { - generation: u32, // order by generation number - pos: IndexPosition, // tie breaker -} - -impl Eq for IndexPositionByGeneration {} - -impl PartialEq for IndexPositionByGeneration { - fn eq(&self, other: &Self) -> bool { - self.pos == other.pos - } -} - -impl From<&IndexEntry<'_>> for IndexPositionByGeneration { - fn from(entry: &IndexEntry<'_>) -> Self { - IndexPositionByGeneration { - generation: entry.generation_number(), - pos: entry.position(), - } - } -} - -#[derive(Clone)] -pub struct IndexEntry<'a> { - source: &'a dyn IndexSegment, - pos: IndexPosition, - // Position within the source segment - local_pos: u32, -} - -impl Debug for IndexEntry<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("IndexEntry") - .field("pos", &self.pos) - .field("local_pos", &self.local_pos) - .field("commit_id", &self.commit_id().hex()) - .finish() - } -} - -impl PartialEq for IndexEntry<'_> { - fn eq(&self, other: &Self) -> bool { - self.pos == other.pos - } -} - -impl Eq for IndexEntry<'_> {} - -impl Hash for IndexEntry<'_> { - fn hash(&self, state: &mut H) { - self.pos.hash(state) - } -} - -impl<'a> IndexEntry<'a> { - pub fn position(&self) -> IndexPosition { - self.pos - } - - pub fn generation_number(&self) -> u32 { - self.source.segment_generation_number(self.local_pos) - } - - pub fn commit_id(&self) -> CommitId { - self.source.segment_commit_id(self.local_pos) - } - - pub fn change_id(&self) -> ChangeId { - self.source.segment_change_id(self.local_pos) - } - - pub fn num_parents(&self) -> u32 { - self.source.segment_num_parents(self.local_pos) - } - - pub fn parent_positions(&self) -> SmallIndexPositionsVec { - self.source.segment_parent_positions(self.local_pos) - } - - pub fn parents(&self) -> impl ExactSizeIterator> { - let composite = CompositeIndex::new(self.source); - self.parent_positions() - .into_iter() - .map(move |pos| composite.entry_by_pos(pos)) - } -} #[cfg(test)] mod tests { use std::ops::Range; + use std::sync::Arc; use itertools::Itertools; use smallvec::smallvec_inline; use test_case::test_case; + use super::composite::IndexSegment; + use super::entry::SmallIndexPositionsVec; use super::mutable::MutableIndexSegment; use super::*; use crate::backend::{ChangeId, CommitId, ObjectId}; - use crate::index::Index; + use crate::index::{HexPrefix, Index, PrefixResolution}; /// Generator of unique 16-byte ChangeId excluding root id fn change_id_generator() -> impl FnMut() -> ChangeId { @@ -273,7 +117,7 @@ mod tests { assert_eq!(index.commit_id_to_pos(&CommitId::from_hex("ffffff")), None); // Check properties of root entry let entry = index.entry_by_id(&id_0).unwrap(); - assert_eq!(entry.pos, IndexPosition(0)); + assert_eq!(entry.position(), IndexPosition(0)); assert_eq!(entry.commit_id(), id_0); assert_eq!(entry.change_id(), change_id0); assert_eq!(entry.generation_number(), 0); @@ -360,9 +204,9 @@ mod tests { let entry_4 = index.entry_by_id(&id_4).unwrap(); let entry_5 = index.entry_by_id(&id_5).unwrap(); // Check properties of some entries - assert_eq!(entry_0.pos, IndexPosition(0)); + assert_eq!(entry_0.position(), IndexPosition(0)); assert_eq!(entry_0.commit_id(), id_0); - assert_eq!(entry_1.pos, IndexPosition(1)); + assert_eq!(entry_1.position(), IndexPosition(1)); assert_eq!(entry_1.commit_id(), id_1); assert_eq!(entry_1.change_id(), change_id1); assert_eq!(entry_1.generation_number(), 1); @@ -372,8 +216,11 @@ mod tests { smallvec_inline![IndexPosition(0)] ); assert_eq!(entry_1.parents().len(), 1); - assert_eq!(entry_1.parents().next().unwrap().pos, IndexPosition(0)); - assert_eq!(entry_2.pos, IndexPosition(2)); + assert_eq!( + entry_1.parents().next().unwrap().position(), + IndexPosition(0) + ); + assert_eq!(entry_2.position(), IndexPosition(2)); assert_eq!(entry_2.commit_id(), id_2); assert_eq!(entry_2.change_id(), change_id2); assert_eq!(entry_2.generation_number(), 1); @@ -388,7 +235,7 @@ mod tests { entry_3.parent_positions(), smallvec_inline![IndexPosition(2)] ); - assert_eq!(entry_4.pos, IndexPosition(4)); + assert_eq!(entry_4.position(), IndexPosition(4)); assert_eq!(entry_4.generation_number(), 2); assert_eq!(entry_4.num_parents(), 1); assert_eq!( @@ -402,8 +249,14 @@ mod tests { smallvec_inline![IndexPosition(4), IndexPosition(2)] ); assert_eq!(entry_5.parents().len(), 2); - assert_eq!(entry_5.parents().next().unwrap().pos, IndexPosition(4)); - assert_eq!(entry_5.parents().nth(1).unwrap().pos, IndexPosition(2)); + assert_eq!( + entry_5.parents().next().unwrap().position(), + IndexPosition(4) + ); + assert_eq!( + entry_5.parents().nth(1).unwrap().position(), + IndexPosition(2) + ); } #[test_case(false; "in memory")] diff --git a/lib/src/default_index/mutable.rs b/lib/src/default_index/mutable.rs index bbb1ad96f0..11c12c6365 100644 --- a/lib/src/default_index/mutable.rs +++ b/lib/src/default_index/mutable.rs @@ -30,10 +30,10 @@ use itertools::Itertools; use smallvec::SmallVec; use tempfile::NamedTempFile; -use super::composite::CompositeIndex; +use super::composite::{CompositeIndex, IndexSegment}; +use super::entry::{IndexEntry, IndexPosition, SmallIndexPositionsVec}; use super::readonly::{DefaultReadonlyIndex, ReadonlyIndexSegment}; use super::store::IndexLoadError; -use super::{IndexEntry, IndexPosition, IndexSegment, SmallIndexPositionsVec}; use crate::backend::{ChangeId, CommitId, ObjectId}; use crate::commit::Commit; use crate::file_util::persist_content_addressed_temp_file; @@ -120,7 +120,7 @@ impl MutableIndexSegment { entry.generation_number, parent_entry.generation_number() + 1, ); - entry.parent_positions.push(parent_entry.pos); + entry.parent_positions.push(parent_entry.position()); } self.lookup.insert( entry.commit_id.clone(), @@ -384,11 +384,7 @@ impl IndexSegment for MutableIndexSegment { } fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry { - IndexEntry { - source: self, - local_pos, - pos, - } + IndexEntry::new(self, pos, local_pos) } } diff --git a/lib/src/default_index/readonly.rs b/lib/src/default_index/readonly.rs index 342c8f821d..1c01fbfb37 100644 --- a/lib/src/default_index/readonly.rs +++ b/lib/src/default_index/readonly.rs @@ -25,10 +25,10 @@ use std::sync::Arc; use byteorder::{LittleEndian, ReadBytesExt}; use smallvec::SmallVec; -use super::composite::CompositeIndex; +use super::composite::{CompositeIndex, IndexSegment}; +use super::entry::{IndexEntry, IndexPosition, SmallIndexPositionsVec}; use super::mutable::DefaultMutableIndex; use super::store::IndexLoadError; -use super::{IndexEntry, IndexPosition, IndexSegment, SmallIndexPositionsVec}; use crate::backend::{ChangeId, CommitId, ObjectId}; use crate::index::{HexPrefix, Index, MutableIndex, PrefixResolution, ReadonlyIndex}; use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError}; @@ -370,11 +370,7 @@ impl IndexSegment for ReadonlyIndexSegment { } fn segment_entry_by_pos(&self, pos: IndexPosition, local_pos: u32) -> IndexEntry { - IndexEntry { - source: self, - local_pos, - pos, - } + IndexEntry::new(self, pos, local_pos) } } diff --git a/lib/src/default_index/rev_walk.rs b/lib/src/default_index/rev_walk.rs index 1c12939a76..77723f92c3 100644 --- a/lib/src/default_index/rev_walk.rs +++ b/lib/src/default_index/rev_walk.rs @@ -22,7 +22,7 @@ use std::ops::Range; use smallvec::SmallVec; use super::composite::CompositeIndex; -use super::{IndexEntry, IndexPosition, SmallIndexPositionsVec}; +use super::entry::{IndexEntry, IndexPosition, SmallIndexPositionsVec}; trait RevWalkIndex<'a> { type Position: Copy + Ord;