1use xxhash_rust::{xxh3, xxh64};
2
3pub fn is_hex_chunk_name(chunk_name: &str) -> bool {
4 if chunk_name.starts_with("0x") {
6 return false;
7 }
8
9 let base = chunk_name.split('.').next().unwrap_or(chunk_name);
11 if base.len() != 16 {
12 return false;
13 }
14
15 base.chars().all(|c| c.is_ascii_hexdigit())
16}
17
18pub fn normalize_chunk_path(path: &str) -> String {
24 path.to_lowercase().replace('\\', "/")
25}
26
27pub fn hash_layer_name(name: &str) -> u64 {
29 xxh3::xxh3_64(name.to_lowercase().as_bytes())
30}
31
32pub fn hash_chunk_name(name: &str) -> u64 {
38 xxh64::xxh64(name.as_bytes(), 0)
39}
40
41pub fn hash_wad_name(name: &str) -> u64 {
43 xxh3::xxh3_64(name.to_lowercase().as_bytes())
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn normalize_chunk_path_lowercases() {
52 assert_eq!(
53 normalize_chunk_path("Graves.wad.client/Data/File.bin"),
54 "graves.wad.client/data/file.bin"
55 );
56 }
57
58 #[test]
59 fn normalize_chunk_path_converts_backslashes() {
60 assert_eq!(
61 normalize_chunk_path("graves.wad.client\\data\\characters\\graves"),
62 "graves.wad.client/data/characters/graves"
63 );
64 }
65
66 #[test]
67 fn normalize_chunk_path_handles_mixed_separators() {
68 assert_eq!(
69 normalize_chunk_path("Graves.wad.client\\Data/Characters\\Graves"),
70 "graves.wad.client/data/characters/graves"
71 );
72 }
73
74 #[test]
75 fn normalize_chunk_path_noop_on_normalized() {
76 let path = "graves.wad.client/data/file.bin";
77 assert_eq!(normalize_chunk_path(path), path);
78 }
79
80 #[test]
81 fn hash_chunk_name_consistent_after_normalization() {
82 let forward = normalize_chunk_path("graves.wad.client/data/file.bin");
83 let back = normalize_chunk_path("graves.wad.client\\data\\file.bin");
84 let mixed = normalize_chunk_path("Graves.wad.client\\Data/File.bin");
85
86 assert_eq!(hash_chunk_name(&forward), hash_chunk_name(&back));
87 assert_eq!(hash_chunk_name(&forward), hash_chunk_name(&mixed));
88 }
89}