Skip to content

Commit

Permalink
feat: git-hash::Prefix::from_id() (#298)
Browse files Browse the repository at this point in the history
A way to obtain a prefix of an object id, with all non-prefix
bytes set to zero.
  • Loading branch information
Byron committed Feb 11, 2022
1 parent ea77051 commit a36fa03
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 179 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -100,7 +100,7 @@ document-features = { version = "0.1.0", optional = true }
[profile.dev.package]
git-object = { opt-level = 3 }
git-ref = { opt-level = 3 }
git-pack = { opt-level = 3 }
#git-pack = { opt-level = 3 }
git-hash = { opt-level = 3 }
git-actor = { opt-level = 3 }
git-config = { opt-level = 3 }
Expand Down
3 changes: 3 additions & 0 deletions git-hash/Cargo.toml
Expand Up @@ -21,5 +21,8 @@ quick-error = "2.0.0"
hex = "0.4.2"
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }

[dev-dependencies]
git-testtools = { path = "../tests/tools"}

[package.metadata.docs.rs]
all-features = true
2 changes: 1 addition & 1 deletion git-hash/src/lib.rs
Expand Up @@ -11,7 +11,7 @@ use std::{convert::TryFrom, str::FromStr};
pub use borrowed::oid;

mod owned;
pub use owned::ObjectId;
pub use owned::{ObjectId, Prefix};

#[allow(missing_docs)]
pub mod decode {
Expand Down
40 changes: 40 additions & 0 deletions git-hash/src/owned.rs
Expand Up @@ -2,6 +2,46 @@ use std::{borrow::Borrow, convert::TryInto, fmt, ops::Deref};

use crate::{borrowed::oid, Kind, SIZE_OF_SHA1_DIGEST};

/// An partial owned hash possibly identifying an object uniquely,
/// whose non-prefix bytes are zeroed.
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Prefix {
inner: ObjectId,
prefix_len: usize,
}

impl Prefix {
/// TODO: docs
pub fn from_id(id: impl AsRef<oid>, hex_len: usize) -> Self {
let id = id.as_ref();
assert!(
hex_len <= id.kind().len_in_hex(),
"hex_len must not be larger than the maximum hex len of the input id"
);
let prefix = match id.kind() {
Kind::Sha1 => {
let mut b = [0u8; 20];
let copy_len = (hex_len + 1) / 2;
b[..copy_len].copy_from_slice(&id.as_bytes()[..copy_len]);
if hex_len % 2 == 1 {
b[hex_len / 2] &= 0xf0;
}
ObjectId::Sha1(b)
}
};
Prefix {
inner: prefix,
prefix_len: hex_len,
}
}

/// TODO: docs
pub fn prefix(&self) -> &oid {
&self.inner
}
}

/// An owned hash identifying objects, most commonly Sha1
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
Expand Down
33 changes: 33 additions & 0 deletions git-hash/tests/oid/mod.rs
@@ -1,3 +1,36 @@
mod prefix {
mod from_id {
use git_hash::{Kind, ObjectId};
use git_testtools::hex_to_id;

#[test]
fn various_valid_inputs() {
let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
let oid = hex_to_id(oid_hex);

assert_eq!(git_hash::Prefix::from_id(oid, 0).prefix(), ObjectId::null(oid.kind()));

for prefix_len in 1..oid.kind().len_in_hex() {
let mut expected = String::from(&oid_hex[..prefix_len]);
let num_of_zeros = oid.kind().len_in_hex() - prefix_len;
expected.extend(std::iter::repeat('0').take(num_of_zeros));
assert_eq!(
git_hash::Prefix::from_id(oid, prefix_len).prefix().to_hex().to_string(),
expected,
"{}",
prefix_len
);
}
}
#[test]
#[should_panic]
fn panics_if_hex_len_is_longer_than_oid_len_in_hex() {
let kind = Kind::Sha1;
git_hash::Prefix::from_id(ObjectId::null(kind), kind.len_in_hex() + 1);
}
}
}

mod short_hex {
#[test]
fn display_entire_range_sha1() {
Expand Down
2 changes: 1 addition & 1 deletion git-pack/src/multi_index/access.rs
Expand Up @@ -67,7 +67,7 @@ impl File {
}

/// TODO
pub fn lookup_abbrev(&self, _id: impl AsRef<git_hash::oid>, _hex_len: usize) -> Option<EntryIndex> {
pub fn lookup_prefix(&self, _id: impl AsRef<git_hash::oid>, _hex_len: usize) -> Option<EntryIndex> {
todo!()
}

Expand Down
176 changes: 0 additions & 176 deletions git-pack/tests/pack/multi_index.rs

This file was deleted.

74 changes: 74 additions & 0 deletions git-pack/tests/pack/multi_index/mod.rs
@@ -0,0 +1,74 @@
use std::path::PathBuf;

use git_pack::multi_index::File;

fn multi_index() -> (File, PathBuf) {
let path = git_testtools::scripted_fixture_repo_read_only("make_pack_gen_repo_multi_index.sh")
.expect("test fixture exists")
.join(".git/objects/pack/multi-pack-index");
let file = git_pack::multi_index::File::at(&path).unwrap();
(file, path)
}

mod access {
use super::multi_index;
use git_testtools::hex_to_id;
use std::path::PathBuf;

#[test]
fn lookup_abbrev() {
let (file, _path) = multi_index();

for (idx, entry) in file.iter().enumerate() {
let hex_len = (idx % file.object_hash().len_in_hex()).min(7);
let hex_oid = entry.oid.to_hex_with_len(hex_len).to_string();
assert_eq!(hex_oid.len(), hex_len);
let _oid_prefix = git_hash::Prefix::from_id(&entry.oid, hex_len);
// file.lookup_prefix(oid_prefix, hex_len).expect("non-ambiguous")
}
}

#[test]
fn general() {
let (file, path) = multi_index();

assert_eq!(file.version(), git_pack::multi_index::Version::V1);
assert_eq!(file.path(), path);
assert_eq!(file.num_indices(), 1);
assert_eq!(file.object_hash(), git_hash::Kind::Sha1);
assert_eq!(file.num_objects(), 868);
assert_eq!(file.checksum(), hex_to_id("39a3804d0a84de609e4fcb49e66dc1297c75ca11"));
// assert_eq!()
assert_eq!(
file.index_names(),
vec![PathBuf::from("pack-542ad1d1c7c762ea4e36907570ff9e4b5b7dde1b.idx")]
);

for (idx, expected_pack_offset, expected_oid) in &[
(0u32, 25267u64, hex_to_id("000f574443efab4ddbeee3621e49124eb3f8b6d0")),
(140, 30421, hex_to_id("2935a65b1d69fb33c93dabc4cdf65a6f4d30ce4c")),
(867, 24540, hex_to_id("ffea360a6a54c1185eeae4f3cfefc927cf7a35a9")),
] {
let actual_oid = file.oid_at_index(*idx);
assert_eq!(actual_oid, *expected_oid);
assert_eq!(file.lookup(actual_oid), Some(*idx));
let (pack_id, pack_offset) = file.pack_id_and_pack_offset_at_index(*idx);
assert_eq!(pack_id, 0, "we only have one pack here");
assert_eq!(pack_offset, *expected_pack_offset);
}

let mut count = 0;
for (idx, entry) in file.iter().enumerate() {
assert_eq!(entry.oid, file.oid_at_index(idx as u32));
let (pack_index, pack_offset) = file.pack_id_and_pack_offset_at_index(idx as u32);
assert_eq!(pack_index, entry.pack_index);
assert_eq!(pack_offset, entry.pack_offset);
count += 1;
}
assert_eq!(count, file.num_objects());
}
}

mod verify;

mod write;

0 comments on commit a36fa03

Please sign in to comment.