Skip to content

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

Open
dougqh wants to merge 6 commits into
masterfrom
dougqh/subsequence-string-methods
Open

Add window-bounded String and char overloads to SubSequence#11796
dougqh wants to merge 6 commits into
masterfrom
dougqh/subsequence-string-methods

Conversation

@dougqh

@dougqh dougqh 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:

method delegate window 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).

@dougqh dougqh changed the title Add window-bounded String overloads to SubSequence Add window-bounded String and char overloads to SubSequence Jun 30, 2026
@dd-octo-sts

dd-octo-sts Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
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
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.80 s 14.64 s [+0.3%; +2.0%] (maybe worse)
startup:insecure-bank:tracing:Agent 13.68 s 13.74 s [-1.4%; +0.6%] (no difference)
startup:petclinic:appsec:Agent 16.85 s 16.57 s [+0.7%; +2.7%] (maybe worse)
startup:petclinic:iast:Agent 16.86 s 16.91 s [-1.1%; +0.4%] (no difference)
startup:petclinic:profiling:Agent 16.80 s 16.87 s [-1.3%; +0.5%] (no difference)
startup:petclinic:sca:Agent 16.90 s 16.73 s [-0.1%; +2.1%] (no difference)
startup:petclinic:tracing:Agent 16.00 s 16.06 s [-1.3%; +0.5%] (no difference)

Commit: 06b2bb96 · 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.

@dougqh dougqh added comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: performance Performance related changes type: enhancement Enhancements and improvements tag: no release notes Changes to exclude from release notes labels Jun 30, 2026
dougqh and others added 4 commits June 30, 2026 16:27
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>
@dougqh dougqh changed the base branch from dougqh/dbcommenter-scan-overload to master June 30, 2026 20:28
@dougqh dougqh force-pushed the dougqh/subsequence-string-methods branch from e3acc99 to 5ec05e7 Compare June 30, 2026 20:28
@datadog-datadog-us1-prod

This comment has been minimized.

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>
@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 owner June 30, 2026 21:51
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
#10640 -- no production caller invoked it with start>0. Adds a regression
test including the nested case the bug broke worst.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dougqh dougqh added the type: bug Bug report and fix label Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes tag: performance Performance related changes type: bug Bug report and fix type: enhancement Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant