15
15
//! of individual objects while the arena itself is still alive. The benefit
16
16
//! of an arena is very fast allocation; just a pointer bump.
17
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.
18
+ //! This crate implements `TypedArena`, a simple arena that can only hold
19
+ //! objects of a single type.
21
20
22
21
#![ crate_name = "arena" ]
23
22
#![ unstable( feature = "rustc_private" , issue = "27812" ) ]
@@ -51,16 +50,19 @@ use std::ptr;
51
50
use alloc:: heap;
52
51
use alloc:: raw_vec:: RawVec ;
53
52
54
- /// A faster arena that can hold objects of only one type.
53
+ /// An arena that can hold objects of only one type.
55
54
pub struct TypedArena < T > {
55
+ /// The capacity of the first chunk (once it is allocated).
56
+ first_chunk_capacity : usize ,
57
+
56
58
/// A pointer to the next object to be allocated.
57
59
ptr : Cell < * mut T > ,
58
60
59
61
/// A pointer to the end of the allocated area. When this pointer is
60
62
/// reached, a new chunk is allocated.
61
63
end : Cell < * mut T > ,
62
64
63
- /// A vector arena segments .
65
+ /// A vector of arena chunks .
64
66
chunks : RefCell < Vec < TypedArenaChunk < T > > > ,
65
67
66
68
/// Marker indicating that dropping the arena causes its owned
@@ -69,7 +71,7 @@ pub struct TypedArena<T> {
69
71
}
70
72
71
73
struct TypedArenaChunk < T > {
72
- /// Pointer to the next arena segment .
74
+ /// The raw storage for the arena chunk .
73
75
storage : RawVec < T > ,
74
76
}
75
77
@@ -117,26 +119,26 @@ impl<T> TypedArenaChunk<T> {
117
119
const PAGE : usize = 4096 ;
118
120
119
121
impl < T > TypedArena < T > {
120
- /// Creates a new `TypedArena` with preallocated space for many objects .
122
+ /// Creates a new `TypedArena`.
121
123
#[ inline]
122
124
pub fn new ( ) -> TypedArena < T > {
123
125
// Reserve at least one page.
124
126
let elem_size = cmp:: max ( 1 , mem:: size_of :: < T > ( ) ) ;
125
127
TypedArena :: with_capacity ( PAGE / elem_size)
126
128
}
127
129
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.
130
132
#[ inline]
131
133
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 ,
140
142
}
141
143
}
142
144
@@ -171,29 +173,37 @@ impl<T> TypedArena<T> {
171
173
fn grow ( & self ) {
172
174
unsafe {
173
175
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
+ }
178
185
} 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 ;
183
187
}
188
+ chunk = TypedArenaChunk :: < T > :: new ( new_capacity) ;
189
+ self . ptr . set ( chunk. start ( ) ) ;
190
+ self . end . set ( chunk. end ( ) ) ;
191
+ chunks. push ( chunk) ;
184
192
}
185
193
}
186
194
/// Clears the arena. Deallocates all but the longest chunk which may be reused.
187
195
pub fn clear ( & mut self ) {
188
196
unsafe {
189
197
// Clear the last chunk, which is partially filled.
190
198
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) ;
197
207
}
198
208
}
199
209
}
@@ -230,13 +240,14 @@ impl<T> Drop for TypedArena<T> {
230
240
unsafe {
231
241
// Determine how much was filled.
232
242
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
+ }
240
251
}
241
252
// RawVec handles deallocation of `last_chunk` and `self.chunks`.
242
253
}
@@ -260,6 +271,12 @@ mod tests {
260
271
z : i32 ,
261
272
}
262
273
274
+ #[ test]
275
+ pub fn test_unused ( ) {
276
+ let arena: TypedArena < Point > = TypedArena :: new ( ) ;
277
+ assert ! ( arena. chunks. borrow( ) . is_empty( ) ) ;
278
+ }
279
+
263
280
#[ test]
264
281
fn test_arena_alloc_nested ( ) {
265
282
struct Inner {
0 commit comments