#![expect(clippy::cast_lossless, reason = "False positive.")]
use crate::int;
use std::hash::{
BuildHasherDefault,
Hasher,
};
pub type NoHash = BuildHasherDefault<NoHasher>;
#[derive(Debug, Default, Copy, Clone)]
pub struct NoHasher(u64);
macro_rules! write_int {
(@signed $($fn:ident $ty:ident),+ $(,)?) => ($(
#[expect(clippy::cast_sign_loss, reason = "False positive.")]
#[inline]
#[doc = concat!("# Write `", stringify!($ty), "`")]
fn $fn(&mut self, val: $ty) {
debug_assert!(self.0 == 0, "cannot call `Hasher::write_*` more than once");
self.0 = (val as int!(@flip $ty)) as u64;
}
)+);
($($fn:ident $ty:ident),+ $(,)?) => ($(
#[inline]
#[doc = concat!("# Write `", stringify!($ty), "`")]
fn $fn(&mut self, val: $ty) {
debug_assert!(self.0 == 0, "cannot call `Hasher::write_*` more than once");
self.0 = val as u64;
}
)+);
}
impl Hasher for NoHasher {
#[cold]
fn write(&mut self, _bytes: &[u8]) {
unimplemented!("NoHash only implements the type-specific write methods (like `write_u16`)");
}
write_int! {
write_u8 u8,
write_u16 u16,
write_u32 u32,
write_usize usize,
}
write_int! {
@signed
write_i8 i8,
write_i16 i16,
write_i32 i32,
write_isize isize,
}
#[inline]
fn write_u64(&mut self, val: u64) { self.0 = val; }
#[inline]
fn finish(&self) -> u64 { self.0 }
}
#[cfg(test)]
mod tests {
use super::*;
use std::{
collections::HashSet,
num::{
NonZeroU8,
Wrapping,
},
};
#[test]
fn t_nonzero() {
let mut set: HashSet<NonZeroU8, NoHash> = (1..=u8::MAX).filter_map(NonZeroU8::new).collect();
assert_eq!(set.len(), 255);
assert!(!set.insert(NonZeroU8::new(1).unwrap())); }
#[test]
fn t_wrapping() {
let mut set: HashSet<Wrapping<u8>, NoHash> = (0..=u8::MAX).map(Wrapping).collect();
assert_eq!(set.len(), 256);
assert!(!set.insert(Wrapping(0))); }
#[test]
fn t_u8() {
let mut set: HashSet<u8, NoHash> = (0..=u8::MAX).collect();
assert_eq!(set.len(), 256);
assert!(!set.insert(0));
let mut set: HashSet<i8, NoHash> = (i8::MIN..=i8::MAX).collect();
assert_eq!(set.len(), 256);
assert!(!set.insert(0)); }
#[cfg(not(miri))]
#[test]
fn t_u16() {
let mut set: HashSet<u16, NoHash> = (0..=u16::MAX).collect();
assert_eq!(set.len(), 65_536);
assert!(!set.insert(0));
let mut set: HashSet<i16, NoHash> = (i16::MIN..=i16::MAX).collect();
assert_eq!(set.len(), 65_536);
assert!(!set.insert(0)); }
macro_rules! sanity_check_signed {
($ty:ty) => (
let mut set: HashSet<$ty, NoHash> = HashSet::default();
assert_eq!(set.insert(<$ty>::MIN), true);
assert_eq!(set.insert(-2), true);
assert_eq!(set.insert(-1), true);
assert_eq!(set.insert(0), true);
assert_eq!(set.insert(1), true);
assert_eq!(set.insert(2), true);
assert_eq!(set.insert(<$ty>::MAX), true);
assert_eq!(set.insert(0), false); );
}
macro_rules! sanity_check_unsigned {
($ty:ty) => (
let mut set: HashSet<$ty, NoHash> = HashSet::default();
assert_eq!(set.insert(0), true);
assert_eq!(set.insert(1), true);
assert_eq!(set.insert(2), true);
assert_eq!(set.insert(<$ty>::MAX), true);
assert_eq!(set.insert(0), false); );
}
#[cfg(miri)]
#[test]
fn t_u16() {
sanity_check_unsigned!(u16);
sanity_check_signed!(i16);
}
#[test]
fn t_u32() {
sanity_check_unsigned!(u32);
sanity_check_signed!(i32);
}
#[test]
fn t_u64() {
sanity_check_unsigned!(u64);
sanity_check_signed!(i64);
}
#[test]
fn t_usize() {
sanity_check_unsigned!(usize);
sanity_check_signed!(isize);
}
#[test]
#[should_panic]
fn t_u128() {
let mut set: HashSet<u128, NoHash> = HashSet::default();
set.insert(0);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn t_double_write() {
let mut set: HashSet<(u8, u8), NoHash> = HashSet::default();
set.insert((1_u8, 2_u8));
}
#[cfg(not(debug_assertions))]
#[test]
fn t_double_write() {
let mut set: HashSet<(u8, u8), NoHash> = HashSet::default();
assert!(set.insert((1_u8, 2_u8)));
assert!(set.insert((1_u8, 3_u8)));
assert!(set.insert((0_u8, 3_u8))); }
#[test]
#[should_panic]
fn t_write_bytes() {
let mut set: HashSet<&str, NoHash> = HashSet::default();
set.insert("hello");
}
}