1use serde_derive::{Deserialize, Serialize};
2
3#[derive(
4 Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize,
5)]
6#[repr(transparent)]
7#[serde(transparent)]
8pub struct ConstId<const N: u64, const S: u64>(pub u64);
9
10impl<const N: u64, const S: u64> ConstId<N, S> {
11 pub const NAMESPACE: [u64; 4] = const_wyhash::make_secret(N);
12 pub const SEED: u64 = S;
13
14 pub const fn from_str(value: &str) -> Self {
15 let bytes = value.as_bytes();
16 let (a, b, seed) = const_wyhash::wyhash_core(bytes, Self::SEED, Self::NAMESPACE);
17 let hash = const_wyhash::wyhash_finish(a, b, seed, bytes.len() as u64, Self::NAMESPACE[1]);
18 Self(hash)
19 }
20}
21
22#[derive(
23 Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize,
24)]
25#[serde(transparent)]
26#[repr(transparent)]
27pub struct Id(pub ConstId<0, 0x2614_9574_d5fa_c1fe>);
28
29impl Id {
30 pub const EMPTY: Self = Self::from_str("");
31
32 pub const fn from_bits(bits: u64) -> Self {
33 Self(ConstId(bits))
34 }
35
36 pub const fn to_bits(self) -> u64 {
37 self.0 .0
38 }
39
40 pub const fn is_empty(self) -> bool {
41 self.0 .0 == Self::EMPTY.0 .0
42 }
43
44 pub const fn from_str(value: &str) -> Self {
45 if value.is_empty() {
46 Self(ConstId(0))
47 } else {
48 Self(ConstId::from_str(value))
49 }
50 }
51}
52
53#[cfg(test)]
54mod test {
55 use super::Id;
56
57 #[test]
58 fn test_id_case_sensitive() {
59 const UPPERCASE_A: Id = Id::from_str("A");
60 const LOWERCASE_A: Id = Id::from_str("a");
61 assert_ne!(UPPERCASE_A, LOWERCASE_A);
62 }
63
64 fn get_runtime_id(s: &str) -> Id {
65 Id::from_str(s)
66 }
67
68 #[test]
69 fn test_empty() {
70 let id = get_runtime_id("");
71
72 assert_eq!(id, Id::EMPTY);
73 assert_eq!(id.to_bits(), 0)
74 }
75}
76
77pub mod const_wyhash {
78 pub const P0: u64 = 0xa076_1d64_78bd_642f;
82 pub const P1: u64 = 0xe703_7ed1_a0b4_28db;
83
84 const fn wymum(a: u64, b: u64) -> u64 {
85 let r = (a as u128) * (b as u128);
86 ((r >> 64) ^ r) as u64
87 }
88
89 const fn read64(bytes: &[u8], i: usize) -> u64 {
90 u64::from_le_bytes([
91 bytes[i],
92 bytes[i + 1],
93 bytes[i + 2],
94 bytes[i + 3],
95 bytes[i + 4],
96 bytes[i + 5],
97 bytes[i + 6],
98 bytes[i + 7],
99 ])
100 }
101
102 const fn read32(bytes: &[u8], i: usize) -> u64 {
103 u32::from_le_bytes([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]]) as u64
104 }
105
106 #[inline]
107 const fn read_up_to_24(bytes: &[u8]) -> u64 {
108 (bytes[0] as u64) << 16
109 | ((bytes[bytes.len() >> 1]) as u64) << 8
110 | (bytes[bytes.len() - 1]) as u64
111 }
112
113 pub const fn wyhash(bytes: &[u8], seed: u64, secret: [u64; 4]) -> u64 {
115 let seed = seed ^ secret[0];
116 let (a, b, seed) = wyhash_core(bytes, seed, secret);
117 wyhash_finish(a, b, seed, bytes.len() as u64, secret[1])
118 }
119
120 #[inline]
121 pub const fn wyhash_core(bytes: &[u8], seed: u64, secret: [u64; 4]) -> (u64, u64, u64) {
122 let (mut a, mut b) = (0, 0);
123 let mut seed = seed;
124 let length = bytes.len();
125 if length <= 16 {
126 if length >= 4 {
127 a = read32(bytes, 0) << 32 | read32(bytes, (length >> 3) << 2);
128 b = read32(bytes, length - 4) << 32
129 | read32(bytes, length - 4 - ((length >> 3) << 2));
130 } else if length > 0 {
131 a = read_up_to_24(bytes);
132 }
133 } else {
134 let mut index = length;
135 let mut start = 0;
136 if length > 48 {
137 let mut see1 = seed;
138 let mut see2 = seed;
139 while index > 48 {
140 seed = wymum(
141 read64(bytes, start) ^ secret[1],
142 read64(bytes, start + 8) ^ seed,
143 );
144 see1 = wymum(
145 read64(bytes, start + 16) ^ secret[2],
146 read64(bytes, start + 24) ^ see1,
147 );
148 see2 = wymum(
149 read64(bytes, start + 32) ^ secret[3],
150 read64(bytes, start + 40) ^ see2,
151 );
152 index -= 48;
153 start += 48;
154 }
155 seed ^= see1 ^ see2;
156 }
157
158 while index > 16 {
159 seed = wymum(
160 read64(bytes, start) ^ secret[1],
161 read64(bytes, start + 8) ^ seed,
162 );
163 index -= 16;
164 start += 16
165 }
166
167 a = read64(bytes, length - 16);
168 b = read64(bytes, length - 8);
169 }
170 (a, b, seed)
171 }
172
173 #[inline]
174 pub const fn wyhash_finish(a: u64, b: u64, seed: u64, length: u64, secret1: u64) -> u64 {
175 wymum(secret1 ^ length, wymum(a ^ secret1, b ^ seed))
176 }
177
178 pub const fn make_secret(seed: u64) -> [u64; 4] {
180 let c = [
181 15_u8, 23, 27, 29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, 83, 85,
182 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, 135, 139, 141, 142,
183 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172, 177, 178, 180, 184, 195,
184 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226, 228, 232, 240,
185 ];
186 let mut secret = [0_u64; 4];
187 let mut seed = seed;
188
189 let mut i = 0usize;
190 while i < secret.len() {
191 'search: loop {
192 secret[i] = 0;
193 let mut j = 0usize;
194 while j < 64 {
195 let (a, rng) = wyrng(seed);
196 seed = a;
197 let ndx = (rng % c.len() as u64) as usize;
198 secret[i] |= (c[ndx] as u64) << j;
199 j += 8;
200 }
201 if secret[i] % 2 == 0 {
202 continue;
203 }
204
205 let mut j = 0usize;
206 let b = secret[i];
207 while j < i {
208 let a = secret[j];
209 if (a ^ b).count_ones() != 32 {
210 continue 'search;
211 }
212 j += 1;
213 }
214
215 break;
216 }
217 i += 1;
218 }
219 secret
220 }
221
222 pub const fn wyrng(mut seed: u64) -> (u64, u64) {
226 seed = seed.wrapping_add(P0);
227 (seed, wymum(seed, seed ^ P1))
228 }
229}