binius_hash/
serialization.rs

1// Copyright 2024-2025 Irreducible Inc.
2
3use std::{borrow::Borrow, cmp::min};
4
5use binius_utils::{SerializationError, SerializationMode, SerializeBytes};
6use bytes::{BufMut, buf::UninitSlice};
7use digest::{
8	Digest, Output,
9	core_api::{Block, BlockSizeUser},
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>(
69	items: impl IntoIterator<Item = impl Borrow<T>>,
70) -> Result<Output<D>, SerializationError>
71where
72	T: SerializeBytes,
73	D: Digest + BlockSizeUser,
74{
75	let mut hasher = D::new();
76	{
77		let mut buffer = HashBuffer::new(&mut hasher);
78		for item in items {
79			item.borrow()
80				.serialize(&mut buffer, SerializationMode::CanonicalTower)?;
81		}
82	}
83	Ok(hasher.finalize())
84}
85
86#[cfg(test)]
87mod tests {
88	use groestl_crypto::Groestl256;
89
90	use super::*;
91
92	#[test]
93	fn test_hash_buffer_updates() {
94		let message =
95			b"yo, listen up, here's the story about a little guy that lives in a blue world";
96		assert!(message.len() > 64);
97		assert!(message.len() < 128);
98
99		let expected_digest = Groestl256::digest(message);
100
101		let mut hasher = Groestl256::new();
102		{
103			let mut buffer = HashBuffer::new(&mut hasher);
104			buffer.put_slice(message);
105		}
106		assert_eq!(hasher.finalize(), expected_digest);
107	}
108}