Skip to main content

git_internal/
utils.rs

1//! Shared I/O utilities for Git-internal including buffered readers, SHA abstractions, and helpers
2//! for reading pack/file bytes while tracking stream progress.
3
4use std::{
5    io,
6    io::{BufRead, Read},
7};
8
9use sha1::{Digest, Sha1};
10
11use crate::hash::{HashKind, ObjectHash, get_hash_kind};
12/// Read exactly `len` bytes from the given reader.
13pub fn read_bytes(file: &mut impl Read, len: usize) -> io::Result<Vec<u8>> {
14    let mut buf = vec![0; len];
15    file.read_exact(&mut buf)?;
16    Ok(buf)
17}
18
19/// Read an object hash from the given reader.
20pub fn read_sha(file: &mut impl Read) -> io::Result<ObjectHash> {
21    ObjectHash::from_stream(file)
22}
23
24/// A lightweight wrapper that counts bytes read from the underlying reader.
25/// replace deflate.intotal() in decompress_data
26pub struct CountingReader<R> {
27    pub inner: R,
28    pub bytes_read: u64,
29}
30
31impl<R> CountingReader<R> {
32    /// Creates a new `CountingReader` wrapping the given reader.
33    pub fn new(inner: R) -> Self {
34        Self {
35            inner,
36            bytes_read: 0,
37        }
38    }
39}
40
41impl<R: Read> Read for CountingReader<R> {
42    /// Reads data into the provided buffer, updating the byte count.
43    /// Returns the number of bytes read.
44    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
45        let n = self.inner.read(buf)?;
46        self.bytes_read += n as u64;
47        Ok(n)
48    }
49}
50
51impl<R: BufRead> BufRead for CountingReader<R> {
52    /// Fills the internal buffer and returns a slice to it.
53    /// Updates the byte count.
54    /// Returns the number of bytes read.
55    fn fill_buf(&mut self) -> io::Result<&[u8]> {
56        self.inner.fill_buf()
57    }
58
59    /// Consumes `amt` bytes from the internal buffer, updating the byte count.
60    /// Returns the number of bytes consumed.
61    fn consume(&mut self, amt: usize) {
62        self.bytes_read += amt as u64;
63        self.inner.consume(amt);
64    }
65}
66/// a hash abstraction to support both SHA1 and SHA256
67/// which for stream hashing handle use (e.g. Sha1::new())
68/// `std::io::Write` trait to update the hash state
69#[derive(Clone)]
70pub enum HashAlgorithm {
71    Sha1(Sha1),
72    Sha256(sha2::Sha256),
73    // Future: support other hash algorithms
74}
75impl HashAlgorithm {
76    /// Update hash with data
77    pub fn update(&mut self, data: &[u8]) {
78        match self {
79            HashAlgorithm::Sha1(hasher) => hasher.update(data),
80            HashAlgorithm::Sha256(hasher) => hasher.update(data),
81        }
82    }
83    /// Finalize and get hash result
84    pub fn finalize(self) -> Vec<u8> {
85        match self {
86            HashAlgorithm::Sha1(hasher) => hasher.finalize().to_vec(),
87            HashAlgorithm::Sha256(hasher) => hasher.finalize().to_vec(),
88        }
89    }
90    /// Create a new hash algorithm instance based on the current hash kind.
91    pub fn new() -> Self {
92        match get_hash_kind() {
93            HashKind::Sha1 => HashAlgorithm::Sha1(Sha1::new()),
94            HashKind::Sha256 => HashAlgorithm::Sha256(sha2::Sha256::new()),
95        }
96    }
97}
98impl std::io::Write for HashAlgorithm {
99    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
100        self.update(buf);
101        Ok(buf.len())
102    }
103    fn flush(&mut self) -> io::Result<()> {
104        Ok(())
105    }
106}
107impl Default for HashAlgorithm {
108    fn default() -> Self {
109        Self::new()
110    }
111}