diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-01-12 04:28:28 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-01-12 04:51:34 +0800 |
| commit | c5fb22694e95f12c24b8d8af76999be7aea3fcec (patch) | |
| tree | 399d8a24ce491fb635f3d09f2123290fe784059e /crates/utils/sha1_hash | |
| parent | 444754489aca0454eb54e15a49fb8a6db0b68a07 (diff) | |
Reorganize crate structure and move documentation files
Diffstat (limited to 'crates/utils/sha1_hash')
| -rw-r--r-- | crates/utils/sha1_hash/Cargo.toml | 9 | ||||
| -rw-r--r-- | crates/utils/sha1_hash/res/story.txt | 48 | ||||
| -rw-r--r-- | crates/utils/sha1_hash/res/story_crlf.sha1 | 1 | ||||
| -rw-r--r-- | crates/utils/sha1_hash/res/story_lf.sha1 | 1 | ||||
| -rw-r--r-- | crates/utils/sha1_hash/src/lib.rs | 257 |
5 files changed, 0 insertions, 316 deletions
diff --git a/crates/utils/sha1_hash/Cargo.toml b/crates/utils/sha1_hash/Cargo.toml deleted file mode 100644 index e206efd..0000000 --- a/crates/utils/sha1_hash/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "sha1_hash" -edition = "2024" -version.workspace = true - -[dependencies] -tokio = { version = "1.48", features = ["full"] } -sha1 = "0.10" -futures = "0.3" diff --git a/crates/utils/sha1_hash/res/story.txt b/crates/utils/sha1_hash/res/story.txt deleted file mode 100644 index a91f467..0000000 --- a/crates/utils/sha1_hash/res/story.txt +++ /dev/null @@ -1,48 +0,0 @@ -魏曹者,程序员也,发稀甚于代码。 -忽接神秘电话曰: -"贺君中彩,得长生之赐。" -魏曹冷笑曰:"吾命尚不及下版之期。" - -翌日果得U盘。 -接入电脑,弹窗示曰: -"点此确认,即获永生。" -魏曹径点"永拒"。 - -三月后,U盘自格其盘。 -进度条滞于九九。 -客服电话已成空号。 -魏曹乃知身可不死,然体内癌细胞亦得不灭。 - -遂谒主请辞。 -主曰:"巧甚,公司正欲优化。" -魏曹曰:"吾不死。" -主目骤亮:"则可007至司闭。" - -魏曹始试诸死法。 -坠楼,卧医三月,账单令其愿死。 -饮鸩,肝肾永损,然终不得死。 -终决卧轨。 - -择高铁最速者。 -司机探头曰:"兄台,吾亦不死身也。" -"此车已碾如君者二十人矣。" - -二人遂坐轨畔对饮。 -司机曰:"知最讽者何?" -"吾等永存,而所爱者皆逝矣。" - -魏曹忽得系统提示: -"侦得用户消极求生,将启工模。" -自是无日不毕KPI,否则遍尝绝症之苦。 - -是日对镜整寿衣。 -忽见顶生一丝乌发。 -泫然泣下,此兆示其将复活一轮回。 - ---- 忽忆DeepSeek尝作Footer曰: -"文成而Hash1验,若星河之固。" -遂取哈希值校之, -字符流转如天河倒泻, -终得"e3b0c44298fc1c14"之数。 -然文末数字竟阙如残月, -方知此篇亦遭永劫轮回。 diff --git a/crates/utils/sha1_hash/res/story_crlf.sha1 b/crates/utils/sha1_hash/res/story_crlf.sha1 deleted file mode 100644 index bc8ad25..0000000 --- a/crates/utils/sha1_hash/res/story_crlf.sha1 +++ /dev/null @@ -1 +0,0 @@ -40c1d848d8d6a14b9403ee022f2b28dabb3b3a71 diff --git a/crates/utils/sha1_hash/res/story_lf.sha1 b/crates/utils/sha1_hash/res/story_lf.sha1 deleted file mode 100644 index c2e3213..0000000 --- a/crates/utils/sha1_hash/res/story_lf.sha1 +++ /dev/null @@ -1 +0,0 @@ -6838aca280112635a2cbf93440f4c04212f58ee8 diff --git a/crates/utils/sha1_hash/src/lib.rs b/crates/utils/sha1_hash/src/lib.rs deleted file mode 100644 index 96a7897..0000000 --- a/crates/utils/sha1_hash/src/lib.rs +++ /dev/null @@ -1,257 +0,0 @@ -use sha1::{Digest, Sha1}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use tokio::fs::File; -use tokio::io::{AsyncReadExt, BufReader}; -use tokio::task; - -/// # Struct - Sha1Result -/// -/// Records SHA1 calculation results, including the file path and hash value -#[derive(Debug, Clone)] -pub struct Sha1Result { - pub file_path: PathBuf, - pub hash: String, -} - -/// Calc SHA1 hash of a string -pub fn calc_sha1_string<S: AsRef<str>>(input: S) -> String { - let mut hasher = Sha1::new(); - hasher.update(input.as_ref().as_bytes()); - let hash_result = hasher.finalize(); - - hash_result - .iter() - .map(|b| format!("{:02x}", b)) - .collect::<String>() -} - -/// Calc SHA1 hash of a single file -pub async fn calc_sha1<P: AsRef<Path>>( - path: P, - buffer_size: usize, -) -> Result<Sha1Result, Box<dyn std::error::Error + Send + Sync>> { - let file_path = path.as_ref().to_string_lossy().to_string(); - - // Open file asynchronously - let file = File::open(&path).await?; - let mut reader = BufReader::with_capacity(buffer_size, file); - let mut hasher = Sha1::new(); - let mut buffer = vec![0u8; buffer_size]; - - // Read file in chunks and update hash asynchronously - loop { - let n = reader.read(&mut buffer).await?; - if n == 0 { - break; - } - hasher.update(&buffer[..n]); - } - - let hash_result = hasher.finalize(); - - // Convert to hex string - let hash_hex = hash_result - .iter() - .map(|b| format!("{:02x}", b)) - .collect::<String>(); - - Ok(Sha1Result { - file_path: file_path.into(), - hash: hash_hex, - }) -} - -/// Calc SHA1 hashes for multiple files using multi-threading -pub async fn calc_sha1_multi<P, I>( - paths: I, - buffer_size: usize, -) -> Result<Vec<Sha1Result>, Box<dyn std::error::Error + Send + Sync>> -where - P: AsRef<Path> + Send + Sync + 'static, - I: IntoIterator<Item = P>, -{ - let buffer_size = Arc::new(buffer_size); - - // Collect all file paths - let file_paths: Vec<P> = paths.into_iter().collect(); - - if file_paths.is_empty() { - return Ok(Vec::new()); - } - - // Create tasks for each file - let tasks: Vec<_> = file_paths - .into_iter() - .map(|path| { - let buffer_size = Arc::clone(&buffer_size); - task::spawn(async move { calc_sha1(path, *buffer_size).await }) - }) - .collect(); - - // Execute tasks with concurrency limit using join_all - let results: Vec<Result<Sha1Result, Box<dyn std::error::Error + Send + Sync>>> = - futures::future::join_all(tasks) - .await - .into_iter() - .map(|task_result| match task_result { - Ok(Ok(calc_result)) => Ok(calc_result), - Ok(Err(e)) => Err(e), - Err(e) => Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>), - }) - .collect(); - - // Check for any errors and collect successful results - let mut successful_results = Vec::new(); - for result in results { - match result { - Ok(success) => successful_results.push(success), - Err(e) => return Err(e), - } - } - - Ok(successful_results) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - - #[test] - fn test_sha1_string() { - let test_string = "Hello, SHA1!"; - let hash = calc_sha1_string(test_string); - - let expected_hash = "de1c3daadc6f0f1626f4cf56c03e05a1e5d7b187"; - - assert_eq!( - hash, expected_hash, - "SHA1 hash should be consistent for same input" - ); - } - - #[test] - fn test_sha1_string_empty() { - let hash = calc_sha1_string(""); - - // SHA1 of empty string is "da39a3ee5e6b4b0d3255bfef95601890afd80709" - let expected_empty_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; - assert_eq!( - hash, expected_empty_hash, - "SHA1 hash mismatch for empty string" - ); - } - - #[tokio::test] - async fn test_sha1_accuracy() { - // Test file path relative to the crate root - let test_file_path = "res/story.txt"; - // Choose expected hash file based on platform - let expected_hash_path = if cfg!(windows) { - "res/story_crlf.sha1" - } else { - "res/story_lf.sha1" - }; - - // Calculate SHA1 hash - let result = calc_sha1(test_file_path, 8192) - .await - .expect("Failed to calculate SHA1"); - - // Read expected hash from file - let expected_hash = fs::read_to_string(expected_hash_path) - .expect("Failed to read expected hash file") - .trim() - .to_string(); - - // Verify the calculated hash matches expected hash - assert_eq!( - result.hash, expected_hash, - "SHA1 hash mismatch for test file" - ); - - println!("Test file: {}", result.file_path.display()); - println!("Calculated hash: {}", result.hash); - println!("Expected hash: {}", expected_hash); - println!( - "Platform: {}", - if cfg!(windows) { - "Windows" - } else { - "Unix/Linux" - } - ); - } - - #[tokio::test] - async fn test_sha1_empty_file() { - // Create a temporary empty file for testing - let temp_file = "test_empty.txt"; - fs::write(temp_file, "").expect("Failed to create empty test file"); - - let result = calc_sha1(temp_file, 4096) - .await - .expect("Failed to calculate SHA1 for empty file"); - - // SHA1 of empty string is "da39a3ee5e6b4b0d3255bfef95601890afd80709" - let expected_empty_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; - assert_eq!( - result.hash, expected_empty_hash, - "SHA1 hash mismatch for empty file" - ); - - // Clean up - fs::remove_file(temp_file).expect("Failed to remove temporary test file"); - } - - #[tokio::test] - async fn test_sha1_simple_text() { - // Create a temporary file with simple text - let temp_file = "test_simple.txt"; - let test_content = "Hello, SHA1!"; - fs::write(temp_file, test_content).expect("Failed to create simple test file"); - - let result = calc_sha1(temp_file, 4096) - .await - .expect("Failed to calculate SHA1 for simple text"); - - // Note: This test just verifies that the function works without errors - // The actual hash value is not critical for this test - - println!("Simple text test - Calculated hash: {}", result.hash); - - // Clean up - fs::remove_file(temp_file).expect("Failed to remove temporary test file"); - } - - #[tokio::test] - async fn test_sha1_multi_files() { - // Test multiple files calculation - let test_files = vec!["res/story.txt"]; - - let results = calc_sha1_multi(test_files, 8192) - .await - .expect("Failed to calculate SHA1 for multiple files"); - - assert_eq!(results.len(), 1, "Should have calculated hash for 1 file"); - - // Choose expected hash file based on platform - let expected_hash_path = if cfg!(windows) { - "res/story_crlf.sha1" - } else { - "res/story_lf.sha1" - }; - - // Read expected hash from file - let expected_hash = fs::read_to_string(expected_hash_path) - .expect("Failed to read expected hash file") - .trim() - .to_string(); - - assert_eq!( - results[0].hash, expected_hash, - "SHA1 hash mismatch in multi-file test" - ); - } -} |
