diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2017-08-14 19:36:13 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2017-08-14 19:36:13 -0700 |
| commit | 1413253a41de87ce7da73f0733aa3f433b1f5a3b (patch) | |
| tree | 111cba46a53aaaa0733b6b8ba19aece25b6f8533 /src/liballoc | |
| parent | b045c201b2086073f43d76290b9cb2a5a8e16f89 (diff) | |
| parent | 56fe3b2ad0055bb28325f412395577e2b842719a (diff) | |
| download | rust-1413253a41de87ce7da73f0733aa3f433b1f5a3b.tar.gz rust-1413253a41de87ce7da73f0733aa3f433b1f5a3b.zip | |
Merge remote-tracking branch 'origin/master' into gen
Diffstat (limited to 'src/liballoc')
| -rw-r--r-- | src/liballoc/allocator.rs | 12 | ||||
| -rw-r--r-- | src/liballoc/btree/node.rs | 4 | ||||
| -rw-r--r-- | src/liballoc/fmt.rs | 99 | ||||
| -rw-r--r-- | src/liballoc/heap.rs | 2 | ||||
| -rw-r--r-- | src/liballoc/raw_vec.rs | 203 | ||||
| -rw-r--r-- | src/liballoc/string.rs | 2 | ||||
| -rw-r--r-- | src/liballoc/vec.rs | 2 |
7 files changed, 203 insertions, 121 deletions
diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index 42111301a9f..2b3df15f716 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -40,7 +40,7 @@ fn size_align<T>() -> (usize, usize) { /// /// (Note however that layouts are *not* required to have positive /// size, even though many allocators require that all memory -/// requeusts have positive size. A caller to the `Alloc::alloc` +/// requests have positive size. A caller to the `Alloc::alloc` /// method must either ensure that conditions like this are met, or /// use specific allocators with looser requirements.) #[derive(Clone, Debug, PartialEq, Eq)] @@ -240,7 +240,7 @@ impl Layout { /// /// Returns `Some((k, offset))`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded witnin the concatenated record + /// start of the `next` embedded within the concatenated record /// (assuming that the record itself starts at offset 0). /// /// On arithmetic overflow, returns `None`. @@ -297,7 +297,7 @@ impl Layout { /// /// Returns `(k, offset)`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded witnin the concatenated record + /// start of the `next` embedded within the concatenated record /// (assuming that the record itself starts at offset 0). /// /// (The `offset` is always the same as `self.size()`; we use this @@ -354,15 +354,19 @@ pub enum AllocErr { } impl AllocErr { + #[inline] pub fn invalid_input(details: &'static str) -> Self { AllocErr::Unsupported { details: details } } + #[inline] pub fn is_memory_exhausted(&self) -> bool { if let AllocErr::Exhausted { .. } = *self { true } else { false } } + #[inline] pub fn is_request_unsupported(&self) -> bool { if let AllocErr::Unsupported { .. } = *self { true } else { false } } + #[inline] pub fn description(&self) -> &str { match *self { AllocErr::Exhausted { .. } => "allocator memory exhausted", @@ -544,7 +548,7 @@ pub unsafe trait Alloc { /// practice this means implementors should eschew allocating, /// especially from `self` (directly or indirectly). /// - /// Implementions of the allocation and reallocation methods + /// Implementations of the allocation and reallocation methods /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from /// panicking (or aborting) in the event of memory exhaustion; /// instead they should return an appropriate error from the diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 8cea6c482c3..0e61905131f 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -132,7 +132,7 @@ impl<K, V> InternalNode<K, V> { /// An owned pointer to a node. This basically is either `Box<LeafNode<K, V>>` or /// `Box<InternalNode<K, V>>`. However, it contains no information as to which of the two types -/// of nodes is acutally behind the box, and, partially due to this lack of information, has no +/// of nodes is actually behind the box, and, partially due to this lack of information, has no /// destructor. struct BoxedNode<K, V> { ptr: Unique<LeafNode<K, V>> @@ -264,7 +264,7 @@ impl<K, V> Root<K, V> { // correct variance. /// A reference to a node. /// -/// This type has a number of paramaters that controls how it acts: +/// This type has a number of parameters that controls how it acts: /// - `BorrowType`: This can be `Immut<'a>` or `Mut<'a>` for some `'a` or `Owned`. /// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, /// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 4847b21c0b3..480fb4b9eaa 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -10,16 +10,16 @@ //! Utilities for formatting and printing `String`s //! -//! This module contains the runtime support for the `format!` syntax extension. +//! This module contains the runtime support for the [`format!`] syntax extension. //! This macro is implemented in the compiler to emit calls to this module in //! order to format arguments at runtime into strings. //! //! # Usage //! -//! The `format!` macro is intended to be familiar to those coming from C's -//! printf/fprintf functions or Python's `str.format` function. +//! The [`format!`] macro is intended to be familiar to those coming from C's +//! `printf`/`fprintf` functions or Python's `str.format` function. //! -//! Some examples of the `format!` extension are: +//! Some examples of the [`format!`] extension are: //! //! ``` //! format!("Hello"); // => "Hello" @@ -67,7 +67,7 @@ //! ## Named parameters //! //! Rust itself does not have a Python-like equivalent of named parameters to a -//! function, but the `format!` macro is a syntax extension which allows it to +//! function, but the [`format!`] macro is a syntax extension which allows it to //! leverage named parameters. Named parameters are listed at the end of the //! argument list and have the syntax: //! @@ -75,7 +75,7 @@ //! identifier '=' expression //! ``` //! -//! For example, the following `format!` expressions all use named argument: +//! For example, the following [`format!`] expressions all use named argument: //! //! ``` //! format!("{argument}", argument = "test"); // => "test" @@ -102,30 +102,30 @@ //! //! If this syntax is used, then the number of characters to print precedes the //! actual object being formatted, and the number of characters must have the -//! type `usize`. +//! type [`usize`]. //! //! ## Formatting traits //! //! When requesting that an argument be formatted with a particular type, you //! are actually requesting that an argument ascribes to a particular trait. -//! This allows multiple actual types to be formatted via `{:x}` (like `i8` as -//! well as `isize`). The current mapping of types to traits is: +//! This allows multiple actual types to be formatted via `{:x}` (like [`i8`] as +//! well as [`isize`]). The current mapping of types to traits is: //! -//! * *nothing* ⇒ [`Display`](trait.Display.html) -//! * `?` ⇒ [`Debug`](trait.Debug.html) +//! * *nothing* ⇒ [`Display`] +//! * `?` ⇒ [`Debug`] //! * `o` ⇒ [`Octal`](trait.Octal.html) //! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) //! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) //! * `p` ⇒ [`Pointer`](trait.Pointer.html) -//! * `b` ⇒ [`Binary`](trait.Binary.html) +//! * `b` ⇒ [`Binary`] //! * `e` ⇒ [`LowerExp`](trait.LowerExp.html) //! * `E` ⇒ [`UpperExp`](trait.UpperExp.html) //! //! What this means is that any type of argument which implements the -//! `fmt::Binary` trait can then be formatted with `{:b}`. Implementations +//! [`fmt::Binary`][`Binary`] trait can then be formatted with `{:b}`. Implementations //! are provided for these traits for a number of primitive types by the //! standard library as well. If no format is specified (as in `{}` or `{:6}`), -//! then the format trait used is the `Display` trait. +//! then the format trait used is the [`Display`] trait. //! //! When implementing a format trait for your own type, you will have to //! implement a method of the signature: @@ -144,15 +144,15 @@ //! should emit output into the `f.buf` stream. It is up to each format trait //! implementation to correctly adhere to the requested formatting parameters. //! The values of these parameters will be listed in the fields of the -//! `Formatter` struct. In order to help with this, the `Formatter` struct also +//! [`Formatter`] struct. In order to help with this, the [`Formatter`] struct also //! provides some helper methods. //! -//! Additionally, the return value of this function is `fmt::Result` which is a -//! type alias of `Result<(), std::fmt::Error>`. Formatting implementations -//! should ensure that they propagate errors from the `Formatter` (e.g., when -//! calling `write!`) however, they should never return errors spuriously. That +//! Additionally, the return value of this function is [`fmt::Result`] which is a +//! type alias of [`Result`]`<(), `[`std::fmt::Error`]`>`. Formatting implementations +//! should ensure that they propagate errors from the [`Formatter`][`Formatter`] (e.g., when +//! calling [`write!`]) however, they should never return errors spuriously. That //! is, a formatting implementation must and may only return an error if the -//! passed-in `Formatter` returns an error. This is because, contrary to what +//! passed-in [`Formatter`] returns an error. This is because, contrary to what //! the function signature might suggest, string formatting is an infallible //! operation. This function only returns a result because writing to the //! underlying stream might fail and it must provide a way to propagate the fact @@ -209,12 +209,12 @@ //! //! These two formatting traits have distinct purposes: //! -//! - `fmt::Display` implementations assert that the type can be faithfully +//! - [`fmt::Display`][`Display`] implementations assert that the type can be faithfully //! represented as a UTF-8 string at all times. It is **not** expected that //! all types implement the `Display` trait. -//! - `fmt::Debug` implementations should be implemented for **all** public types. +//! - [`fmt::Debug`][`Debug`] implementations should be implemented for **all** public types. //! Output will typically represent the internal state as faithfully as possible. -//! The purpose of the `Debug` trait is to facilitate debugging Rust code. In +//! The purpose of the [`Debug`] trait is to facilitate debugging Rust code. In //! most cases, using `#[derive(Debug)]` is sufficient and recommended. //! //! Some examples of the output from both traits: @@ -227,7 +227,7 @@ //! //! ## Related macros //! -//! There are a number of related macros in the `format!` family. The ones that +//! There are a number of related macros in the [`format!`] family. The ones that //! are currently implemented are: //! //! ```ignore (only-for-syntax-highlight) @@ -241,11 +241,11 @@ //! //! ### `write!` //! -//! This and `writeln` are two macros which are used to emit the format string +//! This and [`writeln!`] are two macros which are used to emit the format string //! to a specified stream. This is used to prevent intermediate allocations of //! format strings and instead directly write the output. Under the hood, this -//! function is actually invoking the `write_fmt` function defined on the -//! `std::io::Write` trait. Example usage is: +//! function is actually invoking the [`write_fmt`] function defined on the +//! [`std::io::Write`] trait. Example usage is: //! //! ``` //! # #![allow(unused_must_use)] @@ -256,7 +256,7 @@ //! //! ### `print!` //! -//! This and `println` emit their output to stdout. Similarly to the `write!` +//! This and [`println!`] emit their output to stdout. Similarly to the [`write!`] //! macro, the goal of these macros is to avoid intermediate allocations when //! printing output. Example usage is: //! @@ -288,8 +288,8 @@ //! my_fmt_fn(format_args!(", or a {} too", "function")); //! ``` //! -//! The result of the `format_args!` macro is a value of type `fmt::Arguments`. -//! This structure can then be passed to the `write` and `format` functions +//! The result of the [`format_args!`] macro is a value of type [`fmt::Arguments`]. +//! This structure can then be passed to the [`write`] and [`format`] functions //! inside this module in order to process the format string. //! The goal of this macro is to even further prevent intermediate allocations //! when dealing formatting strings. @@ -357,7 +357,7 @@ //! * `-` - Currently not used //! * `#` - This flag is indicates that the "alternate" form of printing should //! be used. The alternate forms are: -//! * `#?` - pretty-print the `Debug` formatting +//! * `#?` - pretty-print the [`Debug`] formatting //! * `#x` - precedes the argument with a `0x` //! * `#X` - precedes the argument with a `0x` //! * `#b` - precedes the argument with a `0b` @@ -384,9 +384,9 @@ //! the `0` flag is specified for numerics, then the implicit fill character is //! `0`. //! -//! The value for the width can also be provided as a `usize` in the list of +//! The value for the width can also be provided as a [`usize`] in the list of //! parameters by using the dollar syntax indicating that the second argument is -//! a `usize` specifying the width, for example: +//! a [`usize`] specifying the width, for example: //! //! ``` //! // All of these print "Hello x !" @@ -474,6 +474,29 @@ //! The literal characters `{` and `}` may be included in a string by preceding //! them with the same character. For example, the `{` character is escaped with //! `{{` and the `}` character is escaped with `}}`. +//! +//! [`format!`]: ../../macro.format.html +//! [`usize`]: ../../std/primitive.usize.html +//! [`isize`]: ../../std/primitive.isize.html +//! [`i8`]: ../../std/primitive.i8.html +//! [`Display`]: trait.Display.html +//! [`Binary`]: trait.Binary.html +//! [`fmt::Result`]: type.Result.html +//! [`Result`]: ../../std/result/enum.Result.html +//! [`std::fmt::Error`]: struct.Error.html +//! [`Formatter`]: struct.Formatter.html +//! [`write!`]: ../../std/macro.write.html +//! [`Debug`]: trait.Debug.html +//! [`format!`]: ../../std/macro.format.html +//! [`writeln!`]: ../../std/macro.writeln.html +//! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt +//! [`std::io::Write`]: ../../std/io/trait.Write.html +//! [`println!`]: ../../std/macro.println.html +//! [`write!`]: ../../std/macro.write.html +//! [`format_args!`]: ../../std/macro.format_args.html +//! [`fmt::Arguments`]: struct.Arguments.html +//! [`write`]: fn.write.html +//! [`format`]: fn.format.html #![stable(feature = "rust1", since = "1.0.0")] @@ -498,10 +521,10 @@ pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; use string; -/// The `format` function takes an `Arguments` struct and returns the resulting +/// The `format` function takes an [`Arguments`] struct and returns the resulting /// formatted string. /// -/// The `Arguments` instance can be created with the `format_args!` macro. +/// The [`Arguments`] instance can be created with the [`format_args!`] macro. /// /// # Examples /// @@ -514,7 +537,7 @@ use string; /// assert_eq!(s, "Hello, world!"); /// ``` /// -/// Please note that using [`format!`][format!] might be preferrable. +/// Please note that using [`format!`] might be preferrable. /// Example: /// /// ``` @@ -522,7 +545,9 @@ use string; /// assert_eq!(s, "Hello, world!"); /// ``` /// -/// [format!]: ../macro.format.html +/// [`Arguments`]: struct.Arguments.html +/// [`format_args!`]: ../../std/macro.format_args.html +/// [`format!`]: ../../std/macro.format.html #[stable(feature = "rust1", since = "1.0.0")] pub fn format(args: Arguments) -> string::String { let capacity = args.estimated_capacity(); diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 1d959ac5bf6..820f2d958d9 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -28,6 +28,7 @@ pub mod __core { extern "Rust" { #[allocator] fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; + #[cold] fn __rust_oom(err: *const u8) -> !; fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); fn __rust_usable_size(layout: *const u8, @@ -81,6 +82,7 @@ unsafe impl Alloc for Heap { } #[inline] + #[cold] fn oom(&mut self, err: AllocErr) -> ! { unsafe { __rust_oom(&err as *const AllocErr as *const u8) diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index ca55831220d..6090fc3942a 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use allocator::{Alloc, Layout}; -use core::ptr::{self, Unique}; +use core::cmp; use core::mem; +use core::ops::Drop; +use core::ptr::{self, Unique}; use core::slice; -use heap::Heap; +use heap::{Alloc, Layout, Heap}; use super::boxed::Box; -use core::ops::Drop; -use core::cmp; /// A low-level utility for more ergonomically allocating, reallocating, and deallocating /// a buffer of memory on the heap without having to worry about all the corner cases @@ -222,6 +221,20 @@ impl<T, A: Alloc> RawVec<T, A> { &mut self.a } + fn current_layout(&self) -> Option<Layout> { + if self.cap == 0 { + None + } else { + // We have an allocated chunk of memory, so we can bypass runtime + // checks to get our current layout. + unsafe { + let align = mem::align_of::<T>(); + let size = mem::size_of::<T>() * self.cap; + Some(Layout::from_size_align_unchecked(size, align)) + } + } + } + /// Doubles the size of the type's backing allocation. This is common enough /// to want to do that it's easiest to just have a dedicated method. Slightly /// more efficient logic can be provided for this than the general case. @@ -280,27 +293,40 @@ impl<T, A: Alloc> RawVec<T, A> { // 0, getting to here necessarily means the RawVec is overfull. assert!(elem_size != 0, "capacity overflow"); - let (new_cap, ptr_res) = if self.cap == 0 { - // skip to 4 because tiny Vec's are dumb; but not if that would cause overflow - let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; - let ptr_res = self.a.alloc_array::<T>(new_cap); - (new_cap, ptr_res) - } else { - // Since we guarantee that we never allocate more than isize::MAX bytes, - // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow - let new_cap = 2 * self.cap; - let new_alloc_size = new_cap * elem_size; - alloc_guard(new_alloc_size); - let ptr_res = self.a.realloc_array(self.ptr, self.cap, new_cap); - (new_cap, ptr_res) - }; - - // If allocate or reallocate fail, we'll get `null` back - let uniq = match ptr_res { - Err(err) => self.a.oom(err), - Ok(uniq) => uniq, + let (new_cap, uniq) = match self.current_layout() { + Some(cur) => { + // Since we guarantee that we never allocate more than + // isize::MAX bytes, `elem_size * self.cap <= isize::MAX` as + // a precondition, so this can't overflow. Additionally the + // alignment will never be too large as to "not be + // satisfiable", so `Layout::from_size_align` will always + // return `Some`. + // + // tl;dr; we bypass runtime checks due to dynamic assertions + // in this module, allowing us to use + // `from_size_align_unchecked`. + let new_cap = 2 * self.cap; + let new_size = new_cap * elem_size; + let new_layout = Layout::from_size_align_unchecked(new_size, cur.align()); + alloc_guard(new_size); + let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, + cur, + new_layout); + match ptr_res { + Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), + Err(e) => self.a.oom(e), + } + } + None => { + // skip to 4 because tiny Vec's are dumb; but not if that + // would cause overflow + let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; + match self.a.alloc_array::<T>(new_cap) { + Ok(ptr) => (new_cap, ptr), + Err(e) => self.a.oom(e), + } + } }; - self.ptr = uniq; self.cap = new_cap; } @@ -323,21 +349,27 @@ impl<T, A: Alloc> RawVec<T, A> { pub fn double_in_place(&mut self) -> bool { unsafe { let elem_size = mem::size_of::<T>(); + let old_layout = match self.current_layout() { + Some(layout) => layout, + None => return false, // nothing to double + }; // since we set the capacity to usize::MAX when elem_size is // 0, getting to here necessarily means the RawVec is overfull. assert!(elem_size != 0, "capacity overflow"); - // Since we guarantee that we never allocate more than isize::MAX bytes, - // `elem_size * self.cap <= isize::MAX` as a precondition, so this can't overflow + // Since we guarantee that we never allocate more than isize::MAX + // bytes, `elem_size * self.cap <= isize::MAX` as a precondition, so + // this can't overflow. + // + // Similarly like with `double` above we can go straight to + // `Layout::from_size_align_unchecked` as we know this won't + // overflow and the alignment is sufficiently small. let new_cap = 2 * self.cap; - let new_alloc_size = new_cap * elem_size; - - alloc_guard(new_alloc_size); - + let new_size = new_cap * elem_size; + alloc_guard(new_size); let ptr = self.ptr() as *mut _; - let old_layout = Layout::new::<T>().repeat(self.cap).unwrap().0; - let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0; + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); match self.a.grow_in_place(ptr, old_layout, new_layout) { Ok(_) => { // We can't directly divide `size`. @@ -373,8 +405,6 @@ impl<T, A: Alloc> RawVec<T, A> { /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { unsafe { - let elem_size = mem::size_of::<T>(); - // NOTE: we don't early branch on ZSTs here because we want this // to actually catch "asking for more than usize::MAX" in that case. // If we make it past the first branch then we are guaranteed to @@ -388,21 +418,22 @@ impl<T, A: Alloc> RawVec<T, A> { // Nothing we can really do about these checks :( let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow"); - let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow"); - alloc_guard(new_alloc_size); - - let result = if self.cap == 0 { - self.a.alloc_array::<T>(new_cap) - } else { - self.a.realloc_array(self.ptr, self.cap, new_cap) + let new_layout = match Layout::array::<T>(new_cap) { + Some(layout) => layout, + None => panic!("capacity overflow"), }; - - // If allocate or reallocate fail, we'll get `null` back - let uniq = match result { - Err(err) => self.a.oom(err), - Ok(uniq) => uniq, + alloc_guard(new_layout.size()); + let res = match self.current_layout() { + Some(layout) => { + let old_ptr = self.ptr.as_ptr() as *mut u8; + self.a.realloc(old_ptr, layout, new_layout) + } + None => self.a.alloc(new_layout), + }; + let uniq = match res { + Ok(ptr) => Unique::new_unchecked(ptr as *mut T), + Err(e) => self.a.oom(e), }; - self.ptr = uniq; self.cap = new_cap; } @@ -411,17 +442,14 @@ impl<T, A: Alloc> RawVec<T, A> { /// Calculates the buffer's new size given that it'll hold `used_cap + /// needed_extra_cap` elements. This logic is used in amortized reserve methods. /// Returns `(new_capacity, new_alloc_size)`. - fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> (usize, usize) { - let elem_size = mem::size_of::<T>(); + fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> usize { // Nothing we can really do about these checks :( let required_cap = used_cap.checked_add(needed_extra_cap) .expect("capacity overflow"); // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`. let double_cap = self.cap * 2; // `double_cap` guarantees exponential growth. - let new_cap = cmp::max(double_cap, required_cap); - let new_alloc_size = new_cap.checked_mul(elem_size).expect("capacity overflow"); - (new_cap, new_alloc_size) + cmp::max(double_cap, required_cap) } /// Ensures that the buffer contains at least enough space to hold @@ -489,21 +517,25 @@ impl<T, A: Alloc> RawVec<T, A> { return; } - let (new_cap, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap); - // FIXME: may crash and burn on over-reserve - alloc_guard(new_alloc_size); + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap); - let result = if self.cap == 0 { - self.a.alloc_array::<T>(new_cap) - } else { - self.a.realloc_array(self.ptr, self.cap, new_cap) + let new_layout = match Layout::array::<T>(new_cap) { + Some(layout) => layout, + None => panic!("capacity overflow"), }; - - let uniq = match result { - Err(err) => self.a.oom(err), - Ok(uniq) => uniq, + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size()); + let res = match self.current_layout() { + Some(layout) => { + let old_ptr = self.ptr.as_ptr() as *mut u8; + self.a.realloc(old_ptr, layout, new_layout) + } + None => self.a.alloc(new_layout), + }; + let uniq = match res { + Ok(ptr) => Unique::new_unchecked(ptr as *mut T), + Err(e) => self.a.oom(e), }; - self.ptr = uniq; self.cap = new_cap; } @@ -536,21 +568,24 @@ impl<T, A: Alloc> RawVec<T, A> { // Don't actually need any more capacity. If the current `cap` is 0, we can't // reallocate in place. // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap || self.cap == 0 { + let old_layout = match self.current_layout() { + Some(layout) => layout, + None => return false, + }; + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { return false; } - let (new_cap, new_alloc_size) = self.amortized_new_size(used_cap, needed_extra_cap); - // FIXME: may crash and burn on over-reserve - alloc_guard(new_alloc_size); + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap); // Here, `cap < used_cap + needed_extra_cap <= new_cap` // (regardless of whether `self.cap - used_cap` wrapped). // Therefore we can safely call grow_in_place. let ptr = self.ptr() as *mut _; - let old_layout = Layout::new::<T>().repeat(self.cap).unwrap().0; let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0; + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size()); match self.a.grow_in_place(ptr, old_layout, new_layout) { Ok(_) => { self.cap = new_cap; @@ -599,9 +634,24 @@ impl<T, A: Alloc> RawVec<T, A> { } } else if self.cap != amount { unsafe { - match self.a.realloc_array(self.ptr, self.cap, amount) { + // We know here that our `amount` is greater than zero. This + // implies, via the assert above, that capacity is also greater + // than zero, which means that we've got a current layout that + // "fits" + // + // We also know that `self.cap` is greater than `amount`, and + // consequently we don't need runtime checks for creating either + // layout + let old_size = elem_size * self.cap; + let new_size = elem_size * amount; + let align = mem::align_of::<T>(); + let old_layout = Layout::from_size_align_unchecked(old_size, align); + let new_layout = Layout::from_size_align_unchecked(new_size, align); + match self.a.realloc(self.ptr.as_ptr() as *mut u8, + old_layout, + new_layout) { + Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), Err(err) => self.a.oom(err), - Ok(uniq) => self.ptr = uniq, } } self.cap = amount; @@ -631,10 +681,11 @@ impl<T, A: Alloc> RawVec<T, A> { /// Frees the memory owned by the RawVec *without* trying to Drop its contents. pub unsafe fn dealloc_buffer(&mut self) { let elem_size = mem::size_of::<T>(); - if elem_size != 0 && self.cap != 0 { - let ptr = self.ptr() as *mut u8; - let layout = Layout::new::<T>().repeat(self.cap).unwrap().0; - self.a.dealloc(ptr, layout); + if elem_size != 0 { + if let Some(layout) = self.current_layout() { + let ptr = self.ptr() as *mut u8; + self.a.dealloc(ptr, layout); + } } } } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 322b137e99f..3ed5d2df1ab 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -653,7 +653,7 @@ impl String { /// * `capacity` needs to be the correct value. /// /// Violating these may cause problems like corrupting the allocator's - /// internal datastructures. + /// internal data structures. /// /// The ownership of `ptr` is effectively transferred to the /// `String` which may then deallocate, reallocate or change the diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 160c0ba2ab0..5f68e59289d 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -374,7 +374,7 @@ impl<T> Vec<T> { /// * `capacity` needs to be the capacity that the pointer was allocated with. /// /// Violating these may cause problems like corrupting the allocator's - /// internal datastructures. For example it is **not** safe + /// internal data structures. For example it is **not** safe /// to build a `Vec<u8>` from a pointer to a C `char` array and a `size_t`. /// /// The ownership of `ptr` is effectively transferred to the |
