libpijul_compat/backend/
hash.rs1use 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#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, PartialOrd, Ord)]
13pub enum Hash {
14 None,
17 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#[derive(Copy, Clone, Hash, Eq, Ord, PartialEq, PartialOrd, Serialize)]
91pub enum HashRef<'a> {
92 None,
93 Sha512(&'a [u8]),
94}
95
96impl Hash {
97 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 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 pub fn as_slice(&self) -> &[u8] {
127 match *self {
128 Hash::None => &[],
129 Hash::Sha512(ref e) => &e.0,
130 }
131 }
132
133 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 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 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 pub fn to_base58(&self) -> String {
169 bs58::encode(&self.to_binary()).into_string()
170 }
171}
172impl Hash {
173 pub fn to_base58(&self) -> String {
175 self.as_ref().to_base58()
176 }
177}
178
179impl<'a> HashRef<'a> {
180 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}