qubit_io/wrappers/checksum_writer.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10use std::hash::Hasher;
11use std::io::{
12 Result,
13 Seek,
14 SeekFrom,
15 Write,
16};
17
18/// Writer wrapper that updates a checksum hasher with bytes written.
19///
20/// The wrapped hasher is updated only after the inner writer accepts bytes.
21/// Failed writes do not update the hasher.
22///
23/// The checksum value is whatever the supplied [`Hasher`] reports. The Rust
24/// standard-library hashers are not specified as stable file formats and are
25/// not cryptographic digests; use this wrapper for stream instrumentation
26/// unless the chosen hasher explicitly documents stronger guarantees.
27///
28/// # Examples
29/// ```
30/// use std::collections::hash_map::DefaultHasher;
31/// use std::hash::Hasher;
32/// use std::io::Write;
33///
34/// use qubit_io::ChecksumWriter;
35///
36/// let mut expected = DefaultHasher::new();
37/// expected.write(b"payload");
38///
39/// let mut writer = ChecksumWriter::new(Vec::new(), DefaultHasher::new());
40/// writer.write_all(b"payload")?;
41///
42/// assert_eq!(expected.finish(), writer.checksum());
43/// assert_eq!(b"payload", writer.get_ref().as_slice());
44/// # Ok::<(), std::io::Error>(())
45/// ```
46pub struct ChecksumWriter<W, H> {
47 inner: W,
48 hasher: H,
49}
50
51impl<W, H> ChecksumWriter<W, H>
52where
53 H: Hasher,
54{
55 /// Creates a checksum writer.
56 ///
57 /// # Parameters
58 /// - `inner`: Writer to wrap.
59 /// - `hasher`: Hasher updated with successfully written bytes.
60 ///
61 /// # Returns
62 /// A new checksum writer.
63 #[inline]
64 pub fn new(inner: W, hasher: H) -> Self {
65 Self { inner, hasher }
66 }
67
68 /// Returns the current checksum value.
69 ///
70 /// # Returns
71 /// The value reported by [`Hasher::finish`].
72 #[inline]
73 pub fn checksum(&self) -> u64 {
74 self.hasher.finish()
75 }
76
77 /// Returns an immutable reference to the wrapped writer.
78 ///
79 /// # Returns
80 /// The wrapped writer reference.
81 #[inline]
82 pub fn get_ref(&self) -> &W {
83 &self.inner
84 }
85
86 /// Returns a mutable reference to the wrapped writer.
87 ///
88 /// # Returns
89 /// The wrapped writer reference.
90 #[inline]
91 pub fn get_mut(&mut self) -> &mut W {
92 &mut self.inner
93 }
94
95 /// Returns an immutable reference to the wrapped hasher.
96 ///
97 /// # Returns
98 /// The wrapped hasher reference.
99 #[inline]
100 pub fn hasher_ref(&self) -> &H {
101 &self.hasher
102 }
103
104 /// Returns a mutable reference to the wrapped hasher.
105 ///
106 /// # Returns
107 /// The wrapped hasher reference.
108 #[inline]
109 pub fn hasher_mut(&mut self) -> &mut H {
110 &mut self.hasher
111 }
112
113 /// Consumes this wrapper and returns the wrapped writer and hasher.
114 ///
115 /// # Returns
116 /// A tuple containing the wrapped writer and hasher.
117 #[inline]
118 pub fn into_inner(self) -> (W, H) {
119 (self.inner, self.hasher)
120 }
121}
122
123impl<W, H> Write for ChecksumWriter<W, H>
124where
125 W: Write,
126 H: Hasher,
127{
128 fn write(&mut self, buffer: &[u8]) -> Result<usize> {
129 let count = self.inner.write(buffer)?;
130 self.hasher.write(&buffer[..count]);
131 Ok(count)
132 }
133
134 #[inline]
135 fn flush(&mut self) -> Result<()> {
136 self.inner.flush()
137 }
138}
139
140impl<W, H> Seek for ChecksumWriter<W, H>
141where
142 W: Seek,
143 H: Hasher,
144{
145 #[inline]
146 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
147 self.inner.seek(position)
148 }
149}