Repository navigation
Rust I18N Rewrite - #7322
Rust I18N Rewrite#7322logan-gatlin wants to merge 24 commits into
Conversation
|
Since last review: 0 resolved, 0 still open, 0 new. Not re-run: tests, docs, jsg-gc, kj-style, memory-safety, rust-first (no author changes in their files since the last review) Reviewed commit: db9e3e66 · github run |
033e01c to
0d28d63
Compare
fixed |
|
Still too much unsafe - can't we drop it down more? we really want our rust code to be safe |
d73c508 to
7ef9a0e
Compare
7ef9a0e to
ddc0b9c
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #7322 +/- ##
==========================================
+ Coverage 41.04% 41.14% +0.09%
==========================================
Files 986 990 +4
Lines 284720 285204 +484
Branches 27618 27628 +10
==========================================
+ Hits 116877 117353 +476
- Misses 151757 151761 +4
- Partials 16086 16090 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Yes we can! Most of the unsafe was bindings to |
1bf6f7c to
f263927
Compare
|
After discussing with Mike and James, and running some benchmarks to compare simdutf with my Rust rewrite, I've decided to keep the C bindings to ICU and simdutf. For ICU it is a compatibility concern, if the ICU C library changes to include new additions to unicode, we need to pick those up. A Rust rewrite puts that maintenance burden on us. For simdutf it is a performance concern. Rust CAN achieve the same performance as C, but not without unsafe. The compiler intrinsics that simdutf uses are unsafe in Rust. |
602f8f0 to
61926c5
Compare
12e3bc7 to
861c274
Compare
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.
- Add a shared safe callback adapter for isolate and handle transitions
- Configure rust_icu_sys for Chromium ICU 78 symbol renaming - Pass Rust compiler environment through wd_rust_crate
The transcode result exposes its complete ArrayBuffer through the Buffer\nwrapper, including bytes outside the shortened result view. Initialize the\nbacking store so those unwritten bytes cannot reveal allocator contents.\n\nAdd a node:buffer regression test that inspects both regions outside the\nvisible result.
- Remove the simdutf C++ dependency and FFI bindings - Add Rust codec implementations with parity tests
- Remove the rust_icu_sys dependency - Match ICU substitution and windows-1252 behavior - Add C++ integration coverage
- Remove custom c_char16 support from the cxx bridge - Simplify codec dispatch around the shared Encoding type
- Preserve boundary and Unicode coverage with fewer test inputs - Reduce redundant exhaustive and pseudo-random cases
The test runs every corpus input through both implementations for all 16 encoding pairs, which exceeds the default 15s timeout on the Windows CI runners.
- Move the C++ bridge and raw isolate handling into ffi.rs - Extract codec, dispatch, error, and integration tests into separate files
- Replace safe-Rust codec implementations with ICU and simdutf wrappers - Add rust_icu_sys configuration and propagate Rust environment settings - Move codec coverage into FFI tests and initialize ICU in dispatch tests
- Mark FFI conversion results as must-use - Clarify the encoding conversion ADL comment - Restore the transcode test attribution
The nodejs-exceptions-rust autogate was removed, so the encoding conversion comment no longer points at it.
861c274 to
9d2563a
Compare
- Regenerate the ICU repository from the commit V8 pins - Read U_ICU_VERSION_MAJOR_NUM from that commit and update RUST_ICU_MAJOR_VERSION_NUMBER in build/deps/rust.MODULE.bazel and src/rust/i18n/BUILD.bazel - Document the version pin in the V8 update guide
9d2563a to
9665d29
Compare
The test takes about 60s on the Windows CI runners, which is the whole of the large timeout, so it intermittently times out there.
Rewrites node I18N in Rust. The C++ implementation defers to ICU and simdutf for some conversions. ICU bindings are provided by
rust_icuwhile simdutf bindings are written by hand (I filed a PR to the existing simdutf bindings to make their build work for us, but was ignored).JSG Additions
Uint8Array::from_buffernew_zeroed_with- allocates a buffer, zeroes it, then writes to it from a closure. This allows Rust to safely create and initialize buffers. Since the buffer is created in the same function it is written to, there are provably no other references to the buffer so mutating is safe.