use crate::error::Error;
use crate::hasher::Digest;
use core::fmt::Debug;
pub trait MultihashDigest: Clone + Debug + Eq + Send + Sync + 'static {
fn new(code: u64, input: &[u8]) -> Result<Self, Error>;
fn wrap(code: u64, digest: &[u8]) -> Result<Self, Error>;
fn code(&self) -> u64;
fn size(&self) -> u8;
fn digest(&self) -> &[u8];
#[cfg(feature = "std")]
fn read<R: std::io::Read>(r: R) -> Result<Self, Error>
where
Self: Sized;
#[cfg(feature = "std")]
fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
where
Self: Sized,
{
Self::read(&mut bytes)
}
#[cfg(feature = "std")]
fn write<W: std::io::Write>(&self, w: W) -> Result<(), Error> {
write_multihash(w, self.code(), self.size(), self.digest())
}
#[cfg(feature = "std")]
fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![];
self.write(&mut bytes)
.expect("writing to a vec should never fail");
bytes
}
fn to_raw(&self) -> Result<RawMultihash, Error> {
RawMultihash::from_mh(self)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Decode))]
#[cfg_attr(feature = "scale-codec", derive(parity_scale_codec::Encode))]
pub struct RawMultihash {
code: u64,
size: u8,
digest: crate::UnknownDigest<crate::U64>,
}
impl RawMultihash {
pub fn wrap(code: u64, digest: &[u8]) -> Result<Self, Error> {
Ok(Self {
code,
size: digest.len() as _,
digest: Digest::extend(digest)?,
})
}
pub fn code(&self) -> u64 {
self.code
}
pub fn size(&self) -> u8 {
self.size
}
pub fn digest(&self) -> &[u8] {
&self.digest.as_ref()[..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,
{
Self::read(&mut bytes)
}
#[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
}
pub fn from_mh<MH: MultihashDigest>(mh: &MH) -> Result<Self, Error> {
let digest = Digest::extend(mh.digest())?;
Ok(Self {
code: mh.code(),
size: mh.size(),
digest,
})
}
pub fn to_mh<MH: MultihashDigest>(&self) -> Result<MH, Error> {
MH::wrap(self.code(), self.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_code<R>(mut r: R) -> Result<u64, Error>
where
R: std::io::Read,
{
use unsigned_varint::io::read_u64;
Ok(read_u64(&mut r)?)
}
#[cfg(feature = "std")]
pub fn read_digest<R, S, D>(mut r: R) -> Result<D, Error>
where
R: std::io::Read,
S: crate::hasher::Size,
D: crate::hasher::Digest<S>,
{
use generic_array::GenericArray;
use unsigned_varint::io::read_u64;
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(D::from(digest))
}
#[cfg(feature = "std")]
pub fn read_multihash<R, S, D>(mut r: R) -> Result<(u64, u8, D), Error>
where
R: std::io::Read,
S: crate::hasher::Size,
D: crate::hasher::Digest<S>,
{
use generic_array::GenericArray;
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, D::from(digest)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hasher::Hasher;
use crate::hasher_impl::strobe::Strobe256;
use crate::multihash_impl::Multihash;
#[test]
fn roundtrip() {
let digest = Strobe256::digest(b"hello world");
let hash = Multihash::from(digest);
let mut buf = [0u8; 35];
hash.write(&mut buf[..]).unwrap();
let hash2 = Multihash::read(&buf[..]).unwrap();
assert_eq!(hash, hash2);
}
}