#[cfg(feature = "std")]
#[inline]
fn read_and_update<R>(inner: &mut R, buf: &mut [u8], mut on_data: impl FnMut(&[u8])) -> std::io::Result<usize>
where
R: std::io::Read,
{
let n = inner.read(buf)?;
if let Some(data) = buf.get(..n) {
on_data(data);
}
Ok(n)
}
#[cfg(feature = "std")]
#[inline]
fn read_vectored_and_update<R>(
inner: &mut R,
bufs: &mut [std::io::IoSliceMut<'_>],
mut on_data: impl FnMut(&[u8]),
) -> std::io::Result<usize>
where
R: std::io::Read,
{
let n = inner.read_vectored(bufs)?;
let mut remaining = n;
for buf in bufs {
if remaining == 0 {
break;
}
let to_hash = remaining.min(buf.len());
if to_hash == 0 {
continue;
}
if let Some(data) = buf.get(..to_hash) {
on_data(data);
}
remaining = remaining.strict_sub(to_hash);
}
Ok(n)
}
#[cfg(feature = "std")]
#[inline]
fn write_and_update<W>(inner: &mut W, buf: &[u8], mut on_data: impl FnMut(&[u8])) -> std::io::Result<usize>
where
W: std::io::Write,
{
let n = inner.write(buf)?;
if let Some(data) = buf.get(..n) {
on_data(data);
}
Ok(n)
}
#[cfg(feature = "std")]
#[inline]
fn write_vectored_and_update<W>(
inner: &mut W,
bufs: &[std::io::IoSlice<'_>],
mut on_data: impl FnMut(&[u8]),
) -> std::io::Result<usize>
where
W: std::io::Write,
{
let n = inner.write_vectored(bufs)?;
let mut remaining = n;
for buf in bufs {
if remaining == 0 {
break;
}
let to_hash = remaining.min(buf.len());
if to_hash == 0 {
continue;
}
if let Some(data) = buf.get(..to_hash) {
on_data(data);
}
remaining = remaining.strict_sub(to_hash);
}
Ok(n)
}
#[cfg(feature = "std")]
fn debug_adapter<R: core::fmt::Debug, H: core::fmt::Debug>(
f: &mut core::fmt::Formatter<'_>,
name: &str,
inner: &R,
hasher: &H,
) -> core::fmt::Result {
f.debug_struct(name)
.field("inner", inner)
.field("hasher", hasher)
.finish()
}
#[cfg(feature = "std")]
#[derive(Clone)]
pub struct ChecksumReader<R, C: crate::Checksum> {
inner: R,
hasher: C,
}
#[cfg(feature = "std")]
impl<R: core::fmt::Debug, C: crate::Checksum + core::fmt::Debug> core::fmt::Debug for ChecksumReader<R, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_adapter(f, "ChecksumReader", &self.inner, &self.hasher)
}
}
#[cfg(feature = "std")]
impl<R, C: crate::Checksum> ChecksumReader<R, C> {
#[inline]
#[must_use]
pub fn new(inner: R) -> Self {
Self {
inner,
hasher: C::new(),
}
}
#[inline]
#[must_use]
pub fn with_initial(inner: R, initial: C::Output) -> Self {
Self {
inner,
hasher: C::with_initial(initial),
}
}
#[inline]
#[must_use]
pub fn checksum(&self) -> C::Output {
self.hasher.finalize()
}
#[inline]
pub fn hasher_mut(&mut self) -> &mut C {
&mut self.hasher
}
#[inline]
pub fn into_parts(self) -> (R, C::Output) {
(self.inner, self.hasher.finalize())
}
#[inline]
pub fn into_inner(self) -> R {
self.inner
}
#[inline]
pub fn inner(&self) -> &R {
&self.inner
}
#[inline]
pub fn inner_mut(&mut self) -> &mut R {
&mut self.inner
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read, C: crate::Checksum> std::io::Read for ChecksumReader<R, C> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
read_and_update(&mut self.inner, buf, |data| self.hasher.update(data))
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
read_vectored_and_update(&mut self.inner, bufs, |data| self.hasher.update(data))
}
}
#[cfg(feature = "std")]
#[derive(Clone)]
pub struct ChecksumWriter<W, C: crate::Checksum> {
inner: W,
hasher: C,
}
#[cfg(feature = "std")]
impl<W: core::fmt::Debug, C: crate::Checksum + core::fmt::Debug> core::fmt::Debug for ChecksumWriter<W, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_adapter(f, "ChecksumWriter", &self.inner, &self.hasher)
}
}
#[cfg(feature = "std")]
impl<W, C: crate::Checksum> ChecksumWriter<W, C> {
#[inline]
#[must_use]
pub fn new(inner: W) -> Self {
Self {
inner,
hasher: C::new(),
}
}
#[inline]
#[must_use]
pub fn with_initial(inner: W, initial: C::Output) -> Self {
Self {
inner,
hasher: C::with_initial(initial),
}
}
#[inline]
#[must_use]
pub fn checksum(&self) -> C::Output {
self.hasher.finalize()
}
#[inline]
pub fn hasher_mut(&mut self) -> &mut C {
&mut self.hasher
}
#[inline]
pub fn into_parts(self) -> (W, C::Output) {
(self.inner, self.hasher.finalize())
}
#[inline]
pub fn into_inner(self) -> W {
self.inner
}
#[inline]
pub fn inner(&self) -> &W {
&self.inner
}
#[inline]
pub fn inner_mut(&mut self) -> &mut W {
&mut self.inner
}
}
#[cfg(feature = "std")]
impl<W: std::io::Write, C: crate::Checksum> std::io::Write for ChecksumWriter<W, C> {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
write_and_update(&mut self.inner, buf, |data| self.hasher.update(data))
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
self.inner.flush()
}
#[inline]
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
write_vectored_and_update(&mut self.inner, bufs, |data| self.hasher.update(data))
}
}
#[cfg(feature = "std")]
#[derive(Clone)]
pub struct DigestReader<R, D: crate::traits::Digest> {
inner: R,
hasher: D,
}
#[cfg(feature = "std")]
impl<R: core::fmt::Debug, D: crate::traits::Digest + core::fmt::Debug> core::fmt::Debug for DigestReader<R, D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_adapter(f, "DigestReader", &self.inner, &self.hasher)
}
}
#[cfg(feature = "std")]
impl<R, D: crate::traits::Digest> DigestReader<R, D> {
#[inline]
#[must_use]
pub fn new(inner: R) -> Self {
Self {
inner,
hasher: D::new(),
}
}
#[inline]
#[must_use]
pub fn digest(&self) -> D::Output {
self.hasher.finalize()
}
#[inline]
pub fn hasher_mut(&mut self) -> &mut D {
&mut self.hasher
}
#[inline]
pub fn into_parts(self) -> (R, D::Output) {
(self.inner, self.hasher.finalize())
}
#[inline]
pub fn into_inner(self) -> R {
self.inner
}
#[inline]
pub fn inner(&self) -> &R {
&self.inner
}
#[inline]
pub fn inner_mut(&mut self) -> &mut R {
&mut self.inner
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read, D: crate::traits::Digest> std::io::Read for DigestReader<R, D> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
read_and_update(&mut self.inner, buf, |data| self.hasher.update(data))
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
read_vectored_and_update(&mut self.inner, bufs, |data| self.hasher.update(data))
}
}
#[cfg(feature = "std")]
#[derive(Clone)]
pub struct DigestWriter<W, D: crate::traits::Digest> {
inner: W,
hasher: D,
}
#[cfg(feature = "std")]
impl<W: core::fmt::Debug, D: crate::traits::Digest + core::fmt::Debug> core::fmt::Debug for DigestWriter<W, D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_adapter(f, "DigestWriter", &self.inner, &self.hasher)
}
}
#[cfg(feature = "std")]
impl<W, D: crate::traits::Digest> DigestWriter<W, D> {
#[inline]
#[must_use]
pub fn new(inner: W) -> Self {
Self {
inner,
hasher: D::new(),
}
}
#[inline]
#[must_use]
pub fn digest(&self) -> D::Output {
self.hasher.finalize()
}
#[inline]
pub fn hasher_mut(&mut self) -> &mut D {
&mut self.hasher
}
#[inline]
pub fn into_parts(self) -> (W, D::Output) {
(self.inner, self.hasher.finalize())
}
#[inline]
pub fn into_inner(self) -> W {
self.inner
}
#[inline]
pub fn inner(&self) -> &W {
&self.inner
}
#[inline]
pub fn inner_mut(&mut self) -> &mut W {
&mut self.inner
}
}
#[cfg(feature = "std")]
impl<W: std::io::Write, D: crate::traits::Digest> std::io::Write for DigestWriter<W, D> {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
write_and_update(&mut self.inner, buf, |data| self.hasher.update(data))
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
self.inner.flush()
}
#[inline]
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
write_vectored_and_update(&mut self.inner, bufs, |data| self.hasher.update(data))
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use super::{ChecksumWriter, DigestWriter};
use crate::{Checksum, traits::Digest};
#[derive(Clone, Copy, Debug, Default)]
struct Sum(u32);
impl Checksum for Sum {
const OUTPUT_SIZE: usize = 4;
type Output = u32;
fn new() -> Self {
Self(0)
}
fn with_initial(initial: Self::Output) -> Self {
Self(initial)
}
fn update(&mut self, data: &[u8]) {
self.0 = data.iter().fold(self.0, |acc, &b| acc.wrapping_add(u32::from(b)));
}
fn finalize(&self) -> Self::Output {
self.0
}
fn reset(&mut self) {
self.0 = 0;
}
}
#[derive(Clone, Copy, Debug, Default)]
struct SumDigest(u8);
impl Digest for SumDigest {
const OUTPUT_SIZE: usize = 4;
type Output = [u8; 4];
fn new() -> Self {
Self(0)
}
fn update(&mut self, data: &[u8]) {
self.0 = data.iter().fold(self.0, |acc, &b| acc.wrapping_add(b));
}
fn finalize(&self) -> Self::Output {
[self.0; 4]
}
fn reset(&mut self) {
self.0 = 0;
}
}
#[derive(Debug, Default)]
struct PartialWriter {
inner: Vec<u8>,
max_per_call: usize,
}
impl PartialWriter {
fn new(max_per_call: usize) -> Self {
Self {
inner: Vec::new(),
max_per_call,
}
}
}
impl Write for PartialWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let written = self.max_per_call.min(buf.len());
self.inner.extend_from_slice(&buf[..written]);
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let mut remaining = self.max_per_call;
let mut written = 0usize;
for buf in bufs {
let take = remaining.min(buf.len());
if remaining == 0 {
break;
}
if take == 0 {
continue;
}
self.inner.extend_from_slice(&buf[..take]);
written = written.strict_add(take);
remaining = remaining.strict_sub(take);
}
Ok(written)
}
}
#[inline]
fn checksum_sum(data: &[u8]) -> u32 {
data.iter().fold(0u32, |acc, &b| acc.wrapping_add(u32::from(b)))
}
#[inline]
fn digest_sum(data: &[u8]) -> [u8; 4] {
let sum = data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b));
[sum; 4]
}
#[test]
fn checksum_writer_hashes_only_written_prefix() {
let mut writer = ChecksumWriter::<_, Sum>::new(PartialWriter::new(4));
let written = writer.write(b"abcdef").unwrap();
assert_eq!(written, 4);
assert_eq!(writer.checksum(), checksum_sum(b"abcd"));
let (inner, checksum) = writer.into_parts();
assert_eq!(inner.inner, b"abcd");
assert_eq!(checksum, checksum_sum(b"abcd"));
}
#[test]
fn checksum_writer_vectored_hashes_only_written_prefix() {
let mut writer = ChecksumWriter::<_, Sum>::new(PartialWriter::new(5));
let bufs = [IoSlice::new(b"ab"), IoSlice::new(b"cdef"), IoSlice::new(b"gh")];
let written = writer.write_vectored(&bufs).unwrap();
assert_eq!(written, 5);
assert_eq!(writer.checksum(), checksum_sum(b"abcde"));
let (inner, checksum) = writer.into_parts();
assert_eq!(inner.inner, b"abcde");
assert_eq!(checksum, checksum_sum(b"abcde"));
}
#[test]
fn digest_writer_hashes_only_written_prefix() {
let mut writer = DigestWriter::<_, SumDigest>::new(PartialWriter::new(3));
let written = writer.write(b"abcdef").unwrap();
assert_eq!(written, 3);
assert_eq!(writer.digest(), digest_sum(b"abc"));
let (inner, digest) = writer.into_parts();
assert_eq!(inner.inner, b"abc");
assert_eq!(digest, digest_sum(b"abc"));
}
#[test]
fn digest_writer_vectored_hashes_only_written_prefix() {
let mut writer = DigestWriter::<_, SumDigest>::new(PartialWriter::new(6));
let bufs = [IoSlice::new(b"ab"), IoSlice::new(b"cdef"), IoSlice::new(b"ghij")];
let written = writer.write_vectored(&bufs).unwrap();
assert_eq!(written, 6);
assert_eq!(writer.digest(), digest_sum(b"abcdef"));
let (inner, digest) = writer.into_parts();
assert_eq!(inner.inner, b"abcdef");
assert_eq!(digest, digest_sum(b"abcdef"));
}
#[test]
fn checksum_reader_vectored_skips_empty_buffers() {
let mut reader = Sum::reader(io::Cursor::new(b"abcde".to_vec()));
let mut first = [];
let mut second = [0u8; 2];
let mut third = [0u8; 3];
let mut bufs = [
IoSliceMut::new(&mut first),
IoSliceMut::new(&mut second),
IoSliceMut::new(&mut third),
];
let read = reader.read_vectored(&mut bufs).unwrap();
assert_eq!(read, 5);
assert_eq!(second, *b"ab");
assert_eq!(third, *b"cde");
assert_eq!(reader.checksum(), checksum_sum(b"abcde"));
}
#[test]
fn digest_reader_vectored_skips_empty_buffers() {
let mut reader = SumDigest::reader(io::Cursor::new(b"abcde".to_vec()));
let mut first = [];
let mut second = [0u8; 2];
let mut third = [0u8; 3];
let mut bufs = [
IoSliceMut::new(&mut first),
IoSliceMut::new(&mut second),
IoSliceMut::new(&mut third),
];
let read = reader.read_vectored(&mut bufs).unwrap();
assert_eq!(read, 5);
assert_eq!(second, *b"ab");
assert_eq!(third, *b"cde");
assert_eq!(reader.digest(), digest_sum(b"abcde"));
}
#[test]
fn checksum_writer_vectored_skips_empty_buffers() {
let mut writer = ChecksumWriter::<_, Sum>::new(PartialWriter::new(5));
let bufs = [IoSlice::new(b""), IoSlice::new(b"ab"), IoSlice::new(b"cde")];
let written = writer.write_vectored(&bufs).unwrap();
assert_eq!(written, 5);
assert_eq!(writer.checksum(), checksum_sum(b"abcde"));
let (inner, checksum) = writer.into_parts();
assert_eq!(inner.inner, b"abcde");
assert_eq!(checksum, checksum_sum(b"abcde"));
}
#[test]
fn digest_writer_vectored_skips_empty_buffers() {
let mut writer = DigestWriter::<_, SumDigest>::new(PartialWriter::new(5));
let bufs = [IoSlice::new(b""), IoSlice::new(b"ab"), IoSlice::new(b"cde")];
let written = writer.write_vectored(&bufs).unwrap();
assert_eq!(written, 5);
assert_eq!(writer.digest(), digest_sum(b"abcde"));
let (inner, digest) = writer.into_parts();
assert_eq!(inner.inner, b"abcde");
assert_eq!(digest, digest_sum(b"abcde"));
}
}