libpijul_compat/backend/
hash.rs

1use bs58;
2use sanakirja::{Alignment, Representable};
3use serde;
4use serde::de::{Deserialize, Deserializer, Visitor};
5use serde::ser::{Serialize, Serializer};
6use std;
7use Error;
8
9const SHA512_BYTES: usize = 512 / 8;
10
11/// The external hash of patches.
12#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, PartialOrd, Ord)]
13pub enum Hash {
14    /// None is the hash of the "null patch", which introduced a
15    /// single root vertex at the beginning of the repository.
16    None,
17    /// Patch hashed using the SHA2-512 algorithm.
18    Sha512(Sha512),
19}
20
21pub struct Sha512(pub [u8; SHA512_BYTES]);
22
23impl PartialEq for Sha512 {
24    fn eq(&self, h: &Sha512) -> bool {
25        (&self.0[..]).eq(&h.0[..])
26    }
27}
28impl Eq for Sha512 {}
29impl PartialOrd for Sha512 {
30    fn partial_cmp(&self, h: &Sha512) -> Option<std::cmp::Ordering> {
31        (&self.0[..]).partial_cmp(&h.0[..])
32    }
33}
34impl Ord for Sha512 {
35    fn cmp(&self, h: &Sha512) -> std::cmp::Ordering {
36        (&self.0[..]).cmp(&h.0[..])
37    }
38}
39
40impl std::hash::Hash for Sha512 {
41    fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
42        (&self.0[..]).hash(h)
43    }
44}
45
46struct Sha512Visitor;
47impl<'a> Visitor<'a> for Sha512Visitor {
48    type Value = Sha512;
49
50    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
51        write!(formatter, "A byte slice of length {}", SHA512_BYTES)
52    }
53
54    fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
55        let mut x: [u8; SHA512_BYTES] = [0; SHA512_BYTES];
56        x.copy_from_slice(v);
57        Ok(Sha512(x))
58    }
59}
60
61impl<'a> Deserialize<'a> for Sha512 {
62    fn deserialize<D: Deserializer<'a>>(d: D) -> Result<Sha512, D::Error> {
63        d.deserialize_bytes(Sha512Visitor)
64    }
65}
66
67impl Serialize for Sha512 {
68    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
69        s.serialize_bytes(&self.0[..])
70    }
71}
72
73impl std::fmt::Debug for Sha512 {
74    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
75        (&self.0[..]).fmt(fmt)
76    }
77}
78impl<'a> std::fmt::Debug for HashRef<'a> {
79    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
80        write!(fmt, "{}", self.to_base58())
81    }
82}
83impl std::fmt::Debug for Hash {
84    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
85        self.as_ref().fmt(fmt)
86    }
87}
88
89/// A borrowed version of `Hash`.
90#[derive(Copy, Clone, Hash, Eq, Ord, PartialEq, PartialOrd, Serialize)]
91pub enum HashRef<'a> {
92    None,
93    Sha512(&'a [u8]),
94}
95
96impl Hash {
97    /// Get a `Hash` from a binary slice. This function does not
98    /// compute the digest of anything, it just converts types.
99    pub fn from_binary(v: &[u8]) -> Option<Self> {
100        if v.len() == 0 {
101            None
102        } else {
103            if v[0] == Algorithm::Sha512 as u8 && v.len() == 1 + SHA512_BYTES {
104                let mut hash = [0; SHA512_BYTES];
105                hash.clone_from_slice(&v[1..]);
106                Some(Hash::Sha512(Sha512(hash)))
107            } else if v[0] == Algorithm::None as u8 && v.len() == 1 {
108                Some(Hash::None)
109            } else {
110                None
111            }
112        }
113    }
114
115    /// Decode a hash from a base58-encoded `str`.
116    pub fn from_base58(base58: &str) -> Option<Self> {
117        if let Ok(v) = bs58::decode(base58).into_vec() {
118            Self::from_binary(&v)
119        } else {
120            None
121        }
122    }
123
124    /// This hash as a slice (nonempty iff SHA512). This method
125    /// might disappear when new hashes are introduced.
126    pub fn as_slice(&self) -> &[u8] {
127        match *self {
128            Hash::None => &[],
129            Hash::Sha512(ref e) => &e.0,
130        }
131    }
132
133    /// A borrowed version of this `Hash`, used for instance to
134    /// query the databases.
135    pub fn as_ref(&self) -> HashRef {
136        match *self {
137            Hash::None => HashRef::None,
138            Hash::Sha512(ref e) => HashRef::Sha512(unsafe {
139                std::slice::from_raw_parts(e.0.as_ptr() as *const u8, SHA512_BYTES)
140            }),
141        }
142    }
143
144    /// Create a `Hash` from the binary slice of the patch contents.
145    pub fn of_slice(buf: &[u8]) -> Result<Hash, Error> {
146        use openssl::hash::*;
147        let hash = {
148            let mut hasher = Hasher::new(MessageDigest::sha512())?;
149            hasher.update(buf)?;
150            hasher.finish()?
151        };
152        let mut digest: [u8; SHA512_BYTES] = [0; SHA512_BYTES];
153        digest.clone_from_slice(hash.as_ref());
154        Ok(Hash::Sha512(Sha512(digest)))
155    }
156}
157
158impl<'a> HashRef<'a> {
159    /// Encode this `HashRef` in binary.
160    pub fn to_binary(&self) -> Vec<u8> {
161        let u = self.to_unsafe();
162        let mut v = vec![0; u.onpage_size() as usize];
163        unsafe { u.write_value(v.as_mut_ptr()) }
164        v
165    }
166
167    /// Encode this `HashRef` in base58.
168    pub fn to_base58(&self) -> String {
169        bs58::encode(&self.to_binary()).into_string()
170    }
171}
172impl Hash {
173    /// Encode this `Hash` in base64.
174    pub fn to_base58(&self) -> String {
175        self.as_ref().to_base58()
176    }
177}
178
179impl<'a> HashRef<'a> {
180    /// Build an owned version of a `HashRef`.
181    pub fn to_owned(&self) -> Hash {
182        match *self {
183            HashRef::None => Hash::None,
184            HashRef::Sha512(e) => {
185                let mut hash = [0; SHA512_BYTES];
186                unsafe {
187                    std::ptr::copy_nonoverlapping(
188                        e.as_ptr() as *const u8,
189                        hash.as_mut_ptr() as *mut u8,
190                        SHA512_BYTES,
191                    )
192                }
193                Hash::Sha512(Sha512(hash))
194            }
195        }
196    }
197}
198
199impl Clone for Hash {
200    fn clone(&self) -> Self {
201        self.as_ref().to_owned()
202    }
203}
204
205pub const ROOT_HASH: &'static Hash = &Hash::None;
206
207#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
208#[repr(u8)]
209pub enum Algorithm {
210    None = 0,
211    Sha512 = 1,
212}
213
214#[derive(Clone, Copy, Debug)]
215pub enum UnsafeHash {
216    None,
217    Sha512(*const u8),
218}
219
220impl<'a> HashRef<'a> {
221    pub fn to_unsafe(&self) -> UnsafeHash {
222        match *self {
223            HashRef::None => UnsafeHash::None,
224            HashRef::Sha512(e) => UnsafeHash::Sha512(e.as_ptr()),
225        }
226    }
227    pub unsafe fn from_unsafe(p: UnsafeHash) -> HashRef<'a> {
228        match p {
229            UnsafeHash::None => HashRef::None,
230            UnsafeHash::Sha512(p) => HashRef::Sha512(std::slice::from_raw_parts(p, SHA512_BYTES)),
231        }
232    }
233}
234
235impl Representable for UnsafeHash {
236    fn alignment() -> Alignment {
237        Alignment::B1
238    }
239
240    fn onpage_size(&self) -> u16 {
241        1 + (match *self {
242            UnsafeHash::Sha512(_) => 64,
243            UnsafeHash::None => 0,
244        })
245    }
246    unsafe fn write_value(&self, p: *mut u8) {
247        trace!("write_value {:?} {:?}", self, p);
248        match *self {
249            UnsafeHash::Sha512(q) => {
250                *p = Algorithm::Sha512 as u8;
251                std::ptr::copy(q, p.offset(1), 64)
252            }
253            UnsafeHash::None => *p = Algorithm::None as u8,
254        }
255    }
256    unsafe fn read_value(p: *const u8) -> Self {
257        trace!("read_value {:?} {:?}", p, *p);
258        match std::mem::transmute(*p) {
259            Algorithm::Sha512 => UnsafeHash::Sha512(p.offset(1)),
260            Algorithm::None => UnsafeHash::None,
261        }
262    }
263    unsafe fn cmp_value<T>(&self, _: &T, x: Self) -> std::cmp::Ordering {
264        let a = HashRef::from_unsafe(*self);
265        let b = HashRef::from_unsafe(x);
266        a.cmp(&b)
267    }
268    type PageOffsets = std::iter::Empty<u64>;
269    fn page_offsets(&self) -> Self::PageOffsets {
270        std::iter::empty()
271    }
272}