Keep slice-of-slice and split segments zero-copy#2720
Merged
Conversation
Two zero-copy String optimizations building on the existing SlicedString view:
- Add JsString.CreateSliced(JsString, int, int) that rebases a slice of an
un-materialized SlicedString directly onto its original backing string,
evaluating the retention policy against that original source so chained views
cannot compound the pinning of a large backing string. A materialized view or
any other receiver falls back to the flat path. Slice/Substring/Substr now
pass the JsString receiver instead of s.ToString(), so a slice-of-slice never
materializes the intermediate view. The copy fallback now routes through
Create so short results still hit the cached single-char / empty instances.
- Split now routes each segment and the tail through CreateSliced (policy
decides copy vs view against the source length), and gains a dedicated
split("") fast path that builds an exact-capacity array from cached single-char
JsStrings, skipping the intermediate segment list. Segment list element type
changed from string to JsString accordingly.
Custom JsString subclasses keep the existing plain path (no new materialization).
Adds a SliceOfSlice benchmark and unit tests for slice-of-slice correctness,
split("") on ASCII/non-ASCII, split tail/segment correctness, and the retention
policy still copying small results.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 21, 2026
lahma
added a commit
that referenced
this pull request
Jul 22, 2026
…tring substrings (#2740) Three review follow-ups to the #2720 zero-copy slices: - JsString.Equals(JsString) gets a length pre-check: comparing a short string against a large unmaterialized view materialized the whole view just to fail the compare. Every Length override answers without materializing. - substring()/substr() gain the whole-string identity shortcut slice() already had: strings are immutable, so s.substring(0) returns the receiver instead of allocating a duplicate wrapper or view. - CreateSliced's whole-string branch routes through Create so 0/1-char sources (e.g. ''.split('x') segments) reuse the cached instances. Claude-Session: https://claude.ai/code/session_0115tQFNyyQqc1HQGPLUgZND Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 22, 2026
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.
Part of the V8-gap performance campaign (#1775 follow-up). Two increments on the existing
SlicedStringzero-copy machinery.Slice-of-slice stays zero-copy
str.slice(a,b).slice(c,d)materialized the intermediate view:Slice/Substring/Substrpasseds.ToString()intoCreateSliced. A new internalJsString.CreateSliced(JsString source, start, length)overload rebases a not-yet-materializedSlicedStringreceiver directly onto its original backing string — and evaluates the retention policy against that original source length, so chained views cannot compound the pinning of a large backing string. Materialized views and all other receivers (includingCustomStringsubclasses) take today's flat path.Split cheapening
SplitWithStringSeparatorroutes every segment and the tail throughCreateSliced, letting the policy decide copy vs view per segment, andsplit("")gains a fast path building an exact-capacity array straight from the cached single-charJsStrings.Numbers (default BDN job, A/B vs current main, i.e. after #2717)
Gates
Jint.Tests 3661/3598 (net10/net472), PublicInterface 114/114 (CustomString non-materialization tripwire green), Test262 99,431 passed / 0 failed. Rebased on current main (includes #2716–#2718); new tests cover chained-slice value equality across slice/substring/substr, view retention policy against the original source,
split("")for ASCII/BMP/surrogate pairs, and split segment/tail correctness.🤖 Generated with Claude Code