use std::{cell::RefCell, fmt::Display, hash::Hash, io, str::FromStr};
use colored::Colorize;
use sha1::Digest;
use crate::internal::object::types::ObjectType;
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Default,
serde::Deserialize,
serde::Serialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum HashKind {
#[default]
Sha1,
Sha256,
}
impl HashKind {
pub const fn size(&self) -> usize {
match self {
HashKind::Sha1 => 20,
HashKind::Sha256 => 32,
}
}
pub const fn hex_len(&self) -> usize {
match self {
HashKind::Sha1 => 40,
HashKind::Sha256 => 64,
}
}
pub const fn as_str(&self) -> &'static str {
match self {
HashKind::Sha1 => "sha1",
HashKind::Sha256 => "sha256",
}
}
}
impl std::fmt::Display for HashKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl std::str::FromStr for HashKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"sha1" => Ok(HashKind::Sha1),
"sha256" => Ok(HashKind::Sha256),
_ => Err("Invalid hash kind".to_string()),
}
}
}
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
serde::Deserialize,
serde::Serialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum ObjectHash {
Sha1([u8; 20]),
Sha256([u8; 32]),
}
impl Default for ObjectHash {
fn default() -> Self {
ObjectHash::Sha1([0u8; 20])
}
}
impl Display for ObjectHash {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.as_ref()))
}
}
impl AsRef<[u8]> for ObjectHash {
fn as_ref(&self) -> &[u8] {
match self {
ObjectHash::Sha1(bytes) => bytes.as_slice(),
ObjectHash::Sha256(bytes) => bytes.as_slice(),
}
}
}
impl FromStr for ObjectHash {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.len() {
40 => {
let mut h = [0u8; 20];
let bytes = hex::decode(s).map_err(|e| e.to_string())?;
h.copy_from_slice(bytes.as_slice());
Ok(ObjectHash::Sha1(h))
}
64 => {
let mut h = [0u8; 32];
let bytes = hex::decode(s).map_err(|e| e.to_string())?;
h.copy_from_slice(bytes.as_slice());
Ok(ObjectHash::Sha256(h))
}
_ => Err("Invalid hash length".to_string()),
}
}
}
impl ObjectHash {
pub fn zero_str(kind: HashKind) -> String {
match kind {
HashKind::Sha1 => "0000000000000000000000000000000000000000".to_string(),
HashKind::Sha256 => {
"0000000000000000000000000000000000000000000000000000000000000000".to_string()
}
}
}
pub fn kind(&self) -> HashKind {
match self {
ObjectHash::Sha1(_) => HashKind::Sha1,
ObjectHash::Sha256(_) => HashKind::Sha256,
}
}
pub fn size(&self) -> usize {
self.kind().size()
}
pub fn new(data: &[u8]) -> ObjectHash {
match get_hash_kind() {
HashKind::Sha1 => {
let h = sha1::Sha1::digest(data);
let mut bytes = [0u8; 20];
bytes.copy_from_slice(h.as_ref());
ObjectHash::Sha1(bytes)
}
HashKind::Sha256 => {
let h = sha2::Sha256::digest(data);
let mut bytes = [0u8; 32];
bytes.copy_from_slice(h.as_ref());
ObjectHash::Sha256(bytes)
}
}
}
pub fn from_type_and_data(object_type: ObjectType, data: &[u8]) -> ObjectHash {
let mut d: Vec<u8> = Vec::new();
d.extend(object_type.to_data().unwrap());
d.push(b' ');
d.extend(data.len().to_string().as_bytes());
d.push(b'\x00');
d.extend(data);
ObjectHash::new(&d)
}
pub fn from_bytes(bytes: &[u8]) -> Result<ObjectHash, String> {
let expected_len = get_hash_kind().size();
if bytes.len() != expected_len {
return Err(format!(
"Invalid byte length: got {}, expected {}",
bytes.len(),
expected_len
));
}
match get_hash_kind() {
HashKind::Sha1 => {
let mut h = [0u8; 20];
h.copy_from_slice(bytes);
Ok(ObjectHash::Sha1(h))
}
HashKind::Sha256 => {
let mut h = [0u8; 32];
h.copy_from_slice(bytes);
Ok(ObjectHash::Sha256(h))
}
}
}
pub fn from_bytes_infer_kind(bytes: &[u8]) -> Result<ObjectHash, String> {
match bytes.len() {
20 => {
let mut h = [0u8; 20];
h.copy_from_slice(bytes);
Ok(ObjectHash::Sha1(h))
}
32 => {
let mut h = [0u8; 32];
h.copy_from_slice(bytes);
Ok(ObjectHash::Sha256(h))
}
other => Err(format!(
"Invalid byte length: got {other}, expected 20 (SHA-1) or 32 (SHA-256)"
)),
}
}
pub fn from_stream(data: &mut impl io::Read) -> io::Result<ObjectHash> {
match get_hash_kind() {
HashKind::Sha1 => {
let mut h = [0u8; 20];
data.read_exact(&mut h)?;
Ok(ObjectHash::Sha1(h))
}
HashKind::Sha256 => {
let mut h = [0u8; 32];
data.read_exact(&mut h)?;
Ok(ObjectHash::Sha256(h))
}
}
}
pub fn to_color_str(self) -> String {
self.to_string().red().bold().to_string()
}
pub fn to_data(self) -> Vec<u8> {
self.as_ref().to_vec()
}
pub fn _to_string(&self) -> String {
hex::encode(self.as_ref())
}
pub fn as_mut_bytes(&mut self) -> &mut [u8] {
match self {
ObjectHash::Sha1(bytes) => bytes.as_mut_slice(),
ObjectHash::Sha256(bytes) => bytes.as_mut_slice(),
}
}
}
thread_local! {
static CURRENT_HASH_KIND: RefCell<HashKind> = RefCell::new(HashKind::default());
}
pub fn set_hash_kind(kind: HashKind) {
CURRENT_HASH_KIND.with(|h| {
*h.borrow_mut() = kind;
});
}
pub fn get_hash_kind() -> HashKind {
CURRENT_HASH_KIND.with(|h| *h.borrow())
}
pub struct HashKindGuard {
prev: HashKind,
}
impl Drop for HashKindGuard {
fn drop(&mut self) {
set_hash_kind(self.prev);
}
}
pub fn set_hash_kind_for_test(kind: HashKind) -> HashKindGuard {
let prev = get_hash_kind();
set_hash_kind(kind);
HashKindGuard { prev }
}
#[cfg(test)]
mod tests {
use std::{
io::{BufReader, Read, Seek, SeekFrom},
str::FromStr,
};
use crate::{
hash::{HashKind, ObjectHash, set_hash_kind_for_test},
internal::pack::test_pack_download::download_pack_file,
};
#[test]
fn test_sha1_new() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let data = "Hello, world!".as_bytes();
let sha1 = ObjectHash::new(data);
let expected_sha1_hash = "943a702d06f34599aee1f8da8ef9f7296031d699";
assert_eq!(sha1.to_string(), expected_sha1_hash);
}
#[test]
fn test_sha256_new() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let data = "Hello, world!".as_bytes();
let sha256 = ObjectHash::new(data);
let expected_sha256_hash =
"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3";
assert_eq!(sha256.to_string(), expected_sha256_hash);
}
#[test]
fn test_signature_without_delta() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let (source, _dl_guard) = download_pack_file("small-sha1.pack");
let f = std::fs::File::open(source).unwrap();
let mut buffered = BufReader::new(f);
buffered.seek(SeekFrom::End(-20)).unwrap();
let mut buffer = vec![0; 20];
buffered.read_exact(&mut buffer).unwrap();
let signature = ObjectHash::from_bytes(buffer.as_ref()).unwrap();
assert_eq!(signature.kind(), HashKind::Sha1);
}
#[test]
fn test_signature_without_delta_sha256() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let (source, _dl_guard) = download_pack_file("small-sha256.pack");
let f = std::fs::File::open(source).unwrap();
let mut buffered = BufReader::new(f);
buffered.seek(SeekFrom::End(-32)).unwrap();
let mut buffer = vec![0; 32];
buffered.read_exact(&mut buffer).unwrap();
let signature = ObjectHash::from_bytes(buffer.as_ref()).unwrap();
assert_eq!(signature.kind(), HashKind::Sha256);
}
#[test]
fn test_sha1_from_bytes() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let sha1 = ObjectHash::from_bytes(&[
0x8a, 0xb6, 0x86, 0xea, 0xfe, 0xb1, 0xf4, 0x47, 0x02, 0x73, 0x8c, 0x8b, 0x0f, 0x24,
0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d,
])
.unwrap();
assert_eq!(sha1.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
}
#[test]
fn test_sha256_from_bytes() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let sha256 = ObjectHash::from_bytes(&[
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
0xf2, 0x00, 0x15, 0xad,
])
.unwrap();
assert_eq!(
sha256.to_string(),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
#[test]
fn test_from_stream() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let source = [
0x8a, 0xb6, 0x86, 0xea, 0xfe, 0xb1, 0xf4, 0x47, 0x02, 0x73, 0x8c, 0x8b, 0x0f, 0x24,
0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d,
];
let mut reader = std::io::Cursor::new(source);
let sha1 = ObjectHash::from_stream(&mut reader).unwrap();
assert_eq!(sha1.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
}
#[test]
fn test_sha256_from_stream() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let source = [
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
0xf2, 0x00, 0x15, 0xad,
];
let mut reader = std::io::Cursor::new(source);
let sha256 = ObjectHash::from_stream(&mut reader).unwrap();
assert_eq!(
sha256.to_string(),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
#[test]
fn test_sha1_from_str() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d";
match ObjectHash::from_str(hash_str) {
Ok(hash) => {
assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
}
Err(e) => println!("Error: {e}"),
}
}
#[test]
fn test_sha256_from_str() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let hash_str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
match ObjectHash::from_str(hash_str) {
Ok(hash) => {
assert_eq!(
hash.to_string(),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
Err(e) => println!("Error: {e}"),
}
}
#[test]
fn test_sha1_to_string() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d";
match ObjectHash::from_str(hash_str) {
Ok(hash) => {
assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
}
Err(e) => println!("Error: {e}"),
}
}
#[test]
fn test_sha256_to_string() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let hash_str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
match ObjectHash::from_str(hash_str) {
Ok(hash) => {
assert_eq!(
hash.to_string(),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
Err(e) => println!("Error: {e}"),
}
}
#[test]
fn test_sha1_to_data() {
let _guard = set_hash_kind_for_test(HashKind::Sha1);
let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d";
match ObjectHash::from_str(hash_str) {
Ok(hash) => {
assert_eq!(
hash.to_data(),
vec![
0x8a, 0xb6, 0x86, 0xea, 0xfe, 0xb1, 0xf4, 0x47, 0x02, 0x73, 0x8c, 0x8b,
0x0f, 0x24, 0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d
]
);
}
Err(e) => println!("Error: {e}"),
}
}
#[test]
fn test_sha256_to_data() {
let _guard = set_hash_kind_for_test(HashKind::Sha256);
let hash_str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
match ObjectHash::from_str(hash_str) {
Ok(hash) => {
assert_eq!(
hash.to_data(),
vec![
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,
]
);
}
Err(e) => println!("Error: {e}"),
}
}
}