diff --git a/Cargo.toml b/Cargo.toml index 3f187ce2..dd1dc405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["derive", ".", "codetable"] +members = ["derive", "derive-impl", ".", "codetable"] [package] name = "multihash" @@ -38,3 +38,4 @@ hex = "0.4.2" serde_json = "1.0.58" quickcheck = "1.0.3" arbitrary = "1.1.0" +rand = { version = "0.8.5", features = ["small_rng"] } diff --git a/codetable/Cargo.toml b/codetable/Cargo.toml index 86107080..25e53018 100644 --- a/codetable/Cargo.toml +++ b/codetable/Cargo.toml @@ -16,7 +16,6 @@ sha-3 = { version = "0.10.0", default-features = false, package = "sha3" } strobe-rs = { version = "0.7.0", default-features = false } ripemd-rs = { package = "ripemd", version = "0.1.1"} multihash-derive = { version = "0.8.0", path = "../derive", default-features = false } -multihash = { version = "0.18.0", path = "../" } [dev-dependencies] hex = "0.4.2" diff --git a/codetable/benches/multihash.rs b/codetable/benches/multihash.rs index ee160d6c..5d964a66 100644 --- a/codetable/benches/multihash.rs +++ b/codetable/benches/multihash.rs @@ -1,10 +1,10 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use multihash::Hasher; use multihash_codetable::{ Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, Keccak224, Keccak256, Keccak384, Keccak512, Sha1, Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Strobe256, Strobe512, }; +use multihash_derive::Hasher; use rand::Rng; macro_rules! group_digest { diff --git a/codetable/examples/custom_table.rs b/codetable/examples/custom_table.rs index 99287d17..40235d0d 100644 --- a/codetable/examples/custom_table.rs +++ b/codetable/examples/custom_table.rs @@ -1,8 +1,7 @@ use std::convert::TryFrom; -use multihash::{Hasher, MultihashDigest as _}; use multihash_codetable::Sha2_256; -use multihash_derive::MultihashDigest; +use multihash_derive::{Hasher, MultihashDigest}; // You can implement a custom hasher. This is a SHA2 256-bit hasher that returns a hash that is // truncated to 160 bits. diff --git a/codetable/examples/manual_mh.rs b/codetable/examples/manual_mh.rs index 11f35f52..c3905591 100644 --- a/codetable/examples/manual_mh.rs +++ b/codetable/examples/manual_mh.rs @@ -1,5 +1,5 @@ -use multihash::MultihashDigest; use multihash_codetable::Code; +use multihash_derive::MultihashDigest; /// prefix/multihash generating tool to aid when adding new tests fn prefix_util() { diff --git a/codetable/src/hasher_impl.rs b/codetable/src/hasher_impl.rs index 8fd204bd..5c67523a 100644 --- a/codetable/src/hasher_impl.rs +++ b/codetable/src/hasher_impl.rs @@ -1,4 +1,4 @@ -use multihash::Hasher; +use multihash_derive::Hasher; use std::io; @@ -153,7 +153,7 @@ macro_rules! derive_rustcrypto_hasher { } } - impl ::multihash::Hasher for $name { + impl ::multihash_derive::Hasher for $name { fn update(&mut self, input: &[u8]) { use digest::Digest; self.state.update(input) diff --git a/codetable/src/lib.rs b/codetable/src/lib.rs index 24abbcd2..d9329350 100644 --- a/codetable/src/lib.rs +++ b/codetable/src/lib.rs @@ -88,8 +88,8 @@ pub enum Code { mod tests { use super::*; use crate::hasher_impl::sha3::{Sha3_256, Sha3_512}; - use multihash::MultihashDigest; - use multihash::{Hasher, Multihash}; + use multihash_derive::MultihashDigest; + use multihash_derive::{Hasher, Multihash}; #[test] fn test_hasher_256() { diff --git a/codetable/tests/lib.rs b/codetable/tests/lib.rs index 7e388573..8e3f1e5f 100644 --- a/codetable/tests/lib.rs +++ b/codetable/tests/lib.rs @@ -1,11 +1,11 @@ use std::io::{Cursor, Write}; -use multihash::{Hasher, MultihashDigest}; use multihash_codetable::{ Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, Identity256, Keccak224, Keccak256, Keccak384, Keccak512, Sha1, Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Strobe256, Strobe512, }; +use multihash_derive::{Hasher, MultihashDigest}; #[derive(Clone, Copy, Debug, Eq, multihash_derive::MultihashDigest, PartialEq)] #[mh(alloc_size = 64)] @@ -386,8 +386,8 @@ fn multihash_errors() { #[test] fn blak3_non_default_digest() { - use multihash::MultihashDigest; use multihash_codetable::Blake3Hasher; + use multihash_derive::MultihashDigest; const DIGEST_SIZE: usize = 16; pub struct ContentHasher(Blake3Hasher); diff --git a/derive-impl/Cargo.toml b/derive-impl/Cargo.toml new file mode 100644 index 00000000..da935b8b --- /dev/null +++ b/derive-impl/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "multihash-derive-impl" +version = "0.1.0" +authors = ["David Craven "] +edition = "2018" +description = "Proc macro for deriving custom multihash tables." +license = "MIT" +repository = "https://github.com/multiformats/multihash" +resolver = "2" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { version = "1.0.24", features = ["span-locations"] } +proc-macro-crate = "~1.1.0" +proc-macro-error = "1.0.4" +quote = "1.0.7" +syn = "1.0.42" +synstructure = "0.12.4" + +[features] +default = ["std"] +std = [] + +[dev-dependencies] +pretty_assertions = "1.0.0" +#multihash = { path = "..", default-features = false, features = ["derive", "sha2"] } diff --git a/derive-impl/src/lib.rs b/derive-impl/src/lib.rs new file mode 100644 index 00000000..4c990ee3 --- /dev/null +++ b/derive-impl/src/lib.rs @@ -0,0 +1,66 @@ +//! This proc macro derives a custom Multihash code table from a list of hashers. It also +//! generates a public type called `Multihash` which corresponds to the specified `alloc_size`. +//! +//! The digests are stack allocated with a fixed size. That size needs to be big enough to hold any +//! of the specified hash digests. This cannot be determined reliably on compile-time, hence it +//! needs to set manually via the `alloc_size` attribute. Also you might want to set it to bigger +//! sizes then necessarily needed for backwards/forward compatibility. +//! +//! If you set `#mh(alloc_size = …)` to a too low value, you will get compiler errors. Please note +//! the the sizes are checked only on a syntactic level and *not* on the type level. This means +//! that digest need to have a size const generic, which is a valid `usize`, for example `32` or +//! `64`. +//! +//! You can disable those compiler errors with setting the `no_alloc_size_errors` attribute. This +//! can be useful if you e.g. have specified type aliases for your hash digests and you are sure +//! you use the correct value for `alloc_size`. +//! +//! # Example +//! +//! ``` +//! use multihash::derive::Multihash; +//! use multihash::MultihashDigest; +//! +//! #[derive(Clone, Copy, Debug, Eq, Multihash, PartialEq)] +//! #[mh(alloc_size = 64)] +//! pub enum Code { +//! #[mh(code = 0x01, hasher = multihash::Sha2_256)] +//! Foo, +//! #[mh(code = 0x02, hasher = multihash::Sha2_512)] +//! Bar, +//! } +//! +//! let hash = Code::Foo.digest(b"hello world!"); +//! println!("{:02x?}", hash); +//! ``` +extern crate proc_macro; + +mod multihash; +mod utils; + +use proc_macro::TokenStream; +use proc_macro_error::proc_macro_error; +use synstructure::macros::{parse, DeriveInput}; +use synstructure::{MacroResult, Structure}; + +#[proc_macro_derive(Multihash, attributes(mh))] +#[allow(non_snake_case)] +#[proc_macro_error] +#[deprecated(since = "0.8.1", note = "Use `MultihashDigest` derive instead.")] +pub fn Multihash(i: TokenStream) -> TokenStream { + match parse::(i) { + Ok(p) => match Structure::try_new(&p) { + Ok(s) => multihash::multihash(s).into_stream(), + Err(e) => e.to_compile_error().into(), + }, + Err(e) => e.to_compile_error().into(), + } +} + +#[proc_macro_derive(MultihashDigest, attributes(mh))] +#[allow(non_snake_case)] +#[proc_macro_error] +pub fn MultihashDigest(i: TokenStream) -> TokenStream { + #[allow(deprecated)] + Multihash(i) +} diff --git a/derive/src/multihash.rs b/derive-impl/src/multihash.rs similarity index 98% rename from derive/src/multihash.rs rename to derive-impl/src/multihash.rs index b964e633..237c57d0 100644 --- a/derive/src/multihash.rs +++ b/derive-impl/src/multihash.rs @@ -198,7 +198,7 @@ fn error_code_duplicates(hashes: &[Hash]) { struct ParseError(Span); pub fn multihash(s: Structure) -> TokenStream { - let mh_crate = match utils::use_crate("multihash") { + let mh_crate = match utils::use_crate("multihash-derive") { Ok(ident) => ident, Err(e) => { let err = syn::Error::new(Span::call_site(), e).to_compile_error(); @@ -247,12 +247,12 @@ pub fn multihash(s: Structure) -> TokenStream { } impl core::convert::TryFrom for #code_enum { - type Error = #mh_crate::Error; + type Error = #mh_crate::UnsupportedCode; fn try_from(code: u64) -> Result { match code { #(#code_from_u64,)* - _ => Err(#mh_crate::Error::unsupported_code(code)) + _ => Err(#mh_crate::UnsupportedCode(code)) } } } diff --git a/derive/src/utils.rs b/derive-impl/src/utils.rs similarity index 100% rename from derive/src/utils.rs rename to derive-impl/src/utils.rs diff --git a/derive/Cargo.toml b/derive/Cargo.toml index b59e8ba7..d2172fa2 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -8,21 +8,6 @@ license = "MIT" repository = "https://github.com/multiformats/multihash" resolver = "2" -[lib] -proc-macro = true - [dependencies] -proc-macro2 = { version = "1.0.24", features = ["span-locations"] } -proc-macro-crate = "~1.1.0" -proc-macro-error = "1.0.4" -quote = "1.0.7" -syn = "1.0.42" -synstructure = "0.12.4" - -[features] -default = ["std"] -std = [] - -[dev-dependencies] -pretty_assertions = "1.0.0" -#multihash = { path = "..", default-features = false, features = ["derive", "sha2"] } +multihash-derive-impl = { version = "0.1.0", path = "../derive-impl" } +multihash = { version = "0.18.0", path = "../" } diff --git a/src/hasher.rs b/derive/src/hasher.rs similarity index 100% rename from src/hasher.rs rename to derive/src/hasher.rs diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 4c990ee3..74c7560e 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -33,34 +33,69 @@ //! let hash = Code::Foo.digest(b"hello world!"); //! println!("{:02x?}", hash); //! ``` -extern crate proc_macro; -mod multihash; -mod utils; +mod hasher; -use proc_macro::TokenStream; -use proc_macro_error::proc_macro_error; -use synstructure::macros::{parse, DeriveInput}; -use synstructure::{MacroResult, Structure}; +use std::convert::TryFrom; +use std::fmt; -#[proc_macro_derive(Multihash, attributes(mh))] -#[allow(non_snake_case)] -#[proc_macro_error] -#[deprecated(since = "0.8.1", note = "Use `MultihashDigest` derive instead.")] -pub fn Multihash(i: TokenStream) -> TokenStream { - match parse::(i) { - Ok(p) => match Structure::try_new(&p) { - Ok(s) => multihash::multihash(s).into_stream(), - Err(e) => e.to_compile_error().into(), - }, - Err(e) => e.to_compile_error().into(), +pub use hasher::Hasher; +pub use multihash_derive_impl::MultihashDigest; + +pub use multihash::*; + +/// The given code is not supported by this codetable. +#[derive(Debug)] +pub struct UnsupportedCode(pub u64); + +impl fmt::Display for UnsupportedCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "the code {} is not supported by this codetable", self.0) } } -#[proc_macro_derive(MultihashDigest, attributes(mh))] -#[allow(non_snake_case)] -#[proc_macro_error] -pub fn MultihashDigest(i: TokenStream) -> TokenStream { - #[allow(deprecated)] - Multihash(i) +impl std::error::Error for UnsupportedCode {} + +/// Trait that implements hashing. +/// +/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive]. +/// +/// [`Multihash` derive]: crate::derive +pub trait MultihashDigest: + TryFrom + + Into + + Send + + Sync + + Unpin + + Copy + + Eq + + fmt::Debug + + 'static +{ + /// Calculate the hash of some input data. + /// + /// # Example + /// + /// ``` + /// // `Code` implements `MultihashDigest` + /// use multihash::{Code, MultihashDigest}; + /// + /// let hash = Code::Sha3_256.digest(b"Hello world!"); + /// println!("{:02x?}", hash); + /// ``` + fn digest(&self, input: &[u8]) -> Multihash; + + /// Create a multihash from an existing multihash digest. + /// + /// # Example + /// + /// ``` + /// use multihash::{Code, Hasher, MultihashDigest, Sha3_256}; + /// + /// let mut hasher = Sha3_256::default(); + /// hasher.update(b"Hello world!"); + /// let hash = Code::Sha3_256.wrap(&hasher.finalize()).unwrap(); + /// println!("{:02x?}", hash); + /// ``` + fn wrap(&self, digest: &[u8]) -> std::result::Result, multihash::Error>; } diff --git a/src/error.rs b/src/error.rs index 815ac775..f8df7bc6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,14 +12,6 @@ pub struct Error { } impl Error { - /// The specified code is not supported by code table. - /// FIXME: This should not be in our public API because it is only needed by the custom derive which we have no knowledge of in this crate. - pub fn unsupported_code(code: u64) -> Self { - Self { - kind: Kind::UnsupportedCode(code), - } - } - pub(crate) const fn invalid_size(size: u64) -> Self { Self { kind: Kind::InvalidSize(size), @@ -51,8 +43,6 @@ impl core::fmt::Display for Error { enum Kind { /// Io error. Io(io::Error), - /// Unsupported multihash code. - UnsupportedCode(u64), /// Invalid multihash size. InvalidSize(u64), /// Invalid varint. @@ -80,7 +70,6 @@ impl core::fmt::Display for Kind { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { Self::Io(err) => write!(f, "{err}"), - Self::UnsupportedCode(code) => write!(f, "Unsupported multihash code {code}."), Self::InvalidSize(size) => write!(f, "Invalid multihash size {size}."), Self::Varint(err) => write!(f, "{err}"), } @@ -91,7 +80,6 @@ impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match &self.kind { Kind::Io(inner) => Some(inner), - Kind::UnsupportedCode(_) => None, Kind::InvalidSize(_) => None, Kind::Varint(_) => None, // FIXME: Does not implement `core2::Error`. } diff --git a/src/lib.rs b/src/lib.rs index 42314b32..7ebe7dfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,7 +59,6 @@ extern crate core; #[cfg(any(test, feature = "arb"))] mod arb; mod error; -mod hasher; mod multihash; /// Multihash result. @@ -67,10 +66,7 @@ mod multihash; pub type Result = core::result::Result; pub use crate::error::Error; -pub use crate::multihash::{Multihash, MultihashDigest}; -pub use hasher::Hasher; -#[cfg(feature = "derive")] -pub use multihash_derive as derive; +pub use crate::multihash::Multihash; /// Deprecated type-alias for the [`Multihash`] type. #[deprecated(since = "0.18.0", note = "Use `multihash::Multihash instead.")] diff --git a/src/multihash.rs b/src/multihash.rs index 86491784..d90d1bdd 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -1,7 +1,6 @@ use crate::Error; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::convert::TryFrom; use core::convert::TryInto; use core::fmt::Debug; @@ -14,42 +13,6 @@ use std::io; #[cfg(not(feature = "std"))] use core2::io; -/// Trait that implements hashing. -/// -/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive]. -/// -/// [`Multihash` derive]: crate::derive -pub trait MultihashDigest: - TryFrom + Into + Send + Sync + Unpin + Copy + Eq + Debug + 'static -{ - /// Calculate the hash of some input data. - /// - /// # Example - /// - /// ``` - /// // `Code` implements `MultihashDigest` - /// use multihash::{Code, MultihashDigest}; - /// - /// let hash = Code::Sha3_256.digest(b"Hello world!"); - /// println!("{:02x?}", hash); - /// ``` - fn digest(&self, input: &[u8]) -> Multihash; - - /// Create a multihash from an existing multihash digest. - /// - /// # Example - /// - /// ``` - /// use multihash::{Code, Hasher, MultihashDigest, Sha3_256}; - /// - /// let mut hasher = Sha3_256::default(); - /// hasher.update(b"Hello world!"); - /// let hash = Code::Sha3_256.wrap(&hasher.finalize()).unwrap(); - /// println!("{:02x?}", hash); - /// ``` - fn wrap(&self, digest: &[u8]) -> Result, Error>; -} - /// A Multihash instance that only supports the basic functionality and no hashing. /// /// With this Multihash implementation you can operate on Multihashes in a generic way, but