Skip to content

strings: call highway_memmem directly on all platforms - #34820

Merged
Jarred-Sumner merged 2 commits into
mainfrom
farm/21de1cb0/memmem-unify-memchr
Jul 21, 2026
Merged

Jarred-Sumner merged 2 commits into
mainfrom
farm/21de1cb0/memmem-unify-memchr

Conversation

@robobun

@robobun robobun commented Jul 20, 2026 •

Copy link
Copy Markdown
Collaborator

What

bun_core::strings::memmem previously split on #[cfg]: POSIX called libc::memmem (which resolved to highway_memmem via the link-time symbol override in highway_strings.cpp:2236-2252), Windows called bstr::ByteSlice::find (memchr). The doc comment claimed "libc on posix, scalar fallback on windows", both halves of which were wrong.

This adds a bun_highway::memmem wrapper around the existing highway_memmem C entrypoint and calls it from strings::memmem on all platforms, matching how strings::index_of_char_usize already calls bun_highway::index_of_char.

Why

  • Makes the highway dependency explicit instead of relying on libc symbol interposition
  • Unifies Windows with POSIX (both now use highway_memmem)
  • Drops the #[cfg] split and the misleading doc comment

Semantics

Behavior is identical to the previous POSIX path (same MemMemImpl backend). Empty needle → Some(0), needle longer than haystack → None; both guarded in the Rust wrapper before the FFI call. index_of (the primary caller) additionally guards these itself and is unchanged.

cargo check -p bun_core and cargo clippy pass on linux-x64 and x86_64-pc-windows-msvc. Full bun bd links and smoke tests pass locally.

This is a behavior-preserving refactor; no fail-before test is constructible.

The POSIX path called libc::memmem directly while Windows used
bstr::ByteSlice::find (which wraps memchr::memmem::Finder, a SIMD
implementation with guaranteed-linear worst case). Unify both into the
bstr path so all platforms get the same algorithm instead of
platform-variable libc memmem, matching what last_index_of already does.

Also fixes the stale 'scalar fallback on windows' doc comment; the
Windows path has been SIMD via memchr for some time.
@coderabbitai

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

Memmem search path

Layer / File(s) Summary
Highway FFI and Rust wrapper
src/highway/lib.rs
Adds the highway_memmem FFI declaration and a Rust wrapper that handles edge cases, invokes the native search, and returns the match offset.
Immutable string integration
src/bun_core/string/immutable.rs
Routes memmem through highway::memmem, removing platform-specific libc and bstr implementations.

Suggested reviewers: jarred-sumner

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and accurately summarizes the main change: using highway_memmem directly on all platforms.
Description check ✅ Passed The description covers what changed, why, semantics, and verification, even though it uses different headings than the template.

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Jul 20, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 7:08 PM PT - Jul 20th, 2026

❌ @robobun, your commit 09ffddc has 1 failures in Build #76505 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 34820

That installs a local version of the PR into your bun-34820 executable, so you can run:

bun-34820 --bun

@robobun

robobun commented Jul 20, 2026 •

Copy link
Copy Markdown
Collaborator Author

This is a behavior-preserving refactor: strings::memmem now calls highway_memmem directly via a bun_highway FFI wrapper on all platforms, instead of via libc::memmem symbol interposition on POSIX and bstr on Windows. Same MemMemImpl backend on POSIX as before; Windows now matches. No input produces a different result before vs after, so a fail-before test is not constructible.

CI on build 76505 (09ffddc): 261 passed, 25 canceled. Windows build-bun passed on x64, x64-baseline, and aarch64, confirming the highway_memmem extern links correctly on Windows. All Windows test lanes were canceled due to Azure VM provisioning failures (pre-existing infra issue, also on main). Remaining failures are flaky and unrelated to strings::memmem: test-fs-promises-file-handle-readFile.js (FileHandle GC), require-cache.test.ts (RSS leak threshold on alpine), napi.test.ts (output ordering on alpine aarch64), 20144.test.ts (SIGINT/SIGKILL on darwin), bun-server.test.ts (idle CPU% on darwin), node-tls-server.test.ts (TLS version on darwin).

Ready for maintainer merge.

Comment thread src/bun_core/string/immutable.rs Outdated
Per review: the previous libc::memmem call on POSIX resolved to
highway_memmem via a link-time symbol override in highway_strings.cpp,
not platform libc. Make that explicit by adding a bun_highway::memmem
wrapper and calling it from strings::memmem on all platforms, matching
how index_of_char already works.

Windows now uses highway_memmem too instead of bstr/memchr, so all
platforms run the same substring search.
@robobun robobun changed the title strings: use memchr::memmem on all platforms strings: call highway_memmem directly on all platforms Jul 21, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the follow-up commit addresses my earlier note: instead of swapping to memchr, this now calls highway_memmem directly, so POSIX hits the exact same MemMemImpl backend as before (just explicitly instead of via the memmem symbol alias) and Windows now matches POSIX and JSBuffer.cpp.

What was reviewed:

  • FFI signature matches highway_strings.cpp:2023 (void*(const uint8_t*, size_t, const uint8_t*, size_t) ↔ *const u8/usize).
  • Edge cases: empty needle → Some(0), needle > haystack → None, both guarded in the wrapper (and redundantly in MemMemImpl itself); offset_from is sound since the C++ side returns haystack + index.
  • Wrapper follows the same shape as the neighboring index_of_char; use bun_highway as highway already in scope in immutable.rs.
Extended reasoning...

Overview

Two-file refactor: adds a bun_highway::memmem FFI wrapper around the existing extern "C" highway_memmem entrypoint (src/highway/lib.rs), and rewires bun_core::strings::memmem to call it on all platforms, deleting the #[cfg(windows)]/#[cfg(not(windows))] split (src/bun_core/string/immutable.rs).

This is the second revision. The first (f30210e) routed through memchr::memmem; my earlier review noted that the old libc::memmem call already resolved to highway_memmem via link-time symbol interposition, so that revision was silently swapping backends on a hot path. The author pivoted (09ffddc) to calling highway_memmem directly — which is strictly cleaner than the original code (explicit dependency instead of relying on the .set _memmem, _highway_memmem / alias("highway_memmem") trick) while keeping the same backend on POSIX and unifying Windows with it.

Security risks

None. Read-only substring search over caller-provided &[u8] slices; no allocation, no user-controlled lengths passed to FFI beyond the slice's own .len(). The unsafe block is bounded by the two &[u8] borrows and the empty/oversized-needle guards preceding it.

Level of scrutiny

Low-medium. Hot path (index_of, contains, SplitIterator), but on POSIX this is a no-op at the machine-code level — same HWY_DYNAMIC_DISPATCH(MemMemImpl) reached, just via a direct extern instead of the interposed memmem symbol. The only behavioral delta is Windows moving from bstr/memchr to highway_memmem, which aligns it with the in-tree helper the C++ side (JSBuffer.cpp:1564) already uses — the direction REVIEW.md's "grep for the in-tree helper" rule prefers.

Other factors

  • FFI ABI verified against highway_strings.cpp:2023: parameter types and return (void* ↔ *const u8) match; the wrapper mirrors the established index_of_char pattern in the same file.
  • p.offset_from(haystack.as_ptr()) is well-defined: MemMemImpl returns const_cast<uint8_t*>(haystack + index) on success, so the pointer is within the same allocation and the offset is non-negative.
  • Edge cases (needle_len == 0, haystack_len < needle_len) are guarded on both the Rust and C++ sides, so behavior matches the removed libc::memmem path exactly, including on empty haystacks.
  • The prior inline thread is marked resolved; no outstanding reviewer comments. Bug hunting system found nothing on this revision.

@Jarred-Sumner
Jarred-Sumner merged commit 9aaa44e into main Jul 21, 2026
74 of 76 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the farm/21de1cb0/memmem-unify-memchr branch July 21, 2026 04:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants