binius_hash/
serialization.rs1use std::{borrow::Borrow, cmp::min};
4
5use binius_utils::{SerializationError, SerializeBytes};
6use bytes::{BufMut, buf::UninitSlice};
7use digest::{
8 Digest, Output,
9 block_api::{Block, BlockSizeUser},
10};
11
12#[derive(Debug)]
17pub struct HashBuffer<'a, D: Digest + BlockSizeUser> {
18 digest: &'a mut D,
19 block: Block<D>,
20 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
39#[allow(unsafe_code)]
43unsafe impl<D: Digest + BlockSizeUser> BufMut for HashBuffer<'_, D> {
44 fn remaining_mut(&self) -> usize {
45 usize::MAX
46 }
47
48 unsafe fn advance_mut(&mut self, mut cnt: usize) {
49 while cnt > 0 {
50 let remaining = min(<D as BlockSizeUser>::block_size() - self.index, cnt);
51 cnt -= remaining;
52 self.index += remaining;
53 if self.index == <D as BlockSizeUser>::block_size() {
54 self.flush();
55 }
56 }
57 }
58
59 fn chunk_mut(&mut self) -> &mut UninitSlice {
60 let buffer = &mut self.block[self.index..];
61 buffer.into()
62 }
63}
64
65impl<D: Digest + BlockSizeUser> Drop for HashBuffer<'_, D> {
66 fn drop(&mut self) {
67 self.flush();
68 }
69}
70
71pub fn hash_serialize<T, D>(
73 items: impl IntoIterator<Item = impl Borrow<T>>,
74) -> Result<Output<D>, SerializationError>
75where
76 T: SerializeBytes,
77 D: Digest + BlockSizeUser,
78{
79 let mut hasher = D::new();
80 {
81 let mut buffer = HashBuffer::new(&mut hasher);
82 for item in items {
83 item.borrow().serialize(&mut buffer)?;
84 }
85 }
86 Ok(hasher.finalize())
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::StdDigest;
93
94 #[test]
95 fn test_hash_buffer_updates() {
96 let message =
97 b"yo, listen up, here's the story about a little guy that lives in a blue world";
98 assert!(message.len() > 64);
99 assert!(message.len() < 128);
100
101 let expected_digest = StdDigest::digest(message);
102
103 let mut hasher = StdDigest::new();
104 {
105 let mut buffer = HashBuffer::new(&mut hasher);
106 buffer.put_slice(message);
107 }
108 assert_eq!(hasher.finalize(), expected_digest);
109 }
110}