]> git.lizzy.rs Git - rust.git/blob - src/libarena/lib.rs
7acef128016cf87e5d146e7014b6d2061a6bac62
[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 has two arenas implemented: TypedArena, which is a simpler
19 //! arena but can only hold objects of a single type, and Arena, which is a
20 //! more complex, slower Arena which can hold objects of any type.
21
22 #![crate_id = "arena#0.11.0-pre"]
23 #![crate_type = "rlib"]
24 #![crate_type = "dylib"]
25 #![license = "MIT/ASL2"]
26 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
27        html_favicon_url = "http://www.rust-lang.org/favicon.ico",
28        html_root_url = "http://doc.rust-lang.org/")]
29 #![allow(missing_doc)]
30
31 extern crate collections;
32
33 use std::cell::{Cell, RefCell};
34 use std::cmp;
35 use std::intrinsics::{TyDesc, get_tydesc};
36 use std::intrinsics;
37 use std::mem;
38 use std::num;
39 use std::ptr::read;
40 use std::rc::Rc;
41 use std::rt::heap::allocate;
42
43 // The way arena uses arrays is really deeply awful. The arrays are
44 // allocated, and have capacities reserved, but the fill for the array
45 // will always stay at 0.
46 #[deriving(Clone, PartialEq)]
47 struct Chunk {
48     data: Rc<RefCell<Vec<u8> >>,
49     fill: Cell<uint>,
50     is_copy: Cell<bool>,
51 }
52 impl Chunk {
53     fn capacity(&self) -> uint {
54         self.data.borrow().capacity()
55     }
56
57     unsafe fn as_ptr(&self) -> *u8 {
58         self.data.borrow().as_ptr()
59     }
60 }
61
62 /// A slower reflection-based arena that can allocate objects of any type.
63 ///
64 /// This arena uses Vec<u8> as a backing store to allocate objects from.  For
65 /// each allocated object, the arena stores a pointer to the type descriptor
66 /// followed by the object. (Potentially with alignment padding after each
67 /// element.) When the arena is destroyed, it iterates through all of its
68 /// chunks, and uses the tydesc information to trace through the objects,
69 /// calling the destructors on them.  One subtle point that needs to be
70 /// addressed is how to handle failures while running the user provided
71 /// initializer function. It is important to not run the destructor on
72 /// uninitialized objects, but how to detect them is somewhat subtle. Since
73 /// alloc() can be invoked recursively, it is not sufficient to simply exclude
74 /// the most recent object. To solve this without requiring extra space, we
75 /// use the low order bit of the tydesc pointer to encode whether the object
76 /// it describes has been fully initialized.
77 ///
78 /// As an optimization, objects with destructors are stored in
79 /// different chunks than objects without destructors. This reduces
80 /// overhead when initializing plain-old-data and means we don't need
81 /// to waste time running the destructors of POD.
82 pub struct Arena {
83     // The head is separated out from the list as a unbenchmarked
84     // microoptimization, to avoid needing to case on the list to access the
85     // head.
86     head: Chunk,
87     copy_head: Chunk,
88     chunks: RefCell<Vec<Chunk>>,
89 }
90
91 impl Arena {
92     /// Allocate a new Arena with 32 bytes preallocated.
93     pub fn new() -> Arena {
94         Arena::new_with_size(32u)
95     }
96
97     /// Allocate a new Arena with `initial_size` bytes preallocated.
98     pub fn new_with_size(initial_size: uint) -> Arena {
99         Arena {
100             head: chunk(initial_size, false),
101             copy_head: chunk(initial_size, true),
102             chunks: RefCell::new(Vec::new()),
103         }
104     }
105 }
106
107 fn chunk(size: uint, is_copy: bool) -> Chunk {
108     Chunk {
109         data: Rc::new(RefCell::new(Vec::with_capacity(size))),
110         fill: Cell::new(0u),
111         is_copy: Cell::new(is_copy),
112     }
113 }
114
115 #[unsafe_destructor]
116 impl Drop for Arena {
117     fn drop(&mut self) {
118         unsafe {
119             destroy_chunk(&self.head);
120             for chunk in self.chunks.borrow().iter() {
121                 if !chunk.is_copy.get() {
122                     destroy_chunk(chunk);
123                 }
124             }
125         }
126     }
127 }
128
129 #[inline]
130 fn round_up(base: uint, align: uint) -> uint {
131     (base.checked_add(&(align - 1))).unwrap() & !(&(align - 1))
132 }
133
134 // Walk down a chunk, running the destructors for any objects stored
135 // in it.
136 unsafe fn destroy_chunk(chunk: &Chunk) {
137     let mut idx = 0;
138     let buf = chunk.as_ptr();
139     let fill = chunk.fill.get();
140
141     while idx < fill {
142         let tydesc_data: *uint = mem::transmute(buf.offset(idx as int));
143         let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
144         let (size, align) = ((*tydesc).size, (*tydesc).align);
145
146         let after_tydesc = idx + mem::size_of::<*TyDesc>();
147
148         let start = round_up(after_tydesc, align);
149
150         //debug!("freeing object: idx = {}, size = {}, align = {}, done = {}",
151         //       start, size, align, is_done);
152         if is_done {
153             ((*tydesc).drop_glue)(buf.offset(start as int) as *i8);
154         }
155
156         // Find where the next tydesc lives
157         idx = round_up(start + size, mem::align_of::<*TyDesc>());
158     }
159 }
160
161 // We encode whether the object a tydesc describes has been
162 // initialized in the arena in the low bit of the tydesc pointer. This
163 // is necessary in order to properly do cleanup if a failure occurs
164 // during an initializer.
165 #[inline]
166 fn bitpack_tydesc_ptr(p: *TyDesc, is_done: bool) -> uint {
167     p as uint | (is_done as uint)
168 }
169 #[inline]
170 fn un_bitpack_tydesc_ptr(p: uint) -> (*TyDesc, bool) {
171     ((p & !1) as *TyDesc, p & 1 == 1)
172 }
173
174 impl Arena {
175     fn chunk_size(&self) -> uint {
176         self.copy_head.capacity()
177     }
178     // Functions for the POD part of the arena
179     fn alloc_copy_grow(&mut self, n_bytes: uint, align: uint) -> *u8 {
180         // Allocate a new chunk.
181         let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
182         self.chunks.borrow_mut().push(self.copy_head.clone());
183         self.copy_head =
184             chunk(num::next_power_of_two(new_min_chunk_size + 1u), true);
185
186         return self.alloc_copy_inner(n_bytes, align);
187     }
188
189     #[inline]
190     fn alloc_copy_inner(&mut self, n_bytes: uint, align: uint) -> *u8 {
191         unsafe {
192             let start = round_up(self.copy_head.fill.get(), align);
193             let end = start + n_bytes;
194             if end > self.chunk_size() {
195                 return self.alloc_copy_grow(n_bytes, align);
196             }
197             self.copy_head.fill.set(end);
198
199             //debug!("idx = {}, size = {}, align = {}, fill = {}",
200             //       start, n_bytes, align, head.fill.get());
201
202             self.copy_head.as_ptr().offset(start as int)
203         }
204     }
205
206     #[inline]
207     fn alloc_copy<'a, T>(&'a mut self, op: || -> T) -> &'a T {
208         unsafe {
209             let ptr = self.alloc_copy_inner(mem::size_of::<T>(),
210                                             mem::min_align_of::<T>());
211             let ptr = ptr as *mut T;
212             mem::overwrite(&mut (*ptr), op());
213             return &*ptr;
214         }
215     }
216
217     // Functions for the non-POD part of the arena
218     fn alloc_noncopy_grow(&mut self, n_bytes: uint, align: uint)
219                          -> (*u8, *u8) {
220         // Allocate a new chunk.
221         let new_min_chunk_size = cmp::max(n_bytes, self.chunk_size());
222         self.chunks.borrow_mut().push(self.head.clone());
223         self.head =
224             chunk(num::next_power_of_two(new_min_chunk_size + 1u), false);
225
226         return self.alloc_noncopy_inner(n_bytes, align);
227     }
228
229     #[inline]
230     fn alloc_noncopy_inner(&mut self, n_bytes: uint, align: uint)
231                           -> (*u8, *u8) {
232         unsafe {
233             let tydesc_start = self.head.fill.get();
234             let after_tydesc = self.head.fill.get() + mem::size_of::<*TyDesc>();
235             let start = round_up(after_tydesc, align);
236             let end = start + n_bytes;
237
238             if end > self.head.capacity() {
239                 return self.alloc_noncopy_grow(n_bytes, align);
240             }
241
242             self.head.fill.set(round_up(end, mem::align_of::<*TyDesc>()));
243
244             //debug!("idx = {}, size = {}, align = {}, fill = {}",
245             //       start, n_bytes, align, head.fill);
246
247             let buf = self.head.as_ptr();
248             return (buf.offset(tydesc_start as int), buf.offset(start as int));
249         }
250     }
251
252     #[inline]
253     fn alloc_noncopy<'a, T>(&'a mut self, op: || -> T) -> &'a T {
254         unsafe {
255             let tydesc = get_tydesc::<T>();
256             let (ty_ptr, ptr) =
257                 self.alloc_noncopy_inner(mem::size_of::<T>(),
258                                          mem::min_align_of::<T>());
259             let ty_ptr = ty_ptr as *mut uint;
260             let ptr = ptr as *mut T;
261             // Write in our tydesc along with a bit indicating that it
262             // has *not* been initialized yet.
263             *ty_ptr = mem::transmute(tydesc);
264             // Actually initialize it
265             mem::overwrite(&mut(*ptr), op());
266             // Now that we are done, update the tydesc to indicate that
267             // the object is there.
268             *ty_ptr = bitpack_tydesc_ptr(tydesc, true);
269
270             return &*ptr;
271         }
272     }
273
274     /// Allocate a new item in the arena, using `op` to initialize the value
275     /// and returning a reference to it.
276     #[inline]
277     pub fn alloc<'a, T>(&'a self, op: || -> T) -> &'a T {
278         unsafe {
279             // FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
280             let this: &mut Arena = mem::transmute::<&_, &mut _>(self);
281             if intrinsics::needs_drop::<T>() {
282                 this.alloc_noncopy(op)
283             } else {
284                 this.alloc_copy(op)
285             }
286         }
287     }
288 }
289
290 #[test]
291 fn test_arena_destructors() {
292     let arena = Arena::new();
293     for i in range(0u, 10) {
294         // Arena allocate something with drop glue to make sure it
295         // doesn't leak.
296         arena.alloc(|| Rc::new(i));
297         // Allocate something with funny size and alignment, to keep
298         // things interesting.
299         arena.alloc(|| [0u8, 1u8, 2u8]);
300     }
301 }
302
303 #[test]
304 #[should_fail]
305 fn test_arena_destructors_fail() {
306     let arena = Arena::new();
307     // Put some stuff in the arena.
308     for i in range(0u, 10) {
309         // Arena allocate something with drop glue to make sure it
310         // doesn't leak.
311         arena.alloc(|| { Rc::new(i) });
312         // Allocate something with funny size and alignment, to keep
313         // things interesting.
314         arena.alloc(|| { [0u8, 1u8, 2u8] });
315     }
316     // Now, fail while allocating
317     arena.alloc::<Rc<int>>(|| {
318         // Now fail.
319         fail!();
320     });
321 }
322
323 /// A faster arena that can hold objects of only one type.
324 ///
325 /// Safety note: Modifying objects in the arena that have already had their
326 /// `drop` destructors run can cause leaks, because the destructor will not
327 /// run again for these objects.
328 pub struct TypedArena<T> {
329     /// A pointer to the next object to be allocated.
330     ptr: *T,
331
332     /// A pointer to the end of the allocated area. When this pointer is
333     /// reached, a new chunk is allocated.
334     end: *T,
335
336     /// A pointer to the first arena segment.
337     first: Option<Box<TypedArenaChunk<T>>>,
338 }
339
340 struct TypedArenaChunk<T> {
341     /// Pointer to the next arena segment.
342     next: Option<Box<TypedArenaChunk<T>>>,
343
344     /// The number of elements that this chunk can hold.
345     capacity: uint,
346
347     // Objects follow here, suitably aligned.
348 }
349
350 impl<T> TypedArenaChunk<T> {
351     #[inline]
352     fn new(next: Option<Box<TypedArenaChunk<T>>>, capacity: uint)
353            -> Box<TypedArenaChunk<T>> {
354         let mut size = mem::size_of::<TypedArenaChunk<T>>();
355         size = round_up(size, mem::min_align_of::<T>());
356         let elem_size = mem::size_of::<T>();
357         let elems_size = elem_size.checked_mul(&capacity).unwrap();
358         size = size.checked_add(&elems_size).unwrap();
359
360         let mut chunk = unsafe {
361             let chunk = allocate(size, mem::min_align_of::<TypedArenaChunk<T>>());
362             let mut chunk: Box<TypedArenaChunk<T>> = mem::transmute(chunk);
363             mem::overwrite(&mut chunk.next, next);
364             chunk
365         };
366
367         chunk.capacity = capacity;
368         chunk
369     }
370
371     /// Destroys this arena chunk. If the type descriptor is supplied, the
372     /// drop glue is called; otherwise, drop glue is not called.
373     #[inline]
374     unsafe fn destroy(&mut self, len: uint) {
375         // Destroy all the allocated objects.
376         if intrinsics::needs_drop::<T>() {
377             let mut start = self.start();
378             for _ in range(0, len) {
379                 read(start as *T); // run the destructor on the pointer
380                 start = start.offset(mem::size_of::<T>() as int)
381             }
382         }
383
384         // Destroy the next chunk.
385         let next_opt = mem::replace(&mut self.next, None);
386         match next_opt {
387             None => {}
388             Some(mut next) => {
389                 // We assume that the next chunk is completely filled.
390                 next.destroy(next.capacity)
391             }
392         }
393     }
394
395     // Returns a pointer to the first allocated object.
396     #[inline]
397     fn start(&self) -> *u8 {
398         let this: *TypedArenaChunk<T> = self;
399         unsafe {
400             mem::transmute(round_up(this.offset(1) as uint,
401                                     mem::min_align_of::<T>()))
402         }
403     }
404
405     // Returns a pointer to the end of the allocated space.
406     #[inline]
407     fn end(&self) -> *u8 {
408         unsafe {
409             let size = mem::size_of::<T>().checked_mul(&self.capacity).unwrap();
410             self.start().offset(size as int)
411         }
412     }
413 }
414
415 impl<T> TypedArena<T> {
416     /// Creates a new TypedArena with preallocated space for 8 objects.
417     #[inline]
418     pub fn new() -> TypedArena<T> {
419         TypedArena::with_capacity(8)
420     }
421
422     /// Creates a new TypedArena with preallocated space for the given number of
423     /// objects.
424     #[inline]
425     pub fn with_capacity(capacity: uint) -> TypedArena<T> {
426         let chunk = TypedArenaChunk::<T>::new(None, capacity);
427         TypedArena {
428             ptr: chunk.start() as *T,
429             end: chunk.end() as *T,
430             first: Some(chunk),
431         }
432     }
433
434     /// Allocates an object in the TypedArena, returning a reference to it.
435     #[inline]
436     pub fn alloc<'a>(&'a self, object: T) -> &'a T {
437         unsafe {
438             // FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
439             let this: &mut TypedArena<T> = mem::transmute::<&_, &mut _>(self);
440             if this.ptr == this.end {
441                 this.grow()
442             }
443
444             let ptr: &'a mut T = mem::transmute(this.ptr);
445             mem::overwrite(ptr, object);
446             this.ptr = this.ptr.offset(1);
447             let ptr: &'a T = ptr;
448             ptr
449         }
450     }
451
452     /// Grows the arena.
453     #[inline(never)]
454     fn grow(&mut self) {
455         let chunk = self.first.take_unwrap();
456         let new_capacity = chunk.capacity.checked_mul(&2).unwrap();
457         let chunk = TypedArenaChunk::<T>::new(Some(chunk), new_capacity);
458         self.ptr = chunk.start() as *T;
459         self.end = chunk.end() as *T;
460         self.first = Some(chunk)
461     }
462 }
463
464 #[unsafe_destructor]
465 impl<T> Drop for TypedArena<T> {
466     fn drop(&mut self) {
467         // Determine how much was filled.
468         let start = self.first.get_ref().start() as uint;
469         let end = self.ptr as uint;
470         let diff = (end - start) / mem::size_of::<T>();
471
472         // Pass that to the `destroy` method.
473         unsafe {
474             self.first.get_mut_ref().destroy(diff)
475         }
476     }
477 }
478
479 #[cfg(test)]
480 mod tests {
481     extern crate test;
482     use self::test::Bencher;
483     use super::{Arena, TypedArena};
484
485     struct Point {
486         x: int,
487         y: int,
488         z: int,
489     }
490
491     #[test]
492     pub fn test_copy() {
493         let arena = TypedArena::new();
494         for _ in range(0, 100000) {
495             arena.alloc(Point {
496                 x: 1,
497                 y: 2,
498                 z: 3,
499             });
500         }
501     }
502
503     #[bench]
504     pub fn bench_copy(b: &mut Bencher) {
505         let arena = TypedArena::new();
506         b.iter(|| {
507             arena.alloc(Point {
508                 x: 1,
509                 y: 2,
510                 z: 3,
511             })
512         })
513     }
514
515     #[bench]
516     pub fn bench_copy_nonarena(b: &mut Bencher) {
517         b.iter(|| {
518             box Point {
519                 x: 1,
520                 y: 2,
521                 z: 3,
522             }
523         })
524     }
525
526     #[bench]
527     pub fn bench_copy_old_arena(b: &mut Bencher) {
528         let arena = Arena::new();
529         b.iter(|| {
530             arena.alloc(|| {
531                 Point {
532                     x: 1,
533                     y: 2,
534                     z: 3,
535                 }
536             })
537         })
538     }
539
540     struct Noncopy {
541         string: String,
542         array: Vec<int> ,
543     }
544
545     #[test]
546     pub fn test_noncopy() {
547         let arena = TypedArena::new();
548         for _ in range(0, 100000) {
549             arena.alloc(Noncopy {
550                 string: "hello world".to_string(),
551                 array: vec!( 1, 2, 3, 4, 5 ),
552             });
553         }
554     }
555
556     #[bench]
557     pub fn bench_noncopy(b: &mut Bencher) {
558         let arena = TypedArena::new();
559         b.iter(|| {
560             arena.alloc(Noncopy {
561                 string: "hello world".to_string(),
562                 array: vec!( 1, 2, 3, 4, 5 ),
563             })
564         })
565     }
566
567     #[bench]
568     pub fn bench_noncopy_nonarena(b: &mut Bencher) {
569         b.iter(|| {
570             box Noncopy {
571                 string: "hello world".to_string(),
572                 array: vec!( 1, 2, 3, 4, 5 ),
573             }
574         })
575     }
576
577     #[bench]
578     pub fn bench_noncopy_old_arena(b: &mut Bencher) {
579         let arena = Arena::new();
580         b.iter(|| {
581             arena.alloc(|| Noncopy {
582                 string: "hello world".to_string(),
583                 array: vec!( 1, 2, 3, 4, 5 ),
584             })
585         })
586     }
587 }