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: ab7b0a06e1c9
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7a0a5aaba4ff
Choose a head ref
  • 3 commits
  • 9 files changed
  • 1 contributor

Commits on Mar 5, 2016

  1. [Truffle] Pass the correct encoding through to Rubinius::Mirror::Stri…

    …ng#splice.
    
    Prior to this change, it was expected the caller would splice a string and then set the correct encoding for the final product. In the time in between, the String could be invalid. When we simply modified the underlying byte[] this was an acceptable trade-off. However, now that we're using ropes, it's possible the splice operation ends up using a bad specialized version of a rope operation, as the rope operations are encoding-aware.
    nirvdrum committed Mar 5, 2016

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    127d742 View commit details
  2. [Truffle] Added a single-byte rope cache.

    This cuts down creating duplicate ropes for common cases such as indexing into a byte-optimizable String.
    nirvdrum committed Mar 5, 2016
    Copy the full SHA
    7fbb658 View commit details
  3. Copy the full SHA
    7a0a5aa View commit details
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;

import static org.jruby.truffle.core.rope.CodeRange.CR_7BIT;

public class RopeConstants {

public static final LeafRope EMPTY_ASCII_8BIT_ROPE;
public static final LeafRope EMPTY_US_ASCII_ROPE;
public static final LeafRope EMPTY_UTF8_ROPE;

public static final LeafRope[] UTF8_SINGLE_BYTE_ROPES = new LeafRope[256];
public static final LeafRope[] US_ASCII_SINGLE_BYTE_ROPES = new LeafRope[256];
public static final LeafRope[] ASCII_8BIT_SINGLE_BYTE_ROPES = new LeafRope[256];

static {
final byte[] emptyBytes = new byte[] {};

EMPTY_UTF8_ROPE = new AsciiOnlyLeafRope(emptyBytes, UTF8Encoding.INSTANCE);
EMPTY_US_ASCII_ROPE = new AsciiOnlyLeafRope(emptyBytes, USASCIIEncoding.INSTANCE);
EMPTY_ASCII_8BIT_ROPE = new AsciiOnlyLeafRope(emptyBytes, ASCIIEncoding.INSTANCE);

for (int i = 0; i < 128; i++) {
final byte[] bytes = new byte[] { (byte) i };

UTF8_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, UTF8Encoding.INSTANCE);
US_ASCII_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, USASCIIEncoding.INSTANCE);
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, ASCIIEncoding.INSTANCE);
}

for (int i = 128; i < 256; i++) {
final byte[] bytes = new byte[] { (byte) i };

UTF8_SINGLE_BYTE_ROPES[i] = new InvalidLeafRope(bytes, UTF8Encoding.INSTANCE);
US_ASCII_SINGLE_BYTE_ROPES[i] = new InvalidLeafRope(bytes, USASCIIEncoding.INSTANCE);
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = new ValidLeafRope(bytes, ASCIIEncoding.INSTANCE, 1);
}
}

}
71 changes: 51 additions & 20 deletions truffle/src/main/java/org/jruby/truffle/core/rope/RopeNodes.java
Original file line number Diff line number Diff line change
@@ -55,45 +55,70 @@ public MakeSubstringNode(RubyContext context, SourceSection sourceSection) {
public abstract Rope executeMake(Rope base, int offset, int byteLength);

@Specialization(guards = "byteLength == 0")
public Rope substringZeroLength(Rope base, int offset, int byteLength,
public Rope substringZeroBytes(Rope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile isUTF8,
@Cached("createBinaryProfile()") ConditionProfile isUSAscii,
@Cached("createBinaryProfile()") ConditionProfile isAscii8Bit) {
if (isUTF8.profile(base.getEncoding() == UTF8Encoding.INSTANCE)) {
return RopeOperations.EMPTY_UTF8_ROPE;
return RopeConstants.EMPTY_UTF8_ROPE;
}

if (isUSAscii.profile(base.getEncoding() == USASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_US_ASCII_ROPE;
return RopeConstants.EMPTY_US_ASCII_ROPE;
}

if (isAscii8Bit.profile(base.getEncoding() == ASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_ASCII_8BIT_ROPE;
return RopeConstants.EMPTY_ASCII_8BIT_ROPE;
}

return RopeOperations.withEncoding(RopeOperations.EMPTY_UTF8_ROPE, base.getEncoding());
return RopeOperations.withEncoding(RopeConstants.EMPTY_UTF8_ROPE, base.getEncoding());
}

@Specialization(guards = { "byteLength > 0", "sameAsBase(base, offset, byteLength)" })
@Specialization(guards = "byteLength == 1")
public Rope substringOneByte(Rope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile isUTF8,
@Cached("createBinaryProfile()") ConditionProfile isUSAscii,
@Cached("createBinaryProfile()") ConditionProfile isAscii8Bit) {
final int index = base.get(offset) & 0xff;

if (isUTF8.profile(base.getEncoding() == UTF8Encoding.INSTANCE)) {
return RopeConstants.UTF8_SINGLE_BYTE_ROPES[index];
}

if (isUSAscii.profile(base.getEncoding() == USASCIIEncoding.INSTANCE)) {
return RopeConstants.US_ASCII_SINGLE_BYTE_ROPES[index];
}

if (isAscii8Bit.profile(base.getEncoding() == ASCIIEncoding.INSTANCE)) {
return RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[index];
}

return RopeOperations.withEncoding(RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[index], base.getEncoding());
}

@Specialization(guards = { "byteLength > 1", "sameAsBase(base, offset, byteLength)" })
public Rope substringSameAsBase(Rope base, int offset, int byteLength) {
return base;
}

@Specialization(guards = { "byteLength > 0", "!sameAsBase(base, offset, byteLength)" })
@Specialization(guards = { "byteLength > 1", "!sameAsBase(base, offset, byteLength)" })
public Rope substringLeafRope(LeafRope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile) {
return makeSubstring(base, offset, byteLength, is7BitProfile);
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile isBinaryStringProfile) {
return makeSubstring(base, offset, byteLength, is7BitProfile, isBinaryStringProfile);
}

@Specialization(guards = { "byteLength > 0", "!sameAsBase(base, offset, byteLength)" })
@Specialization(guards = { "byteLength > 1", "!sameAsBase(base, offset, byteLength)" })
public Rope substringSubstringRope(SubstringRope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile) {
return makeSubstring(base.getChild(), offset + base.getOffset(), byteLength, is7BitProfile);
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile isBinaryStringProfile) {
return makeSubstring(base.getChild(), offset + base.getOffset(), byteLength, is7BitProfile, isBinaryStringProfile);
}

@Specialization(guards = { "byteLength > 0", "!sameAsBase(base, offset, byteLength)" })
@Specialization(guards = { "byteLength > 1", "!sameAsBase(base, offset, byteLength)" })
public Rope substringConcatRope(ConcatRope base, int offset, int byteLength,
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile) {
@Cached("createBinaryProfile()") ConditionProfile is7BitProfile,
@Cached("createBinaryProfile()") ConditionProfile isBinaryStringProfile) {
Rope root = base;

while (root instanceof ConcatRope) {
@@ -118,18 +143,24 @@ public Rope substringConcatRope(ConcatRope base, int offset, int byteLength,
if (byteLength == root.byteLength()) {
return root;
} else {
return makeSubstring(root, offset, byteLength, is7BitProfile);
return makeSubstring(root, offset, byteLength, is7BitProfile, isBinaryStringProfile);
}
}

return makeSubstring(root, offset, byteLength, is7BitProfile);
return makeSubstring(root, offset, byteLength, is7BitProfile, isBinaryStringProfile);
}

private Rope makeSubstring(Rope base, int offset, int byteLength, ConditionProfile is7BitProfile) {
private Rope makeSubstring(Rope base, int offset, int byteLength, ConditionProfile is7BitProfile, ConditionProfile isBinaryStringProfile) {
if (is7BitProfile.profile(base.getCodeRange() == CR_7BIT)) {
return new SubstringRope(base, offset, byteLength, byteLength, CR_7BIT);
}

// We short-circuit here to avoid the costly process of recalculating information we already know, such as
// whether the string has a valid code range.
if (isBinaryStringProfile.profile(base.getEncoding() == ASCIIEncoding.INSTANCE)) {
return new SubstringRope(base, offset, byteLength, byteLength, CR_VALID);
}

return makeSubstringNon7Bit(base, offset, byteLength);
}

@@ -359,15 +390,15 @@ public LeafRope makeUnknownLeafRopeEmpty(byte[] bytes, Encoding encoding, CodeRa
@Cached("createBinaryProfile()") ConditionProfile isAscii8Bit,
@Cached("createBinaryProfile()") ConditionProfile isAsciiCompatible) {
if (isUTF8.profile(encoding == UTF8Encoding.INSTANCE)) {
return RopeOperations.EMPTY_UTF8_ROPE;
return RopeConstants.EMPTY_UTF8_ROPE;
}

if (isUSAscii.profile(encoding == USASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_US_ASCII_ROPE;
return RopeConstants.EMPTY_US_ASCII_ROPE;
}

if (isAscii8Bit.profile(encoding == ASCIIEncoding.INSTANCE)) {
return RopeOperations.EMPTY_ASCII_8BIT_ROPE;
return RopeConstants.EMPTY_ASCII_8BIT_ROPE;
}

if (isAsciiCompatible.profile(encoding.isAsciiCompatible())) {
Original file line number Diff line number Diff line change
@@ -36,13 +36,25 @@

public class RopeOperations {

public static final LeafRope EMPTY_ASCII_8BIT_ROPE = create(new byte[] {}, ASCIIEncoding.INSTANCE, CR_7BIT);
public static final LeafRope EMPTY_US_ASCII_ROPE = create(new byte [] {}, USASCIIEncoding.INSTANCE, CR_7BIT);
public static final LeafRope EMPTY_UTF8_ROPE = create(new byte[] {}, UTF8Encoding.INSTANCE, CR_7BIT);

private static final ConcurrentHashMap<Encoding, Charset> encodingToCharsetMap = new ConcurrentHashMap<>();

public static LeafRope create(byte[] bytes, Encoding encoding, CodeRange codeRange) {
if (bytes.length == 1) {
final int index = bytes[0] & 0xff;

if (encoding == UTF8Encoding.INSTANCE) {
return RopeConstants.UTF8_SINGLE_BYTE_ROPES[index];
}

if (encoding == USASCIIEncoding.INSTANCE) {
return RopeConstants.US_ASCII_SINGLE_BYTE_ROPES[index];
}

if (encoding == ASCIIEncoding.INSTANCE) {
return RopeConstants.ASCII_8BIT_SINGLE_BYTE_ROPES[index];
}
}

int characterLength = -1;

if (codeRange == CR_UNKNOWN) {
Original file line number Diff line number Diff line change
@@ -24,9 +24,9 @@
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.encoding.EncodingConverterNodes;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.core.rope.RopeNodes;
import org.jruby.truffle.core.rope.RopeNodesFactory;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyGuards;
@@ -93,7 +93,7 @@ private Object primitiveConvertHelper(DynamicObject encodingConverter, DynamicOb
// Taken from org.jruby.RubyConverter#primitive_convert.

final boolean nonNullSource = source != nil();
Rope sourceRope = nonNullSource ? rope(source) : RopeOperations.EMPTY_UTF8_ROPE;
Rope sourceRope = nonNullSource ? rope(source) : RopeConstants.EMPTY_UTF8_ROPE;
final Rope targetRope = rope(target);
final ByteList outBytes = targetRope.toByteListCopy();

Loading