binius_hash/
serialization.rs1use 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#[derive(Debug)]
18pub struct HashBuffer<'a, D: Digest + BlockSizeUser> {
19 digest: &'a mut D,
20 block: Block<D>,
21 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
68pub 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}