]> git.lizzy.rs Git - rust.git/blob - src/libarena/lib.rs
96fcc81e8e6edfaabaa78e99e73ec23bbebd00a1
[rust.git] / src / libarena / lib.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The arena, a fast but limited type of allocator.
12 //!
13 //! Arenas are a type of allocator that destroy the objects within, all at
14 //! once, once the arena itself is destroyed. They do not support deallocation
15 //! of individual objects while the arena itself is still alive. The benefit
16 //! of an arena is very fast allocation; just a pointer bump.
17 //!
18 //! This crate implements `TypedArena`, a simple arena that can only hold
19 //! objects of a single type.
20
21 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
22        html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
23        html_root_url = "https://doc.rust-lang.org/nightly/",
24        test(no_crate_inject, attr(deny(warnings))))]
25 #![deny(warnings)]
26
27 #![feature(alloc)]
28 #![feature(core_intrinsics)]
29 #![feature(dropck_eyepatch)]
30 #![feature(generic_param_attrs)]
31 #![feature(needs_drop)]
32 #![cfg_attr(test, feature(test))]
33
34 #![allow(deprecated)]
35
36 extern crate alloc;
37
38 use std::cell::{Cell, RefCell};
39 use std::cmp;
40 use std::intrinsics;
41 use std::marker::{PhantomData, Send};
42 use std::mem;
43 use std::ptr;
44 use std::slice;
45
46 use alloc::raw_vec::RawVec;
47
48 /// An arena that can hold objects of only one type.
49 pub struct TypedArena<T> {
50     /// A pointer to the next object to be allocated.
51     ptr: Cell<*mut T>,
52
53     /// A pointer to the end of the allocated area. When this pointer is
54     /// reached, a new chunk is allocated.
55     end: Cell<*mut T>,
56
57     /// A vector of arena chunks.
58     chunks: RefCell<Vec<TypedArenaChunk<T>>>,
59
60     /// Marker indicating that dropping the arena causes its owned
61     /// instances of `T` to be dropped.
62     _own: PhantomData<T>,
63 }
64
65 struct TypedArenaChunk<T> {
66     /// The raw storage for the arena chunk.
67     storage: RawVec<T>,
68 }
69
70 impl<T> TypedArenaChunk<T> {
71     #[inline]
72     unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
73         TypedArenaChunk { storage: RawVec::with_capacity(capacity) }
74     }
75
76     /// Destroys this arena chunk.
77     #[inline]
78     unsafe fn destroy(&mut self, len: usize) {
79         // The branch on needs_drop() is an -O1 performance optimization.
80         // Without the branch, dropping TypedArena<u8> takes linear time.
81         if mem::needs_drop::<T>() {
82             let mut start = self.start();
83             // Destroy all allocated objects.
84             for _ in 0..len {
85                 ptr::drop_in_place(start);
86                 start = start.offset(1);
87             }
88         }
89     }
90
91     // Returns a pointer to the first allocated object.
92     #[inline]
93     fn start(&self) -> *mut T {
94         self.storage.ptr()
95     }
96
97     // Returns a pointer to the end of the allocated space.
98     #[inline]
99     fn end(&self) -> *mut T {
100         unsafe {
101             if mem::size_of::<T>() == 0 {
102                 // A pointer as large as possible for zero-sized elements.
103                 !0 as *mut T
104             } else {
105                 self.start().offset(self.storage.cap() as isize)
106             }
107         }
108     }
109 }
110
111 const PAGE: usize = 4096;
112
113 impl<T> TypedArena<T> {
114     /// Creates a new `TypedArena`.
115     #[inline]
116     pub fn new() -> TypedArena<T> {
117         TypedArena {
118             // We set both `ptr` and `end` to 0 so that the first call to
119             // alloc() will trigger a grow().
120             ptr: Cell::new(0 as *mut T),
121             end: Cell::new(0 as *mut T),
122             chunks: RefCell::new(vec![]),
123             _own: PhantomData,
124         }
125     }
126
127     /// Allocates an object in the `TypedArena`, returning a reference to it.
128     #[inline]
129     pub fn alloc(&self, object: T) -> &mut T {
130         if self.ptr == self.end {
131             self.grow(1)
132         }
133
134         unsafe {
135             if mem::size_of::<T>() == 0 {
136                 self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T);
137                 let ptr = mem::align_of::<T>() as *mut T;
138                 // Don't drop the object. This `write` is equivalent to `forget`.
139                 ptr::write(ptr, object);
140                 &mut *ptr
141             } else {
142                 let ptr = self.ptr.get();
143                 // Advance the pointer.
144                 self.ptr.set(self.ptr.get().offset(1));
145                 // Write into uninitialized memory.
146                 ptr::write(ptr, object);
147                 &mut *ptr
148             }
149         }
150     }
151
152     /// Allocates a slice of objects that are copy into the `TypedArena`, returning a mutable
153     /// reference to it. Will panic if passed a zero-sized types.
154     ///
155     /// Panics:
156     ///  - Zero-sized types
157     ///  - Zero-length slices
158     #[inline]
159     pub fn alloc_slice(&self, slice: &[T]) -> &mut [T]
160         where T: Copy {
161         assert!(mem::size_of::<T>() != 0);
162         assert!(slice.len() != 0);
163
164         let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize;
165         let at_least_bytes = slice.len() * mem::size_of::<T>();
166         if available_capacity_bytes < at_least_bytes {
167             self.grow(slice.len());
168         }
169
170         unsafe {
171             let start_ptr = self.ptr.get();
172             let arena_slice = slice::from_raw_parts_mut(start_ptr, slice.len());
173             self.ptr.set(start_ptr.offset(arena_slice.len() as isize));
174             arena_slice.copy_from_slice(slice);
175             arena_slice
176         }
177     }
178
179     /// Grows the arena.
180     #[inline(never)]
181     #[cold]
182     fn grow(&self, n: usize) {
183         unsafe {
184             let mut chunks = self.chunks.borrow_mut();
185             let (chunk, mut new_capacity);
186             if let Some(last_chunk) = chunks.last_mut() {
187                 let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
188                 let currently_used_cap = used_bytes / mem::size_of::<T>();
189                 if last_chunk.storage.reserve_in_place(currently_used_cap, n) {
190                     self.end.set(last_chunk.end());
191                     return;
192                 } else {
193                     new_capacity = last_chunk.storage.cap();
194                     loop {
195                         new_capacity = new_capacity.checked_mul(2).unwrap();
196                         if new_capacity >= currently_used_cap + n {
197                             break;
198                         }
199                     }
200                 }
201             } else {
202                 let elem_size = cmp::max(1, mem::size_of::<T>());
203                 new_capacity = cmp::max(n, PAGE / elem_size);
204             }
205             chunk = TypedArenaChunk::<T>::new(new_capacity);
206             self.ptr.set(chunk.start());
207             self.end.set(chunk.end());
208             chunks.push(chunk);
209         }
210     }
211
212     /// Clears the arena. Deallocates all but the longest chunk which may be reused.
213     pub fn clear(&mut self) {
214         unsafe {
215             // Clear the last chunk, which is partially filled.
216             let mut chunks_borrow = self.chunks.borrow_mut();
217             if let Some(mut last_chunk) = chunks_borrow.pop() {
218                 self.clear_last_chunk(&mut last_chunk);
219                 // If `T` is ZST, code below has no effect.
220                 for mut chunk in chunks_borrow.drain(..) {
221                     let cap = chunk.storage.cap();
222                     chunk.destroy(cap);
223                 }
224                 chunks_borrow.push(last_chunk);
225             }
226         }
227     }
228
229     // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
230     // chunks.
231     fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
232         // Determine how much was filled.
233         let start = last_chunk.start() as usize;
234         // We obtain the value of the pointer to the first uninitialized element.
235         let end = self.ptr.get() as usize;
236         // We then calculate the number of elements to be dropped in the last chunk,
237         // which is the filled area's length.
238         let diff = if mem::size_of::<T>() == 0 {
239             // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
240             // the number of zero-sized values in the last and only chunk, just out of caution.
241             // Recall that `end` was incremented for each allocated value.
242             end - start
243         } else {
244             (end - start) / mem::size_of::<T>()
245         };
246         // Pass that to the `destroy` method.
247         unsafe {
248             last_chunk.destroy(diff);
249         }
250         // Reset the chunk.
251         self.ptr.set(last_chunk.start());
252     }
253 }
254
255 unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
256     fn drop(&mut self) {
257         unsafe {
258             // Determine how much was filled.
259             let mut chunks_borrow = self.chunks.borrow_mut();
260             if let Some(mut last_chunk) = chunks_borrow.pop() {
261                 // Drop the contents of the last chunk.
262                 self.clear_last_chunk(&mut last_chunk);
263                 // The last chunk will be dropped. Destroy all other chunks.
264                 for chunk in chunks_borrow.iter_mut() {
265                     let cap = chunk.storage.cap();
266                     chunk.destroy(cap);
267                 }
268             }
269             // RawVec handles deallocation of `last_chunk` and `self.chunks`.
270         }
271     }
272 }
273
274 unsafe impl<T: Send> Send for TypedArena<T> {}
275
276 pub struct DroplessArena {
277     /// A pointer to the next object to be allocated.
278     ptr: Cell<*mut u8>,
279
280     /// A pointer to the end of the allocated area. When this pointer is
281     /// reached, a new chunk is allocated.
282     end: Cell<*mut u8>,
283
284     /// A vector of arena chunks.
285     chunks: RefCell<Vec<TypedArenaChunk<u8>>>,
286 }
287
288 impl DroplessArena {
289     pub fn new() -> DroplessArena {
290         DroplessArena {
291             ptr: Cell::new(0 as *mut u8),
292             end: Cell::new(0 as *mut u8),
293             chunks: RefCell::new(vec![]),
294         }
295     }
296
297     pub fn in_arena<T: ?Sized>(&self, ptr: *const T) -> bool {
298         let ptr = ptr as *const u8 as *mut u8;
299         for chunk in &*self.chunks.borrow() {
300             if chunk.start() <= ptr && ptr < chunk.end() {
301                 return true;
302             }
303         }
304
305         false
306     }
307
308     fn align_for<T>(&self) {
309         let align = mem::align_of::<T>();
310         let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1);
311         self.ptr.set(final_address as *mut u8);
312         assert!(self.ptr <= self.end);
313     }
314
315     #[inline(never)]
316     #[cold]
317     fn grow<T>(&self, n: usize) {
318         let needed_bytes = n * mem::size_of::<T>();
319         unsafe {
320             let mut chunks = self.chunks.borrow_mut();
321             let (chunk, mut new_capacity);
322             if let Some(last_chunk) = chunks.last_mut() {
323                 let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
324                 if last_chunk.storage.reserve_in_place(used_bytes, needed_bytes) {
325                     self.end.set(last_chunk.end());
326                     return;
327                 } else {
328                     new_capacity = last_chunk.storage.cap();
329                     loop {
330                         new_capacity = new_capacity.checked_mul(2).unwrap();
331                         if new_capacity >= used_bytes + needed_bytes {
332                             break;
333                         }
334                     }
335                 }
336             } else {
337                 new_capacity = cmp::max(needed_bytes, PAGE);
338             }
339             chunk = TypedArenaChunk::<u8>::new(new_capacity);
340             self.ptr.set(chunk.start());
341             self.end.set(chunk.end());
342             chunks.push(chunk);
343         }
344     }
345
346     #[inline]
347     pub fn alloc<T>(&self, object: T) -> &mut T {
348         unsafe {
349             assert!(!mem::needs_drop::<T>());
350             assert!(mem::size_of::<T>() != 0);
351
352             self.align_for::<T>();
353             let future_end = intrinsics::arith_offset(self.ptr.get(), mem::size_of::<T>() as isize);
354             if (future_end as *mut u8) >= self.end.get() {
355                 self.grow::<T>(1)
356             }
357
358             let ptr = self.ptr.get();
359             // Set the pointer past ourselves
360             self.ptr.set(intrinsics::arith_offset(
361                     self.ptr.get(), mem::size_of::<T>() as isize
362             ) as *mut u8);
363             // Write into uninitialized memory.
364             ptr::write(ptr as *mut T, object);
365             &mut *(ptr as *mut T)
366         }
367     }
368
369     /// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable
370     /// reference to it. Will panic if passed a zero-sized type.
371     ///
372     /// Panics:
373     ///  - Zero-sized types
374     ///  - Zero-length slices
375     #[inline]
376     pub fn alloc_slice<T>(&self, slice: &[T]) -> &mut [T]
377         where T: Copy {
378         assert!(!mem::needs_drop::<T>());
379         assert!(mem::size_of::<T>() != 0);
380         assert!(slice.len() != 0);
381         self.align_for::<T>();
382
383         let future_end = unsafe {
384             intrinsics::arith_offset(self.ptr.get(), (slice.len() * mem::size_of::<T>()) as isize)
385         };
386         if (future_end as *mut u8) >= self.end.get() {
387             self.grow::<T>(slice.len());
388         }
389
390         unsafe {
391             let arena_slice = slice::from_raw_parts_mut(self.ptr.get() as *mut T, slice.len());
392             self.ptr.set(intrinsics::arith_offset(
393                     self.ptr.get(), (slice.len() * mem::size_of::<T>()) as isize
394             ) as *mut u8);
395             arena_slice.copy_from_slice(slice);
396             arena_slice
397         }
398     }
399 }
400
401 #[cfg(test)]
402 mod tests {
403     extern crate test;
404     use self::test::Bencher;
405     use super::TypedArena;
406     use std::cell::Cell;
407
408     #[allow(dead_code)]
409     #[derive(Debug, Eq, PartialEq)]
410     struct Point {
411         x: i32,
412         y: i32,
413         z: i32,
414     }
415
416     #[test]
417     pub fn test_unused() {
418         let arena: TypedArena<Point> = TypedArena::new();
419         assert!(arena.chunks.borrow().is_empty());
420     }
421
422     #[test]
423     fn test_arena_alloc_nested() {
424         struct Inner {
425             value: u8,
426         }
427         struct Outer<'a> {
428             inner: &'a Inner,
429         }
430         enum EI<'e> {
431             I(Inner),
432             O(Outer<'e>),
433         }
434
435         struct Wrap<'a>(TypedArena<EI<'a>>);
436
437         impl<'a> Wrap<'a> {
438             fn alloc_inner<F: Fn() -> Inner>(&self, f: F) -> &Inner {
439                 let r: &EI = self.0.alloc(EI::I(f()));
440                 if let &EI::I(ref i) = r {
441                     i
442                 } else {
443                     panic!("mismatch");
444                 }
445             }
446             fn alloc_outer<F: Fn() -> Outer<'a>>(&self, f: F) -> &Outer {
447                 let r: &EI = self.0.alloc(EI::O(f()));
448                 if let &EI::O(ref o) = r {
449                     o
450                 } else {
451                     panic!("mismatch");
452                 }
453             }
454         }
455
456         let arena = Wrap(TypedArena::new());
457
458         let result =
459             arena.alloc_outer(|| Outer { inner: arena.alloc_inner(|| Inner { value: 10 }) });
460
461         assert_eq!(result.inner.value, 10);
462     }
463
464     #[test]
465     pub fn test_copy() {
466         let arena = TypedArena::new();
467         for _ in 0..100000 {
468             arena.alloc(Point { x: 1, y: 2, z: 3 });
469         }
470     }
471
472     #[bench]
473     pub fn bench_copy(b: &mut Bencher) {
474         let arena = TypedArena::new();
475         b.iter(|| arena.alloc(Point { x: 1, y: 2, z: 3 }))
476     }
477
478     #[bench]
479     pub fn bench_copy_nonarena(b: &mut Bencher) {
480         b.iter(|| {
481             let _: Box<_> = Box::new(Point { x: 1, y: 2, z: 3 });
482         })
483     }
484
485     #[allow(dead_code)]
486     struct Noncopy {
487         string: String,
488         array: Vec<i32>,
489     }
490
491     #[test]
492     pub fn test_noncopy() {
493         let arena = TypedArena::new();
494         for _ in 0..100000 {
495             arena.alloc(Noncopy {
496                 string: "hello world".to_string(),
497                 array: vec![1, 2, 3, 4, 5],
498             });
499         }
500     }
501
502     #[test]
503     pub fn test_typed_arena_zero_sized() {
504         let arena = TypedArena::new();
505         for _ in 0..100000 {
506             arena.alloc(());
507         }
508     }
509
510     #[test]
511     pub fn test_typed_arena_clear() {
512         let mut arena = TypedArena::new();
513         for _ in 0..10 {
514             arena.clear();
515             for _ in 0..10000 {
516                 arena.alloc(Point { x: 1, y: 2, z: 3 });
517             }
518         }
519     }
520
521     // Drop tests
522
523     struct DropCounter<'a> {
524         count: &'a Cell<u32>,
525     }
526
527     impl<'a> Drop for DropCounter<'a> {
528         fn drop(&mut self) {
529             self.count.set(self.count.get() + 1);
530         }
531     }
532
533     #[test]
534     fn test_typed_arena_drop_count() {
535         let counter = Cell::new(0);
536         {
537             let arena: TypedArena<DropCounter> = TypedArena::new();
538             for _ in 0..100 {
539                 // Allocate something with drop glue to make sure it doesn't leak.
540                 arena.alloc(DropCounter { count: &counter });
541             }
542         };
543         assert_eq!(counter.get(), 100);
544     }
545
546     #[test]
547     fn test_typed_arena_drop_on_clear() {
548         let counter = Cell::new(0);
549         let mut arena: TypedArena<DropCounter> = TypedArena::new();
550         for i in 0..10 {
551             for _ in 0..100 {
552                 // Allocate something with drop glue to make sure it doesn't leak.
553                 arena.alloc(DropCounter { count: &counter });
554             }
555             arena.clear();
556             assert_eq!(counter.get(), i * 100 + 100);
557         }
558     }
559
560     thread_local! {
561         static DROP_COUNTER: Cell<u32> = Cell::new(0)
562     }
563
564     struct SmallDroppable;
565
566     impl Drop for SmallDroppable {
567         fn drop(&mut self) {
568             DROP_COUNTER.with(|c| c.set(c.get() + 1));
569         }
570     }
571
572     #[test]
573     fn test_typed_arena_drop_small_count() {
574         DROP_COUNTER.with(|c| c.set(0));
575         {
576             let arena: TypedArena<SmallDroppable> = TypedArena::new();
577             for _ in 0..100 {
578                 // Allocate something with drop glue to make sure it doesn't leak.
579                 arena.alloc(SmallDroppable);
580             }
581             // dropping
582         };
583         assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
584     }
585
586     #[bench]
587     pub fn bench_noncopy(b: &mut Bencher) {
588         let arena = TypedArena::new();
589         b.iter(|| {
590             arena.alloc(Noncopy {
591                 string: "hello world".to_string(),
592                 array: vec![1, 2, 3, 4, 5],
593             })
594         })
595     }
596
597     #[bench]
598     pub fn bench_noncopy_nonarena(b: &mut Bencher) {
599         b.iter(|| {
600             let _: Box<_> = Box::new(Noncopy {
601                 string: "hello world".to_string(),
602                 array: vec![1, 2, 3, 4, 5],
603             });
604         })
605     }
606 }