1use fxhash::FxHasher64;
7use std::cmp::{max, min};
8use std::hash::Hasher;
9use std::mem::size_of;
10use std::ops::Not;
11use std::{ptr, slice};
12
13#[repr(u8)]
14#[derive(Copy, Clone, PartialEq, Eq, Debug)]
15pub enum BucketStatus {
16 Empty = 0, Tombstone = 1,
18 Occupied = 2,
19}
20
21#[repr(C)]
22#[derive(Copy, Clone, Debug)]
23pub struct MapHeader {
24 pub capacity: u16, pub element_count: u16, pub key_size: u32,
28 pub value_size: u32,
29
30 pub value_offset: u32,
31 pub bucket_size: u32,
32
33 pub logical_limit: u16,
34 pub key_offset: u8,
35 pub padding_and_secret_code: u8,
36}
37
38pub struct MapInit {
39 pub key_size: u32,
40 pub key_alignment: u8,
41 pub value_size: u32,
42 pub value_alignment: u8,
43 pub capacity: u16,
44 pub logical_limit: u16,
45 pub total_size: u32,
46}
47
48#[derive(Clone, Copy, Debug)]
49pub struct BucketLayout {
50 pub bucket_size: u32,
51 pub key_offset: u8,
52 pub value_offset: u32,
53}
54
55const MAP_BUCKETS_OFFSET: usize = size_of::<MapHeader>();
56const MAX_PROBE_DISTANCE: usize = 32;
57
58#[inline]
59fn calculate_hash_bytes(key_bytes: &[u8]) -> u64 {
60 let mut hasher = FxHasher64::default();
61 hasher.write(key_bytes);
62 hasher.finish()
63}
64
65#[inline]
67#[must_use]
68pub fn calculate_bucket_layout(
69 key_size: u32,
70 key_alignment: u8,
71 value_size: u32,
72 value_alignment: u8,
73) -> BucketLayout {
74 let status_size: u32 = 1;
75 let mut current_offset = status_size;
76
77 let key_align = u32::from(key_alignment);
79 let key_offset = (current_offset + key_align - 1) & !(key_align - 1);
80 current_offset = key_offset + key_size;
81
82 let value_align = u32::from(value_alignment);
84 let value_offset = (current_offset + value_align - 1) & !(value_align - 1);
85 current_offset = value_offset + value_size;
86
87 let bucket_content_alignment = max(key_align, value_align);
89 let bucket_size =
90 (current_offset + bucket_content_alignment - 1) & !(bucket_content_alignment - 1);
91
92 BucketLayout {
93 bucket_size,
94 key_offset: key_offset as u8,
95 value_offset,
96 }
97}
98
99#[must_use]
100pub const fn total_size(capacity: u16, bucket_size: u32) -> u32 {
101 (MAP_BUCKETS_OFFSET + capacity as usize * bucket_size as usize) as u32
102}
103
104#[must_use]
105pub fn layout(
106 key_size: u32,
107 key_alignment: u8,
108 value_size: u32,
109 value_alignment: u8,
110 logical_limit: u16,
111) -> (BucketLayout, MapInit) {
112 let capacity = logical_limit.next_power_of_two();
113 let bucket_layout =
114 calculate_bucket_layout(key_size, key_alignment, value_size, value_alignment);
115 (
116 bucket_layout,
117 MapInit {
118 key_size,
119 key_alignment,
120 value_size,
121 value_alignment,
122 capacity,
123 logical_limit,
124 total_size: total_size(capacity, bucket_layout.bucket_size),
125 },
126 )
127}
128
129pub const SECRET_CODE: u8 = 0x3d;
130
131pub unsafe fn init(map_base: *mut u8, config: &MapInit) {
138 debug_assert!(
139 config.capacity.is_power_of_two(),
140 "Capacity must be a power of two"
141 );
142
143 let map_header = map_base.cast::<MapHeader>();
144 let layout = calculate_bucket_layout(
145 config.key_size,
146 config.key_alignment,
147 config.value_size,
148 config.value_alignment,
149 );
150
151 unsafe {
153 ptr::write(
154 map_header,
155 MapHeader {
156 capacity: config.capacity,
157 logical_limit: config.logical_limit,
158 key_size: config.key_size,
159 value_size: config.value_size,
160 bucket_size: layout.bucket_size,
161 key_offset: layout.key_offset,
162 value_offset: layout.value_offset,
163 element_count: 0,
164 padding_and_secret_code: SECRET_CODE,
165 },
166 );
167 }
168
169 let buckets_start_ptr = unsafe { map_base.add(MAP_BUCKETS_OFFSET) };
171 let capacity = usize::from(config.capacity);
172 let bucket_size = layout.bucket_size as usize;
173
174 for i in 0..capacity {
176 unsafe {
177 ptr::write(
178 buckets_start_ptr.add(i * bucket_size),
179 BucketStatus::Empty as u8,
180 );
181 }
182 }
183}
184
185#[inline]
188unsafe fn matches_key(a: *const u8, b: *const u8, len: usize) -> bool {
189 unsafe {
190 if len <= 16 {
191 match len {
192 0 => true,
193 _ => {
194 for i in 0..len {
195 if *a.add(i) != *b.add(i) {
196 return false;
197 }
198 }
199 true
200 }
201 }
202 } else {
203 slice::from_raw_parts(a, len) == slice::from_raw_parts(b, len)
204 }
205 }
206}
207
208#[inline]
219pub unsafe fn get_or_reserve_entry(base_ptr: *mut u8, key_ptr: *const u8) -> *mut u8 {
220 unsafe {
221 let header = &*base_ptr.cast::<MapHeader>();
222
223 let capacity = header.capacity as usize;
225 let key_size = header.key_size as usize;
226 let bucket_size = header.bucket_size as usize;
227 let key_offset = header.key_offset as usize;
228 let value_offset = header.value_offset as usize;
229
230 debug_assert_eq!(
231 header.padding_and_secret_code, SECRET_CODE,
232 "hashmap, secret code failed"
233 );
234 debug_assert_ne!(key_size, 0, "Key size cannot be zero");
235 debug_assert_ne!(capacity, 0, "Capacity cannot be zero");
236 debug_assert!(
237 capacity.is_power_of_two(),
238 "Capacity must be a power of two"
239 );
240
241 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
242 let key_slice = slice::from_raw_parts(key_ptr, key_size);
243 let hash = calculate_hash_bytes(key_slice);
244
245 let mut index = hash as usize & (capacity - 1);
247
248 let mut first_tombstone = None;
250 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
251
252 for _ in 0..probe_limit {
253 let bucket_ptr = buckets_ptr.add(index * bucket_size);
254 let status = *bucket_ptr;
255
256 match status {
257 status if status == BucketStatus::Empty as u8 => {
258 let insert_index = first_tombstone.unwrap_or(index);
261 let target_bucket = buckets_ptr.add(insert_index * bucket_size);
262
263 *target_bucket = BucketStatus::Occupied as u8;
265 let target_key_ptr = target_bucket.add(key_offset);
266 ptr::copy_nonoverlapping(key_ptr, target_key_ptr, key_size);
267
268 let header_mut = &mut *base_ptr.cast::<MapHeader>();
270 header_mut.element_count += 1;
271
272 return target_bucket.add(value_offset);
273 }
274 status if status == BucketStatus::Occupied as u8 => {
275 let existing_key_ptr = bucket_ptr.add(key_offset);
277 if matches_key(existing_key_ptr, key_ptr, key_size) {
278 return bucket_ptr.add(value_offset);
279 }
280 }
281 status if status == BucketStatus::Tombstone as u8 => {
282 if first_tombstone.is_none() {
284 first_tombstone = Some(index);
285 }
286 }
287 _ => unreachable!(),
288 }
289
290 index = (index + 1) & (capacity - 1);
292 }
293
294 if let Some(tombstone_index) = first_tombstone {
296 let target_bucket = buckets_ptr.add(tombstone_index * bucket_size);
297
298 *target_bucket = BucketStatus::Occupied as u8;
300 let target_key_ptr = target_bucket.add(key_offset);
301 ptr::copy_nonoverlapping(key_ptr, target_key_ptr, key_size);
302
303 let header_mut = &mut *base_ptr.cast::<MapHeader>();
305 header_mut.element_count += 1;
306
307 return target_bucket.add(value_offset);
308 }
309
310 ptr::null_mut()
312 }
313}
314
315#[inline]
322#[must_use]
323pub unsafe fn has(base_ptr: *const u8, key_ptr: *const u8) -> bool {
324 unsafe { lookup(base_ptr.cast_mut(), key_ptr).is_null().not() }
325}
326
327#[inline]
338pub unsafe fn lookup(base_ptr: *mut u8, key_ptr: *const u8) -> *mut u8 {
339 unsafe {
340 let header = &*base_ptr.cast::<MapHeader>();
341
342 let capacity = header.capacity as usize;
343 let key_size = header.key_size as usize;
344 let bucket_size = header.bucket_size as usize;
345 let key_offset = header.key_offset as usize;
346 let value_offset = header.value_offset as usize;
347
348 debug_assert_eq!(
349 header.padding_and_secret_code, SECRET_CODE,
350 "hashmap, secret code failed"
351 );
352 debug_assert_ne!(key_size, 0, "Key size cannot be zero");
353 debug_assert_ne!(capacity, 0, "Capacity cannot be zero");
354 debug_assert!(
355 capacity.is_power_of_two(),
356 "Capacity must be a power of two {capacity}"
357 );
358
359 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
360 let key_slice = slice::from_raw_parts(key_ptr, key_size);
361 let hash = calculate_hash_bytes(key_slice);
362
363 let mut index = hash as usize & (capacity - 1);
365 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
366
367 for _ in 0..probe_limit {
368 let bucket_ptr = buckets_ptr.add(index * bucket_size);
369 let status = *bucket_ptr;
370
371 match status {
372 status if status == BucketStatus::Empty as u8 => {
373 return ptr::null_mut();
376 }
377 status if status == BucketStatus::Occupied as u8 => {
378 let existing_key_ptr = bucket_ptr.add(key_offset);
380 if matches_key(existing_key_ptr, key_ptr, key_size) {
381 return bucket_ptr.add(value_offset);
382 }
383 }
384 _ => {} }
386
387 index = (index + 1) & (capacity - 1);
388 }
389
390 ptr::null_mut()
392 }
393}
394
395#[inline]
406pub unsafe fn remove(base_ptr: *mut u8, key_ptr: *const u8) -> bool {
407 unsafe {
408 let header = &*base_ptr.cast::<MapHeader>();
409
410 let capacity = header.capacity as usize;
411 let key_size = header.key_size as usize;
412 let bucket_size = header.bucket_size as usize;
413 let key_offset = header.key_offset as usize;
414
415 debug_assert_eq!(
416 header.padding_and_secret_code, SECRET_CODE,
417 "hashmap, secret code failed"
418 );
419 debug_assert_ne!(key_size, 0, "Key size cannot be zero");
420 debug_assert_ne!(capacity, 0, "Capacity cannot be zero");
421 debug_assert!(
422 capacity.is_power_of_two(),
423 "Capacity must be a power of two"
424 );
425
426 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
427 let key_slice = slice::from_raw_parts(key_ptr, key_size);
428 let hash = calculate_hash_bytes(key_slice);
429
430 let mut index = hash as usize & (capacity - 1);
432 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
433
434 for _ in 0..probe_limit {
435 let bucket_ptr = buckets_ptr.add(index * bucket_size);
436 let status = *bucket_ptr;
437
438 match status {
439 status if status == BucketStatus::Empty as u8 => {
440 return false;
442 }
443 status if status == BucketStatus::Occupied as u8 => {
444 let existing_key_ptr = bucket_ptr.add(key_offset);
446 if matches_key(existing_key_ptr, key_ptr, key_size) {
447 *bucket_ptr = BucketStatus::Tombstone as u8;
449
450 let header_mut = &mut *base_ptr.cast::<MapHeader>();
452 header_mut.element_count -= 1;
453
454 return true;
455 }
456 }
457 _ => {} }
459
460 index = (index + 1) & (capacity - 1);
461 }
462
463 false
465 }
466}
467
468#[inline]
479pub unsafe fn overwrite(target_base: *mut u8, source: *const u8) -> bool {
480 unsafe {
481 let target_header = &mut *target_base.cast::<MapHeader>();
482 let source_header = &*source.cast::<MapHeader>();
483 debug_assert_eq!(
484 target_header.padding_and_secret_code, SECRET_CODE,
485 "hashmap, secret code failed"
486 );
487 debug_assert_eq!(
488 source_header.padding_and_secret_code, SECRET_CODE,
489 "hashmap, secret code failed"
490 );
491 if target_header.logical_limit < source_header.element_count {
493 return false;
494 }
495
496 debug_assert_eq!(
498 target_header.bucket_size, source_header.bucket_size,
499 "Incompatible bucket sizes"
500 );
501 debug_assert_eq!(
502 target_header.key_size, source_header.key_size,
503 "Incompatible key sizes"
504 );
505 debug_assert_eq!(
506 target_header.value_size, source_header.value_size,
507 "Incompatible value sizes"
508 );
509
510 let source_buckets_ptr = source.add(MAP_BUCKETS_OFFSET);
511 let bucket_size = source_header.bucket_size as usize;
512 let key_offset = source_header.key_offset as usize;
513 let value_offset = source_header.value_offset as usize;
514 let value_size = source_header.value_size as usize;
515
516 for i in 0..source_header.capacity as usize {
518 let source_bucket = source_buckets_ptr.add(i * bucket_size);
519
520 if *source_bucket == BucketStatus::Occupied as u8 {
521 let source_key_ptr = source_bucket.add(key_offset);
522 let source_value_ptr = source_bucket.add(value_offset);
523
524 let target_value_ptr = get_or_reserve_entry(target_base, source_key_ptr);
525
526 if target_value_ptr.is_null() {
527 return false;
528 }
529
530 ptr::copy_nonoverlapping(source_value_ptr, target_value_ptr, value_size);
531 }
532 }
533
534 true
535 }
536}
537
538#[inline]
549pub unsafe fn find_next_valid_entry(base: *mut u8, start_index: u16) -> (*const u8, *mut u8, u16) {
550 unsafe {
551 let map_header = &*base.cast::<MapHeader>();
552 let bucket_size = map_header.bucket_size as usize;
553 let buckets_start = base.add(MAP_BUCKETS_OFFSET);
554 let key_offset = map_header.key_offset as usize;
555 let value_offset = map_header.value_offset as usize;
556 debug_assert_eq!(
557 map_header.padding_and_secret_code, SECRET_CODE,
558 "hashmap, secret code failed"
559 );
560
561 let mut index = start_index as usize;
562
563 while index < map_header.capacity as usize {
564 let entry_ptr = buckets_start.add(index * bucket_size);
565
566 if *entry_ptr == BucketStatus::Occupied as u8 {
568 let key_addr = entry_ptr.add(key_offset);
569 let value_addr = entry_ptr.add(value_offset);
570
571 return (key_addr, value_addr, index as u16);
572 }
573
574 index += 1;
575 }
576
577 (ptr::null(), ptr::null_mut(), 0xFFFF)
578 }
579}