1use std::fs::{File, OpenOptions};
21use std::mem::size_of;
22use std::path::Path;
23use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
24
25use memmap2::{MmapMut, MmapOptions};
26
27pub const T: usize = 8;
29pub const B: usize = 2 * T - 1; pub const NIL: u32 = u32::MAX;
33
34pub const BTREE_MAGIC: u64 = 0x4254_5245_454D_4150; #[repr(C, align(64))]
37pub struct BTreeHeader {
38 pub magic: u64,
39 pub root: AtomicU32,
40 pub node_count: AtomicU32,
41 pub capacity: u64,
42 pub len: AtomicU64,
43 pub free_head: AtomicU32,
46 _pad0: u32,
47 pub version: AtomicU64,
52 _pad: [u8; 16],
53}
54
55const _: () = {
56 assert!(size_of::<BTreeHeader>() == 64);
57};
58
59#[repr(C)]
60pub struct BTreeNode<K: Copy + Ord + Default + 'static, V: Copy + Default + 'static> {
61 pub count: u16,
62 pub is_leaf: u8,
63 _pad: [u8; 5],
64 pub keys: [K; B],
65 pub children: [u32; B + 1],
66 pub values: [V; B],
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum BTreeError {
71 Full,
72 LayoutMismatch,
73 InvalidConfig,
74 IoError(std::io::ErrorKind),
75}
76
77impl From<std::io::Error> for BTreeError {
78 fn from(e: std::io::Error) -> Self {
79 Self::IoError(e.kind())
80 }
81}
82
83pub fn btree_file_size<K, V>(capacity: usize) -> usize
84where
85 K: Copy + Ord + Default + 'static,
86 V: Copy + Default + 'static,
87{
88 size_of::<BTreeHeader>() + capacity * size_of::<BTreeNode<K, V>>()
89}
90
91pub struct SharedBTreeMap<K: Copy + Ord + Default + 'static, V: Copy + Default + 'static> {
92 _file: File,
93 mmap: MmapMut,
94 raw_ptr: *mut u8,
95 capacity: usize,
96 _phantom: std::marker::PhantomData<(K, V)>,
97 header_sidecar: subetha_core::HandshakeHeader,
98 ring_sidecar: Box<subetha_core::ObservationRing>,
99}
100
101unsafe impl<K: Copy + Ord + Default + Send + 'static, V: Copy + Default + Send + 'static> Send
102 for SharedBTreeMap<K, V>
103{
104}
105unsafe impl<K: Copy + Ord + Default + Sync + 'static, V: Copy + Default + Sync + 'static> Sync
106 for SharedBTreeMap<K, V>
107{
108}
109
110impl<K: Copy + Ord + Default + Send + Sync + 'static, V: Copy + Default + Send + Sync + 'static>
111 subetha_sidecar::AdaptiveInstance for SharedBTreeMap<K, V>
112{
113 fn header(&self) -> &subetha_core::HandshakeHeader { &self.header_sidecar }
114 fn ring(&self) -> &subetha_core::ObservationRing { &self.ring_sidecar }
115 fn make_policy(&self) -> Box<dyn subetha_sidecar::Policy> {
116 Box::new(subetha_sidecar::NoMigrationPolicy)
117 }
118}
119
120impl<K: Copy + Ord + Default + 'static, V: Copy + Default + 'static> SharedBTreeMap<K, V> {
121 pub fn create(path: impl AsRef<Path>, capacity: usize) -> Result<Self, BTreeError> {
127 if capacity < 1 {
128 return Err(BTreeError::InvalidConfig);
129 }
130 let (file, mmap) = crate::mmf_attach::create_or_attach(
131 path.as_ref(),
132 btree_file_size::<K, V>(capacity),
133 |ptr| unsafe { Self::init_region(ptr, capacity) },
134 |ptr| unsafe { (*(ptr as *const BTreeHeader)).magic == BTREE_MAGIC },
135 )?;
136 Self::from_region(file, mmap, capacity)
137 }
138
139 pub fn reset(path: impl AsRef<Path>, capacity: usize) -> Result<Self, BTreeError> {
143 if capacity < 1 {
144 return Err(BTreeError::InvalidConfig);
145 }
146 let (file, mmap) = crate::mmf_attach::reset(
147 path.as_ref(),
148 btree_file_size::<K, V>(capacity),
149 |ptr| unsafe { Self::init_region(ptr, capacity) },
150 )?;
151 Self::from_region(file, mmap, capacity)
152 }
153
154 unsafe fn init_region(ptr: *mut u8, capacity: usize) {
161 let hdr = ptr as *mut BTreeHeader;
162 unsafe {
163 (*hdr).capacity = capacity as u64;
164 std::ptr::write(&raw mut (*hdr).root, AtomicU32::new(NIL));
165 std::ptr::write(&raw mut (*hdr).free_head, AtomicU32::new(NIL));
166 std::ptr::write_volatile(&raw mut (*hdr).magic, BTREE_MAGIC);
167 }
168 }
169
170 fn from_region(file: File, mut mmap: MmapMut, capacity: usize) -> Result<Self, BTreeError> {
173 let hdr = unsafe { &*(mmap.as_ptr() as *const BTreeHeader) };
174 if hdr.magic != BTREE_MAGIC || hdr.capacity != capacity as u64 {
175 return Err(BTreeError::LayoutMismatch);
176 }
177 let raw_ptr = mmap.as_mut_ptr();
178 Ok(Self {
179 _file: file, mmap, raw_ptr, capacity,
180 _phantom: std::marker::PhantomData,
181 header_sidecar: subetha_core::HandshakeHeader::new(),
182 ring_sidecar: Box::new(subetha_core::ObservationRing::new()),
183 })
184 }
185
186 pub fn open(path: impl AsRef<Path>, expected_capacity: usize) -> Result<Self, BTreeError> {
187 let total = btree_file_size::<K, V>(expected_capacity);
188 let file = OpenOptions::new().read(true).write(true).open(path.as_ref())?;
189 if file.metadata()?.len() < total as u64 {
190 return Err(BTreeError::LayoutMismatch);
191 }
192 let mmap = unsafe { MmapOptions::new().len(total).map_mut(&file)? };
193 Self::from_region(file, mmap, expected_capacity)
194 }
195
196 #[inline]
197 fn header(&self) -> &BTreeHeader {
198 unsafe { &*(self.raw_ptr as *const BTreeHeader) }
199 }
200
201 #[inline]
202 pub fn len(&self) -> usize {
203 self.header().len.load(Ordering::Acquire) as usize
204 }
205
206 #[inline]
207 pub fn is_empty(&self) -> bool {
208 self.len() == 0
209 }
210
211 #[inline]
212 pub fn capacity(&self) -> usize {
213 self.capacity
214 }
215
216 #[inline]
219 pub fn node_count(&self) -> usize {
220 self.header().node_count.load(Ordering::Acquire) as usize
221 }
222
223 #[inline]
224 fn node(&self, idx: u32) -> *mut BTreeNode<K, V> {
225 let off = size_of::<BTreeHeader>() + idx as usize * size_of::<BTreeNode<K, V>>();
226 unsafe { self.raw_ptr.add(off) as *mut BTreeNode<K, V> }
227 }
228
229 fn alloc_node(&self, is_leaf: bool) -> Result<u32, BTreeError> {
232 let h = self.header();
233 let free = h.free_head.load(Ordering::Acquire);
234 let idx = if free != NIL {
235 let next = unsafe { (*self.node(free)).children[0] };
236 h.free_head.store(next, Ordering::Release);
237 free
238 } else {
239 let idx = h.node_count.fetch_add(1, Ordering::AcqRel);
240 if idx as usize >= self.capacity {
241 h.node_count.fetch_sub(1, Ordering::AcqRel);
242 return Err(BTreeError::Full);
243 }
244 idx
245 };
246 let n = self.node(idx);
247 unsafe {
248 (*n).count = 0;
249 (*n).is_leaf = is_leaf as u8;
250 (*n).children = [NIL; B + 1];
251 }
252 Ok(idx)
253 }
254
255 fn free_node(&self, idx: u32) {
257 let h = self.header();
258 let head = h.free_head.load(Ordering::Acquire);
259 unsafe { (*self.node(idx)).children[0] = head; }
260 h.free_head.store(idx, Ordering::Release);
261 }
262
263 #[inline]
267 fn begin_write(&self) {
268 self.header().version.fetch_add(1, Ordering::AcqRel);
269 }
270 #[inline]
271 fn end_write(&self) {
272 self.header().version.fetch_add(1, Ordering::Release);
273 }
274
275 #[inline]
280 fn search(&self, idx: u32, key: &K) -> (usize, bool) {
281 let n = self.node(idx);
282 let count = (unsafe { (*n).count } as usize).min(B);
283 let (mut lo, mut hi) = (0usize, count);
284 while lo < hi {
285 let mid = (lo + hi) / 2;
286 let mk = unsafe { (*n).keys[mid] };
287 match mk.cmp(key) {
288 std::cmp::Ordering::Less => lo = mid + 1,
289 std::cmp::Ordering::Greater => hi = mid,
290 std::cmp::Ordering::Equal => return (mid, true),
291 }
292 }
293 (lo, false)
294 }
295
296 pub fn get(&self, key: &K) -> Option<V> {
298 let r = self.get_inner(key);
299 self.ring_sidecar.push_op(
300 crate::sidecar_ops::ordered::OP_GET,
301 if r.is_none() { 2 } else { 0 },
302 );
303 r
304 }
305
306 fn get_inner(&self, key: &K) -> Option<V> {
307 let h = self.header();
308 let cap = self.capacity as u32;
309 loop {
310 let v1 = h.version.load(Ordering::Acquire);
311 if v1 & 1 != 0 {
312 std::hint::spin_loop();
313 continue; }
315 let mut idx = h.root.load(Ordering::Acquire);
320 let mut result: Option<V> = None;
321 let mut torn = false;
322 while idx != NIL {
323 if idx >= cap {
324 torn = true;
325 break;
326 }
327 let (pos, found) = self.search(idx, key);
328 let n = self.node(idx);
329 if found {
330 result = Some(unsafe { (*n).values[pos] });
331 break;
332 }
333 if unsafe { (*n).is_leaf } != 0 {
334 break;
335 }
336 idx = unsafe { (*n).children[pos] };
337 }
338 let v2 = h.version.load(Ordering::Acquire);
339 if v1 == v2 && !torn {
340 return result;
341 }
342 std::hint::spin_loop();
343 }
344 }
345
346 pub fn contains_key(&self, key: &K) -> bool {
348 self.get_inner(key).is_some()
349 }
350
351 pub fn insert(&self, key: K, value: V) -> Result<Option<V>, BTreeError> {
354 self.begin_write();
355 let r = self.insert_inner(key, value);
356 self.end_write();
357 self.ring_sidecar.push_op(
358 crate::sidecar_ops::ordered::OP_INSERT,
359 if r.is_err() { 1 } else { 0 },
360 );
361 r
362 }
363
364 fn insert_inner(&self, key: K, value: V) -> Result<Option<V>, BTreeError> {
365 let root = self.header().root.load(Ordering::Acquire);
366 if root == NIL {
367 let r = self.alloc_node(true)?;
368 let n = self.node(r);
369 unsafe {
370 (*n).keys[0] = key;
371 (*n).values[0] = value;
372 (*n).count = 1;
373 }
374 self.header().root.store(r, Ordering::Release);
375 self.header().len.fetch_add(1, Ordering::AcqRel);
376 return Ok(None);
377 }
378 let root = if unsafe { (*self.node(root)).count as usize } == B {
380 let new_root = self.alloc_node(false)?;
381 unsafe {
382 (*self.node(new_root)).children[0] = root;
383 (*self.node(new_root)).count = 0;
384 }
385 self.split_child(new_root, 0)?;
386 self.header().root.store(new_root, Ordering::Release);
387 new_root
388 } else {
389 root
390 };
391 self.insert_nonfull(root, key, value)
392 }
393
394 fn insert_nonfull(&self, mut idx: u32, key: K, value: V) -> Result<Option<V>, BTreeError> {
396 loop {
397 let (pos, found) = self.search(idx, &key);
398 let n = self.node(idx);
399 if found {
400 let old = unsafe { (*n).values[pos] };
401 unsafe { (*n).values[pos] = value; }
402 return Ok(Some(old));
403 }
404 if unsafe { (*n).is_leaf } != 0 {
405 let count = unsafe { (*n).count as usize };
406 unsafe {
407 let mut j = count;
408 while j > pos {
409 (*n).keys[j] = (*n).keys[j - 1];
410 (*n).values[j] = (*n).values[j - 1];
411 j -= 1;
412 }
413 (*n).keys[pos] = key;
414 (*n).values[pos] = value;
415 (*n).count = (count + 1) as u16;
416 }
417 self.header().len.fetch_add(1, Ordering::AcqRel);
418 return Ok(None);
419 }
420 let child = unsafe { (*n).children[pos] };
421 if unsafe { (*self.node(child)).count as usize } == B {
422 self.split_child(idx, pos)?;
423 let n = self.node(idx);
425 let med = unsafe { (*n).keys[pos] };
426 match key.cmp(&med) {
427 std::cmp::Ordering::Equal => {
428 let old = unsafe { (*n).values[pos] };
429 unsafe { (*n).values[pos] = value; }
430 return Ok(Some(old));
431 }
432 std::cmp::Ordering::Greater => idx = unsafe { (*n).children[pos + 1] },
433 std::cmp::Ordering::Less => idx = unsafe { (*n).children[pos] },
434 }
435 } else {
436 idx = child;
437 }
438 }
439 }
440
441 fn split_child(&self, parent: u32, i: usize) -> Result<(), BTreeError> {
444 let full = unsafe { (*self.node(parent)).children[i] };
445 let is_leaf = unsafe { (*self.node(full)).is_leaf };
446 let right = self.alloc_node(is_leaf != 0)?;
447
448 let fp = self.node(full);
449 let rp = self.node(right);
450 unsafe {
451 for j in 0..(T - 1) {
453 (*rp).keys[j] = (*fp).keys[T + j];
454 (*rp).values[j] = (*fp).values[T + j];
455 }
456 if is_leaf == 0 {
457 for j in 0..T {
458 (*rp).children[j] = (*fp).children[T + j];
459 }
460 }
461 (*rp).count = (T - 1) as u16;
462 (*rp).is_leaf = is_leaf;
463 }
464 let median_key = unsafe { (*fp).keys[T - 1] };
465 let median_value = unsafe { (*fp).values[T - 1] };
466 unsafe { (*fp).count = (T - 1) as u16; }
469
470 let pp = self.node(parent);
471 unsafe {
472 let pc = (*pp).count as usize;
473 let mut j = pc;
474 while j > i {
475 (*pp).children[j + 1] = (*pp).children[j];
476 j -= 1;
477 }
478 (*pp).children[i + 1] = right;
479 let mut j = pc;
480 while j > i {
481 (*pp).keys[j] = (*pp).keys[j - 1];
482 (*pp).values[j] = (*pp).values[j - 1];
483 j -= 1;
484 }
485 (*pp).keys[i] = median_key;
486 (*pp).values[i] = median_value;
487 (*pp).count = (pc + 1) as u16;
488 }
489 Ok(())
490 }
491
492 pub fn remove(&self, key: &K) -> Result<Option<V>, BTreeError> {
495 self.begin_write();
496 let r = self.remove_inner(key);
497 self.end_write();
498 self.ring_sidecar.push_op(
499 crate::sidecar_ops::ordered::OP_REMOVE,
500 if r.is_none() { 2 } else { 0 },
501 );
502 Ok(r)
503 }
504
505 fn remove_inner(&self, key: &K) -> Option<V> {
506 let root = self.header().root.load(Ordering::Acquire);
507 if root == NIL {
508 return None;
509 }
510 let removed = self.delete_from(root, key);
511 let rn = self.node(root);
513 if unsafe { (*rn).count } == 0 {
514 if unsafe { (*rn).is_leaf } != 0 {
515 self.header().root.store(NIL, Ordering::Release);
516 } else {
517 let new_root = unsafe { (*rn).children[0] };
518 self.header().root.store(new_root, Ordering::Release);
519 }
520 self.free_node(root);
521 }
522 if removed.is_some() {
523 self.header().len.fetch_sub(1, Ordering::AcqRel);
524 }
525 removed
526 }
527
528 fn delete_from(&self, idx: u32, key: &K) -> Option<V> {
532 let (pos, found) = self.search(idx, key);
533 let n = self.node(idx);
534 let is_leaf = unsafe { (*n).is_leaf } != 0;
535 if found {
536 let old = unsafe { (*n).values[pos] };
537 if is_leaf {
538 let count = unsafe { (*n).count as usize };
539 unsafe {
540 for j in pos..count - 1 {
541 (*n).keys[j] = (*n).keys[j + 1];
542 (*n).values[j] = (*n).values[j + 1];
543 }
544 (*n).count = (count - 1) as u16;
545 }
546 } else {
547 let left = unsafe { (*n).children[pos] };
548 let right = unsafe { (*n).children[pos + 1] };
549 if unsafe { (*self.node(left)).count as usize } >= T {
550 let (pk, pv) = self.max_pair(left);
551 unsafe {
552 (*n).keys[pos] = pk;
553 (*n).values[pos] = pv;
554 }
555 self.delete_from(left, &pk);
556 } else if unsafe { (*self.node(right)).count as usize } >= T {
557 let (sk, sv) = self.min_pair(right);
558 unsafe {
559 (*n).keys[pos] = sk;
560 (*n).values[pos] = sv;
561 }
562 self.delete_from(right, &sk);
563 } else {
564 self.merge_at(idx, pos);
565 self.delete_from(left, key);
566 }
567 }
568 return Some(old);
569 }
570 if is_leaf {
571 return None;
572 }
573 let child = self.ensure_min_degree(idx, pos);
574 self.delete_from(child, key)
575 }
576
577 fn ensure_min_degree(&self, parent: u32, i: usize) -> u32 {
580 let p = self.node(parent);
581 let child = unsafe { (*p).children[i] };
582 if unsafe { (*self.node(child)).count as usize } >= T {
583 return child;
584 }
585 let pcount = unsafe { (*p).count as usize };
586 if i > 0 {
587 let left = unsafe { (*p).children[i - 1] };
588 if unsafe { (*self.node(left)).count as usize } >= T {
589 self.borrow_from_left(parent, i);
590 return child;
591 }
592 }
593 if i < pcount {
594 let right = unsafe { (*p).children[i + 1] };
595 if unsafe { (*self.node(right)).count as usize } >= T {
596 self.borrow_from_right(parent, i);
597 return child;
598 }
599 }
600 if i < pcount {
601 self.merge_at(parent, i);
602 unsafe { (*self.node(parent)).children[i] }
603 } else {
604 self.merge_at(parent, i - 1);
605 unsafe { (*self.node(parent)).children[i - 1] }
606 }
607 }
608
609 fn borrow_from_left(&self, parent: u32, i: usize) {
610 let p = self.node(parent);
611 let child = unsafe { (*p).children[i] };
612 let left = unsafe { (*p).children[i - 1] };
613 let c = self.node(child);
614 let l = self.node(left);
615 unsafe {
616 let cc = (*c).count as usize;
617 let internal = (*c).is_leaf == 0;
618 let mut j = cc;
619 while j > 0 {
620 (*c).keys[j] = (*c).keys[j - 1];
621 (*c).values[j] = (*c).values[j - 1];
622 j -= 1;
623 }
624 if internal {
625 let mut j = cc + 1;
626 while j > 0 {
627 (*c).children[j] = (*c).children[j - 1];
628 j -= 1;
629 }
630 }
631 (*c).keys[0] = (*p).keys[i - 1];
632 (*c).values[0] = (*p).values[i - 1];
633 let lc = (*l).count as usize;
634 if internal {
635 (*c).children[0] = (*l).children[lc];
636 }
637 (*p).keys[i - 1] = (*l).keys[lc - 1];
638 (*p).values[i - 1] = (*l).values[lc - 1];
639 (*l).count = (lc - 1) as u16;
640 (*c).count = (cc + 1) as u16;
641 }
642 }
643
644 fn borrow_from_right(&self, parent: u32, i: usize) {
645 let p = self.node(parent);
646 let child = unsafe { (*p).children[i] };
647 let right = unsafe { (*p).children[i + 1] };
648 let c = self.node(child);
649 let r = self.node(right);
650 unsafe {
651 let cc = (*c).count as usize;
652 let internal = (*c).is_leaf == 0;
653 (*c).keys[cc] = (*p).keys[i];
654 (*c).values[cc] = (*p).values[i];
655 if internal {
656 (*c).children[cc + 1] = (*r).children[0];
657 }
658 (*p).keys[i] = (*r).keys[0];
659 (*p).values[i] = (*r).values[0];
660 let rc = (*r).count as usize;
661 for j in 0..rc - 1 {
662 (*r).keys[j] = (*r).keys[j + 1];
663 (*r).values[j] = (*r).values[j + 1];
664 }
665 if internal {
666 for j in 0..rc {
667 (*r).children[j] = (*r).children[j + 1];
668 }
669 }
670 (*r).count = (rc - 1) as u16;
671 (*c).count = (cc + 1) as u16;
672 }
673 }
674
675 fn merge_at(&self, parent: u32, i: usize) {
678 let p = self.node(parent);
679 let left = unsafe { (*p).children[i] };
680 let right = unsafe { (*p).children[i + 1] };
681 let l = self.node(left);
682 let r = self.node(right);
683 unsafe {
684 let lc = (*l).count as usize;
685 let internal = (*l).is_leaf == 0;
686 (*l).keys[lc] = (*p).keys[i];
687 (*l).values[lc] = (*p).values[i];
688 let rc = (*r).count as usize;
689 for j in 0..rc {
690 (*l).keys[lc + 1 + j] = (*r).keys[j];
691 (*l).values[lc + 1 + j] = (*r).values[j];
692 }
693 if internal {
694 for j in 0..=rc {
695 (*l).children[lc + 1 + j] = (*r).children[j];
696 }
697 }
698 (*l).count = (lc + 1 + rc) as u16;
699 let pc = (*p).count as usize;
700 for j in i..pc - 1 {
701 (*p).keys[j] = (*p).keys[j + 1];
702 (*p).values[j] = (*p).values[j + 1];
703 }
704 for j in i + 1..pc {
705 (*p).children[j] = (*p).children[j + 1];
706 }
707 (*p).count = (pc - 1) as u16;
708 }
709 self.free_node(right);
710 }
711
712 fn max_pair(&self, mut idx: u32) -> (K, V) {
713 loop {
714 let n = self.node(idx);
715 let count = unsafe { (*n).count as usize };
716 if unsafe { (*n).is_leaf } != 0 {
717 return unsafe { ((*n).keys[count - 1], (*n).values[count - 1]) };
718 }
719 idx = unsafe { (*n).children[count] };
720 }
721 }
722
723 fn min_pair(&self, mut idx: u32) -> (K, V) {
724 loop {
725 let n = self.node(idx);
726 if unsafe { (*n).is_leaf } != 0 {
727 return unsafe { ((*n).keys[0], (*n).values[0]) };
728 }
729 idx = unsafe { (*n).children[0] };
730 }
731 }
732
733 pub fn first(&self) -> Option<(K, V)> {
735 let root = self.header().root.load(Ordering::Acquire);
736 let r = if root == NIL { None } else { Some(self.min_pair(root)) };
737 self.ring_sidecar.push_op(
738 crate::sidecar_ops::ordered::OP_GET,
739 if r.is_none() { 2 } else { 0 },
740 );
741 r
742 }
743
744 pub fn iter_ascending(&self) -> Vec<(K, V)> {
746 let mut out = Vec::with_capacity(self.len());
747 let root = self.header().root.load(Ordering::Acquire);
748 if root != NIL {
749 self.walk(root, &mut out);
750 }
751 out
752 }
753
754 fn walk(&self, idx: u32, out: &mut Vec<(K, V)>) {
755 let n = self.node(idx);
756 let count = unsafe { (*n).count as usize };
757 let is_leaf = unsafe { (*n).is_leaf } != 0;
758 for i in 0..count {
759 if !is_leaf {
760 let c = unsafe { (*n).children[i] };
761 self.walk(c, out);
762 }
763 out.push(unsafe { ((*n).keys[i], (*n).values[i]) });
764 }
765 if !is_leaf {
766 let c = unsafe { (*n).children[count] };
767 self.walk(c, out);
768 }
769 }
770
771 pub fn flush(&self) -> Result<(), BTreeError> {
772 self.mmap.flush()?;
773 Ok(())
774 }
775}
776
777#[cfg(test)]
778mod tests {
779 use super::*;
780
781 fn tmp(name: &str) -> std::path::PathBuf {
782 let mut p = std::env::temp_dir();
783 p.push(format!("btree_{name}_{}.bin", std::process::id()));
784 p
785 }
786
787 #[test]
790 fn second_create_attaches_and_keeps_the_tree() {
791 let p = tmp("attach");
792 std::fs::remove_file(&p).ok();
793 let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
794 m.insert(7, 777).unwrap();
795
796 let m2: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
797 assert_eq!(m2.get(&7), Some(777), "attach lost a live entry");
798 assert!(matches!(
799 SharedBTreeMap::<u64, u64>::create(&p, 32),
800 Err(BTreeError::LayoutMismatch),
801 ));
802
803 drop(m);
806 drop(m2);
807 let fresh: SharedBTreeMap<u64, u64> = SharedBTreeMap::reset(&p, 64).unwrap();
808 assert_eq!(fresh.get(&7), None, "reset kept an entry");
809 assert_eq!(fresh.len(), 0);
810 drop(fresh);
811 std::fs::remove_file(&p).ok();
812 }
813
814 #[test]
815 fn insert_get_round_trip() {
816 let p = tmp("rt");
817 let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
818 for i in 0..100u64 {
819 assert_eq!(m.insert(i, i * 10).unwrap(), None);
820 }
821 for i in 0..100u64 {
822 assert_eq!(m.get(&i), Some(i * 10));
823 }
824 assert_eq!(m.get(&1000), None);
825 assert_eq!(m.len(), 100);
826 std::fs::remove_file(&p).ok();
827 }
828
829 #[test]
830 fn duplicate_insert_updates_and_returns_previous() {
831 let p = tmp("dup");
832 let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
833 assert_eq!(m.insert(5, 50).unwrap(), None);
834 assert_eq!(m.insert(5, 55).unwrap(), Some(50));
835 assert_eq!(m.get(&5), Some(55));
836 assert_eq!(m.len(), 1);
837 std::fs::remove_file(&p).ok();
838 }
839
840 #[test]
841 fn many_inserts_stay_sorted_and_findable() {
842 let p = tmp("many");
843 let n = 5000u64;
844 let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 4096).unwrap();
845 let mut x = 0x1234_5678u64;
847 let mut inserted = Vec::new();
848 for _ in 0..n {
849 x ^= x << 13; x ^= x >> 7; x ^= x << 17;
850 let k = x % 1_000_000;
851 if m.insert(k, k.wrapping_mul(3)).unwrap().is_none() {
852 inserted.push(k);
853 }
854 }
855 for &k in &inserted {
856 assert_eq!(m.get(&k), Some(k.wrapping_mul(3)), "missing {k}");
857 }
858 let asc = m.iter_ascending();
860 for w in asc.windows(2) {
861 assert!(w[0].0 < w[1].0, "order broken: {} >= {}", w[0].0, w[1].0);
862 }
863 assert_eq!(asc.len(), inserted.len());
864 std::fs::remove_file(&p).ok();
865 }
866
867 #[test]
868 fn cross_handle_visibility() {
869 let p = tmp("xhandle");
870 let a: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 64).unwrap();
871 for i in 0..50u64 { a.insert(i, i).unwrap(); }
872 let b: SharedBTreeMap<u64, u64> = SharedBTreeMap::open(&p, 64).unwrap();
873 for i in 0..50u64 { assert_eq!(b.get(&i), Some(i)); }
874 std::fs::remove_file(&p).ok();
875 }
876
877 #[test]
878 fn random_ops_match_std_btreemap() {
879 use std::collections::BTreeMap;
880 let p = tmp("oracle");
881 let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 16384).unwrap();
882 let mut oracle: BTreeMap<u64, u64> = BTreeMap::new();
883 let mut x = 0xdead_beef_1234_5678u64;
884 let mut rng = || {
885 x ^= x << 13; x ^= x >> 7; x ^= x << 17; x
886 };
887 for _ in 0..80_000 {
888 let k = rng() % 3000;
889 match rng() % 3 {
890 0 | 1 => {
891 let v = rng();
892 assert_eq!(m.insert(k, v).unwrap(), oracle.insert(k, v), "insert {k}");
893 }
894 _ => {
895 assert_eq!(m.remove(&k).unwrap(), oracle.remove(&k), "remove {k}");
896 }
897 }
898 assert_eq!(m.len(), oracle.len(), "len after op on {k}");
899 }
900 for (k, v) in &oracle {
901 assert_eq!(m.get(k), Some(*v), "final get {k}");
902 }
903 let asc = m.iter_ascending();
904 let oref: Vec<(u64, u64)> = oracle.iter().map(|(k, v)| (*k, *v)).collect();
905 assert_eq!(asc, oref, "iteration order / contents mismatch");
906 std::fs::remove_file(&p).ok();
907 }
908
909 #[test]
910 fn first_returns_smallest() {
911 let p = tmp("first");
912 let m: SharedBTreeMap<u64, u64> = SharedBTreeMap::create(&p, 1024).unwrap();
913 assert_eq!(m.first(), None);
914 for i in (0..500u64).rev() {
915 m.insert(i, i * 7).unwrap();
916 }
917 assert_eq!(m.first(), Some((0, 0)));
918 m.remove(&0).unwrap();
919 assert_eq!(m.first(), Some((1, 7)));
920 std::fs::remove_file(&p).ok();
921 }
922
923 #[test]
924 fn concurrent_readers_during_inserts() {
925 use std::sync::Arc;
926 use std::sync::atomic::AtomicBool;
927 let p = tmp("concurrent");
928 let m = Arc::new(SharedBTreeMap::<u64, u64>::create(&p, 16384).unwrap());
929 for i in 0..1000u64 {
930 m.insert(i, i.wrapping_mul(2)).unwrap();
931 }
932 let stop = Arc::new(AtomicBool::new(false));
933 let readers: Vec<_> = (0..4)
934 .map(|_| {
935 let m = m.clone();
936 let stop = stop.clone();
937 std::thread::spawn(move || {
938 while !stop.load(Ordering::Relaxed) {
939 for i in 0..1000u64 {
940 if let Some(v) = m.get(&i) {
944 assert_eq!(v, i.wrapping_mul(2), "torn read at {i}");
945 }
946 }
947 }
948 })
949 })
950 .collect();
951 for i in 1000..6000u64 {
952 m.insert(i, i.wrapping_mul(2)).unwrap();
953 }
954 stop.store(true, Ordering::Relaxed);
955 for r in readers {
956 r.join().unwrap();
957 }
958 std::fs::remove_file(&p).ok();
959 }
960}