From dce8372a4dd48cf8bcd020638a1edac1ac8751bc Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Tue, 21 Jul 2026 10:10:30 +0300 Subject: [PATCH] Keep slice-of-slice and split segments zero-copy 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 --- Jint.Benchmark/StringSliceBenchmarks.cs | 21 +++ Jint.Tests/Runtime/StringTests.cs | 153 +++++++++++++++++++ Jint/Native/JsString.cs | 34 ++++- Jint/Native/String/StringExecutionContext.cs | 4 +- Jint/Native/String/StringPrototype.cs | 73 ++++----- 5 files changed, 246 insertions(+), 39 deletions(-) diff --git a/Jint.Benchmark/StringSliceBenchmarks.cs b/Jint.Benchmark/StringSliceBenchmarks.cs index 382ab52b01..ab9a0b2125 100644 --- a/Jint.Benchmark/StringSliceBenchmarks.cs +++ b/Jint.Benchmark/StringSliceBenchmarks.cs @@ -14,6 +14,8 @@ namespace Jint.Benchmark; /// SearchOnSlice exercises indexOf/startsWith/endsWith/includes on a fresh large slice each /// iteration — the case where the inherited base search methods materialize the whole substring /// on every call. With zero-copy span search overrides this drops to ~0 allocation. +/// SliceOfSlice slices a large view of a view: without receiver unwrapping the second slice +/// materializes the intermediate view; with it, it rebases straight onto the backing string. /// [MemoryDiagnoser] [HideColumns("Error", "Gen0", "Gen1", "Gen2")] @@ -26,6 +28,7 @@ public class StringSliceBenchmarks private Prepared