#![doc(html_root_url = "https://docs.rs/phf_shared/0.14.0")]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
extern crate std as core;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::num::Wrapping;
use siphasher::sip128::{Hash128, Hasher128, SipHasher13};
mod hasher;
use hasher::PortableSipHasher;
#[cfg(feature = "ptrhash")]
pub mod ptrhash;
#[non_exhaustive]
pub struct Hashes {
pub g: u32,
pub f1: u32,
pub f2: u32,
}
pub type HashKey = u64;
#[inline]
pub fn displace(f1: u32, f2: u32, d1: u32, d2: u32) -> u32 {
(Wrapping(d2) + Wrapping(f1) * Wrapping(d1) + Wrapping(f2)).0
}
#[inline]
pub fn hash<T: ?Sized + PhfHash>(x: &T, key: &HashKey) -> Hashes {
let mut hasher = PortableSipHasher::new(SipHasher13::new_with_keys(0, *key));
x.phf_hash(&mut hasher);
let Hash128 {
h1: lower,
h2: upper,
} = hasher.finish128();
Hashes {
g: (lower >> 32) as u32,
f1: lower as u32,
f2: upper as u32,
}
}
#[inline]
pub fn get_index(hashes: &Hashes, disps: &[(u32, u32)], len: usize) -> u32 {
let (d1, d2) = disps[(hashes.g % (disps.len() as u32)) as usize];
displace(hashes.f1, hashes.f2, d1, d2) % (len as u32)
}
pub trait PhfHash {
fn phf_hash<H: Hasher>(&self, state: &mut H);
fn phf_hash_slice<H: Hasher>(data: &[Self], state: &mut H)
where
Self: Sized,
{
state.write_u64(data.len() as u64);
for piece in data {
piece.phf_hash(state);
}
}
}
pub trait FmtConst {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
pub trait PhfBorrow<B: ?Sized> {
fn borrow(&self) -> &B;
}
pub trait PhfEq<B: ?Sized> {
fn phf_eq(&self, other: &B) -> bool;
}
impl<K, B: ?Sized + Eq> PhfEq<B> for K
where
K: PhfBorrow<B>,
{
fn phf_eq(&self, other: &B) -> bool {
self.borrow() == other
}
}
macro_rules! delegate_debug (
($ty:ty) => {
impl FmtConst for $ty {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
}
);
delegate_debug!(str);
delegate_debug!(char);
delegate_debug!(u8);
delegate_debug!(i8);
delegate_debug!(u16);
delegate_debug!(i16);
delegate_debug!(u32);
delegate_debug!(i32);
delegate_debug!(u64);
delegate_debug!(i64);
delegate_debug!(usize);
delegate_debug!(isize);
delegate_debug!(u128);
delegate_debug!(i128);
delegate_debug!(bool);
macro_rules! impl_reflexive(
($($t:ty),*) => (
$(impl PhfBorrow<$t> for $t {
fn borrow(&self) -> &$t {
self
}
})*
)
);
impl_reflexive!(
str,
char,
u8,
i8,
u16,
i16,
u32,
i32,
u64,
i64,
usize,
isize,
u128,
i128,
bool,
[u8]
);
#[cfg(feature = "std")]
impl PhfBorrow<str> for String {
fn borrow(&self) -> &str {
self
}
}
#[cfg(feature = "std")]
delegate_debug!(String);
#[cfg(feature = "std")]
impl PhfHash for String {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
(**self).phf_hash(state)
}
}
#[cfg(feature = "std")]
impl<T: PhfHash> PhfHash for Vec<T> {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.as_slice().phf_hash(state)
}
}
impl<'a, T: 'a + PhfHash + ?Sized> PhfHash for &'a T {
fn phf_hash<H: Hasher>(&self, state: &mut H) {
(*self).phf_hash(state)
}
}
impl<'a, T: 'a + FmtConst + ?Sized> FmtConst for &'a T {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(*self).fmt_const(f)
}
}
impl PhfBorrow<str> for &str {
fn borrow(&self) -> &str {
self
}
}
#[cfg(feature = "std")]
impl<T> PhfBorrow<[T]> for Vec<T> {
fn borrow(&self) -> &[T] {
self
}
}
impl<T> PhfBorrow<[T]> for &[T] {
fn borrow(&self) -> &[T] {
self
}
}
impl<T, const N: usize> PhfBorrow<[T; N]> for &[T; N] {
fn borrow(&self) -> &[T; N] {
self
}
}
impl PhfHash for str {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().phf_hash(state)
}
}
#[cfg(feature = "unicase")]
impl<S> PhfHash for unicase::UniCase<S>
where
unicase::UniCase<S>: Hash,
{
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}
#[cfg(feature = "unicase")]
impl<S> FmtConst for unicase::UniCase<S>
where
S: AsRef<str>,
{
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_ascii() {
f.write_str("UniCase::ascii(")?;
} else {
f.write_str("UniCase::unicode(")?;
}
self.as_ref().fmt_const(f)?;
f.write_str(")")
}
}
#[cfg(feature = "unicase")]
impl<'b, 'a: 'b, S: ?Sized + 'a> PhfBorrow<unicase::UniCase<&'b S>> for unicase::UniCase<&'a S> {
fn borrow(&self) -> &unicase::UniCase<&'b S> {
self
}
}
#[cfg(feature = "unicase")]
impl<S> PhfHash for unicase::Ascii<S>
where
unicase::Ascii<S>: Hash,
{
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}
#[cfg(feature = "unicase")]
impl<S> FmtConst for unicase::Ascii<S>
where
S: AsRef<str>,
{
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Ascii::new(")?;
self.as_ref().fmt_const(f)?;
f.write_str(")")
}
}
#[cfg(feature = "unicase")]
impl<'b, 'a: 'b, S: ?Sized + 'a> PhfBorrow<unicase::Ascii<&'b S>> for unicase::Ascii<&'a S> {
fn borrow(&self) -> &unicase::Ascii<&'b S> {
self
}
}
#[cfg(feature = "uncased")]
impl PhfHash for uncased::UncasedStr {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}
#[cfg(feature = "uncased")]
impl FmtConst for uncased::UncasedStr {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("UncasedStr::new(")?;
self.as_str().fmt_const(f)?;
f.write_str(")")
}
}
#[cfg(feature = "uncased")]
impl PhfBorrow<uncased::UncasedStr> for &uncased::UncasedStr {
fn borrow(&self) -> &uncased::UncasedStr {
self
}
}
macro_rules! integer_impl (
($t:ty) => (
impl PhfHash for $t {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state);
}
}
)
);
integer_impl!(u16);
integer_impl!(i16);
integer_impl!(u32);
integer_impl!(i32);
integer_impl!(u64);
integer_impl!(i64);
integer_impl!(usize);
integer_impl!(isize);
integer_impl!(u128);
integer_impl!(i128);
macro_rules! single_byte_impl (
($t:ty) => (
impl PhfHash for $t {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state);
}
#[inline]
fn phf_hash_slice<H: Hasher>(slice: &[$t], state: &mut H) {
state.write_u64(slice.len() as u64);
state.write(unsafe { &*(slice as *const [$t] as *const [u8]) });
}
}
)
);
single_byte_impl!(u8);
single_byte_impl!(i8);
single_byte_impl!(bool);
impl PhfHash for char {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
(*self as u32).phf_hash(state)
}
}
impl<T: PhfHash, const N: usize> PhfHash for [T; N] {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
<[T]>::phf_hash(self, state);
}
}
impl<T: PhfHash> PhfHash for [T] {
#[inline]
fn phf_hash<H: Hasher>(&self, state: &mut H) {
T::phf_hash_slice(self, state);
}
}
fn fmt_slice<T: core::fmt::Debug>(slice: &[T], f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "&{:?}", slice)
}
fn fmt_array<T: core::fmt::Debug>(array: &[T], f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", array)
}
macro_rules! slice_impl (
($t:ty) => (
impl FmtConst for [$t] {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_slice(self, f)
}
}
#[cfg(feature = "std")]
impl FmtConst for Vec<$t> {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_slice().fmt_const(f)
}
}
)
);
macro_rules! array_impl (
($t:ty) => (
impl<const N: usize> FmtConst for [$t; N] {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_array(self, f)
}
}
impl<const N: usize> PhfBorrow<[$t]> for [$t; N] {
fn borrow(&self) -> &[$t] {
self
}
}
)
);
slice_impl!(u8);
slice_impl!(i8);
slice_impl!(u16);
slice_impl!(i16);
slice_impl!(u32);
slice_impl!(i32);
slice_impl!(u64);
slice_impl!(i64);
slice_impl!(usize);
slice_impl!(isize);
slice_impl!(u128);
slice_impl!(i128);
slice_impl!(bool);
slice_impl!(char);
array_impl!(u8);
array_impl!(i8);
array_impl!(u16);
array_impl!(i16);
array_impl!(u32);
array_impl!(i32);
array_impl!(u64);
array_impl!(i64);
array_impl!(usize);
array_impl!(isize);
array_impl!(u128);
array_impl!(i128);
array_impl!(bool);
array_impl!(char);
macro_rules! tuple_impl {
($($t:ident),+) => {
impl<$($t: PhfHash),+> PhfHash for ($($t,)+) {
fn phf_hash<HS: Hasher>(&self, state: &mut HS) {
#[allow(non_snake_case)]
let ($($t,)+) = self;
$(
$t.phf_hash(state);
)+
}
}
impl<$($t: FmtConst),+> FmtConst for ($($t,)+) {
fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(non_snake_case)]
let ($($t,)+) = self;
write!(f, "(")?;
let mut first = true;
$(
if !core::mem::replace(&mut first, false) {
write!(f, ", ")?;
}
$t.fmt_const(f)?;
)+
write!(f, ")")
}
}
};
}
macro_rules! tuple_eq_impl {
($(($left_ty:ident, $left:ident, $right_ty:ident, $right:ident)),+) => {
impl<$($left_ty, $right_ty),+> PhfEq<($($right_ty,)+)> for ($($left_ty,)+)
where
$($left_ty: PartialEq<$right_ty>),+
{
fn phf_eq(&self, other: &($($right_ty,)+)) -> bool {
let ($($left,)+) = self;
let ($($right,)+) = other;
true $(&& $left == $right)+
}
}
};
}
tuple_impl!(A);
tuple_impl!(A, B);
tuple_impl!(A, B, C);
tuple_impl!(A, B, C, D);
tuple_impl!(A, B, C, D, E);
tuple_impl!(A, B, C, D, E, F);
tuple_impl!(A, B, C, D, E, F, G);
tuple_impl!(A, B, C, D, E, F, G, HT);
tuple_impl!(A, B, C, D, E, F, G, HT, I);
tuple_impl!(A, B, C, D, E, F, G, HT, I, J);
tuple_impl!(A, B, C, D, E, F, G, HT, I, J, K);
tuple_impl!(A, B, C, D, E, F, G, HT, I, J, K, L);
tuple_eq_impl!((A, a, AT, at));
tuple_eq_impl!((A, a, AT, at), (B, b, BT, bt));
tuple_eq_impl!((A, a, AT, at), (B, b, BT, bt), (C, c, CT, ct));
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft),
(G, g, GT, gt)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft),
(G, g, GT, gt),
(H, h, HT, ht)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft),
(G, g, GT, gt),
(H, h, HT, ht),
(I, i, IT, it)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft),
(G, g, GT, gt),
(H, h, HT, ht),
(I, i, IT, it),
(J, j, JT, jt)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft),
(G, g, GT, gt),
(H, h, HT, ht),
(I, i, IT, it),
(J, j, JT, jt),
(K, k, KT, kt)
);
tuple_eq_impl!(
(A, a, AT, at),
(B, b, BT, bt),
(C, c, CT, ct),
(D, d, DT, dt),
(E, e, ET, et),
(F, ff, FT, ft),
(G, g, GT, gt),
(H, h, HT, ht),
(I, i, IT, it),
(J, j, JT, jt),
(K, k, KT, kt),
(L, l, LT, lt)
);
#[cfg(test)]
mod tests {
use super::*;
#[derive(PartialEq, Debug)]
enum HashCall {
Bytes(Vec<u8>),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
Usize(usize),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
Isize(isize),
}
#[derive(PartialEq, Debug)]
struct TestHasher {
calls: Vec<HashCall>,
}
impl Hasher for TestHasher {
fn finish(&self) -> u64 {
panic!("only used for tests");
}
fn write(&mut self, bytes: &[u8]) {
self.calls.push(HashCall::Bytes(bytes.to_vec()));
}
fn write_u8(&mut self, i: u8) {
self.calls.push(HashCall::U8(i));
}
fn write_u16(&mut self, i: u16) {
self.calls.push(HashCall::U16(i));
}
fn write_u32(&mut self, i: u32) {
self.calls.push(HashCall::U32(i));
}
fn write_u64(&mut self, i: u64) {
self.calls.push(HashCall::U64(i));
}
fn write_u128(&mut self, i: u128) {
self.calls.push(HashCall::U128(i));
}
fn write_usize(&mut self, i: usize) {
self.calls.push(HashCall::Usize(i));
}
fn write_i8(&mut self, i: i8) {
self.calls.push(HashCall::I8(i));
}
fn write_i16(&mut self, i: i16) {
self.calls.push(HashCall::I16(i));
}
fn write_i32(&mut self, i: i32) {
self.calls.push(HashCall::I32(i));
}
fn write_i64(&mut self, i: i64) {
self.calls.push(HashCall::I64(i));
}
fn write_i128(&mut self, i: i128) {
self.calls.push(HashCall::I128(i));
}
fn write_isize(&mut self, i: isize) {
self.calls.push(HashCall::Isize(i));
}
}
fn test_hash<T: PhfHash>(x: T) -> Vec<HashCall> {
let mut state = TestHasher { calls: Vec::new() };
x.phf_hash(&mut state);
state.calls
}
#[test]
fn byte_slices_are_hashed_efficiently() {
assert_eq!(
test_hash(&[1u8, 2, 3]),
[HashCall::U64(3), HashCall::Bytes([1, 2, 3].to_vec())]
);
assert_eq!(
test_hash(&[1i8, 2, 3]),
[HashCall::U64(3), HashCall::Bytes([1, 2, 3].to_vec())]
);
assert_eq!(
test_hash(&[false, true]),
[HashCall::U64(2), HashCall::Bytes([0, 1].to_vec())]
);
}
#[test]
fn slices_and_arrays_are_hashed_consistently() {
assert_eq!(test_hash(&[1u8, 2, 3]), test_hash(&[1u8, 2, 3][..]));
assert_eq!(test_hash(&[1u16, 2, 3]), test_hash(&[1u16, 2, 3][..]));
}
#[test]
fn array_reference_borrow_is_generic() {
fn assert_borrow<K, B: ?Sized>()
where
K: PhfBorrow<B>,
{
}
assert_borrow::<&[u32; 2], [u32; 2]>();
assert_borrow::<&[bool; 2], [bool; 2]>();
assert_borrow::<&[char; 2], [char; 2]>();
}
#[test]
fn tuple_eq_allows_shorter_reference_lifetimes() {
fn assert_ref_tuple<'a>(key: &(&'a str, &'a str)) -> bool
where
(&'static str, &'static str): PhfEq<(&'a str, &'a str)>,
{
("a", "b").phf_eq(key)
}
fn assert_mixed_tuple<'a>(key: &(u32, &'a str)) -> bool
where
(u32, &'static str): PhfEq<(u32, &'a str)>,
{
(1, "a").phf_eq(key)
}
let a = String::from("a");
let b = String::from("b");
assert!(assert_ref_tuple(&(a.as_str(), b.as_str())));
assert!(assert_mixed_tuple(&(1, a.as_str())));
}
#[test]
fn variable_width_slice_elements_are_delimited() {
assert_ne!(test_hash(&["ab", "c"]), test_hash(&["a", "bc"]));
let key = 0;
let left = hash(&["ab", "c"], &key);
let right = hash(&["a", "bc"], &key);
assert!(
(left.g, left.f1, left.f2) != (right.g, right.f1, right.f2),
"different string arrays must not produce identical PHF hashes"
);
}
}