|
| 1 | +#[cfg(test)] |
| 2 | +mod tests; |
| 3 | + |
1 | 4 | /// A buffer indexed by a u64 sequence number. |
2 | 5 | /// |
3 | 6 | /// This behaves idential to a BTreeMap<u64, T>, but is optimized for the case where |
@@ -191,6 +194,10 @@ impl<T> SortedIndexBuffer<T> { |
191 | 194 | } |
192 | 195 |
|
193 | 196 | /// Turn into an iterator over all (index, value) pairs in the buffer in ascending order of their keys. |
| 197 | + /// |
| 198 | + /// This is an explicit method instead of implementing IntoIterator, so we can return a |
| 199 | + /// DoubleEndedIterator without having to name the iterator type. |
| 200 | + #[allow(clippy::should_implement_trait)] |
194 | 201 | pub fn into_iter(self) -> impl DoubleEndedIterator<Item = (u64, T)> { |
195 | 202 | let base = base(self.min, self.max); |
196 | 203 | self.data |
@@ -432,195 +439,3 @@ fn base(min: u64, max: u64) -> u64 { |
432 | 439 | let mask = (buf_len as u64) / 2 - 1; |
433 | 440 | min & !mask |
434 | 441 | } |
435 | | - |
436 | | -#[cfg(test)] |
437 | | -mod tests { |
438 | | - use std::collections::BTreeMap; |
439 | | - |
440 | | - use proptest::prelude::*; |
441 | | - |
442 | | - use super::*; |
443 | | - |
444 | | - /// Randomly permutes an iterator such that each element is displaced by at most `k` positions. |
445 | | - pub fn lag_permute<I: Iterator>(iter: I, k: usize) -> impl Iterator<Item = I::Item> { |
446 | | - let mut source = iter; |
447 | | - let mut buffer = Vec::with_capacity(k + 1); |
448 | | - let mut rng = rand::rng(); |
449 | | - |
450 | | - std::iter::from_fn(move || { |
451 | | - buffer.extend((&mut source).take(k + 1 - buffer.len())); |
452 | | - |
453 | | - if buffer.is_empty() { |
454 | | - return None; |
455 | | - } |
456 | | - |
457 | | - Some(buffer.swap_remove(rng.random_range(0..buffer.len()))) |
458 | | - }) |
459 | | - } |
460 | | - |
461 | | - #[test] |
462 | | - fn test_usage() { |
463 | | - let elements = lag_permute(0..10000, 100).collect::<Vec<_>>(); |
464 | | - let mut reference = BTreeMap::<u64, u64>::new(); |
465 | | - let mut pb = SortedIndexBuffer::<u64>::default(); |
466 | | - let d = 100; |
467 | | - let add = elements |
468 | | - .iter() |
469 | | - .map(|x| Some(*x)) |
470 | | - .chain(std::iter::repeat_n(None, d)); |
471 | | - let remove = std::iter::repeat_n(None, d).chain(elements.iter().map(|x| Some(*x))); |
472 | | - for (a, r) in add.zip(remove) { |
473 | | - if let Some(i) = a { |
474 | | - pb.insert(i, i * 10); |
475 | | - reference.insert(i, i * 10); |
476 | | - } |
477 | | - if let Some(i) = r { |
478 | | - let v1 = pb.remove(i); |
479 | | - let v2 = reference.remove(&i); |
480 | | - assert_eq!(v1, v2); |
481 | | - } |
482 | | - assert_same(pb.iter(), reference.iter().map(|(k, v)| (*k, v))); |
483 | | - pb.check_invariants_expensive(); |
484 | | - } |
485 | | - assert!(reference.is_empty()); |
486 | | - assert!(pb.is_empty()); |
487 | | - } |
488 | | - |
489 | | - #[test] |
490 | | - fn test_range_iterators() { |
491 | | - let mut pb = SortedIndexBuffer::default(); |
492 | | - for i in 0..100 { |
493 | | - pb.insert(i, i * 10); |
494 | | - } |
495 | | - |
496 | | - // Test keys_range |
497 | | - let keys: Vec<_> = pb.keys_range(10..20).collect(); |
498 | | - assert_eq!(keys, (10..20).collect::<Vec<_>>()); |
499 | | - |
500 | | - // Test reverse |
501 | | - let keys_rev: Vec<_> = pb.keys_range(10..20).rev().collect(); |
502 | | - assert_eq!(keys_rev, (10..20).rev().collect::<Vec<_>>()); |
503 | | - |
504 | | - // Test values_range |
505 | | - let values: Vec<_> = pb.values_range(10..20).cloned().collect(); |
506 | | - assert_eq!(values, (10..20).map(|i| i * 10).collect::<Vec<_>>()); |
507 | | - |
508 | | - // Test iter_range |
509 | | - let pairs: Vec<_> = pb.iter_range(10..20).map(|(k, v)| (k, *v)).collect(); |
510 | | - assert_eq!(pairs, (10..20).map(|i| (i, i * 10)).collect::<Vec<_>>()); |
511 | | - } |
512 | | - |
513 | | - #[test] |
514 | | - fn test_retain() { |
515 | | - let mut pb = SortedIndexBuffer::default(); |
516 | | - for i in 0..100 { |
517 | | - pb.insert(i, i * 10); |
518 | | - } |
519 | | - |
520 | | - pb.retain(|i, _v| i % 2 == 0); |
521 | | - |
522 | | - assert_eq!(pb.keys().next(), Some(0)); |
523 | | - assert_eq!(pb.keys().next_back(), Some(98)); |
524 | | - assert_eq!(pb.keys().count(), 50); |
525 | | - for i in 0..100 { |
526 | | - if i % 2 == 0 { |
527 | | - assert_eq!(pb.get(i), Some(&(i * 10))); |
528 | | - } else { |
529 | | - assert_eq!(pb.get(i), None); |
530 | | - } |
531 | | - } |
532 | | - pb.check_invariants_expensive(); |
533 | | - |
534 | | - pb.retain(|_, _| false); |
535 | | - assert!(pb.is_empty()); |
536 | | - pb.check_invariants_expensive(); |
537 | | - } |
538 | | - |
539 | | - #[test] |
540 | | - fn test_retain_range() { |
541 | | - let mut pb = SortedIndexBuffer::default(); |
542 | | - for i in 0..100 { |
543 | | - pb.insert(i, i * 10); |
544 | | - } |
545 | | - |
546 | | - pb.retain_range(20..80); |
547 | | - |
548 | | - assert_eq!(pb.keys().next(), Some(20)); |
549 | | - assert_eq!(pb.keys().next_back(), Some(79)); |
550 | | - assert_eq!(pb.keys().count(), 60); |
551 | | - for i in 20..80 { |
552 | | - assert_eq!(pb.get(i), Some(&(i * 10))); |
553 | | - } |
554 | | - pb.check_invariants_expensive(); |
555 | | - |
556 | | - // Retain with range outside current bounds -> empty |
557 | | - pb.retain_range(200..300); |
558 | | - assert!(pb.is_empty()); |
559 | | - pb.check_invariants_expensive(); |
560 | | - |
561 | | - // Rebuild and retain with superset range -> no-op |
562 | | - for i in 10..20 { |
563 | | - pb.insert(i, i * 10); |
564 | | - } |
565 | | - pb.retain_range(0..100); |
566 | | - assert_eq!(pb.keys().count(), 10); |
567 | | - pb.check_invariants_expensive(); |
568 | | - } |
569 | | - |
570 | | - fn assert_same<I1, I2, T>(iter1: I1, iter2: I2) |
571 | | - where |
572 | | - I1: Iterator<Item = T>, |
573 | | - I2: Iterator<Item = T>, |
574 | | - T: PartialEq + std::fmt::Debug, |
575 | | - { |
576 | | - let vec1: Vec<T> = iter1.collect(); |
577 | | - let vec2: Vec<T> = iter2.collect(); |
578 | | - assert_eq!(vec1, vec2); |
579 | | - } |
580 | | - |
581 | | - #[derive(Debug, Clone)] |
582 | | - enum InsertRemoveGetOp { |
583 | | - Insert(u64, u64), |
584 | | - Remove(u64), |
585 | | - Get(u64), |
586 | | - } |
587 | | - |
588 | | - fn op_strategy() -> impl Strategy<Value = InsertRemoveGetOp> { |
589 | | - prop_oneof![ |
590 | | - (0..1000u64, any::<u64>()).prop_map(|(k, v)| InsertRemoveGetOp::Insert(k, v)), |
591 | | - (0..1000u64).prop_map(InsertRemoveGetOp::Remove), |
592 | | - (0..1000u64).prop_map(InsertRemoveGetOp::Get), |
593 | | - ] |
594 | | - } |
595 | | - |
596 | | - proptest! { |
597 | | - #[test] |
598 | | - fn test_insert_remove_get(ops in prop::collection::vec(op_strategy(), 0..1000)) { |
599 | | - let mut pb = SortedIndexBuffer::default(); |
600 | | - let mut reference = BTreeMap::new(); |
601 | | - |
602 | | - for op in ops { |
603 | | - match op { |
604 | | - InsertRemoveGetOp::Insert(k, v) => { |
605 | | - pb.insert(k, v); |
606 | | - reference.insert(k, v); |
607 | | - } |
608 | | - InsertRemoveGetOp::Remove(k) => { |
609 | | - let v1 = pb.remove(k); |
610 | | - let v2 = reference.remove(&k); |
611 | | - assert_eq!(v1, v2); |
612 | | - } |
613 | | - InsertRemoveGetOp::Get(k) => { |
614 | | - let v1 = pb.get(k); |
615 | | - let v2 = reference.get(&k); |
616 | | - assert_eq!(v1, v2); |
617 | | - } |
618 | | - } |
619 | | - pb.check_invariants_expensive(); |
620 | | - } |
621 | | - |
622 | | - // Final state should match |
623 | | - assert_same(pb.iter(), reference.iter().map(|(k, v)| (*k, v))); |
624 | | - } |
625 | | - } |
626 | | -} |
0 commit comments