]> git.lizzy.rs Git - rust.git/commitdiff
Allow reallocation to different alignment
authorTim Diekmann <tim.diekmann@3dvision.de>
Tue, 18 Aug 2020 20:39:33 +0000 (22:39 +0200)
committerTim Diekmann <tim.diekmann@3dvision.de>
Wed, 19 Aug 2020 04:46:47 +0000 (06:46 +0200)
library/alloc/src/alloc.rs
library/alloc/src/raw_vec.rs
library/core/src/alloc/mod.rs
library/std/src/alloc.rs
src/test/ui/realloc-16687.rs

index fa5eb1823f1a0755c721abbed9a13b5e5e5a0b84..e325bb4b3efa80995d42c3a1508182ac531d43fd 100644 (file)
@@ -3,7 +3,7 @@
 #![stable(feature = "alloc_module", since = "1.28.0")]
 
 use core::intrinsics::{self, min_align_of_val, size_of_val};
-use core::ptr::{NonNull, Unique};
+use core::ptr::{self, NonNull, Unique};
 
 #[stable(feature = "alloc_module", since = "1.28.0")]
 #[doc(inline)]
@@ -180,36 +180,45 @@ fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>,
     unsafe fn grow_impl(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
         zeroed: bool,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size >= layout.size(),
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        match layout.size() {
-            // SAFETY: the caller must ensure that the `new_size` does not overflow.
-            // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-            0 => unsafe {
-                let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
-                self.alloc_impl(new_layout, zeroed)
-            },
+        match old_layout.size() {
+            0 => self.alloc_impl(new_layout, zeroed),
 
             // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
             // as required by safety conditions. Other conditions must be upheld by the caller
-            old_size => unsafe {
-                // `realloc` probably checks for `new_size >= size` or something similar.
-                intrinsics::assume(new_size >= layout.size());
+            old_size if old_layout.align() == new_layout.align() => unsafe {
+                let new_size = new_layout.size();
+
+                // `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
+                intrinsics::assume(new_size >= old_layout.size());
 
-                let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
+                let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 if zeroed {
                     raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
                 }
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
+            // both the old and new memory allocation are valid for reads and writes for `old_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            old_size => unsafe {
+                let new_ptr = self.alloc_impl(new_layout, zeroed)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
@@ -239,52 +248,64 @@ unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, false) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
     }
 
     #[inline]
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, true) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
     }
 
     #[inline]
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size <= layout.size(),
-            "`new_size` must be smaller than or equal to `layout.size()`"
+            new_layout.size() <= old_layout.size(),
+            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
         );
 
-        match new_size {
+        match new_layout.size() {
             // SAFETY: conditions must be upheld by the caller
             0 => unsafe {
-                self.dealloc(ptr, layout);
-                Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
+                self.dealloc(ptr, old_layout);
+                Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
             },
 
             // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
-            new_size => unsafe {
-                // `realloc` probably checks for `new_size <= size` or something similar.
-                intrinsics::assume(new_size <= layout.size());
+            new_size if old_layout.align() == new_layout.align() => unsafe {
+                // `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
+                intrinsics::assume(new_size <= old_layout.size());
 
-                let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
+                let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
+            // both the old and new memory allocation are valid for reads and writes for `new_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            new_size => unsafe {
+                let new_ptr = self.alloc(new_layout)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
@@ -297,7 +318,7 @@ unsafe fn shrink(
 unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
     let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
     match Global.alloc(layout) {
-        Ok(ptr) => ptr.as_non_null_ptr().as_ptr(),
+        Ok(ptr) => ptr.as_mut_ptr(),
         Err(_) => handle_alloc_error(layout),
     }
 }
index 247b636c808acf5e15a2b16acf212860872c73b3..e00cb361c0019f8400365ee51115f6fef44a98aa 100644 (file)
@@ -465,8 +465,9 @@ fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> {
         let new_size = amount * mem::size_of::<T>();
 
         let ptr = unsafe {
-            self.alloc.shrink(ptr, layout, new_size).map_err(|_| TryReserveError::AllocError {
-                layout: Layout::from_size_align_unchecked(new_size, layout.align()),
+            let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
+            self.alloc.shrink(ptr, layout, new_layout).map_err(|_| TryReserveError::AllocError {
+                layout: new_layout,
                 non_exhaustive: (),
             })?
         };
@@ -493,14 +494,12 @@ fn finish_grow<A>(
     alloc_guard(new_layout.size())?;
 
     let memory = if let Some((ptr, old_layout)) = current_memory {
-        debug_assert_eq!(old_layout.align(), new_layout.align());
-        unsafe { alloc.grow(ptr, old_layout, new_layout.size()) }
+        unsafe { alloc.grow(ptr, old_layout, new_layout) }
     } else {
         alloc.alloc(new_layout)
-    }
-    .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?;
+    };
 
-    Ok(memory)
+    memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })
 }
 
 unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec<T, A> {
index ad4f8bf1397c2de696b727dc4ab927204ac3cd4d..c1fda2fce641fead20d505a0e293dfb4d10d9b01 100644 (file)
@@ -147,9 +147,8 @@ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
     /// Attempts to extend the memory block.
     ///
     /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
-    /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s
-    /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the
-    /// allocation referenced by `ptr` to fit the new layout.
+    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
+    /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
     ///
     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
     /// transferred to this allocator. The memory may or may not have been freed, and should be
@@ -163,11 +162,9 @@ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
     ///
     /// # Safety
     ///
-    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
-    /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.),
-    /// * `new_size` must be greater than or equal to `layout.size()`, and
-    /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
-    ///   (i.e., the rounded value must be less than or equal to `usize::MAX`).
+    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
+    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
+    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
     ///
     /// [*currently allocated*]: #currently-allocated-memory
     /// [*fit*]: #memory-fitting
@@ -188,28 +185,24 @@ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
-        let size = layout.size();
         debug_assert!(
-            new_size >= size,
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        // SAFETY: the caller must ensure that the `new_size` does not overflow.
-        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
         let new_ptr = self.alloc(new_layout)?;
 
-        // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
-        // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
-        // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
-        // `copy_nonoverlapping` is safe.
-        // The safety contract for `dealloc` must be upheld by the caller.
+        // SAFETY: because `new_layout.size()` must be greater than or equal to
+        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
+        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
+        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
+        // safe. The safety contract for `dealloc` must be upheld by the caller.
         unsafe {
-            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
-            self.dealloc(ptr, layout);
+            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
+            self.dealloc(ptr, old_layout);
         }
 
         Ok(new_ptr)
@@ -220,21 +213,19 @@ unsafe fn grow(
     ///
     /// The memory block will contain the following contents after a successful call to
     /// `grow_zeroed`:
-    ///   * Bytes `0..layout.size()` are preserved from the original allocation.
-    ///   * Bytes `layout.size()..old_size` will either be preserved or zeroed, depending on the
-    ///     allocator implementation. `old_size` refers to the size of the memory block prior to
-    ///     the `grow_zeroed` call, which may be larger than the size that was originally requested
-    ///     when it was allocated.
+    ///   * Bytes `0..old_layout.size()` are preserved from the original allocation.
+    ///   * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on
+    ///     the allocator implementation. `old_size` refers to the size of the memory block prior
+    ///     to the `grow_zeroed` call, which may be larger than the size that was originally
+    ///     requested when it was allocated.
     ///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
-    ///     block returned by the `grow` call.
+    ///     block returned by the `grow_zeroed` call.
     ///
     /// # Safety
     ///
-    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
-    /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.),
-    /// * `new_size` must be greater than or equal to `layout.size()`, and
-    /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
-    ///   (i.e., the rounded value must be less than or equal to `usize::MAX`).
+    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
+    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
+    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
     ///
     /// [*currently allocated*]: #currently-allocated-memory
     /// [*fit*]: #memory-fitting
@@ -255,28 +246,24 @@ unsafe fn grow(
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
-        let size = layout.size();
         debug_assert!(
-            new_size >= size,
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        // SAFETY: the caller must ensure that the `new_size` does not overflow.
-        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
         let new_ptr = self.alloc_zeroed(new_layout)?;
 
-        // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
-        // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
-        // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
-        // `copy_nonoverlapping` is safe.
-        // The safety contract for `dealloc` must be upheld by the caller.
+        // SAFETY: because `new_layout.size()` must be greater than or equal to
+        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
+        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
+        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
+        // safe. The safety contract for `dealloc` must be upheld by the caller.
         unsafe {
-            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
-            self.dealloc(ptr, layout);
+            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
+            self.dealloc(ptr, old_layout);
         }
 
         Ok(new_ptr)
@@ -285,9 +272,8 @@ unsafe fn grow_zeroed(
     /// Attempts to shrink the memory block.
     ///
     /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
-    /// memory. The pointer is suitable for holding data described by a new layout with `layout`’s
-    /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the
-    /// allocation referenced by `ptr` to fit the new layout.
+    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
+    /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
     ///
     /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
     /// transferred to this allocator. The memory may or may not have been freed, and should be
@@ -301,9 +287,9 @@ unsafe fn grow_zeroed(
     ///
     /// # Safety
     ///
-    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
-    /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), and
-    /// * `new_size` must be smaller than or equal to `layout.size()`.
+    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
+    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
+    /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
     ///
     /// [*currently allocated*]: #currently-allocated-memory
     /// [*fit*]: #memory-fitting
@@ -324,28 +310,24 @@ unsafe fn grow_zeroed(
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
-        let size = layout.size();
         debug_assert!(
-            new_size <= size,
-            "`new_size` must be smaller than or equal to `layout.size()`"
+            new_layout.size() <= old_layout.size(),
+            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
         );
 
-        // SAFETY: the caller must ensure that the `new_size` does not overflow.
-        // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-        let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
         let new_ptr = self.alloc(new_layout)?;
 
-        // SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new
-        // memory allocation are valid for reads and writes for `new_size` bytes. Also, because the
-        // old allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
-        // `copy_nonoverlapping` is safe.
-        // The safety contract for `dealloc` must be upheld by the caller.
+        // SAFETY: because `new_layout.size()` must be lower than or equal to
+        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
+        // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
+        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
+        // safe. The safety contract for `dealloc` must be upheld by the caller.
         unsafe {
-            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
-            self.dealloc(ptr, layout);
+            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size());
+            self.dealloc(ptr, old_layout);
         }
 
         Ok(new_ptr)
@@ -385,32 +367,32 @@ unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).grow(ptr, layout, new_size) }
+        unsafe { (**self).grow(ptr, old_layout, new_layout) }
     }
 
     #[inline]
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).grow_zeroed(ptr, layout, new_size) }
+        unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
     }
 
     #[inline]
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).shrink(ptr, layout, new_size) }
+        unsafe { (**self).shrink(ptr, old_layout, new_layout) }
     }
 }
index b4009c86419d5148e3fb6f79a17e6d43b1d692e0..64d8edf33bd3bcf393e9d94e028f3ea07849a8e0 100644 (file)
@@ -154,36 +154,45 @@ fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>,
     unsafe fn grow_impl(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
         zeroed: bool,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size >= layout.size(),
-            "`new_size` must be greater than or equal to `layout.size()`"
+            new_layout.size() >= old_layout.size(),
+            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
         );
 
-        match layout.size() {
-            // SAFETY: the caller must ensure that the `new_size` does not overflow.
-            // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
-            0 => unsafe {
-                let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
-                self.alloc_impl(new_layout, zeroed)
-            },
+        match old_layout.size() {
+            0 => self.alloc_impl(new_layout, zeroed),
 
             // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
             // as required by safety conditions. Other conditions must be upheld by the caller
-            old_size => unsafe {
-                // `realloc` probably checks for `new_size >= size` or something similar.
-                intrinsics::assume(new_size >= layout.size());
+            old_size if old_layout.align() == new_layout.align() => unsafe {
+                let new_size = new_layout.size();
+
+                // `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
+                intrinsics::assume(new_size >= old_layout.size());
 
-                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
+                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 if zeroed {
                     raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
                 }
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
+            // both the old and new memory allocation are valid for reads and writes for `old_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            old_size => unsafe {
+                let new_ptr = self.alloc_impl(new_layout, zeroed)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
@@ -215,52 +224,64 @@ unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
     unsafe fn grow(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, false) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
     }
 
     #[inline]
     unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         // SAFETY: all conditions must be upheld by the caller
-        unsafe { self.grow_impl(ptr, layout, new_size, true) }
+        unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
     }
 
     #[inline]
     unsafe fn shrink(
         &mut self,
         ptr: NonNull<u8>,
-        layout: Layout,
-        new_size: usize,
+        old_layout: Layout,
+        new_layout: Layout,
     ) -> Result<NonNull<[u8]>, AllocErr> {
         debug_assert!(
-            new_size <= layout.size(),
-            "`new_size` must be smaller than or equal to `layout.size()`"
+            new_layout.size() <= old_layout.size(),
+            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
         );
 
-        match new_size {
+        match new_layout.size() {
             // SAFETY: conditions must be upheld by the caller
             0 => unsafe {
-                self.dealloc(ptr, layout);
-                Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
+                self.dealloc(ptr, old_layout);
+                Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
             },
 
             // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
-            new_size => unsafe {
-                // `realloc` probably checks for `new_size <= size` or something similar.
-                intrinsics::assume(new_size <= layout.size());
+            new_size if old_layout.align() == new_layout.align() => unsafe {
+                // `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
+                intrinsics::assume(new_size <= old_layout.size());
 
-                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
+                let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
                 let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
                 Ok(NonNull::slice_from_raw_parts(ptr, new_size))
             },
+
+            // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
+            // both the old and new memory allocation are valid for reads and writes for `new_size`
+            // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+            // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+            // for `dealloc` must be upheld by the caller.
+            new_size => unsafe {
+                let new_ptr = self.alloc(new_layout)?;
+                ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
+                self.dealloc(ptr, old_layout);
+                Ok(new_ptr)
+            },
         }
     }
 }
index bdcd47a7260d30f22664b0ba720117a9bea2110b..2e07fdcbe830cd8074492028bd8ed67da90f1549 100644 (file)
@@ -48,7 +48,7 @@ unsafe fn allocate(layout: Layout) -> *mut u8 {
             println!("allocate({:?}) = {:?}", layout, ptr);
         }
 
-        ptr.as_non_null_ptr().as_ptr()
+        ptr.as_mut_ptr()
     }
 
     unsafe fn deallocate(ptr: *mut u8, layout: Layout) {
@@ -65,23 +65,17 @@ unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 {
         }
 
         let memory = if new.size() > old.size() {
-            Global.grow(
-                NonNull::new_unchecked(ptr),
-                old,
-                new.size(),
-            )
+            Global.grow(NonNull::new_unchecked(ptr), old, new)
         } else {
-            Global.shrink(NonNull::new_unchecked(ptr), old, new.size())
+            Global.shrink(NonNull::new_unchecked(ptr), old, new)
         };
 
-        let ptr = memory.unwrap_or_else(|_| {
-            handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align()))
-        });
+        let ptr = memory.unwrap_or_else(|_| handle_alloc_error(new));
 
         if PRINT {
             println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ptr);
         }
-        ptr.as_non_null_ptr().as_ptr()
+        ptr.as_mut_ptr()
     }
 
     fn idx_to_size(i: usize) -> usize {