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.
Merge branch 'truffle-ropes-2' into truffle-ropes-on-head
Browse files Browse the repository at this point in the history
nirvdrum committed Jan 22, 2016
2 parents 8ebd02e + 82ebf67 commit 60cdec0
Showing 21 changed files with 304 additions and 105 deletions.
3 changes: 2 additions & 1 deletion test/truffle/compiler/pe/core/string_pe.rb
Original file line number Diff line number Diff line change
@@ -17,9 +17,10 @@
example "'abc'.bytesize", 3
example "'こにちわ'.bytesize", 12

tagged_example "'abc' == 'abc'", true # seems to fail sometimes
example "'abc' == 'abc'", true
example "x = 'abc'; x == x", true
example "x = 'abc'; x == x.dup", true
example "x = 'abc'; 'abc' == x.dup", true

example "'abc'.ascii_only?", true
example "'こにちわ'.ascii_only?", false
4 changes: 4 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/nodes/RubyNode.java
Original file line number Diff line number Diff line change
@@ -157,6 +157,10 @@ public DynamicObject getSymbol(ByteList name) {
return getContext().getSymbol(name);
}

public DynamicObject getSymbol(Rope name) {
return getContext().getSymbol(name);
}

/** Creates a String from the ByteList, with unknown CR */
protected DynamicObject createString(ByteList bytes) {
return StringOperations.createString(getContext(), bytes);
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ protected DynamicObject toSymbolSymbol(DynamicObject symbol) {

@Specialization(guards = "isRubyString(string)")
protected DynamicObject toSymbolString(DynamicObject string) {
return getSymbol(StringOperations.getByteListReadOnly(string));
return getSymbol(StringOperations.rope(string));
}

@Specialization
Original file line number Diff line number Diff line change
@@ -192,7 +192,7 @@ public Object isCompatibleRegexpRegexp(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubyRegexp(first)", "isRubySymbol(second)"})
public Object isCompatibleRegexpSymbol(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.SYMBOL.getByteList(second).getEncoding());
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.SYMBOL.getRope(second).getEncoding());

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
@@ -204,7 +204,7 @@ public Object isCompatibleRegexpSymbol(DynamicObject first, DynamicObject second
@TruffleBoundary
@Specialization(guards = {"isRubySymbol(first)", "isRubyRegexp(second)"})
public Object isCompatibleSymbolRegexp(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.SYMBOL.getByteList(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.SYMBOL.getRope(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());

if (compatibleEncoding != null) {
return getEncoding(compatibleEncoding);
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
import org.jruby.truffle.runtime.core.ArrayOperations;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

@@ -197,8 +198,8 @@ public Object getIndex(DynamicObject matchData, int index, int length) {
@Specialization(guards = "isRubySymbol(index)")
public Object getIndexSymbol(DynamicObject matchData, DynamicObject index, NotProvided length) {
try {
ByteList value = Layouts.SYMBOL.getByteList(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getUnsafeBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));
final Rope value = Layouts.SYMBOL.getRope(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));

return getIndex(matchData, i, NotProvided.INSTANCE);
} catch (final ValueException e) {
Original file line number Diff line number Diff line change
@@ -2073,7 +2073,7 @@ public ToSymNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject toSym(DynamicObject string) {
return getSymbol(StringOperations.getByteListReadOnly(string));
return getSymbol(rope(string));
}
}

Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ public EncodingNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject encoding(DynamicObject symbol) {
return EncodingNodes.getEncoding(Layouts.SYMBOL.getByteList(symbol).getEncoding());
return EncodingNodes.getEncoding(Layouts.SYMBOL.getRope(symbol).getEncoding());
}

}
@@ -176,7 +176,7 @@ public ToSNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject toS(DynamicObject symbol) {
return createString(Layouts.SYMBOL.getByteList(symbol).dup());
return createString(Layouts.SYMBOL.getRope(symbol));
}

}
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ public CachedDispatchNode(
if (RubyGuards.isRubySymbol(cachedName)) {
cachedNameAsSymbol = (DynamicObject) cachedName;
} else if (RubyGuards.isRubyString(cachedName)) {
cachedNameAsSymbol = context.getSymbol(StringOperations.getByteListReadOnly((DynamicObject) cachedName));
cachedNameAsSymbol = context.getSymbol(StringOperations.rope((DynamicObject) cachedName));
} else if (cachedName instanceof String) {
cachedNameAsSymbol = context.getSymbol((String) cachedName);
} else {
Original file line number Diff line number Diff line change
@@ -16,20 +16,19 @@
import org.jruby.truffle.nodes.objects.AllocateObjectNode;
import org.jruby.truffle.nodes.objects.AllocateObjectNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.rope.LeafRope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

public class StringLiteralNode extends RubyNode {

private final LeafRope rope;
private final Rope rope;

@Child private AllocateObjectNode allocateObjectNode;

public StringLiteralNode(RubyContext context, SourceSection sourceSection, ByteList byteList, int codeRange) {
super(context, sourceSection);
assert byteList != null;
this.rope = RopeOperations.create(byteList.bytes(), byteList.getEncoding(), codeRange);
this.rope = context.getRopeTable().getRope(byteList.bytes(), byteList.getEncoding(), codeRange);
allocateObjectNode = AllocateObjectNodeGen.create(context, sourceSection, false, null, null);
}

Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ public DynamicObject encodingGetObjectEncodingString(DynamicObject string) {

@Specialization(guards = "isRubySymbol(symbol)")
public DynamicObject encodingGetObjectEncodingSymbol(DynamicObject symbol) {
return EncodingNodes.getEncoding(Layouts.SYMBOL.getByteList(symbol).getEncoding());
return EncodingNodes.getEncoding(Layouts.SYMBOL.getRope(symbol).getEncoding());
}

@Specialization(guards = "isRubyEncoding(encoding)")
Original file line number Diff line number Diff line change
@@ -247,11 +247,9 @@ public DynamicObject stringAwkSplit(DynamicObject string, int lim) {
private DynamicObject makeString(DynamicObject source, int index, int length) {
assert RubyGuards.isRubyString(source);

final ByteList bytes = new ByteList(StringOperations.getByteListReadOnly(source), index, length);
bytes.setEncoding(Layouts.STRING.getRope(source).getEncoding());
final Rope rope = RopeOperations.substring(rope(source), index, length);

// TODO (nirvdrum 08-Jan-16) We should be able to work out the code range from the parent string in some cases.
final DynamicObject ret = Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(Layouts.BASIC_OBJECT.getLogicalClass(source)), StringOperations.ropeFromByteList(bytes, StringSupport.CR_UNKNOWN), null);
final DynamicObject ret = Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(Layouts.BASIC_OBJECT.getLogicalClass(source)), rope, null);
taintResultNode.maybeTaint(source, ret);

return ret;
@@ -1225,9 +1223,10 @@ public Object stringRindex(DynamicObject string, DynamicObject pattern, int star
throw new RaiseException(getContext().getCoreLibrary().argumentError("negative start given", this));
}

final ByteList buf = StringOperations.getByteListReadOnly(string);
final int total = buf.getRealSize();
final int matchSize = Layouts.STRING.getRope(pattern).byteLength();
final Rope stringRope = rope(string);
final Rope patternRope = rope(pattern);
final int total = stringRope.byteLength();
final int matchSize = patternRope.byteLength();

if (pos >= total) {
pos = total - 1;
@@ -1239,10 +1238,10 @@ public Object stringRindex(DynamicObject string, DynamicObject pattern, int star
}

case 1: {
final int matcher = StringOperations.getByteListReadOnly(pattern).get(0);
final int matcher = patternRope.get(0);

while (pos >= 0) {
if (buf.get(pos) == matcher) {
if (stringRope.get(pos) == matcher) {
return pos;
}

@@ -1260,7 +1259,8 @@ public Object stringRindex(DynamicObject string, DynamicObject pattern, int star
int cur = pos;

while (cur >= 0) {
if (ByteList.memcmp(StringOperations.getByteListReadOnly(string).getUnsafeBytes(), cur, StringOperations.getByteListReadOnly(pattern).getUnsafeBytes(), 0, matchSize) == 0) {
// TODO (nirvdrum 21-Jan-16): Investigate a more rope efficient memcmp.
if (ByteList.memcmp(stringRope.getBytes(), cur, patternRope.getBytes(), 0, matchSize) == 0) {
return cur;
}

@@ -1299,16 +1299,18 @@ public DynamicObject stringPattern(DynamicObject stringClass, int size, int valu

@Specialization(guards = "isRubyString(string)")
public DynamicObject stringPattern(DynamicObject stringClass, int size, DynamicObject string) {
final Rope rope = rope(string);
final byte[] bytes = new byte[size];
final ByteList byteList = StringOperations.getByteListReadOnly(string);

if (byteList.length() > 0) {
for (int n = 0; n < size; n += byteList.length()) {
System.arraycopy(byteList.unsafeBytes(), byteList.begin(), bytes, n, Math.min(byteList.length(), size - n));
// TODO (nirvdrum 21-Jan-16): Investigate whether using a ConcatRope would be better here.
if (! rope.isEmpty()) {
for (int n = 0; n < size; n += rope.byteLength()) {
System.arraycopy(rope.getBytes(), rope.begin(), bytes, n, Math.min(rope.byteLength(), size - n));
}
}

return allocateObjectNode.allocate(stringClass, StringOperations.ropeFromByteList(new ByteList(bytes), StringSupport.CR_UNKNOWN), null);
// TODO (nirvdrum 21-Jan-16): Verify the encoding and code range are correct.
return allocateObjectNode.allocate(stringClass, RopeOperations.create(bytes, encoding(string), StringSupport.CR_UNKNOWN), null);
}

}
11 changes: 11 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/runtime/RubyContext.java
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.ArrayOperations;
import org.jruby.truffle.runtime.core.CoreLibrary;
import org.jruby.truffle.runtime.core.RopeTable;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.core.SymbolTable;
import org.jruby.truffle.runtime.ffi.LibCClockGetTime;
@@ -56,6 +57,7 @@
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.object.ObjectIDOperations;
import org.jruby.truffle.runtime.platform.CrtExterns;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rubinius.RubiniusConfiguration;
import org.jruby.truffle.runtime.sockets.NativeSockets;
import org.jruby.truffle.runtime.subsystems.*;
@@ -96,6 +98,7 @@ public class RubyContext extends ExecutionContext {
private final ObjectSpaceManager objectSpaceManager;
private final ThreadManager threadManager;
private final AtExitManager atExitManager;
private final RopeTable ropeTable = new RopeTable();
private final SymbolTable symbolTable = new SymbolTable(this);
private final Warnings warnings;
private final SafepointManager safepointManager;
@@ -382,6 +385,10 @@ public void load(Source source, Node currentNode) {
parseAndExecute(source, UTF8Encoding.INSTANCE, ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null, true, DeclarationContext.TOP_LEVEL, currentNode);
}

public RopeTable getRopeTable() {
return ropeTable;
}

public SymbolTable getSymbolTable() {
return symbolTable;
}
@@ -394,6 +401,10 @@ public DynamicObject getSymbol(ByteList name) {
return symbolTable.getSymbol(name);
}

public DynamicObject getSymbol(Rope name) {
return symbolTable.getSymbol(name);
}

@TruffleBoundary
public Object instanceEval(ByteList code, Object self, String filename, Node currentNode) {
final Source source = Source.fromText(code, filename);
108 changes: 108 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/runtime/core/RopeTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2013, 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.runtime.core;

import com.oracle.truffle.api.CompilerDirectives;
import org.jcodings.Encoding;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.util.StringSupport;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RopeTable {

private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final WeakHashMap<Key, WeakReference<Rope>> ropesTable = new WeakHashMap<>();

@CompilerDirectives.TruffleBoundary
public Rope getRope(byte[] bytes, Encoding encoding, int codeRange) {
final Key key = new Key(bytes, encoding);

lock.readLock().lock();

try {
final WeakReference<Rope> ropeReference = ropesTable.get(key);

if (ropeReference != null) {
final Rope rope = ropeReference.get();

if (rope != null) {
return rope;
}
}
} finally {
lock.readLock().unlock();
}

lock.writeLock().lock();

try {
final WeakReference<Rope> ropeReference = ropesTable.get(key);

if (ropeReference != null) {
final Rope rope = ropeReference.get();

if (rope != null) {
return rope;
}
}

final Rope rope = RopeOperations.create(bytes, encoding, codeRange);

ropesTable.put(key, new WeakReference<>(rope));

return rope;
} finally {
lock.writeLock().unlock();
}
}

public static class Key {

private final byte[] bytes;
private final Encoding encoding;
private int hashCode;

public Key(byte[] bytes, Encoding encoding) {
this.bytes = bytes;
this.encoding = encoding;
this.hashCode = Arrays.hashCode(bytes);
}

@Override
public int hashCode() {
return hashCode;
}

@Override
public boolean equals(Object o) {
if (o instanceof Key) {
final Key other = (Key) o;

return encoding == other.encoding && Arrays.equals(bytes, other.bytes);
}

return false;
}

@Override
public String toString() {
return RopeOperations.create(bytes, encoding, StringSupport.CR_UNKNOWN).toString();
}

}

}
Original file line number Diff line number Diff line change
@@ -32,44 +32,40 @@ public String toString() {

@Override
public int getCodeRange() {
return Layouts.SYMBOL.getCodeRange(symbol);
return Layouts.SYMBOL.getRope(symbol).getCodeRange();
}

@CompilerDirectives.TruffleBoundary
@Override
public int scanForCodeRange() {
final ByteList byteList = Layouts.SYMBOL.getByteList(symbol);

int cr = Layouts.SYMBOL.getCodeRange(symbol);
int cr = Layouts.SYMBOL.getRope(symbol).getCodeRange();

if (cr == StringSupport.CR_UNKNOWN) {
cr = StringSupport.codeRangeScan(byteList.getEncoding(), byteList);
Layouts.SYMBOL.setCodeRange(symbol, cr);
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException("The code range should never be unknown");
}

return cr;
}

@Override
public boolean isCodeRangeValid() {
return Layouts.SYMBOL.getCodeRange(symbol) == StringSupport.CR_VALID;
return Layouts.SYMBOL.getRope(symbol).getCodeRange() == StringSupport.CR_VALID;
}

@Override
public void setCodeRange(int codeRange) {
Layouts.SYMBOL.setCodeRange(symbol, codeRange);
throw new UnsupportedOperationException("Can't set code range on a Symbol");
}

@Override
public void clearCodeRange() {
Layouts.SYMBOL.setCodeRange(symbol, StringSupport.CR_UNKNOWN);
throw new UnsupportedOperationException("Can't clear code range on a Symbol");
}

@Override
public void keepCodeRange() {
if (Layouts.SYMBOL.getCodeRange(symbol) == StringSupport.CR_BROKEN) {
Layouts.SYMBOL.setCodeRange(symbol, StringSupport.CR_UNKNOWN);
}
throw new UnsupportedOperationException("Can't keep code range on a Symbol");
}

@Override
@@ -90,12 +86,12 @@ public void modifyAndKeepCodeRange() {
@Override
public Encoding checkEncoding(CodeRangeable other) {
// TODO (nirvdrum Jan. 13, 2015): This should check if the encodings are compatible rather than just always succeeding.
return Layouts.SYMBOL.getByteList(symbol).getEncoding();
return Layouts.SYMBOL.getRope(symbol).getEncoding();
}

@Override
public ByteList getByteList() {
return Layouts.SYMBOL.getByteList(symbol);
return Layouts.SYMBOL.getRope(symbol).getUnsafeByteList();
}

}
Original file line number Diff line number Diff line change
@@ -11,8 +11,12 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.specific.USASCIIEncoding;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.truffle.runtime.rope.LeafRope;
import org.jruby.truffle.runtime.rope.RopeOperations;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

@@ -28,23 +32,28 @@ public class SymbolTable {
private final RubyContext context;

private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final WeakHashMap<ByteList, WeakReference<DynamicObject>> symbolsTable = new WeakHashMap<>();
private final WeakHashMap<Rope, WeakReference<DynamicObject>> symbolsTable = new WeakHashMap<>();

public SymbolTable(RubyContext context) {
this.context = context;
}

@CompilerDirectives.TruffleBoundary
public DynamicObject getSymbol(String string) {
return getSymbol(StringOperations.createByteList(string));
return getSymbol(RopeOperations.create(ByteList.encode(string, "ISO-8859-1"), USASCIIEncoding.INSTANCE, StringSupport.CR_UNKNOWN));
}

@CompilerDirectives.TruffleBoundary
public DynamicObject getSymbol(ByteList bytes) {
return getSymbol(StringOperations.ropeFromByteList(bytes));
}

@CompilerDirectives.TruffleBoundary
public synchronized DynamicObject getSymbol(Rope rope) {
lock.readLock().lock();

try {
final WeakReference<DynamicObject> symbolReference = symbolsTable.get(bytes);
final WeakReference<DynamicObject> symbolReference = symbolsTable.get(rope);

if (symbolReference != null) {
final DynamicObject symbol = symbolReference.get();
@@ -60,7 +69,7 @@ public DynamicObject getSymbol(ByteList bytes) {
lock.writeLock().lock();

try {
final WeakReference<DynamicObject> symbolReference = symbolsTable.get(bytes);
final WeakReference<DynamicObject> symbolReference = symbolsTable.get(rope);

if (symbolReference != null) {
final DynamicObject symbol = symbolReference.get();
@@ -70,17 +79,18 @@ public DynamicObject getSymbol(ByteList bytes) {
}
}

final ByteList storedBytes = bytes.dup();

final DynamicObject symbolClass = context.getCoreLibrary().getSymbolClass();
final Rope flattenedRope = RopeOperations.flatten(rope);
final String string = ByteList.decode(flattenedRope.getBytes(), flattenedRope.begin(), flattenedRope.byteLength(), "ISO-8859-1");

final DynamicObject newSymbol = Layouts.SYMBOL.createSymbol(
Layouts.CLASS.getInstanceFactory(symbolClass),
storedBytes.toString(), storedBytes,
storedBytes.toString().hashCode(),
StringSupport.CR_UNKNOWN, null);
string,
flattenedRope,
string.hashCode(),
null);

symbolsTable.put(storedBytes, new WeakReference<>(newSymbol));
symbolsTable.put(flattenedRope, new WeakReference<>(newSymbol));
return newSymbol;
} finally {
lock.writeLock().unlock();
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import com.oracle.truffle.api.object.DynamicObjectFactory;
import org.jruby.truffle.om.dsl.api.Nullable;
import org.jruby.truffle.runtime.core.SymbolCodeRangeableWrapper;
import org.jruby.truffle.runtime.rope.Rope;
import org.jruby.util.ByteList;

@org.jruby.truffle.om.dsl.api.Layout
@@ -23,23 +24,19 @@ DynamicObjectFactory createSymbolShape(DynamicObject logicalClass,

DynamicObject createSymbol(DynamicObjectFactory factory,
String string,
ByteList byteList,
Rope rope,
int hashCode,
int codeRange,
@Nullable SymbolCodeRangeableWrapper codeRangeableWrapper);

boolean isSymbol(Object object);
boolean isSymbol(DynamicObject object);

String getString(DynamicObject object);

ByteList getByteList(DynamicObject object);
Rope getRope(DynamicObject object);

int getHashCode(DynamicObject object);

int getCodeRange(DynamicObject object);
void setCodeRange(DynamicObject object, int codeRange);

SymbolCodeRangeableWrapper getCodeRangeableWrapper(DynamicObject object);
void setCodeRangeableWrapper(DynamicObject object, SymbolCodeRangeableWrapper codeRangeableWrapper);

Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
*/
package org.jruby.truffle.runtime.rope;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import org.jcodings.Encoding;
import org.jruby.truffle.runtime.core.StringOperations;

@@ -31,6 +32,16 @@ public ConcatRope(Rope left, Rope right, Encoding encoding) {
this.right = right;
}

@Override
@TruffleBoundary
public int get(int index) {
if (index < left.byteLength()) {
return left.get(index);
}

return right.get(index - left.byteLength());
}

@Override
public byte[] getBytes() {
if (bytes == null) {
@@ -76,26 +87,6 @@ public byte[] extractRange(int offset, int length) {
return right.extractRange(offset - leftLength, length);
}

@Override
public int hashCode() {
return left.hashCode() + right.hashCode();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o instanceof ConcatRope) {
final ConcatRope other = (ConcatRope) o;

return left.equals(other.left) && right.equals(other.right);
}

return false;
}

public Rope getLeft() {
return left;
}
30 changes: 5 additions & 25 deletions truffle/src/main/java/org/jruby/truffle/runtime/rope/LeafRope.java
Original file line number Diff line number Diff line change
@@ -16,13 +16,17 @@
public abstract class LeafRope extends Rope {

private final byte[] bytes;
private int hashCode = -1;

public LeafRope(byte[] bytes, Encoding encoding, int codeRange, boolean singleByteOptimizable, int characterLength) {
super(encoding, codeRange, singleByteOptimizable, bytes.length, characterLength, 1);
this.bytes = bytes;
}

@Override
public int get(int index) {
return bytes[index];
}

@Override
public byte[] getBytes() {
return bytes;
@@ -40,30 +44,6 @@ public byte[] extractRange(int offset, int length) {
return ret;
}

@Override
public int hashCode() {
if (hashCode == -1) {
hashCode = Arrays.hashCode(bytes) + getEncoding().hashCode();
}

return hashCode;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o instanceof LeafRope) {
final LeafRope other = (LeafRope) o;

return getEncoding() == other.getEncoding() && Arrays.equals(bytes, other.getBytes());
}

return false;
}

@Override
public String toString() {
// This should be used for debugging only.
32 changes: 32 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/runtime/rope/Rope.java
Original file line number Diff line number Diff line change
@@ -9,9 +9,12 @@
*/
package org.jruby.truffle.runtime.rope;

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import org.jcodings.Encoding;
import org.jruby.util.ByteList;

import java.util.Arrays;

public abstract class Rope {

private final Encoding encoding;
@@ -20,6 +23,7 @@ public abstract class Rope {
private final int byteLength;
private final int characterLength;
private final int ropeDepth;
@CompilationFinal private int hashCode = -1;

protected Rope(Encoding encoding, int codeRange, boolean singleByteOptimizable, int byteLength, int characterLength, int ropeDepth) {
this.encoding = encoding;
@@ -48,6 +52,8 @@ public final ByteList getUnsafeByteList() {

public final ByteList toByteListCopy() { return new ByteList(getBytes(), getEncoding(), true); }

public abstract int get(int index);

public abstract byte[] getBytes();

public byte[] getBytesCopy() {
@@ -87,4 +93,30 @@ public int realSize() {
public int getRealSize() {
return realSize();
}

@Override
public int hashCode() {
if (hashCode == -1) {
hashCode = RopeOperations.hashForRange(this, 1, 0, byteLength);
}

return hashCode;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o instanceof Rope) {
final Rope other = (Rope) o;

// TODO (nirvdrum 21-Jan-16): We really should be taking the encoding into account here. We're currenly not because it breaks the symbol table.
return byteLength() == other.byteLength() && Arrays.equals(getBytes(), other.getBytes());
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -105,6 +105,11 @@ private static Rope makeSubstring(Rope base, int offset, int byteLength) {
return new SubstringRope(base, offset, byteLength, byteLength, StringSupport.CR_7BIT);
}

return makeSubstringNon7Bit(base, offset, byteLength);
}

@TruffleBoundary
private static Rope makeSubstringNon7Bit(Rope base, int offset, int byteLength) {
final long packedLengthAndCodeRange = calculateCodeRangeAndLength(base.getEncoding(), base.getBytes(), offset, offset + byteLength);
final int codeRange = StringSupport.unpackArg(packedLengthAndCodeRange);
final int characterLength = StringSupport.unpackResult(packedLengthAndCodeRange);
@@ -176,4 +181,61 @@ public static int strLength(Encoding enc, byte[] bytes, int p, int end) {
return StringSupport.strLength(enc, bytes, p, end);
}

public static LeafRope flatten(Rope rope) {
if (rope instanceof LeafRope) {
return (LeafRope) rope;
}

return create(rope.getBytes(), rope.getEncoding(), rope.getCodeRange());
}

public static int hashCodeForLeafRope(byte[] bytes, int startingHashCode, int offset, int length) {
assert offset <= bytes.length;
assert length <= bytes.length;

int hashCode = startingHashCode;
final int endIndex = offset + length;
for (int i = offset; i < endIndex; i++) {
hashCode = 31 * hashCode + bytes[i];
}

return hashCode;
}

@TruffleBoundary
public static int hashForRange(Rope rope, int startingHashCode, int offset, int length) {
if (rope instanceof LeafRope) {
return hashCodeForLeafRope(rope.getBytes(), startingHashCode, offset, length);
} else if (rope instanceof SubstringRope) {
final SubstringRope substringRope = (SubstringRope) rope;

return hashForRange(substringRope.getChild(), startingHashCode, offset + substringRope.getOffset(), length);
} else if (rope instanceof ConcatRope) {
final ConcatRope concatRope = (ConcatRope) rope;
final Rope left = concatRope.getLeft();
final Rope right = concatRope.getRight();

int hash = startingHashCode;
final int leftLength = left.byteLength();

if (offset < leftLength) {
// The left branch might not be large enough to extract the full hash code we want. In that case,
// we'll extract what we can and extract the difference from the right side.
if (offset + length > leftLength) {
final int coveredByLeft = leftLength - offset;
hash = hashForRange(left, hash, offset, coveredByLeft);
hash = hashForRange(right, hash, 0, length - coveredByLeft);

return hash;
} else {
return hashForRange(left, hash, offset, length);
}
}

return hashForRange(right, hash, offset - leftLength, length);
} else {
throw new RuntimeException("Hash code not supported for rope of type: " + rope.getClass().getName());
}
}

}
Original file line number Diff line number Diff line change
@@ -26,6 +26,11 @@ public SubstringRope(Rope child, int offset, int byteLength, int characterLength
this.offset = offset;
}

@Override
public int get(int index) {
return child.get(index + offset);
}

@Override
public byte[] getBytes() {
if (bytes == null) {

0 comments on commit 60cdec0

Please sign in to comment.