binius_hash/
serialization.rs

1// Copyright 2024-2025 Irreducible Inc.
2
3use std::{borrow::Borrow, cmp::min};
4
5use binius_transcript::BufMut;
6use binius_utils::{SerializationError, SerializeBytes};
7use bytes::buf::UninitSlice;
8use digest::{
9	Digest, Output,
10	core_api::{Block, BlockSizeUser},
11};
12
13/// Adapter that wraps [`Digest`] references and exposes the [`BufMut`] interface.
14///
15/// This adapter is useful so that structs that implement [`SerializeBytes`] can be serialized
16/// directly to a hasher.
17#[derive(Debug)]
18pub struct HashBuffer<'a, D: Digest + BlockSizeUser> {
19	digest: &'a mut D,
20	block: Block<D>,
21	/// Invariant: `index` is always strictly less than `D::block_size()`.
22	index: usize,
23}
24
25impl<'a, D: Digest + BlockSizeUser> HashBuffer<'a, D> {
26	pub fn new(digest: &'a mut D) -> Self {
27		Self {
28			digest,
29			block: <Block<D>>::default(),
30			index: 0,
31		}
32	}
33
34	fn flush(&mut self) {
35		self.digest.update(&self.block.as_slice()[..self.index]);
36		self.index = 0;
37	}
38}
39
40unsafe impl<D: Digest + BlockSizeUser> BufMut for HashBuffer<'_, D> {
41	fn remaining_mut(&self) -> usize {
42		usize::MAX
43	}
44
45	unsafe fn advance_mut(&mut self, mut cnt: usize) {
46		while cnt > 0 {
47			let remaining = min(<D as BlockSizeUser>::block_size() - self.index, cnt);
48			cnt -= remaining;
49			self.index += remaining;
50			if self.index == <D as BlockSizeUser>::block_size() {
51				self.flush();
52			}
53		}
54	}
55
56	fn chunk_mut(&mut self) -> &mut UninitSlice {
57		let buffer = &mut self.block[self.index..];
58		buffer.into()
59	}
60}
61
62impl<D: Digest + BlockSizeUser> Drop for HashBuffer<'_, D> {
63	fn drop(&mut self) {
64		self.flush()
65	}
66}
67
68/// Hashes a sequence of serializable items.
69pub fn hash_serialize<T, D>(
70	items: impl IntoIterator<Item = impl Borrow<T>>,
71) -> Result<Output<D>, SerializationError>
72where
73	T: SerializeBytes,
74	D: Digest + BlockSizeUser,
75{
76	let mut hasher = D::new();
77	{
78		let mut buffer = HashBuffer::new(&mut hasher);
79		for item in items {
80			item.borrow().serialize(&mut buffer)?;
81		}
82	}
83	Ok(hasher.finalize())
84}
85
86#[cfg(test)]
87mod tests {
88	use super::*;
89	use crate::StdDigest;
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 = StdDigest::digest(message);
99
100		let mut hasher = StdDigest::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}