From 31b236a70d564660cb9d39fbfe26106cb3338814 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Thu, 4 Jan 2024 16:42:52 +0900 Subject: [PATCH] object_id: move HexPrefix and PrefixResolution from index module --- cli/src/commands/bench.rs | 2 +- lib/src/default_index/composite.rs | 4 +- lib/src/default_index/mod.rs | 4 +- lib/src/default_index/mutable.rs | 4 +- lib/src/default_index/readonly.rs | 4 +- lib/src/default_revset_engine.rs | 2 +- lib/src/id_prefix.rs | 3 +- lib/src/index.rs | 142 +---------------------------- lib/src/object_id.rs | 141 ++++++++++++++++++++++++++++ lib/src/op_walk.rs | 3 +- lib/src/repo.rs | 4 +- lib/src/revset.rs | 2 +- lib/tests/test_id_prefix.rs | 5 +- lib/tests/test_revset.rs | 3 +- 14 files changed, 160 insertions(+), 163 deletions(-) diff --git a/cli/src/commands/bench.rs b/cli/src/commands/bench.rs index d8cced533..b8fef0a34 100644 --- a/cli/src/commands/bench.rs +++ b/cli/src/commands/bench.rs @@ -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}; diff --git a/lib/src/default_index/composite.rs b/lib/src/default_index/composite.rs index f4c41de11..273313668 100644 --- a/lib/src/default_index/composite.rs +++ b/lib/src/default_index/composite.rs @@ -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}; diff --git a/lib/src/default_index/mod.rs b/lib/src/default_index/mod.rs index 7a3a10ba6..dc487801d 100644 --- a/lib/src/default_index/mod.rs +++ b/lib/src/default_index/mod.rs @@ -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 { diff --git a/lib/src/default_index/mutable.rs b/lib/src/default_index/mutable.rs index 2342fca54..ba99b2adb 100644 --- a/lib/src/default_index/mutable.rs +++ b/lib/src/default_index/mutable.rs @@ -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; diff --git a/lib/src/default_index/readonly.rs b/lib/src/default_index/readonly.rs index 791847f99..e0d8ae4f8 100644 --- a/lib/src/default_index/readonly.rs +++ b/lib/src/default_index/readonly.rs @@ -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; diff --git a/lib/src/default_revset_engine.rs b/lib/src/default_revset_engine.rs index ed8cf505b..fd4265b1b 100644 --- a/lib/src/default_revset_engine.rs +++ b/lib/src/default_revset_engine.rs @@ -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, diff --git a/lib/src/id_prefix.rs b/lib/src/id_prefix.rs index f749e4599..1d599602a 100644 --- a/lib/src/id_prefix.rs +++ b/lib/src/id_prefix.rs @@ -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}; diff --git a/lib/src/index.rs b/lib/src/index.rs index 878130065..18ad86128 100644 --- a/lib/src/index.rs +++ b/lib/src/index.rs @@ -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, - has_odd_byte: bool, -} - -impl HexPrefix { - pub fn new(prefix: &str) -> Option { - 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]) { - 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(&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 { - NoMatch, - SingleMatch(T), - AmbiguousMatch, -} - -impl PrefixResolution { - pub fn map(self, f: impl FnOnce(T) -> U) -> PrefixResolution { - match self { - PrefixResolution::NoMatch => PrefixResolution::NoMatch, - PrefixResolution::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)), - PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch, - } - } -} - -impl PrefixResolution { - pub fn plus(&self, other: &PrefixResolution) -> PrefixResolution { - 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)); - } -} diff --git a/lib/src/object_id.rs b/lib/src/object_id.rs index 015acbd09..9c43f2dc3 100644 --- a/lib/src/object_id.rs +++ b/lib/src/object_id.rs @@ -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, + has_odd_byte: bool, +} + +impl HexPrefix { + pub fn new(prefix: &str) -> Option { + 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]) { + 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(&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 { + NoMatch, + SingleMatch(T), + AmbiguousMatch, +} + +impl PrefixResolution { + pub fn map(self, f: impl FnOnce(T) -> U) -> PrefixResolution { + match self { + PrefixResolution::NoMatch => PrefixResolution::NoMatch, + PrefixResolution::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)), + PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch, + } + } +} + +impl PrefixResolution { + pub fn plus(&self, other: &PrefixResolution) -> PrefixResolution { + 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)); + } +} diff --git a/lib/src/op_walk.rs b/lib/src/op_walk.rs index 5949eea7e..6fab77cd0 100644 --- a/lib/src/op_walk.rs +++ b/lib/src/op_walk.rs @@ -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; diff --git a/lib/src/repo.rs b/lib/src/repo.rs index f5e50183b..f64c64792 100644 --- a/lib/src/repo.rs +++ b/lib/src/repo.rs @@ -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, diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 22a5bdb2e..7549e1d51 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -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}; diff --git a/lib/tests/test_id_prefix.rs b/lib/tests/test_id_prefix.rs index d7960dded..a9ec064dd 100644 --- a/lib/tests/test_id_prefix.rs +++ b/lib/tests/test_id_prefix.rs @@ -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}; diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index 5ff2abd3a..4faa11101 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -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;