/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-egraph-0.91.1/src/bumpvec.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Vectors allocated in arenas, with small per-vector overhead. |
2 | | |
3 | | use std::marker::PhantomData; |
4 | | use std::mem::MaybeUninit; |
5 | | use std::ops::Range; |
6 | | |
7 | | /// A vector of `T` stored within a `BumpArena`. |
8 | | /// |
9 | | /// This is something like a normal `Vec`, except that all accesses |
10 | | /// and updates require a separate borrow of the `BumpArena`. This, in |
11 | | /// turn, makes the Vec itself very compact: only three `u32`s (12 |
12 | | /// bytes). The `BumpSlice` variant is only two `u32`s (8 bytes) and |
13 | | /// is sufficient to reconstruct a slice, but not grow the vector. |
14 | | /// |
15 | | /// The `BumpVec` does *not* implement `Clone` or `Copy`; it |
16 | | /// represents unique ownership of a range of indices in the arena. If |
17 | | /// dropped, those indices will be unavailable until the arena is |
18 | | /// freed. This is "fine" (it is normally how arena allocation |
19 | | /// works). To explicitly free and make available for some |
20 | | /// allocations, a very rudimentary reuse mechanism exists via |
21 | | /// `BumpVec::free(arena)`. (The allocation path opportunistically |
22 | | /// checks the first range on the freelist, and can carve off a piece |
23 | | /// of it if larger than needed, but it does not attempt to traverse |
24 | | /// the entire freelist; this is a compromise between bump-allocation |
25 | | /// speed and memory efficiency, which also influences speed through |
26 | | /// cached-memory reuse.) |
27 | | /// |
28 | | /// The type `T` should not have a `Drop` implementation. This |
29 | | /// typically means that it does not own any boxed memory, |
30 | | /// sub-collections, or other resources. This is important for the |
31 | | /// efficiency of the data structure (otherwise, to call `Drop` impls, |
32 | | /// the arena needs to track which indices are live or dead; the |
33 | | /// BumpVec itself cannot do the drop because it does not retain a |
34 | | /// reference to the arena). Note that placing a `T` with a `Drop` |
35 | | /// impl in the arena is still *safe*, because leaking (that is, never |
36 | | /// calling `Drop::drop()`) is safe. It is merely less efficient, and |
37 | | /// so should be avoided if possible. |
38 | | #[derive(Debug)] |
39 | | pub struct BumpVec<T> { |
40 | | base: u32, |
41 | | len: u32, |
42 | | cap: u32, |
43 | | _phantom: PhantomData<T>, |
44 | | } |
45 | | |
46 | | /// A slice in an arena: like a `BumpVec`, but has a fixed size that |
47 | | /// cannot grow. The size of this struct is one 32-bit word smaller |
48 | | /// than `BumpVec`. It is copyable/cloneable because it will never be |
49 | | /// freed. |
50 | | #[derive(Debug, Clone, Copy)] |
51 | | pub struct BumpSlice<T> { |
52 | | base: u32, |
53 | | len: u32, |
54 | | _phantom: PhantomData<T>, |
55 | | } |
56 | | |
57 | | #[derive(Default)] |
58 | | pub struct BumpArena<T> { |
59 | | vec: Vec<MaybeUninit<T>>, |
60 | | freelist: Vec<Range<u32>>, |
61 | | } |
62 | | |
63 | | impl<T> BumpArena<T> { |
64 | | /// Create a new arena into which one can allocate `BumpVec`s. |
65 | 0 | pub fn new() -> Self { |
66 | 0 | Self { |
67 | 0 | vec: vec![], |
68 | 0 | freelist: vec![], |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | | /// Create a new arena, pre-allocating space for `cap` total `T` |
73 | | /// elements. |
74 | 0 | pub fn arena_with_capacity(cap: usize) -> Self { |
75 | 0 | Self { |
76 | 0 | vec: Vec::with_capacity(cap), |
77 | 0 | freelist: Vec::with_capacity(cap / 16), |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | /// Create a new `BumpVec` with the given pre-allocated capacity |
82 | | /// and zero length. |
83 | 0 | pub fn vec_with_capacity(&mut self, cap: usize) -> BumpVec<T> { |
84 | 0 | let cap = u32::try_from(cap).unwrap(); |
85 | 0 | if let Some(range) = self.maybe_freelist_alloc(cap) { |
86 | 0 | BumpVec { |
87 | 0 | base: range.start, |
88 | 0 | len: 0, |
89 | 0 | cap, |
90 | 0 | _phantom: PhantomData, |
91 | 0 | } |
92 | | } else { |
93 | 0 | let base = self.vec.len() as u32; |
94 | 0 | for _ in 0..cap { |
95 | 0 | self.vec.push(MaybeUninit::uninit()); |
96 | 0 | } |
97 | 0 | BumpVec { |
98 | 0 | base, |
99 | 0 | len: 0, |
100 | 0 | cap, |
101 | 0 | _phantom: PhantomData, |
102 | 0 | } |
103 | | } |
104 | 0 | } |
105 | | |
106 | | /// Create a new `BumpVec` with a single element. The capacity is |
107 | | /// also only one element; growing the vector further will require |
108 | | /// a reallocation. |
109 | 0 | pub fn single(&mut self, t: T) -> BumpVec<T> { |
110 | 0 | let mut vec = self.vec_with_capacity(1); |
111 | 0 | unsafe { |
112 | 0 | self.write_into_index(vec.base, t); |
113 | 0 | } |
114 | 0 | vec.len = 1; |
115 | 0 | vec |
116 | 0 | } |
117 | | |
118 | | /// Create a new `BumpVec` with the sequence from an iterator. |
119 | 0 | pub fn from_iter<I: Iterator<Item = T>>(&mut self, i: I) -> BumpVec<T> { |
120 | 0 | let base = self.vec.len() as u32; |
121 | 0 | self.vec.extend(i.map(|item| MaybeUninit::new(item))); |
122 | 0 | let len = self.vec.len() as u32 - base; |
123 | 0 | BumpVec { |
124 | 0 | base, |
125 | 0 | len, |
126 | 0 | cap: len, |
127 | 0 | _phantom: PhantomData, |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | /// Append two `BumpVec`s, returning a new one. Consumes both |
132 | | /// vectors. This will use the capacity at the end of `a` if |
133 | | /// possible to move `b`'s elements into place; otherwise it will |
134 | | /// need to allocate new space. |
135 | 0 | pub fn append(&mut self, a: BumpVec<T>, b: BumpVec<T>) -> BumpVec<T> { |
136 | 0 | if (a.cap - a.len) >= b.len { |
137 | 0 | self.append_into_cap(a, b) |
138 | | } else { |
139 | 0 | self.append_into_new(a, b) |
140 | | } |
141 | 0 | } |
142 | | |
143 | | /// Helper: read the `T` out of a given arena index. After |
144 | | /// reading, that index becomes uninitialized. |
145 | 0 | unsafe fn read_out_of_index(&self, index: u32) -> T { |
146 | 0 | // Note that we don't actually *track* uninitialized status |
147 | 0 | // (and this is fine because we will never `Drop` and we never |
148 | 0 | // allow a `BumpVec` to refer to an uninitialized index, so |
149 | 0 | // the bits are effectively dead). We simply read the bits out |
150 | 0 | // and return them. |
151 | 0 | self.vec[index as usize].as_ptr().read() |
152 | 0 | } |
153 | | |
154 | | /// Helper: write a `T` into the given arena index. Index must |
155 | | /// have been uninitialized previously. |
156 | 0 | unsafe fn write_into_index(&mut self, index: u32, t: T) { |
157 | 0 | self.vec[index as usize].as_mut_ptr().write(t); |
158 | 0 | } |
159 | | |
160 | | /// Helper: move a `T` from one index to another. Old index |
161 | | /// becomes uninitialized and new index must have previously been |
162 | | /// uninitialized. |
163 | 0 | unsafe fn move_item(&mut self, from: u32, to: u32) { |
164 | 0 | let item = self.read_out_of_index(from); |
165 | 0 | self.write_into_index(to, item); |
166 | 0 | } |
167 | | |
168 | | /// Helper: push a `T` onto the end of the arena, growing its |
169 | | /// storage. The `T` to push is read out of another index, and |
170 | | /// that index subsequently becomes uninitialized. |
171 | 0 | unsafe fn push_item(&mut self, from: u32) -> u32 { |
172 | 0 | let index = self.vec.len() as u32; |
173 | 0 | let item = self.read_out_of_index(from); |
174 | 0 | self.vec.push(MaybeUninit::new(item)); |
175 | 0 | index |
176 | 0 | } |
177 | | |
178 | | /// Helper: append `b` into the capacity at the end of `a`. |
179 | 0 | fn append_into_cap(&mut self, mut a: BumpVec<T>, b: BumpVec<T>) -> BumpVec<T> { |
180 | 0 | debug_assert!(a.cap - a.len >= b.len); |
181 | 0 | for i in 0..b.len { |
182 | | // Safety: initially, the indices in `b` are initialized; |
183 | | // the indices in `a`'s cap, beyond its length, are |
184 | | // uninitialized. We move the initialized contents from |
185 | | // `b` to the tail beyond `a`, and we consume `b` (so it |
186 | | // no longer exists), and we update `a`'s length to cover |
187 | | // the initialized contents in their new location. |
188 | 0 | unsafe { |
189 | 0 | self.move_item(b.base + i, a.base + a.len + i); |
190 | 0 | } |
191 | | } |
192 | 0 | a.len += b.len; |
193 | 0 | b.free(self); |
194 | 0 | a |
195 | 0 | } |
196 | | |
197 | | /// Helper: return a range of indices that are available |
198 | | /// (uninitialized) according to the freelist for `len` elements, |
199 | | /// if possible. |
200 | 0 | fn maybe_freelist_alloc(&mut self, len: u32) -> Option<Range<u32>> { |
201 | 0 | if let Some(entry) = self.freelist.last_mut() { |
202 | 0 | if entry.len() >= len as usize { |
203 | 0 | let base = entry.start; |
204 | 0 | entry.start += len; |
205 | 0 | if entry.start == entry.end { |
206 | 0 | self.freelist.pop(); |
207 | 0 | } |
208 | 0 | return Some(base..(base + len)); |
209 | 0 | } |
210 | 0 | } |
211 | 0 | None |
212 | 0 | } |
213 | | |
214 | | /// Helper: append `a` and `b` into a completely new allocation. |
215 | 0 | fn append_into_new(&mut self, a: BumpVec<T>, b: BumpVec<T>) -> BumpVec<T> { |
216 | 0 | // New capacity: round up to a power of two. |
217 | 0 | let len = a.len + b.len; |
218 | 0 | let cap = round_up_power_of_two(len); |
219 | | |
220 | 0 | if let Some(range) = self.maybe_freelist_alloc(cap) { |
221 | 0 | for i in 0..a.len { |
222 | | // Safety: the indices in `a` must be initialized. We read |
223 | | // out the item and copy it to a new index; the old index |
224 | | // is no longer covered by a BumpVec, because we consume |
225 | | // `a`. |
226 | 0 | unsafe { |
227 | 0 | self.move_item(a.base + i, range.start + i); |
228 | 0 | } |
229 | | } |
230 | 0 | for i in 0..b.len { |
231 | | // Safety: the indices in `b` must be initialized. We read |
232 | | // out the item and copy it to a new index; the old index |
233 | | // is no longer covered by a BumpVec, because we consume |
234 | | // `b`. |
235 | 0 | unsafe { |
236 | 0 | self.move_item(b.base + i, range.start + a.len + i); |
237 | 0 | } |
238 | | } |
239 | | |
240 | 0 | a.free(self); |
241 | 0 | b.free(self); |
242 | 0 |
|
243 | 0 | BumpVec { |
244 | 0 | base: range.start, |
245 | 0 | len, |
246 | 0 | cap, |
247 | 0 | _phantom: PhantomData, |
248 | 0 | } |
249 | | } else { |
250 | 0 | self.vec.reserve(cap as usize); |
251 | 0 | let base = self.vec.len() as u32; |
252 | 0 | for i in 0..a.len { |
253 | | // Safety: the indices in `a` must be initialized. We read |
254 | | // out the item and copy it to a new index; the old index |
255 | | // is no longer covered by a BumpVec, because we consume |
256 | | // `a`. |
257 | 0 | unsafe { |
258 | 0 | self.push_item(a.base + i); |
259 | 0 | } |
260 | | } |
261 | 0 | for i in 0..b.len { |
262 | | // Safety: the indices in `b` must be initialized. We read |
263 | | // out the item and copy it to a new index; the old index |
264 | | // is no longer covered by a BumpVec, because we consume |
265 | | // `b`. |
266 | 0 | unsafe { |
267 | 0 | self.push_item(b.base + i); |
268 | 0 | } |
269 | | } |
270 | 0 | let len = self.vec.len() as u32 - base; |
271 | 0 |
|
272 | 0 | for _ in len..cap { |
273 | 0 | self.vec.push(MaybeUninit::uninit()); |
274 | 0 | } |
275 | | |
276 | 0 | a.free(self); |
277 | 0 | b.free(self); |
278 | 0 |
|
279 | 0 | BumpVec { |
280 | 0 | base, |
281 | 0 | len, |
282 | 0 | cap, |
283 | 0 | _phantom: PhantomData, |
284 | 0 | } |
285 | | } |
286 | 0 | } |
287 | | |
288 | | /// Returns the size of the backing `Vec`. |
289 | 0 | pub fn size(&self) -> usize { |
290 | 0 | self.vec.len() |
291 | 0 | } |
292 | | } |
293 | | |
294 | 0 | fn round_up_power_of_two(x: u32) -> u32 { |
295 | 0 | debug_assert!(x > 0); |
296 | 0 | debug_assert!(x < 0x8000_0000); |
297 | 0 | let log2 = 32 - (x - 1).leading_zeros(); |
298 | 0 | 1 << log2 |
299 | 0 | } |
300 | | |
301 | | impl<T> BumpVec<T> { |
302 | | /// Returns a slice view of this `BumpVec`, given a borrow of the |
303 | | /// arena. |
304 | 0 | pub fn as_slice<'a>(&'a self, arena: &'a BumpArena<T>) -> &'a [T] { |
305 | 0 | let maybe_uninit_slice = |
306 | 0 | &arena.vec[(self.base as usize)..((self.base + self.len) as usize)]; |
307 | 0 | // Safety: the index range we represent must be initialized. |
308 | 0 | unsafe { std::mem::transmute(maybe_uninit_slice) } |
309 | 0 | } |
310 | | |
311 | | /// Returns a mutable slice view of this `BumpVec`, given a |
312 | | /// mutable borrow of the arena. |
313 | 0 | pub fn as_mut_slice<'a>(&'a mut self, arena: &'a mut BumpArena<T>) -> &'a mut [T] { |
314 | 0 | let maybe_uninit_slice = |
315 | 0 | &mut arena.vec[(self.base as usize)..((self.base + self.len) as usize)]; |
316 | 0 | // Safety: the index range we represent must be initialized. |
317 | 0 | unsafe { std::mem::transmute(maybe_uninit_slice) } |
318 | 0 | } |
319 | | |
320 | | /// Returns the length of this vector. Does not require access to |
321 | | /// the arena. |
322 | 0 | pub fn len(&self) -> usize { |
323 | 0 | self.len as usize |
324 | 0 | } |
325 | | |
326 | | /// Returns the capacity of this vector. Does not require access |
327 | | /// to the arena. |
328 | 0 | pub fn cap(&self) -> usize { |
329 | 0 | self.cap as usize |
330 | 0 | } |
331 | | |
332 | | /// Reserve `extra_len` capacity at the end of the vector, |
333 | | /// reallocating if necessary. |
334 | 0 | pub fn reserve(&mut self, extra_len: usize, arena: &mut BumpArena<T>) { |
335 | 0 | let extra_len = u32::try_from(extra_len).unwrap(); |
336 | 0 | if self.cap - self.len < extra_len { |
337 | 0 | if self.base + self.cap == arena.vec.len() as u32 { |
338 | 0 | for _ in 0..extra_len { |
339 | 0 | arena.vec.push(MaybeUninit::uninit()); |
340 | 0 | } |
341 | 0 | self.cap += extra_len; |
342 | | } else { |
343 | 0 | let new_cap = self.cap + extra_len; |
344 | 0 | let new = arena.vec_with_capacity(new_cap as usize); |
345 | | unsafe { |
346 | 0 | for i in 0..self.len { |
347 | 0 | arena.move_item(self.base + i, new.base + i); |
348 | 0 | } |
349 | | } |
350 | 0 | self.base = new.base; |
351 | 0 | self.cap = new.cap; |
352 | | } |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | | /// Push an item, growing the capacity if needed. |
357 | 0 | pub fn push(&mut self, t: T, arena: &mut BumpArena<T>) { |
358 | 0 | if self.cap > self.len { |
359 | 0 | unsafe { |
360 | 0 | arena.write_into_index(self.base + self.len, t); |
361 | 0 | } |
362 | 0 | self.len += 1; |
363 | 0 | } else if (self.base + self.cap) as usize == arena.vec.len() { |
364 | 0 | arena.vec.push(MaybeUninit::new(t)); |
365 | 0 | self.cap += 1; |
366 | 0 | self.len += 1; |
367 | 0 | } else { |
368 | 0 | let new_cap = round_up_power_of_two(self.cap + 1); |
369 | 0 | let extra = new_cap - self.cap; |
370 | 0 | self.reserve(extra as usize, arena); |
371 | 0 | unsafe { |
372 | 0 | arena.write_into_index(self.base + self.len, t); |
373 | 0 | } |
374 | 0 | self.len += 1; |
375 | 0 | } |
376 | 0 | } |
377 | | |
378 | | /// Clone, if `T` is cloneable. |
379 | 0 | pub fn clone(&self, arena: &mut BumpArena<T>) -> BumpVec<T> |
380 | 0 | where |
381 | 0 | T: Clone, |
382 | 0 | { |
383 | 0 | let mut new = arena.vec_with_capacity(self.len as usize); |
384 | 0 | for i in 0..self.len { |
385 | 0 | let item = self.as_slice(arena)[i as usize].clone(); |
386 | 0 | new.push(item, arena); |
387 | 0 | } |
388 | 0 | new |
389 | 0 | } |
390 | | |
391 | | /// Truncate the length to a smaller-or-equal length. |
392 | 0 | pub fn truncate(&mut self, len: usize) { |
393 | 0 | let len = len as u32; |
394 | 0 | assert!(len <= self.len); |
395 | 0 | self.len = len; |
396 | 0 | } |
397 | | |
398 | | /// Consume the BumpVec and return its indices to a free pool in |
399 | | /// the arena. |
400 | 0 | pub fn free(self, arena: &mut BumpArena<T>) { |
401 | 0 | arena.freelist.push(self.base..(self.base + self.cap)); |
402 | 0 | } |
403 | | |
404 | | /// Freeze the capacity of this BumpVec, turning it into a slice, |
405 | | /// for a smaller struct (8 bytes rather than 12). Once this |
406 | | /// exists, it is copyable, because the slice will never be freed. |
407 | 0 | pub fn freeze(self, arena: &mut BumpArena<T>) -> BumpSlice<T> { |
408 | 0 | if self.cap > self.len { |
409 | 0 | arena |
410 | 0 | .freelist |
411 | 0 | .push((self.base + self.len)..(self.base + self.cap)); |
412 | 0 | } |
413 | 0 | BumpSlice { |
414 | 0 | base: self.base, |
415 | 0 | len: self.len, |
416 | 0 | _phantom: PhantomData, |
417 | 0 | } |
418 | 0 | } |
419 | | } |
420 | | |
421 | | impl<T> BumpSlice<T> { |
422 | | /// Returns a slice view of the `BumpSlice`, given a borrow of the |
423 | | /// arena. |
424 | 0 | pub fn as_slice<'a>(&'a self, arena: &'a BumpArena<T>) -> &'a [T] { |
425 | 0 | let maybe_uninit_slice = |
426 | 0 | &arena.vec[(self.base as usize)..((self.base + self.len) as usize)]; |
427 | 0 | // Safety: the index range we represent must be initialized. |
428 | 0 | unsafe { std::mem::transmute(maybe_uninit_slice) } |
429 | 0 | } |
430 | | |
431 | | /// Returns a mutable slice view of the `BumpSlice`, given a |
432 | | /// mutable borrow of the arena. |
433 | 0 | pub fn as_mut_slice<'a>(&'a mut self, arena: &'a mut BumpArena<T>) -> &'a mut [T] { |
434 | 0 | let maybe_uninit_slice = |
435 | 0 | &mut arena.vec[(self.base as usize)..((self.base + self.len) as usize)]; |
436 | 0 | // Safety: the index range we represent must be initialized. |
437 | 0 | unsafe { std::mem::transmute(maybe_uninit_slice) } |
438 | 0 | } |
439 | | |
440 | | /// Returns the length of the `BumpSlice`. |
441 | 0 | pub fn len(&self) -> usize { |
442 | 0 | self.len as usize |
443 | 0 | } |
444 | | } |
445 | | |
446 | | impl<T> std::default::Default for BumpVec<T> { |
447 | 0 | fn default() -> Self { |
448 | 0 | BumpVec { |
449 | 0 | base: 0, |
450 | 0 | len: 0, |
451 | 0 | cap: 0, |
452 | 0 | _phantom: PhantomData, |
453 | 0 | } |
454 | 0 | } |
455 | | } |
456 | | |
457 | | impl<T> std::default::Default for BumpSlice<T> { |
458 | 0 | fn default() -> Self { |
459 | 0 | BumpSlice { |
460 | 0 | base: 0, |
461 | 0 | len: 0, |
462 | 0 | _phantom: PhantomData, |
463 | 0 | } |
464 | 0 | } |
465 | | } |
466 | | |
467 | | #[cfg(test)] |
468 | | mod test { |
469 | | use super::*; |
470 | | |
471 | | #[test] |
472 | | fn test_round_up() { |
473 | | assert_eq!(1, round_up_power_of_two(1)); |
474 | | assert_eq!(2, round_up_power_of_two(2)); |
475 | | assert_eq!(4, round_up_power_of_two(3)); |
476 | | assert_eq!(4, round_up_power_of_two(4)); |
477 | | assert_eq!(32, round_up_power_of_two(24)); |
478 | | assert_eq!(0x8000_0000, round_up_power_of_two(0x7fff_ffff)); |
479 | | } |
480 | | |
481 | | #[test] |
482 | | fn test_basic() { |
483 | | let mut arena: BumpArena<u32> = BumpArena::new(); |
484 | | |
485 | | let a = arena.single(1); |
486 | | let b = arena.single(2); |
487 | | let c = arena.single(3); |
488 | | let ab = arena.append(a, b); |
489 | | assert_eq!(ab.as_slice(&arena), &[1, 2]); |
490 | | assert_eq!(ab.cap(), 2); |
491 | | let abc = arena.append(ab, c); |
492 | | assert_eq!(abc.len(), 3); |
493 | | assert_eq!(abc.cap(), 4); |
494 | | assert_eq!(abc.as_slice(&arena), &[1, 2, 3]); |
495 | | assert_eq!(arena.size(), 9); |
496 | | let mut d = arena.single(4); |
497 | | // Should have reused the freelist. |
498 | | assert_eq!(arena.size(), 9); |
499 | | assert_eq!(d.len(), 1); |
500 | | assert_eq!(d.cap(), 1); |
501 | | assert_eq!(d.as_slice(&arena), &[4]); |
502 | | d.as_mut_slice(&mut arena)[0] = 5; |
503 | | assert_eq!(d.as_slice(&arena), &[5]); |
504 | | abc.free(&mut arena); |
505 | | let d2 = d.clone(&mut arena); |
506 | | let dd = arena.append(d, d2); |
507 | | // Should have reused the freelist. |
508 | | assert_eq!(arena.size(), 9); |
509 | | assert_eq!(dd.as_slice(&arena), &[5, 5]); |
510 | | let mut e = arena.from_iter([10, 11, 12].into_iter()); |
511 | | e.push(13, &mut arena); |
512 | | assert_eq!(arena.size(), 13); |
513 | | e.reserve(4, &mut arena); |
514 | | assert_eq!(arena.size(), 17); |
515 | | let _f = arena.from_iter([1, 2, 3, 4, 5, 6, 7, 8].into_iter()); |
516 | | assert_eq!(arena.size(), 25); |
517 | | e.reserve(8, &mut arena); |
518 | | assert_eq!(e.cap(), 16); |
519 | | assert_eq!(e.as_slice(&arena), &[10, 11, 12, 13]); |
520 | | // `e` must have been copied now that `f` is at the end of the |
521 | | // arena. |
522 | | assert_eq!(arena.size(), 41); |
523 | | } |
524 | | } |