Skip to content

Commit

Permalink
Showing 4 changed files with 130 additions and 16 deletions.
59 changes: 59 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/MutableRope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/

package org.jruby.truffle.core.rope;

import org.jcodings.Encoding;
import org.jruby.util.ByteList;

public class MutableRope extends LeafRope {

private final ByteList byteList;

protected MutableRope(byte[] bytes, Encoding encoding, CodeRange codeRange, boolean singleByteOptimizable, int characterLength) {
super(bytes, encoding, codeRange, singleByteOptimizable, characterLength);
this.byteList = new ByteList(bytes, encoding, true);
}

public MutableRope(Rope original) {
this(original.getBytes(),
original.getEncoding(),
original.getCodeRange(),
original.isSingleByteOptimizable(),
original.characterLength());
}

@Override
public Rope withEncoding(Encoding newEncoding, CodeRange newCodeRange) {
byteList.setEncoding(newEncoding);
return this;
}

@Override
public byte getByteSlow(int index) {
return (byte) byteList.get(index);
}

@Override
public byte[] extractRange(int offset, int length) {
return new ByteList(byteList, offset, length).bytes();
}

public ByteList getByteList() {
return byteList;
}

@Override
public String toString() {
// This should be used for debugging only.
return byteList.toString();
}

}
35 changes: 28 additions & 7 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeNodes.java
Original file line number Diff line number Diff line change
@@ -29,6 +29,8 @@
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.language.RubyNode;
import org.jruby.util.ByteList;
import org.jruby.util.ByteListHolder;
import org.jruby.util.StringSupport;

import static org.jruby.truffle.core.rope.CodeRange.CR_7BIT;
@@ -202,12 +204,12 @@ public MakeConcatNode(RubyContext context, SourceSection sourceSection) {

public abstract Rope executeMake(Rope left, Rope right, Encoding encoding);

@Specialization(guards = { "left.isEmpty()", "right.getEncoding() == encoding" })
@Specialization(guards = { "left.isEmpty()", "!isMutableRope(left)", "right.getEncoding() == encoding" })
public Rope concatEmptyLeftSameEncoding(Rope left, Rope right, Encoding encoding) {
return right;
}

@Specialization(guards = { "left.isEmpty()", "right.getEncoding() != encoding" })
@Specialization(guards = { "left.isEmpty()", "!isMutableRope(left)", "right.getEncoding() != encoding" })
public Rope concatEmptyLeftDifferentEncoding(Rope left, Rope right, Encoding encoding) {
return RopeOperations.withEncoding(right, encoding);
}
@@ -222,7 +224,21 @@ public Rope concatEmptyRightDifferentEncoding(Rope left, Rope right, Encoding en
return RopeOperations.withEncoding(left, encoding);
}

@Specialization(guards = { "!left.isEmpty()", "!right.isEmpty()", "left.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
@Specialization(guards = "isMutableRope(left)")
public Rope concatMutableRope(MutableRope left, Rope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
final ByteList byteList = left.getByteList();

byteList.append(right.getBytes());

if (differentEncodingProfile.profile(byteList.getEncoding() != encoding)) {
byteList.setEncoding(encoding);
}

return left;
}

@Specialization(guards = { "!left.isEmpty()", "!right.isEmpty()", "!isMutableRope(left)", "left.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
public Rope concatLeaves(LeafRope left, LeafRope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile) {
@@ -240,7 +256,7 @@ public Rope concatLeaves(LeafRope left, LeafRope right, Encoding encoding,
return makeLeafRopeNode.executeMake(bytes, encoding, codeRange);
}

@Specialization(guards = { "!right.isEmpty()", "left.byteLength() >= SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
@Specialization(guards = { "!right.isEmpty()", "!isMutableRope(left)", "left.byteLength() >= SHORT_LEAF_BYTESIZE_THRESHOLD", "right.byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD" })
public Rope concatLeavesGeneral(LeafRope left, LeafRope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile,
@@ -252,9 +268,10 @@ public Rope concatLeavesGeneral(LeafRope left, LeafRope right, Encoding encoding
public Rope concatWithReduce(ConcatRope left, LeafRope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile isLeftSingleByteOptimizableProfile) {
@Cached("createBinaryProfile()") ConditionProfile isLeftSingleByteOptimizableProfile,
@Cached("createBinaryProfile()") ConditionProfile shouldCompactProfile) {

if ((left.getRight().byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD) && (left.getRight() instanceof LeafRope)) {
if (shouldCompactProfile.profile((left.getRight().byteLength() < SHORT_LEAF_BYTESIZE_THRESHOLD) && (left.getRight() instanceof LeafRope))) {
final Rope compacted = concatLeaves((LeafRope) left.getRight(), right, encoding, sameCodeRangeProfile, brokenCodeRangeProfile);
return concat(left.getLeft(), compacted, encoding, sameCodeRangeProfile, brokenCodeRangeProfile, isLeftSingleByteOptimizableProfile);
}
@@ -270,7 +287,7 @@ public Rope concatSubstringLeaf(SubstringRope left, LeafRope right, Encoding enc
return concat(left, right, encoding, sameCodeRangeProfile, brokenCodeRangeProfile, isLeftSingleByteOptimizableProfile);
}

@Specialization(guards = { "!left.isEmpty()", "!right.isEmpty()" })
@Specialization(guards = { "!isMutableRope(left)" })
public Rope concat(Rope left, Rope right, Encoding encoding,
@Cached("createBinaryProfile()") ConditionProfile sameCodeRangeProfile,
@Cached("createBinaryProfile()") ConditionProfile brokenCodeRangeProfile,
@@ -325,6 +342,10 @@ protected static boolean isShortLeafRope(Rope rope) {
protected static boolean isLeafRope(Rope rope) {
return rope instanceof LeafRope;
}

protected static boolean isMutableRope(Rope rope) {
return rope instanceof MutableRope;
}
}


Original file line number Diff line number Diff line change
@@ -75,6 +75,7 @@
import org.jruby.truffle.core.kernel.KernelNodesFactory;
import org.jruby.truffle.core.numeric.FixnumLowerNodeGen;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.MutableRope;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.core.rope.RopeNodes;
@@ -1210,32 +1211,40 @@ public ForceEncodingNode(RubyContext context, SourceSection sourceSection) {
@TruffleBoundary
@Specialization(guards = "isRubyString(encodingName)")
public DynamicObject forceEncodingString(DynamicObject string, DynamicObject encodingName,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
final DynamicObject encoding = EncodingNodes.getEncoding(encodingName.toString());
return forceEncodingEncoding(string, encoding, differentEncodingProfile);
return forceEncodingEncoding(string, encoding, differentEncodingProfile, mutableRopeProfile);
}

@Specialization(guards = "isRubyEncoding(rubyEncoding)")
public DynamicObject forceEncodingEncoding(DynamicObject string, DynamicObject rubyEncoding,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
final Encoding encoding = EncodingOperations.getEncoding(rubyEncoding);
final Rope rope = rope(string);

if (differentEncodingProfile.profile(encoding(string) != encoding)) {
StringOperations.forceEncoding(string, encoding);
if (differentEncodingProfile.profile(rope.getEncoding() != encoding)) {
if (mutableRopeProfile.profile(rope instanceof MutableRope)) {
((MutableRope) rope).getByteList().setEncoding(encoding);
} else {
StringOperations.forceEncoding(string, encoding);
}
}

return string;
}

@Specialization(guards = { "!isRubyString(encoding)", "!isRubyEncoding(encoding)" })
public DynamicObject forceEncoding(VirtualFrame frame, DynamicObject string, Object encoding,
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile) {
@Cached("createBinaryProfile()") ConditionProfile differentEncodingProfile,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
if (toStrNode == null) {
CompilerDirectives.transferToInterpreter();
toStrNode = insert(ToStrNodeGen.create(getContext(), getSourceSection(), null));
}

return forceEncodingString(string, toStrNode.executeToStr(frame, encoding), differentEncodingProfile);
return forceEncodingString(string, toStrNode.executeToStr(frame, encoding), differentEncodingProfile, mutableRopeProfile);
}

}
@@ -1877,8 +1886,16 @@ public SizeNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public int size(DynamicObject string) {
return rope(string).characterLength();
public int size(DynamicObject string,
@Cached("createBinaryProfile()") ConditionProfile mutableRopeProfile) {
final Rope rope = rope(string);

if (mutableRopeProfile.profile(rope instanceof MutableRope)) {
// TODO (nirvdrum 11-Mar-16): This response is only correct for CR_7BIT. Mutable ropes have not been updated for multi-byte characters.
return ((MutableRope) rope).getByteList().realSize();
} else {
return rope.characterLength();
}
}

}
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@
import org.jruby.truffle.core.binding.BindingNodes;
import org.jruby.truffle.core.hash.BucketsStrategy;
import org.jruby.truffle.core.rope.CodeRange;
import org.jruby.truffle.core.rope.MutableRope;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
@@ -417,6 +418,22 @@ public DynamicObject debugPrint(DynamicObject string) {

}

@CoreMethod(names = "convert_to_mutable_rope", onSingleton = true, required = 1)
public abstract static class ConvertToMutableRope extends CoreMethodArrayArgumentsNode {

public ConvertToMutableRope(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isRubyString(string)")
public DynamicObject convertToMutableRope(DynamicObject string) {
final MutableRope mutableRope = new MutableRope(StringOperations.rope(string));
StringOperations.setRope(string, mutableRope);

return string;
}
}

@CoreMethod(names = "debug_print_rope", onSingleton = true, required = 1, optional = 1)
public abstract static class DebugPrintRopeNode extends CoreMethodArrayArgumentsNode {

0 comments on commit 1ac43bb

Please sign in to comment.