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