Skip to content

Enable RISC-V build #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ categories = ["cryptography::cryptocurrencies"]

[dependencies]
lazy_static = { version = "1.1", optional = true }
cpufeatures = { version = "0.2", optional = true }
ring = "0.17"

[target.'cfg(target_arch = "x86_64")'.dependencies]
cpufeatures = "0.2"
sha2 = "0.10"

[dev-dependencies]
Expand All @@ -23,6 +25,5 @@ rustc-hex = "2"
wasm-bindgen-test = "0.3.33"

[features]
default = ["zero_hash_cache", "detect-cpufeatures"]
default = ["zero_hash_cache"]
zero_hash_cache = ["lazy_static"]
detect-cpufeatures = ["cpufeatures"]
52 changes: 19 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
//! Now this crate serves primarily as a wrapper over two SHA256 crates: `sha2` and `ring` – which
//! it switches between at runtime based on the availability of SHA intrinsics.

mod sha2_impl;

pub use self::DynamicContext as Context;
use sha2::Digest;

#[cfg(target_arch = "x86_64")]
use sha2_impl::Sha2CrateImpl;

#[cfg(feature = "zero_hash_cache")]
use lazy_static::lazy_static;
Expand Down Expand Up @@ -54,35 +58,6 @@ pub trait Sha256 {
fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN];
}

/// Implementation of SHA256 using the `sha2` crate (fastest on CPUs with SHA extensions).
struct Sha2CrateImpl;

impl Sha256Context for sha2::Sha256 {
fn new() -> Self {
sha2::Digest::new()
}

fn update(&mut self, bytes: &[u8]) {
sha2::Digest::update(self, bytes)
}

fn finalize(self) -> [u8; HASH_LEN] {
sha2::Digest::finalize(self).into()
}
}

impl Sha256 for Sha2CrateImpl {
type Context = sha2::Sha256;

fn hash(&self, input: &[u8]) -> Vec<u8> {
Self::Context::digest(input).into_iter().collect()
}

fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
Self::Context::digest(input).into()
}
}

/// Implementation of SHA256 using the `ring` crate (fastest on CPUs without SHA extensions).
pub struct RingImpl;

Expand Down Expand Up @@ -120,34 +95,39 @@ impl Sha256 for RingImpl {

/// Default dynamic implementation that switches between available implementations.
pub enum DynamicImpl {
#[cfg(target_arch = "x86_64")]
Sha2,
Ring,
}

// Runtime latch for detecting the availability of SHA extensions on x86_64.
//
// Inspired by the runtime switch within the `sha2` crate itself.
#[cfg(all(feature = "detect-cpufeatures", target_arch = "x86_64"))]
#[cfg(target_arch = "x86_64")]
cpufeatures::new!(x86_sha_extensions, "sha", "sse2", "ssse3", "sse4.1");

#[inline(always)]
pub fn have_sha_extensions() -> bool {
#[cfg(all(feature = "detect-cpufeatures", target_arch = "x86_64"))]
#[cfg(target_arch = "x86_64")]
return x86_sha_extensions::get();

#[cfg(not(all(feature = "detect-cpufeatures", target_arch = "x86_64")))]
#[cfg(not(target_arch = "x86_64"))]
return false;
}

impl DynamicImpl {
/// Choose the best available implementation based on the currently executing CPU.
#[inline(always)]
pub fn best() -> Self {
#[cfg(target_arch = "x86_64")]
if have_sha_extensions() {
Self::Sha2
} else {
Self::Ring
}

#[cfg(not(target_arch = "x86_64"))]
Self::Ring
}
}

Expand All @@ -157,6 +137,7 @@ impl Sha256 for DynamicImpl {
#[inline(always)]
fn hash(&self, input: &[u8]) -> Vec<u8> {
match self {
#[cfg(target_arch = "x86_64")]
Self::Sha2 => Sha2CrateImpl.hash(input),
Self::Ring => RingImpl.hash(input),
}
Expand All @@ -165,6 +146,7 @@ impl Sha256 for DynamicImpl {
#[inline(always)]
fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
match self {
#[cfg(target_arch = "x86_64")]
Self::Sha2 => Sha2CrateImpl.hash_fixed(input),
Self::Ring => RingImpl.hash_fixed(input),
}
Expand All @@ -175,27 +157,31 @@ impl Sha256 for DynamicImpl {
///
/// This enum ends up being 8 bytes larger than the largest inner context.
pub enum DynamicContext {
#[cfg(target_arch = "x86_64")]
Sha2(sha2::Sha256),
Ring(ring::digest::Context),
}

impl Sha256Context for DynamicContext {
fn new() -> Self {
match DynamicImpl::best() {
#[cfg(target_arch = "x86_64")]
DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()),
DynamicImpl::Ring => Self::Ring(Sha256Context::new()),
}
}

fn update(&mut self, bytes: &[u8]) {
match self {
#[cfg(target_arch = "x86_64")]
Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes),
Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes),
}
}

fn finalize(self) -> [u8; HASH_LEN] {
match self {
#[cfg(target_arch = "x86_64")]
Self::Sha2(ctxt) => Sha256Context::finalize(ctxt),
Self::Ring(ctxt) => Sha256Context::finalize(ctxt),
}
Expand Down
35 changes: 35 additions & 0 deletions src/sha2_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This implementation should only be compiled on x86_64 due to its dependency on the `sha2` and
// `cpufeatures` crates which do not compile on some architectures like RISC-V.
#![cfg(target_arch = "x86_64")]

use crate::{Sha256, Sha256Context, HASH_LEN};
use sha2::Digest;

/// Implementation of SHA256 using the `sha2` crate (fastest on x86_64 CPUs with SHA extensions).
pub struct Sha2CrateImpl;

impl Sha256Context for sha2::Sha256 {
fn new() -> Self {
sha2::Digest::new()
}

fn update(&mut self, bytes: &[u8]) {
sha2::Digest::update(self, bytes)
}

fn finalize(self) -> [u8; HASH_LEN] {
sha2::Digest::finalize(self).into()
}
}

impl Sha256 for Sha2CrateImpl {
type Context = sha2::Sha256;

fn hash(&self, input: &[u8]) -> Vec<u8> {
Self::Context::digest(input).into_iter().collect()
}

fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
Self::Context::digest(input).into()
}
}
Loading