ion_binary_rs/
ion_hash.rs

1use crate::ion_hash_encoder::encode_value;
2use crate::IonValue;
3use digest::Digest;
4use sha2::Sha256;
5use std::cmp::{Ordering, PartialEq};
6use std::marker::PhantomData;
7
8/// Ion Hash implementation. Once the hasher is initialized you can add new values to it
9/// and it will perform the dot operation internally. Once you added everything you want
10/// to add just call `get()` and it will provide you with a &[u8] slice containing the
11/// hash.
12///
13/// You can use the method digest if you want to hash only one IonValue.
14///
15/// ```rust,no_run
16/// use sha2::Sha256;
17/// use ion_binary_rs::{IonHash, IonValue};
18/// use std::collections::HashMap;
19///
20/// let mut ion_struct = HashMap::new();
21///
22/// ion_struct.insert("Model".to_string(), IonValue::String("CLK 350".to_string()));
23/// ion_struct.insert("Type".to_string(), IonValue::String("Sedan".to_string()));
24/// ion_struct.insert("Color".to_string(), IonValue::String("White".to_string()));
25/// ion_struct.insert(
26///     "VIN".to_string(),
27///     IonValue::String("1C4RJFAG0FC625797".to_string()),
28/// );
29/// ion_struct.insert("Make".to_string(), IonValue::String("Mercedes".to_string()));
30/// ion_struct.insert("Year".to_string(), IonValue::Integer(2019));
31///
32/// let ion_value = IonValue::Struct(ion_struct);
33///
34/// let hash = IonHash::digest::<Sha256>(&ion_value);
35///
36/// println!("{:X?}", hash);
37/// ```
38///
39#[derive(Debug)]
40pub struct IonHash<D: Digest = Sha256> {
41    buffer: Vec<u8>,
42    hasher_type: PhantomData<D>,
43}
44
45impl<D: Digest> IonHash<D> {
46    /// Hashes the bytes and perform a dot operation with
47    /// current version of the IonHash hash.
48    pub fn add_bytes(&mut self, value: &[u8]) {
49        let value = IonHash::from_bytes::<D>(value);
50
51        self.dot(value);
52    }
53
54    /// Assumes that the bytes are already hashed and performs
55    /// the dot operation with current version of the IonHash
56    /// hash.
57    pub fn add_hashed_bytes(&mut self, value: &[u8]) {
58        let value = IonHash::from_hashes_bytes::<D>(value);
59
60        self.dot(value);
61    }
62
63    /// Serializes and hashes the Ion Value and performs
64    /// the dot operation with current version of the IonHash
65    /// hash.
66    pub fn add_ion_value(&mut self, value: &IonValue) {
67        let buffer = encode_value::<D>(value);
68
69        let value = IonHash::from_bytes::<D>(&buffer);
70
71        self.dot(value);
72    }
73
74    /// performs the dot operation with current version of the
75    /// IonHash hash.
76    pub fn dot(&mut self, value: IonHash<D>) -> &mut Self {
77        if value.buffer.is_empty() {
78            return self;
79        }
80
81        if self.buffer.is_empty() {
82            self.buffer = value.buffer;
83            return self;
84        }
85
86        let mut buffer: Vec<u8> = vec![];
87
88        if *self < value {
89            buffer.extend(self.get());
90            buffer.extend(value.get());
91        } else {
92            buffer.extend(value.get());
93            buffer.extend(self.get());
94        }
95
96        self.buffer = D::digest(&buffer).to_vec();
97
98        self
99    }
100
101    /// Gets the current hash. Useful for when you need to
102    /// extract the final result after several operations.
103    pub fn get(&self) -> &[u8] {
104        &self.buffer
105    }
106}
107
108impl IonHash {
109    /// Creates an empty Ion Hash with the default hasher: Sha256
110    pub fn new() -> IonHash {
111        IonHash {
112            buffer: vec![],
113            hasher_type: PhantomData,
114        }
115    }
116
117    /// Creates a hasher with some starting bytes which will
118    /// be first hashed
119    pub fn from_bytes<D: Digest>(buf: &[u8]) -> IonHash<D> {
120        let hased_bytes = D::digest(buf);
121        IonHash::from_hashes_bytes(&hased_bytes)
122    }
123
124    /// Creates a hasher with some starting hash
125    pub fn from_hashes_bytes<D: Digest>(buf: &[u8]) -> IonHash<D> {
126        IonHash {
127            buffer: buf.to_vec(),
128            hasher_type: PhantomData,
129        }
130    }
131
132    /// Creates a hasher with some starting Ion Value which will
133    /// be first serialized and hashed
134    pub fn from_ion_value<D: Digest>(value: &IonValue) -> IonHash<D> {
135        let mut hash = IonHash::with_hasher::<D>();
136
137        hash.add_ion_value(value);
138
139        hash
140    }
141
142    /// Creates an empty hasher using the provided hasher
143    pub fn with_hasher<D: Digest>() -> IonHash<D> {
144        IonHash {
145            buffer: vec![],
146            hasher_type: PhantomData,
147        }
148    }
149
150    /// Shorthand method for hashing an Ion Value in one step.
151    pub fn digest<D: Digest>(value: &IonValue) -> Vec<u8> {
152        IonHash::from_ion_value::<D>(value).get().to_vec()
153    }
154
155    /// Shorthand method for hashing an Ion Value in one step.
156    /// It uses the default hasher: Sha256
157    pub fn default_digest(value: &IonValue) -> Vec<u8> {
158        IonHash::from_ion_value::<Sha256>(value).get().to_vec()
159    }
160}
161
162impl Default for IonHash {
163    fn default() -> IonHash<Sha256> {
164        IonHash::with_hasher::<Sha256>()
165    }
166}
167
168impl<D: Digest> PartialEq for IonHash<D> {
169    fn eq(&self, _: &IonHash<D>) -> bool {
170        self.buffer == self.get()
171    }
172}
173
174impl<D: Digest> PartialOrd for IonHash<D> {
175    fn partial_cmp(&self, value: &IonHash<D>) -> Option<Ordering> {
176        self.buffer
177            .iter()
178            .rev()
179            .map(|byte| *byte as i8)
180            .partial_cmp(value.get().iter().rev().map(|byte| *byte as i8))
181    }
182}