| 1 | //! Content hashing for clipboard deduplication |
| 2 | |
| 3 | use sha2::{Digest, Sha256}; |
| 4 | |
| 5 | /// Compute SHA256 hash of clipboard content |
| 6 | pub fn compute_hash(data: &[u8]) -> String { |
| 7 | let mut hasher = Sha256::new(); |
| 8 | hasher.update(data); |
| 9 | format!("SHA256:{}", hex::encode(hasher.finalize())) |
| 10 | } |
| 11 | |
| 12 | /// Quick hash from first N bytes + length for large content |
| 13 | /// Useful for quick deduplication check before transferring |
| 14 | #[allow(dead_code)] |
| 15 | pub fn compute_quick_hash(data: &[u8], size: u64) -> String { |
| 16 | const SAMPLE_SIZE: usize = 4096; |
| 17 | let mut hasher = Sha256::new(); |
| 18 | hasher.update(&data[..data.len().min(SAMPLE_SIZE)]); |
| 19 | hasher.update(size.to_le_bytes()); |
| 20 | format!("SHA256-QUICK:{}", hex::encode(hasher.finalize())) |
| 21 | } |
| 22 | |
| 23 | #[cfg(test)] |
| 24 | mod tests { |
| 25 | use super::*; |
| 26 | |
| 27 | #[test] |
| 28 | fn test_compute_hash() { |
| 29 | let data = b"Hello, World!"; |
| 30 | let hash = compute_hash(data); |
| 31 | assert!(hash.starts_with("SHA256:")); |
| 32 | assert_eq!(hash.len(), 7 + 64); // "SHA256:" + 64 hex chars |
| 33 | } |
| 34 | |
| 35 | #[test] |
| 36 | fn test_same_content_same_hash() { |
| 37 | let data1 = b"test content"; |
| 38 | let data2 = b"test content"; |
| 39 | assert_eq!(compute_hash(data1), compute_hash(data2)); |
| 40 | } |
| 41 | |
| 42 | #[test] |
| 43 | fn test_different_content_different_hash() { |
| 44 | let data1 = b"test content 1"; |
| 45 | let data2 = b"test content 2"; |
| 46 | assert_ne!(compute_hash(data1), compute_hash(data2)); |
| 47 | } |
| 48 | } |
| 49 |