Skip to content
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

feat: introduce Multihash::new which can be called from const contexts #331

Closed
wants to merge 8 commits into from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use multihash::Multihash;
const SHA2_256: u64 = 0x12;

fn main() {
let hash = Multihash::<64>::wrap(SHA2_256, b"my digest");
let hash = Multihash::<64>::new(SHA2_256, b"my digest");
println!("{:?}", hash);
}
```
Expand Down
2 changes: 1 addition & 1 deletion codetable/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ where
let mut expected_cursor = Cursor::new(&expected_bytes);
let multihash = code.digest(b"hello world");

assert_eq!(Multihash::wrap(code.into(), &digest).unwrap(), multihash);
assert_eq!(Multihash::new(code.into(), &digest).unwrap(), multihash);
assert_eq!(multihash.code(), u64::from(code));
assert_eq!(multihash.size() as usize, digest.len());
assert_eq!(multihash.digest(), digest);
Expand Down
2 changes: 1 addition & 1 deletion derive-impl/src/multihash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl Hash {
quote!(Self::#ident => {
let mut hasher = #hasher::default();
hasher.update(input);
Multihash::wrap(#code, hasher.finalize()).unwrap()
Multihash::new(#code, hasher.finalize()).unwrap()
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion derive/tests/multihash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn uses_correct_hasher() {
hasher.update(b"foobar");
let digest = hasher.finalize();

let multihash2 = Multihash::wrap(0x38b64f, digest).unwrap();
let multihash2 = Multihash::new(0x38b64f, digest).unwrap();

assert_eq!(multihash1, multihash2)
}
6 changes: 3 additions & 3 deletions examples/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Identity hashing means we don't actually perform any hashing.
//! Instead, we just store data directly in place of the "digest".
//!
//! [`Multihash::wrap`] returns an error in case the provided digest is too big for the available space.
//! [`Multihash::new`] returns an error in case the provided digest is too big for the available space.
//! Make sure you construct a [`Multihash`] with a large enough buffer for your data.
//!
//! Typically, the way you want to use the "identity" hash is:
Expand All @@ -17,8 +17,8 @@ use multihash::Multihash;
const IDENTITY_HASH_CODE: u64 = 0;

fn main() {
let identity_hash = Multihash::<64>::wrap(IDENTITY_HASH_CODE, b"foobar").unwrap();
let wrap_err = Multihash::<2>::wrap(IDENTITY_HASH_CODE, b"foobar").unwrap_err();
let identity_hash = Multihash::<64>::new(IDENTITY_HASH_CODE, b"foobar").unwrap();
let wrap_err = Multihash::<2>::new(IDENTITY_HASH_CODE, b"foobar").unwrap_err();

assert_eq!(identity_hash.digest(), b"foobar");
assert_eq!(wrap_err.to_string(), "Invalid multihash size 6.");
Expand Down
6 changes: 3 additions & 3 deletions src/arb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl<const S: usize> quickcheck::Arbitrary for Multihash<S> {
let size = rng.gen_range(0..S);
let mut data = [0; S];
rng.fill_bytes(&mut data);
Multihash::wrap(code, &data[..size]).unwrap()
Multihash::new(code, &data[..size]).unwrap()
}
}

Expand All @@ -58,7 +58,7 @@ impl<'a, const S: usize> arbitrary::Arbitrary<'a> for Multihash<S> {
let size = u.int_in_range(0..=S)?;
let data = u.bytes(size)?;

Ok(Multihash::wrap(code, data).unwrap())
Ok(Multihash::new(code, data).unwrap())
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
Expand All @@ -76,7 +76,7 @@ mod tests {
let mut u = Unstructured::new(&[2, 4, 13, 5, 6, 7, 8, 9, 6]);

let mh = <Multihash<16> as Arbitrary>::arbitrary(&mut u).unwrap();
let mh2 = Multihash::<16>::wrap(1037, &[6, 7, 8, 9, 6]).unwrap();
let mh2 = Multihash::<16>::new(1037, &[6, 7, 8, 9, 6]).unwrap();
assert_eq!(mh, mh2);
}
}
51 changes: 45 additions & 6 deletions src/multihash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::Error;
use alloc::vec::Vec;

use core::convert::TryInto;
use core::fmt::Debug;
use core::fmt::{self, Debug};

use unsigned_varint::encode as varint_encode;

Expand Down Expand Up @@ -54,12 +54,41 @@ impl<const S: usize> Default for Multihash<S> {
}
}

/// Returned from [`Multihash::new`]
#[derive(Debug)]
#[non_exhaustive]
pub enum CreationError {
/// The digest exceeds the capacity of the multihash
DigestTooBig,
}
aatifsyed marked this conversation as resolved.
Show resolved Hide resolved

impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CreationError::DigestTooBig => {
f.write_str("The digest exceeds the capacity of the multihash")
}
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for CreationError {}

impl<const S: usize> Multihash<S> {
/// Wraps the digest in a multihash.
pub const fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
/// Wrap the digest in a multihash
aatifsyed marked this conversation as resolved.
Show resolved Hide resolved
/// ```rust
/// # use multihash::Multihash;
/// const MULTIHASH: Multihash<64> = match Multihash::new(0, &[]) {
/// Ok(ok) => ok,
/// Err(_) => panic!(),
/// };
/// ```
aatifsyed marked this conversation as resolved.
Show resolved Hide resolved
pub const fn new(code: u64, input_digest: &[u8]) -> Result<Self, CreationError> {
if input_digest.len() > S {
return Err(Error::invalid_size(input_digest.len() as _));
return Err(CreationError::DigestTooBig);
}

let size = input_digest.len();
let mut digest = [0; S];
let mut i = 0;
Expand All @@ -74,6 +103,16 @@ impl<const S: usize> Multihash<S> {
})
}

/// Wraps the digest in a multihash.
#[deprecated = "use Multihash::new insteads"]
aatifsyed marked this conversation as resolved.
Show resolved Hide resolved
#[doc(hidden)]
pub const fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
match Self::new(code, input_digest) {
Ok(ok) => Ok(ok),
Err(CreationError::DigestTooBig) => Err(Error::invalid_size(input_digest.len() as _)),
}
}

/// Returns the code of the multihash.
pub const fn code(&self) -> u64 {
self.code
Expand Down Expand Up @@ -321,14 +360,14 @@ mod tests {
fn test_scale() {
use parity_scale_codec::{Decode, Encode};

let mh1 = Multihash::<32>::wrap(0, b"hello world").unwrap();
let mh1 = Multihash::<32>::new(0, b"hello world").unwrap();
// println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
let mh1_bytes = mh1.encode();
// println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
let mh2: Multihash<32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
assert_eq!(mh1, mh2);

let mh3 = Multihash::<64>::wrap(0, b"hello world").unwrap();
let mh3 = Multihash::<64>::new(0, b"hello world").unwrap();
// println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
let mh3_bytes = mh3.encode();
// println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
Expand Down
4 changes: 2 additions & 2 deletions src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ mod tests {
// This is a concatenation of `SHA2_256_CODE + DIGEST_LENGTH + DIGEST`.
let expected_json = format!("[{},{},159,228,204,198,222,22,114,79,58,48,199,232,242,84,243,198,71,25,134,172,177,248,216,207,142,150,206,42,215,219,231,251]", SHA2_256_CODE as u8, DIGEST.len() as u8);

let mh = Multihash::<32>::wrap(SHA2_256_CODE, &DIGEST).unwrap();
let mh = Multihash::<32>::new(SHA2_256_CODE, &DIGEST).unwrap();

let json = serde_json::to_string(&mh).unwrap();
assert_eq!(json, expected_json);
Expand Down Expand Up @@ -124,7 +124,7 @@ mod tests {
251,
];

let mh = Multihash::<32>::wrap(SHA2_256_CODE, &DIGEST).unwrap();
let mh = Multihash::<32>::new(SHA2_256_CODE, &DIGEST).unwrap();

// As bytes.
assert_tokens(&mh, &[Token::Bytes(&ENCODED_MULTIHASH_BYTES)]);
Expand Down