Skip to content

Commit fa072c1

Browse files
committed
refactor: move contents from lib.rs to digests.rs
1 parent e8106d5 commit fa072c1

File tree

2 files changed

+331
-329
lines changed

2 files changed

+331
-329
lines changed

src/digests.rs

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
use std::convert::TryFrom;
2+
use std::{cmp, fmt, hash};
3+
4+
use blake2b_simd::{blake2b, Params as Blake2bVariable};
5+
use blake2s_simd::{blake2s, Params as Blake2sVariable};
6+
use sha2::Digest;
7+
use tiny_keccak::Keccak;
8+
use unsigned_varint::{decode, encode};
9+
10+
use crate::errors::{DecodeError, DecodeOwnedError, EncodeError};
11+
use crate::hashes::Hash;
12+
use crate::storage::Storage;
13+
14+
// Helper macro for encoding input into output using sha1, sha2, tiny_keccak, or blake2
15+
macro_rules! encode {
16+
(sha1, Sha1, $input:expr, $output:expr) => {{
17+
let mut hasher = sha1::Sha1::new();
18+
hasher.update($input);
19+
$output.copy_from_slice(&hasher.digest().bytes());
20+
}};
21+
(sha2, $algorithm:ident, $input:expr, $output:expr) => {{
22+
let mut hasher = sha2::$algorithm::default();
23+
hasher.input($input);
24+
$output.copy_from_slice(hasher.result().as_ref());
25+
}};
26+
(tiny, $constructor:ident, $input:expr, $output:expr) => {{
27+
let mut kec = Keccak::$constructor();
28+
kec.update($input);
29+
kec.finalize($output);
30+
}};
31+
(blake2, $algorithm:ident, $input:expr, $output:expr) => {{
32+
let hash = $algorithm($input);
33+
$output.copy_from_slice(hash.as_ref());
34+
}};
35+
(blake2_256, $constructor:ident, $input:expr, $output:expr) => {{
36+
let hash = $constructor::new()
37+
.hash_length(32)
38+
.to_state()
39+
.update($input)
40+
.finalize();
41+
$output.copy_from_slice(hash.as_ref());
42+
}};
43+
(blake2_128, $constructor:ident, $input:expr, $output:expr) => {{
44+
let hash = $constructor::new()
45+
.hash_length(16)
46+
.to_state()
47+
.update($input)
48+
.finalize();
49+
$output.copy_from_slice(hash.as_ref());
50+
}};
51+
}
52+
53+
// And another one to keep the matching DRY
54+
macro_rules! match_encoder {
55+
($hash:ident for ($input:expr, $output:expr) {
56+
$( $hashtype:ident => $lib:ident :: $method:ident, )*
57+
}) => ({
58+
match $hash {
59+
$(
60+
Hash::$hashtype => encode!($lib, $method, $input, $output),
61+
)*
62+
63+
_ => return Err(EncodeError::UnsupportedType)
64+
}
65+
})
66+
}
67+
68+
/// Encodes data into a multihash.
69+
///
70+
/// # Errors
71+
///
72+
/// Will return an error if the specified hash type is not supported. See the docs for `Hash`
73+
/// to see what is supported.
74+
///
75+
/// # Examples
76+
///
77+
/// ```
78+
/// use multihash::{encode, Hash};
79+
///
80+
/// assert_eq!(
81+
/// encode(Hash::SHA2256, b"hello world").unwrap().to_vec(),
82+
/// vec![18, 32, 185, 77, 39, 185, 147, 77, 62, 8, 165, 46, 82, 215, 218, 125, 171, 250, 196,
83+
/// 132, 239, 227, 122, 83, 128, 238, 144, 136, 247, 172, 226, 239, 205, 233]
84+
/// );
85+
/// ```
86+
///
87+
pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
88+
// Custom length encoding for the identity multihash
89+
if let Hash::Identity = hash {
90+
if u64::from(std::u32::MAX) < as_u64(input.len()) {
91+
return Err(EncodeError::UnsupportedInputLength);
92+
}
93+
let mut buf = encode::u16_buffer();
94+
let code = encode::u16(hash.code(), &mut buf);
95+
let mut len_buf = encode::u32_buffer();
96+
let size = encode::u32(input.len() as u32, &mut len_buf);
97+
Ok(Multihash {
98+
storage: Storage::from_slices(&[&code, &size, &input]),
99+
})
100+
} else {
101+
let (offset, mut output) = encode_hash(hash);
102+
match_encoder!(hash for (input, &mut output[offset ..]) {
103+
SHA1 => sha1::Sha1,
104+
SHA2256 => sha2::Sha256,
105+
SHA2512 => sha2::Sha512,
106+
SHA3224 => tiny::new_sha3_224,
107+
SHA3256 => tiny::new_sha3_256,
108+
SHA3384 => tiny::new_sha3_384,
109+
SHA3512 => tiny::new_sha3_512,
110+
Keccak224 => tiny::new_keccak224,
111+
Keccak256 => tiny::new_keccak256,
112+
Keccak384 => tiny::new_keccak384,
113+
Keccak512 => tiny::new_keccak512,
114+
Blake2b512 => blake2::blake2b,
115+
Blake2b256 => blake2_256::Blake2bVariable,
116+
Blake2s256 => blake2::blake2s,
117+
Blake2s128 => blake2_128::Blake2sVariable,
118+
});
119+
120+
Ok(Multihash {
121+
storage: Storage::from_slice(&output),
122+
})
123+
}
124+
}
125+
126+
// Encode the given [`Hash`] value and ensure the returned [`Vec<u8>`]
127+
// has enough capacity to hold the actual digest.
128+
fn encode_hash(hash: Hash) -> (usize, Vec<u8>) {
129+
let mut buf = encode::u16_buffer();
130+
let code = encode::u16(hash.code(), &mut buf);
131+
132+
let len = code.len() + 1 + usize::from(hash.size());
133+
134+
let mut output = Vec::with_capacity(len);
135+
output.extend_from_slice(code);
136+
output.push(hash.size());
137+
output.resize(len, 0);
138+
139+
(code.len() + 1, output)
140+
}
141+
142+
/// Represents a valid multihash.
143+
#[derive(Clone)]
144+
pub struct Multihash {
145+
storage: Storage,
146+
}
147+
148+
impl fmt::Debug for Multihash {
149+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150+
f.debug_tuple("Multihash").field(&self.as_bytes()).finish()
151+
}
152+
}
153+
154+
impl PartialEq for Multihash {
155+
fn eq(&self, other: &Self) -> bool {
156+
self.storage.bytes() == other.storage.bytes()
157+
}
158+
}
159+
160+
impl Eq for Multihash {}
161+
162+
impl hash::Hash for Multihash {
163+
fn hash<H: hash::Hasher>(&self, state: &mut H) {
164+
self.storage.bytes().hash(state);
165+
}
166+
}
167+
168+
impl Multihash {
169+
/// Verifies whether `bytes` contains a valid multihash, and if so returns a `Multihash`.
170+
pub fn from_bytes(bytes: Vec<u8>) -> Result<Multihash, DecodeOwnedError> {
171+
if let Err(err) = MultihashRef::from_slice(&bytes) {
172+
return Err(DecodeOwnedError {
173+
error: err,
174+
data: bytes,
175+
});
176+
}
177+
Ok(Multihash {
178+
storage: Storage::from_slice(&bytes),
179+
})
180+
}
181+
182+
/// Returns the bytes representation of the multihash.
183+
pub fn into_bytes(self) -> Vec<u8> {
184+
self.to_vec()
185+
}
186+
187+
/// Returns the bytes representation of the multihash.
188+
pub fn to_vec(&self) -> Vec<u8> {
189+
Vec::from(self.as_bytes())
190+
}
191+
192+
/// Returns the bytes representation of this multihash.
193+
pub fn as_bytes(&self) -> &[u8] {
194+
self.storage.bytes()
195+
}
196+
197+
/// Builds a `MultihashRef` corresponding to this `Multihash`.
198+
pub fn as_ref(&self) -> MultihashRef {
199+
MultihashRef {
200+
bytes: self.as_bytes(),
201+
}
202+
}
203+
204+
/// Returns which hashing algorithm is used in this multihash.
205+
pub fn algorithm(&self) -> Hash {
206+
self.as_ref().algorithm()
207+
}
208+
209+
/// Returns the hashed data.
210+
pub fn digest(&self) -> &[u8] {
211+
self.as_ref().digest()
212+
}
213+
}
214+
215+
impl AsRef<[u8]> for Multihash {
216+
fn as_ref(&self) -> &[u8] {
217+
self.as_bytes()
218+
}
219+
}
220+
221+
impl<'a> PartialEq<MultihashRef<'a>> for Multihash {
222+
fn eq(&self, other: &MultihashRef<'a>) -> bool {
223+
&*self.as_bytes() == other.as_bytes()
224+
}
225+
}
226+
227+
impl TryFrom<Vec<u8>> for Multihash {
228+
type Error = DecodeOwnedError;
229+
230+
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
231+
Multihash::from_bytes(value)
232+
}
233+
}
234+
235+
impl PartialOrd for Multihash {
236+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
237+
Some(self.cmp(other))
238+
}
239+
}
240+
241+
impl Ord for Multihash {
242+
fn cmp(&self, other: &Self) -> cmp::Ordering {
243+
self.as_ref().cmp(&other.as_ref())
244+
}
245+
}
246+
247+
/// Represents a valid multihash.
248+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
249+
pub struct MultihashRef<'a> {
250+
bytes: &'a [u8],
251+
}
252+
253+
impl<'a> MultihashRef<'a> {
254+
/// Creates a `MultihashRef` from the given `input`.
255+
pub fn from_slice(input: &'a [u8]) -> Result<Self, DecodeError> {
256+
if input.is_empty() {
257+
return Err(DecodeError::BadInputLength);
258+
}
259+
260+
// Ensure `Hash::code` returns a `u16` so that our `decode::u16` here is correct.
261+
std::convert::identity::<fn(Hash) -> u16>(Hash::code);
262+
let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;
263+
264+
let alg = Hash::from_code(code).ok_or(DecodeError::UnknownCode)?;
265+
266+
// handle the identity case
267+
if alg == Hash::Identity {
268+
let (hash_len, bytes) = decode::u32(&bytes).map_err(|_| DecodeError::BadInputLength)?;
269+
if as_u64(bytes.len()) != u64::from(hash_len) {
270+
return Err(DecodeError::BadInputLength);
271+
}
272+
return Ok(MultihashRef { bytes: input });
273+
}
274+
275+
let hash_len = usize::from(alg.size());
276+
277+
// Length of input after hash code should be exactly hash_len + 1
278+
if bytes.len() != hash_len + 1 {
279+
return Err(DecodeError::BadInputLength);
280+
}
281+
282+
if usize::from(bytes[0]) != hash_len {
283+
return Err(DecodeError::BadInputLength);
284+
}
285+
286+
Ok(MultihashRef { bytes: input })
287+
}
288+
289+
/// Returns which hashing algorithm is used in this multihash.
290+
pub fn algorithm(&self) -> Hash {
291+
let code = decode::u16(&self.bytes)
292+
.expect("multihash is known to be valid algorithm")
293+
.0;
294+
Hash::from_code(code).expect("multihash is known to be valid")
295+
}
296+
297+
/// Returns the hashed data.
298+
pub fn digest(&self) -> &'a [u8] {
299+
let bytes = decode::u16(&self.bytes)
300+
.expect("multihash is known to be valid digest")
301+
.1;
302+
&bytes[1..]
303+
}
304+
305+
/// Builds a `Multihash` that owns the data.
306+
///
307+
/// This operation allocates.
308+
pub fn to_owned(&self) -> Multihash {
309+
Multihash {
310+
storage: Storage::from_slice(self.bytes),
311+
}
312+
}
313+
314+
/// Returns the bytes representation of this multihash.
315+
pub fn as_bytes(&self) -> &'a [u8] {
316+
&self.bytes
317+
}
318+
}
319+
320+
impl<'a> PartialEq<Multihash> for MultihashRef<'a> {
321+
fn eq(&self, other: &Multihash) -> bool {
322+
self.as_bytes() == &*other.as_bytes()
323+
}
324+
}
325+
326+
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
327+
fn as_u64(a: usize) -> u64 {
328+
a as u64
329+
}

0 commit comments

Comments
 (0)