Skip to content

Commit

Permalink
Merge pull request #14 from baoyachi/feature/native_sha256
Browse files Browse the repository at this point in the history
Refactor code
  • Loading branch information
baoyachi authored Jul 15, 2023
2 parents f046475 + 198c3dc commit d8b1d88
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 18 deletions.
35 changes: 33 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ jobs:
os:
- ubuntu-latest
- macos-latest
- windows-latest
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
Expand All @@ -37,7 +36,7 @@ jobs:
- name: Build Release
run: cargo build --release
- name: Run tests
run: cargo test --all
run: cargo test --all-features --all

# build on nightly
- uses: actions-rs/toolchain@v1
Expand All @@ -46,3 +45,35 @@ jobs:
override: true
- name: Build on nightly
run: cargo build --release
windows-vcpkg:
name: windows-vcpkg
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: sfackler/actions/rustup@master
- run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
id: rust-version
- run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
- run: vcpkg install openssl:x64-windows-static-md
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
components: clippy, rustfmt
- name: Check format
run: cargo fmt --all -- --check
- name: Check fix
run: cargo fix && cargo fix
- name: Check with clippy
run: cargo clippy --all -- -D warnings
- name: Build Release
run: cargo build --release
- name: Run tests
run: cargo test --all-features --all
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Build on nightly
run: cargo build --release

13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sha256"
version = "1.1.5"
version = "1.2.0"
authors = ["baoyachi <[email protected]>"]
edition = "2018"
description = "sha256 crypto digest"
Expand All @@ -15,3 +15,14 @@ license = "MIT OR Apache-2.0"
[dependencies]
hex = "0.4.2"
sha2 = { version = "0.10.6", default-features = false }
openssl = { version = "0.10.54", optional = true, default-features = false }
async-trait = "0.1.68"
tokio = { version = "1.28.2", features = ["io-util"] }
bytes = "1.4.0"

[features]
native_openssl = ["openssl"]

[dev-dependencies]
tokio = { version = "1.28.2", features = ["full"] }
tokio-test = "0.4.2"
265 changes: 250 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@
//!
//! ```
#[cfg(feature = "native_openssl")]
use crate::openssl_sha256::OpenSslSha256;
use bytes::BytesMut;
use sha2::digest::Output;
use sha2::{Digest, Sha256};
use std::fmt::Debug;
use std::fs;
use std::io;
use std::io::{BufReader, Read};
use std::path::Path;
use tokio::io::AsyncReadExt;

/// sha256 digest string
///
Expand Down Expand Up @@ -60,10 +65,50 @@ pub fn digest<D: Sha256Digest>(input: D) -> String {
/// let val = try_digest(input).unwrap();
/// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
/// ```
#[deprecated(
since = "1.2.0",
note = "Use new function `try_async_digest()` instead"
)]
pub fn try_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
input.digest()
}

/// sha256 digest file
///
/// # Examples
///
/// ```rust
/// use sha256::{try_async_digest};
/// use std::path::Path;
/// let input = Path::new("./foo.file");
/// tokio_test::block_on(async{
/// let val = try_async_digest(input).await.unwrap();
/// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
/// });
///
/// ```
pub async fn try_async_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
input.async_digest().await
}

/// sha256 digest file
///
/// # Examples
///
/// ```rust
/// use sha256::{try_async_openssl_digest};
/// use std::path::Path;
/// let input = Path::new("./foo.file");
/// tokio_test::block_on(async{
/// let val = try_async_openssl_digest(input).await.unwrap();
/// assert_eq!(val,"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1")
/// });
/// ```
#[cfg(feature = "native_openssl")]
pub async fn try_async_openssl_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
input.async_openssl_digest().await
}

/// sha256 digest bytes
///
/// # Examples
Expand Down Expand Up @@ -101,9 +146,16 @@ pub trait Sha256Digest {
fn digest(self) -> String;
}

#[async_trait::async_trait]
pub trait TrySha256Digest {
type Error: Debug;

fn digest(self) -> Result<String, Self::Error>;

async fn async_digest(self) -> Result<String, Self::Error>;

#[cfg(feature = "native_openssl")]
async fn async_openssl_digest(self) -> Result<String, Self::Error>;
}

impl<const N: usize> Sha256Digest for &[u8; N] {
Expand Down Expand Up @@ -148,27 +200,210 @@ impl Sha256Digest for &String {
}
}

impl TrySha256Digest for &Path {
#[async_trait::async_trait]
impl<P> TrySha256Digest for P
where
P: AsRef<Path> + Send,
{
type Error = io::Error;

fn digest(self) -> Result<String, Self::Error> {
let mut sha = Sha256::new();
let f = fs::File::open(self)?;
let mut reader = BufReader::new(f);
// let bytes = fs::read(self)?;
let mut buf = [0u8; 1024];
loop {
let len = reader.read(&mut buf)?;
if len == 0 {
break;
}
sha.update(&buf[0..len]);
}
let hash = sha.finalize();
let reader = BufReader::new(f);
let sha = Sha256::new();
calc(reader, sha)
}

async fn async_digest(self) -> Result<String, Self::Error> {
let f = tokio::fs::File::open(self).await?;
let reader = tokio::io::BufReader::new(f);
let sha = Sha256::new();
async_calc(reader, sha).await
}

Ok(hex::encode(hash))
#[cfg(feature = "native_openssl")]
async fn async_openssl_digest(self) -> Result<String, Self::Error> {
let f = tokio::fs::File::open(self).await?;
let reader = tokio::io::BufReader::new(f);
let sha = OpenSslSha256::new();
async_calc(reader, sha).await
}
}

fn __digest__(data: &[u8]) -> String {
hex::encode(sha2::Sha256::digest(data))
hex::encode(Sha256::digest(data))
}

trait CalculatorInput {
fn read_inner(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
}

#[async_trait::async_trait]
trait AsyncCalculatorInput {
async fn read_inner(&mut self, buf: &mut BytesMut) -> std::io::Result<usize>;
}

impl<T> CalculatorInput for T
where
T: Read,
{
fn read_inner(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read(buf)
}
}

#[async_trait::async_trait]
impl<R> AsyncCalculatorInput for tokio::io::BufReader<R>
where
R: tokio::io::AsyncRead + Unpin + Send,
{
async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result<usize> {
self.read_buf(buf).await
}
}

trait CalculatorSelector {
type FinishType: AsRef<[u8]>;
fn update_inner(&mut self, data: &[u8]);
fn finish_inner(self) -> Self::FinishType;
}

impl CalculatorSelector for Sha256 {
type FinishType = Output<Sha256>;

fn update_inner(&mut self, data: &[u8]) {
self.update(data)
}

fn finish_inner(self) -> Self::FinishType {
self.finalize()
}
}

fn calc<I, S>(mut input: I, mut selector: S) -> io::Result<String>
where
I: CalculatorInput,
S: CalculatorSelector,
{
let mut buf = [0u8; 1024];
loop {
let len = input.read_inner(&mut buf)?;
if len == 0 {
break;
}
selector.update_inner(&buf[0..len]);
}
let hash = selector.finish_inner();
Ok(hex::encode(hash))
}

async fn async_calc<I, S>(mut input: I, mut selector: S) -> io::Result<String>
where
I: AsyncCalculatorInput,
S: CalculatorSelector,
{
let mut buf = BytesMut::with_capacity(1024);
loop {
let len = input.read_inner(&mut buf).await?;
if len == 0 {
break;
}
selector.update_inner(&buf[0..len]);
}
let hash = selector.finish_inner();
Ok(hex::encode(hash))
}

#[cfg(feature = "native_openssl")]
mod openssl_sha256 {
use crate::CalculatorSelector;

pub type OpenSslSha256 = openssl::sha::Sha256;

impl CalculatorSelector for OpenSslSha256 {
type FinishType = [u8; 32];

fn update_inner(&mut self, data: &[u8]) {
self.update(data)
}

fn finish_inner(self) -> Self::FinishType {
self.finish()
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[cfg(feature = "native_openssl")]
#[test]
fn test_openssl() {
let input = Path::new("./foo.file");
let f = fs::File::open(input).unwrap();
let reader = BufReader::new(f);
let sha = openssl::sha::Sha256::new();
assert_eq!(
"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
calc(reader, sha).unwrap()
);
}

#[test]
fn test_sha256() {
let input = Path::new("./foo.file");
let f = fs::File::open(input).unwrap();
let reader = BufReader::new(f);
let sha = Sha256::new();
assert_eq!(
"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
calc(reader, sha).unwrap()
);
}

#[cfg(feature = "native_openssl")]
#[tokio::test]
async fn test_async_openssl() {
let input = Path::new("./foo.file");
let f = tokio::fs::File::open(input).await.unwrap();
let reader = tokio::io::BufReader::new(f);
let sha = openssl::sha::Sha256::new();
assert_eq!(
"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
async_calc(reader, sha).await.unwrap()
);
}

#[cfg(feature = "native_openssl")]
#[tokio::test]
async fn test_async() {
let input = Path::new("./foo.file");
let f = tokio::fs::File::open(input).await.unwrap();
let reader = tokio::io::BufReader::new(f);
let sha = Sha256::new();
assert_eq!(
"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
async_calc(reader, sha).await.unwrap()
);
}

#[cfg(feature = "native_openssl")]
#[tokio::test]
async fn test_try_async_openssl_digest() {
let hash = try_async_openssl_digest("./foo.file").await.unwrap();
assert_eq!(
"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
hash
);
}

#[tokio::test]
async fn test_try_async_digest() {
let hash = try_async_digest("./foo.file").await.unwrap();
assert_eq!(
"433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
hash
);
}
}

0 comments on commit d8b1d88

Please sign in to comment.