Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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
27 changes: 2 additions & 25 deletions src/collections/array_hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1867,35 +1867,12 @@ impl<V: Default, A: Allocator + HashbrownAllocator + Clone + Default> StringHash
}

// ──────────────────────────────────────────────────────────────────────────
// StringHashMapContext + PrehashedCaseInsensitive
// string_hash_map
// ──────────────────────────────────────────────────────────────────────────

/// `bun.StringHashMapContext` — spelled as a module rather than a unit
/// struct so callers can path-access the nested `PrehashedCaseInsensitive`
/// type (`StringHashMapContext::PrehashedCaseInsensitive`) on stable Rust,
/// which forbids inherent associated types.
#[allow(non_snake_case)]
pub mod StringHashMapContext {
pub use super::string_hash_map::PrehashedCaseInsensitive;
}

/// String-hash helpers, namespaced so call sites can write
/// `bun_collections::string_hash_map::{PrehashedCaseInsensitive, GetOrPutResult}`.
/// `bun_collections::string_hash_map::GetOrPutResult`.
pub mod string_hash_map {
/// `bun.StringHashMapContext.PrehashedCaseInsensitive` — owns a lowercased
/// copy of the input. Dropped via `Box`.
pub struct PrehashedCaseInsensitive {
pub input: Box<[u8]>,
}

impl PrehashedCaseInsensitive {
pub fn init(input: &[u8]) -> Self {
let mut out = vec![0u8; input.len()].into_boxed_slice();
bun_core::strings::copy_lowercase(input, &mut out);
Self { input: out }
}
}

/// Result type alias for `StringHashMap::get_or_put*` so callers can name
/// it as `string_hash_map::GetOrPutResult<'_, V>`.
pub type GetOrPutResult<'a, V> = super::StringHashMapGetOrPut<'a, V>;
Expand Down
4 changes: 2 additions & 2 deletions src/collections/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ pub mod array_hash_map;
pub use array_hash_map::{
ArrayHashMap, ArrayHashMapExt, AutoContext, CaseInsensitiveAsciiStringArrayHashMap,
CaseInsensitiveAsciiStringContext, Entry, GetOrPutResult, MapEntry, OccupiedEntry,
StringArrayHashMap, StringHashMap, StringHashMapContext, StringHashMapKey,
StringHashMapUnownedKey, StringSet, VacantEntry, string_hash_map,
StringArrayHashMap, StringHashMap, StringHashMapKey, StringHashMapUnownedKey, StringSet,
VacantEntry, string_hash_map,
};
/// Downstream crates name hashbrown's iterator/entry types in struct fields
/// (e.g. `bun_resolver::DirEntryDirIter`). `StringHashMap` `Deref`s to a
Expand Down
30 changes: 8 additions & 22 deletions src/resolver/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,29 +427,15 @@ impl DirEntry {
| DK::EventPort => return Ok(()),
};

// Lowercase the entry basename once. The same bytes drive the
// previous-generation case-insensitive probe, the new entry's
// lowercased key, *and* the insert into `self.data` — and the hash is
// computed once here (`name_hash`) rather than re-derived by the probe
// and again by the insert. A stack scratch covers the common short
// case (matches `DirEntry::get`); only a basename longer than
// `MAX_PATH_BYTES` — which `getdents`/`FindNextFile` can't produce —
// would touch the heap.
// Nothing can open an entry whose path does not fit a path buffer.
if strings::without_trailing_slash(self.dir).len() + 1 + name_slice.len() >= MAX_PATH_BYTES
{
return Ok(());
}
Comment thread
claude[bot] marked this conversation as resolved.
Resolved

// Lowercased and hashed once for the probe, the key and the insert below.
let mut name_lc_buf = bun_paths::path_buffer_pool::get();
let name_lc_heap: Option<bun_collections::StringHashMapContext::PrehashedCaseInsensitive> =
if name_slice.len() <= MAX_PATH_BYTES {
None
} else {
Some(
bun_collections::StringHashMapContext::PrehashedCaseInsensitive::init(
name_slice,
),
)
};
let name_lc: &[u8] = match &name_lc_heap {
Some(p) => &p.input[..],
None => strings::copy_lowercase_if_needed(name_slice, &mut name_lc_buf[..]),
};
let name_lc: &[u8] = strings::copy_lowercase_if_needed(name_slice, &mut name_lc_buf[..]);
let name_hash = self.data.hash_key(name_lc);

let stored: *mut Entry = 'brk: {
Expand Down
21 changes: 7 additions & 14 deletions src/resolver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,12 @@ pub mod fs {
join_abs_string_buf::<platform::Loose>(self.top_level_dir, buf, parts)
}

/// Returns `None` on overflow.
/// `None` when the joined path does not fit `buf` with room for a NUL.
pub fn abs_buf_checked<'b>(&self, parts: &[&[u8]], buf: &'b mut [u8]) -> Option<&'b [u8]> {
use bun_paths::resolve_path::{join_abs_string_buf_checked, platform};
let capacity = buf.len();
join_abs_string_buf_checked::<platform::Loose>(self.top_level_dir, buf, parts)
.filter(|joined| joined.len() < capacity)
}

/// Normalizes `str` (separators, `.`/`..` segments) into `buf`.
Expand All @@ -374,19 +376,10 @@ pub mod fs {
join_abs_string::<platform::Loose>(self.top_level_dir, parts)
}

/// Like `abs`, but interns the joined path into `DirnameStore` and
/// returns the `'static` copy.
pub(crate) fn abs_alloc(
&self,
parts: &[&[u8]],
) -> core::result::Result<&'static [u8], bun_alloc::AllocError> {
use bun_paths::resolve_path::{join_abs_string, platform};
let joined = join_abs_string::<platform::Loose>(self.top_level_dir, parts);
// Route through DirnameStore so
// the resolver's `&'static [u8]` storage contract holds.
DirnameStore::instance()
.append_slice(joined)
.map_err(|_| bun_alloc::AllocError)
/// Like `abs`, but spills into `spill` when the result does not fit the threadlocal buffer.
pub(crate) fn abs_spill<'b>(&self, spill: &'b mut Vec<u8>, parts: &[&[u8]]) -> &'b [u8] {
use bun_paths::resolve_path::{join_abs_string_spill, platform};
join_abs_string_spill::<platform::Loose>(self.top_level_dir, spill, parts)
}

/// Relative path from `from` to `to`. Returns a slice into the
Expand Down
Loading
Loading