1const fn crc16_table() -> [u16; 256] {
17 let mut table = [0u16; 256];
18 let mut i = 0usize;
19 while i < 256 {
20 let mut crc = (i as u16) << 8;
21 let mut bit = 0;
22 while bit < 8 {
23 crc = if crc & 0x8000 != 0 {
24 (crc << 1) ^ 0x1021
25 } else {
26 crc << 1
27 };
28 bit += 1;
29 }
30 table[i] = crc;
31 i += 1;
32 }
33 table
34}
35
36static CRC16_TABLE: [u16; 256] = crc16_table();
37
38#[inline]
40pub fn crc16(data: &[u8]) -> u16 {
41 let mut crc: u16 = 0;
42 for &b in data {
43 let idx = (((crc >> 8) ^ b as u16) & 0xff) as usize;
44 crc = (crc << 8) ^ CRC16_TABLE[idx];
45 }
46 crc
47}
48
49pub const SLOT_COUNT: u16 = 16384;
51
52#[inline]
59pub fn slot_of(key: &[u8]) -> u16 {
60 crc16(hash_tag(key)) & (SLOT_COUNT - 1)
61}
62
63#[inline]
67pub fn hash_tag(key: &[u8]) -> &[u8] {
68 let Some(open) = key.iter().position(|&b| b == b'{') else {
69 return key;
70 };
71 let rest = &key[open + 1..];
72 let Some(close) = rest.iter().position(|&b| b == b'}') else {
73 return key;
74 };
75 if close == 0 {
76 return key;
78 }
79 &rest[..close]
80}
81
82const fn crc32c_table() -> [u32; 256] {
87 let mut table = [0u32; 256];
88 let mut i = 0usize;
89 while i < 256 {
90 let mut crc = i as u32;
91 let mut bit = 0;
92 while bit < 8 {
93 crc = if crc & 1 != 0 {
94 (crc >> 1) ^ 0x82F6_3B78
95 } else {
96 crc >> 1
97 };
98 bit += 1;
99 }
100 table[i] = crc;
101 i += 1;
102 }
103 table
104}
105
106static CRC32C_TABLE: [u32; 256] = crc32c_table();
107
108#[inline]
109fn crc32c_software(mut crc: u32, data: &[u8]) -> u32 {
110 crc = !crc;
111 for &b in data {
112 crc = (crc >> 8) ^ CRC32C_TABLE[((crc ^ b as u32) & 0xff) as usize];
113 }
114 !crc
115}
116
117#[inline]
119pub fn crc32c(crc: u32, data: &[u8]) -> u32 {
120 #[cfg(target_arch = "x86_64")]
121 {
122 if is_x86_feature_detected!("sse4.2") {
123 return unsafe { crc32c_sse42(crc, data) };
125 }
126 }
127 crc32c_software(crc, data)
128}
129
130#[cfg(target_arch = "x86_64")]
131#[target_feature(enable = "sse4.2")]
132unsafe fn crc32c_sse42(crc: u32, data: &[u8]) -> u32 {
133 use core::arch::x86_64::{_mm_crc32_u8, _mm_crc32_u64};
134
135 let mut c = !crc;
136 let (words, rest) = data.as_chunks::<8>();
140 for chunk in words {
141 c = _mm_crc32_u64(c as u64, u64::from_le_bytes(*chunk)) as u32;
147 }
148 for &b in rest {
149 c = _mm_crc32_u8(c, b);
150 }
151 !c
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
161 fn redis_slot_examples() {
162 assert_eq!(crc16(b"123456789"), 0x31C3);
163 assert_eq!(slot_of(b"foo"), 12182);
164 assert_eq!(slot_of(b"bar"), 5061);
165 }
166
167 #[test]
168 fn hash_tags_pick_the_inner_run() {
169 assert_eq!(hash_tag(b"{user1000}.following"), b"user1000");
170 assert_eq!(hash_tag(b"foo{}{bar}"), b"foo{}{bar}");
171 assert_eq!(hash_tag(b"foo{{bar}}zap"), b"{bar");
172 assert_eq!(hash_tag(b"foo{bar}{zap}"), b"bar");
173 assert_eq!(hash_tag(b"nothing"), b"nothing");
174 }
175
176 #[test]
177 fn tagged_keys_land_on_one_slot() {
178 assert_eq!(
179 slot_of(b"{user1000}.following"),
180 slot_of(b"{user1000}.followers")
181 );
182 }
183
184 #[test]
185 fn crc32c_reference() {
186 assert_eq!(crc32c(0, b"123456789"), 0xE306_9283);
188 assert_eq!(crc32c(0, b""), 0);
189 }
190
191 #[test]
194 fn crc32c_hardware_matches_software() {
195 let buf: Vec<u8> = (0..1000u32).map(|i| (i * 7 % 251) as u8).collect();
196 for n in [0usize, 1, 7, 8, 9, 15, 16, 63, 64, 65, 999, 1000] {
197 assert_eq!(
198 crc32c(0, &buf[..n]),
199 crc32c_software(0, &buf[..n]),
200 "length {n} disagrees between the two paths"
201 );
202 }
203 }
204
205 #[test]
206 fn crc32c_is_resumable() {
207 let buf: Vec<u8> = (0..256u32).map(|i| i as u8).collect();
208 let one_shot = crc32c(0, &buf);
209 let split = crc32c(crc32c(0, &buf[..100]), &buf[100..]);
210 assert_eq!(one_shot, split);
211 }
212}