Skip to content

Commit 50c98a8

Browse files
committed
Add vec::Drain{,Filter}::keep_rest
These methods allow to cancel draining of unyielded elements.
1 parent a2da4af commit 50c98a8

File tree

4 files changed

+153
-3
lines changed

4 files changed

+153
-3
lines changed

library/alloc/src/vec/drain.rs

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::alloc::{Allocator, Global};
22
use core::fmt;
33
use core::iter::{FusedIterator, TrustedLen};
4-
use core::mem;
4+
use core::mem::{self, ManuallyDrop};
55
use core::ptr::{self, NonNull};
66
use core::slice::{self};
77

@@ -65,6 +65,59 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
6565
pub fn allocator(&self) -> &A {
6666
unsafe { self.vec.as_ref().allocator() }
6767
}
68+
69+
/// Keep unyielded elements in the source `Vec`.
70+
#[unstable(feature = "drain_keep_rest", issue = "none")]
71+
pub fn keep_rest(self) {
72+
// At this moment layout looks like this:
73+
//
74+
// [head] [yielded by next] [unyielded] [yielded by next_back] [tail]
75+
// ^-- start \_________/-- unyielded_len \____/-- self.tail_len
76+
// ^-- unyielded_ptr ^-- tail
77+
//
78+
// Normally `Drop` impl would drop [unyielded] and then move [tail] to the `start`.
79+
// Here we want to
80+
// 1. Move [unyielded] to `start`
81+
// 2. Move [tail] to a new start at `start + len(unyielded)`
82+
// 3. Update length of the original vec to `len(head) + len(unyielded) + len(tail)`
83+
// a. In case of ZST, this is the only thing we want to do
84+
// 4. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
85+
let mut this = ManuallyDrop::new(self);
86+
87+
unsafe {
88+
let source_vec = this.vec.as_mut();
89+
90+
let start = source_vec.len();
91+
let tail = this.tail_start;
92+
93+
let unyielded_len = this.iter.len();
94+
let unyielded_ptr = this.iter.as_slice().as_ptr();
95+
96+
// ZSTs have no identity, so we don't need to move them around.
97+
let needs_move = mem::size_of::<T>() != 0;
98+
99+
if needs_move {
100+
let start_ptr = source_vec.as_mut_ptr().add(start);
101+
102+
// memmove back unyielded elements
103+
if unyielded_ptr != start_ptr {
104+
let src = unyielded_ptr;
105+
let dst = start_ptr;
106+
107+
ptr::copy(src, dst, unyielded_len);
108+
}
109+
110+
// memmove back untouched tail
111+
if tail != (start + unyielded_len) {
112+
let src = source_vec.as_ptr().add(tail);
113+
let dst = start_ptr.add(unyielded_len);
114+
ptr::copy(src, dst, this.tail_len);
115+
}
116+
}
117+
118+
source_vec.set_len(start + unyielded_len + this.tail_len);
119+
}
120+
}
68121
}
69122

70123
#[stable(feature = "vec_drain_as_slice", since = "1.46.0")]

library/alloc/src/vec/drain_filter.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::alloc::{Allocator, Global};
2-
use core::ptr::{self};
3-
use core::slice::{self};
2+
use core::mem::{self, ManuallyDrop};
3+
use core::ptr;
4+
use core::slice;
45

56
use super::Vec;
67

@@ -54,6 +55,42 @@ where
5455
pub fn allocator(&self) -> &A {
5556
self.vec.allocator()
5657
}
58+
59+
/// Keep unyielded elements in the source `Vec`.
60+
#[unstable(feature = "drain_keep_rest", issue = "none")]
61+
pub fn keep_rest(self) {
62+
// At this moment layout looks like this:
63+
//
64+
// _____________________/-- old_len
65+
// / \
66+
// [kept] [yielded] [tail]
67+
// \_______/ ^-- idx
68+
// \-- del
69+
//
70+
// Normally `Drop` impl would drop [tail] (via .for_each(drop), ie still calling `pred`)
71+
//
72+
// 1. Move [tail] after [kept]
73+
// 2. Update length of the original vec to `old_len - del`
74+
// a. In case of ZST, this is the only thing we want to do
75+
// 3. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
76+
let mut this = ManuallyDrop::new(self);
77+
78+
unsafe {
79+
// ZSTs have no identity, so we don't need to move them around.
80+
let needs_move = mem::size_of::<T>() != 0;
81+
82+
if needs_move && this.idx < this.old_len && this.del > 0 {
83+
let ptr = this.vec.as_mut_ptr();
84+
let src = ptr.add(this.idx);
85+
let dst = src.sub(this.del);
86+
let tail_len = this.old_len - this.idx;
87+
src.copy_to(dst, tail_len);
88+
}
89+
90+
let new_len = this.old_len - this.del;
91+
this.vec.set_len(new_len);
92+
}
93+
}
5794
}
5895

5996
#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#![feature(bench_black_box)]
4646
#![feature(strict_provenance)]
4747
#![feature(once_cell)]
48+
#![feature(drain_keep_rest)]
4849

4950
use std::collections::hash_map::DefaultHasher;
5051
use std::hash::{Hash, Hasher};

library/alloc/tests/vec.rs

+59
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,36 @@ fn test_drain_leak() {
793793
assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]);
794794
}
795795

796+
#[test]
797+
fn test_drain_keep_rest() {
798+
let mut v = vec![0, 1, 2, 3, 4, 5, 6];
799+
let mut drain = v.drain(1..6);
800+
assert_eq!(drain.next(), Some(1));
801+
assert_eq!(drain.next_back(), Some(5));
802+
assert_eq!(drain.next(), Some(2));
803+
804+
drain.keep_rest();
805+
assert_eq!(v, &[0, 3, 4, 6]);
806+
}
807+
808+
#[test]
809+
fn test_drain_keep_rest_all() {
810+
let mut v = vec![0, 1, 2, 3, 4, 5, 6];
811+
v.drain(1..6).keep_rest();
812+
assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]);
813+
}
814+
815+
#[test]
816+
fn test_drain_keep_rest_none() {
817+
let mut v = vec![0, 1, 2, 3, 4, 5, 6];
818+
let mut drain = v.drain(1..6);
819+
820+
drain.by_ref().for_each(drop);
821+
822+
drain.keep_rest();
823+
assert_eq!(v, &[0, 6]);
824+
}
825+
796826
#[test]
797827
fn test_splice() {
798828
let mut v = vec![1, 2, 3, 4, 5];
@@ -1478,6 +1508,35 @@ fn drain_filter_unconsumed() {
14781508
assert_eq!(vec, [2, 4]);
14791509
}
14801510

1511+
#[test]
1512+
fn test_drain_filter_keep_rest() {
1513+
let mut v = vec![0, 1, 2, 3, 4, 5, 6];
1514+
let mut drain = v.drain_filter(|&mut x| x % 2 == 0);
1515+
assert_eq!(drain.next(), Some(0));
1516+
assert_eq!(drain.next(), Some(2));
1517+
1518+
drain.keep_rest();
1519+
assert_eq!(v, &[1, 3, 4, 5, 6]);
1520+
}
1521+
1522+
#[test]
1523+
fn test_drain_filter_keep_rest_all() {
1524+
let mut v = vec![0, 1, 2, 3, 4, 5, 6];
1525+
v.drain_filter(|_| true).keep_rest();
1526+
assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]);
1527+
}
1528+
1529+
#[test]
1530+
fn test_drain_filter_keep_rest_none() {
1531+
let mut v = vec![0, 1, 2, 3, 4, 5, 6];
1532+
let mut drain = v.drain_filter(|_| true);
1533+
1534+
drain.by_ref().for_each(drop);
1535+
1536+
drain.keep_rest();
1537+
assert_eq!(v, &[]);
1538+
}
1539+
14811540
#[test]
14821541
fn test_reserve_exact() {
14831542
// This is all the same as test_reserve

0 commit comments

Comments
 (0)