mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 06:36:08 +03:00
feat(allocator): Implement SwcAlloc
(#9232)
**Related issue:** - This is a part of https://github.com/swc-project/swc/pull/9230
This commit is contained in:
parent
c9ac23beb0
commit
e343eb6de2
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -62,6 +62,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@ -406,6 +415,9 @@ name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
@ -3688,7 +3700,13 @@ dependencies = [
|
||||
name = "swc_allocator"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
"ptr_meta",
|
||||
"rkyv",
|
||||
"scoped-tls",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3810,6 +3828,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"siphasher",
|
||||
"sourcemap",
|
||||
"swc_allocator",
|
||||
"swc_atoms",
|
||||
"swc_eq_ignore_macros",
|
||||
"swc_visit",
|
||||
|
@ -40,6 +40,7 @@ resolver = "2"
|
||||
|
||||
Inflector = "0.11.4"
|
||||
ahash = "0.8.8"
|
||||
allocator-api2 = "0.2.18"
|
||||
ansi_term = "0.12.1"
|
||||
anyhow = "1.0.81"
|
||||
arbitrary = "1"
|
||||
@ -95,6 +96,7 @@ resolver = "2"
|
||||
phf = "0.11.2"
|
||||
pretty_assertions = "1.3"
|
||||
proc-macro2 = "1.0.24"
|
||||
ptr_meta = "0.1.4"
|
||||
quote = "1.0.7"
|
||||
rayon = "1.7.0"
|
||||
regex = "1.5.4"
|
||||
|
@ -8,5 +8,19 @@ name = "swc_allocator"
|
||||
repository = { workspace = true }
|
||||
version = "0.1.1"
|
||||
|
||||
[features]
|
||||
rkyv = ["dep:rkyv"]
|
||||
serde = ["dep:serde", "dep:serde_derive"]
|
||||
|
||||
[dependencies]
|
||||
bumpalo = { workspace = true, features = ["boxed", "collections"] }
|
||||
allocator-api2 = { workspace = true, features = ["serde"] }
|
||||
bumpalo = { workspace = true, features = [
|
||||
"allocator-api2",
|
||||
"boxed",
|
||||
"collections",
|
||||
] }
|
||||
ptr_meta = { workspace = true }
|
||||
rkyv = { workspace = true, optional = true }
|
||||
scoped-tls = { workspace = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
serde_derive = { workspace = true, optional = true }
|
||||
|
153
crates/swc_allocator/src/alloc.rs
Normal file
153
crates/swc_allocator/src/alloc.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use std::{alloc::Layout, ptr::NonNull};
|
||||
|
||||
use allocator_api2::alloc::Global;
|
||||
use scoped_tls::scoped_thread_local;
|
||||
|
||||
use crate::Allocator;
|
||||
|
||||
scoped_thread_local!(pub(crate) static ALLOC: Allocator);
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SwcAlloc;
|
||||
|
||||
impl SwcAlloc {
|
||||
/// `true` is passed to `f` if the box is allocated with a custom allocator.
|
||||
fn with_allocator<T>(
|
||||
&self,
|
||||
f: impl FnOnce(&dyn allocator_api2::alloc::Allocator, bool) -> T,
|
||||
) -> T {
|
||||
if ALLOC.is_set() {
|
||||
ALLOC.with(|a| {
|
||||
//
|
||||
f(&&**a as &dyn allocator_api2::alloc::Allocator, true)
|
||||
})
|
||||
} else {
|
||||
f(&allocator_api2::alloc::Global, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the last bit to 1
|
||||
fn mark_ptr_as_arena_mode(ptr: NonNull<[u8]>) -> NonNull<[u8]> {
|
||||
let (mut raw_ptr, metadata) = ptr_meta::PtrExt::to_raw_parts(ptr.as_ptr());
|
||||
|
||||
raw_ptr = (raw_ptr as usize | 1) as *mut ();
|
||||
|
||||
unsafe {
|
||||
// Safety:
|
||||
NonNull::new_unchecked(ptr_meta::from_raw_parts_mut(raw_ptr, metadata))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ptr_in_arena_mode(ptr: NonNull<u8>) -> bool {
|
||||
let ptr = ptr.as_ptr() as usize;
|
||||
ptr & 1 == 1
|
||||
}
|
||||
|
||||
unsafe impl allocator_api2::alloc::Allocator for SwcAlloc {
|
||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, allocator_api2::alloc::AllocError> {
|
||||
self.with_allocator(|a, is_arena_mode| {
|
||||
let ptr = a.allocate(layout)?;
|
||||
|
||||
if is_arena_mode {
|
||||
Ok(mark_ptr_as_arena_mode(ptr))
|
||||
} else {
|
||||
Ok(ptr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn allocate_zeroed(
|
||||
&self,
|
||||
layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, allocator_api2::alloc::AllocError> {
|
||||
self.with_allocator(|a, is_arena_mode| {
|
||||
let ptr = a.allocate_zeroed(layout)?;
|
||||
|
||||
if is_arena_mode {
|
||||
Ok(mark_ptr_as_arena_mode(ptr))
|
||||
} else {
|
||||
Ok(ptr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||
if is_ptr_in_arena_mode(ptr) {
|
||||
debug_assert!(
|
||||
ALLOC.is_set(),
|
||||
"Deallocating a pointer allocated with arena mode with a non-arena mode allocator"
|
||||
);
|
||||
|
||||
ALLOC.with(|alloc| {
|
||||
unsafe {
|
||||
// Safety: We are in unsafe fn
|
||||
(&**alloc).deallocate(ptr, layout)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Global.deallocate(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn grow(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, allocator_api2::alloc::AllocError> {
|
||||
if is_ptr_in_arena_mode(ptr) {
|
||||
debug_assert!(
|
||||
ALLOC.is_set(),
|
||||
"Growing a pointer allocated with arena mode with a non-arena mode allocator"
|
||||
);
|
||||
|
||||
ALLOC.with(|alloc| (&**alloc).grow(ptr, old_layout, new_layout))
|
||||
} else {
|
||||
Global.grow(ptr, old_layout, new_layout)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn grow_zeroed(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, allocator_api2::alloc::AllocError> {
|
||||
if is_ptr_in_arena_mode(ptr) {
|
||||
debug_assert!(
|
||||
ALLOC.is_set(),
|
||||
"Growing a pointer allocated with arena mode with a non-arena mode allocator"
|
||||
);
|
||||
|
||||
ALLOC.with(|alloc| (&**alloc).grow_zeroed(ptr, old_layout, new_layout))
|
||||
} else {
|
||||
Global.grow_zeroed(ptr, old_layout, new_layout)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn shrink(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, allocator_api2::alloc::AllocError> {
|
||||
if is_ptr_in_arena_mode(ptr) {
|
||||
debug_assert!(
|
||||
ALLOC.is_set(),
|
||||
"Shrinking a pointer allocated with arena mode with a non-arena mode allocator"
|
||||
);
|
||||
|
||||
ALLOC.with(|alloc| (&**alloc).shrink(ptr, old_layout, new_layout))
|
||||
} else {
|
||||
Global.shrink(ptr, old_layout, new_layout)
|
||||
}
|
||||
}
|
||||
|
||||
fn by_ref(&self) -> &Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self
|
||||
}
|
||||
}
|
623
crates/swc_allocator/src/boxed/mod.rs
Normal file
623
crates/swc_allocator/src/boxed/mod.rs
Normal file
@ -0,0 +1,623 @@
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
io::{self, BufRead, Read, Seek},
|
||||
iter::FusedIterator,
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use crate::alloc::SwcAlloc;
|
||||
|
||||
#[cfg(feature = "rkyv")]
|
||||
mod rkyv;
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde;
|
||||
|
||||
/// A special `Box` which has size of [`std::boxed::Box`] but **may** be
|
||||
/// allocated with a custom allocator.
|
||||
///
|
||||
///
|
||||
/// # Representation
|
||||
///
|
||||
/// The last bit is 1 if the box is allocated with a custom allocator.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Box<T: ?Sized>(pub(crate) allocator_api2::boxed::Box<T, SwcAlloc>);
|
||||
|
||||
impl<T> From<T> for Box<T> {
|
||||
#[inline(always)]
|
||||
fn from(v: T) -> Self {
|
||||
Box::new(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> From<allocator_api2::boxed::Box<T, SwcAlloc>> for Box<T> {
|
||||
#[inline(always)]
|
||||
fn from(v: allocator_api2::boxed::Box<T, SwcAlloc>) -> Self {
|
||||
Box(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Box<T>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Box::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Box<T> {
|
||||
/// Allocates a new box with `value`.
|
||||
///
|
||||
/// See [`std::boxed::Box::new`].
|
||||
#[inline(always)]
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(allocator_api2::boxed::Box::new_in(value, SwcAlloc))
|
||||
}
|
||||
|
||||
/// Moves the value out of the box.
|
||||
pub fn unbox(self) -> T {
|
||||
allocator_api2::boxed::Box::into_inner(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Box<T> {
|
||||
/// Constructs a box from a raw pointer.
|
||||
///
|
||||
/// After calling this function, the raw pointer is owned by the
|
||||
/// resulting `Box`. Specifically, the `Box` destructor will call
|
||||
/// the destructor of `T` and free the allocated memory. For this
|
||||
/// to be safe, the memory must have been allocated in accordance
|
||||
/// with the [memory layout] used by `Box` .
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because improper use may lead to
|
||||
/// memory problems. For example, a double-free may occur if the
|
||||
/// function is called twice on the same raw pointer.
|
||||
///
|
||||
/// The safety conditions are described in the [memory layout] section.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Recreate a `Box` which was previously converted to a raw pointer
|
||||
/// using [`Box::into_raw`]:
|
||||
/// ```
|
||||
/// let x = Box::new(5);
|
||||
/// let ptr = Box::into_raw(x);
|
||||
/// let x = unsafe { Box::from_raw(ptr) };
|
||||
/// ```
|
||||
/// Manually create a `Box` from scratch by using the global allocator:
|
||||
/// ```
|
||||
/// use std::alloc::{alloc, Layout};
|
||||
///
|
||||
/// unsafe {
|
||||
/// let ptr = alloc(Layout::new::<i32>()) as *mut i32;
|
||||
/// // In general .write is required to avoid attempting to destruct
|
||||
/// // the (uninitialized) previous contents of `ptr`, though for this
|
||||
/// // simple example `*ptr = 5` would have worked as well.
|
||||
/// ptr.write(5);
|
||||
/// let x = Box::from_raw(ptr);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [memory layout]: self#memory-layout
|
||||
/// [`Layout`]: crate::Layout
|
||||
pub unsafe fn from_raw(raw: *mut T) -> Self {
|
||||
Self(allocator_api2::boxed::Box::from_raw_in(raw, SwcAlloc))
|
||||
}
|
||||
|
||||
/// Consumes the `Box`, returning a wrapped raw pointer.
|
||||
///
|
||||
/// The pointer will be properly aligned and non-null.
|
||||
///
|
||||
/// After calling this function, the caller is responsible for the
|
||||
/// memory previously managed by the `Box`. In particular, the
|
||||
/// caller should properly destroy `T` and release the memory, taking
|
||||
/// into account the [memory layout] used by `Box`. The easiest way to
|
||||
/// do this is to convert the raw pointer back into a `Box` with the
|
||||
/// [`Box::from_raw`] function, allowing the `Box` destructor to perform
|
||||
/// the cleanup.
|
||||
///
|
||||
/// Note: this is an associated function, which means that you have
|
||||
/// to call it as `Box::into_raw(b)` instead of `b.into_raw()`. This
|
||||
/// is so that there is no conflict with a method on the inner type.
|
||||
///
|
||||
/// # Examples
|
||||
/// Converting the raw pointer back into a `Box` with [`Box::from_raw`]
|
||||
/// for automatic cleanup:
|
||||
/// ```
|
||||
/// let x = Box::new(String::from("Hello"));
|
||||
/// let ptr = Box::into_raw(x);
|
||||
/// let x = unsafe { Box::from_raw(ptr) };
|
||||
/// ```
|
||||
/// Manual cleanup by explicitly running the destructor and deallocating
|
||||
/// the memory:
|
||||
/// ```
|
||||
/// use std::alloc::{dealloc, Layout};
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// let x = Box::new(String::from("Hello"));
|
||||
/// let ptr = Box::into_raw(x);
|
||||
/// unsafe {
|
||||
/// ptr::drop_in_place(ptr);
|
||||
/// dealloc(ptr as *mut u8, Layout::new::<String>());
|
||||
/// }
|
||||
/// ```
|
||||
/// Note: This is equivalent to the following:
|
||||
/// ```
|
||||
/// let x = Box::new(String::from("Hello"));
|
||||
/// let ptr = Box::into_raw(x);
|
||||
/// unsafe {
|
||||
/// drop(Box::from_raw(ptr));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [memory layout]: self#memory-layout
|
||||
pub fn into_raw(b: Self) -> *mut T {
|
||||
allocator_api2::boxed::Box::into_raw(b.0)
|
||||
}
|
||||
|
||||
/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement
|
||||
/// [`Unpin`], then `*boxed` will be pinned in memory and unable to be
|
||||
/// moved.
|
||||
///
|
||||
/// This conversion does not allocate on the heap and happens in place.
|
||||
///
|
||||
/// This is also available via [`From`].
|
||||
///
|
||||
/// Constructing and pinning a `Box` with
|
||||
/// <code>Box::into_pin([Box::new]\(x))</code> can also be written more
|
||||
/// concisely using <code>[Box::pin]\(x)</code>. This `into_pin` method
|
||||
/// is useful if you already have a `Box<T>`, or you are constructing a
|
||||
/// (pinned) `Box` in a different way than with [`Box::new`].
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// It's not recommended that crates add an impl like `From<Box<T>> for
|
||||
/// Pin<T>`, as it'll introduce an ambiguity when calling `Pin::from`.
|
||||
/// A demonstration of such a poor impl is shown below.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use std::pin::Pin;
|
||||
/// struct Foo; // A type defined in this crate.
|
||||
/// impl From<Box<()>> for Pin<Foo> {
|
||||
/// fn from(_: Box<()>) -> Pin<Foo> {
|
||||
/// Pin::new(Foo)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let foo = Box::new(());
|
||||
/// let bar = Pin::from(foo);
|
||||
/// ```
|
||||
pub fn into_pin(boxed: Self) -> Pin<Self> {
|
||||
// It's not possible to move or replace the insides of a `Pin<Box<T>>`
|
||||
// when `T: !Unpin`, so it's safe to pin it directly without any
|
||||
// additional requirements.
|
||||
unsafe { Pin::new_unchecked(boxed) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> AsRef<T> for Box<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> AsMut<T> for Box<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Borrow<T> for Box<T> {
|
||||
fn borrow(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> BorrowMut<T> for Box<T> {
|
||||
fn borrow_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for Box<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for Box<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + BufRead> BufRead for Box<T> {
|
||||
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
|
||||
self.0.fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.0.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Debug for Box<T>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Display for Box<T>
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Iterator for Box<T>
|
||||
where
|
||||
T: Iterator,
|
||||
{
|
||||
type Item = T::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.0.count()
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Self::Item> {
|
||||
self.0.last()
|
||||
}
|
||||
|
||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||
self.0.nth(n)
|
||||
}
|
||||
|
||||
fn all<F>(&mut self, f: F) -> bool
|
||||
where
|
||||
F: FnMut(Self::Item) -> bool,
|
||||
{
|
||||
self.0.all(f)
|
||||
}
|
||||
|
||||
fn any<F>(&mut self, f: F) -> bool
|
||||
where
|
||||
F: FnMut(Self::Item) -> bool,
|
||||
{
|
||||
self.0.any(f)
|
||||
}
|
||||
|
||||
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
|
||||
where
|
||||
P: FnMut(&Self::Item) -> bool,
|
||||
{
|
||||
self.0.find(predicate)
|
||||
}
|
||||
|
||||
fn position<P>(&mut self, predicate: P) -> Option<usize>
|
||||
where
|
||||
P: FnMut(Self::Item) -> bool,
|
||||
{
|
||||
self.0.position(predicate)
|
||||
}
|
||||
|
||||
fn max(self) -> Option<Self::Item>
|
||||
where
|
||||
Self::Item: Ord,
|
||||
{
|
||||
self.0.max()
|
||||
}
|
||||
|
||||
fn min(self) -> Option<Self::Item>
|
||||
where
|
||||
Self::Item: Ord,
|
||||
{
|
||||
self.0.min()
|
||||
}
|
||||
|
||||
fn by_ref(&mut self) -> &mut Self {
|
||||
self.0.by_ref();
|
||||
self
|
||||
}
|
||||
|
||||
fn collect<B>(self) -> B
|
||||
where
|
||||
B: std::iter::FromIterator<Self::Item>,
|
||||
{
|
||||
self.0.collect()
|
||||
}
|
||||
|
||||
fn fold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.0.fold(init, f)
|
||||
}
|
||||
|
||||
fn for_each<F>(self, f: F)
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Item),
|
||||
{
|
||||
self.0.for_each(f)
|
||||
}
|
||||
|
||||
fn partition<B, F>(self, f: F) -> (B, B)
|
||||
where
|
||||
Self: Sized,
|
||||
B: Default + Extend<Self::Item>,
|
||||
F: FnMut(&Self::Item) -> bool,
|
||||
{
|
||||
self.0.partition(f)
|
||||
}
|
||||
|
||||
fn reduce<F>(mut self, f: F) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Item, Self::Item) -> Self::Item,
|
||||
{
|
||||
let first = self.next()?;
|
||||
Some(self.fold(first, f))
|
||||
}
|
||||
|
||||
fn find_map<B, F>(&mut self, f: F) -> Option<B>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self::Item) -> Option<B>,
|
||||
{
|
||||
self.0.find_map(f)
|
||||
}
|
||||
|
||||
fn max_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(&Self::Item) -> B,
|
||||
{
|
||||
self.0.max_by_key(f)
|
||||
}
|
||||
|
||||
fn max_by<F>(self, compare: F) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(&Self::Item, &Self::Item) -> std::cmp::Ordering,
|
||||
{
|
||||
self.0.max_by(compare)
|
||||
}
|
||||
|
||||
fn min_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(&Self::Item) -> B,
|
||||
{
|
||||
self.0.min_by_key(f)
|
||||
}
|
||||
|
||||
fn min_by<F>(self, compare: F) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(&Self::Item, &Self::Item) -> std::cmp::Ordering,
|
||||
{
|
||||
self.0.min_by(compare)
|
||||
}
|
||||
|
||||
fn sum<S>(self) -> S
|
||||
where
|
||||
Self: Sized,
|
||||
S: std::iter::Sum<Self::Item>,
|
||||
{
|
||||
self.0.sum()
|
||||
}
|
||||
|
||||
fn product<P>(self) -> P
|
||||
where
|
||||
Self: Sized,
|
||||
P: std::iter::Product<Self::Item>,
|
||||
{
|
||||
self.0.product()
|
||||
}
|
||||
|
||||
fn cmp<I>(self, other: I) -> std::cmp::Ordering
|
||||
where
|
||||
I: IntoIterator<Item = Self::Item>,
|
||||
Self::Item: Ord,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.cmp(other)
|
||||
}
|
||||
|
||||
fn partial_cmp<I>(self, other: I) -> Option<std::cmp::Ordering>
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialOrd<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.partial_cmp(other)
|
||||
}
|
||||
|
||||
fn eq<I>(self, other: I) -> bool
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialEq<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.eq(other)
|
||||
}
|
||||
|
||||
fn ne<I>(self, other: I) -> bool
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialEq<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.ne(other)
|
||||
}
|
||||
|
||||
fn lt<I>(self, other: I) -> bool
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialOrd<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.lt(other)
|
||||
}
|
||||
|
||||
fn le<I>(self, other: I) -> bool
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialOrd<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.le(other)
|
||||
}
|
||||
|
||||
fn gt<I>(self, other: I) -> bool
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialOrd<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.gt(other)
|
||||
}
|
||||
|
||||
fn ge<I>(self, other: I) -> bool
|
||||
where
|
||||
I: IntoIterator,
|
||||
Self::Item: PartialOrd<I::Item>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.ge(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DoubleEndedIterator for Box<T>
|
||||
where
|
||||
T: DoubleEndedIterator,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.0.next_back()
|
||||
}
|
||||
|
||||
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
||||
self.0.nth_back(n)
|
||||
}
|
||||
|
||||
fn rfold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.0.rfold(init, f)
|
||||
}
|
||||
|
||||
fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
P: FnMut(&Self::Item) -> bool,
|
||||
{
|
||||
self.0.rfind(predicate)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> fmt::Pointer for Box<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Read for Box<T>
|
||||
where
|
||||
T: Read,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut std::vec::Vec<u8>) -> std::io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
|
||||
self.0.read_to_string(buf)
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
||||
self.0.read_exact(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Seek for Box<T>
|
||||
where
|
||||
T: Seek,
|
||||
{
|
||||
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
||||
self.0.seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> fmt::Write for Box<T>
|
||||
where
|
||||
T: fmt::Write,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.write_str(s)
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
self.0.write_char(c)
|
||||
}
|
||||
|
||||
fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
||||
self.0.write_fmt(args)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> io::Write for Box<T>
|
||||
where
|
||||
T: io::Write,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.0.write_all(buf)
|
||||
}
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
|
||||
self.0.write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FusedIterator for Box<T> where T: ?Sized + FusedIterator {}
|
||||
|
||||
impl<T> ExactSizeIterator for Box<T>
|
||||
where
|
||||
T: ?Sized + ExactSizeIterator,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
59
crates/swc_allocator/src/boxed/rkyv.rs
Normal file
59
crates/swc_allocator/src/boxed/rkyv.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use std::{alloc, cmp};
|
||||
|
||||
use rkyv::{
|
||||
boxed::{ArchivedBox, BoxResolver},
|
||||
Archive, ArchivePointee, ArchiveUnsized, Deserialize, DeserializeUnsized, Fallible, Serialize,
|
||||
SerializeUnsized,
|
||||
};
|
||||
|
||||
use super::Box;
|
||||
|
||||
impl<T: ArchiveUnsized + ?Sized> Archive for Box<T> {
|
||||
type Archived = ArchivedBox<T::Archived>;
|
||||
type Resolver = BoxResolver<T::MetadataResolver>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) {
|
||||
ArchivedBox::resolve_from_ref(self.as_ref(), pos, resolver, out);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SerializeUnsized<S> + ?Sized, S: Fallible + ?Sized> Serialize<S> for Box<T> {
|
||||
#[inline]
|
||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||
ArchivedBox::serialize_from_ref(self.as_ref(), serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Deserialize<Box<T>, D> for ArchivedBox<T::Archived>
|
||||
where
|
||||
T: ArchiveUnsized + ?Sized,
|
||||
T::Archived: DeserializeUnsized<T, D>,
|
||||
D: Fallible + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn deserialize(&self, deserializer: &mut D) -> Result<Box<T>, D::Error> {
|
||||
unsafe {
|
||||
let data_address = self
|
||||
.get()
|
||||
.deserialize_unsized(deserializer, |layout| alloc::alloc(layout))?;
|
||||
let metadata = self.get().deserialize_metadata(deserializer)?;
|
||||
let ptr = ptr_meta::from_raw_parts_mut(data_address, metadata);
|
||||
Ok(Box::from_raw(ptr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArchivePointee + PartialEq<U> + ?Sized, U: ?Sized> PartialEq<Box<U>> for ArchivedBox<T> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Box<U>) -> bool {
|
||||
self.get().eq(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArchivePointee + PartialOrd<U> + ?Sized, U: ?Sized> PartialOrd<Box<U>> for ArchivedBox<T> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Box<U>) -> Option<cmp::Ordering> {
|
||||
self.get().partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
27
crates/swc_allocator/src/boxed/serde.rs
Normal file
27
crates/swc_allocator/src/boxed/serde.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use super::Box;
|
||||
|
||||
impl<T> Serialize for Box<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Box<T>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Box<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Box(allocator_api2::boxed::Box::deserialize(deserializer)?))
|
||||
}
|
||||
}
|
@ -2,15 +2,34 @@
|
||||
//!
|
||||
//! API designed after [`oxc_allocator`](https://github.com/oxc-project/oxc/tree/725571aad193ec6ba779c820baeb4a7774533ed7/crates/oxc_allocator/src).
|
||||
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bumpalo::Bump;
|
||||
|
||||
use crate::alloc::ALLOC;
|
||||
|
||||
mod alloc;
|
||||
pub mod boxed;
|
||||
pub mod vec;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Allocator {
|
||||
alloc: Bump,
|
||||
}
|
||||
|
||||
impl Allocator {
|
||||
/// Invokes `f` in a scope where the allocations are done in this allocator.
|
||||
#[inline(always)]
|
||||
pub fn scope<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
ALLOC.set(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bump> for Allocator {
|
||||
fn from(alloc: Bump) -> Self {
|
||||
Self { alloc }
|
||||
@ -30,115 +49,3 @@ impl DerefMut for Allocator {
|
||||
&mut self.alloc
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Box<'alloc, T>(bumpalo::boxed::Box<'alloc, T>);
|
||||
|
||||
impl<'alloc, T> Box<'alloc, T> {
|
||||
#[inline(always)]
|
||||
pub fn new(alloc: &'alloc Allocator, value: T) -> Self {
|
||||
Self(bumpalo::boxed::Box::new_in(value, alloc))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc, T> Deref for Box<'alloc, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc, T> DerefMut for Box<'alloc, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Vec<'alloc, T>(bumpalo::collections::Vec<'alloc, T>);
|
||||
|
||||
impl<'alloc, T> Vec<'alloc, T> {
|
||||
#[inline(always)]
|
||||
pub fn new(alloc: &'alloc Allocator) -> Self {
|
||||
Self(bumpalo::collections::Vec::new_in(alloc))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_capacity(alloc: &'alloc Allocator, capacity: usize) -> Self {
|
||||
Self(bumpalo::collections::Vec::with_capacity_in(capacity, alloc))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc, T> Deref for Vec<'alloc, T> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &[T] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc, T> DerefMut for Vec<'alloc, T> {
|
||||
fn deref_mut(&mut self) -> &mut [T] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'alloc, T> IntoIterator for Vec<'alloc, T> {
|
||||
type IntoIter = bumpalo::collections::vec::IntoIter<'alloc, T>;
|
||||
type Item = T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct String<'alloc>(bumpalo::collections::String<'alloc>);
|
||||
|
||||
impl<'alloc> String<'alloc> {
|
||||
#[inline(always)]
|
||||
pub fn new(alloc: &'alloc Allocator) -> Self {
|
||||
Self(bumpalo::collections::String::new_in(alloc))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_capacity(alloc: &'alloc Allocator, capacity: usize) -> Self {
|
||||
Self(bumpalo::collections::String::with_capacity_in(
|
||||
capacity, alloc,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for String<'_> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for String<'_> {
|
||||
fn deref_mut(&mut self) -> &mut str {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum CowStr<'alloc> {
|
||||
Borrowed(&'alloc str),
|
||||
Owned(String<'alloc>),
|
||||
}
|
||||
|
||||
impl Deref for CowStr<'_> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
match self {
|
||||
CowStr::Borrowed(s) => s,
|
||||
CowStr::Owned(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
241
crates/swc_allocator/src/vec/mod.rs
Normal file
241
crates/swc_allocator/src/vec/mod.rs
Normal file
@ -0,0 +1,241 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[cfg(feature = "rkyv")]
|
||||
mod rkyv;
|
||||
|
||||
use crate::{alloc::SwcAlloc, boxed::Box};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde_derive::Serialize, serde_derive::Deserialize)
|
||||
)]
|
||||
pub struct Vec<T>(allocator_api2::vec::Vec<T, SwcAlloc>);
|
||||
|
||||
impl<T> Vec<T> {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self(allocator_api2::vec::Vec::with_capacity_in(
|
||||
capacity, SwcAlloc,
|
||||
))
|
||||
}
|
||||
|
||||
/// Converts the vector into [`Box<[T]>`][owned slice].
|
||||
///
|
||||
/// Before doing the conversion, this method discards excess capacity like
|
||||
/// [`shrink_to_fit`].
|
||||
///
|
||||
/// [owned slice]: Box
|
||||
/// [`shrink_to_fit`]: Vec::shrink_to_fit
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let v = vec![1, 2, 3];
|
||||
///
|
||||
/// let slice = v.into_boxed_slice();
|
||||
/// ```
|
||||
///
|
||||
/// Any excess capacity is removed:
|
||||
///
|
||||
/// ```
|
||||
/// let mut vec = Vec::with_capacity(10);
|
||||
/// vec.extend([1, 2, 3]);
|
||||
///
|
||||
/// assert!(vec.capacity() >= 10);
|
||||
/// let slice = vec.into_boxed_slice();
|
||||
/// assert_eq!(slice.into_vec().capacity(), 3);
|
||||
/// ```
|
||||
pub fn into_boxed_slice(self) -> Box<[T]> {
|
||||
self.0.into_boxed_slice().into()
|
||||
}
|
||||
|
||||
/// Consumes and leaks the `Vec`, returning a mutable reference to the
|
||||
/// contents, `&'a mut [T]`. Note that the type `T` must outlive the
|
||||
/// chosen lifetime `'a`. If the type has only static references, or
|
||||
/// none at all, then this may be chosen to be `'static`.
|
||||
///
|
||||
/// As of Rust 1.57, this method does not reallocate or shrink the `Vec`,
|
||||
/// so the leaked allocation may include unused capacity that is not part
|
||||
/// of the returned slice.
|
||||
///
|
||||
/// This function is mainly useful for data that lives for the remainder of
|
||||
/// the program's life. Dropping the returned reference will cause a memory
|
||||
/// leak.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Simple usage:
|
||||
///
|
||||
/// ```
|
||||
/// let x = vec![1, 2, 3];
|
||||
/// let static_ref: &'static mut [usize] = x.leak();
|
||||
/// static_ref[0] += 1;
|
||||
/// assert_eq!(static_ref, &[2, 2, 3]);
|
||||
/// ```
|
||||
pub fn leak(self) -> &'static mut [T] {
|
||||
self.0.leak()
|
||||
}
|
||||
|
||||
/// Creates a `Vec<T>` directly from a pointer, a length, and a capacity.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is highly unsafe, due to the number of invariants that aren't
|
||||
/// checked:
|
||||
///
|
||||
/// * `ptr` must have been allocated using the global allocator, such as via
|
||||
/// the [`alloc::alloc`] function.
|
||||
/// * `T` needs to have the same alignment as what `ptr` was allocated with.
|
||||
/// (`T` having a less strict alignment is not sufficient, the alignment
|
||||
/// really needs to be equal to satisfy the [`dealloc`] requirement that
|
||||
/// memory must be allocated and deallocated with the same layout.)
|
||||
/// * The size of `T` times the `capacity` (ie. the allocated size in bytes)
|
||||
/// needs to be the same size as the pointer was allocated with. (Because
|
||||
/// similar to alignment, [`dealloc`] must be called with the same layout
|
||||
/// `size`.)
|
||||
/// * `length` needs to be less than or equal to `capacity`.
|
||||
/// * The first `length` values must be properly initialized values of type
|
||||
/// `T`.
|
||||
/// * `capacity` needs to be the capacity that the pointer was allocated
|
||||
/// with.
|
||||
/// * The allocated size in bytes must be no larger than `isize::MAX`. See
|
||||
/// the safety documentation of [`pointer::offset`].
|
||||
///
|
||||
/// These requirements are always upheld by any `ptr` that has been
|
||||
/// allocated via `Vec<T>`. Other allocation sources are allowed if the
|
||||
/// invariants are upheld.
|
||||
///
|
||||
/// Violating these may cause problems like corrupting the allocator's
|
||||
/// internal data structures. For example it is normally **not** safe
|
||||
/// to build a `Vec<u8>` from a pointer to a C `char` array with length
|
||||
/// `size_t`, doing so is only safe if the array was initially allocated by
|
||||
/// a `Vec` or `String`.
|
||||
/// It's also not safe to build one from a `Vec<u16>` and its length,
|
||||
/// because the allocator cares about the alignment, and these two types
|
||||
/// have different alignments. The buffer was allocated with alignment 2
|
||||
/// (for `u16`), but after turning it into a `Vec<u8>` it'll be
|
||||
/// deallocated with alignment 1. To avoid these issues, it is often
|
||||
/// preferable to do casting/transmuting using [`slice::from_raw_parts`]
|
||||
/// instead.
|
||||
///
|
||||
/// The ownership of `ptr` is effectively transferred to the
|
||||
/// `Vec<T>` which may then deallocate, reallocate or change the
|
||||
/// contents of memory pointed to by the pointer at will. Ensure
|
||||
/// that nothing else uses the pointer after calling this
|
||||
/// function.
|
||||
///
|
||||
/// [`String`]: crate::string::String
|
||||
/// [`alloc::alloc`]: crate::alloc::alloc
|
||||
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ptr;
|
||||
/// use std::mem;
|
||||
///
|
||||
/// let v = vec![1, 2, 3];
|
||||
// FIXME Update this when vec_into_raw_parts is stabilized
|
||||
/// // Prevent running `v`'s destructor so we are in complete control
|
||||
/// // of the allocation.
|
||||
/// let mut v = mem::ManuallyDrop::new(v);
|
||||
///
|
||||
/// // Pull out the various important pieces of information about `v`
|
||||
/// let p = v.as_mut_ptr();
|
||||
/// let len = v.len();
|
||||
/// let cap = v.capacity();
|
||||
///
|
||||
/// unsafe {
|
||||
/// // Overwrite memory with 4, 5, 6
|
||||
/// for i in 0..len {
|
||||
/// ptr::write(p.add(i), 4 + i);
|
||||
/// }
|
||||
///
|
||||
/// // Put everything back together into a Vec
|
||||
/// let rebuilt = Vec::from_raw_parts(p, len, cap);
|
||||
/// assert_eq!(rebuilt, [4, 5, 6]);
|
||||
/// }
|
||||
/// ```
|
||||
pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self {
|
||||
Self(allocator_api2::vec::Vec::from_raw_parts_in(
|
||||
ptr, length, capacity, SwcAlloc,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Vec<T> {
|
||||
type Target = allocator_api2::vec::Vec<T, SwcAlloc>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Vec<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Vec<T> {
|
||||
fn default() -> Self {
|
||||
Self(allocator_api2::vec::Vec::new_in(SwcAlloc))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for Vec<T> {
|
||||
type IntoIter = allocator_api2::vec::IntoIter<T, SwcAlloc>;
|
||||
type Item = T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
impl<'a, T> IntoIterator for &'a Vec<T> {
|
||||
type IntoIter = std::slice::Iter<'a, T>;
|
||||
type Item = &'a T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a mut Vec<T> {
|
||||
type IntoIter = std::slice::IterMut<'a, T>;
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<T> for Vec<T> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut vec = Vec::default();
|
||||
vec.extend(iter);
|
||||
vec
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Box<[T]>> for Vec<T> {
|
||||
fn from(v: Box<[T]>) -> Self {
|
||||
Self(allocator_api2::vec::Vec::from(v.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for Box<[T]> {
|
||||
fn from(v: Vec<T>) -> Self {
|
||||
Box(v.0.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Extend<T> for Vec<T> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
self.0.extend(iter)
|
||||
}
|
||||
}
|
42
crates/swc_allocator/src/vec/rkyv.rs
Normal file
42
crates/swc_allocator/src/vec/rkyv.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use rkyv::{vec::ArchivedVec, DeserializeUnsized};
|
||||
|
||||
use super::Vec;
|
||||
use crate::boxed::Box;
|
||||
|
||||
impl<T> rkyv::Archive for Vec<T>
|
||||
where
|
||||
T: rkyv::Archive,
|
||||
{
|
||||
type Archived = rkyv::vec::ArchivedVec<T::Archived>;
|
||||
type Resolver = rkyv::vec::VecResolver;
|
||||
|
||||
unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) {
|
||||
rkyv::vec::ArchivedVec::resolve_from_slice(self, pos, resolver, out);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: rkyv::Serialize<S>, S: rkyv::ser::ScratchSpace + rkyv::ser::Serializer + ?Sized>
|
||||
rkyv::Serialize<S> for Vec<T>
|
||||
{
|
||||
#[inline]
|
||||
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
|
||||
ArchivedVec::<T::Archived>::serialize_from_slice(self, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: rkyv::Archive, D: rkyv::Fallible + ?Sized> rkyv::Deserialize<Vec<T>, D>
|
||||
for ArchivedVec<T::Archived>
|
||||
where
|
||||
[T::Archived]: rkyv::DeserializeUnsized<[T], D>,
|
||||
{
|
||||
#[inline]
|
||||
fn deserialize(&self, deserializer: &mut D) -> Result<Vec<T>, D::Error> {
|
||||
unsafe {
|
||||
let data_address =
|
||||
(**self).deserialize_unsized(deserializer, |layout| std::alloc::alloc(layout))?;
|
||||
let metadata = self.as_slice().deserialize_metadata(deserializer)?;
|
||||
let ptr = ptr_meta::from_raw_parts_mut(data_address, metadata);
|
||||
Ok(Box::<[T]>::from_raw(ptr).into())
|
||||
}
|
||||
}
|
||||
}
|
1
crates/swc_allocator/tests/apis.rs
Normal file
1
crates/swc_allocator/tests/apis.rs
Normal file
@ -0,0 +1 @@
|
||||
|
@ -65,6 +65,7 @@ url = { workspace = true }
|
||||
ast_node = { version = "0.9.8", path = "../ast_node" }
|
||||
better_scoped_tls = { version = "0.1.1", path = "../better_scoped_tls" }
|
||||
from_variant = { version = "0.1.8", path = "../from_variant" }
|
||||
swc_allocator = { version = "0.1.1", path = "../swc_allocator" }
|
||||
swc_atoms = { version = "0.6.5", path = "../swc_atoms" }
|
||||
swc_eq_ignore_macros = { version = "0.1.3", path = "../swc_eq_ignore_macros" }
|
||||
swc_visit = { version = "0.5.14", path = "../swc_visit" }
|
||||
|
@ -63,6 +63,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EqIgnoreSpan for swc_allocator::vec::Vec<T>
|
||||
where
|
||||
T: EqIgnoreSpan,
|
||||
{
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
self.len() == other.len()
|
||||
&& self
|
||||
.iter()
|
||||
.zip(other.iter())
|
||||
.all(|(a, b)| a.eq_ignore_span(b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive with `#[derive(TypeEq)]`.
|
||||
pub trait TypeEq {
|
||||
/// **Note**: This method should return `true` for non-type values.
|
||||
@ -172,6 +185,26 @@ macro_rules! deref {
|
||||
|
||||
deref!(Box, Rc, Arc);
|
||||
|
||||
impl<N> EqIgnoreSpan for swc_allocator::boxed::Box<N>
|
||||
where
|
||||
N: EqIgnoreSpan,
|
||||
{
|
||||
#[inline]
|
||||
fn eq_ignore_span(&self, other: &Self) -> bool {
|
||||
(**self).eq_ignore_span(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> TypeEq for swc_allocator::boxed::Box<N>
|
||||
where
|
||||
N: TypeEq,
|
||||
{
|
||||
#[inline]
|
||||
fn type_eq(&self, other: &Self) -> bool {
|
||||
(**self).type_eq(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, N> EqIgnoreSpan for &'a N
|
||||
where
|
||||
N: EqIgnoreSpan,
|
||||
|
@ -190,3 +190,12 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Spanned for swc_allocator::boxed::Box<T>
|
||||
where
|
||||
T: Spanned,
|
||||
{
|
||||
fn span(&self) -> Span {
|
||||
self.as_ref().span()
|
||||
}
|
||||
}
|
||||
|
@ -54,3 +54,18 @@ impl Take for Span {
|
||||
DUMMY_SP
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Take for swc_allocator::boxed::Box<T>
|
||||
where
|
||||
T: Take,
|
||||
{
|
||||
fn dummy() -> Self {
|
||||
swc_allocator::boxed::Box::new(T::dummy())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Take for swc_allocator::vec::Vec<T> {
|
||||
fn dummy() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user