Skip to content

Commit

Permalink
[Truffle] Moveed RopeOperations.codePoint out to a node.
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvdrum committed Nov 17, 2016
1 parent cbde59c commit 51fd3a3
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 40 deletions.
59 changes: 59 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeNodes.java
Expand Up @@ -22,6 +22,7 @@
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.Encoding;
Expand Down Expand Up @@ -820,6 +821,64 @@ public int getByteConcatRope(ConcatRope rope, int index,

}

@NodeChildren({
@NodeChild(type = RubyNode.class, value = "rope"),
@NodeChild(type = RubyNode.class, value = "index")
})
public abstract static class GetCodePointNode extends RubyNode {

public static GetCodePointNode create() {
return RopeNodesFactory.GetCodePointNodeGen.create(null, null);
}

public abstract int executeGetCodePoint(Rope rope, int index);

@Specialization(guards = "rope.isSingleByteOptimizable()")
public int getCodePointSingleByte(Rope rope, int index,
@Cached("create()") GetByteNode getByteNode) {
return getByteNode.executeGetByte(rope, index);
}

@Specialization(guards = { "!rope.isSingleByteOptimizable()", "rope.getEncoding().isUTF8()" })
public int getCodePointUTF8(Rope rope, int index,
@Cached("create()") GetByteNode getByteNode,
@Cached("createBinaryProfile()") ConditionProfile singleByteCharProfile,
@Cached("create()") BranchProfile errorProfile) {
final int firstByte = getByteNode.executeGetByte(rope, index);
if (singleByteCharProfile.profile(firstByte <= 127)) {
return firstByte;
}

return getCodePointMultiByte(rope, index, errorProfile);
}

@Specialization(guards = { "!rope.isSingleByteOptimizable()", "!rope.getEncoding().isUTF8()" })
public int getCodePointMultiByte(Rope rope, int index,
@Cached("create()") BranchProfile errorProfile) {
final byte[] bytes = rope.getBytes();
final Encoding encoding = rope.getEncoding();

final int characterLength = preciseCharacterLength(encoding, bytes, index, rope.byteLength());
if (characterLength <= 0) {
errorProfile.enter();
throw new RaiseException(getContext().getCoreExceptions().argumentError("invalid byte sequence in " + encoding, null));
}

return mbcToCode(encoding, bytes, index, rope.byteLength());
}

@TruffleBoundary
private int preciseCharacterLength(Encoding encoding, byte[] bytes, int start, int end) {
return StringSupport.preciseLength(encoding, bytes, start, end);
}

@TruffleBoundary
private int mbcToCode(Encoding encoding, byte[] bytes, int start, int end) {
return encoding.mbcToCode(bytes, start, end);
}

}

@NodeChildren({
@NodeChild(type = RubyNode.class, value = "rope")
})
Expand Down
Expand Up @@ -600,19 +600,4 @@ private static CodeRange commonCodeRange(CodeRange first, CodeRange second) {
return CR_VALID;
}

@TruffleBoundary(throwsControlFlowException = true)
public static int codePoint(RubyContext context, Rope rope, int start) {
byte[] bytes = rope.getBytes();
int p = start;
int end = rope.byteLength();
Encoding enc = rope.getEncoding();

assert p < end : "empty string";
int cl = StringSupport.preciseLength(enc, bytes, p, end);
if (cl <= 0) {
throw new RaiseException(context.getCoreExceptions().argumentError("invalid byte sequence in " + enc, null));
}
return enc.mbcToCode(bytes, p, end);
}

}
Expand Up @@ -1522,7 +1522,8 @@ public Object lstripBangSingleByte(DynamicObject string) {

@TruffleBoundary
@Specialization(guards = { "!isEmpty(string)", "!isSingleByteOptimizable(string)" })
public Object lstripBang(DynamicObject string) {
public Object lstripBang(DynamicObject string,
@Cached("create()") RopeNodes.GetCodePointNode getCodePointNode) {
// Taken from org.jruby.RubyString#lstrip_bang19 and org.jruby.RubyString#multiByteLStrip.

final Rope rope = rope(string);
Expand All @@ -1532,7 +1533,7 @@ public Object lstripBang(DynamicObject string) {

int p = s;
while (p < end) {
int c = RopeOperations.codePoint(getContext(), rope, p);
int c = getCodePointNode.executeGetCodePoint(rope, p);
if (!ASCIIEncoding.INSTANCE.isSpace(c)) break;
p += StringSupport.codeLength(enc, c);
}
Expand All @@ -1551,29 +1552,15 @@ public Object lstripBang(DynamicObject string) {
@ImportStatic(StringGuards.class)
public abstract static class OrdNode extends CoreMethodArrayArgumentsNode {

@Child private RopeNodes.GetByteNode ropeGetByteNode;

@Specialization(guards = "isEmpty(string)")
public int ordEmpty(DynamicObject string) {
throw new RaiseException(coreExceptions().argumentError("empty string", this));
}

// TODO (nirvdrum 03-Feb-16): Is it possible to have a single-byte optimizable string that isn't ASCII-compatible?
@Specialization(guards = { "!isEmpty(string)", "isSingleByteOptimizable(string)" })
public int ordAsciiOnly(DynamicObject string) {
if (ropeGetByteNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
ropeGetByteNode = insert(RopeNodes.GetByteNode.create());
}

return ropeGetByteNode.executeGetByte(rope(string), 0);
}

@Specialization(guards = { "!isEmpty(string)", "!isSingleByteOptimizable(string)" })
public int ord(DynamicObject string) {
final Rope rope = rope(string);

return RopeOperations.codePoint(getContext(), rope, 0);
@Specialization(guards = "!isEmpty(string)")
public int ord(DynamicObject string,
@Cached("create()") RopeNodes.GetCodePointNode getCodePointNode) {
return getCodePointNode.executeGetCodePoint(rope(string), 0);
}

}
Expand Down Expand Up @@ -1651,7 +1638,8 @@ public Object rstripBangSingleByte(DynamicObject string) {

@TruffleBoundary
@Specialization(guards = { "!isEmpty(string)", "!isSingleByteOptimizable(string)" })
public Object rstripBang(DynamicObject string) {
public Object rstripBang(DynamicObject string,
@Cached("create()") RopeNodes.GetCodePointNode getCodePointNode) {
// Taken from org.jruby.RubyString#rstrip_bang19 and org.jruby.RubyString#multiByteRStrip19.

final Rope rope = rope(string);
Expand All @@ -1663,7 +1651,7 @@ public Object rstripBang(DynamicObject string) {
int endp = end;
int prev;
while ((prev = prevCharHead(enc, bytes, start, endp, end)) != -1) {
int point = RopeOperations.codePoint(getContext(), rope, prev);
int point = getCodePointNode.executeGetCodePoint(rope, prev);
if (point != 0 && !ASCIIEncoding.INSTANCE.isSpace(point)) break;
endp = prev;
}
Expand Down Expand Up @@ -2646,10 +2634,12 @@ public boolean validEncodingQuery(DynamicObject string) {
@CoreMethod(names = "capitalize!", raiseIfFrozenSelf = true)
public abstract static class CapitalizeBangNode extends CoreMethodArrayArgumentsNode {

@Child private RopeNodes.GetCodePointNode getCodePointNode;
@Child private RopeNodes.MakeLeafRopeNode makeLeafRopeNode;

public CapitalizeBangNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
getCodePointNode = RopeNodes.GetCodePointNode.create();
makeLeafRopeNode = RopeNodesFactory.MakeLeafRopeNodeGen.create(null, null, null, null);
}

Expand All @@ -2674,15 +2664,15 @@ public DynamicObject capitalizeBang(DynamicObject string) {
byte[] bytes = rope.getBytesCopy();
boolean modify = false;

int c = RopeOperations.codePoint(getContext(), rope, s);
int c = getCodePointNode.executeGetCodePoint(rope, s);
if (enc.isLower(c)) {
enc.codeToMbc(StringSupport.toUpper(enc, c), bytes, s);
modify = true;
}

s += StringSupport.codeLength(enc, c);
while (s < end) {
c = RopeOperations.codePoint(getContext(), rope, s);
c = getCodePointNode.executeGetCodePoint(rope, s);
if (enc.isUpper(c)) {
enc.codeToMbc(StringSupport.toLower(enc, c), bytes, s);
modify = true;
Expand Down Expand Up @@ -2815,11 +2805,13 @@ public DynamicObject stringAppend(DynamicObject string, DynamicObject other) {
@Primitive(name = "string_awk_split")
public static abstract class StringAwkSplitPrimitiveNode extends PrimitiveArrayArgumentsNode {

@Child private RopeNodes.GetCodePointNode getCodePointNode;
@Child private RopeNodes.MakeSubstringNode makeSubstringNode;
@Child private TaintResultNode taintResultNode;

public StringAwkSplitPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
getCodePointNode = RopeNodes.GetCodePointNode.create();
makeSubstringNode = RopeNodesFactory.MakeSubstringNodeGen.create(null, null, null);
taintResultNode = new TaintResultNode(context, sourceSection);
}
Expand Down Expand Up @@ -2847,7 +2839,7 @@ public DynamicObject stringAwkSplit(DynamicObject string, int lim) {
if (singlebyte) {
c = bytes[p++] & 0xff;
} else {
c = RopeOperations.codePoint(getContext(), rope, p);
c = getCodePointNode.executeGetCodePoint(rope, p);
p += StringSupport.length(enc, bytes, p, end);
}

Expand Down

0 comments on commit 51fd3a3

Please sign in to comment.