blot/
core.rs

1// Copyright 2018 Arnau Siches
2//
3// Licensed under the MIT license <LICENSE or http://opensource.org/licenses/MIT>.
4// This file may not be copied, modified, or distributed except according to
5// those terms.
6
7//! Blot core implementation.
8//!
9//! This module defines the [`Blot`] trait and the blot implementation for most Rust primitives.
10
11use multihash::{Harvest, Hash, Multihash};
12use std;
13use std::collections::{BTreeMap, HashMap, HashSet};
14use tag::Tag;
15
16/// Trait for blot implementations.
17pub trait Blot {
18    fn blot<T: Multihash>(&self, &T) -> Harvest;
19
20    fn digest<D: Multihash>(&self, digester: D) -> Hash<D> {
21        let digest = self.blot(&digester);
22        Hash::new(digester, digest)
23    }
24}
25
26impl<'a, T: ?Sized + Blot> Blot for &'a T {
27    #[inline]
28    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
29        T::blot(*self, digester)
30    }
31}
32
33impl Blot for str {
34    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
35        digester.digest_primitive(Tag::Unicode, self.as_bytes())
36    }
37}
38
39impl Blot for String {
40    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
41        digester.digest_primitive(Tag::Unicode, self.as_bytes())
42    }
43}
44
45impl Blot for [u8] {
46    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
47        digester.digest_primitive(Tag::Raw, self)
48    }
49}
50
51impl<'a, T: Blot> Blot for Option<T> {
52    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
53        match self {
54            None => digester.digest_primitive(Tag::Null, "".as_bytes()),
55            Some(a) => a.blot(digester),
56        }
57    }
58}
59
60impl<'a> Blot for bool {
61    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
62        let string = if *self { "1" } else { "0" };
63        digester.digest_primitive(Tag::Bool, string.as_bytes())
64    }
65}
66
67macro_rules! blot_integer (($type:ident) => {
68    impl Blot for $type {
69        fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
70            digester.digest_primitive(Tag::Integer, self.to_string().as_bytes())
71        }
72    }
73});
74
75blot_integer!(u8);
76blot_integer!(u16);
77blot_integer!(u32);
78blot_integer!(u64);
79blot_integer!(usize);
80blot_integer!(i8);
81blot_integer!(i16);
82blot_integer!(i32);
83blot_integer!(i64);
84blot_integer!(isize);
85
86impl<T: Blot> Blot for Vec<T> {
87    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
88        let list: Vec<Vec<u8>> = self
89            .iter()
90            .map(|item| {
91                item.blot(digester)
92                    .as_ref()
93                    .iter()
94                    .map(|x| *x)
95                    .collect::<Vec<u8>>()
96            }).collect();
97
98        digester.digest_collection(Tag::List, list)
99    }
100}
101
102impl<T: Blot + Eq + std::hash::Hash> Blot for HashSet<T> {
103    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
104        let mut list: Vec<Vec<u8>> = self
105            .iter()
106            .map(|item| {
107                item.blot(digester)
108                    .as_ref()
109                    .iter()
110                    .map(|x| *x)
111                    .collect::<Vec<u8>>()
112            }).collect();
113
114        list.sort_unstable();
115
116        digester.digest_collection(Tag::Set, list)
117    }
118}
119
120impl<K, V> Blot for HashMap<K, V>
121where
122    K: Blot + Eq + std::hash::Hash,
123    V: Blot + PartialEq,
124{
125    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
126        let mut list: Vec<Vec<u8>> = self
127            .iter()
128            .map(|(k, v)| {
129                let mut res: Vec<u8> = Vec::with_capacity(64);
130                res.extend_from_slice(k.blot(digester).as_ref());
131                res.extend_from_slice(v.blot(digester).as_ref());
132
133                res
134            }).collect();
135
136        list.sort_unstable();
137
138        digester.digest_collection(Tag::Dict, list)
139    }
140}
141
142impl<K, V> Blot for BTreeMap<K, V>
143where
144    K: Blot + Eq + std::hash::Hash,
145    V: Blot + PartialEq,
146{
147    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
148        let mut list: Vec<Vec<u8>> = self
149            .iter()
150            .map(|(k, v)| {
151                let mut res: Vec<u8> = Vec::with_capacity(64);
152                res.extend_from_slice(k.blot(digester).as_ref());
153                res.extend_from_slice(v.blot(digester).as_ref());
154
155                res
156            }).collect();
157
158        list.sort_unstable();
159
160        digester.digest_collection(Tag::Dict, list)
161    }
162}
163
164impl Blot for f32 {
165    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
166        (*self as f64).blot(digester)
167    }
168}
169
170impl Blot for f64 {
171    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
172        if self.is_nan() {
173            digester.digest_primitive(Tag::Float, "NaN".as_bytes())
174        } else if self.is_infinite() {
175            let s = if self.is_sign_negative() {
176                "-Infinity"
177            } else {
178                "Infinity"
179            };
180            digester.digest_primitive(Tag::Float, s.as_bytes())
181        } else {
182            digester.digest_primitive(Tag::Float, float_normalize(*self).as_bytes())
183        }
184    }
185}
186
187pub fn float_normalize(mut f: f64) -> String {
188    if f == 0.0 {
189        return "+0:".to_owned();
190    }
191
192    let mut s = String::new();
193
194    // sign
195    if f < 0. {
196        s.push('-');
197        f = -f;
198    } else {
199        s.push('+');
200    }
201
202    // exponent
203    let mut e = 0;
204
205    while f > 1. {
206        f = f / 2.;
207        e = e + 1;
208    }
209
210    while f <= 0.5 {
211        f = f * 2.;
212        e = e - 1;
213    }
214
215    s.push_str(&e.to_string());
216    s.push(':');
217
218    // mantissa
219    assert!(f <= 1.);
220    assert!(f > 0.5);
221    // TODO: Return Result
222
223    while f != 0. {
224        if f >= 1. {
225            s.push('1');
226            f = f - 1.;
227        } else {
228            s.push('0');
229        }
230
231        assert!(f < 1.);
232        assert!(s.len() < 1000);
233
234        f = f * 2.;
235    }
236
237    s
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243    use hex::FromHex;
244    use multihash::Sha2256;
245
246    #[test]
247    fn bool_blot_raw() {
248        let expected = "7dc96f776c8423e57a2785489a3f9c43fb6e756876d6ad9a9cac4aa4e72ec193";
249        let actual = true.digest(Sha2256);
250
251        assert_eq!(format!("{}", actual.digest()), expected);
252    }
253
254    #[test]
255    fn unicode_blot() {
256        let pairs = [
257            (
258                "ԱԲաբ",
259                "12202a2a4485a4e338d8df683971956b1090d2f5d33955a81ecaad1a75125f7a316c",
260            ),
261            (
262                "ϓ",
263                "1220f72826713a01881404f34975447bd6edcb8de40b191dc57097ebf4f5417a554d",
264            ),
265            (
266                "foo",
267                "1220a6a6e5e783c363cd95693ec189c2682315d956869397738679b56305f2095038",
268            ),
269        ];
270        for (raw, expected) in pairs.iter() {
271            let actual = format!("{}", raw.digest(Sha2256));
272            assert_eq!(&actual, expected);
273        }
274    }
275
276    #[test]
277    fn null_blot() {
278        let expected = "12201b16b1df538ba12dc3f97edbb85caa7050d46c148134290feba80f8236c83db9";
279        let actual = format!("{}", None::<String>.digest(Sha2256));
280
281        assert_eq!(actual, expected);
282    }
283
284    #[test]
285    fn raw_blot() {
286        let expected = "1220e318859db4d2acc89c0d503ddbcf8331625125a79018d19cf8f8d1336b7eb39e";
287        let bytes =
288            Vec::from_hex("6b18693874513ba13da54d61aafa7cad0c8f5573f3431d6f1c04b07ddb27d6bb")
289                .unwrap();
290        let actual = format!("{}", (&bytes[..]).digest(Sha2256));
291        assert_eq!(actual, expected);
292    }
293
294    #[test]
295    fn bool_blot() {
296        assert_eq!(
297            format!("{}", true.digest(Sha2256)),
298            "12207dc96f776c8423e57a2785489a3f9c43fb6e756876d6ad9a9cac4aa4e72ec193"
299        );
300        assert_eq!(
301            format!("{}", false.digest(Sha2256)),
302            "1220c02c0b965e023abee808f2b548d8d5193a8b5229be6f3121a6f16e2d41a449b3"
303        );
304    }
305
306    #[test]
307    fn int_blot() {
308        let pairs = [
309            (
310                0,
311                "1220a4e167a76a05add8a8654c169b07b0447a916035aef602df103e8ae0fe2ff390",
312            ),
313            (
314                42,
315                "1220ebc35dc1b8e2602b72beb8d8e5bcdb2babe90f57bcb54ad7282ec798659d2196",
316            ),
317        ];
318        for (raw, expected) in pairs.iter() {
319            let actual = format!("{}", raw.digest(Sha2256));
320            assert_eq!(&actual, expected);
321        }
322    }
323
324    #[test]
325    fn zero_float_blot() {
326        let expected = "122060101d8c9cb988411468e38909571f357daa67bff5a7b0a3f9ae295cd4aba33d";
327        let actual = format!("{}", 0.0.digest(Sha2256));
328        assert_eq!(actual, expected);
329    }
330    #[test]
331    fn float_blot() {
332        use std::f64;
333        let pairs = [
334            (
335                -0.0,
336                "122060101d8c9cb988411468e38909571f357daa67bff5a7b0a3f9ae295cd4aba33d",
337            ),
338            (
339                f64::NAN,
340                "12205d6c301a98d835732d459d7018a8d546872f7ba3c39a45ba481746d2c6d566d9",
341            ),
342            (
343                f64::INFINITY,
344                "1220e0309b2362dc6aaf595338cd9e116761640f74927bcdc4f76e8e6433738f25c7",
345            ),
346            (
347                f64::NEG_INFINITY,
348                "12201167518d5554ba86d9b176af0a57f29d425bedaa9847c245cc397b37533228f7",
349            ),
350        ];
351        for (raw, expected) in pairs.iter() {
352            let actual = format!("{}", raw.digest(Sha2256));
353            assert_eq!(&actual, expected);
354        }
355    }
356
357    #[test]
358    fn empty_list_blot() {
359        let expected = "1220acac86c0e609ca906f632b0e2dacccb2b77d22b0621f20ebece1a4835b93f6f0";
360        let list: Vec<u8> = vec![];
361        let actual = format!("{}", list.digest(Sha2256));
362        assert_eq!(actual, expected);
363    }
364
365    #[test]
366    fn list_blot() {
367        let pairs = [
368            (
369                vec!["foo"],
370                "1220268bc27d4974d9d576222e4cdbb8f7c6bd6791894098645a19eeca9c102d0964",
371            ),
372            (
373                vec!["foo", "bar"],
374                "122032ae896c413cfdc79eec68be9139c86ded8b279238467c216cf2bec4d5f1e4a2",
375            ),
376        ];
377        for (raw, expected) in pairs.iter() {
378            let actual = format!("{}", raw.digest(Sha2256));
379            assert_eq!(&actual, expected);
380        }
381    }
382
383    #[test]
384    fn empty_set_blot() {
385        let expected = "1220043a718774c572bd8a25adbeb1bfcd5c0256ae11cecf9f9c3f925d0e52beaf89";
386        let set: HashSet<u8> = HashSet::new();
387        let actual = format!("{}", set.digest(Sha2256));
388        assert_eq!(actual, expected);
389    }
390
391    #[test]
392    fn set_blot() {
393        let expected = "1220a4fef47742c80337b2eb0dcc6ed36610c93aca0afef86a65f381020b9de2284d";
394        let mut set: HashSet<&str> = HashSet::new();
395        set.insert("foo");
396        let actual = format!("{}", set.digest(Sha2256));
397        assert_eq!(actual, expected);
398    }
399
400    #[test]
401    fn empty_dict_blot() {
402        let expected = "122018ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4";
403        let dict: HashMap<&str, u8> = HashMap::new();
404        let actual = format!("{}", dict.digest(Sha2256));
405        assert_eq!(actual, expected);
406    }
407
408    #[test]
409    fn dict_blot() {
410        let expected = "12207ef5237c3027d6c58100afadf37796b3d351025cf28038280147d42fdc53b960";
411        let mut dict: HashMap<&str, &str> = HashMap::new();
412        dict.insert("foo", "bar");
413        let actual = format!("{}", dict.digest(Sha2256));
414        assert_eq!(actual, expected);
415    }
416}