#![forbid(unsafe_code)]
use core::fmt;
use core::iter::{self, FusedIterator};
use core::mem;
use core::ops::{ControlFlow, RangeBounds};
use super::BBTreeMap;
use crate::allocator::{Allocator, Global};
use crate::inline_key::{Auto, InlineKeyStrategy};
use crate::tree::build::TreeBuilder;
use crate::tree::join::DetachedTree;
use crate::tree::node_stack::KeySide;
use crate::tree::root::Tree;
use crate::tree::walk::DyingWalk;
#[must_use = "iterators are lazy and do nothing unless consumed; \
use `retain` to remove and drop elements"]
pub struct ExtractIf<'a, K, V, R, F, S: InlineKeyStrategy<K> = Auto, A: Allocator + Clone = Global>
{
walk: ExtractionWalk<'a, K, V, R, S, A>,
pred: F,
}
pub(crate) struct ExtractionWalk<'a, K, V, R, S: InlineKeyStrategy<K>, A: Allocator + Clone> {
map: &'a mut BBTreeMap<K, V, S, A>,
had_tree: bool,
length: usize,
kept: TreeBuilder<K, V, S, A>,
source: ExtractionSource<K, V, S, A>,
after: DetachedTree<K, V, S, A>,
range: R,
}
struct ExtractionSource<K, V, S: InlineKeyStrategy<K>, A: Allocator> {
rest: DyingWalk<K, V, S, A>,
under_test: Option<(K, V)>,
finished: bool,
}
impl<K, V, S: InlineKeyStrategy<K>, A: Allocator> ExtractionSource<K, V, S, A> {
fn kept_until<'s>(
&'s mut self,
mut pick: impl FnMut(&K, &mut V) -> bool + 's,
mut on_pick: impl FnMut((K, V)) -> ControlFlow<()> + 's,
) -> impl Iterator<Item = (K, V)> + 's {
iter::from_fn(move || {
loop {
if self.finished {
return None;
}
let Some(entry) = self.rest.next() else {
self.finished = true;
return None;
};
let (key, value) = self.under_test.insert(entry);
if !pick(key, value) {
return self.under_test.take();
}
let picked = self.under_test.take().expect("the entry just tested");
if on_pick(picked).is_break() {
return None;
}
}
})
}
}
impl<'a, K: Ord, V, R: RangeBounds<K>, S: InlineKeyStrategy<K>, A: Allocator + Clone>
ExtractionWalk<'a, K, V, R, S, A>
{
pub(crate) fn new(map: &'a mut BBTreeMap<K, V, S, A>, range: R) -> Self {
let length = mem::take(&mut map.length);
let had_tree = map.tree.has_root();
let tree = map.tree.take();
let alloc = &map.alloc;
let (kept, after) = (
TreeBuilder::new(alloc.clone()),
DetachedTree::new(tree, alloc.clone()),
);
let rest = DyingWalk::new(None, 0, alloc.clone());
let mut walk = Self {
map,
had_tree,
length,
kept,
source: ExtractionSource {
rest,
under_test: None,
finished: false,
},
after,
range,
};
if length > 0 {
walk.cut_out_range();
}
walk
}
fn cut_out_range(&mut self) {
let alloc = &self.map.alloc;
if let Some((start, side)) = KeySide::of_start(self.range.start_bound()) {
let tree = self.after.tree_mut();
let cut = tree.split_off_at(start, side, alloc);
self.kept = TreeBuilder::continuing(tree.take(), alloc.clone());
cut.finish(Some(self.kept.tree_mut()), self.after.tree_mut());
}
let tree = self.after.tree_mut();
let Some((end, side)) = KeySide::of_end(self.range.end_bound()) else {
self.source.rest = DyingWalk::new(tree.take().root_page(), usize::MAX, alloc.clone());
return;
};
let cut = tree.split_off_at(end, side, alloc);
self.source.rest = DyingWalk::new(tree.take().root_page(), usize::MAX, alloc.clone());
cut.finish(None, self.after.tree_mut());
}
}
impl<K, V, R, S: InlineKeyStrategy<K>, A: Allocator + Clone> ExtractionWalk<'_, K, V, R, S, A> {
pub(crate) fn next(&mut self, pick: &mut impl FnMut(&K, &mut V) -> bool) -> Option<(K, V)> {
let mut picked = None;
self.kept.extend(self.source.kept_until(pick, |entry| {
picked = Some(entry);
ControlFlow::Break(())
}));
if picked.is_some() {
self.length -= 1;
}
picked
}
pub(crate) fn retain(&mut self, mut keep: impl FnMut(&K, &mut V) -> bool) {
let length = &mut self.length;
self.kept.extend(self.source.kept_until(
|key, value| !keep(key, value),
|entry| {
*length -= 1;
drop(entry);
ControlFlow::Continue(())
},
));
}
pub(crate) fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.length))
}
pub(crate) fn peek(&self) -> Option<(&K, &V)> {
let source = &self.source;
if source.finished {
return None;
}
if let Some((key, value)) = &source.under_test {
return Some((key, value));
}
let mut rest = source.rest.remaining();
rest.next().or_else(|| self.after.first_key_value())
}
}
impl<K, V, R: Unpin, S: InlineKeyStrategy<K>, A: Allocator + Clone + Unpin> Unpin
for ExtractionWalk<'_, K, V, R, S, A>
{
}
impl<K, V, R, S: InlineKeyStrategy<K>, A: Allocator + Clone> Drop
for ExtractionWalk<'_, K, V, R, S, A>
{
fn drop(&mut self) {
let source = &mut self.source;
let rest = &mut source.rest;
self.kept.extend(
source
.under_test
.take()
.into_iter()
.chain(iter::from_fn(|| rest.next())),
);
let map = &mut *self.map;
let kept = mem::replace(&mut self.kept, TreeBuilder::new(map.alloc.clone()));
let dropped = kept.dropped();
let (kept, _) = kept.finish();
map.tree = Tree::concat(kept, self.after.take(), &map.alloc);
map.length = self.length - dropped;
if self.had_tree && !map.tree.has_root() {
map.tree = Tree::new(&map.alloc);
}
}
}
impl<K, V, R, F, S, A> Iterator for ExtractIf<'_, K, V, R, F, S, A>
where
K: PartialOrd,
R: RangeBounds<K>,
F: FnMut(&K, &mut V) -> bool,
S: InlineKeyStrategy<K>,
A: Allocator + Clone,
{
type Item = (K, V);
fn next(&mut self) -> Option<(K, V)> {
self.walk.next(&mut self.pred)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.walk.size_hint()
}
}
impl<K, V, R, F, S, A> FusedIterator for ExtractIf<'_, K, V, R, F, S, A>
where
K: PartialOrd,
R: RangeBounds<K>,
F: FnMut(&K, &mut V) -> bool,
S: InlineKeyStrategy<K>,
A: Allocator + Clone,
{
}
impl<K: fmt::Debug, V: fmt::Debug, R, F, S: InlineKeyStrategy<K>, A: Allocator + Clone> fmt::Debug
for ExtractIf<'_, K, V, R, F, S, A>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExtractIf")
.field("peek", &self.walk.peek())
.finish_non_exhaustive()
}
}
impl<K: Ord, V, S: InlineKeyStrategy<K>, A: Allocator + Clone> BBTreeMap<K, V, S, A> {
pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, S, A>
where
R: RangeBounds<K>,
F: FnMut(&K, &mut V) -> bool,
{
ExtractIf {
walk: ExtractionWalk::new(self, range),
pred,
}
}
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(&K, &mut V) -> bool,
{
ExtractionWalk::new(self, ..).retain(f);
}
}