triblespace_core/patch/
bytetable.rs1use rand::seq::SliceRandom;
39use rand::thread_rng;
40use std::fmt::Debug;
41use std::sync::Once;
42
43const BUCKET_ENTRY_COUNT: usize = 2;
45
46const MAX_SLOT_COUNT: usize = 256;
48
49const MAX_RETRIES: usize = 2;
52
53static mut RANDOM_PERMUTATION_RAND: [u8; 256] = [0; 256];
55static mut RANDOM_PERMUTATION_HASH: [u8; 256] = [0; 256];
56static INIT: Once = Once::new();
57
58pub fn init() {
61 INIT.call_once(|| {
62 let mut rng = thread_rng();
63 let mut bytes: [u8; 256] = [0; 256];
64
65 for (i, b) in bytes.iter_mut().enumerate() {
66 *b = i as u8;
67 }
68
69 bytes.shuffle(&mut rng);
70 unsafe {
71 RANDOM_PERMUTATION_HASH = bytes;
72 }
73
74 bytes.shuffle(&mut rng);
75 unsafe {
76 RANDOM_PERMUTATION_RAND = bytes;
77 }
78 });
79}
80
81pub unsafe trait ByteEntry {
89 fn key(&self) -> u8;
91}
92
93#[inline]
101fn cheap_hash(byte_key: u8) -> u8 {
102 byte_key
103}
104
105#[inline]
108fn rand_hash(byte_key: u8) -> u8 {
109 unsafe { RANDOM_PERMUTATION_HASH[byte_key as usize] }
110}
111
112#[inline]
114fn compress_hash(slot_count: usize, hash: u8) -> u8 {
115 let bucket_count = (slot_count / BUCKET_ENTRY_COUNT) as u8;
116 let mask = bucket_count - 1;
117 hash & mask
118}
119
120#[derive(Clone, Copy, Default, PartialEq, Eq)]
125pub(crate) struct ByteSet([u128; 2]);
126
127impl ByteSet {
128 pub(crate) fn new_empty() -> Self {
129 ByteSet([0, 0])
130 }
131
132 pub(crate) fn insert(&mut self, idx: u8) {
133 let bit = (idx & 0b0111_1111) as u32;
134 self.0[(idx >> 7) as usize] |= 1u128 << bit;
135 }
136
137 pub(crate) fn remove(&mut self, idx: u8) {
138 let bit = (idx & 0b0111_1111) as u32;
139 self.0[(idx >> 7) as usize] &= !(1u128 << bit);
140 }
141
142 pub(crate) fn contains(&self, idx: u8) -> bool {
143 let bit = (idx & 0b0111_1111) as u32;
144 (self.0[(idx >> 7) as usize] & (1u128 << bit)) != 0
145 }
146
147 #[cfg_attr(not(feature = "parallel"), allow(dead_code))]
149 pub(crate) fn intersect(&self, other: &ByteSet) -> ByteSet {
150 ByteSet([self.0[0] & other.0[0], self.0[1] & other.0[1]])
151 }
152
153 #[cfg_attr(not(feature = "parallel"), allow(dead_code))]
155 pub(crate) fn symmetric_difference(&self, other: &ByteSet) -> ByteSet {
156 ByteSet([self.0[0] ^ other.0[0], self.0[1] ^ other.0[1]])
157 }
158
159 #[allow(dead_code)]
161 pub(crate) fn popcount(&self) -> u32 {
162 self.0[0].count_ones() + self.0[1].count_ones()
163 }
164
165 #[cfg_attr(not(feature = "parallel"), allow(dead_code))]
169 pub(crate) fn drain_next_ascending(&mut self) -> Option<u8> {
170 if self.0[0] != 0 {
171 let bit = self.0[0].trailing_zeros();
172 self.0[0] &= !(1u128 << bit);
173 Some(bit as u8)
174 } else if self.0[1] != 0 {
175 let bit = self.0[1].trailing_zeros();
176 self.0[1] &= !(1u128 << bit);
177 Some(128 + bit as u8)
178 } else {
179 None
180 }
181 }
182}
183
184fn plan_insert<T: ByteEntry + Debug>(
185 table: &mut [Option<T>],
186 bucket_idx: usize,
187 depth: usize,
188 visited: &mut ByteSet,
189) -> Option<usize> {
190 let bucket_start = bucket_idx * BUCKET_ENTRY_COUNT;
191
192 for slot_idx in 0..BUCKET_ENTRY_COUNT {
193 if table[bucket_start + slot_idx].is_none() {
194 return Some(bucket_start + slot_idx);
195 }
196 }
197
198 if depth == 0 {
199 return None;
200 }
201
202 for slot_idx in 0..BUCKET_ENTRY_COUNT {
203 let key = table[bucket_start + slot_idx]
204 .as_ref()
205 .expect("slot must be occupied")
206 .key();
207 if visited.contains(key) {
208 continue;
209 }
210 visited.insert(key);
211
212 let cheap = compress_hash(table.len(), cheap_hash(key)) as usize;
213 let rand = compress_hash(table.len(), rand_hash(key)) as usize;
214 let alt_idx = if bucket_idx == cheap { rand } else { cheap };
216 if alt_idx != bucket_idx {
217 if let Some(hole_idx) = plan_insert(table, alt_idx, depth - 1, visited) {
218 table[hole_idx] = table[bucket_start + slot_idx].take();
219 visited.remove(key);
220 return Some(bucket_start + slot_idx);
221 }
222 }
223
224 visited.remove(key);
225 }
226
227 None
228}
229
230pub trait ByteTable<T: ByteEntry + Debug> {
232 fn table_get(&self, byte_key: u8) -> Option<&T>;
234 fn table_get_slot(&mut self, byte_key: u8) -> Option<&mut Option<T>>;
236 fn table_insert(&mut self, entry: T) -> Option<T>;
238 fn table_grow(&mut self, grown: &mut Self);
240}
241
242impl<T: ByteEntry + Debug> ByteTable<T> for [Option<T>] {
243 fn table_get(&self, byte_key: u8) -> Option<&T> {
244 let cheap_start =
245 compress_hash(self.len(), cheap_hash(byte_key)) as usize * BUCKET_ENTRY_COUNT;
246 for slot in 0..BUCKET_ENTRY_COUNT {
247 if let Some(entry) = self[cheap_start + slot].as_ref() {
248 if entry.key() == byte_key {
249 return Some(entry);
250 }
251 }
252 }
253
254 let rand_start =
255 compress_hash(self.len(), rand_hash(byte_key)) as usize * BUCKET_ENTRY_COUNT;
256 for slot in 0..BUCKET_ENTRY_COUNT {
257 if let Some(entry) = self[rand_start + slot].as_ref() {
258 if entry.key() == byte_key {
259 return Some(entry);
260 }
261 }
262 }
263 None
264 }
265
266 fn table_get_slot(&mut self, byte_key: u8) -> Option<&mut Option<T>> {
267 let cheap_start =
268 compress_hash(self.len(), cheap_hash(byte_key)) as usize * BUCKET_ENTRY_COUNT;
269 for slot in 0..BUCKET_ENTRY_COUNT {
270 let idx = cheap_start + slot;
271 if let Some(entry) = self[idx].as_ref() {
272 if entry.key() == byte_key {
273 return Some(&mut self[idx]);
274 }
275 }
276 }
277
278 let rand_start =
279 compress_hash(self.len(), rand_hash(byte_key)) as usize * BUCKET_ENTRY_COUNT;
280 for slot in 0..BUCKET_ENTRY_COUNT {
281 let idx = rand_start + slot;
282 if let Some(entry) = self[idx].as_ref() {
283 if entry.key() == byte_key {
284 return Some(&mut self[idx]);
285 }
286 }
287 }
288 None
289 }
290
291 fn table_insert(&mut self, inserted: T) -> Option<T> {
293 debug_assert!(self.table_get(inserted.key()).is_none());
294
295 let mut visited = ByteSet::new_empty();
296 let key = inserted.key();
297 visited.insert(key);
298 let limit = if self.len() == MAX_SLOT_COUNT {
299 MAX_SLOT_COUNT
300 } else {
301 MAX_RETRIES
302 };
303
304 let cheap_bucket = compress_hash(self.len(), cheap_hash(key)) as usize;
305 if let Some(slot) = plan_insert(self, cheap_bucket, limit, &mut visited) {
306 self[slot] = Some(inserted);
307 return None;
308 }
309
310 let rand_bucket = compress_hash(self.len(), rand_hash(key)) as usize;
311 if let Some(slot) = plan_insert(self, rand_bucket, limit, &mut visited) {
312 self[slot] = Some(inserted);
313 return None;
314 }
315
316 Some(inserted)
317 }
318
319 fn table_grow(&mut self, grown: &mut Self) {
320 debug_assert!(self.len() * 2 == grown.len());
321 let buckets_len = self.len() / BUCKET_ENTRY_COUNT;
322 let grown_len = grown.len();
323 let (lower_portion, upper_portion) = grown.split_at_mut(self.len());
324 for bucket_index in 0..buckets_len {
325 let start = bucket_index * BUCKET_ENTRY_COUNT;
326 for slot in 0..BUCKET_ENTRY_COUNT {
327 if let Some(entry) = self[start + slot].take() {
328 let byte_key = entry.key();
329 let cheap_index = compress_hash(grown_len, cheap_hash(byte_key));
330 let rand_index = compress_hash(grown_len, rand_hash(byte_key));
331
332 let dest_bucket =
333 if bucket_index as u8 == cheap_index || bucket_index as u8 == rand_index {
334 &mut lower_portion[start..start + BUCKET_ENTRY_COUNT]
335 } else {
336 &mut upper_portion[start..start + BUCKET_ENTRY_COUNT]
337 };
338
339 for dest_slot in dest_bucket.iter_mut() {
340 if dest_slot.is_none() {
341 *dest_slot = Some(entry);
342 break;
343 }
344 }
345 }
346 }
347 }
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354 use proptest::prelude::*;
355
356 #[derive(Copy, Clone, Debug)]
357 #[repr(C)]
358 struct DummyEntry {
359 value: u8,
360 }
361
362 impl DummyEntry {
363 fn new(byte_key: u8) -> Self {
364 DummyEntry { value: byte_key }
365 }
366 }
367
368 unsafe impl ByteEntry for DummyEntry {
369 fn key(&self) -> u8 {
370 self.value
371 }
372 }
373
374 proptest! {
375 #[test]
376 fn empty_table_then_empty_get(n in 0u8..255) {
377 init();
378 let table: [Option<DummyEntry>; 4] = [None; 4];
379 prop_assert!(table.table_get(n).is_none());
380 }
381
382 #[test]
383 fn single_insert_success(n in 0u8..255) {
384 init();
385 let mut table: [Option<DummyEntry>; 4] = [None; 4];
386 let entry = DummyEntry::new(n);
387 let displaced = table.table_insert(entry);
388 prop_assert!(displaced.is_none());
389 prop_assert!(table.table_get(n).is_some());
390 }
391
392 #[test]
393 fn insert_success(entry_set in prop::collection::hash_set(0u8..255, 1..32)) {
394 init();
395
396 let entries: Vec<_> = entry_set.iter().copied().collect();
397 let mut displaced: Option<DummyEntry> = None;
398 let mut i = 0;
399
400 macro_rules! insert_step {
401 ($table:ident, $grown_table:ident, $grown_size:expr) => {
402 while displaced.is_none() && i < entries.len() {
403 displaced = $table.table_insert(DummyEntry::new(entries[i]));
404 if(displaced.is_none()) {
405 for j in 0..=i {
406 prop_assert!($table.table_get(entries[j]).is_some(),
407 "Missing value {} after insert", entries[j]);
408 }
409 }
410 i += 1;
411 }
412
413 if displaced.is_none() {return Ok(())};
414
415 let mut $grown_table: [Option<DummyEntry>; $grown_size] = [None; $grown_size];
416 $table.table_grow(&mut $grown_table);
417 displaced = $grown_table.table_insert(displaced.unwrap());
418
419 if displaced.is_none() {
420 for j in 0..i {
421 prop_assert!(
422 $grown_table.table_get(entries[j]).is_some(),
423 "Missing value {} after growth with hash {:?}",
424 entries[j],
425 unsafe { RANDOM_PERMUTATION_HASH }
426 );
427 }
428 }
429 };
430 }
431
432 let mut table2: [Option<DummyEntry>; 2] = [None, None];
433 insert_step!(table2, table4, 4);
434 insert_step!(table4, table8, 8);
435 insert_step!(table8, table16, 16);
436 insert_step!(table16, table32, 32);
437 insert_step!(table32, table64, 64);
438 insert_step!(table64, table128, 128);
439 insert_step!(table128, table256, 256);
440
441 prop_assert!(displaced.is_none());
442 }
443 }
444
445 #[test]
446 fn sequential_insert_all_keys() {
447 init();
448 let mut table: [Option<DummyEntry>; 256] = [None; 256];
449 for n in 0u8..=255 {
450 assert!(table.table_insert(DummyEntry::new(n)).is_none());
451 }
452 }
453}