mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-18 10:07:28 +00:00
repo: move IdIndex to revset engine
This commit is contained in:
parent
13a000caa7
commit
d4e1156957
2 changed files with 185 additions and 190 deletions
|
@ -18,17 +18,17 @@ use std::iter::Peekable;
|
|||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::backend::{ChangeId, CommitId};
|
||||
use crate::backend::{ChangeId, CommitId, ObjectId};
|
||||
use crate::default_index_store::{CompositeIndex, IndexEntry, IndexPosition};
|
||||
use crate::default_revset_graph_iterator::RevsetGraphIterator;
|
||||
use crate::index::{HexPrefix, PrefixResolution};
|
||||
use crate::matchers::{EverythingMatcher, Matcher, PrefixMatcher};
|
||||
use crate::repo::{IdIndex, Repo};
|
||||
use crate::repo::Repo;
|
||||
use crate::revset::{
|
||||
ChangeIdIndex, Revset, RevsetError, RevsetExpression, RevsetFilterPredicate, RevsetGraphEdge,
|
||||
RevsetIteratorExt, GENERATION_RANGE_FULL,
|
||||
};
|
||||
use crate::rewrite;
|
||||
use crate::{backend, rewrite};
|
||||
|
||||
trait ToPredicateFn<'index> {
|
||||
/// Creates function that tests if the given entry is included in the set.
|
||||
|
@ -111,6 +111,82 @@ impl ChangeIdIndex for ChangeIdIndexImpl<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct IdIndex<K, V>(Vec<(K, V)>);
|
||||
|
||||
impl<K, V> IdIndex<K, V>
|
||||
where
|
||||
K: ObjectId + Ord,
|
||||
{
|
||||
/// Creates new index from the given entries. Multiple values can be
|
||||
/// associated with a single key.
|
||||
pub fn from_vec(mut vec: Vec<(K, V)>) -> Self {
|
||||
vec.sort_unstable_by(|(k0, _), (k1, _)| k0.cmp(k1));
|
||||
IdIndex(vec)
|
||||
}
|
||||
|
||||
/// Looks up entries with the given prefix, and collects values if matched
|
||||
/// entries have unambiguous keys.
|
||||
pub fn resolve_prefix_with<U>(
|
||||
&self,
|
||||
prefix: &HexPrefix,
|
||||
mut value_mapper: impl FnMut(&V) -> U,
|
||||
) -> PrefixResolution<Vec<U>> {
|
||||
let mut range = self.resolve_prefix_range(prefix).peekable();
|
||||
if let Some((first_key, _)) = range.peek().copied() {
|
||||
let maybe_entries: Option<Vec<_>> = range
|
||||
.map(|(k, v)| (k == first_key).then(|| value_mapper(v)))
|
||||
.collect();
|
||||
if let Some(entries) = maybe_entries {
|
||||
PrefixResolution::SingleMatch(entries)
|
||||
} else {
|
||||
PrefixResolution::AmbiguousMatch
|
||||
}
|
||||
} else {
|
||||
PrefixResolution::NoMatch
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over entries with the given prefix.
|
||||
pub fn resolve_prefix_range<'a: 'b, 'b>(
|
||||
&'a self,
|
||||
prefix: &'b HexPrefix,
|
||||
) -> impl Iterator<Item = (&'a K, &'a V)> + 'b {
|
||||
let min_bytes = prefix.min_prefix_bytes();
|
||||
let pos = self.0.partition_point(|(k, _)| k.as_bytes() < min_bytes);
|
||||
self.0[pos..]
|
||||
.iter()
|
||||
.take_while(|(k, _)| prefix.matches(k))
|
||||
.map(|(k, v)| (k, v))
|
||||
}
|
||||
|
||||
/// This function returns the shortest length of a prefix of `key` that
|
||||
/// disambiguates it from every other key in the index.
|
||||
///
|
||||
/// The length to be returned is a number of hexadecimal digits.
|
||||
///
|
||||
/// This has some properties that we do not currently make much use of:
|
||||
///
|
||||
/// - The algorithm works even if `key` itself is not in the index.
|
||||
///
|
||||
/// - In the special case when there are keys in the trie for which our
|
||||
/// `key` is an exact prefix, returns `key.len() + 1`. Conceptually, in
|
||||
/// order to disambiguate, you need every letter of the key *and* the
|
||||
/// additional fact that it's the entire key). This case is extremely
|
||||
/// unlikely for hashes with 12+ hexadecimal characters.
|
||||
pub fn shortest_unique_prefix_len(&self, key: &K) -> usize {
|
||||
let pos = self.0.partition_point(|(k, _)| k < key);
|
||||
let left = pos.checked_sub(1).map(|p| &self.0[p]);
|
||||
let right = self.0[pos..].iter().find(|(k, _)| k != key);
|
||||
itertools::chain(left, right)
|
||||
.map(|(neighbor, _value)| {
|
||||
backend::common_hex_len(key.as_bytes(), neighbor.as_bytes()) + 1
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
struct EagerRevset<'index> {
|
||||
index_entries: Vec<IndexEntry<'index>>,
|
||||
}
|
||||
|
@ -712,6 +788,112 @@ mod tests {
|
|||
use crate::default_index_store::MutableIndexImpl;
|
||||
use crate::index::Index;
|
||||
|
||||
#[test]
|
||||
fn test_id_index_resolve_prefix() {
|
||||
fn sorted(resolution: PrefixResolution<Vec<i32>>) -> PrefixResolution<Vec<i32>> {
|
||||
match resolution {
|
||||
PrefixResolution::SingleMatch(mut xs) => {
|
||||
xs.sort(); // order of values might not be preserved by IdIndex
|
||||
PrefixResolution::SingleMatch(xs)
|
||||
}
|
||||
_ => resolution,
|
||||
}
|
||||
}
|
||||
let id_index = IdIndex::from_vec(vec![
|
||||
(ChangeId::from_hex("0000"), 0),
|
||||
(ChangeId::from_hex("0099"), 1),
|
||||
(ChangeId::from_hex("0099"), 2),
|
||||
(ChangeId::from_hex("0aaa"), 3),
|
||||
(ChangeId::from_hex("0aab"), 4),
|
||||
]);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0").unwrap(), |&v| v),
|
||||
PrefixResolution::AmbiguousMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("00").unwrap(), |&v| v),
|
||||
PrefixResolution::AmbiguousMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("000").unwrap(), |&v| v),
|
||||
PrefixResolution::SingleMatch(vec![0]),
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0001").unwrap(), |&v| v),
|
||||
PrefixResolution::NoMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
sorted(id_index.resolve_prefix_with(&HexPrefix::new("009").unwrap(), |&v| v)),
|
||||
PrefixResolution::SingleMatch(vec![1, 2]),
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0aa").unwrap(), |&v| v),
|
||||
PrefixResolution::AmbiguousMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0aab").unwrap(), |&v| v),
|
||||
PrefixResolution::SingleMatch(vec![4]),
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("f").unwrap(), |&v| v),
|
||||
PrefixResolution::NoMatch,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_index_shortest_unique_prefix_len() {
|
||||
// No crash if empty
|
||||
let id_index = IdIndex::from_vec(vec![] as Vec<(ChangeId, ())>);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("00")),
|
||||
0
|
||||
);
|
||||
|
||||
let id_index = IdIndex::from_vec(vec![
|
||||
(ChangeId::from_hex("ab"), ()),
|
||||
(ChangeId::from_hex("acd0"), ()),
|
||||
(ChangeId::from_hex("acd0"), ()), // duplicated key is allowed
|
||||
]);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("acd0")),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("ac")),
|
||||
3
|
||||
);
|
||||
|
||||
let id_index = IdIndex::from_vec(vec![
|
||||
(ChangeId::from_hex("ab"), ()),
|
||||
(ChangeId::from_hex("acd0"), ()),
|
||||
(ChangeId::from_hex("acf0"), ()),
|
||||
(ChangeId::from_hex("a0"), ()),
|
||||
(ChangeId::from_hex("ba"), ()),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("a0")),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("ba")),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("ab")),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("acd0")),
|
||||
3
|
||||
);
|
||||
// If it were there, the length would be 1.
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("c0")),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/// Generator of unique 16-byte ChangeId excluding root id
|
||||
fn change_id_generator() -> impl FnMut() -> ChangeId {
|
||||
let mut iter = (1_u128..).map(|n| ChangeId::new(n.to_le_bytes().into()));
|
||||
|
|
187
lib/src/repo.rs
187
lib/src/repo.rs
|
@ -1265,190 +1265,3 @@ mod dirty_cell {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IdIndex<K, V>(Vec<(K, V)>);
|
||||
|
||||
impl<K, V> IdIndex<K, V>
|
||||
where
|
||||
K: ObjectId + Ord,
|
||||
{
|
||||
/// Creates new index from the given entries. Multiple values can be
|
||||
/// associated with a single key.
|
||||
pub fn from_vec(mut vec: Vec<(K, V)>) -> Self {
|
||||
vec.sort_unstable_by(|(k0, _), (k1, _)| k0.cmp(k1));
|
||||
IdIndex(vec)
|
||||
}
|
||||
|
||||
/// Looks up entries with the given prefix, and collects values if matched
|
||||
/// entries have unambiguous keys.
|
||||
pub fn resolve_prefix_with<U>(
|
||||
&self,
|
||||
prefix: &HexPrefix,
|
||||
mut value_mapper: impl FnMut(&V) -> U,
|
||||
) -> PrefixResolution<Vec<U>> {
|
||||
let mut range = self.resolve_prefix_range(prefix).peekable();
|
||||
if let Some((first_key, _)) = range.peek().copied() {
|
||||
let maybe_entries: Option<Vec<_>> = range
|
||||
.map(|(k, v)| (k == first_key).then(|| value_mapper(v)))
|
||||
.collect();
|
||||
if let Some(entries) = maybe_entries {
|
||||
PrefixResolution::SingleMatch(entries)
|
||||
} else {
|
||||
PrefixResolution::AmbiguousMatch
|
||||
}
|
||||
} else {
|
||||
PrefixResolution::NoMatch
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over entries with the given prefix.
|
||||
pub fn resolve_prefix_range<'a: 'b, 'b>(
|
||||
&'a self,
|
||||
prefix: &'b HexPrefix,
|
||||
) -> impl Iterator<Item = (&'a K, &'a V)> + 'b {
|
||||
let min_bytes = prefix.min_prefix_bytes();
|
||||
let pos = self.0.partition_point(|(k, _)| k.as_bytes() < min_bytes);
|
||||
self.0[pos..]
|
||||
.iter()
|
||||
.take_while(|(k, _)| prefix.matches(k))
|
||||
.map(|(k, v)| (k, v))
|
||||
}
|
||||
|
||||
/// This function returns the shortest length of a prefix of `key` that
|
||||
/// disambiguates it from every other key in the index.
|
||||
///
|
||||
/// The length to be returned is a number of hexadecimal digits.
|
||||
///
|
||||
/// This has some properties that we do not currently make much use of:
|
||||
///
|
||||
/// - The algorithm works even if `key` itself is not in the index.
|
||||
///
|
||||
/// - In the special case when there are keys in the trie for which our
|
||||
/// `key` is an exact prefix, returns `key.len() + 1`. Conceptually, in
|
||||
/// order to disambiguate, you need every letter of the key *and* the
|
||||
/// additional fact that it's the entire key). This case is extremely
|
||||
/// unlikely for hashes with 12+ hexadecimal characters.
|
||||
pub fn shortest_unique_prefix_len(&self, key: &K) -> usize {
|
||||
let pos = self.0.partition_point(|(k, _)| k < key);
|
||||
let left = pos.checked_sub(1).map(|p| &self.0[p]);
|
||||
let right = self.0[pos..].iter().find(|(k, _)| k != key);
|
||||
itertools::chain(left, right)
|
||||
.map(|(neighbor, _value)| {
|
||||
backend::common_hex_len(key.as_bytes(), neighbor.as_bytes()) + 1
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_id_index_resolve_prefix() {
|
||||
fn sorted(resolution: PrefixResolution<Vec<i32>>) -> PrefixResolution<Vec<i32>> {
|
||||
match resolution {
|
||||
PrefixResolution::SingleMatch(mut xs) => {
|
||||
xs.sort(); // order of values might not be preserved by IdIndex
|
||||
PrefixResolution::SingleMatch(xs)
|
||||
}
|
||||
_ => resolution,
|
||||
}
|
||||
}
|
||||
let id_index = IdIndex::from_vec(vec![
|
||||
(ChangeId::from_hex("0000"), 0),
|
||||
(ChangeId::from_hex("0099"), 1),
|
||||
(ChangeId::from_hex("0099"), 2),
|
||||
(ChangeId::from_hex("0aaa"), 3),
|
||||
(ChangeId::from_hex("0aab"), 4),
|
||||
]);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0").unwrap(), |&v| v),
|
||||
PrefixResolution::AmbiguousMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("00").unwrap(), |&v| v),
|
||||
PrefixResolution::AmbiguousMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("000").unwrap(), |&v| v),
|
||||
PrefixResolution::SingleMatch(vec![0]),
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0001").unwrap(), |&v| v),
|
||||
PrefixResolution::NoMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
sorted(id_index.resolve_prefix_with(&HexPrefix::new("009").unwrap(), |&v| v)),
|
||||
PrefixResolution::SingleMatch(vec![1, 2]),
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0aa").unwrap(), |&v| v),
|
||||
PrefixResolution::AmbiguousMatch,
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("0aab").unwrap(), |&v| v),
|
||||
PrefixResolution::SingleMatch(vec![4]),
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.resolve_prefix_with(&HexPrefix::new("f").unwrap(), |&v| v),
|
||||
PrefixResolution::NoMatch,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_index_shortest_unique_prefix_len() {
|
||||
// No crash if empty
|
||||
let id_index = IdIndex::from_vec(vec![] as Vec<(ChangeId, ())>);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("00")),
|
||||
0
|
||||
);
|
||||
|
||||
let id_index = IdIndex::from_vec(vec![
|
||||
(ChangeId::from_hex("ab"), ()),
|
||||
(ChangeId::from_hex("acd0"), ()),
|
||||
(ChangeId::from_hex("acd0"), ()), // duplicated key is allowed
|
||||
]);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("acd0")),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("ac")),
|
||||
3
|
||||
);
|
||||
|
||||
let id_index = IdIndex::from_vec(vec![
|
||||
(ChangeId::from_hex("ab"), ()),
|
||||
(ChangeId::from_hex("acd0"), ()),
|
||||
(ChangeId::from_hex("acf0"), ()),
|
||||
(ChangeId::from_hex("a0"), ()),
|
||||
(ChangeId::from_hex("ba"), ()),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("a0")),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("ba")),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("ab")),
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("acd0")),
|
||||
3
|
||||
);
|
||||
// If it were there, the length would be 1.
|
||||
assert_eq!(
|
||||
id_index.shortest_unique_prefix_len(&ChangeId::from_hex("c0")),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue