use crate::hasher::{Digest, Size};
use crate::Error;
use core::convert::TryFrom;
#[cfg(feature = "std")]
use core::convert::TryInto;
use core::fmt::Debug;
use generic_array::{ArrayLength, GenericArray};
pub trait MultihashCode:
TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
{
type AllocSize: Size;
fn digest(&self, input: &[u8]) -> Multihash<Self::AllocSize>;
fn multihash_from_digest<'a, S, D>(digest: &'a D) -> Multihash<Self::AllocSize>
where
S: Size,
D: Digest<S>,
Self: From<&'a D>;
}
#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
#[cfg_attr(feature = "serde-codec", serde(bound = "S: Size"))]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Multihash<S: Size> {
code: u64,
size: u8,
digest: GenericArray<u8, S>,
}
impl<S: Size> Copy for Multihash<S> where <S as ArrayLength<u8>>::ArrayType: Copy {}
impl<S: Size> Multihash<S> {
pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
if input_digest.len() > S::to_usize() {
return Err(Error::InvalidSize(input_digest.len() as _));
}
let size = input_digest.len();
let mut digest = GenericArray::default();
digest[..size].copy_from_slice(input_digest);
Ok(Self {
code,
size: size as u8,
digest,
})
}
pub fn code(&self) -> u64 {
self.code
}
pub fn size(&self) -> u8 {
self.size
}
pub fn digest(&self) -> &[u8] {
&self.digest[..self.size as usize]
}
#[cfg(feature = "std")]
pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error>
where
Self: Sized,
{
let (code, size, digest) = read_multihash(r)?;
Ok(Self { code, size, digest })
}
#[cfg(feature = "std")]
pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
where
Self: Sized,
{
let result = Self::read(&mut bytes)?;
if !bytes.is_empty() {
return Err(Error::InvalidSize(bytes.len().try_into().expect(
"Currently the maximum size is 255, therefore always fits into usize",
)));
}
Ok(result)
}
#[cfg(feature = "std")]
pub fn write<W: std::io::Write>(&self, w: W) -> Result<(), Error> {
write_multihash(w, self.code(), self.size(), self.digest())
}
#[cfg(feature = "std")]
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![];
self.write(&mut bytes)
.expect("writing to a vec should never fail");
bytes
}
}
#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Encode for Multihash<crate::U32> {
fn encode_to<EncOut: parity_scale_codec::Output>(&self, dest: &mut EncOut) {
let mut digest = [0; 32];
digest.copy_from_slice(&self.digest);
dest.push(&self.code);
dest.push(&self.size);
dest.push(&digest);
}
}
#[cfg(feature = "scale-codec")]
impl parity_scale_codec::EncodeLike for Multihash<crate::U32> {}
#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Decode for Multihash<crate::U32> {
fn decode<DecIn: parity_scale_codec::Input>(
input: &mut DecIn,
) -> Result<Self, parity_scale_codec::Error> {
Ok(Multihash {
code: parity_scale_codec::Decode::decode(input)?,
size: parity_scale_codec::Decode::decode(input)?,
digest: {
let digest = <[u8; 32]>::decode(input)?;
GenericArray::clone_from_slice(&digest)
},
})
}
}
#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Encode for Multihash<crate::U64> {
fn encode_to<EncOut: parity_scale_codec::Output>(&self, dest: &mut EncOut) {
let mut digest = [0; 64];
digest.copy_from_slice(&self.digest);
dest.push(&self.code);
dest.push(&self.size);
dest.push(&digest);
}
}
#[cfg(feature = "scale-codec")]
impl parity_scale_codec::EncodeLike for Multihash<crate::U64> {}
#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Decode for Multihash<crate::U64> {
fn decode<DecIn: parity_scale_codec::Input>(
input: &mut DecIn,
) -> Result<Self, parity_scale_codec::Error> {
Ok(Multihash {
code: parity_scale_codec::Decode::decode(input)?,
size: parity_scale_codec::Decode::decode(input)?,
digest: {
let digest = <[u8; 64]>::decode(input)?;
GenericArray::clone_from_slice(&digest)
},
})
}
}
#[cfg(feature = "std")]
pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
where
W: std::io::Write,
{
use unsigned_varint::encode as varint_encode;
let mut code_buf = varint_encode::u64_buffer();
let code = varint_encode::u64(code, &mut code_buf);
let mut size_buf = varint_encode::u8_buffer();
let size = varint_encode::u8(size, &mut size_buf);
w.write_all(code)?;
w.write_all(size)?;
w.write_all(digest)?;
Ok(())
}
#[cfg(feature = "std")]
pub fn read_multihash<R, S>(mut r: R) -> Result<(u64, u8, GenericArray<u8, S>), Error>
where
R: std::io::Read,
S: Size,
{
use unsigned_varint::io::read_u64;
let code = read_u64(&mut r)?;
let size = read_u64(&mut r)?;
if size > S::to_u64() || size > u8::MAX as u64 {
return Err(Error::InvalidSize(size));
}
let mut digest = GenericArray::default();
r.read_exact(&mut digest[..size as usize])?;
Ok((code, size as u8, digest))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::multihash_impl::Code;
#[test]
fn roundtrip() {
let hash = Code::Sha2_256.digest(b"hello world");
let mut buf = [0u8; 35];
hash.write(&mut buf[..]).unwrap();
let hash2 = Multihash::read(&buf[..]).unwrap();
assert_eq!(hash, hash2);
}
#[test]
#[cfg(feature = "scale-codec")]
fn test_scale() {
use parity_scale_codec::{Decode, Encode};
let mh = Multihash::<crate::U32>::default();
let bytes = mh.encode();
let mh2: Multihash<crate::U32> = Decode::decode(&mut &bytes[..]).unwrap();
assert_eq!(mh, mh2);
}
#[test]
#[cfg(feature = "serde-codec")]
fn test_serde() {
let mh = Multihash::<crate::U32>::default();
let bytes = serde_json::to_string(&mh).unwrap();
let mh2 = serde_json::from_str(&bytes).unwrap();
assert_eq!(mh, mh2);
}
}