animgraph/core/
id.rs

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    // constified version of wyhash-rs
79    // https://github.com/eldruin/wyhash-rs/blob/8707fbe33bcd8354819712a93a4457cc83c367c2/src/final3/functions.rs
80
81    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    /// Generate a hash for the input data and seed
114    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    /// Generate new secret for wyhash
179    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    /// Pseudo-Random Number Generator (PRNG)
223    ///
224    /// Note that the input seed is updated
225    pub const fn wyrng(mut seed: u64) -> (u64, u64) {
226        seed = seed.wrapping_add(P0);
227        (seed, wymum(seed, seed ^ P1))
228    }
229}