Re: [PATCH 11/14] rust: add functionality to hash an object
- From
Patrick Steinhardt <ps@pks.im>
- Date
- Oct 28, 2025, 09:18 UTC
- Message-ID
- <aQCKYtvzaP6SXmDE@pks.im>
- In-Reply-To
- <20251027004404.2152927-12-sandals@crustytoothpaste.net>
On Mon, Oct 27, 2025 at 12:44:01AM +0000, brian m. carlson wrote:
Show 7 quoted lines
> In a future commit, we'll want to hash some data when dealing with a > loose object map. Let's make this easy by creating a structure to hash > objects and calling into the C functions as necessary to perform the > hashing. For now, we only implement safe hashing, but in the future we > could add unsafe hashing if we want. Implement Clone and Drop to > appropriately manage our memory. Additionally implement Write to make > it easy to use with other formats that implement this trait.
What exactly do you mean with "safe" and "unsafe" hashing? Also, can't we drop this distinction for now until we have a need for it?
Show 13 quoted lines
> diff --git a/src/hash.rs b/src/hash.rs
> index a5b9493bd8..8798a50aef 100644
> --- a/src/hash.rs
> +++ b/src/hash.rs
> @@ -39,6 +40,81 @@ impl ObjectID {
> }
> }
>
> +pub struct Hasher {
> + algo: HashAlgorithm,
> + safe: bool,
> + ctx: *mut c_void,
> +}Nit: missing documentation.
Show 5 quoted lines
> +impl Hasher {
> + /// Create a new safe hasher.
> + pub fn new(algo: HashAlgorithm) -> Hasher {
> + let ctx = unsafe { c::git_hash_alloc() };
> + unsafe { c::git_hash_init(ctx, algo.hash_algo_ptr()) };I already noticed this in the patch that introduced this, but wouldn't it make sense to expose `git_hash_new()` instead of the combination of `alloc() + init()`?
Show 45 quoted lines
> + Hasher {
> + algo,
> + safe: true,
> + ctx,
> + }
> + }
> +
> + /// Return whether this is a safe hasher.
> + pub fn is_safe(&self) -> bool {
> + self.safe
> + }
> +
> + /// Update the hasher with the specified data.
> + pub fn update(&mut self, data: &[u8]) {
> + unsafe { c::git_hash_update(self.ctx, data.as_ptr() as *const c_void, data.len()) };
> + }
> +
> + /// Return an object ID, consuming the hasher.
> + pub fn into_oid(self) -> ObjectID {
> + let mut oid = ObjectID {
> + hash: [0u8; 32],
> + algo: self.algo as u32,
> + };
> + unsafe { c::git_hash_final_oid(&mut oid as *mut ObjectID as *mut c_void, self.ctx) };
> + oid
> + }
> +
> + /// Return a hash as a `Vec`, consuming the hasher.
> + pub fn into_vec(self) -> Vec<u8> {
> + let mut v = vec![0u8; self.algo.raw_len()];
> + unsafe { c::git_hash_final(v.as_mut_ptr(), self.ctx) };
> + v
> + }
> +}
> +
> +impl Write for Hasher {
> + fn write(&mut self, data: &[u8]) -> io::Result<usize> {
> + self.update(data);
> + Ok(data.len())
> + }
> +
> + fn flush(&mut self) -> io::Result<()> {
> + Ok(())
> + }
> +}Yup, sensible to implement this interface.
Show 11 quoted lines
> +impl Clone for Hasher {
> + fn clone(&self) -> Hasher {
> + let ctx = unsafe { c::git_hash_alloc() };
> + unsafe { c::git_hash_clone(ctx, self.ctx) };
> + Hasher {
> + algo: self.algo,
> + safe: self.safe,
> + ctx,
> + }
> + }
> +}Makes sense.
Show 5 quoted lines
> +impl Drop for Hasher {
> + fn drop(&mut self) {
> + unsafe { c::git_hash_free(self.ctx) };
> + }
> +}Likewise.
Patrick