Skip to content

Add window-bounded String and char overloads to SubSequence - #11796

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 8 commits into
masterfrom
dougqh/subsequence-string-methods
Jul 17, 2026
Merged

Add window-bounded String and char overloads to SubSequence#11796
gh-worker-dd-mergequeue-cf854d[bot] merged 8 commits into
masterfrom
dougqh/subsequence-string-methods

Conversation

@dougqh

@dougqhdougqh commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Rounds out SubSequence to support the other string comparison methods.

Motivation

SubSequence is intended as a drop-in replacement for a String.substring call followed up a comparison operation. To fulfill that purpose, this PR adds methods that were missing in prior PRs.

Additional Notes

Since normally the String equivalents don't typically accept CharSequence, most comparison methods have been modified to just take String.

This simplifies the implementation of the methods and should also make them a little faster, since String methods are often intrinsified.

String's startsWith(prefix, off) / regionMatches / indexOf(…, from) bound-check against the backing string's full length, not the view's endIndex. So each method first guards against this view's window, then delegates:

methoddelegatewindow guard
equals(String)regionMatches(beginIndex, o, 0, o.length())o.length() == length()
equalsIgnoreCase(String)regionMatches(true, …)o.length() == length()
startsWith(String)startsWith(prefix, beginIndex)prefix.length() <= length()
endsWith(String)startsWith(suffix, endIndex - len)len <= length()
indexOf(String)indexOf(needle, beginIndex)idx + needle.length() <= endIndex
lastIndexOf(String)lastIndexOf(needle, endIndex - len)idx >= beginIndex
startsWith(char)charAt(beginIndex)beginIndex < endIndex
endsWith(char)charAt(endIndex - 1)beginIndex < endIndex
indexOf(char)indexOf(c, beginIndex)idx < endIndex
lastIndexOf(char)lastIndexOf(c, endIndex - 1)idx >= beginIndex

indexOf/lastIndexOf return a window-relative offset (or -1). A needle/char present in the backing string but outside the view is correctly not found.

Equality

Mirrors String's split between equals and contentEquals:

  • equals(String) — region-compare fast path.
  • contentEquals(CharSequence) — general char-by-char comparison (null → false); two views with equal content are content-equal.
  • equals(Object)String → the fast path; any other CharSequence (incl. another SubSequence) → contentEquals.

equalsIgnoreCase(null) returns false, matching String.equalsIgnoreCase.

hashCode() is the String hash polynomial computed directly over the window (same value as toString().hashCode(), but without materializing the substring), so it stays consistent with equals while preserving the zero-copy property even when a view is hashed.

Tests

SubSequenceTest gains coverage for case-sensitivity, over/undershoot of both window ends (String and char), the equals(Object) dispatch and contentEquals, the empty window, and window-relative indexOf/lastIndexOf.

🤖 Generated with Claude Code

Also fixes

A latent bug in SubSequence.subSequence(int start, int end) (from #10640): the absolute end index was computed as beginIndex + start + end, overshooting by start (only correct when start == 0). The CharSequence contract treats start/end as offsets in this view's coordinates, so it is now beginIndex + end. Latent — no production caller passed start > 0 — with a regression test added (incl. the nested-subSequence case).

@dougqhdougqh changed the title Add window-bounded String overloads to SubSequenceAdd window-bounded String and char overloads to SubSequenceJun 30, 2026
@dd-octo-sts

dd-octo-stsBot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

SuiteStatus
Startup🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
ScenarioCandidatemasterΔ (95% CI of mean)
startup:insecure-bank:iast:Agent14.00 s13.96 s[-0.4%; +1.0%] (no difference)
startup:insecure-bank:tracing:Agent12.97 s13.00 s[-1.0%; +0.5%] (no difference)
startup:petclinic:appsec:Agent16.35 s16.80 s[-7.1%; +1.7%] (no difference)
startup:petclinic:iast:Agent16.79 s16.58 s[-3.4%; +5.8%] (no difference)
startup:petclinic:profiling:Agent16.17 s16.77 s[-8.0%; +0.9%] (no difference)
startup:petclinic:sca:Agent16.85 s16.89 s[-1.2%; +0.8%] (no difference)
startup:petclinic:tracing:Agent16.08 s16.05 s[-0.8%; +1.3%] (no difference)

Commit:c46c918a · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@dougqhdougqh added comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: performance Performance related changes type: feature Enhancements and improvements tag: no release notes Changes to exclude from release notes labels Jun 30, 2026
@dougqh
dougqh changed the base branch from dougqh/dbcommenter-scan-overload to masterJune 30, 2026 20:28
@dougqh
dougqhforce-pushed the dougqh/subsequence-string-methods branch from e3acc99 to 5ec05e7CompareJune 30, 2026 20:28
@datadog-datadog-us1-prod

This comment has been minimized.

@dougqh
dougqh marked this pull request as ready for review June 30, 2026 21:51
@dougqh
dougqh requested a review from a team as a code ownerJune 30, 2026 21:51
@dougqhdougqh added the type: bug fix Bug fix label Jun 30, 2026
dougqhand others added 7 commits July 14, 2026 15:38
equals/equalsIgnoreCase/startsWith/endsWith/indexOf take a String and
delegate to String's region/offset methods (regionMatches, startsWith,
indexOf) instead of a per-char CharSequence loop. Each guards against
this view's [beginIndex, endIndex) window first so the delegated read
stays in range, then reuses the JDK's backing-array compare (Latin1
fast path / intrinsics). equals(Object) now routes Strings through the
fast path, keeping the charAt loop only for non-String CharSequences.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Single-character leading/trailing/search checks (e.g. a leading '{' or a
trailing ';') read charAt(beginIndex)/charAt(endIndex-1) or delegate to
String.indexOf(int, from), each bounded to the [beginIndex, endIndex)
window. indexOf(char) returns a window-relative offset or -1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uals
- lastIndexOf(String) and lastIndexOf(char), window-bounded like indexOf,
returning a window-relative offset.
- Restructure equality to mirror String's API: equals(String) is the
region-compare fast path, contentEquals(CharSequence) is the general
char-by-char comparison, and equals(Object) dispatches String -> the
fast path, any other CharSequence -> contentEquals. This keeps two
equal-content views equal() while giving String args the fast path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace toString().hashCode() with the String hash polynomial evaluated
directly over [beginIndex, endIndex). Same value (so equals/hashCode stay
consistent), but hashing a view no longer materializes a substring --
preserving the zero-copy property the class exists for.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The #11736 CharSequence (charAt-loop) versions are superseded by the
String-delegating overloads here; String-literal callers (SQLCommenter)
bind to the String overloads. Removes the redundant pair + their tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CharSequence contract treats start/end as offsets in this view's
coordinates, so absolute end is beginIndex+end, not beginIndex+start+end
(which overshoots by start; only correct when start==0). Latent since
test including the nested case the bug broke worst.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…metry
Add a String-specialized contentEquals(String) using String.regionMatches, avoiding
the CharSequence path's virtual charAt dispatch, and make equals(String) a thin alias
for it so contentEquals is the primary content-comparison API. Suppress the SpotBugs
EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS on equals(Object) -- the cross-type view
equality is intentional and documented.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dougqh
dougqhforce-pushed the dougqh/subsequence-string-methods branch from a3c8305 to 093793bCompareJuly 14, 2026 19:59

@bric3bric3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-approving

@dougqh
dougqh enabled auto-merge July 17, 2026 18:37
@dougqh
dougqh added this pull request to the merge queueJul 17, 2026
@dd-octo-sts

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351Bot commented Jul 17, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-07-17 19:25:09 UTC ℹ️ Start processing command /merge


2026-07-17 19:25:13 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 2h (p90).


2026-07-17 20:22:44 UTC ℹ️ MergeQueue: This merge request was merged

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Jul 17, 2026
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854dBot merged commit b82b440 into masterJul 17, 2026
587 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854dBot deleted the dougqh/subsequence-string-methods branch July 17, 2026 20:22
@github-actionsgithub-actionsBot added this to the 1.65.0 milestone Jul 17, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: coreTracer coretag: ai generatedLargely based on code generated by an AI or LLMtag: no release notesChanges to exclude from release notestag: performancePerformance related changestype: bug fixBug fixtype: featureEnhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dougqh@bric3