diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 5c6f6b22aae06..6f5b7910505ca 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -14,7 +14,7 @@ use core::ops::Drop; use core::ptr::{self, NonNull, Unique}; use core::slice; -use alloc::{Alloc, Layout, Global, oom}; +use alloc::{Alloc, Layout, Global, oom, Excess}; use alloc::CollectionAllocErr; use alloc::CollectionAllocErr::*; use boxed::Box; @@ -92,17 +92,17 @@ impl RawVec { alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow()); // handles ZSTs and `cap = 0` alike - let ptr = if alloc_size == 0 { - NonNull::::dangling().as_opaque() + let (ptr, cap) = if alloc_size == 0 { + (NonNull::::dangling().as_opaque(), cap) } else { let align = mem::align_of::(); let result = if zeroed { - a.alloc_zeroed(Layout::from_size_align(alloc_size, align).unwrap()) + a.alloc_zeroed_excess(Layout::from_size_align(alloc_size, align).unwrap()) } else { - a.alloc(Layout::from_size_align(alloc_size, align).unwrap()) + a.alloc_excess(Layout::from_size_align(alloc_size, align).unwrap()) }; match result { - Ok(ptr) => ptr, + Ok(Excess(ptr, alloc_size)) => (ptr, alloc_size / elem_size), Err(_) => oom(), } }; @@ -407,16 +407,17 @@ impl RawVec { alloc_guard(new_layout.size())?; - let res = match self.current_layout() { + let Excess(ptr, alloc_size) = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + self.a.realloc_excess(NonNull::from(self.ptr).as_opaque(), + layout, new_layout.size()) } - None => self.a.alloc(new_layout), - }; + None => self.a.alloc_excess(new_layout), + }?; - self.ptr = res?.cast().into(); - self.cap = new_cap; + self.ptr = ptr.cast().into(); + self.cap = alloc_size / mem::size_of::(); Ok(()) } @@ -485,16 +486,16 @@ impl RawVec { // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size())?; - let res = match self.current_layout() { + let Excess(ptr, alloc_size) = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + self.a.realloc_excess(NonNull::from(self.ptr).as_opaque(), + layout, new_layout.size()) } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; + None => self.a.alloc_excess(new_layout), + }?; + self.ptr = ptr.cast().into(); + self.cap = alloc_size / mem::size_of::(); Ok(()) } @@ -604,11 +605,11 @@ impl RawVec { let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow()); - match self.a.grow_in_place( + match self.a.grow_in_place_excess( NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(), ) { - Ok(_) => { - self.cap = new_cap; + Ok(Excess(_, alloc_size)) => { + self.cap = alloc_size / mem::size_of::(); true } Err(_) => { @@ -666,10 +667,13 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(NonNull::from(self.ptr).as_opaque(), + match self.a.realloc_excess(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { - Ok(p) => self.ptr = p.cast().into(), + Ok(Excess(ptr, alloc_size)) => { + self.ptr = ptr.cast().into(); + self.cap = alloc_size / mem::size_of::(); + }, Err(_) => oom(), } } diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 690cbcb559bbf..54ae9a9514170 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -574,6 +574,10 @@ impl Vec { /// It will drop down as close as possible to the length but the allocator /// may still inform the vector that there is space for a few more elements. /// + /// Note: `shrink_to_fit` is a non-binding request. Whether the `Vec` size + /// will be shrunk at all, and if so by how much depends on the particular + /// allocator being used. + /// /// # Examples /// /// ``` @@ -598,6 +602,10 @@ impl Vec { /// Panics if the current capacity is smaller than the supplied /// minimum capacity. /// + /// Note: `shrink_to` is a non-binding request. Whether the `Vec` size + /// will be shrunk at all, and if so by how much depends on the particular + /// allocator being used. + /// /// # Examples /// /// ``` diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 674c4fb57c7f0..23c7ef4657346 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -794,6 +794,28 @@ pub unsafe trait Alloc { .map(|p| Excess(p, usable_size.1)) } + /// Behaves like `alloc`, but also ensures that the contents are set to zero + /// before being returned. For some `layout` inputs, like arrays, this may + /// include extra storage usable for additional data. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc_zeroed_excess(&mut self, layout: Layout) -> Result { + let usable_size = self.usable_size(&layout); + self.alloc_zeroed(layout).map(|p| Excess(p, usable_size.1)) + } + /// Attempts to extend the allocation referenced by `ptr` to fit `new_size`. /// /// If this returns `Ok`, then the allocator has asserted that the @@ -845,6 +867,60 @@ pub unsafe trait Alloc { } } + /// Attempts to extend the allocation referenced by `ptr` to fit `new_size`. + /// For some `layout` inputs, like arrays, this may include extra storage + /// usable for additional data. + /// + /// If this returns `Ok`, then the allocator has asserted that the + /// memory block referenced by `ptr` now fits `new_size`, and thus can + /// be used to carry data of a layout of that size and same alignment as + /// `layout`. (The allocator is allowed to + /// expend effort to accomplish this, such as extending the memory block to + /// include successor blocks, or virtual memory tricks.) + /// + /// Regardless of what this method returns, ownership of the + /// memory block referenced by `ptr` has not been transferred, and + /// the contents of the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above); note the + /// `new_size` argument need not fit it, + /// + /// * `new_size` must not be less than `layout.size()`, + /// + /// # Errors + /// + /// Returns `Err(CannotReallocInPlace)` when the allocator is + /// unable to assert that the memory block referenced by `ptr` + /// could fit `layout`. + /// + /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// method; clients are expected either to be able to recover from + /// `grow_in_place` failures without aborting, or to fall back on + /// another reallocation method before resorting to an abort. + unsafe fn grow_in_place_excess(&mut self, + ptr: NonNull, + layout: Layout, + new_size: usize) -> Result { + let _ = ptr; // this default implementation doesn't care about the actual address. + debug_assert!(new_size >= layout.size); + let (_l, u) = self.usable_size(&layout); + // _l <= layout.size() [guaranteed by usable_size()] + // layout.size() <= new_layout.size() [required by this method] + if new_size <= u { + return Ok(Excess(ptr, u)); + } else { + return Err(CannotReallocInPlace); + } + } + + /// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`. /// /// If this returns `Ok`, then the allocator has asserted that the @@ -900,6 +976,62 @@ pub unsafe trait Alloc { } } + /// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`. + /// For some `layout` inputs, like arrays, this may include extra storage + /// usable for additional data. + /// + /// If this returns `Ok`, then the allocator has asserted that the + /// memory block referenced by `ptr` now fits `new_size`, and + /// thus can only be used to carry data of that smaller + /// layout. (The allocator is allowed to take advantage of this, + /// carving off portions of the block for reuse elsewhere.) The + /// truncated contents of the block within the smaller layout are + /// unaltered, and ownership of block has not been transferred. + /// + /// If this returns `Err`, then the memory block is considered to + /// still represent the original (larger) `layout`. None of the + /// block has been carved off for reuse elsewhere, ownership of + /// the memory block has not been transferred, and the contents of + /// the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above); note the + /// `new_size` argument need not fit it, + /// + /// * `new_size` must not be greater than `layout.size()` + /// (and must be greater than zero), + /// + /// # Errors + /// + /// Returns `Err(CannotReallocInPlace)` when the allocator is + /// unable to assert that the memory block referenced by `ptr` + /// could fit `layout`. + /// + /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// method; clients are expected either to be able to recover from + /// `shrink_in_place` failures without aborting, or to fall back + /// on another reallocation method before resorting to an abort. + unsafe fn shrink_in_place_excess(&mut self, + ptr: NonNull, + layout: Layout, + new_size: usize) -> Result { + let _ = ptr; // this default implementation doesn't care about the actual address. + debug_assert!(new_size <= layout.size); + let (l, u) = self.usable_size(&layout); + // layout.size() <= _u [guaranteed by usable_size()] + // new_layout.size() <= layout.size() [required by this method] + if l <= new_size { + return Ok(Excess(ptr, u)); + } else { + return Err(CannotReallocInPlace); + } + } // == COMMON USAGE PATTERNS == // alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array