Skip to main content

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}