Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 9 additions & 24 deletions src/bun_core/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -989,23 +989,13 @@ pub use bun_alloc::SEP;
pub struct PathBuffer(pub [u8; MAX_PATH_BYTES]);
impl PathBuffer {
pub const ZEROED: Self = Self([0; MAX_PATH_BYTES]);
/// Zig `= undefined`. The bytes are immediately overwritten by the syscall
/// that fills it, so the initial contents are never observed.
/// Zero-initialized scratch buffer.
///
/// On Windows `MAX_PATH_BYTES` is 98 302 (vs 4 096 Linux / 1 024 macOS), so
/// the previous `Self::ZEROED` body here was a ~100 KB `memset` at every
/// one of the ~400 call sites — turning hot loops (glob scan, module load,
/// stack-trace formatting) into multi-GB zero-fill workloads and timing out
/// the leak/stress tests. Match the Zig spec and leave the bytes uninit.
/// The name is kept for Zig-port call sites, but this safe constructor must
/// return a fully initialized `[u8; MAX_PATH_BYTES]`.
#[inline]
#[allow(invalid_value, clippy::uninit_assumed_init)]
pub fn uninit() -> Self {
// SAFETY: `PathBuffer` is `repr(transparent)` over `[u8; N]`; every bit
// pattern is a valid `u8`, and callers treat this as a write-only
// scratch buffer (length-tracked) exactly like Zig
// `var buf: bun.PathBuffer = undefined`. No byte is read before being
// written by the consuming syscall / encoder.
unsafe { core::mem::MaybeUninit::uninit().assume_init() }
Self::ZEROED
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
Expand Down Expand Up @@ -1041,18 +1031,13 @@ impl core::ops::DerefMut for PathBuffer {
pub struct WPathBuffer(pub [u16; PATH_MAX_WIDE]);
impl WPathBuffer {
pub const ZEROED: Self = Self([0; PATH_MAX_WIDE]);
/// Zig `= undefined`. See [`PathBuffer::uninit`] — `PATH_MAX_WIDE` is
/// 32 767 `u16`s (~64 KB), and these are allocated per Windows syscall
/// for UTF-8→UTF-16 path conversion, so zero-initialising dominated the
/// hot path on Windows.
/// Zero-initialized wide-path scratch buffer.
///
/// The name is kept for Zig-port call sites, but this safe constructor must
/// return a fully initialized `[u16; PATH_MAX_WIDE]`.
#[inline]
#[allow(invalid_value, clippy::uninit_assumed_init)]
pub fn uninit() -> Self {
// SAFETY: `repr(transparent)` over `[u16; N]`; every bit pattern is a
// valid `u16`. Callers treat this as a write-only scratch buffer and
// track the written length out-of-band — mirrors Zig
// `var wbuf: bun.WPathBuffer = undefined`.
unsafe { core::mem::MaybeUninit::uninit().assume_init() }
Self::ZEROED
}
/// Inherent `as_slice` so `wbuf.as_slice()` resolves here instead of the
/// unstable `<[u16]>::as_slice` (`str_as_str` feature) via `Deref`.
Expand Down
16 changes: 4 additions & 12 deletions src/install/lockfile/Tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,10 @@ pub const MAX_DEPTH: usize = (MAX_PATH_BYTES / b"node_modules".len()) + 1;

pub type DepthBuf = [Id; MAX_DEPTH];

/// Zig `var depth_buf: Tree.DepthBuf = undefined;` — write-only scratch buffer
/// for [`relative_path_and_depth`]. Every slot is written before it is read
/// (index 0 unconditionally, indices `1..depth_buf_len` in the parent-walk
/// loop), so leaving the ~1.4 KB array uninitialised matches the spec and
/// avoids a `memset` per tree in the `--frozen-lockfile` no-change path.
/// Same shape/contract as [`bun_core::PathBuffer::uninit`].
/// Zero-initialized scratch buffer for [`relative_path_and_depth`].
#[inline]
#[allow(invalid_value, clippy::uninit_assumed_init)]
pub fn depth_buf_uninit() -> DepthBuf {
// SAFETY: `DepthBuf` is `[u32; N]`; every bit pattern is a valid `u32`.
// Callers treat this as a write-only scratch buffer — no element is read
// before being assigned by `relative_path_and_depth`.
unsafe { core::mem::MaybeUninit::uninit().assume_init() }
[0; MAX_DEPTH]
}

impl Tree {
Expand Down Expand Up @@ -236,7 +227,8 @@ impl<'a, const PATH_STYLE: IteratorPathStyle> Iterator<'a, PATH_STYLE> {
dependencies,
string_bytes,
path_buf: PathBuffer::uninit(),
// Zig: `depth_stack: DepthBuf = undefined` (Tree.zig:94)
// Zig used undefined storage here; Rust zero-initializes this buffer
// so the safe constructor returns a valid `DepthBuf`.
depth_stack: depth_buf_uninit(),
};
if PATH_STYLE == IteratorPathStyle::NodeModules {
Expand Down