typed_arena/lib.rs
1//! The arena, a fast but limited type of allocator.
2//!
3//! Arenas are a type of allocator that destroy the objects within,
4//! all at once, once the arena itself is destroyed.
5//! They do not support deallocation of individual objects while the arena itself is still alive.
6//! The benefit of an arena is very fast allocation; just a vector push.
7//!
8//! This is an equivalent of the old
9//! [`arena::TypedArena`](https://doc.rust-lang.org/1.1.0/arena/struct.TypedArena.html)
10//! type that was once distributed with nightly rustc but has since been
11//! removed.
12//!
13//! It is slightly less efficient, but simpler internally and uses much less unsafe code.
14//! It is based on a `Vec<Vec<T>>` instead of raw pointers and manual drops.
15//!
16//! ## Example
17//!
18//! ```
19//! use typed_arena::Arena;
20//!
21//! struct Monster {
22//! level: u32,
23//! }
24//!
25//! let monsters = Arena::new();
26//!
27//! let vegeta = monsters.alloc(Monster { level: 9001 });
28//! assert!(vegeta.level > 9000);
29//! ```
30//!
31//! ## Safe Cycles
32//!
33//! All allocated objects get the same lifetime, so you can safely create cycles
34//! between them. This can be useful for certain data structures, such as graphs
35//! and trees with parent pointers.
36//!
37//! ```
38//! use std::cell::Cell;
39//! use typed_arena::Arena;
40//!
41//! struct CycleParticipant<'a> {
42//! other: Cell<Option<&'a CycleParticipant<'a>>>,
43//! }
44//!
45//! let arena = Arena::new();
46//!
47//! let a = arena.alloc(CycleParticipant { other: Cell::new(None) });
48//! let b = arena.alloc(CycleParticipant { other: Cell::new(None) });
49//!
50//! a.other.set(Some(b));
51//! b.other.set(Some(a));
52//! ```
53
54// Potential optimizations:
55// 1) add and stabilize a method for in-place reallocation of vecs.
56// 2) add and stabilize placement new.
57// 3) use an iterator. This may add far too much unsafe code.
58
59#![deny(missing_docs)]
60#![cfg_attr(not(any(feature = "std", test)), no_std)]
61#![cfg_attr(not(feature = "std"), feature(alloc))]
62
63#[cfg(not(feature = "std"))]
64extern crate alloc;
65
66#[cfg(any(feature = "std", test))]
67extern crate core;
68
69#[cfg(not(feature = "std"))]
70use alloc::Vec;
71
72use core::cell::RefCell;
73use core::cmp;
74use core::iter;
75use core::mem;
76use core::slice;
77
78#[cfg(test)]
79mod test;
80
81// Initial size in bytes.
82const INITIAL_SIZE: usize = 1024;
83// Minimum capacity. Must be larger than 0.
84const MIN_CAPACITY: usize = 1;
85
86/// An arena of objects of type `T`.
87///
88/// ## Example
89///
90/// ```
91/// use typed_arena::Arena;
92///
93/// struct Monster {
94/// level: u32,
95/// }
96///
97/// let monsters = Arena::new();
98///
99/// let vegeta = monsters.alloc(Monster { level: 9001 });
100/// assert!(vegeta.level > 9000);
101/// ```
102pub struct Arena<T> {
103 chunks: RefCell<ChunkList<T>>,
104}
105
106struct ChunkList<T> {
107 current: Vec<T>,
108 rest: Vec<Vec<T>>,
109}
110
111impl<T> Arena<T> {
112 /// Construct a new arena.
113 ///
114 /// ## Example
115 ///
116 /// ```
117 /// use typed_arena::Arena;
118 ///
119 /// let arena = Arena::new();
120 /// # arena.alloc(1);
121 /// ```
122 pub fn new() -> Arena<T> {
123 let size = cmp::max(1, mem::size_of::<T>());
124 Arena::with_capacity(INITIAL_SIZE / size)
125 }
126
127 /// Construct a new arena with capacity for `n` values pre-allocated.
128 ///
129 /// ## Example
130 ///
131 /// ```
132 /// use typed_arena::Arena;
133 ///
134 /// let arena = Arena::with_capacity(1337);
135 /// # arena.alloc(1);
136 /// ```
137 pub fn with_capacity(n: usize) -> Arena<T> {
138 let n = cmp::max(MIN_CAPACITY, n);
139 Arena {
140 chunks: RefCell::new(ChunkList {
141 current: Vec::with_capacity(n),
142 rest: Vec::new(),
143 }),
144 }
145 }
146
147 /// Allocates a value in the arena, and returns a mutable reference
148 /// to that value.
149 ///
150 /// ## Example
151 ///
152 /// ```
153 /// use typed_arena::Arena;
154 ///
155 /// let arena = Arena::new();
156 /// let x = arena.alloc(42);
157 /// assert_eq!(*x, 42);
158 /// ```
159 pub fn alloc(&self, value: T) -> &mut T {
160 &mut self.alloc_extend(iter::once(value))[0]
161 }
162
163 /// Uses the contents of an iterator to allocate values in the arena.
164 /// Returns a mutable slice that contains these values.
165 ///
166 /// ## Example
167 ///
168 /// ```
169 /// use typed_arena::Arena;
170 ///
171 /// let arena = Arena::new();
172 /// let abc = arena.alloc_extend("abcdefg".chars().take(3));
173 /// assert_eq!(abc, ['a', 'b', 'c']);
174 /// ```
175 pub fn alloc_extend<I>(&self, iterable: I) -> &mut [T]
176 where I: IntoIterator<Item = T>
177 {
178 let mut iter = iterable.into_iter();
179
180 let mut chunks = self.chunks.borrow_mut();
181
182 let iter_min_len = iter.size_hint().0;
183 let mut next_item_index;
184 if chunks.current.len() + iter_min_len > chunks.current.capacity() {
185 chunks.reserve(iter_min_len);
186 chunks.current.extend(iter);
187 next_item_index = 0;
188 } else {
189 next_item_index = chunks.current.len();
190 let mut i = 0;
191 while let Some(elem) = iter.next() {
192 if chunks.current.len() == chunks.current.capacity() {
193 // The iterator was larger than we could fit into the current chunk.
194 let chunks = &mut *chunks;
195 // Create a new chunk into which we can freely push the entire iterator into
196 chunks.reserve(i + 1);
197 let previous_chunk = chunks.rest.last_mut().unwrap();
198 let previous_chunk_len = previous_chunk.len();
199 // Move any elements we put into the previous chunk into this new chunk
200 chunks.current.extend(previous_chunk.drain(previous_chunk_len - i..));
201 chunks.current.push(elem);
202 // And the remaining elements in the iterator
203 chunks.current.extend(iter);
204 next_item_index = 0;
205 break;
206 } else {
207 chunks.current.push(elem);
208 }
209 i += 1;
210 }
211 }
212 let new_slice_ref = {
213 let new_slice_ref = &mut chunks.current[next_item_index..];
214
215 // Extend the lifetime from that of `chunks_borrow` to that of `self`.
216 // This is OK because we’re careful to never move items
217 // by never pushing to inner `Vec`s beyond their initial capacity.
218 // The returned reference is unique (`&mut`):
219 // the `Arena` never gives away references to existing items.
220 unsafe { mem::transmute::<&mut [T], &mut [T]>(new_slice_ref) }
221 };
222
223 new_slice_ref
224 }
225
226 /// Allocates space for a given number of values, but doesn't initialize it.
227 ///
228 /// ## Unsafety and Undefined Behavior
229 ///
230 /// The same caveats that apply to
231 /// [`std::mem::uninitialized`](https://doc.rust-lang.org/nightly/std/mem/fn.uninitialized.html)
232 /// apply here:
233 ///
234 /// > **This is incredibly dangerous and should not be done lightly. Deeply
235 /// consider initializing your memory with a default value instead.**
236 ///
237 /// In particular, it is easy to trigger undefined behavior by allocating
238 /// uninitialized values, failing to properly initialize them, and then the
239 /// `Arena` will attempt to drop them when it is dropped. Initializing an
240 /// uninitialized value is trickier than it might seem: a normal assignment
241 /// to a field will attempt to drop the old, uninitialized value, which
242 /// almost certainly also triggers undefined behavior. You must also
243 /// consider all the places where your code might "unexpectedly" drop values
244 /// earlier than it "should" because of unwinding during panics.
245 pub unsafe fn alloc_uninitialized(&self, num: usize) -> *mut [T] {
246 let mut chunks = self.chunks.borrow_mut();
247
248 if chunks.current.len() + num > chunks.current.capacity() {
249 chunks.reserve(num);
250 }
251
252 // At this point, the current chunk must have free capacity.
253 let next_item_index = chunks.current.len();
254 chunks.current.set_len(next_item_index + num);
255 // Extend the lifetime...
256 &mut chunks.current[next_item_index..] as *mut _
257 }
258
259 /// Returns unused space.
260 ///
261 /// *This unused space is still not considered "allocated".* Therefore, it
262 /// won't be dropped unless there are further calls to `alloc`,
263 /// `alloc_uninitialized`, or `alloc_extend` which is why the method is
264 /// safe.
265 pub fn uninitialized_array(&self) -> *mut [T] {
266 let chunks = self.chunks.borrow();
267 let len = chunks.current.capacity() - chunks.current.len();
268 let next_item_index = chunks.current.len();
269 let slice = &chunks.current[next_item_index..];
270 unsafe { slice::from_raw_parts_mut(slice.as_ptr() as *mut T, len) as *mut _ }
271 }
272
273 /// Convert this `Arena` into a `Vec<T>`.
274 ///
275 /// Items in the resulting `Vec<T>` appear in the order that they were
276 /// allocated in.
277 ///
278 /// ## Example
279 ///
280 /// ```
281 /// use typed_arena::Arena;
282 ///
283 /// let arena = Arena::new();
284 ///
285 /// arena.alloc("a");
286 /// arena.alloc("b");
287 /// arena.alloc("c");
288 ///
289 /// let easy_as_123 = arena.into_vec();
290 ///
291 /// assert_eq!(easy_as_123, vec!["a", "b", "c"]);
292 /// ```
293 pub fn into_vec(self) -> Vec<T> {
294 let mut chunks = self.chunks.into_inner();
295 // keep order of allocation in the resulting Vec
296 let n = chunks.rest.iter().fold(chunks.current.len(), |a, v| a + v.len());
297 let mut result = Vec::with_capacity(n);
298 for mut vec in chunks.rest {
299 result.append(&mut vec);
300 }
301 result.append(&mut chunks.current);
302 result
303 }
304}
305
306impl<T> ChunkList<T> {
307 #[inline(never)]
308 #[cold]
309 fn reserve(&mut self, additional: usize) {
310 let double_cap = self.current.capacity().checked_mul(2).expect("capacity overflow");
311 let required_cap = additional.checked_next_power_of_two().expect("capacity overflow");
312 let new_capacity = cmp::max(double_cap, required_cap);
313 let chunk = mem::replace(&mut self.current, Vec::with_capacity(new_capacity));
314 self.rest.push(chunk);
315 }
316}