1use std::collections::BTreeMap;
37
38pub const NSHARDS_M: usize = 256;
41const SHARD_MASK_M: u64 = (NSHARDS_M - 1) as u64;
42const SHARD_SHIFT_M: u32 = 64 - 8; pub const FRONT_BITS_MEMORY: u8 = 16;
47const FRONT_SLOTS_MEMORY: usize = 1 << FRONT_BITS_MEMORY;
48const SPARSE_PAGE_BITS: u8 = 8;
49const SPARSE_PAGE_SIZE: usize = 1 << SPARSE_PAGE_BITS;
50const NO_PAGE: u32 = u32::MAX;
51
52const FRONT_TAG_MASK: u64 = 0b111;
54const FRONT_EMPTY: u64 = 0;
55const FRONT_SINGLE: u64 = 0b001;
56const FRONT_MICRO4: u64 = 0b010;
57const FRONT_MICRO8: u64 = 0b011;
58const FRONT_MICRO16: u64 = 0b100;
59const FRONT_FALLBACK: u64 = 0b101;
60
61#[inline(always)]
64pub fn deterministic_permutation_scatter(mut x: u64) -> u64 {
65 x ^= x >> 30;
66 x = x.wrapping_mul(0xbf58_476d_1ce4_e5b9);
67 x ^= x >> 27;
68 x = x.wrapping_mul(0x94d0_49bb_1331_11eb);
69 x ^ (x >> 31)
70}
71
72#[inline(always)]
74fn front_prefix(scattered: u64) -> usize {
75 (scattered >> (64 - FRONT_BITS_MEMORY)) as usize
76}
77
78#[inline(always)]
79fn pack_front_single(shard_id: usize, leaf_id: usize) -> u64 {
80 debug_assert!(shard_id < (1 << 29));
81 debug_assert!(leaf_id < (1 << 32));
82 ((shard_id as u64) << 35) | ((leaf_id as u64) << 3) | FRONT_SINGLE
83}
84
85#[inline(always)]
86fn unpack_front_single(packed: u64) -> (usize, usize) {
87 ((packed >> 35) as usize, ((packed >> 3) as u32) as usize)
88}
89
90#[inline(always)]
91fn pack_front_micro(bucket_id: usize, tag: u64) -> u64 {
92 debug_assert!(bucket_id < (1_usize << 61));
93 ((bucket_id as u64) << 3) | tag
94}
95
96#[inline(always)]
97fn unpack_front_micro(packed: u64) -> usize {
98 (packed >> 3) as usize
99}
100
101#[derive(Clone, Debug)]
103pub struct MicroBucket4 {
104 pub shard_id: u16,
105 pub count: u8,
106 pub keys: [u64; 4],
107 pub leaf_ids: [u32; 4],
108}
109
110impl MicroBucket4 {
111 #[inline(always)]
112 fn match_mask(&self, key: u64) -> u32 {
113 let mask = ((self.keys[0] == key) as u32)
114 | (((self.keys[1] == key) as u32) << 1)
115 | (((self.keys[2] == key) as u32) << 2)
116 | (((self.keys[3] == key) as u32) << 3);
117 let live = if self.count >= 4 { 0b1111 } else { (1u32 << self.count) - 1 };
118 mask & live
119 }
120}
121
122#[derive(Clone, Debug)]
124pub struct MicroBucket8 {
125 pub shard_id: u16,
126 pub count: u8,
127 pub keys: [u64; 8],
128 pub leaf_ids: [u32; 8],
129}
130
131impl MicroBucket8 {
132 #[inline(always)]
133 fn match_mask(&self, key: u64) -> u32 {
134 let mut mask = 0u32;
135 for slot in 0..8 {
136 mask |= ((self.keys[slot] == key) as u32) << slot;
137 }
138 let live = if self.count >= 8 { 0xff } else { (1u32 << self.count) - 1 };
139 mask & live
140 }
141}
142
143#[derive(Clone, Debug)]
145pub struct MicroBucket16 {
146 pub shard_id: u16,
147 pub count: u8,
148 pub keys: [u64; 16],
149 pub leaf_ids: [u32; 16],
150}
151
152impl MicroBucket16 {
153 #[inline(always)]
154 fn match_mask(&self, key: u64) -> u32 {
155 let mut mask = 0u32;
156 for slot in 0..16 {
157 mask |= ((self.keys[slot] == key) as u32) << slot;
158 }
159 let live = if self.count >= 16 {
160 u16::MAX as u32
161 } else {
162 (1u32 << self.count) - 1
163 };
164 mask & live
165 }
166}
167
168#[derive(Clone, Debug)]
171pub struct LeafNode<V: Clone> {
172 pub key: u64,
173 pub value: V,
174}
175
176const ART_EMPTY: u32 = u32::MAX;
192const NODE48_EMPTY: u8 = u8::MAX;
193
194#[derive(Clone, Copy, Debug, PartialEq, Eq)]
195struct TaggedNode(u32);
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq)]
198enum NodeKind {
199 Leaf,
200 Node4,
201 Node16,
202 Node32,
203 Node48,
204 Node256,
205}
206
207impl TaggedNode {
208 const TAG_BITS: u32 = 3;
209 const INDEX_SHIFT: u32 = 3;
210 const LEAF: u32 = 0;
211 const N4: u32 = 1;
212 const N16: u32 = 2;
213 const N32: u32 = 3;
214 const N48: u32 = 4;
215 const N256: u32 = 5;
216
217 fn new(index: usize, tag: u32) -> Self {
218 debug_assert!(tag < (1 << Self::TAG_BITS));
219 assert!(index < (1usize << (32 - Self::INDEX_SHIFT)));
220 Self(((index as u32) << Self::INDEX_SHIFT) | tag)
221 }
222 fn leaf(i: usize) -> Self { Self::new(i, Self::LEAF) }
223 fn n4(i: usize) -> Self { Self::new(i, Self::N4) }
224 fn n16(i: usize) -> Self { Self::new(i, Self::N16) }
225 fn n32(i: usize) -> Self { Self::new(i, Self::N32) }
226 fn n48(i: usize) -> Self { Self::new(i, Self::N48) }
227 fn n256(i: usize) -> Self { Self::new(i, Self::N256) }
228
229 fn kind(self) -> NodeKind {
230 match self.0 & ((1 << Self::TAG_BITS) - 1) {
231 Self::LEAF => NodeKind::Leaf,
232 Self::N4 => NodeKind::Node4,
233 Self::N16 => NodeKind::Node16,
234 Self::N32 => NodeKind::Node32,
235 Self::N48 => NodeKind::Node48,
236 Self::N256 => NodeKind::Node256,
237 _ => unreachable!(),
238 }
239 }
240
241 fn index(self) -> usize { (self.0 >> Self::INDEX_SHIFT) as usize }
242}
243
244#[derive(Clone, Debug)]
245struct ArtNode4 { count: u8, keys: [u8; 4], children: [u32; 4] }
246#[derive(Clone, Debug)]
247struct ArtNode16 { count: u8, keys: [u8; 16], children: [u32; 16] }
248#[derive(Clone, Debug)]
249struct ArtNode32 { count: u8, keys: [u8; 32], children: [u32; 32] }
250#[derive(Clone, Debug)]
251struct ArtNode48 { count: u8, child_index: [u8; 256], children: [u32; 48] }
252#[derive(Clone, Debug)]
253struct ArtNode256 { count: u16, children: [u32; 256] }
254
255#[derive(Clone, Debug)]
259struct ArtSlab<V: Clone> {
260 entries: Vec<TaggedNode>,
261 leaves: Vec<LeafNode<V>>,
262 node4: Vec<ArtNode4>,
263 node16: Vec<ArtNode16>,
264 node32: Vec<ArtNode32>,
265 node48: Vec<ArtNode48>,
266 node256: Vec<ArtNode256>,
267}
268
269impl<V: Clone> ArtSlab<V> {
270 fn new() -> Self {
271 Self {
272 entries: Vec::new(),
273 leaves: Vec::new(),
274 node4: Vec::new(),
275 node16: Vec::new(),
276 node32: Vec::new(),
277 node48: Vec::new(),
278 node256: Vec::new(),
279 }
280 }
281
282 fn alloc_entry(&mut self, t: TaggedNode) -> u32 {
283 let id = self.entries.len() as u32;
284 self.entries.push(t);
285 id
286 }
287 fn alloc_leaf(&mut self, key: u64, value: V) -> u32 {
288 let lid = self.leaves.len();
289 self.leaves.push(LeafNode { key, value });
290 self.alloc_entry(TaggedNode::leaf(lid))
291 }
292 fn alloc_node4(&mut self) -> u32 {
293 let nid = self.node4.len();
294 self.node4.push(ArtNode4 { count: 0, keys: [0; 4], children: [ART_EMPTY; 4] });
295 self.alloc_entry(TaggedNode::n4(nid))
296 }
297
298 #[inline(always)]
300 fn radix(scattered: u64, depth: usize) -> u8 {
301 if depth >= 8 { 0 } else { (scattered >> ((7 - depth) * 8)) as u8 }
302 }
303
304 fn find_child(&self, entry_id: u32, radix: u8) -> Option<u32> {
305 let entry = self.entries[entry_id as usize];
306 match entry.kind() {
307 NodeKind::Leaf => None,
308 NodeKind::Node4 => {
309 let n = &self.node4[entry.index()];
310 for i in 0..n.count as usize {
311 if n.keys[i] == radix { return Some(n.children[i]); }
312 }
313 None
314 }
315 NodeKind::Node16 => {
316 let n = &self.node16[entry.index()];
317 for i in 0..n.count as usize {
318 if n.keys[i] == radix { return Some(n.children[i]); }
319 }
320 None
321 }
322 NodeKind::Node32 => {
323 let n = &self.node32[entry.index()];
324 for i in 0..n.count as usize {
325 if n.keys[i] == radix { return Some(n.children[i]); }
326 }
327 None
328 }
329 NodeKind::Node48 => {
330 let n = &self.node48[entry.index()];
331 let slot = n.child_index[radix as usize];
332 if slot == NODE48_EMPTY { None } else { Some(n.children[slot as usize]) }
333 }
334 NodeKind::Node256 => {
335 let c = self.node256[entry.index()].children[radix as usize];
336 if c == ART_EMPTY { None } else { Some(c) }
337 }
338 }
339 }
340
341 fn add_child(&mut self, entry_id: u32, radix: u8, child: u32) {
342 let entry = self.entries[entry_id as usize];
343 match entry.kind() {
344 NodeKind::Node4 if self.node4[entry.index()].count < 4 => {
345 let n = &mut self.node4[entry.index()];
346 let s = n.count as usize;
347 n.keys[s] = radix; n.children[s] = child; n.count += 1;
348 }
349 NodeKind::Node4 => { self.grow_4_to_16(entry_id); self.add_child(entry_id, radix, child); }
350 NodeKind::Node16 if self.node16[entry.index()].count < 16 => {
351 let n = &mut self.node16[entry.index()];
352 let s = n.count as usize;
353 n.keys[s] = radix; n.children[s] = child; n.count += 1;
354 }
355 NodeKind::Node16 => { self.grow_16_to_32(entry_id); self.add_child(entry_id, radix, child); }
356 NodeKind::Node32 if self.node32[entry.index()].count < 32 => {
357 let n = &mut self.node32[entry.index()];
358 let s = n.count as usize;
359 n.keys[s] = radix; n.children[s] = child; n.count += 1;
360 }
361 NodeKind::Node32 => { self.grow_32_to_48(entry_id); self.add_child(entry_id, radix, child); }
362 NodeKind::Node48 if self.node48[entry.index()].count < 48 => {
363 let n = &mut self.node48[entry.index()];
364 let s = n.count as usize;
365 n.child_index[radix as usize] = s as u8;
366 n.children[s] = child;
367 n.count += 1;
368 }
369 NodeKind::Node48 => { self.grow_48_to_256(entry_id); self.add_child(entry_id, radix, child); }
370 NodeKind::Node256 => {
371 let n = &mut self.node256[entry.index()];
372 if n.children[radix as usize] == ART_EMPTY { n.count += 1; }
373 n.children[radix as usize] = child;
374 }
375 NodeKind::Leaf => unreachable!("cannot add child to leaf"),
376 }
377 }
378
379 fn grow_4_to_16(&mut self, entry_id: u32) {
380 let old_idx = self.entries[entry_id as usize].index();
381 let old = &self.node4[old_idx];
382 let mut next = ArtNode16 { count: old.count, keys: [0; 16], children: [ART_EMPTY; 16] };
383 let c = old.count as usize;
384 next.keys[..c].copy_from_slice(&old.keys[..c]);
385 next.children[..c].copy_from_slice(&old.children[..c]);
386 let new_idx = self.node16.len();
387 self.node16.push(next);
388 self.entries[entry_id as usize] = TaggedNode::n16(new_idx);
389 }
390 fn grow_16_to_32(&mut self, entry_id: u32) {
391 let old_idx = self.entries[entry_id as usize].index();
392 let old = &self.node16[old_idx];
393 let mut next = ArtNode32 { count: old.count, keys: [0; 32], children: [ART_EMPTY; 32] };
394 let c = old.count as usize;
395 next.keys[..c].copy_from_slice(&old.keys[..c]);
396 next.children[..c].copy_from_slice(&old.children[..c]);
397 let new_idx = self.node32.len();
398 self.node32.push(next);
399 self.entries[entry_id as usize] = TaggedNode::n32(new_idx);
400 }
401 fn grow_32_to_48(&mut self, entry_id: u32) {
402 let old_idx = self.entries[entry_id as usize].index();
403 let old = &self.node32[old_idx];
404 let mut next = ArtNode48 {
405 count: old.count, child_index: [NODE48_EMPTY; 256], children: [ART_EMPTY; 48],
406 };
407 for i in 0..old.count as usize {
408 next.child_index[old.keys[i] as usize] = i as u8;
409 next.children[i] = old.children[i];
410 }
411 let new_idx = self.node48.len();
412 self.node48.push(next);
413 self.entries[entry_id as usize] = TaggedNode::n48(new_idx);
414 }
415 fn grow_48_to_256(&mut self, entry_id: u32) {
416 let old_idx = self.entries[entry_id as usize].index();
417 let old = &self.node48[old_idx];
418 let mut next = ArtNode256 { count: old.count as u16, children: [ART_EMPTY; 256] };
419 for radix in 0..=u8::MAX {
420 let s = old.child_index[radix as usize];
421 if s != NODE48_EMPTY {
422 next.children[radix as usize] = old.children[s as usize];
423 }
424 }
425 let new_idx = self.node256.len();
426 self.node256.push(next);
427 self.entries[entry_id as usize] = TaggedNode::n256(new_idx);
428 }
429
430 fn insert(
434 &mut self,
435 root: Option<u32>,
436 key: u64,
437 scattered: u64,
438 value: V,
439 ) -> (u32, Option<V>) {
440 match root {
441 None => {
442 let leaf = self.alloc_leaf(key, value);
443 (leaf, None)
444 }
445 Some(r) => {
446 let prev = self.insert_at(r, 1, key, scattered, value);
447 (r, prev)
448 }
449 }
450 }
451
452 fn insert_at(
453 &mut self,
454 node_id: u32,
455 depth: usize,
456 key: u64,
457 scattered: u64,
458 value: V,
459 ) -> Option<V> {
460 if self.entries[node_id as usize].kind() == NodeKind::Leaf {
461 let leaf_id = self.entries[node_id as usize].index();
462 let leaf = &mut self.leaves[leaf_id];
463 if leaf.key == key {
464 return Some(std::mem::replace(&mut leaf.value, value));
465 }
466 let old_leaf_entry = self.alloc_entry(TaggedNode::leaf(leaf_id));
468 let new_leaf_entry = self.alloc_leaf(key, value);
469 let old_scattered = deterministic_permutation_scatter(self.leaves[leaf_id].key);
470 self.join_leaves_in_place(node_id, depth, old_scattered, old_leaf_entry, scattered, new_leaf_entry);
471 return None;
472 }
473 let radix = Self::radix(scattered, depth);
474 if let Some(child) = self.find_child(node_id, radix) {
475 return self.insert_at(child, depth + 1, key, scattered, value);
476 }
477 let leaf = self.alloc_leaf(key, value);
478 self.add_child(node_id, radix, leaf);
479 None
480 }
481
482 fn join_leaves_in_place(
483 &mut self,
484 target: u32,
485 depth: usize,
486 left_scattered: u64,
487 left: u32,
488 right_scattered: u64,
489 right: u32,
490 ) {
491 let lr = Self::radix(left_scattered, depth);
492 let rr = Self::radix(right_scattered, depth);
493 let n4 = self.node4.len();
494 self.node4.push(ArtNode4 { count: 0, keys: [0; 4], children: [ART_EMPTY; 4] });
495 self.entries[target as usize] = TaggedNode::n4(n4);
496 if lr == rr && depth < 7 {
497 let child = self.alloc_node4();
498 self.add_child(target, lr, child);
499 self.join_leaves_in_place(child, depth + 1, left_scattered, left, right_scattered, right);
500 } else {
501 self.add_child(target, lr, left);
502 self.add_child(target, rr, right);
503 }
504 }
505
506 fn get(&self, root: Option<u32>, key: u64, scattered: u64) -> Option<&V> {
507 let mut node_id = root?;
508 let mut depth = 1;
509 loop {
510 let entry = self.entries[node_id as usize];
511 match entry.kind() {
512 NodeKind::Leaf => {
513 let leaf = &self.leaves[entry.index()];
514 return if leaf.key == key { Some(&leaf.value) } else { None };
515 }
516 _ => {
517 let r = Self::radix(scattered, depth);
518 let child = self.find_child(node_id, r)?;
519 node_id = child;
520 depth += 1;
521 }
522 }
523 }
524 }
525}
526
527#[derive(Clone, Debug)]
530struct Shard<V: Clone> {
531 leaves: Vec<LeafNode<V>>,
532 pre_seal: BTreeMap<u64, u32>,
534 art: ArtSlab<V>,
538 art_root: Option<u32>,
539}
540
541impl<V: Clone> Shard<V> {
542 fn new() -> Self {
543 Self {
544 leaves: Vec::new(),
545 pre_seal: BTreeMap::new(),
546 art: ArtSlab::new(),
547 art_root: None,
548 }
549 }
550
551 fn art_insert(&mut self, key: u64, scattered: u64, value: V) {
552 let (new_root, _prev) = self.art.insert(self.art_root, key, scattered, value);
553 self.art_root = Some(new_root);
554 }
555
556 fn art_get(&self, key: u64, scattered: u64) -> Option<&V> {
557 self.art.get(self.art_root, key, scattered)
558 }
559}
560
561#[derive(Clone, Debug)]
565struct FrontDir {
566 page_table: Vec<u32>,
567 pages: Vec<Box<[u64; SPARSE_PAGE_SIZE]>>,
568}
569
570impl FrontDir {
571 fn empty() -> Self {
572 Self {
573 page_table: vec![NO_PAGE; FRONT_SLOTS_MEMORY / SPARSE_PAGE_SIZE],
574 pages: Vec::new(),
575 }
576 }
577
578 #[inline(always)]
579 fn get(&self, prefix: usize) -> u64 {
580 let page_id = self.page_table[prefix >> SPARSE_PAGE_BITS];
581 if page_id == NO_PAGE {
582 FRONT_EMPTY
583 } else {
584 self.pages[page_id as usize][prefix & (SPARSE_PAGE_SIZE - 1)]
585 }
586 }
587
588 fn ensure_page(&mut self, page: usize) -> usize {
589 if self.page_table[page] == NO_PAGE {
590 self.page_table[page] = self.pages.len() as u32;
591 self.pages.push(Box::new([FRONT_EMPTY; SPARSE_PAGE_SIZE]));
592 }
593 self.page_table[page] as usize
594 }
595
596 fn set(&mut self, prefix: usize, value: u64) {
597 let page = prefix >> SPARSE_PAGE_BITS;
598 let pid = self.ensure_page(page);
599 self.pages[pid][prefix & (SPARSE_PAGE_SIZE - 1)] = value;
600 }
601}
602
603#[derive(Clone, Debug)]
605pub struct DHarhtMemory<V: Clone> {
606 shards: Vec<Shard<V>>,
607 front: FrontDir,
608 micro4: Vec<MicroBucket4>,
609 micro8: Vec<MicroBucket8>,
610 micro16: Vec<MicroBucket16>,
611 overflow: BTreeMap<u64, V>,
614 sealed: bool,
615 total_entries: u64,
616 micro_overflow_count: u64,
618 max_collision_group: u32,
619}
620
621impl<V: Clone> DHarhtMemory<V> {
622 pub fn new() -> Self {
623 Self {
624 shards: (0..NSHARDS_M).map(|_| Shard::new()).collect(),
625 front: FrontDir::empty(),
626 micro4: Vec::new(),
627 micro8: Vec::new(),
628 micro16: Vec::new(),
629 overflow: BTreeMap::new(),
630 sealed: false,
631 total_entries: 0,
632 micro_overflow_count: 0,
633 max_collision_group: 0,
634 }
635 }
636
637 pub fn is_sealed(&self) -> bool {
638 self.sealed
639 }
640 pub fn len(&self) -> u64 {
641 self.total_entries
642 }
643 pub fn is_empty(&self) -> bool {
644 self.total_entries == 0
645 }
646 pub fn micro_overflow_count(&self) -> u64 {
647 self.micro_overflow_count
648 }
649 pub fn max_collision_group(&self) -> u32 {
650 self.max_collision_group
651 }
652
653 pub fn approx_memory_bytes(&self) -> usize {
657 use std::mem::size_of;
658 let mut total = size_of::<Self>();
659 for shard in &self.shards {
660 total += size_of::<Shard<V>>();
661 total += shard.leaves.capacity() * size_of::<LeafNode<V>>();
662 total += shard.pre_seal.len() * 56;
664 }
665 total += self.front.page_table.capacity() * size_of::<u32>();
667 total += self.front.pages.len() * size_of::<Box<[u64; SPARSE_PAGE_SIZE]>>();
668 total += self.front.pages.len() * SPARSE_PAGE_SIZE * size_of::<u64>();
669 total += self.micro4.capacity() * size_of::<MicroBucket4>();
671 total += self.micro8.capacity() * size_of::<MicroBucket8>();
672 total += self.micro16.capacity() * size_of::<MicroBucket16>();
673 total += self.overflow.len() * (size_of::<u64>() + size_of::<V>() + 56);
675 total
676 }
677
678 #[inline(always)]
679 fn shard_id(scattered: u64) -> usize {
680 ((scattered >> SHARD_SHIFT_M) & SHARD_MASK_M) as usize
681 }
682
683 pub fn insert(&mut self, key: u64, value: V) -> Option<V> {
686 debug_assert!(!self.sealed, "DHarhtMemory: insert after seal");
687 let scattered = deterministic_permutation_scatter(key);
688 let s = Self::shard_id(scattered);
689 let shard = &mut self.shards[s];
690 if let Some(&leaf_id) = shard.pre_seal.get(&key) {
691 let prev = std::mem::replace(&mut shard.leaves[leaf_id as usize].value, value);
692 return Some(prev);
693 }
694 let leaf_id = shard.leaves.len() as u32;
695 shard.leaves.push(LeafNode { key, value });
696 shard.pre_seal.insert(key, leaf_id);
697 self.total_entries += 1;
698 None
699 }
700
701 #[inline]
704 pub fn get(&self, key: u64) -> Option<&V> {
705 let scattered = deterministic_permutation_scatter(key);
706 if !self.sealed {
707 let s = Self::shard_id(scattered);
708 let shard = &self.shards[s];
709 return shard
710 .pre_seal
711 .get(&key)
712 .map(|&leaf_id| &shard.leaves[leaf_id as usize].value);
713 }
714 let prefix = front_prefix(scattered);
715 let entry = self.front.get(prefix);
716 match entry & FRONT_TAG_MASK {
717 t if t == FRONT_SINGLE => {
718 let (sid, lid) = unpack_front_single(entry);
719 let leaf = &self.shards[sid].leaves[lid];
720 if leaf.key == key { Some(&leaf.value) } else { None }
721 }
722 t if t == FRONT_MICRO4 => {
723 let b = &self.micro4[unpack_front_micro(entry)];
724 let m = b.match_mask(key);
725 if m == 0 {
726 return None;
727 }
728 let slot = m.trailing_zeros() as usize;
729 let leaf = &self.shards[b.shard_id as usize].leaves[b.leaf_ids[slot] as usize];
730 Some(&leaf.value)
731 }
732 t if t == FRONT_MICRO8 => {
733 let b = &self.micro8[unpack_front_micro(entry)];
734 let m = b.match_mask(key);
735 if m == 0 {
736 return None;
737 }
738 let slot = m.trailing_zeros() as usize;
739 let leaf = &self.shards[b.shard_id as usize].leaves[b.leaf_ids[slot] as usize];
740 Some(&leaf.value)
741 }
742 t if t == FRONT_MICRO16 => {
743 let b = &self.micro16[unpack_front_micro(entry)];
744 let m = b.match_mask(key);
745 if m == 0 {
746 return None;
747 }
748 let slot = m.trailing_zeros() as usize;
749 let leaf = &self.shards[b.shard_id as usize].leaves[b.leaf_ids[slot] as usize];
750 Some(&leaf.value)
751 }
752 t if t == FRONT_FALLBACK => {
753 let s = Self::shard_id(scattered);
760 if let Some(v) = self.shards[s].art_get(key, scattered) {
761 Some(v)
762 } else {
763 self.overflow.get(&key)
764 }
765 }
766 _ => None,
767 }
768 }
769
770 pub fn contains_key(&self, key: u64) -> bool {
771 self.get(key).is_some()
772 }
773
774 pub fn seal_for_lookup(&mut self) {
779 self.micro4.clear();
780 self.micro8.clear();
781 self.micro16.clear();
782 self.overflow.clear();
783 self.front = FrontDir::empty();
784 self.micro_overflow_count = 0;
785 self.max_collision_group = 0;
786
787 let mut buckets: BTreeMap<usize, Vec<(usize, usize, u64)>> = BTreeMap::new();
790 for (sid, shard) in self.shards.iter().enumerate() {
791 for (lid, leaf) in shard.leaves.iter().enumerate() {
792 let prefix = front_prefix(deterministic_permutation_scatter(leaf.key));
793 buckets.entry(prefix).or_default().push((sid, lid, leaf.key));
794 }
795 }
796
797 for (prefix, mut group) in buckets {
798 group.sort_by_key(|&(s, l, k)| (k, s, l));
800 self.max_collision_group =
801 self.max_collision_group.max(group.len() as u32);
802
803 let packed = match group.len() {
804 0 => continue,
805 1 => {
806 let (sid, lid, _) = group[0];
807 pack_front_single(sid, lid)
808 }
809 2..=4 => {
810 let bid = self.micro4.len();
811 let mut b = MicroBucket4 {
812 shard_id: 0,
813 count: group.len() as u8,
814 keys: [0; 4],
815 leaf_ids: [0; 4],
816 };
817 for (slot, &(s, l, k)) in group.iter().enumerate() {
818 b.shard_id = s as u16;
819 b.keys[slot] = k;
820 b.leaf_ids[slot] = l as u32;
821 }
822 self.micro4.push(b);
823 pack_front_micro(bid, FRONT_MICRO4)
824 }
825 5..=8 => {
826 let bid = self.micro8.len();
827 let mut b = MicroBucket8 {
828 shard_id: 0,
829 count: group.len() as u8,
830 keys: [0; 8],
831 leaf_ids: [0; 8],
832 };
833 for (slot, &(s, l, k)) in group.iter().enumerate() {
834 b.shard_id = s as u16;
835 b.keys[slot] = k;
836 b.leaf_ids[slot] = l as u32;
837 }
838 self.micro8.push(b);
839 pack_front_micro(bid, FRONT_MICRO8)
840 }
841 9..=16 => {
842 let bid = self.micro16.len();
843 let mut b = MicroBucket16 {
844 shard_id: 0,
845 count: group.len() as u8,
846 keys: [0; 16],
847 leaf_ids: [0; 16],
848 };
849 for (slot, &(s, l, k)) in group.iter().enumerate() {
850 b.shard_id = s as u16;
851 b.keys[slot] = k;
852 b.leaf_ids[slot] = l as u32;
853 }
854 self.micro16.push(b);
855 pack_front_micro(bid, FRONT_MICRO16)
856 }
857 n => {
858 self.micro_overflow_count += n as u64;
867 for &(s, l, k) in &group {
868 let v = self.shards[s].leaves[l].value.clone();
869 let scattered = deterministic_permutation_scatter(k);
870 self.shards[s].art_insert(k, scattered, v);
871 }
872 FRONT_FALLBACK
873 }
874 };
875 self.front.set(prefix, packed);
876 }
877 self.sealed = true;
878 }
879
880 pub fn iter_sorted(&self) -> Vec<(u64, &V)> {
884 if !self.sealed {
885 let mut all: Vec<(u64, &V)> = Vec::with_capacity(self.total_entries as usize);
886 for shard in &self.shards {
887 for leaf in &shard.leaves {
888 all.push((leaf.key, &leaf.value));
889 }
890 }
891 all.sort_by_key(|&(k, _)| k);
892 return all;
893 }
894 let mut all: Vec<(u64, &V)> = Vec::with_capacity(self.total_entries as usize);
895 for shard in &self.shards {
896 for leaf in &shard.leaves {
897 all.push((leaf.key, &leaf.value));
898 }
899 }
900 all.sort_by_key(|&(k, _)| k);
901 all
902 }
903}
904
905impl<V: Clone> Default for DHarhtMemory<V> {
906 fn default() -> Self {
907 Self::new()
908 }
909}
910
911pub fn shape_hash<V: Clone + std::hash::Hash>(t: &DHarhtMemory<V>) -> u64 {
913 use std::hash::{Hash, Hasher};
914 let mut h = std::collections::hash_map::DefaultHasher::new();
915 let entries = t.iter_sorted();
916 h.write_u64(entries.len() as u64);
917 for (k, v) in entries {
918 h.write_u64(k);
919 v.hash(&mut h);
920 }
921 h.write_u64(t.micro_overflow_count);
922 h.write_u32(t.max_collision_group);
923 h.write_usize(t.micro4.len());
924 h.write_usize(t.micro8.len());
925 h.write_usize(t.micro16.len());
926 deterministic_permutation_scatter(h.finish())
927}
928
929#[cfg(test)]
930mod tests {
931 use super::*;
932
933 #[test]
934 fn insert_get_update_works() {
935 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
936 assert_eq!(t.insert(7, 70), None);
937 assert_eq!(t.insert(9, 90), None);
938 assert_eq!(t.get(7), Some(&70));
939 assert_eq!(t.get(9), Some(&90));
940 assert_eq!(t.get(11), None);
941 assert_eq!(t.insert(7, 700), Some(70));
942 assert_eq!(t.get(7), Some(&700));
943 }
944
945 #[test]
946 fn seal_preserves_all_entries() {
947 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
948 for i in 0..5_000u64 {
949 t.insert(i, i * 2);
950 }
951 t.seal_for_lookup();
952 for i in 0..5_000u64 {
953 assert_eq!(t.get(i), Some(&(i * 2)));
954 }
955 }
956
957 #[test]
958 fn deterministic_double_build_same_shape_hash() {
959 fn build() -> DHarhtMemory<u64> {
960 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
961 for i in 0..1_000u64 {
962 t.insert(deterministic_permutation_scatter(i ^ 0x9e37_79b9_7f4a_7c15), i);
963 }
964 t.seal_for_lookup();
965 t
966 }
967 assert_eq!(shape_hash(&build()), shape_hash(&build()));
968 }
969
970 #[test]
971 fn micro16_capacity_enforced() {
972 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
973 for i in 0..50_000u64 {
975 t.insert(i, i);
976 }
977 t.seal_for_lookup();
978 assert!(t.micro_overflow_count() < 100, "unexpected micro overflow: {}", t.micro_overflow_count());
980 for i in 0..50_000u64 {
983 assert_eq!(t.get(i), Some(&i));
984 }
985 }
986
987 #[test]
988 fn full_key_equality_no_false_positive() {
989 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
990 for i in 0..2_000u64 {
991 t.insert(i, i);
992 }
993 t.seal_for_lookup();
994 for i in 2_000..3_000u64 {
995 assert_eq!(t.get(i), None, "false positive at {}", i);
996 }
997 }
998
999 #[test]
1000 fn art_fallback_handles_forced_overflow() {
1001 let target_shard_prefix: u64 = {
1007 let s = deterministic_permutation_scatter(0);
1008 (s >> 40) & 0xFF_FFFF
1009 };
1010 let mut colliding: Vec<u64> = Vec::with_capacity(32);
1011 let mut k: u64 = 0;
1012 while colliding.len() < 32 && k < 100_000_000 {
1013 let s = deterministic_permutation_scatter(k);
1014 if (s >> 40) & 0xFF_FFFF == target_shard_prefix {
1015 colliding.push(k);
1016 }
1017 k += 1;
1018 }
1019 if colliding.len() >= 17 {
1023 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
1024 for (i, &k) in colliding.iter().enumerate() {
1025 t.insert(k, i as u64);
1026 }
1027 t.seal_for_lookup();
1028 assert!(
1030 t.micro_overflow_count() > 0,
1031 "expected ART fallback to be triggered; overflow={}",
1032 t.micro_overflow_count()
1033 );
1034 for (i, &k) in colliding.iter().enumerate() {
1036 assert_eq!(
1037 t.get(k),
1038 Some(&(i as u64)),
1039 "ART fallback lost key {}",
1040 k
1041 );
1042 }
1043 }
1044 }
1045
1046 #[test]
1047 fn matches_btreemap_oracle() {
1048 use std::collections::BTreeMap;
1049 let mut t: DHarhtMemory<u64> = DHarhtMemory::new();
1050 let mut oracle: BTreeMap<u64, u64> = BTreeMap::new();
1051 let mut x: u64 = 0xCAFEBABE;
1052 for _ in 0..5_000 {
1053 x = deterministic_permutation_scatter(x);
1054 let p1 = t.insert(x, x);
1055 let p2 = oracle.insert(x, x);
1056 assert_eq!(p1, p2);
1057 }
1058 t.seal_for_lookup();
1059 for (k, v) in &oracle {
1060 assert_eq!(t.get(*k), Some(v));
1061 }
1062 }
1063}