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