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
#![warn(unsafe_code)]
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
use std::{
	default::Default,
	hash::{BuildHasherDefault, Hasher},
};

#[cfg(not(feature = "std"))]
use core::{
	default::Default,
	hash::{BuildHasherDefault, Hasher},
};

#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};

pub struct JoaatHasher(u32);

impl Default for JoaatHasher {
	#[inline]
	fn default() -> Self {
		Self(0)
	}
}

impl JoaatHasher {
	/// Create a Joaat hasher with an initial hash.
	#[inline]
	#[must_use]
	pub const fn with_initial_hash(hash: u32) -> Self {
		Self(hash)
	}
}

impl Hasher for JoaatHasher {
	#[inline]
	fn finish(&self) -> u64 {
		let mut hash = self.0;
		hash = hash.wrapping_add(hash.wrapping_shl(3));
		hash ^= hash.wrapping_shr(11);
		hash = hash.wrapping_add(hash.wrapping_shl(15));
		hash as _
	}

	#[inline]
	fn write(&mut self, bytes: &[u8]) {
		for byte in bytes.iter() {
			self.0 = self.0.wrapping_add(u32::from(*byte));
			self.0 = self.0.wrapping_add(self.0.wrapping_shl(10));
			self.0 ^= self.0.wrapping_shr(6);
		}
	}
}

/// A builder for default Joaat hashers.
pub type JoaatBuildHasher = BuildHasherDefault<JoaatHasher>;

/// Hashes bytes from an iterator.
#[inline]
#[must_use]
pub fn hash_iter<I: Iterator<Item = u8>>(input: I) -> u32 {
	let mut hasher = JoaatHasher::default();

	for byte in input {
		hasher.write_u8(byte);
	}

	hasher.finish() as _
}

/// Hashes text converting alphabetical characters to ASCII lowercase.
#[inline]
#[must_use]
pub fn hash_ascii_lowercase(bytes: &[u8]) -> u32 {
	hash_iter(bytes.iter().map(|c| c.to_ascii_lowercase()))
}

/// Hashes a slice of bytes.
#[inline]
#[must_use]
pub fn hash_bytes(bytes: &[u8]) -> u32 {
	let mut hasher = JoaatHasher::default();
	hasher.write(bytes);
	hasher.finish() as _
}

/// A `HashMap` using a default Joaat hasher.
#[cfg(feature = "std")]
pub type JoaatHashMap<K, V> = HashMap<K, V, JoaatBuildHasher>;

/// A `HashSet` using a default Joaat hasher.
#[cfg(feature = "std")]
pub type JoaatHashSet<T> = HashSet<T, JoaatBuildHasher>;

#[cfg(test)]
#[allow(clippy::unreadable_literal)]
mod tests {
	use super::*;

	#[test]
	fn test() {
		assert_eq!(hash_bytes(b""), 0);
		assert_eq!(hash_bytes(b"a"), 0xCA2E9442);
		assert_eq!(hash_bytes(b"b"), 0x00DB819B);
		assert_eq!(hash_bytes(b"c"), 0xEEBA5D59);
		assert_eq!(
			hash_bytes(b"The quick brown fox jumps over the lazy dog"),
			0x519E91F5
		);
		assert_eq!(
			hash_bytes(b"The quick brown fox jumps over the lazy dog."),
			0xAE8EF3CB
		);
	}
}