Skip to content

Commit 2111aff

Browse files
SimonSapinmattico
authored andcommitted
Add Vec::splice and String::splice
1 parent 2bd4b5c commit 2111aff

File tree

6 files changed

+313
-7
lines changed

6 files changed

+313
-7
lines changed

src/libcollections/slice.rs

+1-5
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

+129-1
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,63 @@ 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 even if the iterator is not
1390+
/// 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 = *range.start().unwrap_or(&0);
1425+
let end = *range.end().unwrap_or(&len);
1426+
1427+
// Take out two simultaneous borrows. The &mut String won't be accessed
1428+
// until iteration is over, in Drop.
1429+
let self_ptr = self as *mut _;
1430+
// slicing does the appropriate bounds checks
1431+
let chars_iter = self[start..end].chars();
1432+
1433+
Splice {
1434+
start: start,
1435+
end: end,
1436+
iter: chars_iter,
1437+
string: self_ptr,
1438+
replace_with: replace_with
1439+
}
1440+
}
1441+
13851442
/// Converts this `String` into a `Box<str>`.
13861443
///
13871444
/// This will drop any excess capacity.
@@ -2145,3 +2202,74 @@ impl<'a> DoubleEndedIterator for Drain<'a> {
21452202

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

src/libcollections/tests/lib.rs

+1
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

+8
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,14 @@ 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+
422430
#[test]
423431
fn test_extend_ref() {
424432
let mut a = "foo".to_string();

src/libcollections/tests/vec.rs

+10
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,16 @@ fn test_drain_inclusive_out_of_bounds() {
579579
v.drain(5...5);
580580
}
581581

582+
#[test]
583+
fn 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+
582592
#[test]
583593
fn test_into_boxed_slice() {
584594
let xs = vec![1, 2, 3];

src/libcollections/vec.rs

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

0 commit comments

Comments
 (0)