Skip to content

Instantly share code, notes, and snippets.

@Lucretiel
Last active October 1, 2020 04:29
Show Gist options
  • Save Lucretiel/1fe436b0f498acca6d84789c2e30cc44 to your computer and use it in GitHub Desktop.
Save Lucretiel/1fe436b0f498acca6d84789c2e30cc44 to your computer and use it in GitHub Desktop.
A wrapper async reader that hashes the content that's read through it
use std::{
hash::Hasher,
io,
pin::Pin,
task::{Context, Poll},
};
use futures::AsyncRead;
use pin_project::pin_project;
#[pin_project]
#[derive(Debug, Clone)]
pub struct AsyncReadHash<H, R> {
#[pin]
reader: R,
hasher: H,
}
impl<H: Hasher, R: AsyncRead> AsyncReadHash<H, R> {
pub fn with_hasher(reader: R, hasher: H) -> Self {
Self { reader, hasher }
}
}
impl<H: Hasher, R> AsyncReadHash<H, R> {
pub fn current_hash(&self) -> u64 {
self.hasher.finish()
}
}
impl<H: Hasher + Default, R: AsyncRead> AsyncReadHash<H, R> {
pub fn new(reader: R) -> Self {
Self::with_hasher(reader, H::default())
}
}
impl<H: Hasher, R: AsyncRead> AsyncRead for AsyncReadHash<H, R> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
let this = self.project();
// Note: instead of using pin_project, we could do it manually, like
// so:
//
// let reader = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().reader) };
// let hasher = unsafe { &mut self.get_unchecked_mut().hasher };
match this.reader.poll_read(cx, buf) {
Poll::Ready(Ok(amount_read)) => {
this.hasher.write(&buf[..amount_read]);
Poll::Ready(Ok(amount_read))
}
result => result,
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment