Revise alloc chapter to use the new alloc API

pull/123/head
Jason King 6 years ago
parent f1ff93b668
commit f5c1024978

@ -16,12 +16,10 @@ want to use `empty` because there's no real allocation to talk about but
So:
```rust,ignore
#![feature(alloc, heap_api)]
use std::mem;
impl<T> Vec<T> {
fn new() -> Self {
pub fn new() -> Self {
assert!(mem::size_of::<T>() != 0, "We're not ready to handle ZSTs");
Vec { ptr: Unique::empty(), len: 0, cap: 0 }
}
@ -37,11 +35,11 @@ that, we'll need to use the rest of the heap APIs. These basically allow us to
talk directly to Rust's allocator (jemalloc by default).
We'll also need a way to handle out-of-memory (OOM) conditions. The standard
library calls `std::alloc::oom()`, which in turn calls the the `oom` langitem.
By default this just aborts the program by executing an illegal cpu instruction.
The reason we abort and don't panic is because unwinding can cause allocations
to happen, and that seems like a bad thing to do when your allocator just came
back with "hey I don't have any more memory".
library calls `std::alloc::handle_alloc_error()`, which in turn calls the the
`oom` langitem. By default this just aborts the program by executing an illegal
cpu instruction. The reason we abort and don't panic is because unwinding can
cause allocations to happen, and that seems like a bad thing to do when your
allocator just came back with "hey I don't have any more memory".
Of course, this is a bit silly since most platforms don't actually run out of
memory in a conventional way. Your operating system will probably kill the
@ -157,7 +155,11 @@ such we will guard against this case explicitly.
Ok with all the nonsense out of the way, let's actually allocate some memory:
```rust,ignore
use std::alloc::oom;
#![feature(allocator_api, ptr_internals)]
use std::alloc::{Alloc, Global, Layout, handle_alloc_error};
use std::mem;
use std::ptr::{NonNull, Unique};
fn grow(&mut self) {
// this is all pretty delicate, so let's say it's all unsafe
@ -167,8 +169,11 @@ fn grow(&mut self) {
let elem_size = mem::size_of::<T>();
let (new_cap, ptr) = if self.cap == 0 {
let ptr = heap::allocate(elem_size, align);
(1, ptr)
let layout = Layout::from_size_align_unchecked(elem_size, align);
match Global.alloc(layout) {
Ok(ptr) => (1, ptr),
Err(_) => handle_alloc_error(layout),
}
} else {
// as an invariant, we can assume that `self.cap < isize::MAX`,
// so this doesn't need to be checked.
@ -182,26 +187,24 @@ fn grow(&mut self) {
// we need to make. We lose the ability to allocate e.g. 2/3rds of
// the address space with a single Vec of i16's on 32-bit though.
// Alas, poor Yorick -- I knew him, Horatio.
assert!(old_num_bytes <= (::std::isize::MAX as usize) / 2,
assert!(old_num_bytes <= (std::isize::MAX as usize) / 2,
"capacity overflow");
let layout = Layout::from_size_align_unchecked(old_num_bytes, align);
let new_num_bytes = old_num_bytes * 2;
let ptr = heap::reallocate(self.ptr.as_ptr() as *mut _,
old_num_bytes,
new_num_bytes,
align);
(new_cap, ptr)
match Global.realloc(NonNull::from(self.ptr).cast(), layout, new_num_bytes) {
Ok(ptr) => (new_cap, ptr),
Err(_) => handle_alloc_error(layout),
}
};
// If allocate or reallocate fail, we'll get `null` back
if ptr.is_null() { oom(); }
self.ptr = Unique::new(ptr as *mut _);
self.ptr = ptr.cast().into();
self.cap = new_cap;
}
}
```
Nothing particularly tricky here. Just computing sizes and alignments and doing
some careful multiplication checks.
some careful multiplication checks. An instance of `std::alloc::Layout` that describes
memory layout is required for the new Alloc API. The pointer types in `Global.alloc` and
`Global.realloc` are `NonNull<u8>`, so conversions and casts are needed.

Loading…
Cancel
Save