perf(visualizer): fix O(n²) chars().nth scan and reduce allocations#344
Merged
Conversation
`generate_line_utf16_tables` used `content.chars().nth(i + 1)` where `i`
is a byte offset from `char_indices()`. That treated `i` as a char
index, so each `\r\n` check walked the string from the start (O(n) per
check, O(n²) over the loop) and returned the wrong char whenever
preceding content contained multi-byte UTF-8. `\n` is always a single
ASCII byte, so peeking `content.as_bytes().get(i + 1)` is both O(1) and
correct.
Also replace `s.push_str(&format!(...))` in `get_text` with `writeln!`,
which writes directly into the output `String` instead of allocating an
intermediate one per token.
Add `#[inline]` on `Token::{get_dst_line, get_dst_col, get_src_line,
get_src_col, get_source_id, get_name_id}`. Release+LTO already inlines
these through `serialize_mappings`, but the annotation helps debug
builds and downstream crates that don't enable LTO.
No behavior change — visualizer insta snapshots are unchanged.
Merging this PR will not alter performance
Comparing Footnotes
|
Merged
graphite-app Bot
pushed a commit
to oxc-project/oxc
that referenced
this pull request
May 26, 2026
#22742) ## Summary Pin `oxc_sourcemap` to https://github.com/oxc-project/oxc-sourcemap rev [`abae0f7`](oxc-project/oxc-sourcemap@abae0f7) (current main) to pick up: - the `SourceMap<'a>` / `OwnedSourceMap` lifetime API — zero-copy borrow-parsed sourcemaps, plus a lifetime-free owned wrapper for downstream struct fields - the visualizer perf/correctness fix in [oxc-sourcemap#344](oxc-project/oxc-sourcemap#344) (O(n²) `chars().nth()` scan that also misread CRLF after multi-byte UTF-8) ## Changes Two production call sites in `oxc_codegen` migrate from the lifetime-free `SourceMap` to `OwnedSourceMap`: - `CodegenReturn::map: Option<oxc_sourcemap::OwnedSourceMap>` - `SourcemapBuilder::into_sourcemap() -> oxc_sourcemap::OwnedSourceMap` (via the new `SourceMapBuilder::into_owned_sourcemap` helper) `OwnedSourceMap` derefs to `SourceMap<'static>`, so existing downstream consumers (`to_data_url`, `to_json_string`, `oxc_sourcemap::napi::SourceMap::from`, `SourcemapVisualizer::new`) keep working through auto-deref. No call-site changes in `napi/`, `examples/`, or `tests/` beyond one test in `sourcemap_builder.rs` whose old assertion compared against `&Arc<str>` and now matches `Option<&str>`. ## Follow-up Unpin and switch back to a crates.io version once `oxc_sourcemap` cuts the next release. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
generate_line_utf16_tables:content.chars().nth(i + 1)treated the byte offseti(fromchar_indices()) as a char index. That made the\r\ncheck walk the string from the start on every line break (O(n²) over the loop) and return the wrong char whenever earlier content contained multi-byte UTF-8.\nis always a single ASCII byte, so peekingcontent.as_bytes().get(i + 1)is both O(1) and correct.s.push_str(&format!(...))inSourcemapVisualizer::get_textwithwriteln!(s, ...)— formats directly into the outputStringinstead of allocating an intermediate one per token.Tokenaccessors (get_dst_line,get_dst_col,get_src_line,get_src_col,get_source_id,get_name_id)#[inline]. Release+LTO already inlines them throughserialize_mappings, but the annotation helps debug builds and downstream crates that don't enable LTO.No behavior change — visualizer insta snapshots match exactly.