comemo/prehashed.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
use std::any::Any;
use std::cmp::{Ord, PartialOrd};
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use siphasher::sip128::{Hasher128, SipHasher13};
/// A wrapper type with precomputed hash.
///
/// This is useful if you want to pass large values of `T` to memoized
/// functions. Especially recursive structures like trees benefit from
/// intermediate prehashed nodes.
///
/// Note that for a value `v` of type `T`, `hash(v)` is not necessarily equal to
/// `hash(Prehashed::new(v))`. Writing the precomputed hash into a hasher's
/// state produces different output than writing the value's parts directly.
/// However, that seldomly matters as you are typically either dealing with
/// values of type `T` or with values of type `Prehashed<T>`, not a mix of both.
///
/// # Equality
/// Because comemo uses high-quality 128 bit hashes in all places, the risk of a
/// hash collision is reduced to an absolute minimum. Therefore, this type
/// additionally provides `PartialEq` and `Eq` implementations that compare by
/// hash instead of by value. For this to be correct, your hash implementation
/// **must feed all information relevant to the `PartialEq` impl to the
/// hasher.**
#[derive(Copy, Clone)]
pub struct Prehashed<T: ?Sized> {
/// The precomputed hash.
hash: u128,
/// The wrapped item.
item: T,
}
impl<T: Hash + 'static> Prehashed<T> {
/// Compute an item's hash and wrap it.
#[inline]
pub fn new(item: T) -> Self {
Self { hash: hash(&item), item }
}
/// Return the wrapped value.
#[inline]
pub fn into_inner(self) -> T {
self.item
}
/// Update the wrapped value and recompute the hash.
#[inline]
pub fn update<F, U>(&mut self, f: F) -> U
where
F: FnOnce(&mut T) -> U,
{
let output = f(&mut self.item);
self.hash = hash(&self.item);
output
}
}
/// Hash the item.
fn hash<T: Hash + 'static>(item: &T) -> u128 {
// Also hash the TypeId because the type might be converted
// through an unsized coercion.
let mut state = SipHasher13::new();
item.type_id().hash(&mut state);
item.hash(&mut state);
state.finish128().as_u128()
}
impl<T: ?Sized> Deref for Prehashed<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.item
}
}
impl<T: Hash + 'static> From<T> for Prehashed<T> {
#[inline]
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: ?Sized> Hash for Prehashed<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u128(self.hash);
}
}
impl<T: Debug + ?Sized> Debug for Prehashed<T> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.item.fmt(f)
}
}
impl<T: Default + Hash + 'static> Default for Prehashed<T> {
#[inline]
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: ?Sized> Eq for Prehashed<T> {}
impl<T: ?Sized> PartialEq for Prehashed<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.hash == other.hash
}
}
impl<T: Ord + ?Sized> Ord for Prehashed<T> {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.item.cmp(&other.item)
}
}
impl<T: PartialOrd + ?Sized> PartialOrd for Prehashed<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.item.partial_cmp(&other.item)
}
}