object_id: move HexPrefix and PrefixResolution from index module

This commit is contained in:
Yuya Nishihara 2024-01-04 16:42:52 +09:00
parent fa5e40719c
commit 31b236a70d
14 changed files with 160 additions and 163 deletions

View file

@ -20,7 +20,7 @@ use std::time::Instant;
use clap::Subcommand;
use criterion::measurement::Measurement;
use criterion::{BatchSize, BenchmarkGroup, BenchmarkId, Criterion};
use jj_lib::index::HexPrefix;
use jj_lib::object_id::HexPrefix;
use jj_lib::repo::Repo;
use crate::cli_util::{CommandError, CommandHelper, WorkspaceCommandHelper};

View file

@ -27,8 +27,8 @@ use super::entry::{
use super::readonly::ReadonlyIndexSegment;
use super::rev_walk::RevWalk;
use crate::backend::{ChangeId, CommitId};
use crate::index::{HexPrefix, Index, PrefixResolution};
use crate::object_id::ObjectId;
use crate::index::Index;
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
use crate::store::Store;
use crate::{default_revset_engine, hex_util};

View file

@ -44,8 +44,8 @@ mod tests {
use super::mutable::MutableIndexSegment;
use super::*;
use crate::backend::{ChangeId, CommitId};
use crate::index::{HexPrefix, Index, PrefixResolution};
use crate::object_id::ObjectId;
use crate::index::Index;
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
/// Generator of unique 16-byte ChangeId excluding root id
fn change_id_generator() -> impl FnMut() -> ChangeId {

View file

@ -35,8 +35,8 @@ use super::readonly::{DefaultReadonlyIndex, ReadonlyIndexSegment};
use crate::backend::{ChangeId, CommitId};
use crate::commit::Commit;
use crate::file_util::persist_content_addressed_temp_file;
use crate::index::{HexPrefix, Index, MutableIndex, PrefixResolution, ReadonlyIndex};
use crate::object_id::ObjectId;
use crate::index::{Index, MutableIndex, ReadonlyIndex};
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
use crate::store::Store;

View file

@ -31,8 +31,8 @@ use super::entry::{IndexPosition, LocalPosition, SmallIndexPositionsVec};
use super::mutable::DefaultMutableIndex;
use crate::backend::{ChangeId, CommitId};
use crate::default_revset_engine;
use crate::index::{HexPrefix, Index, MutableIndex, PrefixResolution, ReadonlyIndex};
use crate::object_id::ObjectId;
use crate::index::{Index, MutableIndex, ReadonlyIndex};
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
use crate::store::Store;

View file

@ -27,8 +27,8 @@ use crate::backend::{ChangeId, CommitId, MillisSinceEpoch};
use crate::default_index::{AsCompositeIndex, CompositeIndex, IndexEntry, IndexPosition};
use crate::default_revset_graph_iterator::RevsetGraphIterator;
use crate::id_prefix::{IdIndex, IdIndexSource, IdIndexSourceEntry};
use crate::index::{HexPrefix, PrefixResolution};
use crate::matchers::{EverythingMatcher, Matcher, PrefixMatcher, Visit};
use crate::object_id::{HexPrefix, PrefixResolution};
use crate::repo_path::RepoPath;
use crate::revset::{
ChangeIdIndex, ResolvedExpression, ResolvedPredicateExpression, Revset, RevsetEvaluationError,

View file

@ -23,8 +23,7 @@ use once_cell::unsync::OnceCell;
use crate::backend::{ChangeId, CommitId};
use crate::hex_util;
use crate::index::{HexPrefix, PrefixResolution};
use crate::object_id::ObjectId;
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
use crate::repo::Repo;
use crate::revset::{DefaultSymbolResolver, RevsetExpression};

View file

@ -22,7 +22,7 @@ use thiserror::Error;
use crate::backend::CommitId;
use crate::commit::Commit;
use crate::object_id::ObjectId;
use crate::object_id::{HexPrefix, PrefixResolution};
use crate::op_store::OperationId;
use crate::operation::Operation;
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
@ -106,143 +106,3 @@ pub trait MutableIndex {
fn merge_in(&mut self, other: &dyn ReadonlyIndex);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HexPrefix {
// For odd-length prefix, lower 4 bits of the last byte is padded with 0
min_prefix_bytes: Vec<u8>,
has_odd_byte: bool,
}
impl HexPrefix {
pub fn new(prefix: &str) -> Option<HexPrefix> {
let has_odd_byte = prefix.len() & 1 != 0;
let min_prefix_bytes = if has_odd_byte {
hex::decode(prefix.to_owned() + "0").ok()?
} else {
hex::decode(prefix).ok()?
};
Some(HexPrefix {
min_prefix_bytes,
has_odd_byte,
})
}
pub fn from_bytes(bytes: &[u8]) -> Self {
HexPrefix {
min_prefix_bytes: bytes.to_owned(),
has_odd_byte: false,
}
}
pub fn hex(&self) -> String {
let mut hex_string = hex::encode(&self.min_prefix_bytes);
if self.has_odd_byte {
hex_string.pop().unwrap();
}
hex_string
}
/// Minimum bytes that would match this prefix. (e.g. "abc0" for "abc")
///
/// Use this to partition a sorted slice, and test `matches(id)` from there.
pub fn min_prefix_bytes(&self) -> &[u8] {
&self.min_prefix_bytes
}
/// Returns the bytes representation if this prefix can be a full id.
pub fn as_full_bytes(&self) -> Option<&[u8]> {
(!self.has_odd_byte).then_some(&self.min_prefix_bytes)
}
fn split_odd_byte(&self) -> (Option<u8>, &[u8]) {
if self.has_odd_byte {
let (&odd, prefix) = self.min_prefix_bytes.split_last().unwrap();
(Some(odd), prefix)
} else {
(None, &self.min_prefix_bytes)
}
}
pub fn matches<Q: ObjectId>(&self, id: &Q) -> bool {
let id_bytes = id.as_bytes();
let (maybe_odd, prefix) = self.split_odd_byte();
if id_bytes.starts_with(prefix) {
if let Some(odd) = maybe_odd {
matches!(id_bytes.get(prefix.len()), Some(v) if v & 0xf0 == odd)
} else {
true
}
} else {
false
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PrefixResolution<T> {
NoMatch,
SingleMatch(T),
AmbiguousMatch,
}
impl<T> PrefixResolution<T> {
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PrefixResolution<U> {
match self {
PrefixResolution::NoMatch => PrefixResolution::NoMatch,
PrefixResolution::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)),
PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
}
}
}
impl<T: Clone> PrefixResolution<T> {
pub fn plus(&self, other: &PrefixResolution<T>) -> PrefixResolution<T> {
match (self, other) {
(PrefixResolution::NoMatch, other) => other.clone(),
(local, PrefixResolution::NoMatch) => local.clone(),
(PrefixResolution::AmbiguousMatch, _) => PrefixResolution::AmbiguousMatch,
(_, PrefixResolution::AmbiguousMatch) => PrefixResolution::AmbiguousMatch,
(PrefixResolution::SingleMatch(_), PrefixResolution::SingleMatch(_)) => {
PrefixResolution::AmbiguousMatch
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hex_prefix_prefixes() {
let prefix = HexPrefix::new("").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"");
let prefix = HexPrefix::new("1").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"\x10");
let prefix = HexPrefix::new("12").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"\x12");
let prefix = HexPrefix::new("123").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"\x12\x30");
}
#[test]
fn test_hex_prefix_matches() {
let id = CommitId::from_hex("1234");
assert!(HexPrefix::new("").unwrap().matches(&id));
assert!(HexPrefix::new("1").unwrap().matches(&id));
assert!(HexPrefix::new("12").unwrap().matches(&id));
assert!(HexPrefix::new("123").unwrap().matches(&id));
assert!(HexPrefix::new("1234").unwrap().matches(&id));
assert!(!HexPrefix::new("12345").unwrap().matches(&id));
assert!(!HexPrefix::new("a").unwrap().matches(&id));
assert!(!HexPrefix::new("1a").unwrap().matches(&id));
assert!(!HexPrefix::new("12a").unwrap().matches(&id));
assert!(!HexPrefix::new("123a").unwrap().matches(&id));
}
}

View file

@ -79,3 +79,144 @@ macro_rules! impl_id_type {
}
pub(crate) use {id_type, impl_id_type};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HexPrefix {
// For odd-length prefix, lower 4 bits of the last byte is padded with 0
min_prefix_bytes: Vec<u8>,
has_odd_byte: bool,
}
impl HexPrefix {
pub fn new(prefix: &str) -> Option<HexPrefix> {
let has_odd_byte = prefix.len() & 1 != 0;
let min_prefix_bytes = if has_odd_byte {
hex::decode(prefix.to_owned() + "0").ok()?
} else {
hex::decode(prefix).ok()?
};
Some(HexPrefix {
min_prefix_bytes,
has_odd_byte,
})
}
pub fn from_bytes(bytes: &[u8]) -> Self {
HexPrefix {
min_prefix_bytes: bytes.to_owned(),
has_odd_byte: false,
}
}
pub fn hex(&self) -> String {
let mut hex_string = hex::encode(&self.min_prefix_bytes);
if self.has_odd_byte {
hex_string.pop().unwrap();
}
hex_string
}
/// Minimum bytes that would match this prefix. (e.g. "abc0" for "abc")
///
/// Use this to partition a sorted slice, and test `matches(id)` from there.
pub fn min_prefix_bytes(&self) -> &[u8] {
&self.min_prefix_bytes
}
/// Returns the bytes representation if this prefix can be a full id.
pub fn as_full_bytes(&self) -> Option<&[u8]> {
(!self.has_odd_byte).then_some(&self.min_prefix_bytes)
}
fn split_odd_byte(&self) -> (Option<u8>, &[u8]) {
if self.has_odd_byte {
let (&odd, prefix) = self.min_prefix_bytes.split_last().unwrap();
(Some(odd), prefix)
} else {
(None, &self.min_prefix_bytes)
}
}
pub fn matches<Q: ObjectId>(&self, id: &Q) -> bool {
let id_bytes = id.as_bytes();
let (maybe_odd, prefix) = self.split_odd_byte();
if id_bytes.starts_with(prefix) {
if let Some(odd) = maybe_odd {
matches!(id_bytes.get(prefix.len()), Some(v) if v & 0xf0 == odd)
} else {
true
}
} else {
false
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PrefixResolution<T> {
NoMatch,
SingleMatch(T),
AmbiguousMatch,
}
impl<T> PrefixResolution<T> {
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PrefixResolution<U> {
match self {
PrefixResolution::NoMatch => PrefixResolution::NoMatch,
PrefixResolution::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)),
PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
}
}
}
impl<T: Clone> PrefixResolution<T> {
pub fn plus(&self, other: &PrefixResolution<T>) -> PrefixResolution<T> {
match (self, other) {
(PrefixResolution::NoMatch, other) => other.clone(),
(local, PrefixResolution::NoMatch) => local.clone(),
(PrefixResolution::AmbiguousMatch, _) => PrefixResolution::AmbiguousMatch,
(_, PrefixResolution::AmbiguousMatch) => PrefixResolution::AmbiguousMatch,
(PrefixResolution::SingleMatch(_), PrefixResolution::SingleMatch(_)) => {
PrefixResolution::AmbiguousMatch
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::CommitId;
#[test]
fn test_hex_prefix_prefixes() {
let prefix = HexPrefix::new("").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"");
let prefix = HexPrefix::new("1").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"\x10");
let prefix = HexPrefix::new("12").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"\x12");
let prefix = HexPrefix::new("123").unwrap();
assert_eq!(prefix.min_prefix_bytes(), b"\x12\x30");
}
#[test]
fn test_hex_prefix_matches() {
let id = CommitId::from_hex("1234");
assert!(HexPrefix::new("").unwrap().matches(&id));
assert!(HexPrefix::new("1").unwrap().matches(&id));
assert!(HexPrefix::new("12").unwrap().matches(&id));
assert!(HexPrefix::new("123").unwrap().matches(&id));
assert!(HexPrefix::new("1234").unwrap().matches(&id));
assert!(!HexPrefix::new("12345").unwrap().matches(&id));
assert!(!HexPrefix::new("a").unwrap().matches(&id));
assert!(!HexPrefix::new("1a").unwrap().matches(&id));
assert!(!HexPrefix::new("12a").unwrap().matches(&id));
assert!(!HexPrefix::new("123a").unwrap().matches(&id));
}
}

View file

@ -22,8 +22,7 @@ use std::sync::Arc;
use itertools::Itertools as _;
use thiserror::Error;
use crate::index::HexPrefix;
use crate::object_id::ObjectId as _;
use crate::object_id::{HexPrefix, ObjectId as _};
use crate::op_heads_store::{OpHeadResolutionError, OpHeadsStore};
use crate::op_store::{OpStore, OpStoreError, OpStoreResult, OperationId};
use crate::operation::Operation;

View file

@ -38,9 +38,9 @@ use crate::default_index::DefaultIndexStore;
use crate::default_submodule_store::DefaultSubmoduleStore;
use crate::file_util::{IoResultExt as _, PathError};
use crate::git_backend::GitBackend;
use crate::index::{HexPrefix, Index, IndexStore, MutableIndex, PrefixResolution, ReadonlyIndex};
use crate::index::{Index, IndexStore, MutableIndex, ReadonlyIndex};
use crate::local_backend::LocalBackend;
use crate::object_id::ObjectId;
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
use crate::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};
use crate::op_store::{
OpStore, OpStoreError, OperationId, RefTarget, RemoteRef, RemoteRefState, WorkspaceId,

View file

@ -35,7 +35,7 @@ use crate::backend::{BackendError, BackendResult, ChangeId, CommitId};
use crate::commit::Commit;
use crate::git;
use crate::hex_util::to_forward_hex;
use crate::index::{HexPrefix, PrefixResolution};
use crate::object_id::{HexPrefix, PrefixResolution};
use crate::op_store::WorkspaceId;
use crate::repo::Repo;
use crate::repo_path::{FsPathParseError, RepoPathBuf};

View file

@ -15,9 +15,8 @@
use itertools::Itertools;
use jj_lib::backend::{CommitId, MillisSinceEpoch, Signature, Timestamp};
use jj_lib::id_prefix::IdPrefixContext;
use jj_lib::index::HexPrefix;
use jj_lib::index::PrefixResolution::{AmbiguousMatch, NoMatch, SingleMatch};
use jj_lib::object_id::ObjectId;
use jj_lib::object_id::PrefixResolution::{AmbiguousMatch, NoMatch, SingleMatch};
use jj_lib::object_id::{HexPrefix, ObjectId};
use jj_lib::repo::Repo;
use jj_lib::revset::RevsetExpression;
use testutils::{TestRepo, TestRepoBackend};

View file

@ -25,8 +25,7 @@ use jj_lib::backend::{ChangeId, CommitId, MillisSinceEpoch, Signature, Timestamp
use jj_lib::commit::Commit;
use jj_lib::git;
use jj_lib::git_backend::GitBackend;
use jj_lib::index::{HexPrefix, PrefixResolution};
use jj_lib::object_id::ObjectId;
use jj_lib::object_id::{HexPrefix, ObjectId, PrefixResolution};
use jj_lib::op_store::{RefTarget, RemoteRef, RemoteRefState, WorkspaceId};
use jj_lib::repo::Repo;
use jj_lib::repo_path::RepoPath;