1515//! of individual objects while the arena itself is still alive. The benefit
1616//! of an arena is very fast allocation; just a pointer bump.
1717//!
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.
18+ //! This crate implements `TypedArena`, a simple arena that can only hold
19+ //! objects of a single type.
2120
2221#![ crate_name = "arena" ]
2322#![ unstable( feature = "rustc_private" , issue = "27812" ) ]
@@ -51,16 +50,19 @@ use std::ptr;
5150use alloc:: heap;
5251use alloc:: raw_vec:: RawVec ;
5352
54- /// A faster arena that can hold objects of only one type.
53+ /// An arena that can hold objects of only one type.
5554pub struct TypedArena < T > {
55+ /// The capacity of the first chunk (once it is allocated).
56+ first_chunk_capacity : usize ,
57+
5658 /// A pointer to the next object to be allocated.
5759 ptr : Cell < * mut T > ,
5860
5961 /// A pointer to the end of the allocated area. When this pointer is
6062 /// reached, a new chunk is allocated.
6163 end : Cell < * mut T > ,
6264
63- /// A vector arena segments .
65+ /// A vector of arena chunks .
6466 chunks : RefCell < Vec < TypedArenaChunk < T > > > ,
6567
6668 /// Marker indicating that dropping the arena causes its owned
@@ -69,7 +71,7 @@ pub struct TypedArena<T> {
6971}
7072
7173struct TypedArenaChunk < T > {
72- /// Pointer to the next arena segment .
74+ /// The raw storage for the arena chunk .
7375 storage : RawVec < T > ,
7476}
7577
@@ -117,26 +119,26 @@ impl<T> TypedArenaChunk<T> {
117119const PAGE : usize = 4096 ;
118120
119121impl < T > TypedArena < T > {
120- /// Creates a new `TypedArena` with preallocated space for many objects .
122+ /// Creates a new `TypedArena`.
121123 #[ inline]
122124 pub fn new ( ) -> TypedArena < T > {
123125 // Reserve at least one page.
124126 let elem_size = cmp:: max ( 1 , mem:: size_of :: < T > ( ) ) ;
125127 TypedArena :: with_capacity ( PAGE / elem_size)
126128 }
127129
128- /// Creates a new `TypedArena` with preallocated space for the given number of
129- /// objects.
130+ /// Creates a new `TypedArena`. Each chunk used within the arena will have
131+ /// space for at least the given number of objects.
130132 #[ inline]
131133 pub fn with_capacity ( capacity : usize ) -> TypedArena < T > {
132- unsafe {
133- let chunk = TypedArenaChunk :: < T > :: new ( cmp:: max ( 1 , capacity) ) ;
134- TypedArena {
135- ptr : Cell :: new ( chunk . start ( ) ) ,
136- end : Cell :: new ( chunk . end ( ) ) ,
137- chunks : RefCell :: new ( vec ! [ chunk ] ) ,
138- _own : PhantomData ,
139- }
134+ TypedArena {
135+ first_chunk_capacity : cmp:: max ( 1 , capacity) ,
136+ // We set both `ptr` and `end` to 0 so that the first call to
137+ // alloc() will trigger a grow().
138+ ptr : Cell :: new ( 0 as * mut T ) ,
139+ end : Cell :: new ( 0 as * mut T ) ,
140+ chunks : RefCell :: new ( vec ! [ ] ) ,
141+ _own : PhantomData ,
140142 }
141143 }
142144
@@ -171,29 +173,37 @@ impl<T> TypedArena<T> {
171173 fn grow ( & self ) {
172174 unsafe {
173175 let mut chunks = self . chunks . borrow_mut ( ) ;
174- let prev_capacity = chunks. last ( ) . unwrap ( ) . storage . cap ( ) ;
175- let new_capacity = prev_capacity. checked_mul ( 2 ) . unwrap ( ) ;
176- if chunks. last_mut ( ) . unwrap ( ) . storage . double_in_place ( ) {
177- self . end . set ( chunks. last ( ) . unwrap ( ) . end ( ) ) ;
176+ let ( chunk, new_capacity) ;
177+ if let Some ( last_chunk) = chunks. last_mut ( ) {
178+ if last_chunk. storage . double_in_place ( ) {
179+ self . end . set ( last_chunk. end ( ) ) ;
180+ return ;
181+ } else {
182+ let prev_capacity = last_chunk. storage . cap ( ) ;
183+ new_capacity = prev_capacity. checked_mul ( 2 ) . unwrap ( ) ;
184+ }
178185 } else {
179- let chunk = TypedArenaChunk :: < T > :: new ( new_capacity) ;
180- self . ptr . set ( chunk. start ( ) ) ;
181- self . end . set ( chunk. end ( ) ) ;
182- chunks. push ( chunk) ;
186+ new_capacity = self . first_chunk_capacity ;
183187 }
188+ chunk = TypedArenaChunk :: < T > :: new ( new_capacity) ;
189+ self . ptr . set ( chunk. start ( ) ) ;
190+ self . end . set ( chunk. end ( ) ) ;
191+ chunks. push ( chunk) ;
184192 }
185193 }
186194 /// Clears the arena. Deallocates all but the longest chunk which may be reused.
187195 pub fn clear ( & mut self ) {
188196 unsafe {
189197 // Clear the last chunk, which is partially filled.
190198 let mut chunks_borrow = self . chunks . borrow_mut ( ) ;
191- let last_idx = chunks_borrow. len ( ) - 1 ;
192- self . clear_last_chunk ( & mut chunks_borrow[ last_idx] ) ;
193- // If `T` is ZST, code below has no effect.
194- for mut chunk in chunks_borrow. drain ( ..last_idx) {
195- let cap = chunk. storage . cap ( ) ;
196- chunk. destroy ( cap) ;
199+ if let Some ( mut last_chunk) = chunks_borrow. pop ( ) {
200+ self . clear_last_chunk ( & mut last_chunk) ;
201+ // If `T` is ZST, code below has no effect.
202+ for mut chunk in chunks_borrow. drain ( ..) {
203+ let cap = chunk. storage . cap ( ) ;
204+ chunk. destroy ( cap) ;
205+ }
206+ chunks_borrow. push ( last_chunk) ;
197207 }
198208 }
199209 }
@@ -230,13 +240,14 @@ impl<T> Drop for TypedArena<T> {
230240 unsafe {
231241 // Determine how much was filled.
232242 let mut chunks_borrow = self . chunks . borrow_mut ( ) ;
233- let mut last_chunk = chunks_borrow. pop ( ) . unwrap ( ) ;
234- // Drop the contents of the last chunk.
235- self . clear_last_chunk ( & mut last_chunk) ;
236- // The last chunk will be dropped. Destroy all other chunks.
237- for chunk in chunks_borrow. iter_mut ( ) {
238- let cap = chunk. storage . cap ( ) ;
239- chunk. destroy ( cap) ;
243+ if let Some ( mut last_chunk) = chunks_borrow. pop ( ) {
244+ // Drop the contents of the last chunk.
245+ self . clear_last_chunk ( & mut last_chunk) ;
246+ // The last chunk will be dropped. Destroy all other chunks.
247+ for chunk in chunks_borrow. iter_mut ( ) {
248+ let cap = chunk. storage . cap ( ) ;
249+ chunk. destroy ( cap) ;
250+ }
240251 }
241252 // RawVec handles deallocation of `last_chunk` and `self.chunks`.
242253 }
@@ -260,6 +271,12 @@ mod tests {
260271 z : i32 ,
261272 }
262273
274+ #[ test]
275+ pub fn test_unused ( ) {
276+ let arena: TypedArena < Point > = TypedArena :: new ( ) ;
277+ assert ! ( arena. chunks. borrow( ) . is_empty( ) ) ;
278+ }
279+
263280 #[ test]
264281 fn test_arena_alloc_nested ( ) {
265282 struct Inner {
0 commit comments