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

Commits on Sep 28, 2015

  1. Copy the full SHA
    3bbe011 View commit details
  2. Copy the full SHA
    723dd77 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
@@ -1789,38 +1789,19 @@ public IRubyObject downcase_bang19(ThreadContext context) {
}

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

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

private IRubyObject multiByteDowncase(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.isUpper(c)) {
bytes[s] = AsciiTables.ToLowerCaseTable[c];
modify = true;
}
s++;
} else {
c = codePoint(runtime, enc, bytes, s, end);
if (enc.isUpper(c)) {
enc.codeToMbc(toLower(enc, c), bytes, s);
modify = true;
}
s += codeLength(enc, c);
}
try {
boolean modify = StringSupport.multiByteDowncase(enc, bytes, s, end);

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


45 changes: 45 additions & 0 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -382,6 +382,13 @@ public static int codePoint(Ruby runtime, Encoding enc, byte[]bytes, int p, int
return enc.mbcToCode(bytes, p, end);
}

public static int codePoint(Encoding enc, byte[] bytes, int p, int end) {
if (p >= end) throw new IllegalArgumentException("empty string");
int cl = preciseLength(enc, bytes, p, end);
if (cl <= 0) throw new IllegalArgumentException("invalid byte sequence in " + enc);
return enc.mbcToCode(bytes, p, end);
}

public static int codeLength(Encoding enc, int c) {
return enc.codeToMbcLength(c);
}
@@ -2189,4 +2196,42 @@ private static int rb_memsearch_qs_utf8(byte[] xsBytes, int xs, int m, byte[] ys
}
return -1;
}

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

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

return modify;
}

public static boolean multiByteDowncase(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.isUpper(c)) {
bytes[s] = AsciiTables.ToLowerCaseTable[c];
modify = true;
}
s++;
} else {
c = codePoint(enc, bytes, s, end);
if (enc.isUpper(c)) {
enc.codeToMbc(toLower(enc, c), bytes, s);
modify = true;
}
s += codeLength(enc, c);
}
}

return modify;
}
}
74 changes: 49 additions & 25 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -924,40 +924,69 @@ private Object deleteBangSlow(DynamicObject string, DynamicObject... otherString
}
}

@CoreMethod(names = "downcase", taintFromSelf = true)
public abstract static class DowncaseNode extends CoreMethodArrayArgumentsNode {
@CoreMethod(names = "downcase!", raiseIfFrozenSelf = true)
@ImportStatic(StringGuards.class)
public abstract static class DowncaseBangNode extends CoreMethodArrayArgumentsNode {

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

@TruffleBoundary
@Specialization
public DynamicObject downcase(DynamicObject string) {
final ByteList newByteList = StringNodesHelper.downcase(getContext().getRuntime(), Layouts.STRING.getByteList(string));
return Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(Layouts.BASIC_OBJECT.getLogicalClass(string)), newByteList, StringSupport.CR_UNKNOWN, null);
}
}
@Specialization(guards = "isSingleByteOptimizable(string)")
public DynamicObject downcaseSingleByte(DynamicObject string) {
final CodeRangeable codeRangeable = Layouts.STRING.getCodeRangeableWrapper(string);
final ByteList bytes = codeRangeable.getByteList();

@CoreMethod(names = "downcase!", raiseIfFrozenSelf = true)
public abstract static class DowncaseBangNode extends CoreMethodArrayArgumentsNode {
if (bytes.realSize() == 0) {
return nil();
}

public DowncaseBangNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
codeRangeable.modifyAndKeepCodeRange();

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

@TruffleBoundary
@Specialization
@Specialization(guards = "!isSingleByteOptimizable(string)")
public DynamicObject downcase(DynamicObject string) {
final ByteList newByteList = StringNodesHelper.downcase(getContext().getRuntime(), Layouts.STRING.getByteList(string));
final CodeRangeable codeRangeable = Layouts.STRING.getCodeRangeableWrapper(string);
final ByteList bytes = codeRangeable.getByteList();
final Encoding encoding = bytes.getEncoding();

if (newByteList.equal(Layouts.STRING.getByteList(string))) {
if (bytes.realSize() == 0) {
return nil();
} else {
Layouts.STRING.setByteList(string, newByteList);
}

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

codeRangeable.modifyAndKeepCodeRange();

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

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

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

@CoreMethod(names = "each_byte", needsBlock = true, returnsEnumeratorIfNoBlock = true)
@@ -2244,11 +2273,6 @@ public static ByteList upcase(Ruby runtime, ByteList string) {
return runtime.newString(string).upcase(runtime.getCurrentContext()).getByteList();
}

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

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

6 changes: 6 additions & 0 deletions truffle/src/main/ruby/core/string.rb
Original file line number Diff line number Diff line change
@@ -12,4 +12,10 @@ def %(args)
sprintf(self, *args)
end

def downcase
s = dup
s.downcase!
s
end

end