1#![no_std]
7#![allow(clippy::similar_names)] #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_ptr_alignment)] #![allow(clippy::branches_sharing_code)] use core::cmp::{max, min};
13use core::hash::Hasher;
14use core::mem::size_of;
15use core::ops::Not;
16use core::{ptr, slice};
17use fxhash::FxHasher64;
18
19#[repr(u8)]
20#[derive(Copy, Clone, PartialEq, Eq, Debug)]
21pub enum BucketStatus {
22 Empty = 0, Tombstone = 1,
24 Occupied = 2,
25}
26
27#[repr(C)]
28#[derive(Copy, Clone, Debug)]
29pub struct MapHeader {
30 pub capacity: u16, pub element_count: u16, pub key_size: u32,
34 pub value_size: u32,
35
36 pub value_offset: u32,
37 pub bucket_size: u32,
38
39 pub logical_limit: u16,
40 pub key_offset: u8,
41 pub padding_and_secret_code: u8,
42}
43
44pub struct MapInit {
45 pub key_size: u32,
46 pub key_alignment: u8,
47 pub value_size: u32,
48 pub value_alignment: u8,
49 pub capacity: u16,
50 pub logical_limit: u16,
51 pub total_size: u32,
52}
53
54#[derive(Clone, Copy, Debug)]
55pub struct BucketLayout {
56 pub bucket_size: u32,
57 pub key_offset: u8,
58 pub value_offset: u32,
59}
60
61const MAP_BUCKETS_OFFSET: usize = size_of::<MapHeader>();
62const MAX_PROBE_DISTANCE: usize = 32;
63
64#[inline]
65fn calculate_hash_bytes(key_bytes: &[u8]) -> u64 {
66 let mut hasher = FxHasher64::default();
67 hasher.write(key_bytes);
68 hasher.finish()
69}
70
71#[inline]
72fn index_from_hash(hash: u64, capacity: u16) -> usize {
73 assert!(capacity.is_power_of_two());
74
75 ((hash >> 48) as usize) & ((capacity as usize) - 1)
78}
79
80#[inline]
82#[must_use]
83pub fn calculate_bucket_layout(
84 key_size: u32,
85 key_alignment: u8,
86 value_size: u32,
87 value_alignment: u8,
88) -> BucketLayout {
89 let status_size: u32 = 1;
90 let mut current_offset = status_size;
91
92 let key_align = u32::from(key_alignment);
94 let key_offset = (current_offset + key_align - 1) & !(key_align - 1);
95 current_offset = key_offset + key_size;
96
97 let value_align = u32::from(value_alignment);
99 let value_offset = (current_offset + value_align - 1) & !(value_align - 1);
100 current_offset = value_offset + value_size;
101
102 let bucket_content_alignment = max(key_align, value_align);
104 let bucket_size =
105 (current_offset + bucket_content_alignment - 1) & !(bucket_content_alignment - 1);
106
107 BucketLayout {
108 bucket_size,
109 key_offset: key_offset as u8,
110 value_offset,
111 }
112}
113
114#[must_use]
115pub const fn total_size(capacity: u16, bucket_size: u32) -> u32 {
116 (MAP_BUCKETS_OFFSET + capacity as usize * bucket_size as usize) as u32
117}
118
119#[must_use]
120pub fn layout(
121 key_size: u32,
122 key_alignment: u8,
123 value_size: u32,
124 value_alignment: u8,
125 logical_limit: u16,
126) -> (BucketLayout, MapInit) {
127 let capacity = logical_limit.next_power_of_two();
128 let bucket_layout =
129 calculate_bucket_layout(key_size, key_alignment, value_size, value_alignment);
130 (
131 bucket_layout,
132 MapInit {
133 key_size,
134 key_alignment,
135 value_size,
136 value_alignment,
137 capacity,
138 logical_limit,
139 total_size: total_size(capacity, bucket_layout.bucket_size),
140 },
141 )
142}
143
144pub const SECRET_CODE: u8 = 0x3d;
145
146pub unsafe fn init(map_base: *mut u8, config: &MapInit) {
157 assert!(
158 config.capacity.is_power_of_two(),
159 "Capacity must be a power of two"
160 );
161
162 let map_header = map_base.cast::<MapHeader>();
163 let layout = calculate_bucket_layout(
164 config.key_size,
165 config.key_alignment,
166 config.value_size,
167 config.value_alignment,
168 );
169
170 unsafe {
172 ptr::write(
173 map_header,
174 MapHeader {
175 capacity: config.capacity,
176 logical_limit: config.logical_limit,
177 key_size: config.key_size,
178 value_size: config.value_size,
179 bucket_size: layout.bucket_size,
180 key_offset: layout.key_offset,
181 value_offset: layout.value_offset,
182 element_count: 0,
183 padding_and_secret_code: SECRET_CODE,
184 },
185 );
186 }
187
188 let buckets_start_ptr = unsafe { map_base.add(MAP_BUCKETS_OFFSET) };
190 let capacity = usize::from(config.capacity);
191 let bucket_size = layout.bucket_size as usize;
192
193 for i in 0..capacity {
195 unsafe {
196 ptr::write(
197 buckets_start_ptr.add(i * bucket_size),
198 BucketStatus::Empty as u8,
199 );
200 }
201 }
202}
203
204#[inline]
214#[must_use]
215pub const unsafe fn validate(base_ptr: *const u8) -> bool {
216 unsafe {
217 let header = &*base_ptr.cast::<MapHeader>();
218
219 if header.padding_and_secret_code != SECRET_CODE {
220 return false;
221 }
222
223 if header.key_size == 0 {
224 return false;
225 }
226 if header.capacity == 0 {
227 return false;
228 }
229
230 if !header.capacity.is_power_of_two() {
231 return false;
232 }
233
234 if header.element_count > header.capacity {
235 return false;
236 }
237
238 if header.logical_limit > header.capacity {
239 return false;
240 }
241
242 true
243 }
244}
245
246#[inline]
256unsafe fn assert_validate(base_ptr: *const u8) {
257 unsafe {
258 assert!(validate(base_ptr), "Invalid map structure");
259 }
260}
261
262#[inline]
265unsafe fn matches_key(a: *const u8, b: *const u8, len: usize) -> bool {
266 unsafe {
267 if len <= 16 {
268 if len == 0 {
269 true
270 } else {
271 for i in 0..len {
272 if *a.add(i) != *b.add(i) {
273 return false;
274 }
275 }
276 true
277 }
278 } else {
279 slice::from_raw_parts(a, len) == slice::from_raw_parts(b, len)
280 }
281 }
282}
283
284#[inline]
299pub unsafe fn reserve_entry(base_ptr: *mut u8, key_ptr: *const u8) -> *mut u8 {
300 unsafe {
301 assert_validate(base_ptr);
302
303 let header = &*base_ptr.cast::<MapHeader>();
304 let capacity = header.capacity as usize;
305 let key_size = header.key_size as usize;
306 let bucket_size = header.bucket_size as usize;
307 let key_offset = header.key_offset as usize;
308 let value_offset = header.value_offset as usize;
309
310 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
311 let key_slice = slice::from_raw_parts(key_ptr, key_size);
312 let hash = calculate_hash_bytes(key_slice);
313
314 let mut index = index_from_hash(hash, header.capacity);
315
316 let mut first_tombstone = None;
317 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
318
319 for _ in 0..probe_limit {
320 let bucket_ptr = buckets_ptr.add(index * bucket_size);
321 let status = *bucket_ptr;
322
323 match status {
324 status if status == BucketStatus::Empty as u8 => {
325 let insert_index = first_tombstone.unwrap_or(index);
327 let target_bucket = buckets_ptr.add(insert_index * bucket_size);
328
329 *target_bucket = BucketStatus::Occupied as u8;
330 let target_key_ptr = target_bucket.add(key_offset);
331 ptr::copy_nonoverlapping(key_ptr, target_key_ptr, key_size);
332
333 let header_mut = &mut *base_ptr.cast::<MapHeader>();
335 header_mut.element_count += 1;
336
337 return target_bucket.add(value_offset);
338 }
339 status if status == BucketStatus::Occupied as u8 => {
340 let existing_key_ptr = bucket_ptr.add(key_offset);
341 assert!(
342 !matches_key(existing_key_ptr, key_ptr, key_size),
343 "Key already exists in map"
344 );
345 }
346 status if status == BucketStatus::Tombstone as u8 => {
347 if first_tombstone.is_none() {
348 first_tombstone = Some(index);
349 }
350 }
351 _ => unreachable!(),
352 }
353
354 index = (index + 1) & (capacity - 1);
356 }
357
358 if let Some(tombstone_index) = first_tombstone {
360 let target_bucket = buckets_ptr.add(tombstone_index * bucket_size);
361
362 *target_bucket = BucketStatus::Occupied as u8;
364 let target_key_ptr = target_bucket.add(key_offset);
365 ptr::copy_nonoverlapping(key_ptr, target_key_ptr, key_size);
366
367 let header_mut = &mut *base_ptr.cast::<MapHeader>();
369 header_mut.element_count += 1;
370
371 return target_bucket.add(value_offset);
372 }
373
374 ptr::null_mut()
376 }
377}
378
379#[inline]
390pub unsafe fn get_or_reserve_entry(base_ptr: *mut u8, key_ptr: *const u8) -> *mut u8 {
391 unsafe {
392 assert_validate(base_ptr);
393
394 let header = &*base_ptr.cast::<MapHeader>();
395 let capacity = header.capacity as usize;
396 let key_size = header.key_size as usize;
397 let bucket_size = header.bucket_size as usize;
398 let key_offset = header.key_offset as usize;
399 let value_offset = header.value_offset as usize;
400
401 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
402 let key_slice = slice::from_raw_parts(key_ptr, key_size);
403 let hash = calculate_hash_bytes(key_slice);
404
405 let mut index = index_from_hash(hash, header.capacity);
407
408 let mut first_tombstone = None;
410 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
411
412 for _ in 0..probe_limit {
413 let bucket_ptr = buckets_ptr.add(index * bucket_size);
414 let status = *bucket_ptr;
415
416 match status {
417 status if status == BucketStatus::Empty as u8 => {
418 let insert_index = first_tombstone.unwrap_or(index);
421 let target_bucket = buckets_ptr.add(insert_index * bucket_size);
422
423 *target_bucket = BucketStatus::Occupied as u8;
425 let target_key_ptr = target_bucket.add(key_offset);
426 ptr::copy_nonoverlapping(key_ptr, target_key_ptr, key_size);
427
428 let header_mut = &mut *base_ptr.cast::<MapHeader>();
430 header_mut.element_count += 1;
431
432 return target_bucket.add(value_offset);
433 }
434 status if status == BucketStatus::Occupied as u8 => {
435 let existing_key_ptr = bucket_ptr.add(key_offset);
437 if matches_key(existing_key_ptr, key_ptr, key_size) {
438 return bucket_ptr.add(value_offset);
439 }
440 }
441 status if status == BucketStatus::Tombstone as u8 => {
442 if first_tombstone.is_none() {
444 first_tombstone = Some(index);
445 }
446 }
447 _ => unreachable!(),
448 }
449
450 index = (index + 1) & (capacity - 1);
452 }
453
454 if let Some(tombstone_index) = first_tombstone {
456 let target_bucket = buckets_ptr.add(tombstone_index * bucket_size);
457
458 *target_bucket = BucketStatus::Occupied as u8;
460 let target_key_ptr = target_bucket.add(key_offset);
461 ptr::copy_nonoverlapping(key_ptr, target_key_ptr, key_size);
462
463 let header_mut = &mut *base_ptr.cast::<MapHeader>();
465 header_mut.element_count += 1;
466
467 return target_bucket.add(value_offset);
468 }
469
470 ptr::null_mut()
472 }
473}
474
475#[inline]
482#[must_use]
483pub unsafe fn has(base_ptr: *const u8, key_ptr: *const u8) -> bool {
484 unsafe { lookup(base_ptr.cast_mut(), key_ptr).is_null().not() }
485}
486
487#[inline]
498pub unsafe fn lookup(base_ptr: *mut u8, key_ptr: *const u8) -> *mut u8 {
499 unsafe {
500 assert_validate(base_ptr);
501
502 let header = &*base_ptr.cast::<MapHeader>();
503 let capacity = header.capacity as usize;
504 let key_size = header.key_size as usize;
505 let bucket_size = header.bucket_size as usize;
506 let key_offset = header.key_offset as usize;
507 let value_offset = header.value_offset as usize;
508
509 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
510 let key_slice = slice::from_raw_parts(key_ptr, key_size);
511 let hash = calculate_hash_bytes(key_slice);
512
513 let mut index = index_from_hash(hash, header.capacity);
515 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
516
517 for _ in 0..probe_limit {
518 let bucket_ptr = buckets_ptr.add(index * bucket_size);
519 let status = *bucket_ptr;
520
521 match status {
522 status if status == BucketStatus::Empty as u8 => {
523 return ptr::null_mut();
526 }
527 status if status == BucketStatus::Occupied as u8 => {
528 let existing_key_ptr = bucket_ptr.add(key_offset);
530 if matches_key(existing_key_ptr, key_ptr, key_size) {
531 return bucket_ptr.add(value_offset);
532 }
533 }
534 _ => {} }
536
537 index = (index + 1) & (capacity - 1);
538 }
539
540 ptr::null_mut()
542 }
543}
544
545#[inline]
556pub unsafe fn remove(base_ptr: *mut u8, key_ptr: *const u8) -> bool {
557 unsafe {
558 assert_validate(base_ptr);
559
560 let header = &*base_ptr.cast::<MapHeader>();
561 let capacity = header.capacity as usize;
562 let key_size = header.key_size as usize;
563 let bucket_size = header.bucket_size as usize;
564 let key_offset = header.key_offset as usize;
565
566 let buckets_ptr = base_ptr.add(MAP_BUCKETS_OFFSET);
567 let key_slice = slice::from_raw_parts(key_ptr, key_size);
568 let hash = calculate_hash_bytes(key_slice);
569
570 let mut index = index_from_hash(hash, header.capacity);
572 let probe_limit = min(capacity, MAX_PROBE_DISTANCE);
573
574 for _ in 0..probe_limit {
575 let bucket_ptr = buckets_ptr.add(index * bucket_size);
576 let status = *bucket_ptr;
577
578 match status {
579 status if status == BucketStatus::Empty as u8 => {
580 return false;
582 }
583 status if status == BucketStatus::Occupied as u8 => {
584 let existing_key_ptr = bucket_ptr.add(key_offset);
586 if matches_key(existing_key_ptr, key_ptr, key_size) {
587 *bucket_ptr = BucketStatus::Tombstone as u8;
589
590 let header_mut = &mut *base_ptr.cast::<MapHeader>();
592 header_mut.element_count -= 1;
593
594 return true;
595 }
596 }
597 _ => {} }
599
600 index = (index + 1) & (capacity - 1);
601 }
602
603 false
605 }
606}
607
608#[inline]
623pub unsafe fn overwrite(target_base: *mut u8, source: *const u8) -> bool {
624 unsafe {
625 assert_validate(target_base);
626 assert_validate(source);
627
628 let target_header = &mut *target_base.cast::<MapHeader>();
629 let source_header = &*source.cast::<MapHeader>();
630 if target_header.logical_limit < source_header.element_count {
632 return false;
633 }
634
635 assert_eq!(
637 target_header.bucket_size, source_header.bucket_size,
638 "Incompatible bucket sizes"
639 );
640 assert_eq!(
641 target_header.key_size, source_header.key_size,
642 "Incompatible key sizes"
643 );
644 assert_eq!(
645 target_header.value_size, source_header.value_size,
646 "Incompatible value sizes"
647 );
648
649 let source_buckets_ptr = source.add(MAP_BUCKETS_OFFSET);
650 let bucket_size = source_header.bucket_size as usize;
651 let key_offset = source_header.key_offset as usize;
652 let value_offset = source_header.value_offset as usize;
653 let value_size = source_header.value_size as usize;
654
655 for i in 0..source_header.capacity as usize {
657 let source_bucket = source_buckets_ptr.add(i * bucket_size);
658
659 if *source_bucket == BucketStatus::Occupied as u8 {
660 let source_key_ptr = source_bucket.add(key_offset);
661 let source_value_ptr = source_bucket.add(value_offset);
662
663 let target_value_ptr = get_or_reserve_entry(target_base, source_key_ptr);
664
665 if target_value_ptr.is_null() {
666 return false;
667 }
668
669 ptr::copy_nonoverlapping(source_value_ptr, target_value_ptr, value_size);
670 }
671 }
672
673 true
674 }
675}
676
677#[inline]
688pub unsafe fn find_next_valid_entry(base: *mut u8, start_index: u16) -> (*const u8, *mut u8, u16) {
689 unsafe {
690 assert_validate(base);
691
692 let map_header = &*base.cast::<MapHeader>();
693 let bucket_size = map_header.bucket_size as usize;
694 let buckets_start = base.add(MAP_BUCKETS_OFFSET);
695 let key_offset = map_header.key_offset as usize;
696 let value_offset = map_header.value_offset as usize;
697
698 let mut index = start_index as usize;
699
700 while index < map_header.capacity as usize {
701 let entry_ptr = buckets_start.add(index * bucket_size);
702
703 if *entry_ptr == BucketStatus::Occupied as u8 {
705 let key_addr = entry_ptr.add(key_offset);
706 let value_addr = entry_ptr.add(value_offset);
707
708 return (key_addr, value_addr, index as u16);
709 }
710
711 index += 1;
712 }
713
714 (ptr::null(), ptr::null_mut(), 0xFFFF)
715 }
716}