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

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
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.