tiny_multihash/
multihash.rs1use crate::hasher::{Digest, Size};
2use crate::Error;
3use core::convert::TryFrom;
4#[cfg(feature = "std")]
5use core::convert::TryInto;
6use core::fmt::Debug;
7use generic_array::{ArrayLength, GenericArray};
8
9pub trait MultihashCode:
15 TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
16{
17 type AllocSize: Size;
19
20 fn digest(&self, input: &[u8]) -> Multihash<Self::AllocSize>;
32
33 fn multihash_from_digest<'a, S, D>(digest: &'a D) -> Multihash<Self::AllocSize>
46 where
47 S: Size,
48 D: Digest<S>,
49 Self: From<&'a D>;
50}
51
52#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
74#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
75#[cfg_attr(feature = "serde-codec", serde(bound = "S: Size"))]
76#[derive(Clone, Debug, Default, Eq, PartialEq)]
77pub struct Multihash<S: Size> {
78 code: u64,
80 size: u8,
82 digest: GenericArray<u8, S>,
84}
85
86impl<S: Size> Copy for Multihash<S> where <S as ArrayLength<u8>>::ArrayType: Copy {}
87
88impl<S: Size> Multihash<S> {
89 pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
91 if input_digest.len() > S::to_usize() {
92 return Err(Error::InvalidSize(input_digest.len() as _));
93 }
94 let size = input_digest.len();
95 let mut digest = GenericArray::default();
96 digest[..size].copy_from_slice(input_digest);
97 Ok(Self {
98 code,
99 size: size as u8,
100 digest,
101 })
102 }
103
104 pub fn code(&self) -> u64 {
106 self.code
107 }
108
109 pub fn size(&self) -> u8 {
111 self.size
112 }
113
114 pub fn digest(&self) -> &[u8] {
116 &self.digest[..self.size as usize]
117 }
118
119 #[cfg(feature = "std")]
121 pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error>
122 where
123 Self: Sized,
124 {
125 let (code, size, digest) = read_multihash(r)?;
126 Ok(Self { code, size, digest })
127 }
128
129 #[cfg(feature = "std")]
134 pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
135 where
136 Self: Sized,
137 {
138 let result = Self::read(&mut bytes)?;
139 if !bytes.is_empty() {
141 return Err(Error::InvalidSize(bytes.len().try_into().expect(
142 "Currently the maximum size is 255, therefore always fits into usize",
143 )));
144 }
145
146 Ok(result)
147 }
148
149 #[cfg(feature = "std")]
151 pub fn write<W: std::io::Write>(&self, w: W) -> Result<(), Error> {
152 write_multihash(w, self.code(), self.size(), self.digest())
153 }
154
155 #[cfg(feature = "std")]
157 pub fn to_bytes(&self) -> Vec<u8> {
158 let mut bytes = vec![];
159 self.write(&mut bytes)
160 .expect("writing to a vec should never fail");
161 bytes
162 }
163}
164
165#[cfg(feature = "scale-codec")]
166impl parity_scale_codec::Encode for Multihash<crate::U32> {
167 fn encode_to<EncOut: parity_scale_codec::Output>(&self, dest: &mut EncOut) {
168 let mut digest = [0; 32];
169 digest.copy_from_slice(&self.digest);
170 dest.push(&self.code);
171 dest.push(&self.size);
172 dest.push(&digest);
173 }
174}
175
176#[cfg(feature = "scale-codec")]
177impl parity_scale_codec::EncodeLike for Multihash<crate::U32> {}
178
179#[cfg(feature = "scale-codec")]
180impl parity_scale_codec::Decode for Multihash<crate::U32> {
181 fn decode<DecIn: parity_scale_codec::Input>(
182 input: &mut DecIn,
183 ) -> Result<Self, parity_scale_codec::Error> {
184 Ok(Multihash {
185 code: parity_scale_codec::Decode::decode(input)?,
186 size: parity_scale_codec::Decode::decode(input)?,
187 digest: {
188 let digest = <[u8; 32]>::decode(input)?;
189 GenericArray::clone_from_slice(&digest)
190 },
191 })
192 }
193}
194
195#[cfg(feature = "scale-codec")]
196impl parity_scale_codec::Encode for Multihash<crate::U64> {
197 fn encode_to<EncOut: parity_scale_codec::Output>(&self, dest: &mut EncOut) {
198 let mut digest = [0; 64];
199 digest.copy_from_slice(&self.digest);
200 dest.push(&self.code);
201 dest.push(&self.size);
202 dest.push(&digest);
203 }
204}
205
206#[cfg(feature = "scale-codec")]
207impl parity_scale_codec::EncodeLike for Multihash<crate::U64> {}
208
209#[cfg(feature = "scale-codec")]
210impl parity_scale_codec::Decode for Multihash<crate::U64> {
211 fn decode<DecIn: parity_scale_codec::Input>(
212 input: &mut DecIn,
213 ) -> Result<Self, parity_scale_codec::Error> {
214 Ok(Multihash {
215 code: parity_scale_codec::Decode::decode(input)?,
216 size: parity_scale_codec::Decode::decode(input)?,
217 digest: {
218 let digest = <[u8; 64]>::decode(input)?;
219 GenericArray::clone_from_slice(&digest)
220 },
221 })
222 }
223}
224
225#[cfg(feature = "std")]
227pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
228where
229 W: std::io::Write,
230{
231 use unsigned_varint::encode as varint_encode;
232
233 let mut code_buf = varint_encode::u64_buffer();
234 let code = varint_encode::u64(code, &mut code_buf);
235
236 let mut size_buf = varint_encode::u8_buffer();
237 let size = varint_encode::u8(size, &mut size_buf);
238
239 w.write_all(code)?;
240 w.write_all(size)?;
241 w.write_all(digest)?;
242 Ok(())
243}
244
245#[cfg(feature = "std")]
252pub fn read_multihash<R, S>(mut r: R) -> Result<(u64, u8, GenericArray<u8, S>), Error>
253where
254 R: std::io::Read,
255 S: Size,
256{
257 use unsigned_varint::io::read_u64;
258
259 let code = read_u64(&mut r)?;
260 let size = read_u64(&mut r)?;
261
262 if size > S::to_u64() || size > u8::MAX as u64 {
263 return Err(Error::InvalidSize(size));
264 }
265
266 let mut digest = GenericArray::default();
267 r.read_exact(&mut digest[..size as usize])?;
268 Ok((code, size as u8, digest))
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274 use crate::multihash_impl::Code;
275
276 #[test]
277 fn roundtrip() {
278 let hash = Code::Sha2_256.digest(b"hello world");
279 let mut buf = [0u8; 35];
280 hash.write(&mut buf[..]).unwrap();
281 let hash2 = Multihash::read(&buf[..]).unwrap();
282 assert_eq!(hash, hash2);
283 }
284
285 #[test]
286 #[cfg(feature = "scale-codec")]
287 fn test_scale() {
288 use parity_scale_codec::{Decode, Encode};
289
290 let mh = Multihash::<crate::U32>::default();
291 let bytes = mh.encode();
292 let mh2: Multihash<crate::U32> = Decode::decode(&mut &bytes[..]).unwrap();
293 assert_eq!(mh, mh2);
294 }
295
296 #[test]
297 #[cfg(feature = "serde-codec")]
298 fn test_serde() {
299 let mh = Multihash::<crate::U32>::default();
300 let bytes = serde_json::to_string(&mh).unwrap();
301 let mh2 = serde_json::from_str(&bytes).unwrap();
302 assert_eq!(mh, mh2);
303 }
304}