david-mollitor-db opened a new pull request, #58675:
URL: https://github.com/apache/spark/pull/58675
### What changes were proposed in this pull request?
The trim overloads that take a set of trim characters —
`trimLeft(UTF8String)` and `trimRight(UTF8String)`, with `trim(UTF8String)`
delegating to both — allocate a throwaway `UTF8String` for every source
character they examine. Each iteration calls `copyUTF8String(...)` (a `new
byte[]` + `copyMemory` + `fromBytes`) only to pass that one character to
`trimString.find(searchChar, 0)`. Trimming *k* leading/trailing characters does
O(k) heap allocations.
The character under test is already a byte range in this string's backing
memory, so the membership check can read it directly. This factors `find` into
a raw-bytes overload; the public method delegates to it (byte-for-byte the same
scan):
```java
public int find(UTF8String str, int start) {
return find(str.base, str.offset, str.numBytes, start);
}
private int find(Object strBase, long strOffset, int strNumBytes, int start)
{
assert (strNumBytes > 0);
while (start <= numBytes - strNumBytes) {
if (ByteArrayMethods.arrayEquals(base, offset + start, strBase,
strOffset, strNumBytes)) {
return start;
}
start += 1;
}
return -1;
}
```
`trimLeft`/`trimRight` then compute the (clamped) code-point length and
search the source bytes in place instead of building a `UTF8String`:
```java
// trimLeft inner loop
byte leadByte = this.getByte(searchIdx);
int searchCharBytes = Math.min(numBytesForFirstByte(leadByte), numBytes -
searchIdx);
if (trimString.find(this.base, this.offset + searchIdx, searchCharBytes, 0)
>= 0) {
trimIdx += searchCharBytes;
} else {
break;
}
searchIdx += searchCharBytes;
```
The `Math.min(width, remaining)` clamp reproduces `copyUTF8String`'s
handling of a truncated trailing multi-byte leader exactly. `trimRight` gets
the same substitution inside its existing right-to-left loop.
The two `int[numBytes]` arrays `trimRight` builds up front are deliberately
left in place — removing them would require a right-to-left walk that changes
how truncated trailing multi-byte sequences are handled, a separate and riskier
change.
### Why are the changes needed?
For a leading/trailing run of *k* trim characters, the old code allocates
*k* throwaway `UTF8String` objects (each a `byte[]` + wrapper) purely to run a
membership check. Reading the source character's bytes in place turns O(k)
allocations into O(1), reducing GC pressure on trim-heavy workloads at no cost
to non-matching inputs.
### Does this PR introduce _any_ user-facing change?
No. The needle bytes/length are identical to what `copyUTF8String` produced
(same clamp) and `find` runs the same `arrayEquals` subsequence scan, so
results are byte-for-byte identical, including empty `trimString` (nothing
trimmed) and truncated trailing leaders. The set-membership semantics are
unchanged.
### How was this patch tested?
Existing `UTF8StringSuite` passes (51/51), including
`trimBothWithTrimString`, `trimLeftWithTrimString`, and
`trimRightWithTrimString` — which cover empty/single/multi-char trim sets,
multi-byte characters, all- and nothing-trimmed, and mixed ASCII+multibyte.
This is a behavior-preserving refactor, so no new tests were added.
### Was this patch authored or co-authored using generative AI tooling?
Yes. Generated-by: Claude Code
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]