Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d972d06d1967
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c6c34bc23ba0
Choose a head ref
  • 4 commits
  • 4 files changed
  • 1 contributor

Commits on Apr 12, 2016

  1. Copy the full SHA
    8df9581 View commit details
  2. Copy the full SHA
    ff7bdcf View commit details
  3. 4
    Copy the full SHA
    cbc3631 View commit details

Commits on Apr 13, 2016

  1. [Truffle] Implemented the string_splice primitive for rope buffers.

    This is necessary for methods like String#[]= to work well with rope buffers.
    nirvdrum committed Apr 13, 2016
    4
    Copy the full SHA
    c6c34bc View commit details
Original file line number Diff line number Diff line change
@@ -30,6 +30,11 @@ public RopeBuffer(Rope original) {
original.characterLength());
}

public RopeBuffer(ByteList byteList, CodeRange codeRange, boolean singleByteOptimizable, int characterLength) {
super(byteList.unsafeBytes(), byteList.getEncoding(), codeRange, singleByteOptimizable, characterLength);
this.byteList = byteList;
}

@Override
public Rope withEncoding(Encoding newEncoding, CodeRange newCodeRange) {
byteList.setEncoding(newEncoding);
54 changes: 47 additions & 7 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeNodes.java
Original file line number Diff line number Diff line change
@@ -287,9 +287,9 @@ public Rope concat(Rope left, Rope right, Encoding encoding,
depth);
}

private CodeRange commonCodeRange(CodeRange first, CodeRange second,
ConditionProfile sameCodeRangeProfile,
ConditionProfile brokenCodeRangeProfile) {
public static CodeRange commonCodeRange(CodeRange first, CodeRange second,
ConditionProfile sameCodeRangeProfile,
ConditionProfile brokenCodeRangeProfile) {
if (sameCodeRangeProfile.profile(first == second)) {
return first;
}
@@ -531,14 +531,28 @@ public Rope repeatZero(Rope base, int times,

@Specialization(guards = "times == 1")
public Rope repeatOne(Rope base, int times,
@Cached("create(getContext(), getSourceSection())") WithEncodingNode withEncodingNode) {
@Cached("create(getContext(), getSourceSection())") WithEncodingNode withEncodingNode) {
return base;
}

@Specialization(guards = { "isSingleByteString(base)", "times > 1" })
@Specialization(guards = "times > 1")
public Rope multiplyBuffer(RopeBuffer base, int times) {
final ByteList inputBytes = base.getByteList();
final ByteList outputBytes = new ByteList(inputBytes.realSize() * times);

for (int i = 0; i < times; i++) {
outputBytes.append(inputBytes);
}

outputBytes.setEncoding(inputBytes.getEncoding());

return new RopeBuffer(outputBytes, base.getCodeRange(), base.isSingleByteOptimizable(), base.characterLength() * times);
}

@Specialization(guards = { "!isRopeBuffer(base)", "isSingleByteString(base)", "times > 1" })
@TruffleBoundary
public Rope multiplySingleByteString(Rope base, int times,
@Cached("create(getContext(), getSourceSection())") MakeLeafRopeNode makeLeafRopeNode) {
@Cached("create(getContext(), getSourceSection())") MakeLeafRopeNode makeLeafRopeNode) {
final byte filler = base.getBytes()[0];

byte[] buffer = new byte[times];
@@ -547,7 +561,7 @@ public Rope multiplySingleByteString(Rope base, int times,
return makeLeafRopeNode.executeMake(buffer, base.getEncoding(), base.getCodeRange(), times);
}

@Specialization(guards = { "!isSingleByteString(base)", "times > 1" })
@Specialization(guards = { "!isRopeBuffer(base)", "!isSingleByteString(base)", "times > 1" })
public Rope repeat(Rope base, int times) {
try {
ExactMath.multiplyExact(base.byteLength(), times);
@@ -563,6 +577,9 @@ protected static boolean isSingleByteString(Rope rope) {
return rope.byteLength() == 1;
}

protected static boolean isRopeBuffer(Rope rope) {
return rope instanceof RopeBuffer;
}
}


@@ -647,6 +664,29 @@ public DynamicObject debugPrintConcatRope(ConcatRope rope, int currentLevel, boo
return nil();
}

@TruffleBoundary
@Specialization
public DynamicObject debugPrintRepeatingRope(RepeatingRope rope, int currentLevel, boolean printString) {
printPreamble(currentLevel);

// Converting a rope to a java.lang.String may populate the byte[], so we need to query for the array status beforehand.
final boolean bytesAreNull = rope.getRawBytes() == null;

System.err.println(String.format("%s (%s; BN: %b; BL: %d; CL: %d; CR: %s; T: %d; D: %d)",
printString ? rope.toString() : "<skipped>",
rope.getClass().getSimpleName(),
bytesAreNull,
rope.byteLength(),
rope.characterLength(),
rope.getCodeRange(),
rope.getTimes(),
rope.depth()));

executeDebugPrint(rope.getChild(), currentLevel + 1, printString);

return nil();
}

private void printPreamble(int level) {
if (level > 0) {
for (int i = 0; i < level; i++) {
Original file line number Diff line number Diff line change
@@ -89,6 +89,7 @@
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.RepeatingRope;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeBuffer;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
@@ -1504,7 +1505,7 @@ public Object spliceAppend(DynamicObject string, DynamicObject other, int splice
return string;
}

@Specialization(guards = { "!indexAtEitherBounds(string, spliceByteIndex)", "isRubyString(other)", "isRubyEncoding(rubyEncoding)" })
@Specialization(guards = { "!indexAtEitherBounds(string, spliceByteIndex)", "isRubyString(other)", "isRubyEncoding(rubyEncoding)", "!isRopeBuffer(string)" })
public DynamicObject splice(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace, DynamicObject rubyEncoding) {
if (leftMakeSubstringNode == null) {
CompilerDirectives.transferToInterpreter();
@@ -1541,6 +1542,39 @@ public DynamicObject splice(DynamicObject string, DynamicObject other, int splic
return string;
}

@Specialization(guards = { "!indexAtEitherBounds(string, spliceByteIndex)", "isRubyString(other)", "isRubyEncoding(rubyEncoding)", "isRopeBuffer(string)" })
public DynamicObject spliceBuffer(DynamicObject string, DynamicObject other, int spliceByteIndex, int byteCountToReplace, DynamicObject rubyEncoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile) {
final Encoding encoding = EncodingOperations.getEncoding(rubyEncoding);
final RopeBuffer source = (RopeBuffer) rope(string);
final Rope insert = rope(other);
final int rightSideStartingIndex = spliceByteIndex + byteCountToReplace;

final ByteList byteList = new ByteList(source.byteLength() + insert.byteLength() - byteCountToReplace);

byteList.append(source.getByteList(), 0, spliceByteIndex);

for (int i = 0; i < insert.byteLength(); i++) {
byteList.append(insert.getBytes()[i]);
}

byteList.append(source.getByteList(), rightSideStartingIndex, source.byteLength() - rightSideStartingIndex);
byteList.setEncoding(encoding);

// NB: The character count is only accurate for single byte-optimizable strings. Rope buffers are not supported
// in general use. If we want to allow for wide characters we would need to recalculate the length doing a
// full scan.
final Rope buffer = new RopeBuffer(byteList,
RopeNodes.MakeConcatNode.commonCodeRange(source.getCodeRange(), insert.getCodeRange(), sameCodeRangeProfile, brokenCodeRangeProfile),
source.isSingleByteOptimizable() && insert.isSingleByteOptimizable(),
source.characterLength() + insert.characterLength() - byteCountToReplace);

StringOperations.setRope(string, buffer);

return string;
}

protected boolean indexAtStartBound(int index) {
return index == 0;
}
@@ -1556,6 +1590,12 @@ protected boolean indexAtEitherBounds(DynamicObject string, int index) {

return indexAtStartBound(index) || indexAtEndBound(string, index);
}

protected boolean isRopeBuffer(DynamicObject string) {
assert RubyGuards.isRubyString(string);

return rope(string) instanceof RopeBuffer;
}
}

@RubiniusPrimitive(name = "string_to_inum")
@@ -1655,7 +1695,8 @@ public Object stringSubstring(DynamicObject string, int beg, int len,
@Cached("createBinaryProfile()") ConditionProfile negativeBeginProfile,
@Cached("createBinaryProfile()") ConditionProfile stillNegativeBeginProfile,
@Cached("createBinaryProfile()") ConditionProfile tooLargeTotalProfile,
@Cached("createBinaryProfile()") ConditionProfile negativeLengthProfile) {
@Cached("createBinaryProfile()") ConditionProfile negativeLengthProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
// Taken from org.jruby.RubyString#substr19.
final Rope rope = rope(string);
if (emptyStringProfile.profile(rope.isEmpty())) {
@@ -1684,6 +1725,10 @@ public Object stringSubstring(DynamicObject string, int beg, int len,
beg = 0;
}

if (mutableRopeProfile.profile(rope instanceof RopeBuffer)) {
return makeBuffer(string, beg, len);
}

return makeRope(string, beg, len);
}

@@ -1790,6 +1835,34 @@ private DynamicObject makeRope(DynamicObject string, int beg, int len) {
return ret;
}

private DynamicObject makeBuffer(DynamicObject string, int beg, int len) {
assert RubyGuards.isRubyString(string);

final RopeBuffer buffer = (RopeBuffer) rope(string);

if (allocateNode == null) {
CompilerDirectives.transferToInterpreter();
allocateNode = insert(AllocateObjectNodeGen.create(getContext(), getSourceSection(), null, null));
}

if (taintResultNode == null) {
CompilerDirectives.transferToInterpreter();
taintResultNode = insert(new TaintResultNode(getContext(), getSourceSection()));
}

// NB: This is only valid for the single-byte case. If multi-byte, the code range must be recalculated.
// This is not intended to be a 100% correct implementation, since you should normally be using the
// immutable rope variant.
final DynamicObject ret = allocateNode.allocate(
Layouts.BASIC_OBJECT.getLogicalClass(string),
new RopeBuffer(new ByteList(buffer.getByteList(), beg, len), buffer.getCodeRange(), buffer.isSingleByteOptimizable(), len),
null);

taintResultNode.maybeTaint(string, ret);

return ret;
}

}

@RubiniusPrimitive(name = "string_from_bytearray", needsSelf = false, lowerFixnumParameters = { 1, 2 })
Original file line number Diff line number Diff line change
@@ -490,6 +490,7 @@ public DynamicObject debugPrint(DynamicObject string, boolean printString) {
System.err.println("CL = Character Length");
System.err.println("CR = Code Range");
System.err.println("O = Offset (SubstringRope only)");
System.err.println("T = Times (RepeatingRope only)");
System.err.println("D = Depth");
System.err.println("LD = Left Depth (ConcatRope only)");
System.err.println("RD = Right Depth (ConcatRope only)");