Skip to main content

hash/
partial.rs

1use std::fs::File;
2use std::io::{self, Read, Seek, SeekFrom};
3use std::path::Path;
4use xxhash_rust::xxh64::Xxh64;
5
6/// Tiered read: first `head` bytes + last `tail` bytes (if file larger than `head + tail`).
7#[derive(Debug, Clone)]
8pub struct PartialOptions {
9    pub head: usize,
10    pub tail: usize,
11}
12
13impl Default for PartialOptions {
14    fn default() -> Self {
15        Self {
16            head: 64 * 1024,
17            tail: 64 * 1024,
18        }
19    }
20}
21
22/// xxHash64 over partial content (head + tail strategy), lowercase hex.
23pub fn partial_digest(path: &Path, opts: &PartialOptions) -> io::Result<String> {
24    let mut file = File::open(path)?;
25    let len = file.metadata()?.len() as usize;
26    let mut hasher = Xxh64::new(0);
27
28    if len <= opts.head + opts.tail {
29        let mut buf = Vec::new();
30        file.read_to_end(&mut buf)?;
31        hasher.update(&buf);
32    } else {
33        let mut head = vec![0u8; opts.head];
34        file.read_exact(&mut head)?;
35        hasher.update(&head);
36
37        let tail_start = len.saturating_sub(opts.tail);
38        file.seek(SeekFrom::Start(tail_start as u64))?;
39        let mut tail = vec![0u8; opts.tail];
40        file.read_exact(&mut tail)?;
41        hasher.update(&tail);
42    }
43
44    Ok(format!("{:016x}", hasher.digest()))
45}