use core::{borrow::Borrow, marker::PhantomData};
pub use crate::Hasher;
pub trait PhfKey: Eq {
type ConstKey: ConstKey<PhfKey = Self> + ?Sized;
}
pub trait PhfKeyProxy<PK: ?Sized> {
fn pfh_hash(pk: &PK, state: &mut Hasher);
fn pfh_eq(&self, other: &PK) -> bool;
}
pub trait ConstKey {
type PhfKey: PhfKey<ConstKey = Self> + ?Sized;
}
#[doc(hidden)]
pub struct PrimitiveKey<T: ?Sized>(PhantomData<T>);
macro_rules! prim_impl {
($({$($gen:tt)*})? $t:ty, |$v:ident, $s:ident| $h:expr, |$l:ident, $r:ident| $e:expr) => {
impl$($($gen)*)? PhfKey for $t {
type ConstKey = PrimitiveKey<$t>;
}
impl$($($gen)*)? ConstKey for PrimitiveKey<$t> {
type PhfKey = $t;
}
impl$($($gen)*)? PrimitiveKey<$t> {
pub const fn pfh_hash($v: &<Self as ConstKey>::PhfKey, $s: &mut Hasher) {
$h
}
pub const fn pfh_eq($l: &<Self as ConstKey>::PhfKey, $r: &<Self as ConstKey>::PhfKey) -> bool {
$e
}
}
};
($t:ty, |$v:ident, $s:ident| $h:expr) => {
prim_impl!{$t, |$v, $s| $h, |lhs, rhs| *lhs == *rhs}
};
}
prim_impl! {u8, |v, s| s.write_u8(*v)}
prim_impl! {i8, |v, s| s.write_u8(*v as u8)}
prim_impl! {u16, |v, s| s.write_u16(*v)}
prim_impl! {i16, |v, s| s.write_u16(*v as u16)}
prim_impl! {u32, |v, s| s.write_u32(*v)}
prim_impl! {i32, |v, s| s.write_u32(*v as u32)}
prim_impl! {u64, |v, s| s.write_u64(*v)}
prim_impl! {i64, |v, s| s.write_u64(*v as u64)}
prim_impl! {usize, |v, s| s.write_usize(*v)}
prim_impl! {isize, |v, s| s.write_usize(*v as usize)}
prim_impl! {u128, |v, s| s.write(&v.to_le_bytes())}
prim_impl! {i128, |v, s| s.write(&v.to_le_bytes())}
prim_impl! {[u8], |v, s| s.write(v), |lhs, rhs| {
if lhs.len() != rhs.len() { return false }
let mut i = 0;
while i < lhs.len() {
if lhs[i] != rhs[i] { return false }
i += 1;
}
true
}}
prim_impl! {str, |v, s| s.write(v.as_bytes()), |lhs, rhs| {
let lhs = lhs.as_bytes();
let rhs = rhs.as_bytes();
if lhs.len() != rhs.len() { return false }
let mut i = 0;
while i < lhs.len() {
if lhs[i] != rhs[i] { return false }
i += 1;
}
true
}}
macro_rules! prim_impl_hash {
($($t:ty),* $(,)?) => {$(
impl<PK: ?Sized + Borrow<$t>> PhfKeyProxy<PK> for $t {
#[inline(always)]
fn pfh_hash(pk: &PK, state: &mut Hasher) {
<Self as PhfKey>::ConstKey::pfh_hash(pk.borrow(), state)
}
#[inline(always)]
fn pfh_eq(&self, other: &PK) -> bool {
self == other.borrow()
}
}
)*};
}
prim_impl_hash! {u8, i8, u16, i16, u32, i32, u64, i64, usize, isize, u128, i128, [u8], str}
macro_rules! prim_impl_static {
($($t:ty),* $(,)?) => {$(
prim_impl!{&'static $t, |v, s| PrimitiveKey::<$t>::pfh_hash(*v, s), |lhs, rhs| PrimitiveKey::<$t>::pfh_eq(*lhs, *rhs)}
)*};
}
prim_impl_static! {u8, i8, u16, i16, u32, i32, u64, i64, usize, isize, u128, i128, [u8], str, UncasedStr}
impl<PK: ?Sized, T: ?Sized + PhfKeyProxy<PK>> PhfKeyProxy<PK> for &'_ T {
#[inline(always)]
fn pfh_hash(pk: &PK, state: &mut Hasher) {
T::pfh_hash(pk, state);
}
#[inline(always)]
fn pfh_eq(&self, other: &PK) -> bool {
(*self).pfh_eq(other)
}
}
impl<PK: ?Sized, T: ?Sized + PhfKeyProxy<PK>> PhfKeyProxy<PK> for &'_ mut T {
#[inline(always)]
fn pfh_hash(pk: &PK, state: &mut Hasher) {
T::pfh_hash(pk, state);
}
#[inline(always)]
fn pfh_eq(&self, other: &PK) -> bool {
(**self).pfh_eq(other)
}
}
#[repr(transparent)]
pub struct UncasedStr(pub str);
impl UncasedStr {
#[inline(always)]
pub const fn new(string: &str) -> &UncasedStr {
unsafe { core::mem::transmute(string) }
}
#[inline(always)]
pub const fn as_str(&self) -> &str {
&self.0
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.as_str().len()
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.as_str().is_empty()
}
#[inline]
pub const fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
let mut a = self.as_str().as_bytes();
let mut b = other.as_str().as_bytes();
while let ([first_a, rest_a @ ..], [first_b, rest_b @ ..]) = (a, b) {
if first_a.eq_ignore_ascii_case(first_b) {
a = rest_a;
b = rest_b;
} else {
return false;
}
}
true
}
}
impl PartialEq for UncasedStr {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.eq(other)
}
}
impl Eq for UncasedStr {}
prim_impl! {UncasedStr,
|v, s| {
let mut a = v.as_str().as_bytes();
while let [first_a, rest_a @ ..] = a {
s.write_u8(first_a.to_ascii_lowercase());
a = rest_a;
}
}, |lhs, rhs| lhs.eq(rhs)
}
impl<PK: ?Sized + Borrow<str>> PhfKeyProxy<PK> for UncasedStr {
#[inline(always)]
fn pfh_hash(pk: &PK, state: &mut Hasher) {
<Self as PhfKey>::ConstKey::pfh_hash(UncasedStr::new(pk.borrow()), state)
}
#[inline(always)]
fn pfh_eq(&self, other: &PK) -> bool {
self == UncasedStr::new(other.borrow())
}
}