@@ -204,6 +204,100 @@ impl Allocator {
204
204
self . bump . reset ( ) ;
205
205
}
206
206
207
+ /// Calculate the total capacity of this [`Allocator`] including all chunks, in bytes.
208
+ ///
209
+ /// Note: This is the total amount of memory the [`Allocator`] owns NOT the total size of data
210
+ /// that's been allocated in it. If you want the latter, use [`used_bytes`] instead.
211
+ ///
212
+ /// # Examples
213
+ /// ```
214
+ /// use oxc_allocator::Allocator;
215
+ ///
216
+ /// let capacity = 64 * 1024; // 64 KiB
217
+ /// let mut allocator = Allocator::with_capacity(capacity);
218
+ /// allocator.alloc(123u64); // 8 bytes
219
+ ///
220
+ /// // Result is the capacity (64 KiB), not the size of allocated data (8 bytes).
221
+ /// // `Allocator::with_capacity` may allocate a bit more than requested.
222
+ /// assert!(allocator.capacity() >= capacity);
223
+ /// ```
224
+ ///
225
+ /// [`used_bytes`]: Allocator::used_bytes
226
+ //
227
+ // `#[inline(always)]` because it just delegates to `bumpalo`
228
+ #[ expect( clippy:: inline_always) ]
229
+ #[ inline( always) ]
230
+ pub fn capacity ( & self ) -> usize {
231
+ self . bump . allocated_bytes ( )
232
+ }
233
+
234
+ /// Calculate the total size of data used in this [`Allocator`], in bytes.
235
+ ///
236
+ /// This is the total amount of memory that has been *used* in the [`Allocator`], NOT the amount of
237
+ /// memory the [`Allocator`] owns. If you want the latter, use [`capacity`] instead.
238
+ ///
239
+ /// The result includes:
240
+ ///
241
+ /// 1. Padding bytes between objects which have been allocated to preserve alignment of types
242
+ /// where they have different alignments or have larger-than-typical alignment.
243
+ /// 2. Excess capacity in [`Vec`]s, [`String`]s and [`HashMap`]s.
244
+ /// 3. Objects which were allocated but later dropped. [`Allocator`] does not re-use allocations,
245
+ /// so anything which is allocated into arena continues to take up "dead space", even after it's
246
+ /// no longer referenced anywhere.
247
+ /// 4. "Dead space" left over where a [`Vec`], [`String`] or [`HashMap`] has grown and had to make
248
+ /// a new allocation to accomodate its new larger size. Its old allocation continues to take up
249
+ /// "dead" space in the allocator, unless it was the most recent allocation.
250
+ ///
251
+ /// In practice, this almost always means that the result returned from this function will be an
252
+ /// over-estimate vs the amount of "live" data in the arena.
253
+ ///
254
+ /// However, if you are using the result of this method to create a new `Allocator` to clone
255
+ /// an AST into, it is theoretically possible (though very unlikely) that it may be a slight
256
+ /// under-estimate of the capacity required in new allocator to clone the AST into, depending
257
+ /// on the order that `&str`s were allocated into arena in parser vs the order they get allocated
258
+ /// during cloning. The order allocations are made in affects the amount of padding bytes required.
259
+ ///
260
+ /// # Examples
261
+ /// ```
262
+ /// use oxc_allocator::{Allocator, Vec};
263
+ ///
264
+ /// let capacity = 64 * 1024; // 64 KiB
265
+ /// let mut allocator = Allocator::with_capacity(capacity);
266
+ ///
267
+ /// allocator.alloc(1u8); // 1 byte with alignment 1
268
+ /// allocator.alloc(2u8); // 1 byte with alignment 1
269
+ /// allocator.alloc(3u64); // 8 bytes with alignment 8
270
+ ///
271
+ /// // Only 10 bytes were allocated, but 16 bytes were used, in order to align `3u64` on 8
272
+ /// assert_eq!(allocator.used_bytes(), 16);
273
+ ///
274
+ /// allocator.reset();
275
+ ///
276
+ /// let mut vec = Vec::<u64>::with_capacity_in(2, &allocator);
277
+ ///
278
+ /// // Allocate something else, so `vec`'s allocation is not the most recent
279
+ /// allocator.alloc(123u64);
280
+ ///
281
+ /// // `vec` has to grow beyond it's initial capacity
282
+ /// vec.extend([1, 2, 3, 4]);
283
+ ///
284
+ /// // `vec` takes up 32 bytes, and `123u64` takes up 8 bytes = 40 total.
285
+ /// // But there's an additional 16 bytes consumed for `vec`'s original capacity of 2,
286
+ /// // which is still using up space
287
+ /// assert_eq!(allocator.used_bytes(), 56);
288
+ /// ```
289
+ ///
290
+ /// [`capacity`]: Allocator::capacity
291
+ pub fn used_bytes ( & self ) -> usize {
292
+ let mut bytes = 0 ;
293
+ // SAFETY: No allocations are made while `chunks_iter` is alive. No data is read from the chunks.
294
+ let chunks_iter = unsafe { self . bump . iter_allocated_chunks_raw ( ) } ;
295
+ for ( _, size) in chunks_iter {
296
+ bytes += size;
297
+ }
298
+ bytes
299
+ }
300
+
207
301
/// Get inner [`bumpalo::Bump`].
208
302
///
209
303
/// This method is not public. We don't want to expose `bumpalo::Allocator` to user.
0 commit comments