hash40/
lib.rs

1pub mod errors;
2pub mod label_map;
3
4pub use binrw;
5pub use diff;
6
7mod algorithm;
8
9use errors::*;
10use label_map::LabelMap;
11
12use binrw::binrw as binrw_attr;
13use byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt};
14use diff::Diff;
15use lazy_static::lazy_static;
16
17use std::fmt::{Display, Error as fmtError, Formatter};
18use std::io::{self, Read, Write};
19use std::ops::{Deref, DerefMut};
20use std::str::FromStr;
21use std::sync::{Arc, Mutex};
22
23#[cfg(feature = "serde")]
24use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
25
26lazy_static! {
27    /// The static map used for converting Hash40's between hash and string form.
28    static ref LABELS: Arc<Mutex<LabelMap>> = Arc::new(Mutex::new(LabelMap::default()));
29}
30
31/// The central type of the crate, representing a string hashed using the hash40 algorithm
32/// Hash40 is a combination of a crc32 checksum and string length appended to the top bits
33#[binrw_attr]
34#[repr(transparent)]
35#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
36pub struct Hash40(pub u64);
37
38/// An alias for Hash40::new, which creates a Hash40 from a string
39pub const fn hash40(string: &str) -> Hash40 {
40    Hash40::new(string)
41}
42
43/// An extension of the byteorder trait, to read a Hash40 from a stream
44pub trait ReadHash40: ReadBytesExt {
45    fn read_hash40<T: ByteOrder>(&mut self) -> Result<Hash40, io::Error>;
46
47    fn read_hash40_with_meta<T: ByteOrder>(&mut self) -> Result<(Hash40, u32), io::Error>;
48}
49
50/// An extension of the byteorder trait, to write a Hash40 into a stream
51pub trait WriteHash40: WriteBytesExt {
52    fn write_hash40<T: ByteOrder>(&mut self, hash: Hash40) -> Result<(), io::Error>;
53
54    fn write_hash40_with_meta<T: ByteOrder>(
55        &mut self,
56        hash: Hash40,
57        meta: u32,
58    ) -> Result<(), io::Error>;
59}
60
61impl Hash40 {
62    /// Computes a Hash40 from a string. This method does not respect the static label map,
63    /// nor does it check to see if the provided string is in hexadecimal format already.
64    pub const fn new(string: &str) -> Self {
65        Self(algorithm::hash40(string))
66    }
67
68    /// Converts a hexadecimal string representation of a hash to a Hash40
69    pub fn from_hex_str(value: &str) -> Result<Self, ParseHashError> {
70        if let Some(stripped) = value.strip_prefix("0x") {
71            Ok(Hash40(u64::from_str_radix(stripped, 16)?))
72        } else {
73            Err(ParseHashError::MissingPrefix)
74        }
75    }
76
77    /// Computes a Hash40 from a string. This method checks if the string is a hexadecimal
78    /// value first. If not, it either searches for a reverse label from the static map or
79    /// computes a new hash, depending on the form of the static label map.
80    pub fn from_label(label: &str) -> Result<Self, FromLabelError> {
81        match Self::from_hex_str(label) {
82            Ok(hash) => Ok(hash),
83            Err(err) => match err {
84                ParseHashError::MissingPrefix => {
85                    let lock = LABELS.lock();
86                    let labels = match lock {
87                        Ok(labels) => labels,
88                        Err(err) => err.into_inner(),
89                    };
90                    labels
91                        .hash_of(label)
92                        .ok_or_else(|| FromLabelError::LabelNotFound(String::from(label)))
93                }
94                ParseHashError::ParseError(err) => Err(err.into()),
95            },
96        }
97    }
98
99    /// Searches for the label associated with the hash value. If no label is found, returns
100    /// the hexadecimal value, formatted as `0x0123456789`
101    pub fn to_label(&self) -> String {
102        let lock = LABELS.lock();
103        let labels = match lock {
104            Ok(labels) => labels,
105            Err(err) => err.into_inner(),
106        };
107        labels
108            .label_of(*self)
109            .unwrap_or_else(|| format!("0x{:010x}", self.0))
110    }
111
112    /// Returns the CRC32 part of the hash
113    pub const fn crc(self) -> u32 {
114        self.0 as u32
115    }
116
117    /// Returns the string length part of the hash
118    pub const fn str_len(self) -> u8 {
119        (self.0 >> 32) as u8
120    }
121
122    /// A convenience method provided to access the static label map
123    pub fn label_map() -> Arc<Mutex<LabelMap>> {
124        LABELS.clone()
125    }
126
127    /// Concatenates two Hash40 values, so that the resulting length and CRC would be the same if
128    /// the original data was all hashed together.
129    pub const fn concat(self, other: Self) -> Self {
130        Self(algorithm::hash40_concat(self.0, other.0))
131    }
132
133    /// A convenience method for concatenating a string to a Hash40
134    pub const fn concat_str(self, other: &str) -> Self {
135        self.concat(hash40(other))
136    }
137
138    /// A convenience method for concatenating two Hash40s separated by a path separator
139    pub const fn join_path(self, other: Self) -> Self {
140        self.concat_str("/").concat(other)
141    }
142}
143
144impl FromStr for Hash40 {
145    type Err = FromLabelError;
146
147    fn from_str(f: &str) -> Result<Self, FromLabelError> {
148        Hash40::from_label(f)
149    }
150}
151
152// Hash40 -> string
153impl Display for Hash40 {
154    fn fmt(&self, f: &mut Formatter) -> Result<(), fmtError> {
155        write!(f, "{}", self.to_label())
156    }
157}
158
159impl Deref for Hash40 {
160    type Target = u64;
161
162    fn deref(&self) -> &Self::Target {
163        &self.0
164    }
165}
166
167impl DerefMut for Hash40 {
168    fn deref_mut(&mut self) -> &mut Self::Target {
169        &mut self.0
170    }
171}
172
173impl<R: Read> ReadHash40 for R {
174    fn read_hash40<T: ByteOrder>(&mut self) -> Result<Hash40, io::Error> {
175        Ok(Hash40(self.read_u64::<T>()? & 0xff_ffff_ffff))
176    }
177
178    fn read_hash40_with_meta<T: ByteOrder>(&mut self) -> Result<(Hash40, u32), io::Error> {
179        let long = self.read_u64::<T>()?;
180        Ok((Hash40(long & 0xff_ffff_ffff), (long >> 40) as u32))
181    }
182}
183
184impl<W: Write> WriteHash40 for W {
185    fn write_hash40<T: ByteOrder>(&mut self, hash: Hash40) -> Result<(), io::Error> {
186        self.write_u64::<T>(hash.0)
187    }
188
189    fn write_hash40_with_meta<T: ByteOrder>(
190        &mut self,
191        hash: Hash40,
192        meta: u32,
193    ) -> Result<(), io::Error> {
194        self.write_u64::<T>(hash.0 | (meta as u64) << 40)
195    }
196}
197
198impl Diff for Hash40 {
199    type Repr = Option<Hash40>;
200
201    fn diff(&self, other: &Self) -> Self::Repr {
202        if self == other {
203            None
204        } else {
205            Some(*other)
206        }
207    }
208
209    fn apply(&mut self, diff: &Self::Repr) {
210        if let Some(other) = diff {
211            *self = *other;
212        }
213    }
214
215    fn identity() -> Self {
216        Default::default()
217    }
218}
219
220#[cfg(feature = "serde")]
221impl<'de> de::Visitor<'de> for Hash40Visitor {
222    type Value = Hash40;
223
224    fn expecting(
225        &self,
226        formatter: &mut std::fmt::Formatter,
227    ) -> std::result::Result<(), std::fmt::Error> {
228        formatter.write_str(
229            "A hex-formatted integer hash value, or a string representing for its reversed form",
230        )
231    }
232
233    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
234        Hash40::from_label(&String::from(value)).map_err(de::Error::custom)
235    }
236}
237
238#[cfg(feature = "serde")]
239impl Serialize for Hash40 {
240    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
241        serializer.serialize_str(&self.to_label())
242    }
243}
244
245#[cfg(feature = "serde")]
246impl<'de> Deserialize<'de> for Hash40 {
247    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
248        deserializer.deserialize_str(Hash40Visitor)
249    }
250}
251
252#[cfg(feature = "serde")]
253/// Used to implement serde's Deserialize trait
254struct Hash40Visitor;