Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c7e724a

Browse files
committedApr 25, 2017
Auto merge of #40434 - mattico:splice-update, r=alexcrichton
Implement Vec::splice and String::splice (RFC 1432) RFC: rust-lang/rfcs#1432, tracking issue: #32310 A rebase of #32355 with a few more tests. Let me know if you have any ideas for more tests. cc @SimonSapin
2 parents 0777c75 + feae5a0 commit c7e724a

File tree

8 files changed

+449
-8
lines changed

8 files changed

+449
-8
lines changed
 

‎src/doc/unstable-book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
- [slice_rsplit](library-features/slice-rsplit.md)
196196
- [sort_internals](library-features/sort-internals.md)
197197
- [sort_unstable](library-features/sort-unstable.md)
198+
- [splice](library-features/splice.md)
198199
- [step_by](library-features/step-by.md)
199200
- [step_trait](library-features/step-trait.md)
200201
- [str_checked_slicing](library-features/str-checked-slicing.md)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# `splice`
2+
3+
The tracking issue for this feature is: [#32310]
4+
5+
[#32310]: https://github.com/rust-lang/rust/issues/32310
6+
7+
------------------------
8+
9+
The `splice()` method on `Vec` and `String` allows you to replace a range
10+
of values in a vector or string with another range of values, and returns
11+
the replaced values.
12+
13+
A simple example:
14+
15+
```rust
16+
#![feature(splice)]
17+
let mut s = String::from("α is alpha, β is beta");
18+
let beta_offset = s.find('β').unwrap_or(s.len());
19+
20+
// Replace the range up until the β from the string
21+
let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
22+
assert_eq!(t, "α is alpha, ");
23+
assert_eq!(s, "Α is capital alpha; β is beta");
24+
```

‎src/libcollections/slice.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,13 +1519,9 @@ impl<T: Clone> ToOwned for [T] {
15191519
self.to_vec()
15201520
}
15211521

1522-
// HACK(japaric): with cfg(test) the inherent `[T]::to_vec`, which is required for this method
1523-
// definition, is not available. Since we don't require this method for testing purposes, I'll
1524-
// just stub it
1525-
// NB see the slice::hack module in slice.rs for more information
15261522
#[cfg(test)]
15271523
fn to_owned(&self) -> Vec<T> {
1528-
panic!("not available with cfg(test)")
1524+
hack::to_vec(self)
15291525
}
15301526

15311527
fn clone_into(&self, target: &mut Vec<T>) {

‎src/libcollections/string.rs

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1316,7 +1316,7 @@ impl String {
13161316
self.vec.clear()
13171317
}
13181318

1319-
/// Create a draining iterator that removes the specified range in the string
1319+
/// Creates a draining iterator that removes the specified range in the string
13201320
/// and yields the removed chars.
13211321
///
13221322
/// Note: The element range is removed even if the iterator is not
@@ -1382,6 +1382,71 @@ impl String {
13821382
}
13831383
}
13841384

1385+
/// Creates a splicing iterator that removes the specified range in the string,
1386+
/// replaces with the given string, and yields the removed chars.
1387+
/// The given string doesn’t need to be the same length as the range.
1388+
///
1389+
/// Note: The element range is removed when the `Splice` is dropped,
1390+
/// even if the iterator is not consumed until the end.
1391+
///
1392+
/// # Panics
1393+
///
1394+
/// Panics if the starting point or end point do not lie on a [`char`]
1395+
/// boundary, or if they're out of bounds.
1396+
///
1397+
/// [`char`]: ../../std/primitive.char.html
1398+
///
1399+
/// # Examples
1400+
///
1401+
/// Basic usage:
1402+
///
1403+
/// ```
1404+
/// #![feature(splice)]
1405+
/// let mut s = String::from("α is alpha, β is beta");
1406+
/// let beta_offset = s.find('β').unwrap_or(s.len());
1407+
///
1408+
/// // Replace the range up until the β from the string
1409+
/// let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
1410+
/// assert_eq!(t, "α is alpha, ");
1411+
/// assert_eq!(s, "Α is capital alpha; β is beta");
1412+
/// ```
1413+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
1414+
pub fn splice<'a, 'b, R>(&'a mut self, range: R, replace_with: &'b str) -> Splice<'a, 'b>
1415+
where R: RangeArgument<usize>
1416+
{
1417+
// Memory safety
1418+
//
1419+
// The String version of Splice does not have the memory safety issues
1420+
// of the vector version. The data is just plain bytes.
1421+
// Because the range removal happens in Drop, if the Splice iterator is leaked,
1422+
// the removal will not happen.
1423+
let len = self.len();
1424+
let start = match range.start() {
1425+
Included(&n) => n,
1426+
Excluded(&n) => n + 1,
1427+
Unbounded => 0,
1428+
};
1429+
let end = match range.end() {
1430+
Included(&n) => n + 1,
1431+
Excluded(&n) => n,
1432+
Unbounded => len,
1433+
};
1434+
1435+
// Take out two simultaneous borrows. The &mut String won't be accessed
1436+
// until iteration is over, in Drop.
1437+
let self_ptr = self as *mut _;
1438+
// slicing does the appropriate bounds checks
1439+
let chars_iter = self[start..end].chars();
1440+
1441+
Splice {
1442+
start: start,
1443+
end: end,
1444+
iter: chars_iter,
1445+
string: self_ptr,
1446+
replace_with: replace_with
1447+
}
1448+
}
1449+
13851450
/// Converts this `String` into a `Box<str>`.
13861451
///
13871452
/// This will drop any excess capacity.
@@ -2145,3 +2210,61 @@ impl<'a> DoubleEndedIterator for Drain<'a> {
21452210

21462211
#[unstable(feature = "fused", issue = "35602")]
21472212
impl<'a> FusedIterator for Drain<'a> {}
2213+
2214+
/// A splicing iterator for `String`.
2215+
///
2216+
/// This struct is created by the [`splice()`] method on [`String`]. See its
2217+
/// documentation for more.
2218+
///
2219+
/// [`splice()`]: struct.String.html#method.splice
2220+
/// [`String`]: struct.String.html
2221+
#[derive(Debug)]
2222+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2223+
pub struct Splice<'a, 'b> {
2224+
/// Will be used as &'a mut String in the destructor
2225+
string: *mut String,
2226+
/// Start of part to remove
2227+
start: usize,
2228+
/// End of part to remove
2229+
end: usize,
2230+
/// Current remaining range to remove
2231+
iter: Chars<'a>,
2232+
replace_with: &'b str,
2233+
}
2234+
2235+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2236+
unsafe impl<'a, 'b> Sync for Splice<'a, 'b> {}
2237+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2238+
unsafe impl<'a, 'b> Send for Splice<'a, 'b> {}
2239+
2240+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2241+
impl<'a, 'b> Drop for Splice<'a, 'b> {
2242+
fn drop(&mut self) {
2243+
unsafe {
2244+
let vec = (*self.string).as_mut_vec();
2245+
vec.splice(self.start..self.end, self.replace_with.bytes());
2246+
}
2247+
}
2248+
}
2249+
2250+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2251+
impl<'a, 'b> Iterator for Splice<'a, 'b> {
2252+
type Item = char;
2253+
2254+
#[inline]
2255+
fn next(&mut self) -> Option<char> {
2256+
self.iter.next()
2257+
}
2258+
2259+
fn size_hint(&self) -> (usize, Option<usize>) {
2260+
self.iter.size_hint()
2261+
}
2262+
}
2263+
2264+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2265+
impl<'a, 'b> DoubleEndedIterator for Splice<'a, 'b> {
2266+
#[inline]
2267+
fn next_back(&mut self) -> Option<char> {
2268+
self.iter.next_back()
2269+
}
2270+
}

‎src/libcollections/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![feature(pattern)]
2121
#![feature(placement_in_syntax)]
2222
#![feature(rand)]
23+
#![feature(splice)]
2324
#![feature(step_by)]
2425
#![feature(str_escape)]
2526
#![feature(test)]

‎src/libcollections/tests/string.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,69 @@ fn test_drain() {
419419
assert_eq!(t, "");
420420
}
421421

422+
#[test]
423+
fn test_splice() {
424+
let mut s = "Hello, world!".to_owned();
425+
let t: String = s.splice(7..12, "世界").collect();
426+
assert_eq!(s, "Hello, 世界!");
427+
assert_eq!(t, "world");
428+
}
429+
430+
#[test]
431+
#[should_panic]
432+
fn test_splice_char_boundary() {
433+
let mut s = "Hello, 世界!".to_owned();
434+
s.splice(..8, "");
435+
}
436+
437+
#[test]
438+
fn test_splice_inclusive_range() {
439+
let mut v = String::from("12345");
440+
let t: String = v.splice(2...3, "789").collect();
441+
assert_eq!(v, "127895");
442+
assert_eq!(t, "34");
443+
let t2: String = v.splice(1...2, "A").collect();
444+
assert_eq!(v, "1A895");
445+
assert_eq!(t2, "27");
446+
}
447+
448+
#[test]
449+
#[should_panic]
450+
fn test_splice_out_of_bounds() {
451+
let mut s = String::from("12345");
452+
s.splice(5..6, "789");
453+
}
454+
455+
#[test]
456+
#[should_panic]
457+
fn test_splice_inclusive_out_of_bounds() {
458+
let mut s = String::from("12345");
459+
s.splice(5...5, "789");
460+
}
461+
462+
#[test]
463+
fn test_splice_empty() {
464+
let mut s = String::from("12345");
465+
let t: String = s.splice(1..2, "").collect();
466+
assert_eq!(s, "1345");
467+
assert_eq!(t, "2");
468+
}
469+
470+
#[test]
471+
fn test_splice_unbounded() {
472+
let mut s = String::from("12345");
473+
let t: String = s.splice(.., "").collect();
474+
assert_eq!(s, "");
475+
assert_eq!(t, "12345");
476+
}
477+
478+
#[test]
479+
fn test_splice_forget() {
480+
let mut s = String::from("12345");
481+
::std::mem::forget(s.splice(2..4, "789"));
482+
assert_eq!(s, "12345");
483+
}
484+
422485
#[test]
423486
fn test_extend_ref() {
424487
let mut a = "foo".to_string();

‎src/libcollections/tests/vec.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,69 @@ fn test_drain_inclusive_out_of_bounds() {
579579
v.drain(5...5);
580580
}
581581

582+
#[test]
583+
fn test_splice() {
584+
let mut v = vec![1, 2, 3, 4, 5];
585+
let a = [10, 11, 12];
586+
v.splice(2..4, a.iter().cloned());
587+
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
588+
v.splice(1..3, Some(20));
589+
assert_eq!(v, &[1, 20, 11, 12, 5]);
590+
}
591+
592+
#[test]
593+
fn test_splice_inclusive_range() {
594+
let mut v = vec![1, 2, 3, 4, 5];
595+
let a = [10, 11, 12];
596+
let t1: Vec<_> = v.splice(2...3, a.iter().cloned()).collect();
597+
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
598+
assert_eq!(t1, &[3, 4]);
599+
let t2: Vec<_> = v.splice(1...2, Some(20)).collect();
600+
assert_eq!(v, &[1, 20, 11, 12, 5]);
601+
assert_eq!(t2, &[2, 10]);
602+
}
603+
604+
#[test]
605+
#[should_panic]
606+
fn test_splice_out_of_bounds() {
607+
let mut v = vec![1, 2, 3, 4, 5];
608+
let a = [10, 11, 12];
609+
v.splice(5..6, a.iter().cloned());
610+
}
611+
612+
#[test]
613+
#[should_panic]
614+
fn test_splice_inclusive_out_of_bounds() {
615+
let mut v = vec![1, 2, 3, 4, 5];
616+
let a = [10, 11, 12];
617+
v.splice(5...5, a.iter().cloned());
618+
}
619+
620+
#[test]
621+
fn test_splice_items_zero_sized() {
622+
let mut vec = vec![(), (), ()];
623+
let vec2 = vec![];
624+
let t: Vec<_> = vec.splice(1..2, vec2.iter().cloned()).collect();
625+
assert_eq!(vec, &[(), ()]);
626+
assert_eq!(t, &[()]);
627+
}
628+
629+
#[test]
630+
fn test_splice_unbounded() {
631+
let mut vec = vec![1, 2, 3, 4, 5];
632+
let t: Vec<_> = vec.splice(.., None).collect();
633+
assert_eq!(vec, &[]);
634+
assert_eq!(t, &[1, 2, 3, 4, 5]);
635+
}
636+
637+
#[test]
638+
fn test_splice_forget() {
639+
let mut v = vec![1, 2, 3, 4, 5];
640+
let a = [10, 11, 12];
641+
::std::mem::forget(v.splice(2..4, a.iter().cloned()));
642+
assert_eq!(v, &[1, 2]);
643+
}
644+
582645
#[test]
583646
fn test_into_boxed_slice() {
584647
let xs = vec![1, 2, 3];

‎src/libcollections/vec.rs

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,13 +1057,13 @@ impl<T> Vec<T> {
10571057
self.len += count;
10581058
}
10591059

1060-
/// Create a draining iterator that removes the specified range in the vector
1060+
/// Creates a draining iterator that removes the specified range in the vector
10611061
/// and yields the removed items.
10621062
///
10631063
/// Note 1: The element range is removed even if the iterator is only
10641064
/// partially consumed or not consumed at all.
10651065
///
1066-
/// Note 2: It is unspecified how many elements are removed from the vector,
1066+
/// Note 2: It is unspecified how many elements are removed from the vector
10671067
/// if the `Drain` value is leaked.
10681068
///
10691069
/// # Panics
@@ -1845,6 +1845,54 @@ impl<T> Vec<T> {
18451845
}
18461846
}
18471847
}
1848+
1849+
/// Creates a splicing iterator that replaces the specified range in the vector
1850+
/// with the given `replace_with` iterator and yields the removed items.
1851+
/// `replace_with` does not need to be the same length as `range`.
1852+
///
1853+
/// Note 1: The element range is removed even if the iterator is not
1854+
/// consumed until the end.
1855+
///
1856+
/// Note 2: It is unspecified how many elements are removed from the vector,
1857+
/// if the `Splice` value is leaked.
1858+
///
1859+
/// Note 3: The input iterator `replace_with` is only consumed
1860+
/// when the `Splice` value is dropped.
1861+
///
1862+
/// Note 4: This is optimal if:
1863+
///
1864+
/// * The tail (elements in the vector after `range`) is empty,
1865+
/// * or `replace_with` yields fewer elements than `range`’s length
1866+
/// * or the lower bound of its `size_hint()` is exact.
1867+
///
1868+
/// Otherwise, a temporary vector is allocated and the tail is moved twice.
1869+
///
1870+
/// # Panics
1871+
///
1872+
/// Panics if the starting point is greater than the end point or if
1873+
/// the end point is greater than the length of the vector.
1874+
///
1875+
/// # Examples
1876+
///
1877+
/// ```
1878+
/// #![feature(splice)]
1879+
/// let mut v = vec![1, 2, 3];
1880+
/// let new = [7, 8];
1881+
/// let u: Vec<_> = v.splice(..2, new.iter().cloned()).collect();
1882+
/// assert_eq!(v, &[7, 8, 3]);
1883+
/// assert_eq!(u, &[1, 2]);
1884+
/// ```
1885+
#[inline]
1886+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
1887+
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<I::IntoIter>
1888+
where R: RangeArgument<usize>, I: IntoIterator<Item=T>
1889+
{
1890+
Splice {
1891+
drain: self.drain(range),
1892+
replace_with: replace_with.into_iter(),
1893+
}
1894+
}
1895+
18481896
}
18491897

18501898
#[stable(feature = "extend_ref", since = "1.2.0")]
@@ -2344,3 +2392,125 @@ impl<'a, T> InPlace<T> for PlaceBack<'a, T> {
23442392
&mut *ptr
23452393
}
23462394
}
2395+
2396+
2397+
/// A splicing iterator for `Vec`.
2398+
///
2399+
/// This struct is created by the [`splice()`] method on [`Vec`]. See its
2400+
/// documentation for more.
2401+
///
2402+
/// [`splice()`]: struct.Vec.html#method.splice
2403+
/// [`Vec`]: struct.Vec.html
2404+
#[derive(Debug)]
2405+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2406+
pub struct Splice<'a, I: Iterator + 'a> {
2407+
drain: Drain<'a, I::Item>,
2408+
replace_with: I,
2409+
}
2410+
2411+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2412+
impl<'a, I: Iterator> Iterator for Splice<'a, I> {
2413+
type Item = I::Item;
2414+
2415+
fn next(&mut self) -> Option<Self::Item> {
2416+
self.drain.next()
2417+
}
2418+
2419+
fn size_hint(&self) -> (usize, Option<usize>) {
2420+
self.drain.size_hint()
2421+
}
2422+
}
2423+
2424+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2425+
impl<'a, I: Iterator> DoubleEndedIterator for Splice<'a, I> {
2426+
fn next_back(&mut self) -> Option<Self::Item> {
2427+
self.drain.next_back()
2428+
}
2429+
}
2430+
2431+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2432+
impl<'a, I: Iterator> ExactSizeIterator for Splice<'a, I> {}
2433+
2434+
2435+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2436+
impl<'a, I: Iterator> Drop for Splice<'a, I> {
2437+
fn drop(&mut self) {
2438+
// exhaust drain first
2439+
while let Some(_) = self.drain.next() {}
2440+
2441+
2442+
unsafe {
2443+
if self.drain.tail_len == 0 {
2444+
let vec = &mut *self.drain.vec.as_mut_ptr();
2445+
vec.extend(self.replace_with.by_ref());
2446+
return
2447+
}
2448+
2449+
// First fill the range left by drain().
2450+
if !self.drain.fill(&mut self.replace_with) {
2451+
return
2452+
}
2453+
2454+
// There may be more elements. Use the lower bound as an estimate.
2455+
// FIXME: Is the upper bound a better guess? Or something else?
2456+
let (lower_bound, _upper_bound) = self.replace_with.size_hint();
2457+
if lower_bound > 0 {
2458+
self.drain.move_tail(lower_bound);
2459+
if !self.drain.fill(&mut self.replace_with) {
2460+
return
2461+
}
2462+
}
2463+
2464+
// Collect any remaining elements.
2465+
// This is a zero-length vector which does not allocate if `lower_bound` was exact.
2466+
let mut collected = self.replace_with.by_ref().collect::<Vec<I::Item>>().into_iter();
2467+
// Now we have an exact count.
2468+
if collected.len() > 0 {
2469+
self.drain.move_tail(collected.len());
2470+
let filled = self.drain.fill(&mut collected);
2471+
debug_assert!(filled);
2472+
debug_assert_eq!(collected.len(), 0);
2473+
}
2474+
}
2475+
// Let `Drain::drop` move the tail back if necessary and restore `vec.len`.
2476+
}
2477+
}
2478+
2479+
/// Private helper methods for `Splice::drop`
2480+
impl<'a, T> Drain<'a, T> {
2481+
/// The range from `self.vec.len` to `self.tail_start` contains elements
2482+
/// that have been moved out.
2483+
/// Fill that range as much as possible with new elements from the `replace_with` iterator.
2484+
/// Return whether we filled the entire range. (`replace_with.next()` didn’t return `None`.)
2485+
unsafe fn fill<I: Iterator<Item=T>>(&mut self, replace_with: &mut I) -> bool {
2486+
let vec = &mut *self.vec.as_mut_ptr();
2487+
let range_start = vec.len;
2488+
let range_end = self.tail_start;
2489+
let range_slice = slice::from_raw_parts_mut(
2490+
vec.as_mut_ptr().offset(range_start as isize),
2491+
range_end - range_start);
2492+
2493+
for place in range_slice {
2494+
if let Some(new_item) = replace_with.next() {
2495+
ptr::write(place, new_item);
2496+
vec.len += 1;
2497+
} else {
2498+
return false
2499+
}
2500+
}
2501+
true
2502+
}
2503+
2504+
/// Make room for inserting more elements before the tail.
2505+
unsafe fn move_tail(&mut self, extra_capacity: usize) {
2506+
let vec = &mut *self.vec.as_mut_ptr();
2507+
let used_capacity = self.tail_start + self.tail_len;
2508+
vec.buf.reserve(used_capacity, extra_capacity);
2509+
2510+
let new_tail_start = self.tail_start + extra_capacity;
2511+
let src = vec.as_ptr().offset(self.tail_start as isize);
2512+
let dst = vec.as_mut_ptr().offset(new_tail_start as isize);
2513+
ptr::copy(src, dst, self.tail_len);
2514+
self.tail_start = new_tail_start;
2515+
}
2516+
}

0 commit comments

Comments
 (0)
Please sign in to comment.