mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
parent
d60ceb4a0b
commit
3099709dcd
3 changed files with 147 additions and 3 deletions
|
|
@ -16,12 +16,29 @@ use serde::{Serialize, Serializer};
|
||||||
|
|
||||||
use crate::Allocator;
|
use crate::Allocator;
|
||||||
|
|
||||||
/// A Box without Drop.
|
/// A Box without [`Drop`].
|
||||||
|
///
|
||||||
/// This is used for over coming self-referential structs.
|
/// 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<T>, PhantomData<(&'alloc (), T)>);
|
pub struct Box<'alloc, T: ?Sized>(NonNull<T>, PhantomData<(&'alloc (), T)>);
|
||||||
|
|
||||||
impl<'alloc, T> Box<'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<i32> = 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 {
|
pub fn unbox(self) -> T {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// This pointer read is safe because the reference `self.0` is
|
// 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> {
|
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<i32> = Box::new_in(5, &arena);
|
||||||
|
/// ```
|
||||||
pub fn new_in(value: T, allocator: &Allocator) -> Self {
|
pub fn new_in(value: T, allocator: &Allocator) -> Self {
|
||||||
Self(NonNull::from(allocator.alloc(value)), PhantomData)
|
Self(NonNull::from(allocator.alloc(value)), PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a fake `Box` with a dangling pointer.
|
/// Create a fake [`Box`] with a dangling pointer.
|
||||||
|
///
|
||||||
/// # SAFETY
|
/// # SAFETY
|
||||||
/// Safe to create, but must never be dereferenced, as does not point to a valid `T`.
|
/// 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.
|
/// Only purpose is for mocking types without allocating for const assertions.
|
||||||
|
|
|
||||||
|
|
@ -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::{
|
use std::{
|
||||||
convert::From,
|
convert::From,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
|
|
@ -16,6 +57,13 @@ pub use clone_in::CloneIn;
|
||||||
pub use convert::{FromIn, IntoIn};
|
pub use convert::{FromIn, IntoIn};
|
||||||
pub use vec::Vec;
|
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)]
|
#[derive(Default)]
|
||||||
pub struct Allocator {
|
pub struct Allocator {
|
||||||
bump: Bump,
|
bump: Bump,
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,72 @@ use crate::Allocator;
|
||||||
pub struct Vec<'alloc, T>(vec::Vec<T, &'alloc Bump>);
|
pub struct Vec<'alloc, T>(vec::Vec<T, &'alloc Bump>);
|
||||||
|
|
||||||
impl<'alloc, T> Vec<'alloc, T> {
|
impl<'alloc, T> Vec<'alloc, T> {
|
||||||
|
/// Constructs a new, empty `Vec<T>`.
|
||||||
|
///
|
||||||
|
/// 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<i32> = Vec::new_in(&arena);
|
||||||
|
/// assert!(vec.is_empty());
|
||||||
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_in(allocator: &'alloc Allocator) -> Self {
|
pub fn new_in(allocator: &'alloc Allocator) -> Self {
|
||||||
Self(vec::Vec::new_in(allocator))
|
Self(vec::Vec::new_in(allocator))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a new, empty `Vec<T>` 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<T>` 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]
|
#[inline]
|
||||||
pub fn with_capacity_in(capacity: usize, allocator: &'alloc Allocator) -> Self {
|
pub fn with_capacity_in(capacity: usize, allocator: &'alloc Allocator) -> Self {
|
||||||
Self(vec::Vec::with_capacity_in(capacity, allocator))
|
Self(vec::Vec::with_capacity_in(capacity, allocator))
|
||||||
|
|
@ -126,6 +187,13 @@ mod test {
|
||||||
use super::Vec;
|
use super::Vec;
|
||||||
use crate::Allocator;
|
use crate::Allocator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_with_capacity() {
|
||||||
|
let allocator = Allocator::default();
|
||||||
|
let v: Vec<i32> = Vec::with_capacity_in(10, &allocator);
|
||||||
|
assert!(v.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vec_debug() {
|
fn vec_debug() {
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue