]> git.lizzy.rs Git - rust.git/blob - src/libarena/lib.rs
Auto merge of #30813 - fhahn:fix-ice-semicolon-in-lifetime, r=nrc
[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_name = "arena"]
23 #![unstable(feature = "rustc_private", issue = "27812")]
24 #![crate_type = "rlib"]
25 #![crate_type = "dylib"]
26 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
27        html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
28        html_root_url = "https://doc.rust-lang.org/nightly/",
29        test(no_crate_inject, attr(deny(warnings))))]
30
31 #![feature(alloc)]
32 #![feature(core_intrinsics)]
33 #![feature(drop_in_place)]
34 #![feature(heap_api)]
35 #![feature(raw)]
36 #![feature(heap_api)]
37 #![feature(staged_api)]
38 #![feature(dropck_parametricity)]
39 #![cfg_attr(test, feature(test))]
40
41 #![allow(deprecated)]
42
43 extern crate alloc;
44
45 use std::cell::{Cell, RefCell};
46 use std::cmp;
47 use std::intrinsics;
48 use std::marker::{PhantomData, Send};
49 use std::mem;
50 use std::ptr;
51 use std::slice;
52
53 use alloc::heap;
54 use alloc::raw_vec::RawVec;
55
56 struct Chunk {
57     data: RawVec<u8>,
58     /// Index of the first unused byte.
59     fill: Cell<usize>,
60     /// Indicates whether objects with destructors are stored in this chunk.
61     is_copy: Cell<bool>,
62 }
63
64 impl Chunk {
65     fn new(size: usize, is_copy: bool) -> Chunk {
66         Chunk {
67             data: RawVec::with_capacity(size),
68             fill: Cell::new(0),
69             is_copy: Cell::new(is_copy),
70         }
71     }
72
73     fn capacity(&self) -> usize {
74         self.data.cap()
75     }
76
77     unsafe fn as_ptr(&self) -> *const u8 {
78         self.data.ptr()
79     }
80
81     // Walk down a chunk, running the destructors for any objects stored
82     // in it.
83     unsafe fn destroy(&self) {
84         let mut idx = 0;
85         let buf = self.as_ptr();
86         let fill = self.fill.get();
87
88         while idx < fill {
89             let tydesc_data = buf.offset(idx as isize) as *const usize;
90             let (tydesc, is_done) = un_bitpack_tydesc_ptr(*tydesc_data);
91             let (size, align) = ((*tydesc).size, (*tydesc).align);
92
93             let after_tydesc = idx + mem::size_of::<*const TyDesc>();
94
95             let start = round_up(after_tydesc, align);
96
97             if is_done {
98                 ((*tydesc).drop_glue)(buf.offset(start as isize) as *const i8);
99             }
100
101             // Find where the next tydesc lives
102             idx = round_up(start + size, mem::align_of::<*const TyDesc>());
103         }
104     }
105 }
106
107 /// A slower reflection-based arena that can allocate objects of any type.
108 ///
109 /// This arena uses `RawVec<u8>` as a backing store to allocate objects from.
110 /// For each allocated object, the arena stores a pointer to the type descriptor
111 /// followed by the object (potentially with alignment padding after each
112 /// element). When the arena is destroyed, it iterates through all of its
113 /// chunks, and uses the tydesc information to trace through the objects,
114 /// calling the destructors on them. One subtle point that needs to be
115 /// addressed is how to handle panics while running the user provided
116 /// initializer function. It is important to not run the destructor on
117 /// uninitialized objects, but how to detect them is somewhat subtle. Since
118 /// `alloc()` can be invoked recursively, it is not sufficient to simply exclude
119 /// the most recent object. To solve this without requiring extra space, we
120 /// use the low order bit of the tydesc pointer to encode whether the object
121 /// it describes has been fully initialized.
122 ///
123 /// As an optimization, objects with destructors are stored in different chunks
124 /// than objects without destructors. This reduces overhead when initializing
125 /// plain-old-data (`Copy` types) and means we don't need to waste time running
126 /// their destructors.
127 #[unstable(feature = "rustc_private",
128            reason = "Private to rustc", issue = "0")]
129 #[rustc_deprecated(since = "1.6.0-dev", reason =
130 "The reflection-based arena is superseded by the any-arena crate")]
131 pub struct Arena<'longer_than_self> {
132     // The heads are separated out from the list as a unbenchmarked
133     // microoptimization, to avoid needing to case on the list to access a head.
134     head: RefCell<Chunk>,
135     copy_head: RefCell<Chunk>,
136     chunks: RefCell<Vec<Chunk>>,
137     _marker: PhantomData<*mut &'longer_than_self ()>,
138 }
139
140 impl<'a> Arena<'a> {
141     /// Allocates a new Arena with 32 bytes preallocated.
142     pub fn new() -> Arena<'a> {
143         Arena::new_with_size(32)
144     }
145
146     /// Allocates a new Arena with `initial_size` bytes preallocated.
147     pub fn new_with_size(initial_size: usize) -> Arena<'a> {
148         Arena {
149             head: RefCell::new(Chunk::new(initial_size, false)),
150             copy_head: RefCell::new(Chunk::new(initial_size, true)),
151             chunks: RefCell::new(Vec::new()),
152             _marker: PhantomData,
153         }
154     }
155 }
156
157 impl<'longer_than_self> Drop for Arena<'longer_than_self> {
158     fn drop(&mut self) {
159         unsafe {
160             self.head.borrow().destroy();
161             for chunk in self.chunks.borrow().iter() {
162                 if !chunk.is_copy.get() {
163                     chunk.destroy();
164                 }
165             }
166         }
167     }
168 }
169
170 #[inline]
171 fn round_up(base: usize, align: usize) -> usize {
172     (base.checked_add(align - 1)).unwrap() & !(align - 1)
173 }
174
175 // We encode whether the object a tydesc describes has been
176 // initialized in the arena in the low bit of the tydesc pointer. This
177 // is necessary in order to properly do cleanup if a panic occurs
178 // during an initializer.
179 #[inline]
180 fn bitpack_tydesc_ptr(p: *const TyDesc, is_done: bool) -> usize {
181     p as usize | (is_done as usize)
182 }
183 #[inline]
184 fn un_bitpack_tydesc_ptr(p: usize) -> (*const TyDesc, bool) {
185     ((p & !1) as *const TyDesc, p & 1 == 1)
186 }
187
188 // HACK(eddyb) TyDesc replacement using a trait object vtable.
189 // This could be replaced in the future with a custom DST layout,
190 // or `&'static (drop_glue, size, align)` created by a `const fn`.
191 // Requirements:
192 // * rvalue promotion (issue #1056)
193 // * mem::{size_of, align_of} must be const fns
194 struct TyDesc {
195     drop_glue: fn(*const i8),
196     size: usize,
197     align: usize,
198 }
199
200 trait AllTypes {
201     fn dummy(&self) {}
202 }
203
204 impl<T: ?Sized> AllTypes for T {}
205
206 unsafe fn get_tydesc<T>() -> *const TyDesc {
207     use std::raw::TraitObject;
208
209     let ptr = &*(heap::EMPTY as *const T);
210
211     // Can use any trait that is implemented for all types.
212     let obj = mem::transmute::<&AllTypes, TraitObject>(ptr);
213     obj.vtable as *const TyDesc
214 }
215
216 impl<'longer_than_self> Arena<'longer_than_self> {
217     // Grows a given chunk and returns `false`, or replaces it with a bigger
218     // chunk and returns `true`.
219     // This method is shared by both parts of the arena.
220     #[cold]
221     fn alloc_grow(&self, head: &mut Chunk, used_cap: usize, n_bytes: usize) -> bool {
222         if head.data.reserve_in_place(used_cap, n_bytes) {
223             // In-place reallocation succeeded.
224             false
225         } else {
226             // Allocate a new chunk.
227             let new_min_chunk_size = cmp::max(n_bytes, head.capacity());
228             let new_chunk = Chunk::new((new_min_chunk_size + 1).next_power_of_two(), false);
229             let old_chunk = mem::replace(head, new_chunk);
230             if old_chunk.fill.get() != 0 {
231                 self.chunks.borrow_mut().push(old_chunk);
232             }
233             true
234         }
235     }
236
237     // Functions for the copyable part of the arena.
238
239     #[inline]
240     fn alloc_copy_inner(&self, n_bytes: usize, align: usize) -> *const u8 {
241         let mut copy_head = self.copy_head.borrow_mut();
242         let fill = copy_head.fill.get();
243         let mut start = round_up(fill, align);
244         let mut end = start + n_bytes;
245
246         if end > copy_head.capacity() {
247             if self.alloc_grow(&mut *copy_head, fill, end - fill) {
248                 // Continuing with a newly allocated chunk
249                 start = 0;
250                 end = n_bytes;
251                 copy_head.is_copy.set(true);
252             }
253         }
254
255         copy_head.fill.set(end);
256
257         unsafe { copy_head.as_ptr().offset(start as isize) }
258     }
259
260     #[inline]
261     fn alloc_copy<T, F>(&self, op: F) -> &mut T
262         where F: FnOnce() -> T
263     {
264         unsafe {
265             let ptr = self.alloc_copy_inner(mem::size_of::<T>(), mem::align_of::<T>());
266             let ptr = ptr as *mut T;
267             ptr::write(&mut (*ptr), op());
268             &mut *ptr
269         }
270     }
271
272     // Functions for the non-copyable part of the arena.
273
274     #[inline]
275     fn alloc_noncopy_inner(&self, n_bytes: usize, align: usize) -> (*const u8, *const u8) {
276         let mut head = self.head.borrow_mut();
277         let fill = head.fill.get();
278
279         let mut tydesc_start = fill;
280         let after_tydesc = fill + mem::size_of::<*const TyDesc>();
281         let mut start = round_up(after_tydesc, align);
282         let mut end = round_up(start + n_bytes, mem::align_of::<*const TyDesc>());
283
284         if end > head.capacity() {
285             if self.alloc_grow(&mut *head, tydesc_start, end - tydesc_start) {
286                 // Continuing with a newly allocated chunk
287                 tydesc_start = 0;
288                 start = round_up(mem::size_of::<*const TyDesc>(), align);
289                 end = round_up(start + n_bytes, mem::align_of::<*const TyDesc>());
290             }
291         }
292
293         head.fill.set(end);
294
295         unsafe {
296             let buf = head.as_ptr();
297             (buf.offset(tydesc_start as isize),
298              buf.offset(start as isize))
299         }
300     }
301
302     #[inline]
303     fn alloc_noncopy<T, F>(&self, op: F) -> &mut T
304         where F: FnOnce() -> T
305     {
306         unsafe {
307             let tydesc = get_tydesc::<T>();
308             let (ty_ptr, ptr) = self.alloc_noncopy_inner(mem::size_of::<T>(), mem::align_of::<T>());
309             let ty_ptr = ty_ptr as *mut usize;
310             let ptr = ptr as *mut T;
311             // Write in our tydesc along with a bit indicating that it
312             // has *not* been initialized yet.
313             *ty_ptr = bitpack_tydesc_ptr(tydesc, false);
314             // Actually initialize it
315             ptr::write(&mut (*ptr), op());
316             // Now that we are done, update the tydesc to indicate that
317             // the object is there.
318             *ty_ptr = bitpack_tydesc_ptr(tydesc, true);
319
320             &mut *ptr
321         }
322     }
323
324     /// Allocates a new item in the arena, using `op` to initialize the value,
325     /// and returns a reference to it.
326     #[inline]
327     pub fn alloc<T: 'longer_than_self, F>(&self, op: F) -> &mut T
328         where F: FnOnce() -> T
329     {
330         unsafe {
331             if intrinsics::needs_drop::<T>() {
332                 self.alloc_noncopy(op)
333             } else {
334                 self.alloc_copy(op)
335             }
336         }
337     }
338
339     /// Allocates a slice of bytes of requested length. The bytes are not guaranteed to be zero
340     /// if the arena has previously been cleared.
341     ///
342     /// # Panics
343     ///
344     /// Panics if the requested length is too large and causes overflow.
345     pub fn alloc_bytes(&self, len: usize) -> &mut [u8] {
346         unsafe {
347             // Check for overflow.
348             self.copy_head.borrow().fill.get().checked_add(len).expect("length overflow");
349             let ptr = self.alloc_copy_inner(len, 1);
350             intrinsics::assume(!ptr.is_null());
351             slice::from_raw_parts_mut(ptr as *mut _, len)
352         }
353     }
354
355     /// Clears the arena. Deallocates all but the longest chunk which may be reused.
356     pub fn clear(&mut self) {
357         unsafe {
358             self.head.borrow().destroy();
359             self.head.borrow().fill.set(0);
360             self.copy_head.borrow().fill.set(0);
361             for chunk in self.chunks.borrow().iter() {
362                 if !chunk.is_copy.get() {
363                     chunk.destroy();
364                 }
365             }
366             self.chunks.borrow_mut().clear();
367         }
368     }
369 }
370
371 /// A faster arena that can hold objects of only one type.
372 pub struct TypedArena<T> {
373     /// A pointer to the next object to be allocated.
374     ptr: Cell<*mut T>,
375
376     /// A pointer to the end of the allocated area. When this pointer is
377     /// reached, a new chunk is allocated.
378     end: Cell<*mut T>,
379
380     /// A vector arena segments.
381     chunks: RefCell<Vec<TypedArenaChunk<T>>>,
382
383     /// Marker indicating that dropping the arena causes its owned
384     /// instances of `T` to be dropped.
385     _own: PhantomData<T>,
386 }
387
388 struct TypedArenaChunk<T> {
389     /// Pointer to the next arena segment.
390     storage: RawVec<T>,
391 }
392
393 impl<T> TypedArenaChunk<T> {
394     #[inline]
395     unsafe fn new(capacity: usize) -> TypedArenaChunk<T> {
396         TypedArenaChunk { storage: RawVec::with_capacity(capacity) }
397     }
398
399     /// Destroys this arena chunk.
400     #[inline]
401     unsafe fn destroy(&mut self, len: usize) {
402         // The branch on needs_drop() is an -O1 performance optimization.
403         // Without the branch, dropping TypedArena<u8> takes linear time.
404         if intrinsics::needs_drop::<T>() {
405             let mut start = self.start();
406             // Destroy all allocated objects.
407             for _ in 0..len {
408                 ptr::drop_in_place(start);
409                 start = start.offset(1);
410             }
411         }
412     }
413
414     // Returns a pointer to the first allocated object.
415     #[inline]
416     fn start(&self) -> *mut T {
417         self.storage.ptr()
418     }
419
420     // Returns a pointer to the end of the allocated space.
421     #[inline]
422     fn end(&self) -> *mut T {
423         unsafe {
424             if mem::size_of::<T>() == 0 {
425                 // A pointer as large as possible for zero-sized elements.
426                 !0 as *mut T
427             } else {
428                 self.start().offset(self.storage.cap() as isize)
429             }
430         }
431     }
432 }
433
434 const PAGE: usize = 4096;
435
436 impl<T> TypedArena<T> {
437     /// Creates a new `TypedArena` with preallocated space for many objects.
438     #[inline]
439     pub fn new() -> TypedArena<T> {
440         // Reserve at least one page.
441         let elem_size = cmp::max(1, mem::size_of::<T>());
442         TypedArena::with_capacity(PAGE / elem_size)
443     }
444
445     /// Creates a new `TypedArena` with preallocated space for the given number of
446     /// objects.
447     #[inline]
448     pub fn with_capacity(capacity: usize) -> TypedArena<T> {
449         unsafe {
450             let chunk = TypedArenaChunk::<T>::new(cmp::max(1, capacity));
451             TypedArena {
452                 ptr: Cell::new(chunk.start()),
453                 end: Cell::new(chunk.end()),
454                 chunks: RefCell::new(vec![chunk]),
455                 _own: PhantomData,
456             }
457         }
458     }
459
460     /// Allocates an object in the `TypedArena`, returning a reference to it.
461     #[inline]
462     pub fn alloc(&self, object: T) -> &mut T {
463         if self.ptr == self.end {
464             self.grow()
465         }
466
467         unsafe {
468             if mem::size_of::<T>() == 0 {
469                 self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T);
470                 let ptr = heap::EMPTY as *mut T;
471                 // Don't drop the object. This `write` is equivalent to `forget`.
472                 ptr::write(ptr, object);
473                 &mut *ptr
474             } else {
475                 let ptr = self.ptr.get();
476                 // Advance the pointer.
477                 self.ptr.set(self.ptr.get().offset(1));
478                 // Write into uninitialized memory.
479                 ptr::write(ptr, object);
480                 &mut *ptr
481             }
482         }
483     }
484
485     /// Grows the arena.
486     #[inline(never)]
487     #[cold]
488     fn grow(&self) {
489         unsafe {
490             let mut chunks = self.chunks.borrow_mut();
491             let prev_capacity = chunks.last().unwrap().storage.cap();
492             let new_capacity = prev_capacity.checked_mul(2).unwrap();
493             if chunks.last_mut().unwrap().storage.double_in_place() {
494                 self.end.set(chunks.last().unwrap().end());
495             } else {
496                 let chunk = TypedArenaChunk::<T>::new(new_capacity);
497                 self.ptr.set(chunk.start());
498                 self.end.set(chunk.end());
499                 chunks.push(chunk);
500             }
501         }
502     }
503     /// Clears the arena. Deallocates all but the longest chunk which may be reused.
504     pub fn clear(&mut self) {
505         unsafe {
506             // Clear the last chunk, which is partially filled.
507             let mut chunks_borrow = self.chunks.borrow_mut();
508             let last_idx = chunks_borrow.len() - 1;
509             self.clear_last_chunk(&mut chunks_borrow[last_idx]);
510             // If `T` is ZST, code below has no effect.
511             for mut chunk in chunks_borrow.drain(..last_idx) {
512                 let cap = chunk.storage.cap();
513                 chunk.destroy(cap);
514             }
515         }
516     }
517
518     // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other
519     // chunks.
520     fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk<T>) {
521         // Determine how much was filled.
522         let start = last_chunk.start() as usize;
523         // We obtain the value of the pointer to the first uninitialized element.
524         let end = self.ptr.get() as usize;
525         // We then calculate the number of elements to be dropped in the last chunk,
526         // which is the filled area's length.
527         let diff = if mem::size_of::<T>() == 0 {
528             // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
529             // the number of zero-sized values in the last and only chunk, just out of caution.
530             // Recall that `end` was incremented for each allocated value.
531             end - start
532         } else {
533             (end - start) / mem::size_of::<T>()
534         };
535         // Pass that to the `destroy` method.
536         unsafe {
537             last_chunk.destroy(diff);
538         }
539         // Reset the chunk.
540         self.ptr.set(last_chunk.start());
541     }
542 }
543
544 impl<T> Drop for TypedArena<T> {
545     #[unsafe_destructor_blind_to_params]
546     fn drop(&mut self) {
547         unsafe {
548             // Determine how much was filled.
549             let mut chunks_borrow = self.chunks.borrow_mut();
550             let mut last_chunk = chunks_borrow.pop().unwrap();
551             // Drop the contents of the last chunk.
552             self.clear_last_chunk(&mut last_chunk);
553             // The last chunk will be dropped. Destroy all other chunks.
554             for chunk in chunks_borrow.iter_mut() {
555                 let cap = chunk.storage.cap();
556                 chunk.destroy(cap);
557             }
558             // RawVec handles deallocation of `last_chunk` and `self.chunks`.
559         }
560     }
561 }
562
563 unsafe impl<T: Send> Send for TypedArena<T> {}
564
565 #[cfg(test)]
566 mod tests {
567     extern crate test;
568     use self::test::Bencher;
569     use super::{Arena, TypedArena};
570     use std::cell::Cell;
571     use std::rc::Rc;
572
573     #[allow(dead_code)]
574     #[derive(Debug, Eq, PartialEq)]
575     struct Point {
576         x: i32,
577         y: i32,
578         z: i32,
579     }
580
581     #[test]
582     fn test_arena_alloc_nested() {
583         struct Inner {
584             value: u8,
585         }
586         struct Outer<'a> {
587             inner: &'a Inner,
588         }
589         enum EI<'e> {
590             I(Inner),
591             O(Outer<'e>),
592         }
593
594         struct Wrap<'a>(TypedArena<EI<'a>>);
595
596         impl<'a> Wrap<'a> {
597             fn alloc_inner<F: Fn() -> Inner>(&self, f: F) -> &Inner {
598                 let r: &EI = self.0.alloc(EI::I(f()));
599                 if let &EI::I(ref i) = r {
600                     i
601                 } else {
602                     panic!("mismatch");
603                 }
604             }
605             fn alloc_outer<F: Fn() -> Outer<'a>>(&self, f: F) -> &Outer {
606                 let r: &EI = self.0.alloc(EI::O(f()));
607                 if let &EI::O(ref o) = r {
608                     o
609                 } else {
610                     panic!("mismatch");
611                 }
612             }
613         }
614
615         let arena = Wrap(TypedArena::new());
616
617         let result = arena.alloc_outer(|| {
618             Outer { inner: arena.alloc_inner(|| Inner { value: 10 }) }
619         });
620
621         assert_eq!(result.inner.value, 10);
622     }
623
624     #[test]
625     pub fn test_copy() {
626         let arena = TypedArena::new();
627         for _ in 0..100000 {
628             arena.alloc(Point { x: 1, y: 2, z: 3 });
629         }
630     }
631
632     #[bench]
633     pub fn bench_copy(b: &mut Bencher) {
634         let arena = TypedArena::new();
635         b.iter(|| arena.alloc(Point { x: 1, y: 2, z: 3 }))
636     }
637
638     #[bench]
639     pub fn bench_copy_nonarena(b: &mut Bencher) {
640         b.iter(|| {
641             let _: Box<_> = Box::new(Point { x: 1, y: 2, z: 3 });
642         })
643     }
644
645     #[bench]
646     pub fn bench_copy_old_arena(b: &mut Bencher) {
647         let arena = Arena::new();
648         b.iter(|| arena.alloc(|| Point { x: 1, y: 2, z: 3 }))
649     }
650
651     #[allow(dead_code)]
652     struct Noncopy {
653         string: String,
654         array: Vec<i32>,
655     }
656
657     #[test]
658     pub fn test_noncopy() {
659         let arena = TypedArena::new();
660         for _ in 0..100000 {
661             arena.alloc(Noncopy {
662                 string: "hello world".to_string(),
663                 array: vec![1, 2, 3, 4, 5],
664             });
665         }
666     }
667
668     #[test]
669     pub fn test_typed_arena_zero_sized() {
670         let arena = TypedArena::new();
671         for _ in 0..100000 {
672             arena.alloc(());
673         }
674     }
675
676     #[test]
677     pub fn test_arena_zero_sized() {
678         let arena = Arena::new();
679         let mut points = vec![];
680         for _ in 0..1000 {
681             for _ in 0..100 {
682                 arena.alloc(|| ());
683             }
684             let point = arena.alloc(|| Point { x: 1, y: 2, z: 3 });
685             points.push(point);
686         }
687         for point in &points {
688             assert_eq!(**point, Point { x: 1, y: 2, z: 3 });
689         }
690     }
691
692     #[test]
693     pub fn test_typed_arena_clear() {
694         let mut arena = TypedArena::new();
695         for _ in 0..10 {
696             arena.clear();
697             for _ in 0..10000 {
698                 arena.alloc(Point { x: 1, y: 2, z: 3 });
699             }
700         }
701     }
702
703     #[test]
704     pub fn test_arena_clear() {
705         let mut arena = Arena::new();
706         for _ in 0..10 {
707             arena.clear();
708             for _ in 0..10000 {
709                 arena.alloc(|| Point { x: 1, y: 2, z: 3 });
710                 arena.alloc(|| {
711                     Noncopy {
712                         string: "hello world".to_string(),
713                         array: vec![],
714                     }
715                 });
716             }
717         }
718     }
719
720     #[test]
721     pub fn test_arena_alloc_bytes() {
722         let arena = Arena::new();
723         for i in 0..10000 {
724             arena.alloc(|| Point { x: 1, y: 2, z: 3 });
725             for byte in arena.alloc_bytes(i % 42).iter_mut() {
726                 *byte = i as u8;
727             }
728         }
729     }
730
731     #[test]
732     fn test_arena_destructors() {
733         let arena = Arena::new();
734         for i in 0..10 {
735             // Arena allocate something with drop glue to make sure it
736             // doesn't leak.
737             arena.alloc(|| Rc::new(i));
738             // Allocate something with funny size and alignment, to keep
739             // things interesting.
740             arena.alloc(|| [0u8, 1u8, 2u8]);
741         }
742     }
743
744     #[test]
745     #[should_panic]
746     fn test_arena_destructors_fail() {
747         let arena = Arena::new();
748         // Put some stuff in the arena.
749         for i in 0..10 {
750             // Arena allocate something with drop glue to make sure it
751             // doesn't leak.
752             arena.alloc(|| Rc::new(i));
753             // Allocate something with funny size and alignment, to keep
754             // things interesting.
755             arena.alloc(|| [0u8, 1, 2]);
756         }
757         // Now, panic while allocating
758         arena.alloc::<Rc<i32>, _>(|| {
759             panic!();
760         });
761     }
762
763     // Drop tests
764
765     struct DropCounter<'a> {
766         count: &'a Cell<u32>,
767     }
768
769     impl<'a> Drop for DropCounter<'a> {
770         fn drop(&mut self) {
771             self.count.set(self.count.get() + 1);
772         }
773     }
774
775     #[test]
776     fn test_arena_drop_count() {
777         let counter = Cell::new(0);
778         {
779             let arena = Arena::new();
780             for _ in 0..100 {
781                 // Allocate something with drop glue to make sure it doesn't leak.
782                 arena.alloc(|| DropCounter { count: &counter });
783                 // Allocate something with funny size and alignment, to keep
784                 // things interesting.
785                 arena.alloc(|| [0u8, 1u8, 2u8]);
786             }
787             // dropping
788         };
789         assert_eq!(counter.get(), 100);
790     }
791
792     #[test]
793     fn test_arena_drop_on_clear() {
794         let counter = Cell::new(0);
795         for i in 0..10 {
796             let mut arena = Arena::new();
797             for _ in 0..100 {
798                 // Allocate something with drop glue to make sure it doesn't leak.
799                 arena.alloc(|| DropCounter { count: &counter });
800                 // Allocate something with funny size and alignment, to keep
801                 // things interesting.
802                 arena.alloc(|| [0u8, 1u8, 2u8]);
803             }
804             arena.clear();
805             assert_eq!(counter.get(), i * 100 + 100);
806         }
807     }
808
809     #[test]
810     fn test_typed_arena_drop_count() {
811         let counter = Cell::new(0);
812         {
813             let arena: TypedArena<DropCounter> = TypedArena::new();
814             for _ in 0..100 {
815                 // Allocate something with drop glue to make sure it doesn't leak.
816                 arena.alloc(DropCounter { count: &counter });
817             }
818         };
819         assert_eq!(counter.get(), 100);
820     }
821
822     #[test]
823     fn test_typed_arena_drop_on_clear() {
824         let counter = Cell::new(0);
825         let mut arena: TypedArena<DropCounter> = TypedArena::new();
826         for i in 0..10 {
827             for _ in 0..100 {
828                 // Allocate something with drop glue to make sure it doesn't leak.
829                 arena.alloc(DropCounter { count: &counter });
830             }
831             arena.clear();
832             assert_eq!(counter.get(), i * 100 + 100);
833         }
834     }
835
836     thread_local! {
837         static DROP_COUNTER: Cell<u32> = Cell::new(0)
838     }
839
840     struct SmallDroppable;
841
842     impl Drop for SmallDroppable {
843         fn drop(&mut self) {
844             DROP_COUNTER.with(|c| c.set(c.get() + 1));
845         }
846     }
847
848     #[test]
849     fn test_arena_drop_small_count() {
850         DROP_COUNTER.with(|c| c.set(0));
851         {
852             let arena = Arena::new();
853             for _ in 0..10 {
854                 for _ in 0..10 {
855                     // Allocate something with drop glue to make sure it doesn't leak.
856                     arena.alloc(|| SmallDroppable);
857                 }
858                 // Allocate something with funny size and alignment, to keep
859                 // things interesting.
860                 arena.alloc(|| [0u8, 1u8, 2u8]);
861             }
862             // dropping
863         };
864         assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
865     }
866
867     #[test]
868     fn test_typed_arena_drop_small_count() {
869         DROP_COUNTER.with(|c| c.set(0));
870         {
871             let arena: TypedArena<SmallDroppable> = TypedArena::new();
872             for _ in 0..100 {
873                 // Allocate something with drop glue to make sure it doesn't leak.
874                 arena.alloc(SmallDroppable);
875             }
876             // dropping
877         };
878         assert_eq!(DROP_COUNTER.with(|c| c.get()), 100);
879     }
880
881     #[bench]
882     pub fn bench_noncopy(b: &mut Bencher) {
883         let arena = TypedArena::new();
884         b.iter(|| {
885             arena.alloc(Noncopy {
886                 string: "hello world".to_string(),
887                 array: vec![1, 2, 3, 4, 5],
888             })
889         })
890     }
891
892     #[bench]
893     pub fn bench_noncopy_nonarena(b: &mut Bencher) {
894         b.iter(|| {
895             let _: Box<_> = Box::new(Noncopy {
896                 string: "hello world".to_string(),
897                 array: vec![1, 2, 3, 4, 5],
898             });
899         })
900     }
901
902     #[bench]
903     pub fn bench_noncopy_old_arena(b: &mut Bencher) {
904         let arena = Arena::new();
905         b.iter(|| {
906             arena.alloc(|| {
907                 Noncopy {
908                     string: "hello world".to_string(),
909                     array: vec![1, 2, 3, 4, 5],
910                 }
911             })
912         })
913     }
914 }