Skip to content

Rust I18N Rewrite - #7052

Open
logan-gatlin wants to merge 10 commits into
mainfrom
logan/rust-i18n
Open

Rust I18N Rewrite#7052
logan-gatlin wants to merge 10 commits into
mainfrom
logan/rust-i18n

Conversation

@logan-gatlin

@logan-gatlin logan-gatlin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Rewrites node I18N in Rust.

  • Adds Uint8Array::from_buffer to Rust JSG. Necessary to avoid an extra copy in Rust.
  • Patched cxx to support char16_t primitive
  • Otherwise, a 1:1 translation of Node I18N

@logan-gatlin
logan-gatlin marked this pull request as ready for review August 20, 2026 16:16
@logan-gatlin
logan-gatlin requested review from a team as code owners August 20, 2026 16:16
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

The generated output of @cloudflare/workers-types matches the snapshot in types/generated-snapshot 🎉

@logan-gatlin

Copy link
Copy Markdown
Contributor Author

rebasing broke because workerd-cxx got moved, woops

@codspeed-hq

codspeed-hq Bot commented Aug 20, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 11.3%

⚡ 1 improved benchmark
✅ 71 untouched benchmarks
⏩ 129 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
simpleStringBody[Response] 25.2 µs 22.7 µs +11.3%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing logan/rust-i18n (47bc5b7) with main (de2058f)

Open in CodSpeed

Footnotes

  1. 129 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@logan-gatlin

logan-gatlin commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Turns out the V8 patch exposing ICU publicly wasn't actually necessary

@logan-gatlin

Copy link
Copy Markdown
Contributor Author

Ditched the shim file. There were 3 reasons it existed:

  1. No char16_t support in cxx. This is less trivial to solve than it sounds because char16_t is a primitive, and u16 and i16 are already mapped to other types. This was worth the more permanent fix, so I patched cxx.

  2. I thought cxx coudn't do bindings to overloaded functions - it can. Didn't fact check claude on that :(

  3. ICU adds version numbers to all its symbols through the preprocessor. I thought this would require a stable-named shim, but the preprocessor also runs on the cxx generated shim so it all works out.

Ports workerd::api::node::i18n::transcode to a new Rust crate, //src/rust/i18n, selected at runtime by a new NODEJS_I18N_RUST autogate. The C++ implementation is left in place, byte for byte, as the gate-off/rollback path.

The dispatch table, output-size computation, ICU substitute-character setup, empty-input handling, ISOLATE_LIMIT checks, and truncation all move to Rust (src/rust/i18n/dispatch.rs). The underlying codecs stay the same: a new C++ shim (src/rust/i18n/shim.{h,c++}) exposes the ICU ucnv_* primitives and the four simdutf functions the C++ path already uses, so both paths call identical codecs and cannot silently diverge.

i18n::transcode gets a gate branch at the top, following the createNodeException pattern: it maps api::node::Encoding to the bridge's Encoding enum via a fromImpl overload (kj-rs/convert.h idiom) whose switch has no default arm, rejecting BASE64/BASE64URL/HEX on either side (a deliberate divergence from the unmodified C++ dispatch, which only checks the from encoding). The Rust entry point returns a jsg::v8::ffi::MaybeLocal naming a Uint8Array; ffi-inl.h gains the missing Rust-to-C++ MaybeLocal conversion (maybe_local_from_ffi), mirroring the existing local_from_ffi.

10 Rust unit tests in dispatch.rs cover every (from, to) pair, empty input, unmappable-character substitution, the ASCII->UTF16LE latin1-widening quirk, the UTF8->UTF16LE zero-estimate quirk, invalid UTF-8, odd-length UTF-16LE input, and unpaired surrogates.
Point the src/rust/i18n shim's ICU dependency at :icuuc instead of the umbrella :icu target, per R7 (the shim only uses ucnv_* primitives from ICU's common library, not icui18n).

The bazel/BUILD.icu file vendored via V8's patch set only marks :icu and :icudata public; :icuuc is package-private. Add a small new V8 patch (0039) granting :icuuc public visibility, following the existing precedent of patch 0027 doing the same for :icudata.

Verified: just clippy i18n is clean; bazel test //src/rust/i18n:i18n_test, //src/workerd/api/node/tests:buffer-nodejs-test@, and @all-autogates all pass; full bazel test //... passes 1623/1623, observed running fresh (no cached results).
…ters

c_char is unsigned on aarch64, so the explicit i8 annotations caused a
type mismatch against the cxx-bridge declaration when building for ARM.
Comment thread src/rust/cxx/gen/src/write.rs
Comment thread src/rust/i18n/BUILD.bazel Outdated
Comment thread src/rust/AGENTS.md Outdated
Comment thread src/rust/i18n/lib.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants