1use std::{cell::RefCell, fmt::Display, hash::Hash, io, str::FromStr};
6
7use colored::Colorize;
8use sha1::Digest;
9
10use crate::internal::object::types::ObjectType;
11
12#[derive(
16 Clone,
17 Copy,
18 Debug,
19 PartialEq,
20 Eq,
21 Hash,
22 PartialOrd,
23 Ord,
24 Default,
25 serde::Deserialize,
26 serde::Serialize,
27 rkyv::Archive,
28 rkyv::Serialize,
29 rkyv::Deserialize,
30)]
31pub enum HashKind {
32 #[default]
33 Sha1,
34 Sha256,
35}
36impl HashKind {
37 pub const fn size(&self) -> usize {
39 match self {
40 HashKind::Sha1 => 20,
41 HashKind::Sha256 => 32,
42 }
44 }
45 pub const fn hex_len(&self) -> usize {
47 match self {
48 HashKind::Sha1 => 40,
49 HashKind::Sha256 => 64,
50 }
51 }
52 pub const fn as_str(&self) -> &'static str {
54 match self {
55 HashKind::Sha1 => "sha1",
56 HashKind::Sha256 => "sha256",
57 }
58 }
59}
60impl std::fmt::Display for HashKind {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.write_str(self.as_str())
63 }
64}
65impl std::str::FromStr for HashKind {
66 type Err = String;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 match s.to_ascii_lowercase().as_str() {
70 "sha1" => Ok(HashKind::Sha1),
71 "sha256" => Ok(HashKind::Sha256),
72 _ => Err("Invalid hash kind".to_string()),
73 }
74 }
75}
76
77#[derive(
78 Clone,
79 Copy,
80 Debug,
81 PartialEq,
82 Eq,
83 Hash,
84 PartialOrd,
85 Ord,
86 serde::Deserialize,
87 serde::Serialize,
88 rkyv::Archive,
89 rkyv::Serialize,
90 rkyv::Deserialize,
91)]
92pub enum ObjectHash {
96 Sha1([u8; 20]),
97 Sha256([u8; 32]),
98}
99impl Default for ObjectHash {
100 fn default() -> Self {
101 ObjectHash::Sha1([0u8; 20])
102 }
103}
104impl Display for ObjectHash {
105 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
106 write!(f, "{}", hex::encode(self.as_ref()))
107 }
108}
109impl AsRef<[u8]> for ObjectHash {
110 fn as_ref(&self) -> &[u8] {
111 match self {
112 ObjectHash::Sha1(bytes) => bytes.as_slice(),
113 ObjectHash::Sha256(bytes) => bytes.as_slice(),
114 }
115 }
116}
117impl FromStr for ObjectHash {
119 type Err = String;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 match s.len() {
123 40 => {
124 let mut h = [0u8; 20];
125 let bytes = hex::decode(s).map_err(|e| e.to_string())?;
126 h.copy_from_slice(bytes.as_slice());
127 Ok(ObjectHash::Sha1(h))
128 }
129 64 => {
130 let mut h = [0u8; 32];
131 let bytes = hex::decode(s).map_err(|e| e.to_string())?;
132 h.copy_from_slice(bytes.as_slice());
133 Ok(ObjectHash::Sha256(h))
134 }
135 _ => Err("Invalid hash length".to_string()),
136 }
137 }
138}
139
140impl ObjectHash {
141 pub fn zero_str(kind: HashKind) -> String {
143 match kind {
144 HashKind::Sha1 => "0000000000000000000000000000000000000000".to_string(),
145 HashKind::Sha256 => {
146 "0000000000000000000000000000000000000000000000000000000000000000".to_string()
147 }
148 }
149 }
150
151 pub fn kind(&self) -> HashKind {
153 match self {
154 ObjectHash::Sha1(_) => HashKind::Sha1,
155 ObjectHash::Sha256(_) => HashKind::Sha256,
156 }
157 }
158 pub fn size(&self) -> usize {
160 self.kind().size()
161 }
162
163 pub fn new(data: &[u8]) -> ObjectHash {
165 match get_hash_kind() {
166 HashKind::Sha1 => {
167 let h = sha1::Sha1::digest(data);
168 let mut bytes = [0u8; 20];
169 bytes.copy_from_slice(h.as_ref());
170 ObjectHash::Sha1(bytes)
171 }
172 HashKind::Sha256 => {
173 let h = sha2::Sha256::digest(data);
174 let mut bytes = [0u8; 32];
175 bytes.copy_from_slice(h.as_ref());
176 ObjectHash::Sha256(bytes)
177 }
178 }
179 }
180 pub fn from_type_and_data(object_type: ObjectType, data: &[u8]) -> ObjectHash {
182 let mut d: Vec<u8> = Vec::new();
183 d.extend(object_type.to_data().unwrap());
184 d.push(b' ');
185 d.extend(data.len().to_string().as_bytes());
186 d.push(b'\x00');
187 d.extend(data);
188 ObjectHash::new(&d)
189 }
190 pub fn from_bytes(bytes: &[u8]) -> Result<ObjectHash, String> {
192 let expected_len = get_hash_kind().size();
193 if bytes.len() != expected_len {
194 return Err(format!(
195 "Invalid byte length: got {}, expected {}",
196 bytes.len(),
197 expected_len
198 ));
199 }
200
201 match get_hash_kind() {
202 HashKind::Sha1 => {
203 let mut h = [0u8; 20];
204 h.copy_from_slice(bytes);
205 Ok(ObjectHash::Sha1(h))
206 }
207 HashKind::Sha256 => {
208 let mut h = [0u8; 32];
209 h.copy_from_slice(bytes);
210 Ok(ObjectHash::Sha256(h))
211 }
212 }
213 }
214 pub fn from_bytes_infer_kind(bytes: &[u8]) -> Result<ObjectHash, String> {
225 match bytes.len() {
226 20 => {
227 let mut h = [0u8; 20];
228 h.copy_from_slice(bytes);
229 Ok(ObjectHash::Sha1(h))
230 }
231 32 => {
232 let mut h = [0u8; 32];
233 h.copy_from_slice(bytes);
234 Ok(ObjectHash::Sha256(h))
235 }
236 other => Err(format!(
237 "Invalid byte length: got {other}, expected 20 (SHA-1) or 32 (SHA-256)"
238 )),
239 }
240 }
241 pub fn from_stream(data: &mut impl io::Read) -> io::Result<ObjectHash> {
243 match get_hash_kind() {
244 HashKind::Sha1 => {
245 let mut h = [0u8; 20];
246 data.read_exact(&mut h)?;
247 Ok(ObjectHash::Sha1(h))
248 }
249 HashKind::Sha256 => {
250 let mut h = [0u8; 32];
251 data.read_exact(&mut h)?;
252 Ok(ObjectHash::Sha256(h))
253 }
254 }
255 }
256
257 pub fn to_color_str(self) -> String {
259 self.to_string().red().bold().to_string()
260 }
261
262 pub fn to_data(self) -> Vec<u8> {
264 self.as_ref().to_vec()
265 }
266
267 pub fn _to_string(&self) -> String {
269 hex::encode(self.as_ref())
270 }
271
272 pub fn as_mut_bytes(&mut self) -> &mut [u8] {
274 match self {
275 ObjectHash::Sha1(bytes) => bytes.as_mut_slice(),
276 ObjectHash::Sha256(bytes) => bytes.as_mut_slice(),
277 }
278 }
279}
280
281thread_local! {
282 static CURRENT_HASH_KIND: RefCell<HashKind> = RefCell::new(HashKind::default());
286}
287pub fn set_hash_kind(kind: HashKind) {
289 CURRENT_HASH_KIND.with(|h| {
290 *h.borrow_mut() = kind;
291 });
292}
293
294pub fn get_hash_kind() -> HashKind {
296 CURRENT_HASH_KIND.with(|h| *h.borrow())
297}
298pub struct HashKindGuard {
300 prev: HashKind,
301}
302impl Drop for HashKindGuard {
304 fn drop(&mut self) {
305 set_hash_kind(self.prev);
306 }
307}
308pub fn set_hash_kind_for_test(kind: HashKind) -> HashKindGuard {
310 let prev = get_hash_kind();
311 set_hash_kind(kind);
312 HashKindGuard { prev }
313}
314#[cfg(test)]
315mod tests {
316
317 use std::{
318 io::{BufReader, Read, Seek, SeekFrom},
319 str::FromStr,
320 };
321
322 use crate::{
323 hash::{HashKind, ObjectHash, set_hash_kind_for_test},
324 internal::pack::test_pack_download::download_pack_file,
325 };
326
327 #[test]
329 fn test_sha1_new() {
330 let _guard = set_hash_kind_for_test(HashKind::Sha1);
332 let data = "Hello, world!".as_bytes();
334
335 let sha1 = ObjectHash::new(data);
337
338 let expected_sha1_hash = "943a702d06f34599aee1f8da8ef9f7296031d699";
340
341 assert_eq!(sha1.to_string(), expected_sha1_hash);
342 }
343
344 #[test]
346 fn test_sha256_new() {
347 let _guard = set_hash_kind_for_test(HashKind::Sha256);
348 let data = "Hello, world!".as_bytes();
349 let sha256 = ObjectHash::new(data);
350 let expected_sha256_hash =
351 "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3";
352 assert_eq!(sha256.to_string(), expected_sha256_hash);
353 }
354
355 #[test]
357 fn test_signature_without_delta() {
358 let _guard = set_hash_kind_for_test(HashKind::Sha1);
359 let (source, _dl_guard) = download_pack_file("small-sha1.pack");
360
361 let f = std::fs::File::open(source).unwrap();
362 let mut buffered = BufReader::new(f);
363
364 buffered.seek(SeekFrom::End(-20)).unwrap();
365 let mut buffer = vec![0; 20];
366 buffered.read_exact(&mut buffer).unwrap();
367 let signature = ObjectHash::from_bytes(buffer.as_ref()).unwrap();
368 assert_eq!(signature.kind(), HashKind::Sha1);
369 }
370
371 #[test]
373 fn test_signature_without_delta_sha256() {
374 let _guard = set_hash_kind_for_test(HashKind::Sha256);
375 let (source, _dl_guard) = download_pack_file("small-sha256.pack");
376
377 let f = std::fs::File::open(source).unwrap();
378 let mut buffered = BufReader::new(f);
379
380 buffered.seek(SeekFrom::End(-32)).unwrap();
381 let mut buffer = vec![0; 32];
382 buffered.read_exact(&mut buffer).unwrap();
383 let signature = ObjectHash::from_bytes(buffer.as_ref()).unwrap();
384 assert_eq!(signature.kind(), HashKind::Sha256);
385 }
386
387 #[test]
389 fn test_sha1_from_bytes() {
390 let _guard = set_hash_kind_for_test(HashKind::Sha1);
391 let sha1 = ObjectHash::from_bytes(&[
392 0x8a, 0xb6, 0x86, 0xea, 0xfe, 0xb1, 0xf4, 0x47, 0x02, 0x73, 0x8c, 0x8b, 0x0f, 0x24,
393 0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d,
394 ])
395 .unwrap();
396
397 assert_eq!(sha1.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
398 }
399
400 #[test]
402 fn test_sha256_from_bytes() {
403 let _guard = set_hash_kind_for_test(HashKind::Sha256);
404 let sha256 = ObjectHash::from_bytes(&[
406 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
407 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
408 0xf2, 0x00, 0x15, 0xad,
409 ])
410 .unwrap();
411
412 assert_eq!(
413 sha256.to_string(),
414 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
415 );
416 }
417
418 #[test]
420 fn test_from_stream() {
421 let _guard = set_hash_kind_for_test(HashKind::Sha1);
422 let source = [
423 0x8a, 0xb6, 0x86, 0xea, 0xfe, 0xb1, 0xf4, 0x47, 0x02, 0x73, 0x8c, 0x8b, 0x0f, 0x24,
424 0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d,
425 ];
426 let mut reader = std::io::Cursor::new(source);
427 let sha1 = ObjectHash::from_stream(&mut reader).unwrap();
428 assert_eq!(sha1.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
429 }
430
431 #[test]
433 fn test_sha256_from_stream() {
434 let _guard = set_hash_kind_for_test(HashKind::Sha256);
435 let source = [
436 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
437 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
438 0xf2, 0x00, 0x15, 0xad,
439 ];
440 let mut reader = std::io::Cursor::new(source);
441 let sha256 = ObjectHash::from_stream(&mut reader).unwrap();
442 assert_eq!(
443 sha256.to_string(),
444 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
445 );
446 }
447
448 #[test]
450 fn test_sha1_from_str() {
451 let _guard = set_hash_kind_for_test(HashKind::Sha1);
452 let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d";
453
454 match ObjectHash::from_str(hash_str) {
455 Ok(hash) => {
456 assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
457 }
458 Err(e) => println!("Error: {e}"),
459 }
460 }
461
462 #[test]
464 fn test_sha256_from_str() {
465 let _guard = set_hash_kind_for_test(HashKind::Sha256);
466 let hash_str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
467
468 match ObjectHash::from_str(hash_str) {
469 Ok(hash) => {
470 assert_eq!(
471 hash.to_string(),
472 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
473 );
474 }
475 Err(e) => println!("Error: {e}"),
476 }
477 }
478
479 #[test]
481 fn test_sha1_to_string() {
482 let _guard = set_hash_kind_for_test(HashKind::Sha1);
483 let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d";
484
485 match ObjectHash::from_str(hash_str) {
486 Ok(hash) => {
487 assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d");
488 }
489 Err(e) => println!("Error: {e}"),
490 }
491 }
492
493 #[test]
495 fn test_sha256_to_string() {
496 let _guard = set_hash_kind_for_test(HashKind::Sha256);
497 let hash_str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
498 match ObjectHash::from_str(hash_str) {
499 Ok(hash) => {
500 assert_eq!(
501 hash.to_string(),
502 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
503 );
504 }
505 Err(e) => println!("Error: {e}"),
506 }
507 }
508
509 #[test]
511 fn test_sha1_to_data() {
512 let _guard = set_hash_kind_for_test(HashKind::Sha1);
513 let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d";
514
515 match ObjectHash::from_str(hash_str) {
516 Ok(hash) => {
517 assert_eq!(
518 hash.to_data(),
519 vec![
520 0x8a, 0xb6, 0x86, 0xea, 0xfe, 0xb1, 0xf4, 0x47, 0x02, 0x73, 0x8c, 0x8b,
521 0x0f, 0x24, 0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d
522 ]
523 );
524 }
525 Err(e) => println!("Error: {e}"),
526 }
527 }
528
529 #[test]
531 fn test_sha256_to_data() {
532 let _guard = set_hash_kind_for_test(HashKind::Sha256);
533 let hash_str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
534 match ObjectHash::from_str(hash_str) {
535 Ok(hash) => {
536 assert_eq!(
537 hash.to_data(),
538 vec![
539 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
540 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
541 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,
542 ]
543 );
544 }
545 Err(e) => println!("Error: {e}"),
546 }
547 }
548}