Skip to content

Commit 282ba6e

Browse files
authored
Merge pull request #294 from cuviper/splice
Add splicing iterators
2 parents b5b5493 + 33155c7 commit 282ba6e

File tree

5 files changed

+339
-5
lines changed

5 files changed

+339
-5
lines changed

src/map.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod tests;
1414

1515
pub use self::core::{Entry, IndexedEntry, OccupiedEntry, VacantEntry};
1616
pub use self::iter::{
17-
Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut,
17+
Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Splice, Values, ValuesMut,
1818
};
1919
pub use self::slice::Slice;
2020
pub use crate::mutable_keys::MutableKeys;
@@ -418,6 +418,44 @@ where
418418
let hash = self.hash(&key);
419419
self.core.entry(hash, key)
420420
}
421+
422+
/// Creates a splicing iterator that replaces the specified range in the map
423+
/// with the given `replace_with` key-value iterator and yields the removed
424+
/// items. `replace_with` does not need to be the same length as `range`.
425+
///
426+
/// The `range` is removed even if the iterator is not consumed until the
427+
/// end. It is unspecified how many elements are removed from the map if the
428+
/// `Splice` value is leaked.
429+
///
430+
/// The input iterator `replace_with` is only consumed when the `Splice`
431+
/// value is dropped. If a key from the iterator matches an existing entry
432+
/// in the map (outside of `range`), then the value will be updated in that
433+
/// position. Otherwise, the new key-value pair will be inserted in the
434+
/// replaced `range`.
435+
///
436+
/// ***Panics*** if the starting point is greater than the end point or if
437+
/// the end point is greater than the length of the map.
438+
///
439+
/// # Examples
440+
///
441+
/// ```
442+
/// use indexmap::IndexMap;
443+
///
444+
/// let mut map = IndexMap::from([(0, '_'), (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]);
445+
/// let new = [(5, 'E'), (4, 'D'), (3, 'C'), (2, 'B'), (1, 'A')];
446+
/// let removed: Vec<_> = map.splice(2..4, new).collect();
447+
///
448+
/// // 1 and 4 got new values, while 5, 3, and 2 were newly inserted.
449+
/// assert!(map.into_iter().eq([(0, '_'), (1, 'A'), (5, 'E'), (3, 'C'), (2, 'B'), (4, 'D')]));
450+
/// assert_eq!(removed, &[(2, 'b'), (3, 'c')]);
451+
/// ```
452+
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter, K, V, S>
453+
where
454+
R: RangeBounds<usize>,
455+
I: IntoIterator<Item = (K, V)>,
456+
{
457+
Splice::new(self, range, replace_with.into_iter())
458+
}
421459
}
422460

423461
impl<K, V, S> IndexMap<K, V, S>

src/map/core.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mod raw;
1111

1212
use hashbrown::raw::RawTable;
1313

14-
use crate::vec::{Drain, Vec};
14+
use crate::vec::{self, Vec};
1515
use crate::TryReserveError;
1616
use core::fmt;
1717
use core::mem;
@@ -160,7 +160,7 @@ impl<K, V> IndexMapCore<K, V> {
160160
}
161161
}
162162

163-
pub(crate) fn drain<R>(&mut self, range: R) -> Drain<'_, Bucket<K, V>>
163+
pub(crate) fn drain<R>(&mut self, range: R) -> vec::Drain<'_, Bucket<K, V>>
164164
where
165165
R: RangeBounds<usize>,
166166
{
@@ -192,6 +192,28 @@ impl<K, V> IndexMapCore<K, V> {
192192
Self { indices, entries }
193193
}
194194

195+
pub(crate) fn split_splice<R>(&mut self, range: R) -> (Self, vec::IntoIter<Bucket<K, V>>)
196+
where
197+
R: RangeBounds<usize>,
198+
{
199+
let range = simplify_range(range, self.len());
200+
self.erase_indices(range.start, self.entries.len());
201+
let entries = self.entries.split_off(range.end);
202+
let drained = self.entries.split_off(range.start);
203+
204+
let mut indices = RawTable::with_capacity(entries.len());
205+
raw::insert_bulk_no_grow(&mut indices, &entries);
206+
(Self { indices, entries }, drained.into_iter())
207+
}
208+
209+
/// Append from another map without checking whether items already exist.
210+
pub(crate) fn append_unchecked(&mut self, other: &mut Self) {
211+
self.reserve(other.len());
212+
raw::insert_bulk_no_grow(&mut self.indices, &other.entries);
213+
self.entries.append(&mut other.entries);
214+
other.indices.clear();
215+
}
216+
195217
/// Reserve capacity for `additional` more key-value pairs.
196218
pub(crate) fn reserve(&mut self, additional: usize) {
197219
self.indices.reserve(additional, get_hash(&self.entries));

src/map/iter.rs

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use super::core::IndexMapCore;
12
use super::{Bucket, Entries, IndexMap, Slice};
23

34
use alloc::vec::{self, Vec};
45
use core::fmt;
6+
use core::hash::{BuildHasher, Hash};
57
use core::iter::FusedIterator;
6-
use core::ops::Index;
8+
use core::ops::{Index, RangeBounds};
79
use core::slice;
810

911
impl<'a, K, V, S> IntoIterator for &'a IndexMap<K, V, S> {
@@ -580,3 +582,132 @@ impl<K, V> Default for IntoValues<K, V> {
580582
}
581583
}
582584
}
585+
586+
/// A splicing iterator for `IndexMap`.
587+
///
588+
/// This `struct` is created by [`IndexMap::splice()`].
589+
/// See its documentation for more.
590+
pub struct Splice<'a, I, K, V, S>
591+
where
592+
I: Iterator<Item = (K, V)>,
593+
K: Hash + Eq,
594+
S: BuildHasher,
595+
{
596+
map: &'a mut IndexMap<K, V, S>,
597+
tail: IndexMapCore<K, V>,
598+
drain: vec::IntoIter<Bucket<K, V>>,
599+
replace_with: I,
600+
}
601+
602+
impl<'a, I, K, V, S> Splice<'a, I, K, V, S>
603+
where
604+
I: Iterator<Item = (K, V)>,
605+
K: Hash + Eq,
606+
S: BuildHasher,
607+
{
608+
pub(super) fn new<R>(map: &'a mut IndexMap<K, V, S>, range: R, replace_with: I) -> Self
609+
where
610+
R: RangeBounds<usize>,
611+
{
612+
let (tail, drain) = map.core.split_splice(range);
613+
Self {
614+
map,
615+
tail,
616+
drain,
617+
replace_with,
618+
}
619+
}
620+
}
621+
622+
impl<I, K, V, S> Drop for Splice<'_, I, K, V, S>
623+
where
624+
I: Iterator<Item = (K, V)>,
625+
K: Hash + Eq,
626+
S: BuildHasher,
627+
{
628+
fn drop(&mut self) {
629+
// Finish draining unconsumed items. We don't strictly *have* to do this
630+
// manually, since we already split it into separate memory, but it will
631+
// match the drop order of `vec::Splice` items this way.
632+
let _ = self.drain.nth(usize::MAX);
633+
634+
// Now insert all the new items. If a key matches an existing entry, it
635+
// keeps the original position and only replaces the value, like `insert`.
636+
while let Some((key, value)) = self.replace_with.next() {
637+
// Since the tail is disjoint, we can try to update it first,
638+
// or else insert (update or append) the primary map.
639+
let hash = self.map.hash(&key);
640+
if let Some(i) = self.tail.get_index_of(hash, &key) {
641+
self.tail.as_entries_mut()[i].value = value;
642+
} else {
643+
self.map.core.insert_full(hash, key, value);
644+
}
645+
}
646+
647+
// Finally, re-append the tail
648+
self.map.core.append_unchecked(&mut self.tail);
649+
}
650+
}
651+
652+
impl<I, K, V, S> Iterator for Splice<'_, I, K, V, S>
653+
where
654+
I: Iterator<Item = (K, V)>,
655+
K: Hash + Eq,
656+
S: BuildHasher,
657+
{
658+
type Item = (K, V);
659+
660+
fn next(&mut self) -> Option<Self::Item> {
661+
self.drain.next().map(Bucket::key_value)
662+
}
663+
664+
fn size_hint(&self) -> (usize, Option<usize>) {
665+
self.drain.size_hint()
666+
}
667+
}
668+
669+
impl<I, K, V, S> DoubleEndedIterator for Splice<'_, I, K, V, S>
670+
where
671+
I: Iterator<Item = (K, V)>,
672+
K: Hash + Eq,
673+
S: BuildHasher,
674+
{
675+
fn next_back(&mut self) -> Option<Self::Item> {
676+
self.drain.next_back().map(Bucket::key_value)
677+
}
678+
}
679+
680+
impl<I, K, V, S> ExactSizeIterator for Splice<'_, I, K, V, S>
681+
where
682+
I: Iterator<Item = (K, V)>,
683+
K: Hash + Eq,
684+
S: BuildHasher,
685+
{
686+
fn len(&self) -> usize {
687+
self.drain.len()
688+
}
689+
}
690+
691+
impl<I, K, V, S> FusedIterator for Splice<'_, I, K, V, S>
692+
where
693+
I: Iterator<Item = (K, V)>,
694+
K: Hash + Eq,
695+
S: BuildHasher,
696+
{
697+
}
698+
699+
impl<'a, I, K, V, S> fmt::Debug for Splice<'a, I, K, V, S>
700+
where
701+
I: fmt::Debug + Iterator<Item = (K, V)>,
702+
K: fmt::Debug + Hash + Eq,
703+
V: fmt::Debug,
704+
S: BuildHasher,
705+
{
706+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
707+
// Follow `vec::Splice` in only printing the drain and replacement
708+
f.debug_struct("Splice")
709+
.field("drain", &self.drain)
710+
.field("replace_with", &self.replace_with)
711+
.finish()
712+
}
713+
}

src/set.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ mod slice;
66
#[cfg(test)]
77
mod tests;
88

9-
pub use self::iter::{Difference, Drain, Intersection, IntoIter, Iter, SymmetricDifference, Union};
9+
pub use self::iter::{
10+
Difference, Drain, Intersection, IntoIter, Iter, Splice, SymmetricDifference, Union,
11+
};
1012
pub use self::slice::Slice;
1113

1214
#[cfg(feature = "rayon")]
@@ -423,6 +425,43 @@ where
423425
{
424426
Union::new(self, other)
425427
}
428+
429+
/// Creates a splicing iterator that replaces the specified range in the set
430+
/// with the given `replace_with` iterator and yields the removed items.
431+
/// `replace_with` does not need to be the same length as `range`.
432+
///
433+
/// The `range` is removed even if the iterator is not consumed until the
434+
/// end. It is unspecified how many elements are removed from the set if the
435+
/// `Splice` value is leaked.
436+
///
437+
/// The input iterator `replace_with` is only consumed when the `Splice`
438+
/// value is dropped. If a value from the iterator matches an existing entry
439+
/// in the set (outside of `range`), then the original will be unchanged.
440+
/// Otherwise, the new value will be inserted in the replaced `range`.
441+
///
442+
/// ***Panics*** if the starting point is greater than the end point or if
443+
/// the end point is greater than the length of the set.
444+
///
445+
/// # Examples
446+
///
447+
/// ```
448+
/// use indexmap::IndexSet;
449+
///
450+
/// let mut set = IndexSet::from([0, 1, 2, 3, 4]);
451+
/// let new = [5, 4, 3, 2, 1];
452+
/// let removed: Vec<_> = set.splice(2..4, new).collect();
453+
///
454+
/// // 1 and 4 kept their positions, while 5, 3, and 2 were newly inserted.
455+
/// assert!(set.into_iter().eq([0, 1, 5, 3, 2, 4]));
456+
/// assert_eq!(removed, &[2, 3]);
457+
/// ```
458+
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter, T, S>
459+
where
460+
R: RangeBounds<usize>,
461+
I: IntoIterator<Item = T>,
462+
{
463+
Splice::new(self, range, replace_with.into_iter())
464+
}
426465
}
427466

428467
impl<T, S> IndexSet<T, S>

0 commit comments

Comments
 (0)