const MAP_HASH_MULTIPLIER: i32 = 0xDEEC_E66Du32 as i32;
const MAP_HASH_INCREMENT: i32 = 0xB;
#[must_use]
pub fn utf16_string_hash(text: &str) -> i32 {
text.encode_utf16().fold(0i32, |hash, code_unit| {
hash.wrapping_mul(31).wrapping_add(i32::from(code_unit))
})
}
#[must_use]
pub fn map_entry_hash(name: &str) -> u32 {
(utf16_string_hash(name) ^ MAP_HASH_MULTIPLIER)
.wrapping_mul(MAP_HASH_MULTIPLIER)
.wrapping_add(MAP_HASH_INCREMENT) as u32
}
#[must_use]
pub fn compare_utf16_strings(first: &str, second: &str) -> std::cmp::Ordering {
first.encode_utf16().cmp(second.encode_utf16())
}
#[cfg(test)]
mod tests {
use super::{compare_utf16_strings, map_entry_hash, utf16_string_hash};
#[test]
fn matches_known_hash_values() {
assert_eq!(utf16_string_hash(""), 0);
assert_eq!(utf16_string_hash("a"), 97);
assert_eq!(utf16_string_hash("hello"), 99_162_322);
assert_eq!(utf16_string_hash("Aa"), utf16_string_hash("BB"));
assert_eq!(utf16_string_hash("jcr:primaryType"), 1_317_115_067);
}
#[test]
fn hashes_surrogate_pairs_individually() {
assert_eq!(utf16_string_hash("\u{1D54A}"), 1_772_469);
}
#[test]
fn utf16_order_differs_from_rust_for_supplementary_characters() {
assert_eq!(
compare_utf16_strings("\u{1D54A}", "\u{FFFD}"),
std::cmp::Ordering::Less,
"Java sorts the surrogate pair first"
);
assert!(
"\u{1D54A}" > "\u{FFFD}",
"Rust sorts the supplementary character last"
);
assert_eq!(
compare_utf16_strings("alpha", "beta"),
std::cmp::Ordering::Less
);
assert_eq!(
compare_utf16_strings("same", "same"),
std::cmp::Ordering::Equal
);
}
#[test]
fn map_entry_hash_matches_externally_computed_vectors() {
assert_eq!(map_entry_hash(""), 0xB460_0A74);
assert_eq!(map_entry_hash("a"), 0x3C9C_BB27);
assert_eq!(map_entry_hash("root"), 0xC289_24EE);
assert_eq!(map_entry_hash("content"), 0x9646_6A8F);
assert_eq!(map_entry_hash("jcr:content"), 0x88CE_F29C);
assert_eq!(map_entry_hash("Aa"), 0x6059_D734);
assert_eq!(map_entry_hash("BB"), 0x6059_D734);
}
}