about summary refs log tree commit diff
path: root/library/core/src/alloc
diff options
context:
space:
mode:
authorTim Diekmann <tim.diekmann@3dvision.de>2020-07-28 12:41:18 +0200
committerTim Diekmann <tim.diekmann@3dvision.de>2020-07-28 12:41:18 +0200
commit076ef66ba2f647a627806f376c23b332fb04d3ff (patch)
treee0b01276965228cd9fb1cb2577ed5beb1b9e1910 /library/core/src/alloc
parent1f5d69daccd1f04e42886d9aaf513f2691132d17 (diff)
downloadrust-076ef66ba2f647a627806f376c23b332fb04d3ff.tar.gz
rust-076ef66ba2f647a627806f376c23b332fb04d3ff.zip
Remove in-place allocation and revert to separate methods for zeroed allocations
Fix docs

Diffstat (limited to 'library/core/src/alloc')
-rw-r--r--library/core/src/alloc/mod.rs336
1 files changed, 162 insertions, 174 deletions
diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs
index be4e051b1ca..f5632b578ae 100644
--- a/library/core/src/alloc/mod.rs
+++ b/library/core/src/alloc/mod.rs
@@ -29,66 +29,6 @@ impl fmt::Display for AllocErr {
     }
 }
 
-/// A desired initial state for allocated memory.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-#[unstable(feature = "allocator_api", issue = "32838")]
-pub enum AllocInit {
-    /// The contents of the new memory are uninitialized.
-    Uninitialized,
-    /// The new memory is guaranteed to be zeroed.
-    Zeroed,
-}
-
-impl AllocInit {
-    /// Initialize the specified memory block.
-    ///
-    /// This behaves like calling [`AllocInit::init_offset(memory, 0)`][off].
-    ///
-    /// [off]: AllocInit::init_offset
-    ///
-    /// # Safety
-    ///
-    /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes.
-    ///
-    /// [valid]: ../../core/ptr/index.html#safety
-    #[inline]
-    #[unstable(feature = "allocator_api", issue = "32838")]
-    pub unsafe fn init(self, memory: MemoryBlock) {
-        // SAFETY: the safety contract for `init_offset` must be
-        // upheld by the caller.
-        unsafe { self.init_offset(memory, 0) }
-    }
-
-    /// Initialize the memory block like specified by `init` at the specified `offset`.
-    ///
-    /// This is a no-op for [`AllocInit::Uninitialized`][] and writes zeroes for
-    /// [`AllocInit::Zeroed`][] at `ptr + offset` until `ptr + layout.size()`.
-    ///
-    /// # Safety
-    ///
-    /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes.
-    /// * `offset` must be smaller than or equal to `memory.size`
-    ///
-    /// [valid]: ../../core/ptr/index.html#safety
-    #[inline]
-    #[unstable(feature = "allocator_api", issue = "32838")]
-    pub unsafe fn init_offset(self, memory: MemoryBlock, offset: usize) {
-        debug_assert!(
-            offset <= memory.size,
-            "`offset` must be smaller than or equal to `memory.size`"
-        );
-        match self {
-            AllocInit::Uninitialized => (),
-            AllocInit::Zeroed => {
-                // SAFETY: the caller must guarantee that `offset` is smaller than or equal to `memory.size`,
-                // so the memory from `memory.ptr + offset` of length `memory.size - offset`
-                // is guaranteed to be contaned in `memory` and thus valid for writes.
-                unsafe { memory.ptr.as_ptr().add(offset).write_bytes(0, memory.size - offset) }
-            }
-        }
-    }
-}
-
 /// Represents a block of allocated memory returned by an allocator.
 #[derive(Debug, Copy, Clone)]
 #[unstable(feature = "allocator_api", issue = "32838")]
@@ -97,24 +37,6 @@ pub struct MemoryBlock {
     pub size: usize,
 }
 
-/// A placement constraint when growing or shrinking an existing allocation.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-#[unstable(feature = "allocator_api", issue = "32838")]
-pub enum ReallocPlacement {
-    /// The allocator is allowed to move the allocation to a different memory address.
-    // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
-    //                          allocator" and link it at "valid location".
-    ///
-    /// If the allocation _does_ move, it's the responsibility of the allocator
-    /// to also move the data from the previous location to the new location.
-    MayMove,
-    /// The address of the new memory must not change.
-    ///
-    /// If the allocation would have to be moved to a new location to fit, the
-    /// reallocation request will fail.
-    InPlace,
-}
-
 /// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
 /// data described via [`Layout`][].
 ///
@@ -177,10 +99,25 @@ pub unsafe trait AllocRef {
     ///
     /// On success, returns a [`MemoryBlock`][] meeting the size and alignment guarantees of `layout`.
     ///
-    /// The returned block may have a larger size than specified by `layout.size()` and is
-    /// initialized as specified by [`init`], all the way up to the returned size of the block.
+    /// The returned block may have a larger size than specified by `layout.size()`, and may or may
+    /// not have its contents initialized.
     ///
-    /// [`init`]: AllocInit
+    /// # Errors
+    ///
+    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
+    /// allocator's size or alignment constraints.
+    ///
+    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
+    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
+    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
+    ///
+    /// Clients wishing to abort computation in response to an allocation error are encouraged to
+    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
+    ///
+    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
+    fn alloc(&mut self, layout: Layout) -> Result<MemoryBlock, AllocErr>;
+
+    /// Behaves like `alloc`, but also ensures that the contents are set to zero before being returned.
     ///
     /// # Errors
     ///
@@ -195,7 +132,12 @@ pub unsafe trait AllocRef {
     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
     ///
     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
-    fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;
+    fn alloc_zeroed(&mut self, layout: Layout) -> Result<MemoryBlock, AllocErr> {
+        let memory = self.alloc(layout)?;
+        // SAFETY: `alloc` returns a valid memory block
+        unsafe { memory.ptr.as_ptr().write_bytes(0, memory.size) }
+        Ok(memory)
+    }
 
     /// Deallocates the memory referenced by `ptr`.
     ///
@@ -213,31 +155,82 @@ pub unsafe trait AllocRef {
     /// Returns a new [`MemoryBlock`][] 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. If the [`placement`] is
-    /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
-    ///
-    /// If [`MayMove`] is used then ownership of the memory block referenced by `ptr`
-    /// is transferred to this allocator. The memory may or may not be freed, and should be
-    /// considered unusable (unless of course it is transferred back to the caller again via the
-    /// return value of this method).
-    ///
+    /// allocation referenced by `ptr` to fit the new layout.
+    ///~
     /// If this method returns `Err`, then ownership of the memory block has not been transferred to
     /// this allocator, and the contents of the memory block are unaltered.
     ///
+    /// # 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.),
+    // We can't require that `new_size` is strictly greater than `memory.size` because of ZSTs.
+    // An alternative would be
+    // * `new_size must be strictly greater than `memory.size` or both are zero
+    /// * `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`).
+    ///
+    /// [*currently allocated*]: #currently-allocated-memory
+    /// [*fit*]: #memory-fitting
+    ///
+    /// # Errors
+    ///
+    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
+    /// constraints of the allocator, or if growing otherwise fails.
+    ///
+    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
+    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
+    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
+    ///
+    /// Clients wishing to abort computation in response to an allocation error are encouraged to
+    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
+    ///
+    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
+    unsafe fn grow(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+    ) -> Result<MemoryBlock, AllocErr> {
+        let size = layout.size();
+        debug_assert!(
+            new_size >= size,
+            "`new_size` must be greater than or equal to `layout.size()`"
+        );
+
+        if new_size == size {
+            return Ok(MemoryBlock { ptr, size });
+        }
+
+        let new_layout =
+            // 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.
+            // The caller must ensure that `new_size` is greater than or equal to zero. If it's equal
+            // to zero, it's catched beforehand.
+            unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+        let new_memory = 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_memory`. 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_memory.ptr.as_ptr(), size);
+            self.dealloc(ptr, layout);
+            Ok(new_memory)
+        }
+    }
+
+    /// Behaves like `grow`, but also ensures that the new contents are set to zero before being
+    /// returned.
+    ///
     /// The memory block will contain the following contents after a successful call to `grow`:
     ///   * Bytes `0..layout.size()` are preserved from the original allocation.
-    ///   * Bytes `layout.size()..old_size` will either be preserved or initialized according to
-    ///     [`init`], depending on the allocator implementation. `old_size` refers to the size of
-    ///     the `MemoryBlock` prior to the `grow` call, which may be larger than the size
-    ///     that was originally requested when it was allocated.
-    ///   * Bytes `old_size..new_size` are initialized according to [`init`]. `new_size` refers to
+    ///   * Bytes `layout.size()..new_size` are zeroed. `new_size` refers to
     ///     the size of the `MemoryBlock` returned by the `grow` call.
     ///
-    /// [`InPlace`]: ReallocPlacement::InPlace
-    /// [`MayMove`]: ReallocPlacement::MayMove
-    /// [`placement`]: ReallocPlacement
-    /// [`init`]: AllocInit
-    ///
     /// # Safety
     ///
     /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
@@ -265,45 +258,39 @@ pub unsafe trait AllocRef {
     /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
     ///
     /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
-    unsafe fn grow(
+    unsafe fn grow_zeroed(
         &mut self,
         ptr: NonNull<u8>,
         layout: Layout,
         new_size: usize,
-        placement: ReallocPlacement,
-        init: AllocInit,
     ) -> Result<MemoryBlock, AllocErr> {
-        match placement {
-            ReallocPlacement::InPlace => Err(AllocErr),
-            ReallocPlacement::MayMove => {
-                let size = layout.size();
-                debug_assert!(
-                    new_size >= size,
-                    "`new_size` must be greater than or equal to `layout.size()`"
-                );
+        let size = layout.size();
+        debug_assert!(
+            new_size >= size,
+            "`new_size` must be greater than or equal to `layout.size()`"
+        );
 
-                if new_size == size {
-                    return Ok(MemoryBlock { ptr, size });
-                }
+        if new_size == size {
+            return Ok(MemoryBlock { ptr, size });
+        }
 
-                let new_layout =
-                    // 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.
-                    // The caller must ensure that `new_size` is greater than zero.
-                    unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
-                let new_memory = self.alloc(new_layout, init)?;
+        let new_layout =
+            // 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.
+            // The caller must ensure that `new_size` is greater than or equal to zero. If it's equal
+            // to zero, it's catched beforehand.
+            unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+        let new_memory = 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_memory`. 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_memory.ptr.as_ptr(), size);
-                    self.dealloc(ptr, layout);
-                    Ok(new_memory)
-                }
-            }
+        // 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_memory`. 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_memory.ptr.as_ptr(), size);
+            self.dealloc(ptr, layout);
+            Ok(new_memory)
         }
     }
 
@@ -312,8 +299,7 @@ pub unsafe trait AllocRef {
     /// Returns a new [`MemoryBlock`][] 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. If the [`placement`] is
-    /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
+    /// 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
@@ -323,11 +309,6 @@ pub unsafe trait AllocRef {
     /// If this method returns `Err`, then ownership of the memory block has not been transferred to
     /// this allocator, and the contents of the memory block are unaltered.
     ///
-    /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
-    ///
-    /// [`InPlace`]: ReallocPlacement::InPlace
-    /// [`placement`]: ReallocPlacement
-    ///
     /// # Safety
     ///
     /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
@@ -358,39 +339,33 @@ pub unsafe trait AllocRef {
         ptr: NonNull<u8>,
         layout: Layout,
         new_size: usize,
-        placement: ReallocPlacement,
     ) -> Result<MemoryBlock, AllocErr> {
-        match placement {
-            ReallocPlacement::InPlace => Err(AllocErr),
-            ReallocPlacement::MayMove => {
-                let size = layout.size();
-                debug_assert!(
-                    new_size <= size,
-                    "`new_size` must be smaller than or equal to `layout.size()`"
-                );
+        let size = layout.size();
+        debug_assert!(
+            new_size <= size,
+            "`new_size` must be smaller than or equal to `layout.size()`"
+        );
 
-                if new_size == size {
-                    return Ok(MemoryBlock { ptr, size });
-                }
+        if new_size == size {
+            return Ok(MemoryBlock { ptr, size });
+        }
 
-                let new_layout =
-                // 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.
-                // The caller must ensure that `new_size` is greater than zero.
-                    unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
-                let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
+        let new_layout =
+        // 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.
+        // The caller must ensure that `new_size` is greater than zero.
+            unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+        let new_memory = 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_memory`. 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_memory.ptr.as_ptr(), new_size);
-                    self.dealloc(ptr, layout);
-                    Ok(new_memory)
-                }
-            }
+        // 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_memory`. 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_memory.ptr.as_ptr(), new_size);
+            self.dealloc(ptr, layout);
+            Ok(new_memory)
         }
     }
 
@@ -409,8 +384,13 @@ where
     A: AllocRef + ?Sized,
 {
     #[inline]
-    fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
-        (**self).alloc(layout, init)
+    fn alloc(&mut self, layout: Layout) -> Result<MemoryBlock, AllocErr> {
+        (**self).alloc(layout)
+    }
+
+    #[inline]
+    fn alloc_zeroed(&mut self, layout: Layout) -> Result<MemoryBlock, AllocErr> {
+        (**self).alloc_zeroed(layout)
     }
 
     #[inline]
@@ -425,11 +405,20 @@ where
         ptr: NonNull<u8>,
         layout: Layout,
         new_size: usize,
-        placement: ReallocPlacement,
-        init: AllocInit,
     ) -> Result<MemoryBlock, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).grow(ptr, layout, new_size, placement, init) }
+        unsafe { (**self).grow(ptr, layout, new_size) }
+    }
+
+    #[inline]
+    unsafe fn grow_zeroed(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+    ) -> Result<MemoryBlock, AllocErr> {
+        // SAFETY: the safety contract must be upheld by the caller
+        unsafe { (**self).grow_zeroed(ptr, layout, new_size) }
     }
 
     #[inline]
@@ -438,9 +427,8 @@ where
         ptr: NonNull<u8>,
         layout: Layout,
         new_size: usize,
-        placement: ReallocPlacement,
     ) -> Result<MemoryBlock, AllocErr> {
         // SAFETY: the safety contract must be upheld by the caller
-        unsafe { (**self).shrink(ptr, layout, new_size, placement) }
+        unsafe { (**self).shrink(ptr, layout, new_size) }
     }
 }