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

Commits on Sep 28, 2015

  1. Copy the full SHA
    b271fc7 View commit details
  2. 2
    Copy the full SHA
    bfa7d6c View commit details
  3. Copy the full SHA
    a3c9c3a View commit details
  4. Copy the full SHA
    4e42e84 View commit details
35 changes: 8 additions & 27 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -1715,38 +1715,19 @@ public IRubyObject upcase_bang19(ThreadContext context) {
}

private IRubyObject singleByteUpcase(Ruby runtime, byte[]bytes, int s, int end) {
boolean modify = false;
while (s < end) {
int c = bytes[s] & 0xff;
if (ASCII.isLower(c)) {
bytes[s] = AsciiTables.ToUpperCaseTable[c];
modify = true;
}
s++;
}
boolean modify = StringSupport.singleByteUpcase(bytes, s, end);

return modify ? this : runtime.getNil();
}

private IRubyObject multiByteUpcase(Ruby runtime, Encoding enc, byte[]bytes, int s, int end) {
boolean modify = false;
int c;
while (s < end) {
if (enc.isAsciiCompatible() && Encoding.isAscii(c = bytes[s] & 0xff)) {
if (ASCII.isLower(c)) {
bytes[s] = AsciiTables.ToUpperCaseTable[c];
modify = true;
}
s++;
} else {
c = codePoint(runtime, enc, bytes, s, end);
if (enc.isLower(c)) {
enc.codeToMbc(toUpper(enc, c), bytes, s);
modify = true;
}
s += codeLength(enc, c);
}
try {
boolean modify = StringSupport.multiByteUpcase(enc, bytes, s, end);

return modify ? this : runtime.getNil();
} catch (IllegalArgumentException e) {
throw runtime.newArgumentError(e.getMessage());
}
return modify ? this : runtime.getNil();
}

/** rb_str_downcase / rb_str_downcase_bang
39 changes: 39 additions & 0 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -2234,4 +2234,43 @@ public static boolean multiByteDowncase(Encoding enc, byte[] bytes, int s, int e

return modify;
}

public static boolean singleByteUpcase(byte[] bytes, int s, int end) {
boolean modify = false;

while (s < end) {
int c = bytes[s] & 0xff;
if (ASCIIEncoding.INSTANCE.isLower(c)) {
bytes[s] = AsciiTables.ToUpperCaseTable[c];
modify = true;
}
s++;
}

return modify;
}

public static boolean multiByteUpcase(Encoding enc, byte[] bytes, int s, int end) {
boolean modify = false;
int c;

while (s < end) {
if (enc.isAsciiCompatible() && Encoding.isAscii(c = bytes[s] & 0xff)) {
if (ASCIIEncoding.INSTANCE.isLower(c)) {
bytes[s] = AsciiTables.ToUpperCaseTable[c];
modify = true;
}
s++;
} else {
c = codePoint(enc, bytes, s, end);
if (enc.isLower(c)) {
enc.codeToMbc(toUpper(enc, c), bytes, s);
modify = true;
}
s += codeLength(enc, c);
}
}

return modify;
}
}
86 changes: 66 additions & 20 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -957,20 +957,20 @@ public DynamicObject downcase(DynamicObject string) {
final ByteList bytes = codeRangeable.getByteList();
final Encoding encoding = bytes.getEncoding();

if (bytes.realSize() == 0) {
return nil();
}

if (encoding.isDummy()) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(
getContext().getCoreLibrary().encodingCompatibilityError(
String.format("incompatible encoding with this operation: %s", encoding), this));
}

if (bytes.realSize() == 0) {
return nil();
}

codeRangeable.modifyAndKeepCodeRange();

final boolean modified = StringSupport.multiByteDowncase(encoding, bytes.unsafeBytes(), bytes.begin(), bytes.realSize());
final boolean modified = multiByteDowncase(encoding, bytes.unsafeBytes(), bytes.begin(), bytes.realSize());
if (modified) {
return string;
} else {
@@ -2124,38 +2124,89 @@ public DynamicObject unpack(DynamicObject string, DynamicObject format) {
@CoreMethod(names = "upcase", taintFromSelf = true)
public abstract static class UpcaseNode extends CoreMethodArrayArgumentsNode {

@Child CallDispatchHeadNode dupNode;
@Child CallDispatchHeadNode upcaseBangNode;

public UpcaseNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
dupNode = DispatchHeadNodeFactory.createMethodCall(context);
upcaseBangNode = DispatchHeadNodeFactory.createMethodCall(context);
}

@TruffleBoundary
@Specialization
public DynamicObject upcase(DynamicObject string) {
final ByteList byteListString = StringNodesHelper.upcase(getContext().getRuntime(), Layouts.STRING.getByteList(string));
return Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(Layouts.BASIC_OBJECT.getLogicalClass(string)), byteListString, StringSupport.CR_UNKNOWN, null);
public Object upcase(VirtualFrame frame, DynamicObject string) {
final Object duped = dupNode.call(frame, string, "dup", null);
upcaseBangNode.call(frame, duped, "upcase!", null);

return duped;
}

}

@CoreMethod(names = "upcase!", raiseIfFrozenSelf = true)
@ImportStatic(StringGuards.class)
public abstract static class UpcaseBangNode extends CoreMethodArrayArgumentsNode {

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

@TruffleBoundary
@Specialization
public DynamicObject upcaseBang(DynamicObject string) {
final ByteList byteListString = StringNodesHelper.upcase(getContext().getRuntime(), Layouts.STRING.getByteList(string));
@Specialization(guards = "isSingleByteOptimizable(string)")
public DynamicObject upcaseSingleByte(DynamicObject string) {
final CodeRangeable codeRangeable = Layouts.STRING.getCodeRangeableWrapper(string);
final ByteList bytes = codeRangeable.getByteList();

if (byteListString.equal(Layouts.STRING.getByteList(string))) {
if (bytes.realSize() == 0) {
return nil();
}

codeRangeable.modifyAndKeepCodeRange();

final boolean modified = singleByteUpcase(bytes.unsafeBytes(), bytes.begin(), bytes.realSize());
if (modified) {
return string;
} else {
Layouts.STRING.setByteList(string, byteListString);
return nil();
}
}

@Specialization(guards = "!isSingleByteOptimizable(string)")
public DynamicObject upcase(DynamicObject string) {
final CodeRangeable codeRangeable = Layouts.STRING.getCodeRangeableWrapper(string);
final ByteList bytes = codeRangeable.getByteList();
final Encoding encoding = bytes.getEncoding();

if (encoding.isDummy()) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(
getContext().getCoreLibrary().encodingCompatibilityError(
String.format("incompatible encoding with this operation: %s", encoding), this));
}

if (bytes.realSize() == 0) {
return nil();
}

codeRangeable.modifyAndKeepCodeRange();

final boolean modified = multiByteUpcase(encoding, bytes.unsafeBytes(), bytes.begin(), bytes.realSize());
if (modified) {
return string;
} else {
return nil();
}
}

@TruffleBoundary
private boolean singleByteUpcase(byte[] bytes, int s, int end) {
return StringSupport.singleByteUpcase(bytes, s, end);
}

@TruffleBoundary
private boolean multiByteUpcase(Encoding encoding, byte[] bytes, int s, int end) {
return StringSupport.multiByteUpcase(encoding, bytes, s, end);
}

}

@CoreMethod(names = "valid_encoding?")
@@ -2268,11 +2319,6 @@ public DynamicObject clear(DynamicObject string) {

public static class StringNodesHelper {

@TruffleBoundary
public static ByteList upcase(Ruby runtime, ByteList string) {
return runtime.newString(string).upcase(runtime.getCurrentContext()).getByteList();
}

public static int checkIndex(DynamicObject string, int index, RubyNode node) {
assert RubyGuards.isRubyString(string);