use core::ffi::{c_char, c_void};
#[cfg(target_os = "macos")]
use core::ffi::{c_int, c_uint};
#[allow(non_camel_case_types, dead_code)]
type vm_size_t = usize;
pub const ENABLED: bool = cfg!(debug_assertions) && cfg!(target_os = "macos") && !cfg!(bun_asan);
#[allow(clippy::assertions_on_constants)]
pub fn get_zone(name: &[u8]) -> &'static Zone {
debug_assert!(
ENABLED,
"heap_breakdown::get_zone called with ENABLED=false"
);
use core::cell::UnsafeCell;
struct ZoneTable(UnsafeCell<Vec<(Vec<u8>, Vec<u8>, &'static Zone)>>);
unsafe impl Sync for ZoneTable {}
static LOCK: crate::Mutex = crate::Mutex::new();
static ZONES: ZoneTable = ZoneTable(UnsafeCell::new(Vec::new()));
let _guard = LOCK.lock();
let zones = unsafe { &mut *ZONES.0.get() };
if let Some((_, _, z)) = zones.iter().find(|(k, _, _)| k.as_slice() == name) {
return *z;
}
let mut owned = Vec::with_capacity(name.len() + 1);
owned.extend_from_slice(name);
owned.push(0);
let raw = owned.as_ptr().cast::<c_char>();
let zone = unsafe { Zone::init(raw) };
zones.push((name.to_vec(), owned, zone));
zone
}
bun_opaque::opaque_ffi! {
pub struct Zone;
}
unsafe impl Sync for Zone {}
unsafe impl Send for Zone {}
impl Zone {
#[cfg(target_os = "macos")]
pub unsafe fn init(name: *const c_char) -> &'static Zone {
unsafe {
let zone = malloc_create_zone(0, 0);
malloc_set_zone_name(zone, name);
&*zone
}
}
#[cfg(not(target_os = "macos"))]
#[allow(clippy::missing_safety_doc)]
pub unsafe fn init(_name: *const c_char) -> &'static Zone {
unreachable!("heap_breakdown is macOS-only; guard call sites on ENABLED")
}
#[inline]
pub fn malloc_zone_malloc(&self, size: usize) -> Option<*mut c_void> {
let p = malloc_zone_malloc(self, size);
if p.is_null() { None } else { Some(p) }
}
#[inline]
pub fn malloc_zone_calloc(&self, num_items: usize, size: usize) -> Option<*mut c_void> {
let p = malloc_zone_calloc(self, num_items, size);
if p.is_null() { None } else { Some(p) }
}
#[inline]
pub unsafe fn malloc_zone_free(&self, ptr: *mut c_void) {
unsafe { malloc_zone_free(self.as_mut_ptr(), ptr) }
}
fn aligned_alloc(zone: &Zone, len: usize, alignment: usize) -> Option<*mut u8> {
let eff_alignment = alignment.max(core::mem::size_of::<usize>());
let ptr = malloc_zone_memalign(zone, eff_alignment, len);
if ptr.is_null() {
None
} else {
Some(ptr.cast::<u8>())
}
}
fn raw_alloc(
zone: *mut c_void,
len: usize,
alignment: usize,
_ret_addr: usize,
) -> Option<*mut u8> {
Zone::aligned_alloc(unsafe { &*(zone.cast::<Zone>()) }, len, alignment)
}
pub fn allocator(&'static self) -> &'static dyn crate::Allocator {
self
}
#[inline]
pub fn create<T>(&self, data: T) -> *mut T {
self.try_create(data).expect("OutOfMemory")
}
#[inline]
pub fn try_create<T>(&self, data: T) -> Result<*mut T, crate::AllocError> {
let alignment = core::mem::align_of::<T>();
let raw = Zone::raw_alloc(
self.as_mut_ptr().cast::<c_void>(),
core::mem::size_of::<T>(),
alignment,
0,
)
.ok_or(crate::AllocError)?;
let ptr = raw.cast::<T>();
unsafe { ptr.write(data) };
Ok(ptr)
}
#[inline]
pub fn destroy<T>(&self, ptr: *mut T) {
unsafe { malloc_zone_free(self.as_mut_ptr(), ptr.cast()) };
}
pub fn is_instance(allocator_: &dyn crate::Allocator) -> bool {
allocator_.is::<Self>()
}
}
impl crate::Allocator for Zone {}
#[cfg(target_os = "macos")]
unsafe extern "C" {
pub safe fn malloc_default_zone() -> *mut Zone;
pub(crate) safe fn malloc_create_zone(start_size: vm_size_t, flags: c_uint) -> *mut Zone;
pub fn malloc_destroy_zone(zone: *mut Zone);
pub(crate) safe fn malloc_zone_malloc(zone: &Zone, size: usize) -> *mut c_void;
pub(crate) safe fn malloc_zone_calloc(
zone: &Zone,
num_items: usize,
size: usize,
) -> *mut c_void;
pub safe fn malloc_zone_valloc(zone: &Zone, size: usize) -> *mut c_void;
pub fn malloc_zone_free(zone: *mut Zone, ptr: *mut c_void);
pub fn malloc_zone_realloc(zone: *mut Zone, ptr: *mut c_void, size: usize) -> *mut c_void;
pub fn malloc_zone_from_ptr(ptr: *const c_void) -> *mut Zone;
pub(crate) safe fn malloc_zone_memalign(
zone: &Zone,
alignment: usize,
size: usize,
) -> *mut c_void;
pub fn malloc_zone_batch_malloc(
zone: *mut Zone,
size: usize,
results: *mut *mut c_void,
num_requested: c_uint,
) -> c_uint;
pub fn malloc_zone_batch_free(zone: *mut Zone, to_be_freed: *mut *mut c_void, num: c_uint);
pub safe fn malloc_default_purgeable_zone() -> *mut Zone;
pub fn malloc_make_purgeable(ptr: *mut c_void);
pub fn malloc_make_nonpurgeable(ptr: *mut c_void) -> c_int;
pub fn malloc_zone_register(zone: *mut Zone);
pub fn malloc_zone_unregister(zone: *mut Zone);
pub(crate) fn malloc_set_zone_name(zone: *mut Zone, name: *const c_char);
pub fn malloc_get_zone_name(zone: *mut Zone) -> *const c_char;
pub fn malloc_zone_pressure_relief(zone: *mut Zone, goal: usize) -> usize;
}
#[cfg(not(target_os = "macos"))]
#[allow(clippy::missing_safety_doc)]
mod stubs {
use super::*;
pub fn malloc_zone_malloc(_: &Zone, _: usize) -> *mut c_void {
unreachable!()
}
pub fn malloc_zone_calloc(_: &Zone, _: usize, _: usize) -> *mut c_void {
unreachable!()
}
pub unsafe fn malloc_zone_free(_: *mut Zone, _: *mut c_void) {
unreachable!()
}
pub(crate) fn malloc_zone_memalign(_: &Zone, _: usize, _: usize) -> *mut c_void {
unreachable!()
}
}
#[cfg(not(target_os = "macos"))]
use stubs::malloc_zone_memalign;
#[cfg(not(target_os = "macos"))]
pub use stubs::{malloc_zone_calloc, malloc_zone_free, malloc_zone_malloc};