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

@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.00 s 13.96 s [-0.4%; +1.0%] (no difference)
startup:insecure-bank:tracing:Agent 12.97 s 13.00 s [-1.0%; +0.5%] (no difference)
startup:petclinic:appsec:Agent 16.35 s 16.80 s [-7.1%; +1.7%] (no difference)
startup:petclinic:iast:Agent 16.79 s 16.58 s [-3.4%; +5.8%] (no difference)
startup:petclinic:profiling:Agent 16.17 s 16.77 s [-8.0%; +0.9%] (no difference)
startup:petclinic:sca:Agent 16.85 s 16.89 s [-1.2%; +0.8%] (no difference)
startup:petclinic:tracing:Agent 16.08 s 16.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.

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

@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
@dougqh dougqh added the type: bug fix Bug fix label Jun 30, 2026
dougqh and 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
dougqh force-pushed the dougqh/subsequence-string-methods branch from a3c8305 to 093793b Compare July 14, 2026 19:59

@bric3 bric3 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

Comment thread internal-api/src/main/java/datadog/trace/util/SubSequence.java
Comment thread internal-api/src/main/java/datadog/trace/util/SubSequence.java
@dougqh
dougqh enabled auto-merge July 17, 2026 18:37
@dougqh
dougqh added this pull request to the merge queue Jul 17, 2026
@dd-octo-sts

dd-octo-sts Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot 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-queue Bot 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-cf854d Bot merged commit b82b440 into master Jul 17, 2026
587 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot deleted the dougqh/subsequence-string-methods branch July 17, 2026 20:22
@github-actions github-actions Bot added this to the 1.65.0 milestone Jul 17, 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 fix Bug fix type: feature Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants