1use std::{
2 alloc::{AllocError, Layout},
3 marker::PhantomData,
4 ptr::NonNull,
5};
6
7use twizzler_abi::object::{MAX_SIZE, NULLPAGE_SIZE};
8
9use super::DefaultHashBuilder;
10use crate::{
11 alloc::Allocator,
12 collections::hachage::control::{Tag, TagSliceExt},
13 marker::{BaseType, Invariant},
14 ptr::{GlobalPtr, InvPtr, Ref, RefMut, RefSliceMut},
15 Result,
16};
17
18#[derive(Default, Clone, Copy)]
21pub struct HashTableAlloc {}
22
23const MAX_ALIGNMENT: usize = 4096;
24const MAX_SPACE: usize = MAX_SIZE - NULLPAGE_SIZE * 4 - MAX_ALIGNMENT;
26
27const INVARIANT_GROUP_WIDTH: usize = 64;
30
31impl Allocator for HashTableAlloc {
39 fn alloc(
40 &self,
41 _layout: Layout,
42 ) -> std::result::Result<crate::ptr::GlobalPtr<u8>, std::alloc::AllocError> {
43 panic!("unsupported")
44 }
45
46 unsafe fn dealloc(&self, _ptr: crate::ptr::GlobalPtr<u8>, _layout: Layout) {}
47
48 unsafe fn realloc(
49 &self,
50 _ptr: GlobalPtr<u8>,
51 _layout: Layout,
52 _newsize: usize,
53 ) -> std::result::Result<GlobalPtr<u8>, AllocError> {
54 panic!("unsupported")
55 }
56}
57
58impl HashTableAlloc {
59 fn allocate(
62 &self,
63 table_layout: &TableLayout,
64 buckets: usize,
65 ) -> std::result::Result<crate::ptr::GlobalPtr<u8>, std::alloc::AllocError> {
66 assert!(buckets.is_power_of_two());
67 let (layout, _) = table_layout
68 .calculate_layout_for(buckets)
69 .ok_or(std::alloc::AllocError)?;
70 if layout.align() > MAX_ALIGNMENT {
71 return Err(std::alloc::AllocError);
72 }
73 if layout.size() > MAX_SPACE {
75 return Err(std::alloc::AllocError);
76 }
77 let offset = (MAX_SPACE / (table_layout.size + 1)) * table_layout.size;
78 let corrected_offset = offset + (MAX_ALIGNMENT - offset % MAX_ALIGNMENT);
79 let obj = twizzler_rt_abi::object::twz_rt_get_object_handle((self as *const Self).cast())
80 .unwrap();
81
82 Ok(GlobalPtr::new(
83 obj.id(),
84 (NULLPAGE_SIZE * 2 + corrected_offset) as u64,
85 ))
86 }
87}
88
89#[inline]
90#[allow(clippy::cast_possible_truncation)]
91fn h1(hash: u64) -> usize {
92 hash as usize
94}
95
96#[derive(Copy, Clone)]
97struct TableLayout {
98 pub size: usize,
99 pub ctrl_align: usize,
100}
101
102impl TableLayout {
103 #[inline]
104 const fn new<T: Invariant>() -> Self {
105 let layout = Layout::new::<T>();
106 Self {
107 size: layout.size(),
108 ctrl_align: if layout.align() > INVARIANT_GROUP_WIDTH {
109 layout.align()
110 } else {
111 INVARIANT_GROUP_WIDTH
112 },
113 }
114 }
115
116 fn calculate_ctrl_offset(&self, buckets: usize) -> usize {
117 self.size * buckets + (self.ctrl_align - 1) & !(self.ctrl_align - 1)
118 }
119
120 #[inline]
121 fn calculate_layout_for(&self, buckets: usize) -> Option<(Layout, usize)> {
122 debug_assert!(buckets.is_power_of_two());
123
124 let ctrl_offset = self.calculate_ctrl_offset(buckets);
129 let len = ctrl_offset + buckets + INVARIANT_GROUP_WIDTH;
130
131 Some((
132 unsafe { Layout::from_size_align_unchecked(len, self.ctrl_align) },
133 ctrl_offset,
134 ))
135 }
136}
137
138struct ProbeSeq {
139 pos: usize,
140}
141
142impl ProbeSeq {
143 fn move_next(&mut self, bucket_mask: usize) {
144 self.pos += 1;
145 self.pos &= bucket_mask
146 }
147}
148
149#[inline]
152fn bucket_mask_to_capacity(bucket_mask: usize) -> usize {
153 if bucket_mask < 8 {
154 bucket_mask
157 } else {
158 ((bucket_mask + 1) / 8) * 7
160 }
161}
162
163pub struct CarryCtx<'a> {
165 backing: Ref<'a, u8>,
166}
167
168pub struct CarryCtxMut<'a> {
169 backing: RefMut<'a, u8>,
170}
171
172pub trait Ctx {
173 fn base(&self) -> *const u8;
174
175 #[inline]
176 fn ctrl<'a>(&self, buckets: usize) -> &'a [Tag] {
177 unsafe { std::slice::from_raw_parts(self.base().cast::<Tag>(), buckets) }
178 }
179
180 #[inline]
181 fn bucket<'a, T>(&self, index: usize) -> &'a T {
182 unsafe { self.base().cast::<T>().sub(index + 1).as_ref_unchecked() }
183 }
184}
185
186pub trait CtxMut: Ctx {
187 fn base_mut(&self) -> *mut u8;
188
189 #[inline]
190 fn ctrl_mut(&self, buckets: usize) -> &mut [Tag] {
191 unsafe { std::slice::from_raw_parts_mut(self.base_mut().cast(), buckets) }
192 }
193
194 #[inline]
195 fn bucket_ptr(&self, index: usize, size_of: usize) -> *mut u8 {
196 unsafe { self.base_mut().sub(size_of * (index + 1)) }
197 }
198
199 fn bucket_ref_mut<T>(&self, index: usize) -> &mut T {
200 unsafe {
201 self.base_mut()
202 .cast::<T>()
203 .sub(index + 1)
204 .as_mut_unchecked()
205 }
206 }
207}
208
209impl CarryCtx<'_> {
210 #[inline]
211 pub fn new(base: Ref<u8>) -> CarryCtx<'_> {
212 CarryCtx { backing: base }
213 }
214}
215
216impl CarryCtxMut<'_> {
217 #[inline]
218 pub fn new(base: RefMut<u8>) -> CarryCtxMut<'_> {
219 CarryCtxMut { backing: base }
220 }
221}
222
223impl Ctx for CarryCtx<'_> {
224 #[inline]
225 fn base(&self) -> *const u8 {
226 self.backing.raw()
227 }
228}
229
230impl Ctx for CarryCtxMut<'_> {
231 #[inline]
232 fn base(&self) -> *const u8 {
233 self.backing.raw().cast_const()
234 }
235}
236
237impl CtxMut for CarryCtxMut<'_> {
238 #[inline]
239 fn base_mut(&self) -> *mut u8 {
240 self.backing.raw()
241 }
242}
243
244pub struct RawTable<T: Invariant, S = DefaultHashBuilder, A: Allocator = HashTableAlloc> {
245 table: RawTableInner,
246 hasher: S,
248 alloc: A,
249 _phantom: PhantomData<T>,
250}
251
252impl<T: Invariant, S, A: Allocator> BaseType for RawTable<T, S, A> {}
253
254impl<T: Invariant> RawTable<T, DefaultHashBuilder, HashTableAlloc> {
255 pub fn new() -> Self {
256 Self {
257 table: RawTableInner::new(),
258 hasher: DefaultHashBuilder::default(),
259 alloc: HashTableAlloc::default(),
260 _phantom: PhantomData,
261 }
262 }
263}
264
265impl<T: Invariant, S, A: Allocator> RawTable<T, S, A> {
266 const TABLE_LAYOUT: TableLayout = TableLayout::new::<T>();
267
268 pub fn hasher(&self) -> &S {
269 &self.hasher
270 }
271
272 pub fn allocator(&self) -> &A {
273 &self.alloc
274 }
275
276 pub fn len(&self) -> usize {
277 self.table.items
278 }
279
280 pub fn capacity(&self) -> usize {
281 self.table.growth_left
282 }
283
284 pub fn with_hasher_in(hasher: S, alloc: A) -> Self {
285 Self {
286 table: RawTableInner::new(),
287 hasher,
288 alloc,
289 _phantom: PhantomData,
290 }
291 }
292
293 pub fn remove(
294 &mut self,
295 hash: u64,
296 mut eq: impl FnMut(&T) -> bool,
297 ctx: &impl CtxMut,
298 ) -> Option<T> {
299 match unsafe {
300 self.table
301 .find_inner(hash, &mut |index| eq(&self.bucket(index)), ctx)
302 } {
303 Some(bucket) => Some(unsafe {
304 let item = self.bucket(bucket).raw().read();
305 self.table.erase(bucket);
306 item
307 }),
308 None => None,
309 }
310 }
311
312 pub fn get(&self, hash: u64, mut eq: impl FnMut(&T) -> bool, ctx: &impl Ctx) -> Option<&T> {
313 unsafe {
314 let result = self
315 .table
316 .find_inner(hash, &mut |index| eq(ctx.bucket::<T>(index)), ctx);
317
318 match result {
319 Some(index) => Some(ctx.bucket(index)),
320 None => None,
321 }
322 }
323 }
324
325 pub fn get_mut<'a>(
326 &mut self,
327 hash: u64,
328 mut eq: impl FnMut(&T) -> bool,
329 ctx: &'a impl CtxMut,
330 ) -> Option<&'a mut T> {
331 unsafe {
332 let result = self
333 .table
334 .find_inner(hash, &mut |index| eq(ctx.bucket::<T>(index)), ctx);
335
336 match result {
337 Some(index) => Some(ctx.bucket_ref_mut(index)),
338 None => None,
339 }
340 }
341 }
342
343 pub fn clear(&mut self) {
344 if self.table.is_empty_singleton() {
345 return;
346 }
347 let mut ctrl = unsafe { self.table.ctrl_slice_mut() };
348
349 ctrl.fill_tag(Tag::EMPTY);
350 self.table.items = 0;
351 }
352
353 fn bucket(&self, index: usize) -> Ref<T> {
354 unsafe { self.table.bucket::<T>(index) }
355 }
356
357 pub fn carry_ctx(&self) -> CarryCtx {
358 CarryCtx::new(unsafe { self.table.ctrl.resolve() })
359 }
360
361 pub fn carry_ctx_mut<'a>(&self, base: &RefMut<'a, RawTable<T, S, A>>) -> CarryCtxMut<'a> {
362 CarryCtxMut::new(unsafe { base.table.ctrl.resolve_mut().owned() })
363 }
364
365 pub unsafe fn iter(&self) -> RawIter<T> {
366 self.table.iter()
367 }
368
369 pub unsafe fn backing(&self) -> Ref<u8> {
370 self.table.ctrl.resolve()
371 }
372
373 pub unsafe fn backing_mut(&mut self) -> RefMut<u8> {
374 self.table.ctrl.resolve_mut()
375 }
376}
377
378impl<T: Invariant, S> RawTable<T, S, HashTableAlloc> {
379 pub fn bootstrap(&mut self, capacity: usize) -> Result<()> {
380 unsafe {
381 self.table
382 .bootstrap(&self.alloc, capacity, &Self::TABLE_LAYOUT)
383 }
384 }
385
386 pub fn reserve(&mut self, additional: usize, hasher: impl Fn(&T) -> u64, ctx: &impl CtxMut) {
387 if core::intrinsics::unlikely(additional > self.table.growth_left) {
388 self.reserve_rehash(additional, hasher, ctx).unwrap();
389 }
390 }
391
392 pub fn reserve_rehash(
393 &mut self,
394 additional: usize,
395 hasher: impl Fn(&T) -> u64,
396 ctx: &impl CtxMut,
397 ) -> Result<()> {
398 unsafe {
399 self.table.reserve_rehash_inner(
400 &self.alloc,
401 additional,
402 &|_table, index| hasher(&ctx.bucket(index)),
403 &Self::TABLE_LAYOUT,
404 None,
405 ctx,
406 )
407 }
408 }
409
410 pub unsafe fn resize(
411 &mut self,
412 capacity: usize,
413 hasher: impl Fn(&T) -> u64,
414 ctx: &impl CtxMut,
415 ) -> Result<()> {
416 self.table.resize_inner(
417 &self.alloc,
418 capacity,
419 &|_table, index| hasher(ctx.bucket(index)),
420 &Self::TABLE_LAYOUT,
421 ctx,
422 )
423 }
424
425 pub fn find_or_find_insert_slot(
427 &mut self,
428 hash: u64,
429 mut eq: impl FnMut(&T) -> bool,
430 hasher: impl Fn(&T) -> u64,
431 ctx: &impl CtxMut,
432 ) -> std::result::Result<Ref<T>, usize> {
433 self.reserve(1, hasher, ctx);
435
436 unsafe {
437 match self.table.find_or_find_insert_slot_inner(
438 hash,
439 &mut |index| eq(ctx.bucket(index)),
440 ctx,
441 ) {
442 Ok(index) => Ok(self.bucket(index)),
443 Err(slot) => Err(slot),
444 }
445 }
446 }
447
448 #[inline]
449 pub unsafe fn insert_in_slot(&mut self, hash: u64, slot: usize, value: T, ctx: &impl CtxMut) {
450 {
451 let ctrl = ctx.ctrl_mut(self.table.buckets());
452 let old_ctrl = ctrl[slot];
453 self.table.growth_left -= usize::from(old_ctrl.special_is_empty());
454 ctrl[slot] = Tag::full(hash);
455 self.table.items += 1;
456 }
457
458 let bucket = ctx.bucket_ptr(slot, size_of::<T>()).cast::<T>();
459
460 *bucket = value;
461 }
462}
463
464#[derive(Debug)]
465pub struct RawTableInner {
466 ctrl: InvPtr<u8>,
467 bucket_mask: usize,
468 growth_left: usize,
469 items: usize,
470}
471
472impl RawTableInner {
473 pub fn new() -> Self {
474 Self {
475 ctrl: InvPtr::null(),
476 bucket_mask: 0,
477 growth_left: 0,
478 items: 0,
479 }
480 }
481
482 pub fn len(&self) -> usize {
483 self.items
484 }
485
486 pub fn buckets(&self) -> usize {
487 self.bucket_mask + 1
488 }
489
490 pub fn is_empty(&self) -> bool {
491 self.len() == 0
492 }
493
494 fn is_empty_singleton(&self) -> bool {
495 self.bucket_mask == 0
496 }
497}
498
499impl RawTableInner {
500 fn probe_seq(&self, hash: u64) -> ProbeSeq {
501 ProbeSeq {
502 pos: h1(hash) & self.bucket_mask,
503 }
504 }
505
506 unsafe fn find_or_find_insert_slot_inner(
507 &self,
508 hash: u64,
509 eq: &mut dyn FnMut(usize) -> bool,
510 ctx: &impl Ctx,
511 ) -> std::result::Result<usize, usize> {
512 let tag_hash = Tag::full(hash);
513 let mut probe_seq = self.probe_seq(hash);
514
515 let ctrl = ctx.ctrl(self.buckets());
516 loop {
517 let tag = ctrl[probe_seq.pos];
518
519 if tag.eq(&tag_hash) && eq(probe_seq.pos) {
520 return Ok(probe_seq.pos);
521 }
522
523 if tag.is_special() && tag.special_is_empty() {
527 return Err(probe_seq.pos);
528 }
529
530 probe_seq.move_next(self.bucket_mask);
531 }
532 }
533
534 unsafe fn find_insert_slot(&self, hash: u64, ctx: &impl Ctx) -> usize {
535 let mut probe_seq = self.probe_seq(hash);
536 let ctrl_slice = ctx.ctrl(self.buckets());
537 loop {
538 let tag = ctrl_slice[probe_seq.pos];
539 if tag.is_special() {
540 return probe_seq.pos;
541 }
542 probe_seq.move_next(self.bucket_mask);
543 }
544 }
545
546 unsafe fn find_inner(
547 &self,
548 hash: u64,
549 eq: &mut dyn FnMut(usize) -> bool,
550 ctx: &impl Ctx,
551 ) -> Option<usize> {
552 debug_assert!(!self.is_empty());
553
554 let tag_hash = Tag::full(hash);
555 let mut probe_seq = self.probe_seq(hash);
556 let ctrl_slice = ctx.ctrl(self.buckets());
557
558 loop {
559 let tag = ctrl_slice[probe_seq.pos];
560
561 if tag.eq(&tag_hash) && eq(probe_seq.pos) {
562 return Some(probe_seq.pos);
563 }
564
565 if tag.is_special() && tag.special_is_empty() {
566 return None;
567 }
568
569 probe_seq.move_next(self.bucket_mask);
570 }
571 }
572
573 unsafe fn reserve_rehash_inner(
574 &mut self,
575 alloc: &HashTableAlloc,
576 additional: usize,
577 hasher: &dyn Fn(&mut Self, usize) -> u64,
578 table_layout: &TableLayout,
579 drop: Option<unsafe fn(*mut u8)>,
580 ctx: &impl CtxMut,
581 ) -> Result<()> {
582 let new_items = self.items + additional;
583 let full_capacity = bucket_mask_to_capacity(self.bucket_mask);
584 if new_items <= full_capacity / 2 {
585 self.rehash_in_place(hasher, table_layout.size, drop, ctx);
586 } else {
587 self.resize_inner(
588 alloc,
589 usize::max(new_items, full_capacity + 1),
590 hasher,
591 table_layout,
592 ctx,
593 )?
594 }
595
596 Ok(())
597 }
598
599 unsafe fn rehash_in_place(
601 &mut self,
602 hasher: &dyn Fn(&mut Self, usize) -> u64,
603 size_of: usize,
604 drop: Option<unsafe fn(*mut u8)>,
605 ctx: &impl CtxMut,
606 ) {
607 self.prepare_rehash_in_place(ctx);
608
609 self.rehash_in_place_inner(hasher, size_of, self.buckets(), drop, ctx);
610 }
611
612 unsafe fn rehash_in_place_inner(
613 &mut self,
614 hasher: &dyn Fn(&mut Self, usize) -> u64,
615 size_of: usize,
616 workable_buckets: usize,
617 _drop: Option<unsafe fn(*mut u8)>,
618 ctx: &impl CtxMut,
619 ) {
620 let ctrl = ctx.ctrl_mut(self.buckets());
623
624 'outer: for i in 0..workable_buckets {
625 if ctrl[i] != Tag::DELETED {
626 continue;
627 }
628
629 let i_p = ctx.bucket_ptr(i, size_of);
630 'inner: loop {
631 let hash = hasher(self, i);
632
633 let new_i = self.find_insert_slot(hash, ctx);
634
635 if i == new_i {
636 self.replace_ctrl_hash(new_i, hash, ctx);
637 continue 'outer;
638 }
639
640 let new_i_p = ctx.bucket_ptr(new_i, size_of);
641
642 let prev_ctrl = self.replace_ctrl_hash(new_i, hash, ctx);
643 if prev_ctrl == Tag::EMPTY {
644 ctrl[i] = Tag::EMPTY;
645 std::ptr::copy_nonoverlapping(i_p, new_i_p, size_of);
646 continue 'outer;
647 } else {
648 debug_assert_eq!(prev_ctrl, Tag::DELETED);
649 std::ptr::swap_nonoverlapping(i_p, new_i_p, size_of);
650 continue 'inner;
651 }
652 }
653 }
654
655 self.growth_left = bucket_mask_to_capacity(self.bucket_mask) - self.items;
656 }
657
658 unsafe fn prepare_rehash_in_place(&mut self, ctx: &impl CtxMut) {
659 if self.is_empty() {
660 return;
661 }
662
663 let slice = ctx.ctrl_mut(self.buckets());
664 for i in 0..self.buckets() {
665 if slice[i].is_full() {
666 slice[i] = Tag::DELETED;
667 } else {
668 slice[i] = Tag::EMPTY;
669 }
670 }
671 }
672
673 unsafe fn bootstrap(
674 &mut self,
675 alloc: &HashTableAlloc,
676 capacity: usize,
677 table_layout: &TableLayout,
678 ) -> Result<()> {
679 let buckets = std::cmp::max((capacity * 8 / 7).next_power_of_two(), 8);
680
681 let global = alloc.allocate(table_layout, buckets)?;
682 self.ctrl = InvPtr::new(Ref::from_ptr(self), global)?;
683 self.bucket_mask = buckets - 1;
684 self.growth_left = bucket_mask_to_capacity(self.bucket_mask);
685 self.items = 0;
686
687 self.ctrl_slice_mut().fill_empty();
688
689 Ok(())
690 }
691
692 unsafe fn resize_inner(
698 &mut self,
699 alloc: &HashTableAlloc,
700 capacity: usize,
701 hasher: &dyn Fn(&mut Self, usize) -> u64,
702 table_layout: &TableLayout,
703 ctx: &impl CtxMut,
704 ) -> Result<()> {
705 debug_assert!(self.items <= capacity);
706
707 let old_buckets = self.buckets();
708
709 let buckets = std::cmp::max((capacity * 8 / 7).next_power_of_two(), 8);
712 let global = alloc.allocate(table_layout, buckets)?;
713 if !self.is_empty_singleton() {
714 self.prepare_rehash_in_place(ctx);
715 let p = ctx.base_mut().add(self.buckets()).cast::<Tag>();
716 let slice = std::slice::from_raw_parts_mut(p, buckets - self.buckets());
717 slice.fill_empty();
718
719 self.bucket_mask = buckets - 1;
720 self.growth_left = bucket_mask_to_capacity(self.bucket_mask) - self.items;
721 self.rehash_in_place_inner(hasher, table_layout.size, old_buckets, None, ctx);
722 } else {
723 self.ctrl = InvPtr::new(Ref::from_ptr(self), global)?;
724 self.bucket_mask = buckets - 1;
725 self.growth_left = bucket_mask_to_capacity(self.bucket_mask);
726 self.items = 0;
727
728 self.ctrl_slice_mut().fill_empty();
729 }
730
731 Ok(())
732 }
733
734 unsafe fn erase(&mut self, index: usize) {
735 self.set_ctrl(index, Tag::DELETED);
736 self.items -= 1;
737 }
738
739 unsafe fn bucket<T: Invariant>(&self, index: usize) -> Ref<T> {
740 unsafe { self.ctrl.resolve().cast::<T>().sub(index + 1) }
741 }
742
743 #[inline]
744 unsafe fn replace_ctrl_hash(&mut self, index: usize, hash: u64, ctx: &impl CtxMut) -> Tag {
745 let ctrl = ctx.ctrl_mut(self.buckets());
746 let prev_ctrl = ctrl[index];
747 ctrl[index] = Tag::full(hash);
748 prev_ctrl.to_owned()
749 }
750
751 #[inline]
752 unsafe fn set_ctrl(&mut self, index: usize, ctrl: Tag) {
753 self.ctrl_slice_mut()[index] = ctrl;
754 }
755
756 #[inline]
757 unsafe fn ctrl_slice_mut(&mut self) -> RefSliceMut<'_, Tag> {
758 let r = self.ctrl.resolve().cast::<Tag>().into_mut();
759 let slice = RefSliceMut::from_ref(r, self.buckets());
760 slice
761 }
762
763 unsafe fn iter<T: Invariant>(&self) -> RawIter<T> {
765 let data = self.ctrl.resolve().raw().cast::<T>().sub(1).cast_mut();
766 RawIter::new(
767 self.ctrl.resolve().raw().cast(),
768 NonNull::new(data).unwrap(),
769 self.buckets(),
770 )
771 }
772}
773
774pub struct RawIter<T: Invariant> {
777 data: NonNull<T>,
778 ctrl: *const Tag,
779 top: *const Tag,
780}
781
782impl<T: Invariant> RawIter<T> {
783 pub(crate) unsafe fn new(ctrl: *const Tag, data: NonNull<T>, len: usize) -> RawIter<T> {
784 let end = ctrl.add(len);
785
786 Self {
787 data,
788 ctrl,
789 top: end,
790 }
791 }
792
793 pub(crate) unsafe fn next_impl(&mut self) -> Option<NonNull<T>> {
794 loop {
795 if self.ctrl >= self.top {
796 return None;
797 }
798
799 if (*self.ctrl).is_full() {
800 self.data = self.data.sub(1);
801 self.ctrl = self.ctrl.add(1);
802 return Some(self.data.add(1));
803 }
804 self.data = self.data.sub(1);
805 self.ctrl = self.ctrl.add(1);
806 }
807 }
808}
809
810impl<T: Invariant> Iterator for RawIter<T> {
811 type Item = NonNull<T>;
812
813 fn next(&mut self) -> Option<Self::Item> {
814 unsafe { self.next_impl() }
815 }
816}