Skip to content

Commit 30e6cad

Browse files
author
Lillian Zhang
committed
Replace boundary on StringCharacterIndexPrimitiveNode
with faster specializations for boundary checks and single-byte-optimizable strings. (Mirroring StringByteIndexPrimitiveNode after improvements in #2380).
1 parent b964e28 commit 30e6cad

File tree

1 file changed

+70
-24
lines changed

1 file changed

+70
-24
lines changed

src/main/java/org/truffleruby/core/string/StringNodes.java

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4399,48 +4399,94 @@ protected int notValidUtf8(Object string, int byteIndex,
43994399
}
44004400

44014401
@Primitive(name = "string_character_index", lowerFixnum = 2)
4402-
public abstract static class StringCharacterIndexPrimitiveNode extends PrimitiveArrayArgumentsNode {
4402+
@NodeChild(value = "string", type = RubyNode.class)
4403+
@NodeChild(value = "pattern", type = RubyNode.class)
4404+
@NodeChild(value = "offset", type = RubyNode.class)
4405+
public abstract static class StringCharacterIndexPrimitiveNode extends PrimitiveNode {
44034406

4404-
@TruffleBoundary
4405-
@Specialization
4406-
protected Object stringCharacterIndex(Object string, Object pattern, int offset,
4407-
@CachedLibrary(limit = "2") RubyStringLibrary stringLibrary,
4408-
@CachedLibrary(limit = "2") RubyStringLibrary patternLibrary,
4409-
@Cached RopeNodes.CalculateCharacterLengthNode calculateCharacterLengthNode) {
4410-
if (offset < 0) {
4411-
return nil;
4412-
}
4407+
@Child SingleByteOptimizableNode singleByteOptimizableNode = SingleByteOptimizableNode.create();
44134408

4414-
final Rope stringRope = stringLibrary.getRope(string);
4415-
final Rope patternRope = patternLibrary.getRope(pattern);
4409+
@CreateCast("string")
4410+
protected RubyNode coerceStringToRope(RubyNode string) {
4411+
return ToRopeNodeGen.create(string);
4412+
}
44164413

4417-
final int total = stringRope.byteLength();
4418-
int p = 0;
4419-
final int e = p + total;
4414+
@CreateCast("pattern")
4415+
protected RubyNode coercePatternToRope(RubyNode pattern) {
4416+
return ToRopeNodeGen.create(pattern);
4417+
}
4418+
4419+
@Specialization(guards = "offset < 0")
4420+
protected Object stringCharacterIndexNegativeOffset(Rope stringRope, Rope patternRope, int offset) {
4421+
return nil;
4422+
}
4423+
4424+
@Specialization(
4425+
guards = {
4426+
"offset >= 0",
4427+
"singleByteOptimizableNode.execute(stringRope)",
4428+
"patternRope.byteLength() > stringRope.byteLength()" })
4429+
protected Object stringCharacterIndexPatternTooLarge(Rope stringRope, Rope patternRope, int offset) {
4430+
return nil;
4431+
}
4432+
4433+
@Specialization(
4434+
guards = {
4435+
"offset >= 0",
4436+
"singleByteOptimizableNode.execute(stringRope)",
4437+
"patternRope.byteLength() <= stringRope.byteLength()" })
4438+
protected Object stringCharacterIndexSingleByteOptimizable(Rope stringRope, Rope patternRope, int offset,
4439+
@Cached RopeNodes.BytesNode stringBytesNode,
4440+
@Cached RopeNodes.BytesNode patternBytesNode,
4441+
@Cached LoopConditionProfile loopProfile,
4442+
@Cached("createCountingProfile()") ConditionProfile matchProfile) {
4443+
4444+
int p = offset;
4445+
final int e = stringRope.byteLength();
44204446
final int pe = patternRope.byteLength();
44214447
final int l = e - pe + 1;
44224448

4423-
final byte[] stringBytes = stringRope.getBytes();
4424-
final byte[] patternBytes = patternRope.getBytes();
4449+
final byte[] stringBytes = stringBytesNode.execute(stringRope);
4450+
final byte[] patternBytes = patternBytesNode.execute(patternRope);
44254451

4426-
if (stringRope.isSingleByteOptimizable()) {
4427-
for (p += offset; p < l; p++) {
4428-
if (ArrayUtils.memcmp(stringBytes, p, patternBytes, 0, pe) == 0) {
4452+
try {
4453+
for (; loopProfile.profile(p < l); p++) {
4454+
if (matchProfile.profile(ArrayUtils.memcmp(stringBytes, p, patternBytes, 0, pe) == 0)) {
44294455
return p;
44304456
}
44314457
}
4432-
4433-
return nil;
4458+
} finally {
4459+
LoopNode.reportLoopCount(this, p - offset);
44344460
}
44354461

4462+
return nil;
4463+
}
4464+
4465+
@TruffleBoundary
4466+
@Specialization(
4467+
guards = {
4468+
"offset >= 0",
4469+
"!singleByteOptimizableNode.execute(stringRope)" })
4470+
protected Object stringCharacterIndex(Rope stringRope, Rope patternRope, int offset,
4471+
@Cached RopeNodes.CalculateCharacterLengthNode calculateCharacterLengthNode,
4472+
@Cached RopeNodes.BytesNode stringBytesNode,
4473+
@Cached RopeNodes.BytesNode patternBytesNode) {
4474+
4475+
int p = offset;
4476+
final int e = stringRope.byteLength();
4477+
final int pe = patternRope.byteLength();
4478+
final int l = e - pe + 1;
4479+
4480+
final byte[] stringBytes = stringBytesNode.execute(stringRope);
4481+
final byte[] patternBytes = patternBytesNode.execute(patternRope);
4482+
44364483
final Encoding enc = stringRope.getEncoding();
44374484
final CodeRange cr = stringRope.getCodeRange();
4438-
int index = 0;
44394485
int c = 0;
4486+
int index = 0;
44404487

44414488
while (p < e && index < offset) {
44424489
c = calculateCharacterLengthNode.characterLength(enc, cr, Bytes.fromRange(stringBytes, p, e));
4443-
44444490
if (StringSupport.MBCLEN_CHARFOUND_P(c)) {
44454491
p += c;
44464492
index++;

0 commit comments

Comments
 (0)