binius_hash/
serialization.rs

1// Copyright 2024-2025 Irreducible Inc.
2
3use std::{borrow::Borrow, cmp::min};
4
5use binius_utils::{SerializationMode, SerializeBytes};
6use bytes::{buf::UninitSlice, BufMut};
7use digest::{
8	core_api::{Block, BlockSizeUser},
9	Digest, Output,
10};
11
12/// Adapter that wraps a [`Digest`] references and exposes the [`BufMut`] interface.
13///
14/// This adapter is useful so that structs that implement [`SerializeBytes`] can be serialized
15/// directly to a hasher.
16#[derive(Debug)]
17pub struct HashBuffer<'a, D: Digest + BlockSizeUser> {
18	digest: &'a mut D,
19	block: Block<D>,
20	/// Invariant: `index` is always strictly less than `D::block_size()`.
21	index: usize,
22}
23
24impl<'a, D: Digest + BlockSizeUser> HashBuffer<'a, D> {
25	pub fn new(digest: &'a mut D) -> Self {
26		Self {
27			digest,
28			block: <Block<D>>::default(),
29			index: 0,
30		}
31	}
32
33	fn flush(&mut self) {
34		self.digest.update(&self.block.as_slice()[..self.index]);
35		self.index = 0;
36	}
37}
38
39unsafe impl<D: Digest + BlockSizeUser> BufMut for HashBuffer<'_, D> {
40	fn remaining_mut(&self) -> usize {
41		usize::MAX
42	}
43
44	unsafe fn advance_mut(&mut self, mut cnt: usize) {
45		while cnt > 0 {
46			let remaining = min(<D as BlockSizeUser>::block_size() - self.index, cnt);
47			cnt -= remaining;
48			self.index += remaining;
49			if self.index == <D as BlockSizeUser>::block_size() {
50				self.flush();
51			}
52		}
53	}
54
55	fn chunk_mut(&mut self) -> &mut UninitSlice {
56		let buffer = &mut self.block[self.index..];
57		buffer.into()
58	}
59}
60
61impl<D: Digest + BlockSizeUser> Drop for HashBuffer<'_, D> {
62	fn drop(&mut self) {
63		self.flush()
64	}
65}
66
67/// Hashes a sequence of serializable items.
68pub fn hash_serialize<T, D>(items: impl IntoIterator<Item = impl Borrow<T>>) -> Output<D>
69where
70	T: SerializeBytes,
71	D: Digest + BlockSizeUser,
72{
73	let mut hasher = D::new();
74	{
75		let mut buffer = HashBuffer::new(&mut hasher);
76		for item in items {
77			item.borrow()
78				.serialize(&mut buffer, SerializationMode::CanonicalTower)
79				.expect("HashBuffer has infinite capacity");
80		}
81	}
82	hasher.finalize()
83}
84
85#[cfg(test)]
86mod tests {
87	use groestl_crypto::Groestl256;
88
89	use super::*;
90
91	#[test]
92	fn test_hash_buffer_updates() {
93		let message =
94			b"yo, listen up, here's the story about a little guy that lives in a blue world";
95		assert!(message.len() > 64);
96		assert!(message.len() < 128);
97
98		let expected_digest = Groestl256::digest(message);
99
100		let mut hasher = Groestl256::new();
101		{
102			let mut buffer = HashBuffer::new(&mut hasher);
103			buffer.put_slice(message);
104		}
105		assert_eq!(hasher.finalize(), expected_digest);
106	}
107}