use core::{
cmp::Ordering,
fmt,
iter::FusedIterator,
marker::PhantomData,
ops::{Bound, RangeBounds},
ptr::NonNull,
};
pub use crate::ordered_skip_list::{Drain, Iter};
use crate::{
comparator::{Comparator, OrdComparator},
level_generator::{LevelGenerator, geometric::Geometric},
node::Node,
ordered_skip_list::IntoIter as OslIntoIter,
skip_set::SkipSet,
};
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IntoIter<
T,
const N: usize = 16,
C: Comparator<T> = OrdComparator,
G: LevelGenerator = Geometric,
> {
inner: OslIntoIter<T, N, C, G>,
}
unsafe impl<T: Send, C: Comparator<T> + Send, G: LevelGenerator + Send, const N: usize> Send
for IntoIter<T, N, C, G>
{
}
unsafe impl<T: Sync, C: Comparator<T> + Sync, G: LevelGenerator + Sync, const N: usize> Sync
for IntoIter<T, N, C, G>
{
}
impl<T: fmt::Debug, C: Comparator<T>, G: LevelGenerator, const N: usize> fmt::Debug
for IntoIter<T, N, C, G>
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoIter").finish_non_exhaustive()
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> Iterator for IntoIter<T, N, C, G> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> DoubleEndedIterator
for IntoIter<T, N, C, G>
{
#[inline]
fn next_back(&mut self) -> Option<T> {
self.inner.next_back()
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> ExactSizeIterator
for IntoIter<T, N, C, G>
{
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> FusedIterator
for IntoIter<T, N, C, G>
{
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ExtractIf<
'a,
T,
C: Comparator<T> = OrdComparator,
G: LevelGenerator = Geometric,
R: RangeBounds<T> = core::ops::RangeFull,
F = fn(&T) -> bool,
const N: usize = 16,
> where
F: FnMut(&T) -> bool,
{
set: &'a mut SkipSet<T, N, C, G>,
current: Option<NonNull<Node<T, N>>>,
range: R,
past_hi: bool,
any_removed: bool,
pred: F,
_marker: PhantomData<&'a mut T>,
}
unsafe impl<
T: Send,
C: Comparator<T> + Send,
G: LevelGenerator + Send,
R: RangeBounds<T> + Send,
F: Send,
const N: usize,
> Send for ExtractIf<'_, T, C, G, R, F, N>
where
F: FnMut(&T) -> bool,
{
}
unsafe impl<
T: Sync,
C: Comparator<T> + Sync,
G: LevelGenerator + Sync,
R: RangeBounds<T> + Sync,
F: Sync,
const N: usize,
> Sync for ExtractIf<'_, T, C, G, R, F, N>
where
F: FnMut(&T) -> bool,
{
}
impl<T: fmt::Debug, C: Comparator<T>, G: LevelGenerator, R, F, const N: usize> fmt::Debug
for ExtractIf<'_, T, C, G, R, F, N>
where
R: RangeBounds<T>,
F: FnMut(&T) -> bool,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_list();
let mut ptr = self.current;
while let Some(nn) = ptr {
let node = unsafe { nn.as_ref() };
if let Some(v) = node.value() {
builder.entry(v);
}
ptr = node.next();
}
builder.finish()
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, R, F, const N: usize> Iterator
for ExtractIf<'_, T, C, G, R, F, N>
where
R: RangeBounds<T>,
F: FnMut(&T) -> bool,
{
type Item = T;
#[expect(
clippy::unwrap_in_result,
clippy::expect_used,
reason = "`value()` and `take_value()` return `None` only for the head \
sentinel, which is never reachable via the data-node walk; the \
`expect` fires only on invariant violations"
)]
#[expect(
clippy::multiple_unsafe_ops_per_block,
reason = "raw-pointer dereference, value(), tail-update, and pop() all \
touch provably disjoint heap nodes; splitting across blocks \
would require unsafe-crossing raw-pointer variables"
)]
#[inline]
fn next(&mut self) -> Option<T> {
loop {
if self.past_hi {
return None;
}
let current_nn = self.current?;
let current_ptr: *mut Node<T, N> = current_nn.as_ptr();
let (next_opt, do_skip, do_stop, do_remove) = {
let value_ref: &T = unsafe { (*current_ptr).value() }.expect("data node has value");
let next_opt = unsafe { (*current_ptr).next() };
let cmp = self.set.inner.comparator();
let after_lo = match self.range.start_bound() {
Bound::Unbounded => true,
Bound::Included(lo) => cmp.compare(value_ref, lo) != Ordering::Less,
Bound::Excluded(lo) => cmp.compare(value_ref, lo) == Ordering::Greater,
};
let in_hi = match self.range.end_bound() {
Bound::Unbounded => true,
Bound::Included(hi) => cmp.compare(value_ref, hi) != Ordering::Greater,
Bound::Excluded(hi) => cmp.compare(value_ref, hi) == Ordering::Less,
};
let do_remove = after_lo && in_hi && (self.pred)(value_ref);
(next_opt, !after_lo, after_lo && !in_hi, do_remove)
};
if do_skip {
self.current = next_opt;
continue;
}
if do_stop {
self.past_hi = true;
return None;
}
self.current = next_opt;
if do_remove {
self.any_removed = true;
unsafe {
self.set.inner.decrement_len();
self.set.inner.update_tail_before_pop(current_nn);
let mut boxed = (*current_ptr).pop();
return Some(boxed.take_value().expect("data node has value"));
}
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.set.len()))
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, R, F, const N: usize> FusedIterator
for ExtractIf<'_, T, C, G, R, F, N>
where
R: RangeBounds<T>,
F: FnMut(&T) -> bool,
{
}
impl<T, C: Comparator<T>, G: LevelGenerator, R, F, const N: usize> Drop
for ExtractIf<'_, T, C, G, R, F, N>
where
R: RangeBounds<T>,
F: FnMut(&T) -> bool,
{
#[inline]
fn drop(&mut self) {
if !self.any_removed {
return;
}
unsafe { self.set.inner.rebuild_skip_links() };
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> SkipSet<T, N, C, G> {
#[inline]
pub fn iter(&self) -> Iter<'_, T, N> {
self.inner.iter()
}
#[inline]
pub fn range<R: RangeBounds<T>>(&self, range: R) -> Iter<'_, T, N> {
self.inner.range(range)
}
#[inline]
pub fn drain(&mut self) -> Drain<'_, T> {
self.inner.drain()
}
#[inline]
pub fn extract_if<R: RangeBounds<T>, F: FnMut(&T) -> bool>(
&mut self,
range: R,
pred: F,
) -> ExtractIf<'_, T, C, G, R, F, N> {
let current = unsafe { self.inner.head_ptr().as_ref() }.next();
ExtractIf {
set: self,
current,
range,
past_hi: false,
any_removed: false,
pred,
_marker: PhantomData,
}
}
}
impl<'a, T, C: Comparator<T>, G: LevelGenerator, const N: usize> IntoIterator
for &'a SkipSet<T, N, C, G>
{
type Item = &'a T;
type IntoIter = Iter<'a, T, N>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T, C: Comparator<T>, G: LevelGenerator, const N: usize> IntoIterator for SkipSet<T, N, C, G> {
type Item = T;
type IntoIter = IntoIter<T, N, C, G>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
inner: self.inner.into_iter(),
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::{ExtractIf, IntoIter, SkipSet};
use crate::comparator::FnComparator;
fn make_set(values: &[i32]) -> SkipSet<i32> {
let mut s = SkipSet::new();
for &v in values {
s.insert(v);
}
s
}
fn to_vec(set: &SkipSet<i32>) -> Vec<i32> {
set.iter().copied().collect()
}
#[test]
fn iter_empty() {
let set = make_set(&[]);
let v: Vec<i32> = set.iter().copied().collect();
assert!(v.is_empty());
}
#[test]
fn iter_single() {
let set = make_set(&[42]);
assert_eq!(set.iter().copied().collect::<Vec<_>>(), [42]);
}
#[test]
fn iter_sorted() {
let set = make_set(&[3, 1, 4, 1, 5]);
assert_eq!(set.iter().copied().collect::<Vec<_>>(), [1, 3, 4, 5]);
}
#[test]
fn iter_reverse() {
let set = make_set(&[3, 1, 2]);
assert_eq!(set.iter().copied().rev().collect::<Vec<_>>(), [3, 2, 1]);
}
#[test]
fn iter_double_ended_meet_in_middle() {
let set = make_set(&[1, 2, 3, 4, 5]);
let mut it = set.iter().copied();
assert_eq!(it.next(), Some(1));
assert_eq!(it.next_back(), Some(5));
assert_eq!(it.next(), Some(2));
assert_eq!(it.next_back(), Some(4));
assert_eq!(it.next(), Some(3));
assert_eq!(it.next_back(), None);
assert_eq!(it.next(), None);
}
#[test]
fn range_full() {
let set = make_set(&[1, 2, 3, 4, 5]);
assert_eq!(set.range(..).copied().collect::<Vec<_>>(), [1, 2, 3, 4, 5]);
}
#[test]
fn range_inclusive() {
let set = make_set(&[1, 2, 3, 4, 5]);
assert_eq!(set.range(2..=4).copied().collect::<Vec<_>>(), [2, 3, 4]);
}
#[test]
fn range_exclusive() {
let set = make_set(&[1, 2, 3, 4, 5]);
assert_eq!(set.range(2..4).copied().collect::<Vec<_>>(), [2, 3]);
}
#[test]
fn range_empty() {
let set = make_set(&[1, 2, 3]);
assert!(set.range(5..10).copied().collect::<Vec<i32>>().is_empty());
}
#[test]
fn drain_empty() {
let mut set = make_set(&[]);
let v: Vec<i32> = set.drain().collect();
assert!(v.is_empty());
assert!(set.is_empty());
}
#[test]
fn drain_all() {
let mut set = make_set(&[3, 1, 2]);
let v: Vec<i32> = set.drain().collect();
assert_eq!(v, [1, 2, 3]);
assert!(set.is_empty());
}
#[test]
fn drain_set_empty_after_partial_consume() {
let mut set = make_set(&[1, 2, 3]);
let d = set.drain();
drop(d);
assert!(set.is_empty());
}
#[test]
fn into_iter_forward() {
let set = make_set(&[3, 1, 2]);
let v: Vec<i32> = set.into_iter().collect();
assert_eq!(v, [1, 2, 3]);
}
#[test]
fn into_iter_reverse() {
let set = make_set(&[3, 1, 2]);
let v: Vec<i32> = set.into_iter().rev().collect();
assert_eq!(v, [3, 2, 1]);
}
#[test]
fn into_iter_empty() {
let set = make_set(&[]);
let v: Vec<i32> = set.into_iter().collect();
assert!(v.is_empty());
}
#[test]
fn into_iter_size_hint() {
let set = make_set(&[1, 2, 3]);
let mut it = set.into_iter();
assert_eq!(it.size_hint(), (3, Some(3)));
it.next();
assert_eq!(it.size_hint(), (2, Some(2)));
}
#[test]
fn into_iter_debug_non_exhaustive() {
let set = make_set(&[1]);
let it = set.into_iter();
let s = format!("{it:?}");
assert!(s.contains("IntoIter"));
}
#[test]
fn ref_into_iter_forward() {
let set = make_set(&[3, 1, 2]);
let v: Vec<i32> = (&set).into_iter().copied().collect();
assert_eq!(v, [1, 2, 3]);
}
#[test]
fn into_iter_is_correct_type() {
let set = make_set(&[1, 2, 3]);
let _it: IntoIter<i32> = set.into_iter();
}
#[test]
fn extract_if_empty_set() {
let mut set = make_set(&[]);
let removed: Vec<i32> = set.extract_if(.., |_| true).collect();
assert!(removed.is_empty());
assert!(set.is_empty());
}
#[test]
fn extract_if_none_match() {
let mut set = make_set(&[1, 2, 3]);
let removed: Vec<i32> = set.extract_if(.., |_| false).collect();
assert!(removed.is_empty());
assert_eq!(to_vec(&set), [1, 2, 3]);
}
#[test]
fn extract_if_all_match() {
let mut set = make_set(&[1, 2, 3]);
let removed: Vec<i32> = set.extract_if(.., |_| true).collect();
assert_eq!(removed, [1, 2, 3]);
assert!(set.is_empty());
}
#[test]
#[expect(
clippy::integer_division_remainder_used,
reason = "test intent is clearest with %"
)]
fn extract_if_even() {
let mut set = make_set(&[1, 2, 3, 4, 5]);
let removed: Vec<i32> = set.extract_if(.., |x| x % 2 == 0).collect();
assert_eq!(removed, [2, 4]);
assert_eq!(to_vec(&set), [1, 3, 5]);
}
#[test]
#[expect(
clippy::integer_division_remainder_used,
reason = "test intent is clearest with %"
)]
fn extract_if_odd() {
let mut set = make_set(&[1, 2, 3, 4, 5]);
let removed: Vec<i32> = set.extract_if(.., |x| x % 2 != 0).collect();
assert_eq!(removed, [1, 3, 5]);
assert_eq!(to_vec(&set), [2, 4]);
}
#[test]
#[expect(
clippy::integer_division_remainder_used,
reason = "test intent is clearest with %"
)]
fn extract_if_range_inclusive() {
let mut set = make_set(&[1, 2, 3, 4, 5, 6]);
let removed: Vec<i32> = set.extract_if(2..=5, |x| x % 2 == 0).collect();
assert_eq!(removed, [2, 4]);
assert_eq!(to_vec(&set), [1, 3, 5, 6]);
}
#[test]
fn extract_if_range_exclusive() {
let mut set = make_set(&[1, 2, 3, 4, 5]);
let removed: Vec<i32> = set.extract_if(2..5, |_| true).collect();
assert_eq!(removed, [2, 3, 4]);
assert_eq!(to_vec(&set), [1, 5]);
}
#[test]
fn extract_if_range_from() {
let mut set = make_set(&[1, 2, 3, 4, 5]);
let removed: Vec<i32> = set.extract_if(3.., |_| true).collect();
assert_eq!(removed, [3, 4, 5]);
assert_eq!(to_vec(&set), [1, 2]);
}
#[test]
fn extract_if_range_to_inclusive() {
let mut set = make_set(&[1, 2, 3, 4, 5]);
let removed: Vec<i32> = set.extract_if(..=3, |_| true).collect();
assert_eq!(removed, [1, 2, 3]);
assert_eq!(to_vec(&set), [4, 5]);
}
#[test]
fn extract_if_range_empty_yields_nothing() {
let mut set = make_set(&[1, 2, 3]);
let removed: Vec<i32> = set.extract_if(10..20, |_| true).collect();
assert!(removed.is_empty());
assert_eq!(to_vec(&set), [1, 2, 3]);
}
#[test]
fn extract_if_drop_early_preserves_remaining() {
let mut set = make_set(&[1, 2, 3, 4, 5]);
{
let mut it = set.extract_if(.., |_| true);
assert_eq!(it.next(), Some(1));
assert_eq!(it.next(), Some(2));
}
assert_eq!(to_vec(&set), [3, 4, 5]);
}
#[test]
fn extract_if_drop_with_nothing_removed() {
let mut set = make_set(&[1, 2, 3]);
{
let mut it = set.extract_if(.., |_| false);
it.next(); }
assert_eq!(to_vec(&set), [1, 2, 3]);
}
#[test]
fn extract_if_size_hint() {
let mut set = make_set(&[1, 2, 3]);
let it = set.extract_if(.., |_| true);
let (lo, hi) = it.size_hint();
assert_eq!(lo, 0);
assert_eq!(hi, Some(3));
}
#[test]
fn extract_if_debug() {
let mut set = make_set(&[1, 2, 3]);
let it = set.extract_if(.., |_| false);
let s = format!("{it:?}");
assert!(s.contains('1') || s.contains('['));
}
#[test]
fn extract_if_is_correct_type() {
let mut set = make_set(&[1, 2, 3]);
let _it: ExtractIf<'_, i32, _, _, _, _, 16> = set.extract_if(.., |_| true);
}
#[test]
fn extract_if_custom_comparator() {
use core::cmp::Ordering;
#[expect(
clippy::trivially_copy_pass_by_ref,
reason = "must match Comparator<T> signature"
)]
fn rev(x: &i32, y: &i32) -> Ordering {
y.cmp(x)
}
let fnptr: fn(&i32, &i32) -> Ordering = rev;
let mut set: SkipSet<i32, 16, _> = SkipSet::with_comparator(FnComparator(fnptr));
for v in [1, 2, 3, 4, 5] {
set.insert(v);
}
let removed: Vec<i32> = set.extract_if(..=3, |_| true).collect();
assert_eq!(removed, [5, 4, 3]);
let remaining: Vec<i32> = set.iter().copied().collect();
assert_eq!(remaining, [2, 1]);
}
}