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