From 733b1d56c1fe969ad69b49b54f65c73668317d9c Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 12 Dec 2024 13:57:55 +0800 Subject: [PATCH] refactor: introduce from_stream method and SIZE constant --- mercury/src/hash.rs | 48 ++++++++++++++++++----------- mercury/src/internal/index.rs | 2 +- mercury/src/internal/pack/decode.rs | 10 ++---- mercury/src/utils.rs | 6 +--- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/mercury/src/hash.rs b/mercury/src/hash.rs index 5f346c227..75b6aff6b 100644 --- a/mercury/src/hash.rs +++ b/mercury/src/hash.rs @@ -3,7 +3,7 @@ //! location in the Git internal and mega database. //! -use std::fmt::Display; +use std::{fmt::Display, io}; use colored::Colorize; use serde::{Deserialize, Serialize}; @@ -44,7 +44,7 @@ impl Display for SHA1 { write!(f, "{}", hex::encode(self.0)) } } -impl AsRef<[u8]> for SHA1{ +impl AsRef<[u8]> for SHA1 { fn as_ref(&self) -> &[u8] { &self.0 } @@ -96,6 +96,9 @@ impl std::str::FromStr for SHA1 { /// These method naming conventions (`new`, `from`, `to`) provide clarity and predictability in the API, making it easier for users /// to understand the intended use and functionality of each method within the `SHA1` struct. impl SHA1 { + // The size of the SHA-1 hash value in bytes + pub const SIZE: usize = 20; + /// Calculate the SHA-1 hash of `Vec` data, then create a Hash value pub fn new(data: &Vec) -> SHA1 { // Create a Sha1 object for calculating the SHA-1 hash @@ -122,11 +125,18 @@ impl SHA1 { pub fn from_bytes(bytes: &[u8]) -> SHA1 { let mut h = SHA1::default(); h.0.copy_from_slice(bytes); - h } - /// Export sha1 value to String with the color + /// Read the Hash value from the stream + /// This function will read exactly 20 bytes from the stream + pub fn from_stream(data: &mut impl io::Read) -> io::Result { + let mut h = SHA1::default(); + data.read_exact(&mut h.0)?; + Ok(h) + } + + /// Export sha1 value to String with the color pub fn to_color_str(self) -> String { self.to_string().red().bold().to_string() } @@ -139,7 +149,7 @@ impl SHA1 { #[cfg(test)] mod tests { - + use std::io::BufReader; use std::io::Read; use std::io::Seek; @@ -188,10 +198,18 @@ mod tests { 0xf2, 0x56, 0x7c, 0x36, 0xda, 0x6d, ]); - assert_eq!( - sha1.to_string(), - "8ab686eafeb1f44702738c8b0f24f2567c36da6d" - ); + assert_eq!(sha1.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d"); + } + + #[test] + fn test_from_stream() { + 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 = SHA1::from_stream(&mut reader).unwrap(); + assert_eq!(sha1.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d"); } #[test] @@ -200,10 +218,7 @@ mod tests { match SHA1::from_str(hash_str) { Ok(hash) => { - assert_eq!( - hash.to_string(), - "8ab686eafeb1f44702738c8b0f24f2567c36da6d" - ); + assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d"); } Err(e) => println!("Error: {}", e), } @@ -215,10 +230,7 @@ mod tests { match SHA1::from_str(hash_str) { Ok(hash) => { - assert_eq!( - hash.to_string(), - "8ab686eafeb1f44702738c8b0f24f2567c36da6d" - ); + assert_eq!(hash.to_string(), "8ab686eafeb1f44702738c8b0f24f2567c36da6d"); } Err(e) => println!("Error: {}", e), } @@ -227,7 +239,7 @@ mod tests { #[test] fn test_sha1_to_data() { let hash_str = "8ab686eafeb1f44702738c8b0f24f2567c36da6d"; - + match SHA1::from_str(hash_str) { Ok(hash) => { assert_eq!( diff --git a/mercury/src/internal/index.rs b/mercury/src/internal/index.rs index e2fa7cdf3..d537af91c 100644 --- a/mercury/src/internal/index.rs +++ b/mercury/src/internal/index.rs @@ -257,7 +257,7 @@ impl Index { } // Extensions - while file.bytes_read() + utils::SHA1_SIZE < total_size as usize { + while file.bytes_read() + SHA1::SIZE < total_size as usize { // The remaining 20 bytes must be checksum let sign = utils::read_bytes(file, 4)?; println!("{:?}", String::from_utf8(sign.clone())?); diff --git a/mercury/src/internal/pack/decode.rs b/mercury/src/internal/pack/decode.rs index 680682d86..2766e5960 100644 --- a/mercury/src/internal/pack/decode.rs +++ b/mercury/src/internal/pack/decode.rs @@ -301,11 +301,9 @@ impl Pack { }, ObjectType::HashDelta => { // Read 20 bytes to get the reference object SHA1 hash - let mut buf_ref = [0; 20]; - pack.read_exact(&mut buf_ref).unwrap(); - let ref_sha1 = SHA1::from_bytes(buf_ref.as_ref()); //TODO SHA1::from_stream() + let ref_sha1 = SHA1::from_stream(pack).unwrap(); // Offset is incremented by 20 bytes - *offset += 20; //TODO 改为常量 + *offset += SHA1::SIZE; let (data, raw_size) = self.decompress_data(pack, size)?; *offset += raw_size; @@ -426,9 +424,7 @@ impl Pack { } log_info(i, self); let render_hash = reader.final_hash(); - let mut trailer_buf = [0; 20]; - reader.read_exact(&mut trailer_buf).unwrap(); - self.signature = SHA1::from_bytes(trailer_buf.as_ref()); + self.signature = SHA1::from_stream(&mut reader).unwrap(); if render_hash != self.signature { return Err(GitError::InvalidPackFile(format!( diff --git a/mercury/src/utils.rs b/mercury/src/utils.rs index 1916ce230..5a18386d4 100644 --- a/mercury/src/utils.rs +++ b/mercury/src/utils.rs @@ -3,8 +3,6 @@ use std::io::Read; use crate::hash::SHA1; -pub const SHA1_SIZE: usize = 20; - pub fn read_bytes(file: &mut impl Read, len: usize) -> io::Result> { let mut buf = vec![0; len]; file.read_exact(&mut buf)?; @@ -12,7 +10,5 @@ pub fn read_bytes(file: &mut impl Read, len: usize) -> io::Result> { } pub fn read_sha1(file: &mut impl Read) -> io::Result { - let mut buf = [0; 20]; - file.read_exact(&mut buf)?; - Ok(SHA1::from_bytes(&buf)) + SHA1::from_stream(file) } \ No newline at end of file