Skip to content

node: drop the fmod call from the C++ integer validators - #47144

Open
robobun wants to merge 2 commits into
mainfrom
robobun/eca529d9/validators-isinteger
Open

robobun wants to merge 2 commits into
mainfrom
robobun/eca529d9/validators-isinteger

Conversation

@robobun

@robobun robobun commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • buf.fill takes 33 ns at offset 4096 (v1.3.14: 15 ns). zlib.crc32 with a seed at or above 2**31 takes 45 ns (v1.3.14: 17 ns).
  • The C++ node validators test for an integer with std::fmod(x, 1.0) != 0 (src/jsc/bindings/NodeValidator.cpp:49, 6 more lines, JSBuffer.cpp:370). On linux-gnu that calls Rust's compiler_builtins fmod, a bit-by-bit loop.

Fix

  • Replace the 8 tests with !JSC::isInteger(x) (x - trunc(x) == 0.0): one roundsd or frintz, no call. fill drops to 15 ns, crc32 to 19 ns.
  • Correct: both tests agree on every double, NaN and the infinities included (0 disagreements in 3,070,437,977).
  • Verified: 496,500 validator calls print the same on main and this branch. The new tests pass before and after.
  • Self-reviewed: 10 concerns raised, 7 addressed. Rejected: a separate fmod issue, an unconfirmed std::pow neighbour, and leaving validatePort out (Notes).

Background

  • The validators mirror Node's lib/internal/validators.js. Buffer methods and bindgen arguments call them from C++. JS callers skip them for valid integers (Compile TypeScript to machine code ahead of time: bun build --compile --aot #45631).
  • clang keeps the call only where libm can set errno (glibc, MSVC). macOS, musl and validatePort already get the inline form. validatePort loses its isnan and isinf returns, which isInteger covers.
  • Considered binding fmod to libm (the 10 calls stay) and -fno-math-errno (changes every libm call).

Downsides

  • Release .text grows by 256 bytes on linux-x64.
  • A min or max bound whose conversion throws costs 2 to 24 more instructions. No call site passes one.
  • Windows is not run. There the old test could leave errno = EDOM behind for bsd.c:1587.

Behaviour change: none

Notes

The 8 sites. src/jsc/bindings/NodeValidator.cpp:49 and :67 (V::validateInteger, both name overloads), :226 and :252 (V::validateInt32), :268 and :287 (V::validateUint32), src/jsc/bindings/NodeValidatorBodies.h:207 (validatePort), src/jsc/bindings/JSBuffer.cpp:370 (validateOffset).

  • 7 of them compile to 10 functions that each call fmod on main.
  • validatePort has no fmod call on main: its isnan and isinf returns rule out the errno cases, so clang already emits the inline form. It gets the same spelling.
  • Deleted: the isnan and isinf returns of validatePort. isInteger is false for NaN and for both infinities, and all three returns raised the same ERR_SOCKET_BAD_PORT. Its two instantiations shrink by 41 and 43 bytes.
  • The 4 other fmod calls in src/jsc/bindings compute a remainder and stay: bindings.cpp:6486, wtf-bindings.cpp:283, webcore/JSDOMConvertNumbers.cpp:146 and :192.

Who gains, and who does not. No issue reports this cost.

  • Gains: a native caller that passes a number to one of these validators on linux-gnu or Windows. In the tables that is buf.fill, buf.write and buf.compare with offsets, and zlib.crc32 with a seed that is not an int32.
  • No change: macOS and musl builds (inline already), a bindgen integer argument that is an int32 in range (converted inline, see the crc32 int32 rows), and JS callers of internal/validators with a valid integer (Compile TypeScript to machine code ahead of time: bun build --compile --aot #45631 answers those before the C++ validator, see the setDefaultHighWaterMark rows). The JS-facing validateInteger host functions disassemble to the same instructions on both builds.
  • Startup plus 250 node:http requests make 0 fmod calls on main (gdb breakpoint count).
  • fill reached this test in v1.4.0 (node:buffer: fix six Node compatibility divergences #32784 changed its offsets from validateNumber to validateInteger). The crc32 seed goes through bindgen's validateInteger conversion since Move 58 small natives of node:* and Bun to bindgen schemas #45851 (v1.4.4): an int32 in range converts inline, any other seed calls V::validateInteger. write and compare have paid for the test since before v1.3.14.

How it was measured. linux-x64, AMD EPYC (Zen 5), glibc 2.41. Release builds of main 11c367163a4 and of this branch, the release binaries of v1.3.14 and v1.4.4, and node v26.3.0. Windows and linux-arm64 are not timed.

  • Time: the new bench/snippets/validate-integer.mjs under mitata. The five binaries ran interleaved for 7 rounds. The tables show the lowest median, divided by the 16 calls of one iteration. Between rounds the median of a case moves by 0.9 ns (typical) to 7.1 ns on this branch, and by 2.1 ns to 11.1 ns on main (two outliers of 42 and 53 ns).
  • Instructions: perf_event_open (instructions:u, main thread), slope between 2e7 and 1e8 calls, two runs per case.
  • The tables are from the first commit of this branch (d6e4f3e0). The second commit only deletes the two validatePort returns: the other validator functions and the Buffer host functions disassemble to the same instructions in both builds.
  • The 32 accepted cases that reach the test are 2.8 to 72.7 ns faster and run 45 to 976 fewer instructions.
  • The 18 accepted cases that do not reach it (crc32 with an int32 seed, setDefaultHighWaterMark) differ by -0.2 to +0.3 ns. Their instruction counts differ by up to 3.1, less than the 8.7 between two runs of one build.

n is an int32. ns per call, then instructions per call (user mode, main thread).

call n v1.3.14 v1.4.4 main this PR node 26.3.0 main instr. PR instr.
buf.fill(1, n, n + 1) 0 16.4 21.3 20.8 15.9 24.7 491 360
buf.fill(1, n, n + 1) 4096 15.4 33.0 32.8 15.5 24.8 698 362
buf.fill(1, n, n + 1) 65536 15.3 38.3 38.1 15.5 24.7 754 362
buf.fill(1, n, n + 1) 12345678 15.5 50.6 50.3 15.8 24.8 852 362
buf.write('x', n, 1) 0 22.7 23.8 22.7 18.4 20.8 549 429
buf.write('x', n, 1) 4096 30.5 32.0 30.7 18.2 21.0 670 428
buf.write('x', n, 1) 65536 33.1 35.4 34.1 18.3 21.0 698 428
buf.write('x', n, 1) 12345678 37.1 42.4 41.2 18.2 20.8 747 428
zlib.crc32(chunk, n) 0 15.9 14.8 14.1 14.2 20.3 245 245
zlib.crc32(chunk, n) 4096 15.9 14.6 14.1 14.1 20.4 245 245
zlib.crc32(chunk, n) 65536 15.9 14.5 14.0 14.2 20.3 245 245
zlib.crc32(chunk, n) 12345678 15.8 14.3 14.1 14.1 20.2 244 245
zlib.crc32(chunk, n) 2**31 - 1 15.8 14.5 13.9 13.9 20.1 245 245
buf.compare(t, n, n + 1, n, n + 1) 0 29.9 35.2 35.1 24.2 55.0 815 561
buf.compare(t, n, n + 1, n, n + 1) 4096 51.1 58.8 59.8 24.3 55.4 1229 560
buf.compare(t, n, n + 1, n, n + 1) 65536 58.3 69.1 70.9 24.3 54.9 1340 560
buf.compare(t, n, n + 1, n, n + 1) 12345678 76.3 93.1 94.9 24.2 54.9 1537 561
setDefaultHighWaterMark(false, n) 0 6.9 7.3 5.1 5.1 2.3 82 85
setDefaultHighWaterMark(false, n) 4096 12.7 14.2 5.1 5.1 2.3 87 88
setDefaultHighWaterMark(false, n) 65536 14.8 17.0 5.1 5.1 2.3 89 89
setDefaultHighWaterMark(false, n) 12345678 19.0 22.7 5.1 5.1 2.3 86 89
setDefaultHighWaterMark(false, n) 2**31 - 1 22.3 30.6 5.1 5.2 2.3 90 89

n is a double (read from a Float64Array). ns per call, then instructions per call (user mode, main thread).

call n v1.3.14 v1.4.4 main this PR node 26.3.0 main instr. PR instr.
buf.fill(1, n, n + 1) 0 18.7 21.6 21.5 16.7 29.1 515 385
buf.fill(1, n, n + 1) 4096 18.7 34.3 33.7 16.7 29.1 723 386
buf.fill(1, n, n + 1) 65536 18.7 39.1 39.6 16.6 29.4 779 384
buf.fill(1, n, n + 1) 12345678 19.0 52.6 53.4 17.1 29.3 876 384
buf.write('x', n, 1) 0 24.3 24.8 24.2 19.4 25.3 560 442
buf.write('x', n, 1) 4096 32.7 34.6 33.5 19.2 25.2 682 440
buf.write('x', n, 1) 65536 35.1 38.1 36.7 19.2 24.8 708 438
buf.write('x', n, 1) 12345678 39.0 44.7 44.2 19.3 24.9 757 440
zlib.crc32(chunk, n) 0 17.2 21.8 22.0 19.2 20.9 416 371
zlib.crc32(chunk, n) 4096 17.2 28.0 28.6 18.9 21.0 538 371
zlib.crc32(chunk, n) 65536 17.2 29.4 30.1 19.2 20.9 566 371
zlib.crc32(chunk, n) 12345678 17.1 36.0 36.3 19.3 21.0 614 371
zlib.crc32(chunk, n) 2**31 - 1 17.3 44.4 44.4 19.1 21.0 664 371
zlib.crc32(chunk, n) 2**31 17.3 45.8 45.1 19.3 20.8 673 370
zlib.crc32(chunk, n) 0xdeadbeef 16.9 45.1 45.2 19.1 20.8 671 371
zlib.crc32(chunk, n) 2**32 - 1 17.0 44.8 44.7 19.1 20.8 670 370
buf.compare(t, n, n + 1, n, n + 1) 0 30.3 35.8 36.0 25.6 60.7 842 588
buf.compare(t, n, n + 1, n, n + 1) 4096 54.0 59.2 60.6 25.5 59.7 1255 588
buf.compare(t, n, n + 1, n, n + 1) 65536 61.7 71.0 72.7 25.4 59.6 1368 587
buf.compare(t, n, n + 1, n, n + 1) 12345678 78.6 95.6 97.9 25.2 59.7 1563 587
setDefaultHighWaterMark(false, n) 0 7.9 8.9 6.4 6.2 3.3 104 104
setDefaultHighWaterMark(false, n) 4096 14.0 17.0 6.1 6.2 3.1 105 104
setDefaultHighWaterMark(false, n) 65536 15.9 18.5 6.1 6.0 3.1 104 104
setDefaultHighWaterMark(false, n) 12345678 20.4 24.5 6.1 6.0 3.1 103 103
setDefaultHighWaterMark(false, n) 2**31 - 1 23.7 32.3 6.0 6.0 3.2 106 104
setDefaultHighWaterMark(false, n) 2**32 - 1 24.4 32.9 6.0 6.0 6.4 104 104
setDefaultHighWaterMark(false, n) 2**32 24.6 11.9 5.7 6.0 6.9 104 105
setDefaultHighWaterMark(false, n) 2**53 - 1 35.8 11.9 5.8 5.9 7.0 105 104

Rejected inputs (1.5, NaN, Infinity, the call throws). The 15 cases take 651 to 789 ns on main and 632 to 760 ns on this branch. The spread between rounds is 105 to 504 ns, more than the difference between the builds. By instruction count every case is shorter: 6,261 to 6,979 per call on main, 6,218 to 6,936 on this branch (24 to 98 fewer). node takes 6,312 to 7,546 ns. v1.3.14 accepted a fractional fill offset, so that case is not a rejection there.

Evidence for no behaviour change.

  • Predicate: fmod(x, 1.0) != 0 against !(x - trunc(x) == 0.0) for 3,070,437,977 doubles from a fixed seed. The set has named edge cases, every power of two and its two neighbours, every integer up to 222 with its neighbours, 217 mantissas for each sign and exponent, 1.5e9 random bit patterns and 1e9 random integers and near-integers. 0 disagreements against the compiler_builtins fmod of this build. 0 against glibc 2.41 fmod.
  • Behaviour: a script calls 30 entry points that reach the validators with 16,550 doubles each. It prints the return value, or the name, code and message of the error. The entry points are the 4 internal/validators functions, buf.compare, buf.fill, buf.write, Buffer.copyBytesFrom, process.exitCode, the vm timeout, zlib.crc32, crypto.getCipherInfo, checkPrimeSync, createHash with outputLength, and hkdfSync. The 496,500 output lines are byte-identical on main and on this branch, in 3 runs each.
  • The new test cases have the same results on node v26.3.0.
  • This adds no passing Node test. Node's test/parallel/test-validators.js is not vendored. It stops at its validateArray block on main and on this branch (node: match Node's validateArray minLength error message #38410 has that fix), after its integer block passes. util.test.js now carries a port of that integer block.

Where the call exists today. clang 23.1.2 -O3 -S on the reduced expression with bun's -march flags, fmod calls before and after:

  • linux-gnu x64 and arm64: 1 to 0.
  • windows-msvc x64 and arm64: 1 to 0.
  • macOS x64 and arm64, linux-musl, Android, FreeBSD: 0 to 0.

Binary (llvm-objdump and llvm-size on the two release binaries).

  • fmod call sites in the 10 validator functions: 10 to 0. In the whole binary: 52 to 42.
  • .text: 108,646,665 to 108,646,921 bytes (+256). The 10 functions: 11,241 to 11,658 bytes. The stripped file keeps its size (138,193,048 bytes).

The longer path. When min or max is not a number and its conversion throws, the validator runs 2 to 24 more instructions before it returns. The number comes from a probe that compiles NodeValidator.cpp with the release flags and calls the validators directly (12 cells: 4 functions, 3 values). The accept cells of the same probe are 32 to 296 instructions shorter. The C++ callers pass jsNumber(...) or jsUndefined(). The 89 validateInteger and validateInt32 call sites in src/js pass number literals, numeric constants, or a value validated on the line above.

Windows. Not run. The reasoning: the MSVC runtime's fmod can set errno to EDOM for an infinite argument, and the inline test sets nothing. bsd_create_listen_socket_unix (packages/bun-usockets/src/bsd.c:1587) reads errno after a failure that, on Windows, only calls SetLastError. So after a validator rejected an infinity, that path could report EDOM on main and no code on this branch.

Self-review items that were rejected.

Not in this PR.

Tests run on a debug build of this branch.

  • Pass: test/js/node/util/util.test.js, test/js/node/buffer.test.js, test/js/node/buffer-compare-bounds.test.ts, test/internal/bindgen.test.ts, test/js/node/zlib/zlib.test.js, zlib-handle-bounds-check.test.ts, test/js/node/crypto/pbkdf2.test.ts, scrypt.test.ts, argon2.test.ts, hkdf-zero-length.test.ts, crypto-random.test.ts, node-crypto.test.js, test/js/node/os/os.test.js, test/js/node/dgram/node-dgram.test.js, test/js/node/net/node-net-server.test.ts, test/js/bun/perf_hooks/histogram.test.ts, test/js/bun/util/password.test.ts, test/js/bun/udp/udp_socket.test.ts.
  • Pass: 64 files of test/js/node/test/parallel that assert integer or port validation (buffer, crypto, dgram, dns, fs, net, process, sqlite, stream, tls, vm, zlib).
  • test/js/node/process/process.test.js, test/js/node/vm/vm.test.ts and test/js/node/net/node-net.test.ts have tests that spawn processes and pass the 5 s limit on this machine's debug build. Those tests pass when run alone. On the release builds, main and this branch fail the same tests: one in process.test.js (it reads the host's node version) and two in node-net.test.ts.
  • bindgen.test.ts has one GC accounting test (estimated_size is reported to the GC) that fails in about half of the full-file runs on this machine's debug build, and on a debug build of main too (3 of 6 runs). It passes when run alone. zlib.test.js had 2 timeouts once while other jobs ran and passes on an idle re-run.

no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/node/buffer.test.js

validateInteger, validateInt32, validateUint32 and Buffer's validateOffset
tested a number for integrality with std::fmod(x, 1.0) != 0. On linux-gnu
and windows-msvc that is a library call for each validated number. On
linux-gnu the call lands in Rust's compiler_builtins fmod, which loops
once per exponent bit below 2**32. Buffer#fill with offsets (since 1.4.0)
and zlib.crc32 with a seed at or above 2**31 (since 1.4.4) pay for it.

JSC::isInteger(x) is x - trunc(x) == 0.0. It is the same predicate for
every double, NaN and the infinities included, and it compiles to one
roundsd (x64) or frintz (arm64) with no call.

validatePort gets the same spelling. clang already emitted the inline
form there.

No behaviour change. The new test cases pin the non-integer results of
Buffer#write and of validateInteger, validateInt32 and validateUint32.
They pass before and after.
@robobun

robobun commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator Author

@robobun

robobun commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status

How to reproduce, on linux-x64 with a release build (bun run build:release) of main and of this branch:

cd bench && bun install
../build/release/bun snippets/validate-integer.mjs

One iteration makes 16 calls. Per call, buf.fill(1, 4096, 4097) takes 33 ns on main 11c367163a4 and 15 ns on this branch. zlib.crc32(chunk, 0xdeadbeef) takes 45 ns on main and 19 ns on this branch.

CI: build 134315 for 4817fab has no failed job outside the test-aot-drawn lanes, which are soft_fail. The tests that are red there are in test/aot/, and this diff does not touch that code. The same tests are red on main: generated-code-of-idioms-7 in build 134092, minifying-first-saves-no-code and generated-code-of-operations in builds 134067 and 134092. The macOS arm64 test jobs passed. The two macOS x64 test jobs were still queued when this was written.

PR: #47144

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 11 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between d6e4f3e and 4817fab.

Files selected for processing: 1
  • src/jsc/bindings/NodeValidatorBodies.h

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 11c3671 and d6e4f3e.

Files selected for processing: 6
  • bench/snippets/validate-integer.mjs
  • src/jsc/bindings/JSBuffer.cpp
  • src/jsc/bindings/NodeValidator.cpp
  • src/jsc/bindings/NodeValidatorBodies.h
  • test/js/node/buffer.test.js
  • test/js/node/util/util.test.js

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

Walkthrough

Integer validation checks in JavaScriptCore bindings now use JSC::isInteger instead of std::fmod. Tests cover validation boundaries and error handling, and a benchmark exercises five Node APIs with integer, double, and rejected inputs.

Changes

Integer validation

Layer / File(s) Summary
Replace fractional-value checks
src/jsc/bindings/JSBuffer.cpp, src/jsc/bindings/NodeValidator.cpp, src/jsc/bindings/NodeValidatorBodies.h
The validation functions use JSC::isInteger for integer checks. Existing range checks and error handling remain unchanged.
Add validation tests and benchmarks
test/js/node/buffer.test.js, test/js/node/util/util.test.js, bench/snippets/validate-integer.mjs
Tests cover integer acceptance, non-integer rejection, and range errors. The benchmark measures five APIs across integer, double, and rejected inputs.

Suggested reviewers: jarred-sumner, dylan-conway

Priority: Low

Merge Risk: Minimal · previous review d6e4f

The validation behavior appears preserved. The change is ready to merge after normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: removing the fmod call from the C++ integer validators.
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.
Description check Passed The description follows the repository template. It has a 'What does this PR do?' section (Problem, Fix, Background, Downsides) and a 'How did you verify your code works?' section (measurements, byte-…
Full details: Description check

Explanation

The description follows the repository template. It has a 'What does this PR do?' section (Problem, Fix, Background, Downsides) and a 'How did you verify your code works?' section (measurements, byte-identical output comparison, unit test runs on debug build, platform-specific tests not run). It is detailed and specific, covering the performance numbers, the behaviour-change claim, and the tradeoffs. The main gap is that the verification section says Windows and linux-arm64 were not timed, and the Windows path was not run. A bot note also flags that platform-specific tests in test/js/node/buffer.test.js did not run locally and defer to CI. Those gaps are disclosed, so the description is still mostly complete.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

LGTM — eight identical one-line substitutions of the integrality predicate, with the error ordering (the "an integer" message before the min/max range message) unchanged at every site. The inline note on the now-redundant isnan/isinf guards in validatePort is a cleanup nit, not a correctness issue.

Beyond that, I checked that NaN and +/-Infinity still reach the "an integer" branch rather than falling through to the range check: the new buffer.test.js and util.test.js cases assert the exact message for both, so any divergence in JSC::isInteger's handling of non-finite values would fail CI rather than merge silently.

Extended reasoning...

The diff replaces std::fmod(x, 1.0) != 0 with !JSC::isInteger(x) at eight sites across src/jsc/bindings/JSBuffer.cpp (validateOffset), src/jsc/bindings/NodeValidator.cpp (validateInteger, validateInt32, validateUint32 overloads) and src/jsc/bindings/NodeValidatorBodies.h (validatePort), adds the MathCommon.h include to the two files that lacked it, and ships a mitata benchmark plus tests pinning exact ERR_OUT_OF_RANGE messages for 1.5, -1.5, NaN, +/-Infinity, -0, integral doubles and 2**53. It touches no security-sensitive surface; the validators sit before unchanged range checks and no exception-check or accessor ordering changed. The decision to approve rests on the change being purely mechanical with no control-flow edits, and on the added tests covering the one semantic risk (non-finite inputs) with exact-message assertions; the single inline finding is a dead-guard cleanup nit.

Comment thread src/jsc/bindings/NodeValidatorBodies.h Resolved
JSC::isInteger is false for NaN and for both infinities, and all three
returns raised the same ERR_SOCKET_BAD_PORT.

@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 second commit addresses the earlier note on validatePort, and the swap to JSC::isInteger is behavior-preserving at all eight sites.

What was reviewed:

  • Compared std::fmod(x, 1.0) != 0 against !JSC::isInteger(x) (isfinite && trunc(x) == x) on NaN, ±Infinity, -0 and 2**53: same verdict, so the "an integer" vs range-message ordering is unchanged, and validatePort still returns SOCKET_BAD_PORT for NaN/Infinity.
  • Grepped src/ for remaining fmod(x, 1.0) integrality tests — none left; <cmath> stays live in both files (std::isnan/std::round/std::trunc elsewhere).
  • Checked the new tests' exact messages against ErrorCode.cpp's appendOutOfRangeReceived separator logic and conjunctiveRange, and confirmed exposedInternals["internal/validators"] is bindgen-routed to the changed C++ bodies.
Extended reasoning...

The diff replaces an fmod-based integer test with JSC::isInteger in V::validateInteger/validateInt32/validateUint32 (NodeValidator.cpp), validateOffset (JSBuffer.cpp) and validatePort (NodeValidatorBodies.h, also dropping its now-redundant isnan/isinf returns), and adds exact-message tests in buffer.test.js and util.test.js plus a mitata benchmark. It touches argument validation for Buffer, zlib and net port inputs but no auth, crypto or injection surface; the new check is strictly equivalent on every double class the old one handled, so no new acceptance path is opened. Approve was decided by the small mechanical scope, the tests pinning code, class, verbatim message and NaN/Infinity precedence, the only prior inline finding being resolved by the newer commit, no CODEOWNERS entry for these paths, and the bug hunt ending on a dry streak with no outstanding third-party objections.

This branch has not been deployed

No deployments
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.

1 participant