Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Truffle] Experiment with lazy integer ropes.
Browse files Browse the repository at this point in the history
chrisseaton committed Apr 14, 2016
1 parent 7e590ec commit 41abbe7
Showing 6 changed files with 177 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import org.jruby.truffle.core.CoreMethod;
import org.jruby.truffle.core.CoreMethodArrayArgumentsNode;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.rope.LazyIntRope;
import org.jruby.truffle.core.rope.RopeConstants;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyNode;
@@ -1068,15 +1069,18 @@ public InspectNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization
public DynamicObject inspect(int n) {
return create7BitString(Integer.toString(n), USASCIIEncoding.INSTANCE);
return createString(new LazyIntRope(n));
}

@TruffleBoundary
@Specialization
public DynamicObject inspect(long n) {
if (n >= Integer.MIN_VALUE && n <= Integer.MAX_VALUE) {
return inspect((int) n);
}

return create7BitString(Long.toString(n), USASCIIEncoding.INSTANCE);
}

@@ -1122,10 +1126,9 @@ public ToSNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization
public DynamicObject toS(int n, NotProvided base) {
return create7BitString(Integer.toString(n), USASCIIEncoding.INSTANCE);
return createString(new LazyIntRope(n));
}

@TruffleBoundary
110 changes: 110 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/LazyIntRope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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 com.oracle.truffle.api.CompilerDirectives;
import org.jcodings.Encoding;
import org.jcodings.specific.USASCIIEncoding;

import java.nio.charset.StandardCharsets;

public class LazyIntRope extends LazyRope {

final int value;

public LazyIntRope(int value) {
this(value, USASCIIEncoding.INSTANCE, length(value));
}

protected LazyIntRope(int value, Encoding encoding, int length) {
super(encoding, length, length);
this.value = value;
assert Integer.toString(value).length() == length;
}

private static int length(int value) {
if (value < 0) {
/*
* We can't represent -Integer.MIN_VALUE, and we're about to multiple by 10 to add the space needed for the
* negative character, so handle both of those out-of-range cases.
*/

if (value <= -1000000000) {
return 11;
}

value = -value;
value *= 10;
}

// If values were evenly distributed it would make more sense to do this in the reverse order, but they aren't

if (value < 10) {
return 1;
}

if (value < 100) {
return 2;
}

if (value < 1000) {
return 3;
}

if (value < 10000) {
return 4;
}

if (value < 100000) {
return 5;
}

if (value < 1000000) {
return 6;
}

if (value < 10000000) {
return 7;
}

if (value < 100000000) {
return 8;
}

if (value < 1000000000) {
return 9;
}

return 10;
}

@Override
public Rope withEncoding(Encoding newEncoding, CodeRange newCodeRange) {
if (newCodeRange != getCodeRange()) {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException("Cannot fast-path updating encoding with different code range.");
}

return new LazyIntRope(value, newEncoding, length(value));
}

@Override
public byte[] fulfil() {
if (bytes == null) {
bytes = Integer.toString(value).getBytes(StandardCharsets.US_ASCII);
}

return bytes;
}

public int getValue() {
return value;
}
}
40 changes: 40 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/LazyRope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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;

public abstract class LazyRope extends Rope {

protected LazyRope(Encoding encoding, int byteLength, int characterLength) {
super(encoding, CodeRange.CR_7BIT, true, byteLength, characterLength, 1, null);
}

@Override
protected byte getByteSlow(int index) {
return getBytes()[index];
}

public byte[] getBytes() {
if (bytes == null) {
bytes = fulfil();
}

return bytes;
}

protected abstract byte[] fulfil();

@Override
public String toString() {
return RopeOperations.decodeUTF8(this);
}

}
4 changes: 2 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/core/rope/Rope.java
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ public abstract class Rope {
private final int characterLength;
private final int ropeDepth;
private int hashCode = 0;
private byte[] bytes;
protected byte[] bytes;

protected Rope(Encoding encoding, CodeRange codeRange, boolean singleByteOptimizable, int byteLength, int characterLength, int ropeDepth, byte[] bytes) {
this.encoding = encoding;
@@ -54,7 +54,7 @@ public final byte[] getRawBytes() {
return bytes;
}

public final byte[] getBytes() {
public byte[] getBytes() {
if (bytes == null) {
bytes = RopeOperations.flattenBytes(this);
}
Original file line number Diff line number Diff line change
@@ -783,6 +783,11 @@ public int getByte(Rope rope, int index) {
return rope.getRawBytes()[index] & 0xff;
}

@Specialization(guards = "rope.getRawBytes() == null")
public int getByte(LazyRope rope, int index) {
return rope.getBytes()[index] & 0xff;
}

@Specialization(guards = "rope.getRawBytes() == null")
public int getByteSubstringRope(SubstringRope rope, int index,
@Cached("createBinaryProfile()") ConditionProfile childRawBytesNullProfile) {
Original file line number Diff line number Diff line change
@@ -164,8 +164,9 @@ public static String decodeRope(Ruby runtime, Rope value) {
}

return builder.toString();
}
else {
} else if (value instanceof LazyIntRope) {
return Integer.toString(((LazyIntRope) value).getValue());
} else {
throw new RuntimeException("Decoding to String is not supported for rope of type: " + value.getClass().getName());
}
}
@@ -285,6 +286,8 @@ public static void visitBytes(Rope rope, BytesVisitor visitor, int offset, int l
} else {
visitBytes(child, visitor, 0, remainingEnd);
}
} else if (rope instanceof LazyRope) {
visitor.accept(rope.getBytes(), offset, length);
} else {
throw new UnsupportedOperationException("Don't know how to visit rope of type: " + rope.getClass().getName());
}
@@ -350,6 +353,11 @@ public static byte[] flattenBytes(Rope rope) {
continue;
}

// Force lazy ropes
if (current instanceof LazyRope) {
current.getBytes();
}

if (current.getRawBytes() != null) {
// In the absence of any SubstringRopes, we always take the full contents of the current rope.
if (substringLengths.isEmpty()) {
@@ -562,6 +570,8 @@ public static int hashForRange(Rope rope, int startingHashCode, int offset, int
}

return hash;
} else if (rope instanceof LazyRope) {
return hashCodeForLeafRope(rope.getBytes(), startingHashCode, offset, length);
} else {
throw new RuntimeException("Hash code not supported for rope of type: " + rope.getClass().getName());
}
@@ -664,7 +674,7 @@ public static Rope format(RubyContext context, Object... values) {
valueRope = StringOperations.encodeRope(decodeRope(context.getJRubyRuntime(), stringRope), UTF8Encoding.INSTANCE);
}
} else if (value instanceof Integer) {
valueRope = StringOperations.encodeRope(Integer.toString((int) value), UTF8Encoding.INSTANCE, CodeRange.CR_7BIT);
valueRope = new LazyIntRope((int) value);
} else if (value instanceof String) {
valueRope = context.getRopeTable().getRope((String) value);
} else {

0 comments on commit 41abbe7

Please sign in to comment.