mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +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;
|
||||
|
||||
/// 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<T>, 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<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 {
|
||||
// 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<i32> = 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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -21,11 +21,72 @@ use crate::Allocator;
|
|||
pub struct Vec<'alloc, T>(vec::Vec<T, &'alloc Bump>);
|
||||
|
||||
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]
|
||||
pub fn new_in(allocator: &'alloc Allocator) -> Self {
|
||||
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]
|
||||
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<i32> = Vec::with_capacity_in(10, &allocator);
|
||||
assert!(v.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_debug() {
|
||||
let allocator = Allocator::default();
|
||||
|
|
|
|||
Loading…
Reference in a new issue