#![allow(clippy::disallowed_types)]
#![feature(arbitrary_self_types_pointers)]
#![feature(allocator_api)]
#![feature(thread_local)]
use core::fmt::Write as _;
use core::mem::{MaybeUninit, size_of};
use core::ptr::{NonNull, addr_of_mut};
use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
use std::collections::HashMap;
pub use bun_mimalloc_sys::mimalloc;
pub mod c_thunks;
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Alignment(pub u8); impl Alignment {
#[inline]
pub const fn of<T>() -> Self {
Self(core::mem::align_of::<T>().trailing_zeros() as u8)
}
#[inline]
pub const fn to_byte_units(self) -> usize {
1usize << self.0
}
#[inline]
pub const fn from_byte_units(b: usize) -> Self {
Self(b.trailing_zeros() as u8)
}
}
#[cfg(windows)]
#[repr(C)]
struct MaxAlignT {
_f: f64,
_i: i64,
_p: *const (),
}
#[cfg(windows)]
pub const MAX_ALIGN_T: usize = core::mem::align_of::<MaxAlignT>();
#[cfg(all(target_os = "freebsd", target_arch = "aarch64"))]
pub const MAX_ALIGN_T: usize = 16;
#[cfg(not(any(windows, all(target_os = "freebsd", target_arch = "aarch64"))))]
pub const MAX_ALIGN_T: usize = core::mem::align_of::<libc::max_align_t>();
pub struct AllocatorVTable {
pub alloc: unsafe fn(*mut core::ffi::c_void, usize, Alignment, usize) -> *mut u8,
pub resize: unsafe fn(*mut core::ffi::c_void, &mut [u8], Alignment, usize, usize) -> bool,
pub remap: unsafe fn(*mut core::ffi::c_void, &mut [u8], Alignment, usize, usize) -> *mut u8,
pub free: unsafe fn(*mut core::ffi::c_void, &mut [u8], Alignment, usize),
}
impl AllocatorVTable {
pub const NO_ALLOC: unsafe fn(*mut core::ffi::c_void, usize, Alignment, usize) -> *mut u8 =
|_, _, _, _| core::ptr::null_mut();
pub const NO_RESIZE: unsafe fn(
*mut core::ffi::c_void,
&mut [u8],
Alignment,
usize,
usize,
) -> bool = |_, _, _, _, _| false;
pub const NO_REMAP: unsafe fn(
*mut core::ffi::c_void,
&mut [u8],
Alignment,
usize,
usize,
) -> *mut u8 = |_, _, _, _, _| core::ptr::null_mut();
pub const fn free_only(
free: unsafe fn(*mut core::ffi::c_void, &mut [u8], Alignment, usize),
) -> Self {
Self {
alloc: Self::NO_ALLOC,
resize: Self::NO_RESIZE,
remap: Self::NO_REMAP,
free,
}
}
}
#[derive(Clone, Copy)]
pub struct StdAllocator {
pub ptr: *mut core::ffi::c_void,
pub vtable: &'static AllocatorVTable,
}
pub type VTable = AllocatorVTable;
unsafe impl Send for StdAllocator {}
unsafe impl Sync for StdAllocator {}
impl Default for StdAllocator {
#[inline]
fn default() -> Self {
basic::C_ALLOCATOR
}
}
impl StdAllocator {
#[inline]
pub fn raw_alloc(&self, len: usize, alignment: Alignment, ra: usize) -> Option<*mut u8> {
let p = unsafe { (self.vtable.alloc)(self.ptr, len, alignment, ra) };
if p.is_null() { None } else { Some(p) }
}
#[inline]
pub fn raw_resize(
&self,
buf: &mut [u8],
alignment: Alignment,
new_len: usize,
ra: usize,
) -> bool {
unsafe { (self.vtable.resize)(self.ptr, buf, alignment, new_len, ra) }
}
#[inline]
pub fn raw_remap(
&self,
buf: &mut [u8],
alignment: Alignment,
new_len: usize,
ra: usize,
) -> Option<*mut u8> {
let p = unsafe { (self.vtable.remap)(self.ptr, buf, alignment, new_len, ra) };
if p.is_null() { None } else { Some(p) }
}
#[inline]
pub fn raw_free(&self, buf: &mut [u8], alignment: Alignment, ra: usize) {
unsafe { (self.vtable.free)(self.ptr, buf, alignment, ra) }
}
#[inline]
pub fn free(&self, bytes: &[u8]) {
if bytes.is_empty() {
return;
}
let buf =
unsafe { core::slice::from_raw_parts_mut(bytes.as_ptr().cast_mut(), bytes.len()) };
self.raw_free(buf, Alignment::from_byte_units(1), 0);
}
}
pub struct FixedBufferAllocator<'a> {
end: usize,
buffer: &'a mut [u8],
}
impl<'a> FixedBufferAllocator<'a> {
#[inline]
pub fn init(buffer: &'a mut [u8]) -> Self {
Self { end: 0, buffer }
}
#[inline]
pub fn reset(&mut self) {
self.end = 0;
}
#[inline]
pub fn owns_ptr(&self, p: *const u8) -> bool {
let base = self.buffer.as_ptr() as usize;
let q = p as usize;
q >= base && q < base + self.buffer.len()
}
pub fn alloc(&mut self, len: usize, alignment: Alignment, _ra: usize) -> Option<*mut u8> {
let base = self.buffer.as_mut_ptr() as usize;
let aligned =
(base + self.end + alignment.to_byte_units() - 1) & !(alignment.to_byte_units() - 1);
let new_end = (aligned - base).checked_add(len)?;
if new_end > self.buffer.len() {
return None;
}
self.end = new_end;
Some(aligned as *mut u8)
}
pub fn resize(&mut self, buf: &mut [u8], _a: Alignment, new_len: usize, _ra: usize) -> bool {
let buf_end = buf.as_ptr() as usize - self.buffer.as_ptr() as usize + buf.len();
if buf_end != self.end {
return new_len <= buf.len();
}
let new_end = buf_end - buf.len() + new_len;
if new_end > self.buffer.len() {
return false;
}
self.end = new_end;
true
}
#[inline]
pub fn remap(
&mut self,
buf: &mut [u8],
a: Alignment,
new_len: usize,
ra: usize,
) -> Option<*mut u8> {
if self.resize(buf, a, new_len, ra) {
Some(buf.as_mut_ptr())
} else {
None
}
}
#[inline]
pub fn free(&mut self, buf: &mut [u8], _a: Alignment, _ra: usize) {
let buf_end = buf.as_ptr() as usize - self.buffer.as_ptr() as usize + buf.len();
if buf_end == self.end {
self.end -= buf.len();
}
}
}
pub use mimalloc_arena::MimallocArena;
pub type Arena = MimallocArena;
pub type Bump = bumpalo::Bump;
mod baby_vec;
pub use baby_vec::BabyVec;
pub type ArenaVec<'a, T> = BabyVec<'a, T>;
pub use mimalloc_arena::{ArenaString, ArenaVecExt};
#[inline]
pub fn vec_from_iter_in<'a, T, I>(iter: I, arena: &'a MimallocArena) -> ArenaVec<'a, T>
where
I: IntoIterator<Item = T>,
{
let iter = iter.into_iter();
let (lo, _) = iter.size_hint();
let mut v = ArenaVec::with_capacity_in(lo, arena);
v.extend(iter);
v
}
#[inline]
pub fn transfer_arena<'a, T>(v: &mut ArenaVec<'a, T>, dst: &'a MimallocArena) {
v.set_allocator(dst);
}
#[macro_export]
macro_rules! arena_format {
(in $arena:expr, $($arg:tt)*) => {{
let mut __s = $crate::ArenaString::new_in($arena);
::core::fmt::Write::write_fmt(&mut __s, ::core::format_args!($($arg)*))
.expect("ArenaString::write_fmt is infallible");
__s
}};
}
pub const USE_MIMALLOC: bool = cfg!(not(bun_asan));
#[path = "BufferFallbackAllocator.rs"]
pub mod buffer_fallback_allocator;
pub mod fallback;
#[path = "MaxHeapAllocator.rs"]
pub mod max_heap_allocator;
pub mod maybe_owned;
#[path = "NullableAllocator.rs"]
pub mod nullable_allocator;
pub mod stack_fallback;
pub mod default_alloc {
use core::ffi::c_void;
#[inline]
pub fn malloc(size: usize) -> *mut c_void {
if cfg!(bun_asan) {
unsafe { libc::malloc(size) }
} else {
crate::mimalloc::mi_malloc(size)
}
}
#[inline]
pub fn zalloc(size: usize) -> *mut c_void {
if cfg!(bun_asan) {
unsafe { libc::calloc(1, size) }
} else {
crate::mimalloc::mi_zalloc(size)
}
}
#[inline]
pub fn calloc(count: usize, size: usize) -> *mut c_void {
if cfg!(bun_asan) {
unsafe { libc::calloc(count, size) }
} else {
crate::mimalloc::mi_calloc(count, size)
}
}
#[inline]
pub unsafe fn realloc(ptr: *mut c_void, new_size: usize) -> *mut c_void {
if cfg!(bun_asan) {
unsafe { libc::realloc(ptr, new_size) }
} else {
unsafe { crate::mimalloc::mi_realloc(ptr, new_size) }
}
}
#[inline]
pub unsafe fn free(ptr: *mut c_void) {
if cfg!(bun_asan) {
unsafe { libc::free(ptr) }
} else {
unsafe { crate::mimalloc::mi_free(ptr) }
}
}
#[inline]
pub unsafe fn usable_size(ptr: *const c_void) -> usize {
if ptr.is_null() {
return 0;
}
#[cfg(all(bun_asan, target_os = "linux"))]
return unsafe { libc::malloc_usable_size(ptr.cast_mut()) };
#[cfg(all(bun_asan, target_os = "macos"))]
return unsafe { libc::malloc_size(ptr) };
#[cfg(not(any(all(bun_asan, target_os = "linux"), all(bun_asan, target_os = "macos"))))]
return unsafe { crate::mimalloc::mi_usable_size(ptr) };
}
#[cfg(not(bun_asan))]
#[inline]
pub fn malloc_aligned(size: usize, align: usize) -> *mut c_void {
crate::mimalloc::mi_malloc_auto_align(size, align)
}
#[cfg(bun_asan)]
#[inline]
pub fn malloc_aligned(size: usize, align: usize) -> *mut c_void {
if align <= crate::MAX_ALIGN_T {
return unsafe { libc::malloc(size) };
}
let mut p: *mut c_void = core::ptr::null_mut();
let align = align.max(core::mem::size_of::<*mut c_void>());
if unsafe { libc::posix_memalign(&mut p, align, size) } != 0 {
return core::ptr::null_mut();
}
p
}
#[cfg(not(bun_asan))]
#[inline]
pub fn zalloc_aligned(size: usize, align: usize) -> *mut c_void {
crate::mimalloc::mi_zalloc_auto_align(size, align)
}
#[cfg(bun_asan)]
#[inline]
pub fn zalloc_aligned(size: usize, align: usize) -> *mut c_void {
if align <= crate::MAX_ALIGN_T {
return unsafe { libc::calloc(1, size) };
}
let p = malloc_aligned(size, align);
if !p.is_null() {
unsafe { core::ptr::write_bytes(p.cast::<u8>(), 0, size) };
}
p
}
#[cfg(not(bun_asan))]
#[inline]
pub unsafe fn realloc_aligned(ptr: *mut c_void, new_size: usize, align: usize) -> *mut c_void {
unsafe { crate::mimalloc::mi_realloc_aligned(ptr, new_size, align) }
}
#[cfg(bun_asan)]
#[inline]
pub unsafe fn realloc_aligned(ptr: *mut c_void, new_size: usize, align: usize) -> *mut c_void {
if align <= crate::MAX_ALIGN_T {
return unsafe { libc::realloc(ptr, new_size) };
}
let new_ptr = malloc_aligned(new_size, align);
if new_ptr.is_null() {
return core::ptr::null_mut();
}
if !ptr.is_null() {
unsafe {
let copy = usable_size(ptr).min(new_size);
core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), new_ptr.cast::<u8>(), copy);
libc::free(ptr);
}
}
new_ptr
}
}
pub use buffer_fallback_allocator::BufferFallbackAllocator;
pub use max_heap_allocator::MaxHeapAllocator;
pub use maybe_owned::MaybeOwned;
pub use nullable_allocator::NullableAllocator;
pub use stack_fallback::{ArenaPtr, StackFallback};
#[path = "MimallocArena.rs"]
pub mod mimalloc_arena;
pub mod ast_alloc;
pub use ast_alloc::{AstAlloc, AstVec};
mod hashbrown_bridge;
pub use allocator_api2::alloc::Allocator as HashbrownAllocator;
pub const SEP_STR: &str = if cfg!(windows) { "\\" } else { "/" };
pub const SEP: u8 = if cfg!(windows) { b'\\' } else { b'/' };
#[inline]
pub fn trim_right<'a>(s: &'a [u8], chars: &[u8]) -> &'a [u8] {
let mut end = s.len();
while end > 0 && chars.contains(&s[end - 1]) {
end -= 1;
}
&s[..end]
}
#[inline]
pub fn trim_left<'a>(s: &'a [u8], chars: &[u8]) -> &'a [u8] {
let mut begin = 0usize;
while begin < s.len() && chars.contains(&s[begin]) {
begin += 1;
}
&s[begin..]
}
#[inline]
pub fn trim<'a>(s: &'a [u8], chars: &[u8]) -> &'a [u8] {
trim_right(trim_left(s, chars), chars)
}
pub fn copy_lowercase<'a>(in_: &[u8], out: &'a mut [u8]) -> &'a [u8] {
let mut in_slice = in_;
let mut out_off: usize = 0;
'begin: loop {
for (i, &c) in in_slice.iter().enumerate() {
if let b'A'..=b'Z' = c {
out[out_off..out_off + i].copy_from_slice(&in_slice[0..i]);
out[out_off + i] = c.to_ascii_lowercase();
let end = i + 1;
in_slice = &in_slice[end..];
out_off += end;
continue 'begin;
}
}
out[out_off..out_off + in_slice.len()].copy_from_slice(in_slice);
break;
}
&out[0..in_.len()]
}
pub fn copy_lowercase_if_needed<'a>(in_: &'a [u8], out: &'a mut [u8]) -> &'a [u8] {
if in_.iter().any(u8::is_ascii_uppercase) {
copy_lowercase(in_, out)
} else {
in_
}
}
#[inline]
pub fn ascii_lowercase_buf<const N: usize>(input: &[u8]) -> Option<([u8; N], usize)> {
if input.len() > N {
return None;
}
let mut buf = [0u8; N];
copy_lowercase(input, &mut buf[..input.len()]);
Some((buf, input.len()))
}
#[inline(always)]
pub(crate) fn alloc_result<T>(
p: *mut T,
size: usize,
) -> core::result::Result<NonNull<[u8]>, core::alloc::AllocError> {
NonNull::new(p.cast::<u8>())
.map(|p| NonNull::slice_from_raw_parts(p, size))
.ok_or(core::alloc::AllocError)
}
#[inline]
pub fn fmt_count(args: core::fmt::Arguments<'_>) -> usize {
struct Discarding(usize);
impl core::fmt::Write for Discarding {
#[inline]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.0 += s.len();
Ok(())
}
}
let mut w = Discarding(0);
let _ = core::fmt::write(&mut w, args);
w.0
}
pub struct SliceCursor<'a> {
pub buf: &'a mut [u8],
pub at: usize,
}
impl<'a> SliceCursor<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> Self {
Self { buf, at: 0 }
}
}
impl core::fmt::Write for SliceCursor<'_> {
#[inline]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let bytes = s.as_bytes();
let end = self.at + bytes.len();
if end > self.buf.len() {
return Err(core::fmt::Error);
}
self.buf[self.at..end].copy_from_slice(bytes);
self.at = end;
Ok(())
}
}
pub fn buf_print<'a>(
buf: &'a mut [u8],
args: core::fmt::Arguments<'_>,
) -> core::result::Result<&'a [u8], core::fmt::Error> {
let mut c = SliceCursor { buf, at: 0 };
core::fmt::write(&mut c, args)?;
let len = c.at;
Ok(&c.buf[..len])
}
#[inline]
pub fn buf_print_len(
buf: &mut [u8],
args: core::fmt::Arguments<'_>,
) -> core::result::Result<usize, core::fmt::Error> {
let mut c = SliceCursor { buf, at: 0 };
core::fmt::write(&mut c, args)?;
Ok(c.at)
}
pub struct Mutex(std::sync::Mutex<()>);
impl Mutex {
pub const fn new() -> Self {
Self(std::sync::Mutex::new(()))
}
#[inline]
pub fn lock(&self) -> MutexGuard {
let g = self
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let _guard = unsafe {
core::mem::transmute::<std::sync::MutexGuard<'_, ()>, std::sync::MutexGuard<'static, ()>>(
g,
)
};
MutexGuard { _guard }
}
}
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard {
_guard: std::sync::MutexGuard<'static, ()>,
}
impl Default for Mutex {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AllocError;
impl AllocError {
#[inline]
pub const fn name(self) -> &'static str {
"OutOfMemory"
}
}
#[macro_export]
macro_rules! oom_from_alloc {
($($t:ty),+ $(,)?) => { $(
impl ::core::convert::From<$crate::AllocError> for $t {
#[inline]
fn from(_: $crate::AllocError) -> Self { <$t>::OutOfMemory }
}
)+ };
}
pub struct Mimalloc;
use mimalloc::MI_MAX_ALIGN_SIZE;
unsafe impl core::alloc::GlobalAlloc for Mimalloc {
#[inline]
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
mimalloc::mi_malloc_auto_align(layout.size(), layout.align()).cast()
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 {
mimalloc::mi_zalloc_auto_align(layout.size(), layout.align()).cast()
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: core::alloc::Layout) {
unsafe { mimalloc::mi_free(ptr.cast()) }
}
#[inline]
unsafe fn realloc(
&self,
ptr: *mut u8,
layout: core::alloc::Layout,
new_size: usize,
) -> *mut u8 {
unsafe {
if layout.align() <= MI_MAX_ALIGN_SIZE {
mimalloc::mi_realloc(ptr.cast(), new_size)
} else {
mimalloc::mi_realloc_aligned(ptr.cast(), new_size, layout.align())
}
}
.cast()
}
}
pub unsafe fn realloc_slice(
slice: &mut [u8],
new_size: usize,
) -> core::result::Result<&mut [u8], AllocError> {
let new_ptr = unsafe { mimalloc::mi_realloc(slice.as_mut_ptr().cast(), new_size) };
if new_ptr.is_null() {
return Err(AllocError);
}
Ok(unsafe { core::slice::from_raw_parts_mut(new_ptr.cast::<u8>(), new_size) })
}
pub unsafe fn realloc_raw(
ptr: *mut u8,
new_size: usize,
) -> core::result::Result<*mut u8, AllocError> {
let new_ptr = unsafe { mimalloc::mi_realloc(ptr.cast(), new_size) };
if new_ptr.is_null() {
return Err(AllocError);
}
Ok(new_ptr.cast::<u8>())
}
#[inline]
pub fn usable_size(ptr: *const u8) -> usize {
unsafe { mimalloc::mi_usable_size(ptr.cast()) }
}
#[cold]
#[inline(never)]
pub fn out_of_memory() -> ! {
#[cfg(not(test))]
{
unsafe extern "Rust" {
safe fn __bun_crash_handler_out_of_memory() -> !;
}
__bun_crash_handler_out_of_memory()
}
#[cfg(test)]
{
let _ = std::io::Write::write_all(&mut std::io::stderr(), b"bun: out of memory\n");
std::process::abort()
}
}
static PAGE_SIZE: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
#[inline]
pub fn page_size() -> usize {
*PAGE_SIZE.get_or_init(|| {
#[cfg(unix)]
{
unsafe extern "C" {
safe fn sysconf(name: core::ffi::c_int) -> core::ffi::c_long;
}
sysconf(libc::_SC_PAGESIZE) as usize
}
#[cfg(windows)]
{
#[repr(C)]
struct SystemInfo {
_w_processor_architecture: u16,
_w_reserved: u16,
dw_page_size: u32,
_tail: [*mut core::ffi::c_void; 3],
_ints: [u32; 5],
}
unsafe extern "system" {
safe fn GetSystemInfo(lpSystemInfo: &mut SystemInfo);
}
let mut info = SystemInfo {
_w_processor_architecture: 0,
_w_reserved: 0,
dw_page_size: 0,
_tail: [core::ptr::null_mut(); 3],
_ints: [0; 5],
};
GetSystemInfo(&mut info);
info.dw_page_size as usize
}
})
}
#[unsafe(no_mangle)]
pub extern "C" fn WTF__releaseFastMallocFreeMemoryForThisThread() {
mimalloc::mi_collect(false);
}
pub mod wtf {
#[inline]
pub fn release_fast_malloc_free_memory_for_this_thread() {
crate::WTF__releaseFastMallocFreeMemoryForThisThread();
}
}
#[cfg(test)]
mod wtf_release_tests {
use super::*;
use core::ffi::c_void;
#[test]
fn release_fast_malloc_twice_no_panic() {
WTF__releaseFastMallocFreeMemoryForThisThread();
WTF__releaseFastMallocFreeMemoryForThisThread();
wtf::release_fast_malloc_free_memory_for_this_thread();
}
#[test]
fn allocate_with_mi_malloc_then_collect_no_panic() {
let p: *mut c_void = mimalloc::mi_malloc(4096);
assert!(!p.is_null(), "mi_malloc(4096) must succeed");
unsafe { mimalloc::mi_free(p) };
WTF__releaseFastMallocFreeMemoryForThisThread();
WTF__releaseFastMallocFreeMemoryForThisThread();
}
}
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Tag {
Dead = 0,
WTFStringImpl = 1,
ZigString = 2,
StaticZigString = 3,
Empty = 4,
}
pub const ZS_STATIC_BIT: usize = 1usize << 60;
pub const ZS_UTF8_BIT: usize = 1usize << 61;
pub const ZS_GLOBAL_BIT: usize = 1usize << 62;
pub const ZS_16BIT_BIT: usize = 1usize << 63;
pub const ZS_UNTAG_MASK: usize = (1usize << 53) - 1;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ZigString {
pub _unsafe_ptr_do_not_use: *const u8,
pub len: usize,
}
impl ZigString {
pub const EMPTY: ZigString = ZigString {
_unsafe_ptr_do_not_use: b"".as_ptr(),
len: 0,
};
#[inline]
pub const fn init(slice: &[u8]) -> ZigString {
ZigString {
_unsafe_ptr_do_not_use: slice.as_ptr(),
len: slice.len(),
}
}
#[inline]
pub const fn from_tagged_ptr(ptr: *const u8, len: usize) -> ZigString {
ZigString {
_unsafe_ptr_do_not_use: ptr,
len,
}
}
#[inline]
pub const fn tagged_ptr(&self) -> *const u8 {
self._unsafe_ptr_do_not_use
}
#[inline]
pub fn init_utf16(items: &[u16]) -> ZigString {
let mut out = ZigString {
_unsafe_ptr_do_not_use: items.as_ptr().cast(),
len: items.len(),
};
out.mark_utf16();
out
}
#[inline]
pub const fn length(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn is_16bit(&self) -> bool {
(self._unsafe_ptr_do_not_use as usize) & ZS_16BIT_BIT != 0
}
#[inline]
pub fn is_utf8(&self) -> bool {
(self._unsafe_ptr_do_not_use as usize) & ZS_UTF8_BIT != 0
}
#[inline]
pub fn is_globally_allocated(&self) -> bool {
(self._unsafe_ptr_do_not_use as usize) & ZS_GLOBAL_BIT != 0
}
#[inline]
pub fn is_static(&self) -> bool {
(self._unsafe_ptr_do_not_use as usize) & ZS_STATIC_BIT != 0
}
#[inline]
pub fn mark_utf16(&mut self) {
self._unsafe_ptr_do_not_use =
((self._unsafe_ptr_do_not_use as usize) | ZS_16BIT_BIT) as *const u8;
}
#[inline]
pub fn mark_utf8(&mut self) {
self._unsafe_ptr_do_not_use =
((self._unsafe_ptr_do_not_use as usize) | ZS_UTF8_BIT) as *const u8;
}
#[inline]
pub fn mark_global(&mut self) {
self._unsafe_ptr_do_not_use =
((self._unsafe_ptr_do_not_use as usize) | ZS_GLOBAL_BIT) as *const u8;
}
#[inline]
pub fn mark_static(&mut self) {
self._unsafe_ptr_do_not_use =
((self._unsafe_ptr_do_not_use as usize) | ZS_STATIC_BIT) as *const u8;
}
#[inline]
pub fn untagged(ptr: *const u8) -> *const u8 {
((ptr as usize) & ZS_UNTAG_MASK) as *const u8
}
#[inline]
pub fn slice(&self) -> &[u8] {
if self.len == 0 {
return &[];
}
debug_assert!(
!self.is_16bit(),
"ZigString::slice() on UTF-16 string; use to_slice()"
);
unsafe {
core::slice::from_raw_parts(
Self::untagged(self._unsafe_ptr_do_not_use),
core::cmp::min(self.len, u32::MAX as usize),
)
}
}
#[inline]
pub fn utf16_slice_aligned(&self) -> &[u16] {
if self.len == 0 {
return &[];
}
debug_assert!(self.is_16bit());
unsafe {
core::slice::from_raw_parts(
((self._unsafe_ptr_do_not_use as usize) & ZS_UNTAG_MASK) as *const u16,
self.len,
)
}
}
}
#[repr(C)]
pub struct WTFStringImplStruct {
pub m_ref_count: core::cell::Cell<u32>,
pub m_length: u32,
pub m_ptr: WTFStringImplPtr,
pub m_hash_and_flags: core::cell::Cell<u32>,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union WTFStringImplPtr {
pub latin1: *const u8,
pub utf16: *const u16,
}
pub type WTFStringImpl = *mut WTFStringImplStruct;
impl WTFStringImplStruct {
pub const MAX: u32 = u32::MAX;
pub const S_HASH_FLAG_8BIT_BUFFER: u32 = 1 << 2;
pub const S_REF_COUNT_FLAG_IS_STATIC_STRING: u32 = 0x1;
pub const S_REF_COUNT_INCREMENT: u32 = 0x2;
#[inline]
pub fn length(&self) -> u32 {
self.m_length
}
#[inline]
pub fn is_8bit(&self) -> bool {
(self.m_hash_and_flags.get() & Self::S_HASH_FLAG_8BIT_BUFFER) != 0
}
#[inline]
pub fn byte_length(&self) -> usize {
if self.is_8bit() {
self.m_length as usize
} else {
(self.m_length as usize) * 2
}
}
#[inline]
pub fn memory_cost(&self) -> usize {
self.byte_length()
}
#[inline]
pub fn ref_count(&self) -> u32 {
self.m_ref_count.get() / Self::S_REF_COUNT_INCREMENT
}
#[inline]
pub fn is_static(&self) -> bool {
self.m_ref_count.get() & Self::S_REF_COUNT_FLAG_IS_STATIC_STRING != 0
}
#[inline]
pub fn has_at_least_one_ref(&self) -> bool {
self.m_ref_count.get() > 0
}
#[inline(always)]
fn ref_count_atomic(&self) -> &AtomicU32 {
unsafe { AtomicU32::from_ptr(self.m_ref_count.as_ptr()) }
}
#[inline]
pub fn r#ref(&self) {
let old = self
.ref_count_atomic()
.fetch_add(Self::S_REF_COUNT_INCREMENT, Ordering::Relaxed);
debug_assert!(old > 0); debug_assert!(
old.wrapping_add(Self::S_REF_COUNT_INCREMENT) / Self::S_REF_COUNT_INCREMENT
> old / Self::S_REF_COUNT_INCREMENT
|| old & Self::S_REF_COUNT_FLAG_IS_STATIC_STRING != 0
);
let _ = old;
}
#[inline]
pub fn deref(&self) {
let old = self
.ref_count_atomic()
.fetch_sub(Self::S_REF_COUNT_INCREMENT, Ordering::Relaxed);
debug_assert!(old > 0); if old != Self::S_REF_COUNT_INCREMENT {
return;
}
unsafe { Bun__WTFStringImpl__destroy(self) };
}
#[inline]
pub fn ref_count_allocator(self: *mut Self) -> StdAllocator {
StdAllocator {
ptr: self.cast(),
vtable: StringImplAllocator::VTABLE_PTR,
}
}
#[inline(always)]
pub fn raw_bytes(&self, len: usize) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.m_ptr.latin1, len) }
}
#[inline]
pub fn byte_slice(&self) -> &[u8] {
self.raw_bytes(self.byte_length())
}
#[inline]
pub fn latin1_slice(&self) -> &[u8] {
debug_assert!(self.is_8bit());
self.raw_bytes(self.m_length as usize)
}
#[inline]
pub fn utf16_slice(&self) -> &[u16] {
debug_assert!(!self.is_8bit());
unsafe { core::slice::from_raw_parts(self.m_ptr.utf16, self.m_length as usize) }
}
#[inline]
pub fn utf16_byte_length(&self) -> usize {
if self.is_8bit() {
self.m_length as usize * 2
} else {
self.m_length as usize
}
}
#[inline]
pub fn latin1_byte_length(&self) -> usize {
self.m_length as usize
}
#[inline]
pub fn is_thread_safe(&self) -> bool {
WTFStringImpl__isThreadSafe(self)
}
#[inline]
pub fn ensure_hash(&self) {
Bun__WTFStringImpl__ensureHash(self);
}
#[inline]
pub fn has_prefix(&self, text: &[u8]) -> bool {
unsafe { Bun__WTFStringImpl__hasPrefix(self, text.as_ptr(), text.len()) }
}
#[inline]
pub fn to_zig_string(&self) -> ZigString {
if self.is_8bit() {
ZigString::init(self.latin1_slice())
} else {
ZigString::init_utf16(self.utf16_slice())
}
}
}
unsafe extern "C" {
pub fn Bun__WTFStringImpl__destroy(this: *const WTFStringImplStruct);
pub safe fn Bun__WTFStringImpl__ref(this: &WTFStringImplStruct);
pub fn Bun__WTFStringImpl__deref(this: *const WTFStringImplStruct);
safe fn WTFStringImpl__isThreadSafe(this: &WTFStringImplStruct) -> bool;
safe fn Bun__WTFStringImpl__ensureHash(this: &WTFStringImplStruct);
fn Bun__WTFStringImpl__hasPrefix(
this: *const WTFStringImplStruct,
text_ptr: *const u8,
text_len: usize,
) -> bool;
}
#[allow(non_snake_case)] pub mod StringImplAllocator {
use super::{Alignment, AllocatorVTable, WTFStringImplStruct};
unsafe fn alloc(ptr: *mut core::ffi::c_void, len: usize, _: Alignment, _: usize) -> *mut u8 {
let this = unsafe { &*ptr.cast::<WTFStringImplStruct>() };
if this.byte_length() != len {
return core::ptr::null_mut();
}
this.r#ref();
unsafe { this.m_ptr.latin1 }.cast_mut()
}
unsafe fn free(ptr: *mut core::ffi::c_void, buf: &mut [u8], _: Alignment, _: usize) {
let this = unsafe { &*ptr.cast::<WTFStringImplStruct>() };
debug_assert!(this.byte_slice().as_ptr() == buf.as_ptr());
debug_assert!(this.byte_length() == buf.len());
this.deref();
}
pub static VTABLE: AllocatorVTable = AllocatorVTable {
alloc,
resize: AllocatorVTable::NO_RESIZE,
remap: AllocatorVTable::NO_REMAP,
free,
};
pub const VTABLE_PTR: &AllocatorVTable = &VTABLE;
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union StringImpl {
pub zig_string: ZigString,
pub wtf_string_impl: WTFStringImpl,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct String {
pub tag: Tag,
pub value: StringImpl,
}
impl String {
pub const NAME: &'static str = "BunString";
#[inline]
pub fn is_wtf_allocator(alloc: StdAllocator) -> bool {
core::ptr::eq(alloc.vtable, StringImplAllocator::VTABLE_PTR)
}
pub const EMPTY: String = String {
tag: Tag::Empty,
value: StringImpl {
zig_string: ZigString::EMPTY,
},
};
pub const DEAD: String = String {
tag: Tag::Dead,
value: StringImpl {
zig_string: ZigString::EMPTY,
},
};
#[inline(always)]
fn wtf_impl(&self) -> &WTFStringImplStruct {
debug_assert_eq!(self.tag, Tag::WTFStringImpl);
unsafe { &*self.value.wtf_string_impl }
}
#[inline]
pub fn to_zig_string(&self) -> ZigString {
match self.tag {
Tag::StaticZigString | Tag::ZigString => {
unsafe { self.value.zig_string }
}
Tag::WTFStringImpl => self.wtf_impl().to_zig_string(),
_ => ZigString::EMPTY,
}
}
#[inline]
pub fn length(&self) -> usize {
if self.tag == Tag::WTFStringImpl {
self.wtf_impl().length() as usize
} else {
self.to_zig_string().length()
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.length() == 0
}
#[inline]
pub fn is_8bit(&self) -> bool {
match self.tag {
Tag::WTFStringImpl => self.wtf_impl().is_8bit(),
Tag::StaticZigString | Tag::ZigString => {
unsafe { !self.value.zig_string.is_16bit() }
}
_ => true,
}
}
pub fn eql_comptime(&self, other: &[u8]) -> bool {
let zs = self.to_zig_string();
if zs.is_16bit() {
let u16s = zs.utf16_slice_aligned();
if u16s.len() != other.len() {
return false;
}
u16s.iter()
.copied()
.zip(other.iter().copied())
.all(|(a, b)| a == b as u16)
} else {
zs.slice() == other
}
}
}
impl core::fmt::Display for String {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let zs = self.to_zig_string();
if zs.len == 0 {
return Ok(());
}
if zs.is_16bit() {
for c in core::char::decode_utf16(zs.utf16_slice_aligned().iter().copied()) {
f.write_char(c.unwrap_or(core::char::REPLACEMENT_CHARACTER))?;
}
Ok(())
} else if zs.is_utf8() {
f.write_str(&std::string::String::from_utf8_lossy(zs.slice()))
} else {
for &b in zs.slice() {
f.write_char(b as char)?;
}
Ok(())
}
}
}
pub fn is_slice_in_buffer_t<T>(slice: &[T], buffer: &[T]) -> bool {
let slice_ptr = slice.as_ptr() as usize;
let buffer_ptr = buffer.as_ptr() as usize;
buffer_ptr <= slice_ptr
&& (slice_ptr + std::mem::size_of_val(slice))
<= (buffer_ptr + std::mem::size_of_val(buffer))
}
pub fn is_slice_in_buffer(slice: &[u8], buffer: &[u8]) -> bool {
is_slice_in_buffer_t::<u8>(slice, buffer)
}
pub fn range_of_slice_in_buffer(slice: &[u8], buffer: &[u8]) -> Option<[u32; 2]> {
if !is_slice_in_buffer(slice, buffer) {
return None;
}
let r = [
(slice.as_ptr() as usize).saturating_sub(buffer.as_ptr() as usize) as u32,
slice.len() as u32,
];
debug_assert_eq!(slice, &buffer[r[0] as usize..][..r[1] as usize]);
Some(r)
}
#[inline]
pub unsafe fn default_free(ptr: *mut u8, len: usize) {
if ptr.is_null() || len == 0 {
return;
}
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
basic::C_ALLOCATOR.raw_free(buf, Alignment::from_byte_units(1), 0);
}
pub fn default_dupe(src: &[u8]) -> &'static [u8] {
if src.is_empty() {
return b"";
}
let ptr = basic::C_ALLOCATOR
.raw_alloc(src.len(), Alignment::from_byte_units(1), 0)
.unwrap_or_else(|| crate::out_of_memory());
unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), ptr, src.len());
core::slice::from_raw_parts(ptr, src.len())
}
}
#[inline]
pub unsafe fn secure_zero(p: *mut u8, len: usize) {
unsafe { core::ptr::write_bytes(p, 0, len) };
core::hint::black_box(p);
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
pub fn free_sensitive<T: Copy>(mut slice: Box<[T]>) {
unsafe {
let len = core::mem::size_of_val::<[T]>(&slice);
secure_zero(slice.as_mut_ptr().cast::<u8>(), len);
}
drop(slice);
}
pub unsafe fn free_sensitive_cstr(p: *const core::ffi::c_char) {
if p.is_null() {
return;
}
unsafe {
let len = libc::strlen(p);
secure_zero(p as *mut u8, len);
crate::default_alloc::free(p as *mut core::ffi::c_void);
}
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub struct IndexType(u32);
impl IndexType {
#[inline]
pub const fn new(index: u32, is_overflow: bool) -> Self {
Self((index & 0x7FFF_FFFF) | ((is_overflow as u32) << 31))
}
#[inline]
pub const fn index(self) -> u32 {
self.0 & 0x7FFF_FFFF
}
#[inline]
pub const fn is_overflow(self) -> bool {
(self.0 >> 31) != 0
}
#[inline]
pub fn set_index(&mut self, index: u32) {
self.0 = (self.0 & 0x8000_0000) | (index & 0x7FFF_FFFF);
}
#[inline]
pub fn set_is_overflow(&mut self, v: bool) {
self.0 = (self.0 & 0x7FFF_FFFF) | ((v as u32) << 31);
}
#[inline]
pub const fn raw(self) -> u32 {
self.0
}
}
pub const NOT_FOUND: IndexType = IndexType::new(u32::MAX >> 1, false); pub const UNASSIGNED: IndexType = IndexType::new((u32::MAX >> 1) - 1, false);
#[repr(u8)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum ItemStatus {
Unknown,
Exists,
NotFound,
}
pub mod allocators {
pub use super::*;
}
#[macro_export]
macro_rules! bss_singleton {
($(#[$m:meta])* $vis:vis fn $name:ident() -> $ty:ty) => {
$(#[$m])*
#[inline(always)]
$vis fn $name() -> *mut $ty {
static STORAGE: ::core::sync::atomic::AtomicPtr<$ty> =
::core::sync::atomic::AtomicPtr::new(::core::ptr::null_mut());
let p = STORAGE.load(::core::sync::atomic::Ordering::Acquire);
if !p.is_null() {
return p;
}
#[cold]
#[inline(never)]
fn slow() -> *mut $ty {
let p = $crate::bss_heap_init::<$ty>(<$ty>::init_at).as_ptr();
match STORAGE.compare_exchange(
::core::ptr::null_mut(),
p,
::core::sync::atomic::Ordering::AcqRel,
::core::sync::atomic::Ordering::Acquire,
) {
Ok(_) => p,
Err(winner) => winner,
}
}
slow()
}
};
}
#[doc(hidden)] #[inline]
pub fn bss_heap_init<T>(init_at: unsafe fn(*mut T)) -> NonNull<T> {
let ptr = bss_lazy_bytes(size_of::<T>(), core::mem::align_of::<T>()).cast::<T>();
unsafe { init_at(ptr.as_ptr()) };
ptr
}
#[doc(hidden)]
#[inline]
pub fn bss_lazy_bytes(size: usize, align: usize) -> NonNull<u8> {
debug_assert!(size > 0);
#[cfg(unix)]
let ptr = {
debug_assert!(align <= 4096 && align.is_power_of_two());
bss_arena_bump(size, align)
};
#[cfg(not(unix))]
let ptr = {
mimalloc::mi_zalloc_aligned(size, align).cast::<u8>()
};
NonNull::new(ptr).expect("OOM")
}
#[cfg(unix)]
const BSS_ARENA_SIZE: usize = 4 * 1024 * 1024;
#[cfg(unix)]
fn bss_arena_bump(size: usize, align: usize) -> *mut u8 {
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
static BASE: AtomicPtr<u8> = AtomicPtr::new(core::ptr::null_mut());
static CURSOR: AtomicUsize = AtomicUsize::new(0);
let mut base = BASE.load(Ordering::Acquire);
if base.is_null() {
#[cold]
#[inline(never)]
fn map_arena() -> *mut u8 {
bss_mmap_noreserve(BSS_ARENA_SIZE)
}
let fresh = map_arena();
base = match BASE.compare_exchange(
core::ptr::null_mut(),
fresh,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => fresh,
Err(winner) => winner, };
}
let mut cur = CURSOR.load(Ordering::Relaxed);
loop {
let aligned = (cur + align - 1) & !(align - 1);
let next = aligned + size;
if next > BSS_ARENA_SIZE {
return bss_mmap_noreserve(size);
}
match CURSOR.compare_exchange_weak(cur, next, Ordering::AcqRel, Ordering::Relaxed) {
Ok(_) => return unsafe { base.add(aligned) },
Err(observed) => cur = observed,
}
}
}
#[cfg(unix)]
#[inline]
fn bss_mmap_noreserve(len: usize) -> *mut u8 {
#[cfg(any(target_os = "linux", target_os = "android"))]
const MAP_FLAGS: libc::c_int = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_NORESERVE;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
const MAP_FLAGS: libc::c_int = libc::MAP_PRIVATE | libc::MAP_ANONYMOUS;
let p = unsafe {
libc::mmap(
core::ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
MAP_FLAGS,
-1,
0,
)
};
if p == libc::MAP_FAILED {
crate::out_of_memory();
}
#[cfg(bun_asan)]
{
unsafe extern "C" {
safe fn __lsan_register_root_region(ptr: *const core::ffi::c_void, size: usize);
}
__lsan_register_root_region(p.cast(), len);
}
p.cast::<u8>()
}
#[doc(hidden)]
#[inline]
pub fn bss_lazy_slice<T>(count: usize) -> NonNull<[MaybeUninit<T>]> {
let p =
bss_lazy_bytes(count * size_of::<T>(), core::mem::align_of::<T>()).cast::<MaybeUninit<T>>();
NonNull::slice_from_raw_parts(p, count)
}
#[macro_export]
macro_rules! bss_list {
($(#[$m:meta])* $vis:vis $name:ident : $value_ty:ty, $count:expr) => {
$crate::bss_singleton!($(#[$m])* $vis fn $name() -> $crate::BSSList<$value_ty, { $count }>);
};
}
#[macro_export]
macro_rules! bss_string_list {
($(#[$m:meta])* $vis:vis $name:ident : $count:expr, $item_len:expr) => {
$crate::bss_singleton!($(#[$m])* $vis fn $name() -> $crate::BSSStringList<{ $count }, { $item_len }>);
};
}
#[macro_export]
macro_rules! bss_map_inner {
($(#[$m:meta])* $vis:vis $name:ident : $value_ty:ty, $count:expr, $rm_slash:expr) => {
$crate::bss_singleton!($(#[$m])* $vis fn $name() -> $crate::BSSMapInner<$value_ty, { $count }, { $rm_slash }>);
};
}
#[macro_export]
macro_rules! bss_map {
($(#[$m:meta])* $vis:vis $name:ident : $value_ty:ty, $count:expr, $est_key_len:expr, $rm_slash:expr) => {
$crate::bss_singleton!($(#[$m])* $vis fn $name() -> $crate::BSSMap<$value_ty, { $count }, { $est_key_len }, { $rm_slash }>);
};
}
mod __bss_macro_smoke {
crate::bss_list! { _l : u32, 4 }
crate::bss_string_list! { _sl : 4, 8 }
crate::bss_map_inner! { _mi : u32, 4, true }
crate::bss_map! { _m : u32, 4, 8, false }
}
#[path = "heap_breakdown.rs"]
pub mod heap_breakdown;
#[macro_export]
macro_rules! get_zone {
($name:literal) => {{
static ZONE: ::std::sync::OnceLock<&'static $crate::heap_breakdown::Zone> =
::std::sync::OnceLock::new();
*ZONE.get_or_init(|| {
unsafe {
$crate::heap_breakdown::Zone::init(
concat!($name, "\0").as_ptr().cast::<::core::ffi::c_char>(),
)
}
})
}};
}
type HashKeyType = u64;
#[derive(Default, Clone, Copy)]
pub struct IdentityU64Hasher(u64);
impl core::hash::Hasher for IdentityU64Hasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.0 = bun_wyhash::hash_with_seed(self.0, bytes);
}
#[inline]
fn write_u64(&mut self, n: u64) {
self.0 = n;
}
#[inline]
fn finish(&self) -> u64 {
self.0
}
}
type IndexMapHasher = core::hash::BuildHasherDefault<IdentityU64Hasher>;
pub type IndexMap = HashMap<HashKeyType, IndexType, IndexMapHasher>;
pub type IndexMapManaged = HashMap<HashKeyType, IndexType, IndexMapHasher>;
#[derive(Clone, Copy)]
pub struct Result {
pub hash: HashKeyType,
pub index: IndexType,
pub status: ItemStatus,
}
impl Result {
pub fn has_checked_if_exists(&self) -> bool {
self.index.index() != UNASSIGNED.index()
}
pub fn is_overflowing<const COUNT: usize>(&self) -> bool {
self.index.raw() as usize >= COUNT
}
}
pub trait OverflowBlock {
unsafe fn zero(this: *mut Self);
fn is_full(&self) -> bool;
fn used_mut(&mut self) -> &mut u32;
}
const OVERFLOW_GROUP_MAX: usize = 4095;
type OverflowUsedSize = u16;
pub struct OverflowGroup<Block> {
pub used: OverflowUsedSize,
pub allocated: OverflowUsedSize,
pub ptrs: [Option<Box<Block>>; OVERFLOW_GROUP_MAX],
}
impl<Block: OverflowBlock> OverflowGroup<Block> {
#[inline]
pub fn zero(&mut self) {
self.used = 0;
self.allocated = 0;
}
pub fn tail(&mut self) -> &mut Block {
if self.allocated > 0
&& self.ptrs[self.used as usize]
.as_ref()
.expect("alloc")
.is_full()
{
self.used = self.used.wrapping_add(1);
if self.allocated > self.used {
*self.ptrs[self.used as usize]
.as_mut()
.expect("alloc")
.used_mut() = 0;
}
}
if self.allocated <= self.used {
let mut b: Box<core::mem::MaybeUninit<Block>> = Box::new_uninit();
unsafe { Block::zero(b.as_mut_ptr()) };
self.ptrs[self.allocated as usize] = Some(unsafe { b.assume_init() });
self.allocated = self.allocated.wrapping_add(1);
}
self.ptrs[self.used as usize].as_mut().expect("alloc")
}
#[inline]
pub fn slice(&mut self) -> &mut [Option<Box<Block>>] {
&mut self.ptrs[0..self.used as usize]
}
}
pub struct OverflowListBlock<ValueType, const COUNT: usize> {
pub used: u32,
pub items: [MaybeUninit<ValueType>; COUNT],
}
impl<ValueType, const COUNT: usize> OverflowListBlock<ValueType, COUNT> {
#[inline]
pub fn is_full(&self) -> bool {
self.used as usize >= COUNT
}
pub fn append(&mut self, value: ValueType) -> &mut ValueType {
debug_assert!((self.used as usize) < COUNT);
let index = self.used as usize;
self.items[index].write(value);
self.used = self.used.wrapping_add(1);
unsafe { self.items[index].assume_init_mut() }
}
}
impl<ValueType, const COUNT: usize> OverflowBlock for OverflowListBlock<ValueType, COUNT> {
unsafe fn zero(this: *mut Self) {
unsafe { addr_of_mut!((*this).used).write(0) };
}
fn is_full(&self) -> bool {
(self.used as usize) >= COUNT
}
fn used_mut(&mut self) -> &mut u32 {
&mut self.used
}
}
pub struct OverflowList<ValueType, const COUNT: usize> {
pub list: OverflowGroup<OverflowListBlock<ValueType, COUNT>>,
pub count: u32, }
impl<ValueType, const COUNT: usize> OverflowList<ValueType, COUNT> {
#[inline]
pub fn zero(&mut self) {
self.list.zero();
self.count = 0;
}
#[inline]
pub unsafe fn init_counters_at(slot: *mut Self) {
unsafe {
addr_of_mut!((*slot).list.used).write(0);
addr_of_mut!((*slot).list.allocated).write(0);
addr_of_mut!((*slot).count).write(0);
}
}
#[inline]
pub fn len(&self) -> u32 {
self.count
}
#[inline]
pub fn append(&mut self, value: ValueType) -> &mut ValueType {
self.count += 1;
self.list.tail().append(value)
}
pub fn reset(&mut self) {
for block in self.list.slice() {
block.as_mut().expect("alloc").used = 0;
}
self.list.used = 0;
}
#[inline]
pub fn at_index(&self, index: IndexType) -> &ValueType {
let idx = index.index() as usize;
let block_id = if idx > 0 { idx / COUNT } else { 0 };
debug_assert!(index.is_overflow());
debug_assert!(self.list.used as usize >= block_id);
debug_assert!(
self.list.ptrs[block_id].as_ref().expect("alloc").used as usize > (idx % COUNT)
);
unsafe {
self.list
.ptrs
.get_unchecked(block_id)
.as_ref()
.unwrap_unchecked()
.items
.get_unchecked(idx % COUNT)
.assume_init_ref()
}
}
#[inline]
pub fn at_index_mut(&mut self, index: IndexType) -> &mut ValueType {
let idx = index.index() as usize;
let block_id = if idx > 0 { idx / COUNT } else { 0 };
debug_assert!(index.is_overflow());
debug_assert!(self.list.used as usize >= block_id);
debug_assert!(
self.list.ptrs[block_id].as_ref().expect("alloc").used as usize > (idx % COUNT)
);
unsafe {
self.list
.ptrs
.get_unchecked_mut(block_id)
.as_mut()
.unwrap_unchecked()
.items
.get_unchecked_mut(idx % COUNT)
.assume_init_mut()
}
}
}
#[repr(C)]
pub struct BSSList<ValueType, const COUNT: usize > {
pub mutex: Mutex,
pub head: Option<NonNull<BSSListOverflowBlock<ValueType>>>,
pub used: u32,
pub tail: BSSListOverflowBlock<ValueType>,
pub backing_buf: [MaybeUninit<ValueType>; COUNT],
}
unsafe impl<ValueType: Send, const COUNT: usize> Send for BSSList<ValueType, COUNT> {}
unsafe impl<ValueType: Send, const COUNT: usize> Sync for BSSList<ValueType, COUNT> {}
const BSS_LIST_CHUNK_SIZE: usize = 256;
pub const BSS_OVERFLOW_BLOCK_SIZE: usize = 64;
#[repr(C)]
pub struct BSSListOverflowBlock<ValueType> {
pub used: AtomicU16,
pub prev: Option<Box<BSSListOverflowBlock<ValueType>>>,
pub data: [MaybeUninit<ValueType>; BSS_LIST_CHUNK_SIZE],
}
impl<ValueType> BSSListOverflowBlock<ValueType> {
#[inline]
pub unsafe fn zero(this: *mut Self) {
unsafe {
addr_of_mut!((*this).used).write(AtomicU16::new(0));
addr_of_mut!((*this).prev).write(None);
}
}
pub fn append(&mut self, item: ValueType) -> core::result::Result<&mut ValueType, AllocError> {
let index = self.used.fetch_add(1, Ordering::AcqRel);
if index as usize >= BSS_LIST_CHUNK_SIZE {
return Err(AllocError);
}
self.data[index as usize].write(item);
Ok(unsafe { self.data[index as usize].assume_init_mut() })
}
#[inline(always)]
pub fn append_uninit(
&mut self,
) -> core::result::Result<*mut MaybeUninit<ValueType>, AllocError> {
let index = self.used.fetch_add(1, Ordering::AcqRel);
if index as usize >= BSS_LIST_CHUNK_SIZE {
return Err(AllocError);
}
Ok(unsafe { self.data.as_mut_ptr().add(index as usize) })
}
}
impl<ValueType, const COUNT: usize> BSSList<ValueType, COUNT> {
pub const CHUNK_SIZE: usize = BSS_LIST_CHUNK_SIZE;
const MAX_INDEX: usize = COUNT - 1;
#[inline]
pub fn block_index(index: u32 ) -> usize {
index as usize / BSS_LIST_CHUNK_SIZE
}
pub unsafe fn init_at(slot: *mut Self) {
unsafe {
addr_of_mut!((*slot).mutex).write(Mutex::new());
let tail_ptr = addr_of_mut!((*slot).tail);
addr_of_mut!((*slot).head).write(Some(NonNull::new_unchecked(tail_ptr)));
}
}
pub fn init() -> NonNull<Self> {
bss_heap_init(Self::init_at)
}
pub fn is_overflowing(instance: &Self) -> bool {
instance.used as usize >= COUNT
}
pub fn exists(&self, value: &[u8]) -> bool {
let base = self.backing_buf.as_ptr() as usize;
let end = base + core::mem::size_of_val(&self.backing_buf);
let p = value.as_ptr() as usize;
base <= p && p + value.len() <= end
}
#[cold]
fn append_overflow_uninit(
&mut self,
) -> core::result::Result<*mut MaybeUninit<ValueType>, AllocError> {
self.used += 1;
let mut head_ptr = self.head.unwrap();
let head_full = unsafe {
(*head_ptr.as_ptr()).used.load(Ordering::Acquire) as usize >= BSS_LIST_CHUNK_SIZE
};
if head_full {
let mut new_block: Box<core::mem::MaybeUninit<BSSListOverflowBlock<ValueType>>> =
Box::new_uninit();
unsafe { BSSListOverflowBlock::zero(new_block.as_mut_ptr()) };
let mut new_block = unsafe { new_block.assume_init() };
let tail_ptr: *const BSSListOverflowBlock<ValueType> = core::ptr::addr_of!(self.tail);
new_block.prev = if core::ptr::eq(head_ptr.as_ptr().cast_const(), tail_ptr) {
None
} else {
Some(unsafe { Box::from_raw(head_ptr.as_ptr()) })
};
let raw = Box::into_raw(new_block);
head_ptr = unsafe { NonNull::new_unchecked(raw) };
self.head = Some(head_ptr);
}
unsafe { (*head_ptr.as_ptr()).append_uninit() }
}
#[inline(always)]
pub unsafe fn append_uninit(
this: *mut Self,
) -> core::result::Result<*mut MaybeUninit<ValueType>, AllocError> {
let _guard = unsafe { (*this).mutex.lock() };
let this = unsafe { &mut *this };
if this.used as usize > Self::MAX_INDEX {
this.append_overflow_uninit()
} else {
let index = this.used as usize;
this.used += 1;
Ok(unsafe { this.backing_buf.as_mut_ptr().add(index) })
}
}
#[inline]
pub unsafe fn append(
this: *mut Self,
value: ValueType,
) -> core::result::Result<*mut ValueType, AllocError> {
let slot = unsafe { Self::append_uninit(this)? };
unsafe { Ok(core::ptr::from_mut((*slot).write(value))) }
}
}
impl<ValueType, const COUNT: usize> Drop for BSSList<ValueType, COUNT> {
fn drop(&mut self) {
if let Some(head) = self.head.take() {
let tail_ptr: *const BSSListOverflowBlock<ValueType> = core::ptr::addr_of!(self.tail);
if !core::ptr::eq(head.as_ptr().cast_const(), tail_ptr) {
drop(unsafe { Box::from_raw(head.as_ptr()) });
}
}
}
}
pub struct BSSListPair<ValueType> {
pub index: IndexType,
pub value: *const ValueType,
}
pub struct BSSStringList<
const COUNT: usize,
const ITEM_LENGTH: usize,
> {
pub backing_buf: NonNull<[MaybeUninit<u8>]>, pub backing_buf_used: u64,
pub overflow_list: OverflowList<&'static [u8], BSS_OVERFLOW_BLOCK_SIZE>,
pub slice_buf: NonNull<[MaybeUninit<&'static [u8]>]>, pub slice_buf_used: u16,
pub mutex: Mutex,
}
#[derive(Default, Clone, Copy)]
struct EmptyType {
len: usize,
}
pub trait BSSAppendable {
fn total_len(&self) -> usize;
fn copy_into(&self, dst: &mut [u8]);
}
impl BSSAppendable for EmptyType {
fn total_len(&self) -> usize {
self.len
}
fn copy_into(&self, _dst: &mut [u8]) {}
}
impl BSSAppendable for &[u8] {
fn total_len(&self) -> usize {
self.len()
}
fn copy_into(&self, dst: &mut [u8]) {
dst[..self.len()].copy_from_slice(self);
}
}
impl<const N: usize> BSSAppendable for [&[u8]; N] {
fn total_len(&self) -> usize {
self.iter().map(|s| s.len()).sum()
}
fn copy_into(&self, dst: &mut [u8]) {
let mut remainder = dst;
for val in self {
remainder[..val.len()].copy_from_slice(val);
remainder = &mut remainder[val.len()..];
}
}
}
impl BSSAppendable for &[&[u8]] {
fn total_len(&self) -> usize {
self.iter().map(|s| s.len()).sum()
}
fn copy_into(&self, dst: &mut [u8]) {
let mut remainder = dst;
for val in *self {
remainder[..val.len()].copy_from_slice(val);
remainder = &mut remainder[val.len()..];
}
}
}
impl<const COUNT: usize, const ITEM_LENGTH: usize> BSSStringList<COUNT, ITEM_LENGTH> {
const MAX_INDEX: usize = COUNT - 1;
pub unsafe fn init_at(slot: *mut Self) {
unsafe {
addr_of_mut!((*slot).mutex).write(Mutex::new());
addr_of_mut!((*slot).backing_buf).write(bss_lazy_slice::<u8>(COUNT * ITEM_LENGTH));
addr_of_mut!((*slot).backing_buf_used).write(0);
addr_of_mut!((*slot).slice_buf).write(bss_lazy_slice::<&'static [u8]>(COUNT));
addr_of_mut!((*slot).slice_buf_used).write(0);
OverflowList::init_counters_at(addr_of_mut!((*slot).overflow_list));
}
}
pub fn init() -> NonNull<Self> {
bss_heap_init(Self::init_at)
}
#[inline]
pub fn is_overflowing(instance: &Self) -> bool {
instance.slice_buf_used as usize >= COUNT
}
pub fn exists(&self, value: &[u8]) -> bool {
let base = self.backing_buf.as_ptr().cast::<u8>() as usize;
let end = base + self.backing_buf.len();
let p = value.as_ptr() as usize;
base <= p && p + value.len() <= end
}
pub unsafe fn editable_slice<'a>(ptr: *mut u8, len: usize) -> &'a mut [u8] {
unsafe { core::slice::from_raw_parts_mut(ptr, len) }
}
pub unsafe fn append_mutable<'a, A: BSSAppendable>(
this: *mut Self,
value: &A,
) -> core::result::Result<&'a mut [u8], AllocError> {
let _guard = unsafe { (*this).mutex.lock() };
let (ptr, len) = unsafe { (*this).do_append(value)? };
Ok(unsafe { core::slice::from_raw_parts_mut(ptr, len) })
}
pub unsafe fn get_mutable<'a>(
this: *mut Self,
len: usize,
) -> core::result::Result<&'a mut [u8], AllocError> {
unsafe { Self::append_mutable(this, &EmptyType { len }) }
}
pub unsafe fn print_with_type<'a>(
this: *mut Self,
args: core::fmt::Arguments<'_>,
) -> core::result::Result<&'a [u8], AllocError> {
const STACK: usize = 512;
let mut scratch = [MaybeUninit::<u8>::uninit(); STACK];
let mut c = crate::SliceCursor::new(unsafe {
core::slice::from_raw_parts_mut(scratch.as_mut_ptr().cast::<u8>(), STACK)
});
if core::fmt::write(&mut c, args).is_ok() {
let written: &[u8] = &c.buf[..c.at];
return unsafe { Self::append(this, &written) };
}
let len = crate::fmt_count(args);
let buf = unsafe { Self::append_mutable(this, &EmptyType { len: len + 1 })? };
let buf_len = buf.len();
buf[buf_len - 1] = 0;
let written = crate::buf_print_len(&mut buf[..buf_len - 1], args).expect("counted length");
Ok(&buf[..written])
}
pub unsafe fn print<'a>(
this: *mut Self,
args: core::fmt::Arguments<'_>,
) -> core::result::Result<&'a [u8], AllocError> {
unsafe { Self::print_with_type(this, args) }
}
#[inline]
pub unsafe fn append<'a, A: BSSAppendable>(
this: *mut Self,
value: &A,
) -> core::result::Result<&'a [u8], AllocError> {
let _guard = unsafe { (*this).mutex.lock() };
let (ptr, len) = unsafe { (*this).do_append(value)? };
Ok(unsafe { core::slice::from_raw_parts(ptr, len) })
}
pub unsafe fn append_lower_case<'a>(
this: *mut Self,
value: &[u8],
) -> core::result::Result<&'a [u8], AllocError> {
let _guard = unsafe { (*this).mutex.lock() };
let this_ref = unsafe { &mut *this };
let (ptr, len) = if value.len() <= 256 {
let mut scratch = [0u8; 256];
this_ref.do_append(&crate::copy_lowercase(value, &mut scratch[..value.len()]))?
} else {
let p = mimalloc::mi_malloc(value.len()).cast::<u8>();
if p.is_null() {
return Err(AllocError);
}
let tmp = unsafe { core::slice::from_raw_parts_mut(p, value.len()) };
let r = this_ref.do_append(&crate::copy_lowercase(value, tmp));
unsafe { mimalloc::mi_free(p.cast()) };
r?
};
Ok(unsafe { core::slice::from_raw_parts(ptr, len) })
}
#[inline]
fn do_append<A: BSSAppendable>(
&mut self,
value: &A,
) -> core::result::Result<(*mut u8, usize), AllocError> {
let value_len: usize = value.total_len() + 1;
let (out_ptr, out_len): (*mut u8, usize);
if value_len + (self.backing_buf_used as usize) < self.backing_buf.len() - 1 {
let start = self.backing_buf_used as usize;
self.backing_buf_used += value_len as u64;
let end = self.backing_buf_used as usize;
let dst: &mut [u8] = unsafe {
core::slice::from_raw_parts_mut(
self.backing_buf.as_ptr().cast::<u8>().add(start),
end - start,
)
};
value.copy_into(&mut dst[..value_len - 1]);
dst[value_len - 1] = 0;
(out_ptr, out_len) = (dst.as_mut_ptr(), value_len - 1);
} else {
let ptr = mimalloc::mi_malloc(value_len).cast::<u8>();
if ptr.is_null() {
return Err(AllocError);
}
let value_buf = unsafe { core::slice::from_raw_parts_mut(ptr, value_len) };
value.copy_into(&mut value_buf[..value_len - 1]);
value_buf[value_len - 1] = 0;
let out = &mut value_buf[..value_len - 1];
(out_ptr, out_len) = (out.as_mut_ptr(), out.len());
}
let mut result = IndexType::new(
u32::MAX >> 1,
self.slice_buf_used as usize > Self::MAX_INDEX,
);
if result.is_overflow() {
result.set_index(self.overflow_list.len());
} else {
result.set_index(self.slice_buf_used as u32);
self.slice_buf_used += 1;
}
let stored: &'static [u8] = unsafe { core::slice::from_raw_parts(out_ptr, out_len) };
if result.is_overflow() {
if self.overflow_list.len() == result.index() {
let _ = self.overflow_list.append(stored);
} else {
*self.overflow_list.at_index_mut(result) = stored;
}
} else {
unsafe {
self.slice_buf
.as_ptr()
.cast::<MaybeUninit<&'static [u8]>>()
.add(result.index() as usize)
.write(MaybeUninit::new(stored));
}
}
Ok((out_ptr, out_len))
}
}
pub struct BSSMapInner<ValueType, const COUNT: usize, const REMOVE_TRAILING_SLASHES: bool> {
pub index: IndexMap,
pub overflow_list: OverflowList<ValueType, BSS_OVERFLOW_BLOCK_SIZE>,
pub mutex: Mutex,
pub backing_buf: [MaybeUninit<ValueType>; COUNT],
pub backing_buf_used: u16,
}
impl<ValueType, const COUNT: usize, const REMOVE_TRAILING_SLASHES: bool>
BSSMapInner<ValueType, COUNT, REMOVE_TRAILING_SLASHES>
{
const MAX_INDEX: usize = COUNT - 1;
pub unsafe fn init_at(slot: *mut Self) {
unsafe {
addr_of_mut!((*slot).mutex).write(Mutex::new());
addr_of_mut!((*slot).index).write(IndexMap::default());
addr_of_mut!((*slot).backing_buf_used).write(0);
OverflowList::init_counters_at(addr_of_mut!((*slot).overflow_list));
}
}
pub fn init() -> NonNull<Self> {
bss_heap_init(Self::init_at)
}
pub fn is_overflowing(instance: &Self) -> bool {
instance.backing_buf_used as usize >= COUNT
}
#[inline(always)]
fn key_hash(denormalized_key: &[u8]) -> u64 {
let key = if REMOVE_TRAILING_SLASHES {
trim_right(denormalized_key, SEP_STR.as_bytes())
} else {
denormalized_key
};
bun_wyhash::hash(key)
}
pub fn get_or_put(
&mut self,
denormalized_key: &[u8],
) -> core::result::Result<Result, AllocError> {
let _key = Self::key_hash(denormalized_key);
let _guard = self.mutex.lock();
match self.index.entry(_key) {
std::collections::hash_map::Entry::Occupied(e) => {
let v = *e.get();
Ok(Result {
hash: _key,
index: v,
status: match v.index() {
i if i == NOT_FOUND.index() => ItemStatus::NotFound,
i if i == UNASSIGNED.index() => ItemStatus::Unknown,
_ => ItemStatus::Exists,
},
})
}
std::collections::hash_map::Entry::Vacant(e) => {
e.insert(UNASSIGNED);
Ok(Result {
hash: _key,
index: UNASSIGNED,
status: ItemStatus::Unknown,
})
}
}
}
pub fn get(&mut self, denormalized_key: &[u8]) -> Option<&mut ValueType> {
let _key = Self::key_hash(denormalized_key);
let _guard = self.mutex.lock();
let index = self.index.get(&_key).copied()?;
self.at_index(index)
}
pub fn mark_not_found(&mut self, result: Result) {
let _guard = self.mutex.lock();
self.index.insert(result.hash, NOT_FOUND);
}
pub fn at_index(&mut self, index: IndexType) -> Option<&mut ValueType> {
if index.index() == NOT_FOUND.index() || index.index() == UNASSIGNED.index() {
return None;
}
if index.is_overflow() {
Some(self.overflow_list.at_index_mut(index))
} else {
Some(unsafe { self.backing_buf[index.index() as usize].assume_init_mut() })
}
}
pub fn put(
&mut self,
result: &mut Result,
value: ValueType,
) -> core::result::Result<&mut ValueType, AllocError> {
let _guard = self.mutex.lock();
if result.index.index() == NOT_FOUND.index() || result.index.index() == UNASSIGNED.index() {
result
.index
.set_is_overflow(self.backing_buf_used as usize > Self::MAX_INDEX);
if result.index.is_overflow() {
result.index.set_index(self.overflow_list.len());
} else {
result.index.set_index(self.backing_buf_used as u32);
self.backing_buf_used += 1;
}
}
self.index.insert(result.hash, result.index);
let ret = if result.index.is_overflow() {
if self.overflow_list.len() == result.index.index() {
self.overflow_list.append(value)
} else {
let ptr = self.overflow_list.at_index_mut(result.index);
*ptr = value;
ptr
}
} else {
let idx = result.index.index() as usize;
self.backing_buf[idx].write(value);
unsafe { self.backing_buf[idx].assume_init_mut() }
};
Ok(ret)
}
pub fn remove(&mut self, denormalized_key: &[u8]) -> bool {
let _guard = self.mutex.lock();
let _key = Self::key_hash(denormalized_key);
self.index.remove(&_key).is_some()
}
pub fn values(&mut self) -> &mut [ValueType] {
unsafe {
core::slice::from_raw_parts_mut(
self.backing_buf.as_mut_ptr().cast::<ValueType>(),
self.backing_buf_used as usize,
)
}
}
}
pub struct BSSMap<
ValueType,
const COUNT: usize,
const ESTIMATED_KEY_LENGTH: usize,
const REMOVE_TRAILING_SLASHES: bool,
> {
map: NonNull<BSSMapInner<ValueType, COUNT, REMOVE_TRAILING_SLASHES>>,
pub key_list_buffer: NonNull<[MaybeUninit<u8>]>, pub key_list_buffer_used: usize,
pub key_list_slices: NonNull<[MaybeUninit<&'static [u8]>]>, pub key_list_overflow: Vec<&'static [u8]>,
}
impl<
ValueType,
const COUNT: usize,
const ESTIMATED_KEY_LENGTH: usize,
const REMOVE_TRAILING_SLASHES: bool,
> BSSMap<ValueType, COUNT, ESTIMATED_KEY_LENGTH, REMOVE_TRAILING_SLASHES>
{
pub unsafe fn init_at(slot: *mut Self) {
unsafe {
addr_of_mut!((*slot).map).write(bss_heap_init(BSSMapInner::init_at));
addr_of_mut!((*slot).key_list_buffer)
.write(bss_lazy_slice::<u8>(COUNT * ESTIMATED_KEY_LENGTH));
addr_of_mut!((*slot).key_list_buffer_used).write(0);
addr_of_mut!((*slot).key_list_slices).write(bss_lazy_slice::<&'static [u8]>(COUNT));
addr_of_mut!((*slot).key_list_overflow).write(Vec::new());
}
}
pub fn init() -> NonNull<Self> {
bss_heap_init(Self::init_at)
}
#[inline(always)]
pub fn map(&self) -> &BSSMapInner<ValueType, COUNT, REMOVE_TRAILING_SLASHES> {
unsafe { self.map.as_ref() }
}
#[inline(always)]
pub fn map_mut(&mut self) -> &mut BSSMapInner<ValueType, COUNT, REMOVE_TRAILING_SLASHES> {
unsafe { self.map.as_mut() }
}
pub fn is_overflowing(instance: &Self) -> bool {
instance.map().backing_buf_used as usize >= COUNT
}
pub fn get_or_put(&mut self, key: &[u8]) -> core::result::Result<Result, AllocError> {
self.map_mut().get_or_put(key)
}
pub fn get(&mut self, key: &[u8]) -> Option<&mut ValueType> {
self.map_mut().get(key)
}
pub fn at_index(&mut self, index: IndexType) -> Option<&mut ValueType> {
self.map_mut().at_index(index)
}
pub fn key_at_index(&self, index: IndexType) -> Option<&[u8]> {
match index.index() {
i if i == UNASSIGNED.index() || i == NOT_FOUND.index() => None,
_ => {
if !index.is_overflow() {
let i = index.index() as usize;
debug_assert!(i < COUNT);
Some(unsafe { *self.key_list_slices.cast::<&'static [u8]>().as_ptr().add(i) })
} else {
Some(self.key_list_overflow[index.index() as usize])
}
}
}
}
pub fn put<const STORE_KEY: bool>(
&mut self,
key: &[u8],
result: &mut Result,
value: ValueType,
) -> core::result::Result<&mut ValueType, AllocError> {
let ptr: *mut ValueType = self.map_mut().put(result, value)?;
if STORE_KEY {
self.put_key(key, result)?;
}
Ok(unsafe { &mut *ptr })
}
pub fn is_key_statically_allocated(&self, key: &[u8]) -> bool {
let base = self.key_list_buffer.as_ptr().cast::<u8>() as usize;
let end = base + self.key_list_buffer.len();
let p = key.as_ptr() as usize;
base <= p && p + key.len() <= end
}
pub fn put_key(
&mut self,
key: &[u8],
result: &mut Result,
) -> core::result::Result<(), AllocError> {
let _guard = self.map().mutex.lock();
let slice: &'static [u8] = if self.is_key_statically_allocated(key) {
unsafe { core::slice::from_raw_parts(key.as_ptr(), key.len()) }
} else if self.key_list_buffer_used + key.len() < self.key_list_buffer.len() {
let start = self.key_list_buffer_used;
self.key_list_buffer_used += key.len();
let dst: &mut [u8] = unsafe {
core::slice::from_raw_parts_mut(
self.key_list_buffer.as_ptr().cast::<u8>().add(start),
key.len(),
)
};
dst.copy_from_slice(key);
unsafe { core::slice::from_raw_parts(dst.as_ptr(), dst.len()) }
} else {
let ptr = mimalloc::mi_malloc(key.len().max(1)).cast::<u8>();
if ptr.is_null() {
return Err(AllocError);
}
unsafe { core::ptr::copy_nonoverlapping(key.as_ptr(), ptr, key.len()) };
unsafe { core::slice::from_raw_parts(ptr, key.len()) }
};
let slice = if REMOVE_TRAILING_SLASHES {
trim_right(slice, b"/")
} else {
slice
};
if !result.index.is_overflow() {
let i = result.index.index() as usize;
debug_assert!(i < COUNT);
unsafe {
self.key_list_slices
.as_ptr()
.cast::<MaybeUninit<&'static [u8]>>()
.add(i)
.write(MaybeUninit::new(slice));
}
} else {
let idx = result.index.index() as usize;
if self.key_list_overflow.len() > idx {
let existing_slice = self.key_list_overflow[idx];
if !self.is_key_statically_allocated(existing_slice) {
unsafe {
mimalloc::mi_free(
existing_slice
.as_ptr()
.cast_mut()
.cast::<core::ffi::c_void>(),
)
};
}
self.key_list_overflow[idx] = slice;
} else {
self.key_list_overflow.push(slice);
}
}
Ok(())
}
pub fn mark_not_found(&mut self, result: Result) {
self.map_mut().mark_not_found(result);
}
pub fn remove(&mut self, key: &[u8]) -> bool {
self.map_mut().remove(key)
}
}
pub trait Allocator: 'static {
#[inline]
fn type_id(&self) -> core::any::TypeId {
core::any::TypeId::of::<Self>()
}
}
impl dyn Allocator {
#[inline]
pub fn is<T: Allocator>(&self) -> bool {
Allocator::type_id(self) == core::any::TypeId::of::<T>()
}
}
#[inline]
pub fn is_default(alloc: &dyn Allocator) -> bool {
alloc.is::<DefaultAlloc>()
}
#[derive(Clone, Copy, Default)]
pub struct DefaultAlloc;
impl Allocator for DefaultAlloc {}
static DEFAULT_ALLOC: DefaultAlloc = DefaultAlloc;
#[inline]
pub fn default_allocator() -> &'static dyn Allocator {
&DEFAULT_ALLOC
}
#[path = "basic.rs"]
pub mod basic;
pub mod memory;