Merge rust-bitcoin/rust-bitcoin#3077: hashes: Add a new `hash_reader` function

0a045d87ea hashes: Add a new hash_reader function (Tobin C. Harding)

Pull request description:

  Add a function `hash_reader` that uses the `BufRead` trait to read bytes directly into the hash engine.

  Add the functionality to:

  - as a trait method in the `GeneralHash` trait with default implementation
  - as inherent functions to all the hash types

  Close: #3050

ACKs for top commit:
  Kixunil:
    ACK 0a045d87ea
  apoelstra:
    ACK 0a045d87ea successfully ran local tests

Tree-SHA512: 225f1d72f7a6119313d36422a3f7dc026ddcd27de9c8712c5734ea6056bb21e4857814761dbf2383a7a87fa82573ffc2097f67d08a0785a93e691c1745d0db8c
This commit is contained in:
merge-script 2024-08-06 15:26:45 +00:00
commit d214dc7b09
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
3 changed files with 67 additions and 1 deletions

View File

@ -209,6 +209,13 @@ macro_rules! hash_type {
Self::from_engine(engine)
}
/// Hashes the entire contents of the `reader`.
#[cfg(feature = "bitcoin-io")]
pub fn hash_reader<R: io::BufRead>(reader: &mut R) -> Result<Self, io::Error> {
<Self as crate::GeneralHash>::hash_reader(reader)
}
/// Returns the underlying byte array.
pub const fn to_byte_array(self) -> [u8; $bits / 8] { self.0 }

View File

@ -80,6 +80,9 @@ extern crate alloc;
#[cfg(any(test, feature = "std"))]
extern crate core;
#[cfg(feature = "bitcoin-io")]
extern crate bitcoin_io as io;
#[cfg(feature = "serde")]
/// A generic serialization/deserialization framework.
pub extern crate serde;
@ -232,6 +235,27 @@ pub trait GeneralHash: Hash {
}
Self::from_engine(engine)
}
/// Hashes the entire contents of the `reader`.
#[cfg(feature = "bitcoin-io")]
fn hash_reader<R: io::BufRead>(reader: &mut R) -> Result<Self, io::Error>
where
Self::Engine: Default,
{
let mut engine = Self::engine();
loop {
let bytes = reader.fill_buf()?;
let read = bytes.len();
if read == 0 { // Empty slice means EOF.
break
}
engine.input(bytes);
reader.consume(read);
}
Ok(Self::from_engine(engine))
}
}
/// Trait which applies to hashes of all types.
@ -298,7 +322,8 @@ impl std::error::Error for FromSliceError {}
#[cfg(test)]
mod tests {
use crate::sha256d;
use super::*;
use crate::{sha256, sha256d};
hash_newtype! {
/// A test newtype
@ -323,4 +348,13 @@ mod tests {
let rinsed = hex.parse::<TestNewtype>().expect("failed to parse hex");
assert_eq!(rinsed, orig)
}
#[test]
fn hash_reader() {
let mut reader: &[u8] = b"hello";
assert_eq!(
sha256::Hash::hash_reader(&mut reader).unwrap(),
sha256::Hash::hash(b"hello"),
)
}
}

View File

@ -96,6 +96,24 @@ where
Self::from_engine(engine)
}
/// Hashes the entire contents of the `reader`.
#[cfg(feature = "bitcoin-io")]
pub fn hash_reader<R: io::BufRead>(reader: &mut R) -> Result<Self, io::Error> {
let mut engine = Self::engine();
loop {
let bytes = reader.fill_buf()?;
let read = bytes.len();
if read == 0 { // Empty slice means EOF.
break
}
engine.input(bytes);
reader.consume(read);
}
Ok(Self::from_engine(engine))
}
/// Returns the underlying byte array.
pub const fn to_byte_array(self) -> [u8; 32] { self.0 }
@ -231,6 +249,13 @@ macro_rules! sha256t_hash_newtype {
}
Self::from_engine(engine)
}
/// Hashes the entire contents of the `reader`.
#[cfg(feature = "bitcoin-io")]
#[allow(unused)] // the user of macro may not need this
fn hash_reader<R: io::BufRead>(reader: &mut R) -> Result<Self, io::Error> {
<$hash_name as $crate::GeneralHash>::hash_reader(reader)
}
}
impl $crate::GeneralHash for $hash_name {