diff --git a/crates/oxc_allocator/src/boxed.rs b/crates/oxc_allocator/src/boxed.rs index ec67ea0acf9fc..5ef4eef87a7e8 100644 --- a/crates/oxc_allocator/src/boxed.rs +++ b/crates/oxc_allocator/src/boxed.rs @@ -16,12 +16,29 @@ use serde::{Serialize, Serializer}; use crate::Allocator; -/// A Box without Drop. +/// A Box without [`Drop`]. +/// /// This is used for over coming self-referential structs. -/// It is a memory leak if the boxed value has a `Drop` implementation. +/// It is a memory leak if the boxed value has a [`Drop`] implementation. pub struct Box<'alloc, T: ?Sized>(NonNull, PhantomData<(&'alloc (), T)>); impl<'alloc, T> Box<'alloc, T> { + /// Take ownership of the value stored in this [`Box`], consuming the box in + /// the process. + /// + /// ## Example + /// ``` + /// use oxc_allocator::{Allocator, Box}; + /// + /// let arena = Allocator::default(); + /// + /// // Put `5` into the arena and on the heap. + /// let boxed: Box = Box::new_in(5, &arena); + /// // Move it back to the stack. `boxed` has been consumed. + /// let i = boxed.unbox(); + /// + /// assert_eq!(i, 5); + /// ``` pub fn unbox(self) -> T { // SAFETY: // This pointer read is safe because the reference `self.0` is @@ -34,11 +51,22 @@ impl<'alloc, T> Box<'alloc, T> { } impl<'alloc, T> Box<'alloc, T> { + /// Put a `value` into a memory arena and get back a [`Box`] with ownership + /// to the allocation. + /// + /// ## Example + /// ``` + /// use oxc_allocator::{Allocator, Box}; + /// + /// let arena = Allocator::default(); + /// let in_arena: Box = Box::new_in(5, &arena); + /// ``` pub fn new_in(value: T, allocator: &Allocator) -> Self { Self(NonNull::from(allocator.alloc(value)), PhantomData) } - /// Create a fake `Box` with a dangling pointer. + /// Create a fake [`Box`] with a dangling pointer. + /// /// # SAFETY /// Safe to create, but must never be dereferenced, as does not point to a valid `T`. /// Only purpose is for mocking types without allocating for const assertions. diff --git a/crates/oxc_allocator/src/lib.rs b/crates/oxc_allocator/src/lib.rs index f6882c4bf0ee9..b07443c3d83c3 100644 --- a/crates/oxc_allocator/src/lib.rs +++ b/crates/oxc_allocator/src/lib.rs @@ -1,3 +1,44 @@ +//! # ⚓ Oxc Memory Allocator +//! +//! Oxc uses a bump-based memory arena for faster AST allocations. This crate +//! contains an [`Allocator`] for creating such arenas, as well as ports of +//! memory management data types from `std` adapted to use this arena. +//! +//! ## No `Drop`s +//! Objects allocated into oxc memory arenas are never [`Dropped`](Drop), making +//! it relatively easy to leak memory if you're not careful. Memory is released +//! in bulk when the allocator is dropped. +//! +//! ## Examples +//! ``` +//! use oxc_allocator::{Allocator, Box}; +//! +//! struct Foo { +//! pub a: i32 +//! } +//! impl std::ops::Drop for Foo { +//! fn drop(&mut self) { +//! // Arena boxes are never dropped. +//! unreachable!(); +//! } +//! } +//! +//! let allocator = Allocator::default(); +//! let foo = Box::new_in(Foo { a: 0 }, &allocator); +//! drop(foo); +//! ``` +//! +//! Consumers of the [`oxc` umbrella crate](https://crates.io/crates/oxc) pass +//! [`Allocator`] references to other tools. +//! +//! ``` +//! use oxc::{allocator::Allocator, parser::Parser, span::SourceType}; +//! +//! let allocator = Allocator::default() +//! let parsed = Parser::new(&allocator, "let x = 1;", SourceType::default()); +//! assert!(parsed.errors.is_empty()); +//! ``` + use std::{ convert::From, ops::{Deref, DerefMut}, @@ -16,6 +57,13 @@ pub use clone_in::CloneIn; pub use convert::{FromIn, IntoIn}; pub use vec::Vec; +/// A bump-allocated memory arena based on [bumpalo]. +/// +/// ## No `Drop`s +/// +/// Objects that are bump-allocated will never have their [`Drop`] implementation +/// called — unless you do it manually yourself. This makes it relatively +/// easy to leak memory or other resources. #[derive(Default)] pub struct Allocator { bump: Bump, diff --git a/crates/oxc_allocator/src/vec.rs b/crates/oxc_allocator/src/vec.rs index 6220bbf23fd57..d3d226121edb7 100644 --- a/crates/oxc_allocator/src/vec.rs +++ b/crates/oxc_allocator/src/vec.rs @@ -21,11 +21,72 @@ use crate::Allocator; pub struct Vec<'alloc, T>(vec::Vec); impl<'alloc, T> Vec<'alloc, T> { + /// Constructs a new, empty `Vec`. + /// + /// The vector will not allocate until elements are pushed onto it. + /// + /// # Examples + /// + /// ``` + /// use oxc_allocator::{Allocator, Vec}; + /// + /// let arena = Allocator::default(); + /// + /// let mut vec: Vec = Vec::new_in(&arena); + /// assert!(vec.is_empty()); + /// ``` #[inline] pub fn new_in(allocator: &'alloc Allocator) -> Self { Self(vec::Vec::new_in(allocator)) } + /// Constructs a new, empty `Vec` with at least the specified capacity + /// with the provided allocator. + /// + /// The vector will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// + /// It is important to note that although the returned vector has the + /// minimum *capacity* specified, the vector will have a zero *length*. + /// + /// For `Vec` where `T` is a zero-sized type, there will be no allocation + /// and the capacity will always be `usize::MAX`. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + /// + /// # Examples + /// + /// ``` + /// use oxc_allocator::{Allocator, Vec}; + /// + /// let arena = Allocator::default(); + /// + /// let mut vec = Vec::with_capacity_in(10, &arena); + /// + /// // The vector contains no items, even though it has capacity for more + /// assert_eq!(vec.len(), 0); + /// assert_eq!(vec.capacity(), 10); + /// + /// // These are all done without reallocating... + /// for i in 0..10 { + /// vec.push(i); + /// } + /// assert_eq!(vec.len(), 10); + /// assert_eq!(vec.capacity(), 10); + /// + /// // ...but this may make the vector reallocate + /// vec.push(11); + /// assert_eq!(vec.len(), 11); + /// assert!(vec.capacity() >= 11); + /// + /// // A vector of a zero-sized type will always over-allocate, since no + /// // allocation is necessary + /// let vec_units = Vec::<()>::with_capacity_in(10, &arena); + /// assert_eq!(vec_units.capacity(), usize::MAX); + /// ``` #[inline] pub fn with_capacity_in(capacity: usize, allocator: &'alloc Allocator) -> Self { Self(vec::Vec::with_capacity_in(capacity, allocator)) @@ -126,6 +187,13 @@ mod test { use super::Vec; use crate::Allocator; + #[test] + fn vec_with_capacity() { + let allocator = Allocator::default(); + let v: Vec = Vec::with_capacity_in(10, &allocator); + assert!(v.is_empty()); + } + #[test] fn vec_debug() { let allocator = Allocator::default();