2014-04-02 20:06:55 -07:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 16:48:01 -08:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
2014-08-04 22:48:39 +12:00
|
|
|
|
2014-01-06 17:03:30 -08:00
|
|
|
//! The arena, a fast but limited type of allocator.
|
|
|
|
//!
|
|
|
|
//! Arenas are a type of allocator that destroy the objects within, all at
|
|
|
|
//! once, once the arena itself is destroyed. They do not support deallocation
|
|
|
|
//! of individual objects while the arena itself is still alive. The benefit
|
|
|
|
//! of an arena is very fast allocation; just a pointer bump.
|
2014-04-04 07:57:39 -04:00
|
|
|
//!
|
2016-09-20 09:52:38 +10:00
|
|
|
//! This crate implements `TypedArena`, a simple arena that can only hold
|
|
|
|
//! objects of a single type.
|
2012-08-21 15:32:30 -07:00
|
|
|
|
2015-08-09 14:15:05 -07:00
|
|
|
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
2015-05-15 16:04:01 -07:00
|
|
|
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
|
2015-11-03 14:03:22 +00:00
|
|
|
html_root_url = "https://doc.rust-lang.org/nightly/",
|
|
|
|
test(no_crate_inject, attr(deny(warnings))))]
|
2014-06-17 16:00:04 -07:00
|
|
|
|
2015-01-22 18:22:03 -08:00
|
|
|
#![feature(alloc)]
|
2015-06-09 11:18:03 -07:00
|
|
|
#![feature(core_intrinsics)]
|
2016-12-28 17:47:10 -05:00
|
|
|
#![feature(dropck_eyepatch)]
|
2018-03-08 22:24:10 +03:00
|
|
|
#![cfg_attr(stage0, feature(generic_param_attrs))]
|
2015-01-22 12:33:46 -08:00
|
|
|
#![cfg_attr(test, feature(test))]
|
2014-03-27 15:13:16 -07:00
|
|
|
|
2016-01-06 18:07:21 +01:00
|
|
|
#![allow(deprecated)]
|
|
|
|
|
2014-10-28 17:06:06 -04:00
|
|
|
extern crate alloc;
|
|
|
|
|
2013-12-30 17:32:53 -08:00
|
|
|
use std::cell::{Cell, RefCell};
|
2014-02-06 02:34:33 -05:00
|
|
|
use std::cmp;
|
2014-05-05 18:56:44 -07:00
|
|
|
use std::intrinsics;
|
2015-08-13 18:48:34 +02:00
|
|
|
use std::marker::{PhantomData, Send};
|
2014-05-05 18:56:44 -07:00
|
|
|
use std::mem;
|
2014-05-29 17:40:18 -07:00
|
|
|
use std::ptr;
|
2016-10-17 15:03:40 -06:00
|
|
|
use std::slice;
|
2015-09-08 15:53:46 -07:00
|
|
|
|
2015-08-12 05:52:37 +02:00
|
|
|
use alloc::raw_vec::RawVec;
|
2013-03-05 12:21:02 -08:00
|
|
|
|
2016-09-20 09:52:38 +10:00
|
|
|
/// An arena that can hold objects of only one type.
|
2014-01-06 17:03:30 -08:00
|
|
|
pub struct TypedArena<T> {
|
|
|
|
/// A pointer to the next object to be allocated.
|
2015-08-12 05:52:37 +02:00
|
|
|
ptr: Cell<*mut T>,
|
2014-01-06 17:03:30 -08:00
|
|
|
|
|
|
|
/// A pointer to the end of the allocated area. When this pointer is
|
|
|
|
/// reached, a new chunk is allocated.
|
2015-08-12 05:52:37 +02:00
|
|
|
end: Cell<*mut T>,
|
2014-01-06 17:03:30 -08:00
|
|
|
|
2016-09-20 09:52:38 +10:00
|
|
|
/// A vector of arena chunks.
|
2015-08-12 05:52:37 +02:00
|
|
|
chunks: RefCell<Vec<TypedArenaChunk<T>>>,
|
2015-01-21 20:02:52 +01:00
|
|
|
|
|
|
|
/// Marker indicating that dropping the arena causes its owned
|
|
|
|
/// instances of `T` to be dropped.
|
2015-08-13 18:48:34 +02:00
|
|
|
_own: PhantomData<T>,
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
2014-02-21 07:25:17 -05:00
|
|
|
struct TypedArenaChunk<T> {
|
2016-09-20 09:52:38 +10:00
|
|
|
/// The raw storage for the arena chunk.
|
2015-08-12 05:52:37 +02:00
|
|
|
storage: RawVec<T>,
|
2014-09-05 09:08:30 -04:00
|
|
|
}
|
|
|
|
|
2014-02-21 07:25:17 -05:00
|
|
|
impl<T> TypedArenaChunk<T> {
|
2014-01-06 17:03:30 -08:00
|
|
|
#[inline]
|
2015-08-12 05:52:37 +02:00
|
|
|
unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
|
2017-12-29 23:04:21 +01:00
|
|
|
TypedArenaChunk {
|
|
|
|
storage: RawVec::with_capacity(capacity),
|
|
|
|
}
|
2014-04-25 21:24:51 -04:00
|
|
|
}
|
|
|
|
|
2015-08-12 05:52:37 +02:00
|
|
|
/// Destroys this arena chunk.
|
2014-01-06 17:03:30 -08:00
|
|
|
#[inline]
|
2015-02-09 10:00:46 +03:00
|
|
|
unsafe fn destroy(&mut self, len: usize) {
|
2015-08-12 05:52:37 +02:00
|
|
|
// The branch on needs_drop() is an -O1 performance optimization.
|
|
|
|
// Without the branch, dropping TypedArena<u8> takes linear time.
|
2017-05-10 13:13:42 -04:00
|
|
|
if mem::needs_drop::<T>() {
|
2014-02-21 07:25:17 -05:00
|
|
|
let mut start = self.start();
|
2015-08-12 05:52:37 +02:00
|
|
|
// Destroy all allocated objects.
|
2015-01-26 15:46:12 -05:00
|
|
|
for _ in 0..len {
|
2015-08-12 05:52:37 +02:00
|
|
|
ptr::drop_in_place(start);
|
|
|
|
start = start.offset(1);
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a pointer to the first allocated object.
|
|
|
|
#[inline]
|
2015-08-12 05:52:37 +02:00
|
|
|
fn start(&self) -> *mut T {
|
|
|
|
self.storage.ptr()
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a pointer to the end of the allocated space.
|
|
|
|
#[inline]
|
2015-08-12 05:52:37 +02:00
|
|
|
fn end(&self) -> *mut T {
|
2014-01-06 17:03:30 -08:00
|
|
|
unsafe {
|
2015-08-12 05:52:37 +02:00
|
|
|
if mem::size_of::<T>() == 0 {
|
|
|
|
// A pointer as large as possible for zero-sized elements.
|
|
|
|
!0 as *mut T
|
|
|
|
} else {
|
|
|
|
self.start().offset(self.storage.cap() as isize)
|
|
|
|
}
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-12 05:52:37 +02:00
|
|
|
const PAGE: usize = 4096;
|
|
|
|
|
2014-01-06 17:03:30 -08:00
|
|
|
impl<T> TypedArena<T> {
|
2016-09-20 09:52:38 +10:00
|
|
|
/// Creates a new `TypedArena`.
|
2014-01-06 17:03:30 -08:00
|
|
|
#[inline]
|
|
|
|
pub fn new() -> TypedArena<T> {
|
2016-09-20 09:52:38 +10:00
|
|
|
TypedArena {
|
|
|
|
// We set both `ptr` and `end` to 0 so that the first call to
|
|
|
|
// alloc() will trigger a grow().
|
|
|
|
ptr: Cell::new(0 as *mut T),
|
|
|
|
end: Cell::new(0 as *mut T),
|
|
|
|
chunks: RefCell::new(vec![]),
|
|
|
|
_own: PhantomData,
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-04 22:48:39 +12:00
|
|
|
/// Allocates an object in the `TypedArena`, returning a reference to it.
|
2014-01-06 17:03:30 -08:00
|
|
|
#[inline]
|
2014-10-27 16:31:41 -04:00
|
|
|
pub fn alloc(&self, object: T) -> &mut T {
|
2014-06-10 00:41:44 -03:00
|
|
|
if self.ptr == self.end {
|
2016-10-17 15:03:40 -06:00
|
|
|
self.grow(1)
|
2014-06-10 00:41:44 -03:00
|
|
|
}
|
2014-01-06 17:03:30 -08:00
|
|
|
|
2015-09-08 00:36:29 +02:00
|
|
|
unsafe {
|
2015-08-12 05:52:37 +02:00
|
|
|
if mem::size_of::<T>() == 0 {
|
2017-12-29 23:04:21 +01:00
|
|
|
self.ptr
|
|
|
|
.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1)
|
|
|
|
as *mut T);
|
2017-05-04 14:48:58 -04:00
|
|
|
let ptr = mem::align_of::<T>() as *mut T;
|
2015-08-12 05:52:37 +02:00
|
|
|
// Don't drop the object. This `write` is equivalent to `forget`.
|
|
|
|
ptr::write(ptr, object);
|
|
|
|
&mut *ptr
|
|
|
|
} else {
|
|
|
|
let ptr = self.ptr.get();
|
|
|
|
// Advance the pointer.
|
|
|
|
self.ptr.set(self.ptr.get().offset(1));
|
|
|
|
// Write into uninitialized memory.
|
|
|
|
ptr::write(ptr, object);
|
|
|
|
&mut *ptr
|
|
|
|
}
|
2015-09-08 00:36:29 +02:00
|
|
|
}
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
2017-12-31 17:17:01 +01:00
|
|
|
/// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable
|
2016-10-17 15:03:40 -06:00
|
|
|
/// reference to it. Will panic if passed a zero-sized types.
|
2016-10-24 18:23:29 -06:00
|
|
|
///
|
|
|
|
/// Panics:
|
2017-12-31 17:17:01 +01:00
|
|
|
///
|
2016-10-24 18:23:29 -06:00
|
|
|
/// - Zero-sized types
|
|
|
|
/// - Zero-length slices
|
2016-10-17 15:03:40 -06:00
|
|
|
#[inline]
|
|
|
|
pub fn alloc_slice(&self, slice: &[T]) -> &mut [T]
|
2017-12-29 23:04:21 +01:00
|
|
|
where
|
|
|
|
T: Copy,
|
|
|
|
{
|
2016-10-17 15:03:40 -06:00
|
|
|
assert!(mem::size_of::<T>() != 0);
|
2016-10-24 18:23:29 -06:00
|
|
|
assert!(slice.len() != 0);
|
2016-10-17 15:03:40 -06:00
|
|
|
|
|
|
|
let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize;
|
|
|
|
let at_least_bytes = slice.len() * mem::size_of::<T>();
|
|
|
|
if available_capacity_bytes < at_least_bytes {
|
|
|
|
self.grow(slice.len());
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let start_ptr = self.ptr.get();
|
|
|
|
let arena_slice = slice::from_raw_parts_mut(start_ptr, slice.len());
|
|
|
|
self.ptr.set(start_ptr.offset(arena_slice.len() as isize));
|
|
|
|
arena_slice.copy_from_slice(slice);
|
|
|
|
arena_slice
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-06 17:03:30 -08:00
|
|
|
/// Grows the arena.
|
|
|
|
#[inline(never)]
|
2015-08-12 05:52:37 +02:00
|
|
|
#[cold]
|
2016-10-17 15:03:40 -06:00
|
|
|
fn grow(&self, n: usize) {
|
2014-09-05 09:08:30 -04:00
|
|
|
unsafe {
|
2015-08-12 05:52:37 +02:00
|
|
|
let mut chunks = self.chunks.borrow_mut();
|
2016-10-17 15:03:40 -06:00
|
|
|
let (chunk, mut new_capacity);
|
2016-09-20 09:52:38 +10:00
|
|
|
if let Some(last_chunk) = chunks.last_mut() {
|
2016-10-17 15:03:40 -06:00
|
|
|
let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
|
|
|
|
let currently_used_cap = used_bytes / mem::size_of::<T>();
|
|
|
|
if last_chunk.storage.reserve_in_place(currently_used_cap, n) {
|
2016-09-20 09:52:38 +10:00
|
|
|
self.end.set(last_chunk.end());
|
|
|
|
return;
|
|
|
|
} else {
|
2016-12-23 20:47:47 -07:00
|
|
|
new_capacity = last_chunk.storage.cap();
|
2016-10-17 15:03:40 -06:00
|
|
|
loop {
|
2016-12-23 20:47:47 -07:00
|
|
|
new_capacity = new_capacity.checked_mul(2).unwrap();
|
2016-10-17 15:03:40 -06:00
|
|
|
if new_capacity >= currently_used_cap + n {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-20 09:52:38 +10:00
|
|
|
}
|
2015-08-12 05:52:37 +02:00
|
|
|
} else {
|
2016-09-22 09:47:14 +10:00
|
|
|
let elem_size = cmp::max(1, mem::size_of::<T>());
|
2016-10-17 15:03:40 -06:00
|
|
|
new_capacity = cmp::max(n, PAGE / elem_size);
|
2015-08-12 05:52:37 +02:00
|
|
|
}
|
2016-09-20 09:52:38 +10:00
|
|
|
chunk = TypedArenaChunk::<T>::new(new_capacity);
|
|
|
|
self.ptr.set(chunk.start());
|
|
|
|
self.end.set(chunk.end());
|
|
|
|
chunks.push(chunk);
|
2014-09-05 09:08:30 -04:00
|
|
|
}
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
2016-09-22 09:47:14 +10:00
|
|
|
|
2015-08-13 18:10:19 +02:00
|
|
|
/// Clears the arena. Deallocates all but the longest chunk which may be reused.
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
// Clear the last chunk, which is partially filled.
|
|
|
|
let mut chunks_borrow = self.chunks.borrow_mut();
|
2016-09-20 09:52:38 +10:00
|
|
|
if let Some(mut last_chunk) = chunks_borrow.pop() {
|
|
|
|
self.clear_last_chunk(&mut last_chunk);
|
|
|
|
// If `T` is ZST, code below has no effect.
|
|
|
|
for mut chunk in chunks_borrow.drain(..) {
|
|
|
|
let cap = chunk.storage.cap();
|
|
|
|
chunk.destroy(cap);
|
|
|
|
}
|
|
|
|
chunks_borrow.push(last_chunk);
|
2015-08-13 18:10:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
|
|
|
|
// chunks.
|
|
|
|
fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
|
|
|
|
// Determine how much was filled.
|
|
|
|
let start = last_chunk.start() as usize;
|
|
|
|
// We obtain the value of the pointer to the first uninitialized element.
|
|
|
|
let end = self.ptr.get() as usize;
|
|
|
|
// We then calculate the number of elements to be dropped in the last chunk,
|
|
|
|
// which is the filled area's length.
|
|
|
|
let diff = if mem::size_of::<T>() == 0 {
|
|
|
|
// `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
|
|
|
|
// the number of zero-sized values in the last and only chunk, just out of caution.
|
|
|
|
// Recall that `end` was incremented for each allocated value.
|
|
|
|
end - start
|
|
|
|
} else {
|
|
|
|
(end - start) / mem::size_of::<T>()
|
|
|
|
};
|
|
|
|
// Pass that to the `destroy` method.
|
|
|
|
unsafe {
|
|
|
|
last_chunk.destroy(diff);
|
|
|
|
}
|
|
|
|
// Reset the chunk.
|
|
|
|
self.ptr.set(last_chunk.start());
|
|
|
|
}
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
2016-12-28 17:47:10 -05:00
|
|
|
unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
|
2014-01-06 17:03:30 -08:00
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2014-09-05 09:08:30 -04:00
|
|
|
// Determine how much was filled.
|
2015-08-12 05:52:37 +02:00
|
|
|
let mut chunks_borrow = self.chunks.borrow_mut();
|
2016-09-20 09:52:38 +10:00
|
|
|
if let Some(mut last_chunk) = chunks_borrow.pop() {
|
|
|
|
// Drop the contents of the last chunk.
|
|
|
|
self.clear_last_chunk(&mut last_chunk);
|
|
|
|
// The last chunk will be dropped. Destroy all other chunks.
|
|
|
|
for chunk in chunks_borrow.iter_mut() {
|
|
|
|
let cap = chunk.storage.cap();
|
|
|
|
chunk.destroy(cap);
|
|
|
|
}
|
2015-08-12 05:52:37 +02:00
|
|
|
}
|
2015-08-13 18:10:19 +02:00
|
|
|
// RawVec handles deallocation of `last_chunk` and `self.chunks`.
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-13 18:48:34 +02:00
|
|
|
unsafe impl<T: Send> Send for TypedArena<T> {}
|
|
|
|
|
2016-12-22 10:12:56 -07:00
|
|
|
pub struct DroplessArena {
|
|
|
|
/// A pointer to the next object to be allocated.
|
|
|
|
ptr: Cell<*mut u8>,
|
|
|
|
|
|
|
|
/// A pointer to the end of the allocated area. When this pointer is
|
|
|
|
/// reached, a new chunk is allocated.
|
|
|
|
end: Cell<*mut u8>,
|
|
|
|
|
|
|
|
/// A vector of arena chunks.
|
|
|
|
chunks: RefCell<Vec<TypedArenaChunk<u8>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DroplessArena {
|
|
|
|
pub fn new() -> DroplessArena {
|
|
|
|
DroplessArena {
|
|
|
|
ptr: Cell::new(0 as *mut u8),
|
|
|
|
end: Cell::new(0 as *mut u8),
|
|
|
|
chunks: RefCell::new(vec![]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-31 08:38:27 -07:00
|
|
|
pub fn in_arena<T: ?Sized>(&self, ptr: *const T) -> bool {
|
|
|
|
let ptr = ptr as *const u8 as *mut u8;
|
|
|
|
for chunk in &*self.chunks.borrow() {
|
|
|
|
if chunk.start() <= ptr && ptr < chunk.end() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2016-12-22 10:12:56 -07:00
|
|
|
fn align_for<T>(&self) {
|
|
|
|
let align = mem::align_of::<T>();
|
|
|
|
let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1);
|
|
|
|
self.ptr.set(final_address as *mut u8);
|
|
|
|
assert!(self.ptr <= self.end);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(never)]
|
|
|
|
#[cold]
|
|
|
|
fn grow<T>(&self, n: usize) {
|
|
|
|
let needed_bytes = n * mem::size_of::<T>();
|
|
|
|
unsafe {
|
|
|
|
let mut chunks = self.chunks.borrow_mut();
|
|
|
|
let (chunk, mut new_capacity);
|
|
|
|
if let Some(last_chunk) = chunks.last_mut() {
|
|
|
|
let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
|
2017-12-29 23:04:21 +01:00
|
|
|
if last_chunk
|
|
|
|
.storage
|
|
|
|
.reserve_in_place(used_bytes, needed_bytes)
|
|
|
|
{
|
2016-12-22 10:12:56 -07:00
|
|
|
self.end.set(last_chunk.end());
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
new_capacity = last_chunk.storage.cap();
|
|
|
|
loop {
|
|
|
|
new_capacity = new_capacity.checked_mul(2).unwrap();
|
|
|
|
if new_capacity >= used_bytes + needed_bytes {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2016-12-31 08:38:27 -07:00
|
|
|
new_capacity = cmp::max(needed_bytes, PAGE);
|
2016-12-22 10:12:56 -07:00
|
|
|
}
|
|
|
|
chunk = TypedArenaChunk::<u8>::new(new_capacity);
|
|
|
|
self.ptr.set(chunk.start());
|
|
|
|
self.end.set(chunk.end());
|
|
|
|
chunks.push(chunk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn alloc<T>(&self, object: T) -> &mut T {
|
|
|
|
unsafe {
|
2017-05-10 13:13:42 -04:00
|
|
|
assert!(!mem::needs_drop::<T>());
|
2016-12-22 10:12:56 -07:00
|
|
|
assert!(mem::size_of::<T>() != 0);
|
|
|
|
|
|
|
|
self.align_for::<T>();
|
|
|
|
let future_end = intrinsics::arith_offset(self.ptr.get(), mem::size_of::<T>() as isize);
|
|
|
|
if (future_end as *mut u8) >= self.end.get() {
|
|
|
|
self.grow::<T>(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
let ptr = self.ptr.get();
|
|
|
|
// Set the pointer past ourselves
|
2017-12-29 23:04:21 +01:00
|
|
|
self.ptr.set(
|
|
|
|
intrinsics::arith_offset(self.ptr.get(), mem::size_of::<T>() as isize) as *mut u8,
|
|
|
|
);
|
2016-12-22 10:12:56 -07:00
|
|
|
// Write into uninitialized memory.
|
|
|
|
ptr::write(ptr as *mut T, object);
|
|
|
|
&mut *(ptr as *mut T)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable
|
|
|
|
/// reference to it. Will panic if passed a zero-sized type.
|
|
|
|
///
|
|
|
|
/// Panics:
|
2017-12-31 17:17:01 +01:00
|
|
|
///
|
2016-12-22 10:12:56 -07:00
|
|
|
/// - Zero-sized types
|
|
|
|
/// - Zero-length slices
|
|
|
|
#[inline]
|
|
|
|
pub fn alloc_slice<T>(&self, slice: &[T]) -> &mut [T]
|
2017-12-29 23:04:21 +01:00
|
|
|
where
|
|
|
|
T: Copy,
|
|
|
|
{
|
2017-05-10 13:13:42 -04:00
|
|
|
assert!(!mem::needs_drop::<T>());
|
2016-12-22 10:12:56 -07:00
|
|
|
assert!(mem::size_of::<T>() != 0);
|
|
|
|
assert!(slice.len() != 0);
|
|
|
|
self.align_for::<T>();
|
|
|
|
|
|
|
|
let future_end = unsafe {
|
|
|
|
intrinsics::arith_offset(self.ptr.get(), (slice.len() * mem::size_of::<T>()) as isize)
|
|
|
|
};
|
|
|
|
if (future_end as *mut u8) >= self.end.get() {
|
|
|
|
self.grow::<T>(slice.len());
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let arena_slice = slice::from_raw_parts_mut(self.ptr.get() as *mut T, slice.len());
|
|
|
|
self.ptr.set(intrinsics::arith_offset(
|
2017-12-29 23:04:21 +01:00
|
|
|
self.ptr.get(),
|
|
|
|
(slice.len() * mem::size_of::<T>()) as isize,
|
2016-12-22 10:12:56 -07:00
|
|
|
) as *mut u8);
|
|
|
|
arena_slice.copy_from_slice(slice);
|
|
|
|
arena_slice
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-06 17:03:30 -08:00
|
|
|
#[cfg(test)]
|
2014-02-14 09:49:11 +08:00
|
|
|
mod tests {
|
|
|
|
extern crate test;
|
2014-04-01 09:16:35 +08:00
|
|
|
use self::test::Bencher;
|
2016-03-07 15:42:29 -08:00
|
|
|
use super::TypedArena;
|
2016-01-05 11:02:58 +01:00
|
|
|
use std::cell::Cell;
|
2014-01-06 17:03:30 -08:00
|
|
|
|
2014-09-09 11:32:58 +02:00
|
|
|
#[allow(dead_code)]
|
2016-01-05 11:02:58 +01:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2014-01-06 17:03:30 -08:00
|
|
|
struct Point {
|
2015-02-09 10:00:46 +03:00
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
z: i32,
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
2016-09-20 09:52:38 +10:00
|
|
|
#[test]
|
|
|
|
pub fn test_unused() {
|
|
|
|
let arena: TypedArena<Point> = TypedArena::new();
|
|
|
|
assert!(arena.chunks.borrow().is_empty());
|
|
|
|
}
|
|
|
|
|
2015-02-02 14:10:36 +01:00
|
|
|
#[test]
|
|
|
|
fn test_arena_alloc_nested() {
|
2015-10-11 19:33:51 -07:00
|
|
|
struct Inner {
|
|
|
|
value: u8,
|
|
|
|
}
|
|
|
|
struct Outer<'a> {
|
|
|
|
inner: &'a Inner,
|
|
|
|
}
|
|
|
|
enum EI<'e> {
|
|
|
|
I(Inner),
|
|
|
|
O(Outer<'e>),
|
|
|
|
}
|
2015-02-02 14:10:36 +01:00
|
|
|
|
|
|
|
struct Wrap<'a>(TypedArena<EI<'a>>);
|
|
|
|
|
|
|
|
impl<'a> Wrap<'a> {
|
2015-10-11 19:33:51 -07:00
|
|
|
fn alloc_inner<F: Fn() -> Inner>(&self, f: F) -> &Inner {
|
2015-02-02 14:10:36 +01:00
|
|
|
let r: &EI = self.0.alloc(EI::I(f()));
|
|
|
|
if let &EI::I(ref i) = r {
|
|
|
|
i
|
|
|
|
} else {
|
|
|
|
panic!("mismatch");
|
|
|
|
}
|
|
|
|
}
|
2015-10-11 19:33:51 -07:00
|
|
|
fn alloc_outer<F: Fn() -> Outer<'a>>(&self, f: F) -> &Outer {
|
2015-02-02 14:10:36 +01:00
|
|
|
let r: &EI = self.0.alloc(EI::O(f()));
|
|
|
|
if let &EI::O(ref o) = r {
|
|
|
|
o
|
|
|
|
} else {
|
|
|
|
panic!("mismatch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let arena = Wrap(TypedArena::new());
|
|
|
|
|
2017-12-29 23:04:21 +01:00
|
|
|
let result = arena.alloc_outer(|| Outer {
|
|
|
|
inner: arena.alloc_inner(|| Inner { value: 10 }),
|
|
|
|
});
|
2015-02-02 14:10:36 +01:00
|
|
|
|
|
|
|
assert_eq!(result.inner.value, 10);
|
|
|
|
}
|
|
|
|
|
2014-01-06 17:03:30 -08:00
|
|
|
#[test]
|
2014-03-27 00:01:11 +01:00
|
|
|
pub fn test_copy() {
|
2014-01-06 17:03:30 -08:00
|
|
|
let arena = TypedArena::new();
|
2015-01-24 14:39:32 +00:00
|
|
|
for _ in 0..100000 {
|
2015-10-11 19:33:51 -07:00
|
|
|
arena.alloc(Point { x: 1, y: 2, z: 3 });
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-04-01 09:16:35 +08:00
|
|
|
pub fn bench_copy(b: &mut Bencher) {
|
2014-01-06 17:03:30 -08:00
|
|
|
let arena = TypedArena::new();
|
2015-10-11 19:33:51 -07:00
|
|
|
b.iter(|| arena.alloc(Point { x: 1, y: 2, z: 3 }))
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-04-01 09:16:35 +08:00
|
|
|
pub fn bench_copy_nonarena(b: &mut Bencher) {
|
|
|
|
b.iter(|| {
|
2015-12-03 08:06:16 +01:00
|
|
|
let _: Box<_> = Box::new(Point { x: 1, y: 2, z: 3 });
|
2014-01-06 17:03:30 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-09-09 11:32:58 +02:00
|
|
|
#[allow(dead_code)]
|
2014-03-27 00:01:11 +01:00
|
|
|
struct Noncopy {
|
2014-05-22 16:57:53 -07:00
|
|
|
string: String,
|
2015-02-09 10:00:46 +03:00
|
|
|
array: Vec<i32>,
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2014-03-27 00:01:11 +01:00
|
|
|
pub fn test_noncopy() {
|
2014-01-06 17:03:30 -08:00
|
|
|
let arena = TypedArena::new();
|
2015-01-24 14:39:32 +00:00
|
|
|
for _ in 0..100000 {
|
2014-03-27 00:01:11 +01:00
|
|
|
arena.alloc(Noncopy {
|
2014-05-25 03:17:19 -07:00
|
|
|
string: "hello world".to_string(),
|
2015-11-23 15:32:40 +13:00
|
|
|
array: vec![1, 2, 3, 4, 5],
|
2014-01-06 17:03:30 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-12 05:52:37 +02:00
|
|
|
#[test]
|
2015-08-13 18:10:19 +02:00
|
|
|
pub fn test_typed_arena_zero_sized() {
|
2015-08-12 05:52:37 +02:00
|
|
|
let arena = TypedArena::new();
|
|
|
|
for _ in 0..100000 {
|
|
|
|
arena.alloc(());
|
|
|
|
}
|
|
|
|
}
|
2015-08-13 18:10:19 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn test_typed_arena_clear() {
|
|
|
|
let mut arena = TypedArena::new();
|
|
|
|
for _ in 0..10 {
|
|
|
|
arena.clear();
|
|
|
|
for _ in 0..10000 {
|
2015-12-03 08:06:16 +01:00
|
|
|
arena.alloc(Point { x: 1, y: 2, z: 3 });
|
2015-08-13 18:10:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-05 11:02:58 +01:00
|
|
|
// Drop tests
|
|
|
|
|
|
|
|
struct DropCounter<'a> {
|
|
|
|
count: &'a Cell<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Drop for DropCounter<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.count.set(self.count.get() + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_typed_arena_drop_count() {
|
|
|
|
let counter = Cell::new(0);
|
|
|
|
{
|
|
|
|
let arena: TypedArena<DropCounter> = TypedArena::new();
|
|
|
|
for _ in 0..100 {
|
|
|
|
// Allocate something with drop glue to make sure it doesn't leak.
|
|
|
|
arena.alloc(DropCounter { count: &counter });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
assert_eq!(counter.get(), 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_typed_arena_drop_on_clear() {
|
|
|
|
let counter = Cell::new(0);
|
|
|
|
let mut arena: TypedArena<DropCounter> = TypedArena::new();
|
|
|
|
for i in 0..10 {
|
|
|
|
for _ in 0..100 {
|
|
|
|
// Allocate something with drop glue to make sure it doesn't leak.
|
|
|
|
arena.alloc(DropCounter { count: &counter });
|
|
|
|
}
|
|
|
|
arena.clear();
|
|
|
|
assert_eq!(counter.get(), i * 100 + 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_local! {
|
|
|
|
static DROP_COUNTER: Cell<u32> = Cell::new(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SmallDroppable;
|
|
|
|
|
|
|
|
impl Drop for SmallDroppable {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
DROP_COUNTER.with(|c| c.set(c.get() + 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_typed_arena_drop_small_count() {
|
|
|
|
DROP_COUNTER.with(|c| c.set(0));
|
|
|
|
{
|
|
|
|
let arena: TypedArena<SmallDroppable> = TypedArena::new();
|
|
|
|
for _ in 0..100 {
|
|
|
|
// Allocate something with drop glue to make sure it doesn't leak.
|
|
|
|
arena.alloc(SmallDroppable);
|
|
|
|
}
|
|
|
|
// dropping
|
|
|
|
};
|
|
|
|
assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:20:09 +02:00
|
|
|
#[bench]
|
|
|
|
pub fn bench_noncopy(b: &mut Bencher) {
|
|
|
|
let arena = TypedArena::new();
|
|
|
|
b.iter(|| {
|
|
|
|
arena.alloc(Noncopy {
|
|
|
|
string: "hello world".to_string(),
|
2015-12-03 08:06:16 +01:00
|
|
|
array: vec![1, 2, 3, 4, 5],
|
2015-08-14 14:20:09 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
pub fn bench_noncopy_nonarena(b: &mut Bencher) {
|
|
|
|
b.iter(|| {
|
|
|
|
let _: Box<_> = Box::new(Noncopy {
|
|
|
|
string: "hello world".to_string(),
|
2015-12-03 08:06:16 +01:00
|
|
|
array: vec![1, 2, 3, 4, 5],
|
2015-08-14 14:20:09 +02:00
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
2014-01-06 17:03:30 -08:00
|
|
|
}
|