]> git.lizzy.rs Git - rust.git/commitdiff
Rewrite &[T].to_owned() to allocate directly
authorKevin Ballard <kevin@sb.org>
Sat, 3 May 2014 23:11:30 +0000 (16:11 -0700)
committerKevin Ballard <kevin@sb.org>
Thu, 8 May 2014 19:06:21 +0000 (12:06 -0700)
This used to create a Vec<T> and then call .move_iter().collect() to
convert to a ~[T]. We can't do that anymore, so construct the ~[T] in
place instead. This has the added benefit of avoiding an unnecessary
memory copy (from the Vec<T> to the ~[T]).

src/libstd/slice.rs

index c7cefbb28eef9d0e1a143f919db3f720f2723c3b..a1cc99b4905d7946a2a78afec13b64055a86ba95 100644 (file)
@@ -315,15 +315,23 @@ impl<'a, T: Clone> CloneableVector<T> for &'a [T] {
     #[inline]
     fn to_owned(&self) -> ~[T] {
         let len = self.len();
-        let mut result = Vec::with_capacity(len);
-        // Unsafe code so this can be optimised to a memcpy (or something
-        // similarly fast) when T is Copy. LLVM is easily confused, so any
-        // extra operations during the loop can prevent this optimisation
+        let data_size = len.checked_mul(&mem::size_of::<T>());
+        let data_size = data_size.expect("overflow in to_owned()");
+        let size = mem::size_of::<RawVec<()>>().checked_add(&data_size);
+        let size = size.expect("overflow in to_owned()");
+
         unsafe {
+            let ret = malloc_raw(size) as *mut RawVec<()>;
+
+            (*ret).fill = len * mem::nonzero_size_of::<T>();
+            (*ret).alloc = len * mem::nonzero_size_of::<T>();
+
+            // Be careful with the following loop. We want it to be optimized
+            // to a memcpy (or something similarly fast) when T is Copy. LLVM
+            // is easily confused, so any extra operations during the loop can
+            // prevent this optimization.
             let mut i = 0;
-            let p = result.as_mut_ptr();
-            // Use try_finally here otherwise the write to length
-            // inside the loop stops LLVM from optimising this.
+            let p = &mut (*ret).data as *mut _ as *mut T;
             try_finally(
                 &mut i, (),
                 |i, ()| while *i < len {
@@ -332,9 +340,15 @@ fn to_owned(&self) -> ~[T] {
                         self.unsafe_ref(*i).clone());
                     *i += 1;
                 },
-                |i| result.set_len(*i));
+                |i| if *i < len {
+                    // we must be failing, clean up after ourselves
+                    for j in range(0, *i as int) {
+                        ptr::read(&*p.offset(j));
+                    }
+                    exchange_free(ret as *u8);
+                });
+            cast::transmute(ret)
         }
-        result.move_iter().collect()
     }
 
     #[inline(always)]