Skip to main content

phf_shared/
lib.rs

1//! See [the `phf` crate's documentation][phf] for details.
2//!
3//! [phf]: https://docs.rs/phf
4
5#![doc(html_root_url = "https://docs.rs/phf_shared/0.14.0")]
6#![cfg_attr(not(feature = "std"), no_std)]
7
8#[cfg(feature = "std")]
9extern crate std as core;
10
11use core::fmt;
12use core::hash::{Hash, Hasher};
13use core::num::Wrapping;
14use siphasher::sip128::{Hash128, Hasher128, SipHasher13};
15
16mod hasher;
17use hasher::PortableSipHasher;
18
19#[cfg(feature = "ptrhash")]
20pub mod ptrhash;
21
22#[non_exhaustive]
23pub struct Hashes {
24    pub g: u32,
25    pub f1: u32,
26    pub f2: u32,
27}
28
29/// A central typedef for hash keys
30///
31/// Makes experimentation easier by only needing to be updated here.
32pub type HashKey = u64;
33
34#[inline]
35pub fn displace(f1: u32, f2: u32, d1: u32, d2: u32) -> u32 {
36    (Wrapping(d2) + Wrapping(f1) * Wrapping(d1) + Wrapping(f2)).0
37}
38
39/// `key` is from `phf_generator::HashState`.
40#[inline]
41pub fn hash<T: ?Sized + PhfHash>(x: &T, key: &HashKey) -> Hashes {
42    let mut hasher = PortableSipHasher::new(SipHasher13::new_with_keys(0, *key));
43    x.phf_hash(&mut hasher);
44
45    let Hash128 {
46        h1: lower,
47        h2: upper,
48    } = hasher.finish128();
49
50    Hashes {
51        g: (lower >> 32) as u32,
52        f1: lower as u32,
53        f2: upper as u32,
54    }
55}
56
57/// Return an index into `phf_generator::HashState::map`.
58///
59/// * `hash` is from `hash()` in this crate.
60/// * `disps` is from `phf_generator::HashState::disps`.
61/// * `len` is the length of `phf_generator::HashState::map`.
62#[inline]
63pub fn get_index(hashes: &Hashes, disps: &[(u32, u32)], len: usize) -> u32 {
64    let (d1, d2) = disps[(hashes.g % (disps.len() as u32)) as usize];
65    displace(hashes.f1, hashes.f2, d1, d2) % (len as u32)
66}
67
68/// A trait implemented by types which can be used in PHF data structures.
69///
70/// This differs from the standard library's `Hash` trait in that `PhfHash`'s
71/// results must be architecture independent so that hashes will be consistent
72/// between the host and target when cross compiling.
73pub trait PhfHash {
74    /// Feeds the value into the state given, updating the hasher as necessary.
75    fn phf_hash<H: Hasher>(&self, state: &mut H);
76
77    /// Feeds a slice of this type into the state provided.
78    fn phf_hash_slice<H: Hasher>(data: &[Self], state: &mut H)
79    where
80        Self: Sized,
81    {
82        state.write_u64(data.len() as u64);
83        for piece in data {
84            piece.phf_hash(state);
85        }
86    }
87}
88
89/// Trait for printing types with `const` constructors, used by `phf_codegen` and `phf_macros`.
90pub trait FmtConst {
91    /// Print a `const` expression representing this value.
92    fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
93}
94
95/// Identical to `std::borrow::Borrow` except omitting blanket impls to facilitate other
96/// borrowing patterns.
97///
98/// The same semantic requirements apply:
99///
100/// > In particular `Eq`, `Ord` and `Hash` must be equivalent for borrowed and owned values:
101/// > `x.borrow() == y.borrow()` should give the same result as `x == y`.
102///
103/// (This crate's lookup API only requires `Eq`, `PhfHash`, and [`PhfEq`],
104/// however.)
105///
106/// ### Motivation
107/// The conventional signature for lookup methods on collections looks something like this:
108///
109/// ```ignore
110/// impl<K, V> Map<K, V> where K: PhfHash + Eq {
111///     fn get<T: ?Sized>(&self, key: &T) -> Option<&V> where T: PhfHash + Eq, K: Borrow<T> {
112///         ...
113///     }
114/// }
115/// ```
116///
117/// This allows the key type used for lookup to be different than the key stored in the map so for
118/// example you can use `&str` to look up a value in a `Map<String, _>`. However, this runs into
119/// a problem in the case where `T` and `K` are both a `Foo<_>` type constructor but
120/// the contained type is different (even being the same type with different lifetimes).
121///
122/// The main issue for this crate's API is that, with this method signature, you cannot perform a
123/// lookup on a `Map<UniCase<&'static str>, _>` with a `UniCase<&'a str>` where `'a` is not
124/// `'static`; there is no impl of `Borrow` that resolves to
125/// `impl Borrow<UniCase<'a>> for UniCase<&'static str>` and one cannot be added either because of
126/// all the blanket impls.
127///
128/// Instead, this trait is implemented conservatively, without blanket impls, so that impls like
129/// this may be added. [`PhfEq`] uses these impls for borrowed key forms that can be represented
130/// as a reference to the lookup key type. This is feasible since the set of types that implement
131/// `PhfHash` is intentionally small.
132///
133/// This likely won't be fixable with specialization alone but will require full support for lattice
134/// impls since we technically want to add overlapping blanket impls.
135pub trait PhfBorrow<B: ?Sized> {
136    /// Convert a reference to `self` to a reference to the borrowed type.
137    fn borrow(&self) -> &B;
138}
139
140/// Trait for comparing stored PHF keys with runtime lookup keys.
141///
142/// Lookup keys must hash the same way as the stored key they compare equal to.
143/// Most borrowed key forms use the blanket implementation based on
144/// [`PhfBorrow`]. Tuples are implemented separately so references inside a
145/// tuple can use shorter lifetimes at lookup time. Tuple impls are provided up
146/// to 12 elements.
147pub trait PhfEq<B: ?Sized> {
148    /// Returns `true` if `self` and `other` are equivalent PHF keys.
149    fn phf_eq(&self, other: &B) -> bool;
150}
151
152impl<K, B: ?Sized + Eq> PhfEq<B> for K
153where
154    K: PhfBorrow<B>,
155{
156    fn phf_eq(&self, other: &B) -> bool {
157        self.borrow() == other
158    }
159}
160
161/// Create an impl of `FmtConst` delegating to `fmt::Debug` for types that can deal with it.
162///
163/// Ideally with specialization this could be just one default impl and then specialized where
164/// it doesn't apply.
165macro_rules! delegate_debug (
166    ($ty:ty) => {
167        impl FmtConst for $ty {
168            fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169                write!(f, "{:?}", self)
170            }
171        }
172    }
173);
174
175delegate_debug!(str);
176delegate_debug!(char);
177delegate_debug!(u8);
178delegate_debug!(i8);
179delegate_debug!(u16);
180delegate_debug!(i16);
181delegate_debug!(u32);
182delegate_debug!(i32);
183delegate_debug!(u64);
184delegate_debug!(i64);
185delegate_debug!(usize);
186delegate_debug!(isize);
187delegate_debug!(u128);
188delegate_debug!(i128);
189delegate_debug!(bool);
190
191/// `impl PhfBorrow<T> for T`
192macro_rules! impl_reflexive(
193    ($($t:ty),*) => (
194        $(impl PhfBorrow<$t> for $t {
195            fn borrow(&self) -> &$t {
196                self
197            }
198        })*
199    )
200);
201
202impl_reflexive!(
203    str,
204    char,
205    u8,
206    i8,
207    u16,
208    i16,
209    u32,
210    i32,
211    u64,
212    i64,
213    usize,
214    isize,
215    u128,
216    i128,
217    bool,
218    [u8]
219);
220
221#[cfg(feature = "std")]
222impl PhfBorrow<str> for String {
223    fn borrow(&self) -> &str {
224        self
225    }
226}
227
228#[cfg(feature = "std")]
229delegate_debug!(String);
230
231#[cfg(feature = "std")]
232impl PhfHash for String {
233    #[inline]
234    fn phf_hash<H: Hasher>(&self, state: &mut H) {
235        (**self).phf_hash(state)
236    }
237}
238
239#[cfg(feature = "std")]
240impl<T: PhfHash> PhfHash for Vec<T> {
241    #[inline]
242    fn phf_hash<H: Hasher>(&self, state: &mut H) {
243        self.as_slice().phf_hash(state)
244    }
245}
246
247impl<'a, T: 'a + PhfHash + ?Sized> PhfHash for &'a T {
248    fn phf_hash<H: Hasher>(&self, state: &mut H) {
249        (*self).phf_hash(state)
250    }
251}
252
253impl<'a, T: 'a + FmtConst + ?Sized> FmtConst for &'a T {
254    fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255        (*self).fmt_const(f)
256    }
257}
258
259impl PhfBorrow<str> for &str {
260    fn borrow(&self) -> &str {
261        self
262    }
263}
264
265#[cfg(feature = "std")]
266impl<T> PhfBorrow<[T]> for Vec<T> {
267    fn borrow(&self) -> &[T] {
268        self
269    }
270}
271
272impl<T> PhfBorrow<[T]> for &[T] {
273    fn borrow(&self) -> &[T] {
274        self
275    }
276}
277
278impl<T, const N: usize> PhfBorrow<[T; N]> for &[T; N] {
279    fn borrow(&self) -> &[T; N] {
280        self
281    }
282}
283
284impl PhfHash for str {
285    #[inline]
286    fn phf_hash<H: Hasher>(&self, state: &mut H) {
287        self.as_bytes().phf_hash(state)
288    }
289}
290
291#[cfg(feature = "unicase")]
292impl<S> PhfHash for unicase::UniCase<S>
293where
294    unicase::UniCase<S>: Hash,
295{
296    #[inline]
297    fn phf_hash<H: Hasher>(&self, state: &mut H) {
298        self.hash(state)
299    }
300}
301
302#[cfg(feature = "unicase")]
303impl<S> FmtConst for unicase::UniCase<S>
304where
305    S: AsRef<str>,
306{
307    fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        if self.is_ascii() {
309            f.write_str("UniCase::ascii(")?;
310        } else {
311            f.write_str("UniCase::unicode(")?;
312        }
313
314        self.as_ref().fmt_const(f)?;
315        f.write_str(")")
316    }
317}
318
319#[cfg(feature = "unicase")]
320impl<'b, 'a: 'b, S: ?Sized + 'a> PhfBorrow<unicase::UniCase<&'b S>> for unicase::UniCase<&'a S> {
321    fn borrow(&self) -> &unicase::UniCase<&'b S> {
322        self
323    }
324}
325
326#[cfg(feature = "unicase")]
327impl<S> PhfHash for unicase::Ascii<S>
328where
329    unicase::Ascii<S>: Hash,
330{
331    #[inline]
332    fn phf_hash<H: Hasher>(&self, state: &mut H) {
333        self.hash(state)
334    }
335}
336
337#[cfg(feature = "unicase")]
338impl<S> FmtConst for unicase::Ascii<S>
339where
340    S: AsRef<str>,
341{
342    fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343        f.write_str("Ascii::new(")?;
344        self.as_ref().fmt_const(f)?;
345        f.write_str(")")
346    }
347}
348
349#[cfg(feature = "unicase")]
350impl<'b, 'a: 'b, S: ?Sized + 'a> PhfBorrow<unicase::Ascii<&'b S>> for unicase::Ascii<&'a S> {
351    fn borrow(&self) -> &unicase::Ascii<&'b S> {
352        self
353    }
354}
355
356#[cfg(feature = "uncased")]
357impl PhfHash for uncased::UncasedStr {
358    #[inline]
359    fn phf_hash<H: Hasher>(&self, state: &mut H) {
360        self.hash(state)
361    }
362}
363
364#[cfg(feature = "uncased")]
365impl FmtConst for uncased::UncasedStr {
366    fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        f.write_str("UncasedStr::new(")?;
368        self.as_str().fmt_const(f)?;
369        f.write_str(")")
370    }
371}
372
373#[cfg(feature = "uncased")]
374impl PhfBorrow<uncased::UncasedStr> for &uncased::UncasedStr {
375    fn borrow(&self) -> &uncased::UncasedStr {
376        self
377    }
378}
379
380macro_rules! integer_impl (
381    ($t:ty) => (
382        impl PhfHash for $t {
383            #[inline]
384            fn phf_hash<H: Hasher>(&self, state: &mut H) {
385                self.hash(state);
386            }
387
388            // `phf_hash_slice` cannot use `write` due to possible differences in endianness.
389        }
390    )
391);
392
393integer_impl!(u16);
394integer_impl!(i16);
395integer_impl!(u32);
396integer_impl!(i32);
397integer_impl!(u64);
398integer_impl!(i64);
399integer_impl!(usize);
400integer_impl!(isize);
401integer_impl!(u128);
402integer_impl!(i128);
403
404macro_rules! single_byte_impl (
405    ($t:ty) => (
406        impl PhfHash for $t {
407            #[inline]
408            fn phf_hash<H: Hasher>(&self, state: &mut H) {
409                self.hash(state);
410            }
411
412            #[inline]
413            fn phf_hash_slice<H: Hasher>(slice: &[$t], state: &mut H) {
414                // There is sadly no `[i8]::as_bytes` or `[bool]::as_bytes`.
415                state.write_u64(slice.len() as u64);
416                state.write(unsafe { &*(slice as *const [$t] as *const [u8]) });
417            }
418        }
419    )
420);
421
422single_byte_impl!(u8);
423single_byte_impl!(i8);
424// https://doc.rust-lang.org/reference/types/boolean.html#r-type.bool.repr guarantees that `bool`
425// has a fixed layout.
426single_byte_impl!(bool);
427
428impl PhfHash for char {
429    #[inline]
430    fn phf_hash<H: Hasher>(&self, state: &mut H) {
431        (*self as u32).phf_hash(state)
432    }
433}
434
435impl<T: PhfHash, const N: usize> PhfHash for [T; N] {
436    #[inline]
437    fn phf_hash<H: Hasher>(&self, state: &mut H) {
438        <[T]>::phf_hash(self, state);
439    }
440}
441
442impl<T: PhfHash> PhfHash for [T] {
443    #[inline]
444    fn phf_hash<H: Hasher>(&self, state: &mut H) {
445        T::phf_hash_slice(self, state);
446    }
447}
448
449// minimize duplicated code since formatting drags in quite a bit
450fn fmt_slice<T: core::fmt::Debug>(slice: &[T], f: &mut fmt::Formatter<'_>) -> fmt::Result {
451    // slices need a leading reference
452    write!(f, "&{:?}", slice)
453}
454
455fn fmt_array<T: core::fmt::Debug>(array: &[T], f: &mut fmt::Formatter<'_>) -> fmt::Result {
456    write!(f, "{:?}", array)
457}
458
459macro_rules! slice_impl (
460    ($t:ty) => (
461        impl FmtConst for [$t] {
462            fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463                fmt_slice(self, f)
464            }
465        }
466
467        #[cfg(feature = "std")]
468        impl FmtConst for Vec<$t> {
469            fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470                self.as_slice().fmt_const(f)
471            }
472        }
473    )
474);
475
476macro_rules! array_impl (
477    ($t:ty) => (
478        impl<const N: usize> FmtConst for [$t; N] {
479            fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480                fmt_array(self, f)
481            }
482        }
483
484        impl<const N: usize> PhfBorrow<[$t]> for [$t; N] {
485            fn borrow(&self) -> &[$t] {
486                self
487            }
488        }
489    )
490);
491
492slice_impl!(u8);
493slice_impl!(i8);
494slice_impl!(u16);
495slice_impl!(i16);
496slice_impl!(u32);
497slice_impl!(i32);
498slice_impl!(u64);
499slice_impl!(i64);
500slice_impl!(usize);
501slice_impl!(isize);
502slice_impl!(u128);
503slice_impl!(i128);
504slice_impl!(bool);
505slice_impl!(char);
506
507array_impl!(u8);
508array_impl!(i8);
509array_impl!(u16);
510array_impl!(i16);
511array_impl!(u32);
512array_impl!(i32);
513array_impl!(u64);
514array_impl!(i64);
515array_impl!(usize);
516array_impl!(isize);
517array_impl!(u128);
518array_impl!(i128);
519array_impl!(bool);
520array_impl!(char);
521
522macro_rules! tuple_impl {
523    ($($t:ident),+) => {
524        impl<$($t: PhfHash),+> PhfHash for ($($t,)+) {
525            fn phf_hash<HS: Hasher>(&self, state: &mut HS) {
526                #[allow(non_snake_case)]
527                let ($($t,)+) = self;
528                $(
529                    $t.phf_hash(state);
530                )+
531            }
532        }
533
534        impl<$($t: FmtConst),+> FmtConst for ($($t,)+) {
535            fn fmt_const(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536                #[allow(non_snake_case)]
537                let ($($t,)+) = self;
538                write!(f, "(")?;
539                let mut first = true;
540                $(
541                    if !core::mem::replace(&mut first, false) {
542                        write!(f, ", ")?;
543                    }
544                    $t.fmt_const(f)?;
545                )+
546                write!(f, ")")
547            }
548        }
549    };
550}
551
552macro_rules! tuple_eq_impl {
553    ($(($left_ty:ident, $left:ident, $right_ty:ident, $right:ident)),+) => {
554        impl<$($left_ty, $right_ty),+> PhfEq<($($right_ty,)+)> for ($($left_ty,)+)
555        where
556            $($left_ty: PartialEq<$right_ty>),+
557        {
558            fn phf_eq(&self, other: &($($right_ty,)+)) -> bool {
559                let ($($left,)+) = self;
560                let ($($right,)+) = other;
561                true $(&& $left == $right)+
562            }
563        }
564    };
565}
566
567tuple_impl!(A);
568tuple_impl!(A, B);
569tuple_impl!(A, B, C);
570tuple_impl!(A, B, C, D);
571tuple_impl!(A, B, C, D, E);
572tuple_impl!(A, B, C, D, E, F);
573tuple_impl!(A, B, C, D, E, F, G);
574tuple_impl!(A, B, C, D, E, F, G, HT);
575tuple_impl!(A, B, C, D, E, F, G, HT, I);
576tuple_impl!(A, B, C, D, E, F, G, HT, I, J);
577tuple_impl!(A, B, C, D, E, F, G, HT, I, J, K);
578tuple_impl!(A, B, C, D, E, F, G, HT, I, J, K, L);
579
580tuple_eq_impl!((A, a, AT, at));
581tuple_eq_impl!((A, a, AT, at), (B, b, BT, bt));
582tuple_eq_impl!((A, a, AT, at), (B, b, BT, bt), (C, c, CT, ct));
583tuple_eq_impl!(
584    (A, a, AT, at),
585    (B, b, BT, bt),
586    (C, c, CT, ct),
587    (D, d, DT, dt)
588);
589tuple_eq_impl!(
590    (A, a, AT, at),
591    (B, b, BT, bt),
592    (C, c, CT, ct),
593    (D, d, DT, dt),
594    (E, e, ET, et)
595);
596tuple_eq_impl!(
597    (A, a, AT, at),
598    (B, b, BT, bt),
599    (C, c, CT, ct),
600    (D, d, DT, dt),
601    (E, e, ET, et),
602    (F, ff, FT, ft)
603);
604tuple_eq_impl!(
605    (A, a, AT, at),
606    (B, b, BT, bt),
607    (C, c, CT, ct),
608    (D, d, DT, dt),
609    (E, e, ET, et),
610    (F, ff, FT, ft),
611    (G, g, GT, gt)
612);
613tuple_eq_impl!(
614    (A, a, AT, at),
615    (B, b, BT, bt),
616    (C, c, CT, ct),
617    (D, d, DT, dt),
618    (E, e, ET, et),
619    (F, ff, FT, ft),
620    (G, g, GT, gt),
621    (H, h, HT, ht)
622);
623tuple_eq_impl!(
624    (A, a, AT, at),
625    (B, b, BT, bt),
626    (C, c, CT, ct),
627    (D, d, DT, dt),
628    (E, e, ET, et),
629    (F, ff, FT, ft),
630    (G, g, GT, gt),
631    (H, h, HT, ht),
632    (I, i, IT, it)
633);
634tuple_eq_impl!(
635    (A, a, AT, at),
636    (B, b, BT, bt),
637    (C, c, CT, ct),
638    (D, d, DT, dt),
639    (E, e, ET, et),
640    (F, ff, FT, ft),
641    (G, g, GT, gt),
642    (H, h, HT, ht),
643    (I, i, IT, it),
644    (J, j, JT, jt)
645);
646tuple_eq_impl!(
647    (A, a, AT, at),
648    (B, b, BT, bt),
649    (C, c, CT, ct),
650    (D, d, DT, dt),
651    (E, e, ET, et),
652    (F, ff, FT, ft),
653    (G, g, GT, gt),
654    (H, h, HT, ht),
655    (I, i, IT, it),
656    (J, j, JT, jt),
657    (K, k, KT, kt)
658);
659tuple_eq_impl!(
660    (A, a, AT, at),
661    (B, b, BT, bt),
662    (C, c, CT, ct),
663    (D, d, DT, dt),
664    (E, e, ET, et),
665    (F, ff, FT, ft),
666    (G, g, GT, gt),
667    (H, h, HT, ht),
668    (I, i, IT, it),
669    (J, j, JT, jt),
670    (K, k, KT, kt),
671    (L, l, LT, lt)
672);
673
674#[cfg(test)]
675mod tests {
676    use super::*;
677
678    #[derive(PartialEq, Debug)]
679    enum HashCall {
680        Bytes(Vec<u8>),
681        U8(u8),
682        U16(u16),
683        U32(u32),
684        U64(u64),
685        U128(u128),
686        Usize(usize),
687        I8(i8),
688        I16(i16),
689        I32(i32),
690        I64(i64),
691        I128(i128),
692        Isize(isize),
693        // Ideally we'd handle `write_length_prefix` and `write_str` as well, but they are unstable.
694    }
695
696    #[derive(PartialEq, Debug)]
697    struct TestHasher {
698        calls: Vec<HashCall>,
699    }
700
701    impl Hasher for TestHasher {
702        fn finish(&self) -> u64 {
703            panic!("only used for tests");
704        }
705        fn write(&mut self, bytes: &[u8]) {
706            self.calls.push(HashCall::Bytes(bytes.to_vec()));
707        }
708        fn write_u8(&mut self, i: u8) {
709            self.calls.push(HashCall::U8(i));
710        }
711        fn write_u16(&mut self, i: u16) {
712            self.calls.push(HashCall::U16(i));
713        }
714        fn write_u32(&mut self, i: u32) {
715            self.calls.push(HashCall::U32(i));
716        }
717        fn write_u64(&mut self, i: u64) {
718            self.calls.push(HashCall::U64(i));
719        }
720        fn write_u128(&mut self, i: u128) {
721            self.calls.push(HashCall::U128(i));
722        }
723        fn write_usize(&mut self, i: usize) {
724            self.calls.push(HashCall::Usize(i));
725        }
726        fn write_i8(&mut self, i: i8) {
727            self.calls.push(HashCall::I8(i));
728        }
729        fn write_i16(&mut self, i: i16) {
730            self.calls.push(HashCall::I16(i));
731        }
732        fn write_i32(&mut self, i: i32) {
733            self.calls.push(HashCall::I32(i));
734        }
735        fn write_i64(&mut self, i: i64) {
736            self.calls.push(HashCall::I64(i));
737        }
738        fn write_i128(&mut self, i: i128) {
739            self.calls.push(HashCall::I128(i));
740        }
741        fn write_isize(&mut self, i: isize) {
742            self.calls.push(HashCall::Isize(i));
743        }
744    }
745
746    fn test_hash<T: PhfHash>(x: T) -> Vec<HashCall> {
747        let mut state = TestHasher { calls: Vec::new() };
748        x.phf_hash(&mut state);
749        state.calls
750    }
751
752    #[test]
753    fn byte_slices_are_hashed_efficiently() {
754        assert_eq!(
755            test_hash(&[1u8, 2, 3]),
756            [HashCall::U64(3), HashCall::Bytes([1, 2, 3].to_vec())]
757        );
758        assert_eq!(
759            test_hash(&[1i8, 2, 3]),
760            [HashCall::U64(3), HashCall::Bytes([1, 2, 3].to_vec())]
761        );
762        assert_eq!(
763            test_hash(&[false, true]),
764            [HashCall::U64(2), HashCall::Bytes([0, 1].to_vec())]
765        );
766    }
767
768    #[test]
769    fn slices_and_arrays_are_hashed_consistently() {
770        assert_eq!(test_hash(&[1u8, 2, 3]), test_hash(&[1u8, 2, 3][..]));
771        assert_eq!(test_hash(&[1u16, 2, 3]), test_hash(&[1u16, 2, 3][..]));
772    }
773
774    #[test]
775    fn array_reference_borrow_is_generic() {
776        fn assert_borrow<K, B: ?Sized>()
777        where
778            K: PhfBorrow<B>,
779        {
780        }
781
782        assert_borrow::<&[u32; 2], [u32; 2]>();
783        assert_borrow::<&[bool; 2], [bool; 2]>();
784        assert_borrow::<&[char; 2], [char; 2]>();
785    }
786
787    #[test]
788    fn tuple_eq_allows_shorter_reference_lifetimes() {
789        fn assert_ref_tuple<'a>(key: &(&'a str, &'a str)) -> bool
790        where
791            (&'static str, &'static str): PhfEq<(&'a str, &'a str)>,
792        {
793            ("a", "b").phf_eq(key)
794        }
795
796        fn assert_mixed_tuple<'a>(key: &(u32, &'a str)) -> bool
797        where
798            (u32, &'static str): PhfEq<(u32, &'a str)>,
799        {
800            (1, "a").phf_eq(key)
801        }
802
803        let a = String::from("a");
804        let b = String::from("b");
805        assert!(assert_ref_tuple(&(a.as_str(), b.as_str())));
806        assert!(assert_mixed_tuple(&(1, a.as_str())));
807    }
808
809    #[test]
810    fn variable_width_slice_elements_are_delimited() {
811        assert_ne!(test_hash(&["ab", "c"]), test_hash(&["a", "bc"]));
812
813        let key = 0;
814        let left = hash(&["ab", "c"], &key);
815        let right = hash(&["a", "bc"], &key);
816        assert!(
817            (left.g, left.f1, left.f2) != (right.g, right.f1, right.f2),
818            "different string arrays must not produce identical PHF hashes"
819        );
820    }
821}