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

Commits on Jun 27, 2018

  1. Copy the full SHA
    1e55ac8 View commit details
  2. Copy the full SHA
    a58a0c9 View commit details
  3. Copy the full SHA
    fd5e938 View commit details
  4. [refactor] RubyString's (each) enumerate internals

    use Java array directly when appropriate
    more importantly avoid `newBlankArray` - might have led to an
    invalid array size since the result length isn't really known
    kares committed Jun 27, 2018
    Copy the full SHA
    61cf959 View commit details
  5. [refactor] review String dup-ing on each_xxx with MRI

    ... also revisited/deprecated some unused 19-suffix methods
    kares committed Jun 27, 2018
    Copy the full SHA
    40a760e View commit details
235 changes: 108 additions & 127 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -323,6 +323,10 @@ public final int strLength() {
return StringSupport.strLengthFromRubyString(this);
}

final int strLength(final ByteList bytes, final Encoding enc) {
return StringSupport.strLengthFromRubyString(this, bytes, enc);
}

// MRI: rb_str_sublen
final int subLength(int pos) {
if (pos < 0 || singleByteOptimizable()) return pos;
@@ -797,7 +801,7 @@ public IRubyObject dup() {

// rb_str_new_frozen or rb_str_dup_frozen
public IRubyObject dupFrozen() {
RubyString dup = (RubyString)dup();
RubyString dup = (RubyString) dup();
dup.setFrozen(true);
return dup;
}
@@ -4234,7 +4238,7 @@ private static RubyString getStringForPattern(Ruby runtime, IRubyObject obj) {
*
*/
private static RubyRegexp getPattern(Ruby runtime, IRubyObject obj) {
if (obj instanceof RubyRegexp) return (RubyRegexp)obj;
if (obj instanceof RubyRegexp) return (RubyRegexp) obj;
return RubyRegexp.newRegexpFromStr(runtime, getStringForPattern(runtime, obj), 0);
}

@@ -4246,7 +4250,7 @@ private static IRubyObject getPatternQuoted(ThreadContext context, IRubyObject p

if (!(pat instanceof RubyString)) {
val = pat.checkStringType();
if (val.isNil()) {
if (val == context.nil) {
TypeConverter.checkType(context, pat, context.runtime.getRegexp());
}
pat = val;
@@ -4386,10 +4390,9 @@ public IRubyObject start_with_p(ThreadContext context) {
@JRubyMethod(name = "start_with?")
public IRubyObject start_with_p(ThreadContext context, IRubyObject arg) {
if (arg instanceof RubyRegexp) {
return rindexCommon(context, arg, 0).isNil() ? context.fals : context.tru;
} else {
return start_with_pCommon(arg) ? context.tru : context.fals;
return rindexCommon(context, arg, 0) == context.nil ? context.fals : context.tru;
}
return startWith(arg) ? context.tru : context.fals;
}

@JRubyMethod(name = "start_with?", rest = true)
@@ -4400,17 +4403,14 @@ public IRubyObject start_with_p(ThreadContext context, IRubyObject[]args) {
return context.fals;
}

private boolean start_with_pCommon(IRubyObject arg) {
private boolean startWith(IRubyObject arg) {
RubyString otherString = arg.convertToString();

checkEncoding(otherString);

int otherLength = otherString.value.getRealSize();

if (otherLength == 0) {
// other is '', so return true
return true;
}
if (otherLength == 0) return true; // other is '', so return true

if (value.getRealSize() < otherLength) return false;

@@ -4424,19 +4424,19 @@ public IRubyObject end_with_p(ThreadContext context) {

@JRubyMethod(name = "end_with?")
public IRubyObject end_with_p(ThreadContext context, IRubyObject arg) {
return end_with_pCommon(arg) ? context.tru : context.fals;
return endWith(arg) ? context.tru : context.fals;
}

@JRubyMethod(name = "end_with?", rest = true)
public IRubyObject end_with_p(ThreadContext context, IRubyObject[]args) {
for (int i = 0; i < args.length; i++) {
if (end_with_pCommon(args[i])) return context.tru;
if (endWith(args[i])) return context.tru;
}
return context.fals;
}

// MRI: rb_str_end_with, loop body
private boolean end_with_pCommon(IRubyObject tmp) {
private boolean endWith(IRubyObject tmp) {
int p, s, e;
Encoding enc;

@@ -5503,6 +5503,7 @@ public IRubyObject each_line(ThreadContext context, IRubyObject arg, IRubyObject
return StringSupport.rbStrEnumerateLines(this, context, "each_line", arg, opts, block, false);
}

@Deprecated // no longer used
public IRubyObject each_lineCommon(ThreadContext context, IRubyObject sep, Block block) {
if (sep == context.nil) {
block.yield(context, this);
@@ -5574,19 +5575,14 @@ public IRubyObject lines(ThreadContext context, IRubyObject arg, Block block) {
/**
* rb_str_each_byte
*/
public RubyString each_byte(ThreadContext context, Block block) {
Ruby runtime = context.runtime;
// Check the length every iteration, since
// the block can modify this string.
for (int i = 0; i < value.length(); i++) {
block.yield(context, runtime.newFixnum(value.get(i) & 0xFF));
}
return this;
@JRubyMethod(name = "each_byte")
public IRubyObject each_byte(ThreadContext context, Block block) {
return enumerateBytes(context, "each_byte", block, false);
}

@JRubyMethod(name = "each_byte")
@Deprecated
public IRubyObject each_byte19(ThreadContext context, Block block) {
return enumerateBytes(context, "each_byte", block, false);
return each_byte(context, block);
}

@JRubyMethod
@@ -5595,15 +5591,25 @@ public IRubyObject bytes(ThreadContext context, Block block) {
}

@JRubyMethod(name = "each_char")
public IRubyObject each_char19(ThreadContext context, Block block) {
public IRubyObject each_char(ThreadContext context, Block block) {
return enumerateChars(context, "each_char", block, false);
}

@JRubyMethod(name = "chars")
public IRubyObject chars19(ThreadContext context, Block block) {
public IRubyObject chars(ThreadContext context, Block block) {
return enumerateChars(context, "chars", block, true);
}

@Deprecated
public IRubyObject each_char19(ThreadContext context, Block block) {
return each_char(context, block);
}

@Deprecated
public IRubyObject chars19(ThreadContext context, Block block) {
return chars(context, block);
}

private SizeFn eachCharSizeFn() {
final RubyString self = this;
return new SizeFn() {
@@ -5631,142 +5637,116 @@ public IRubyObject codepoints(ThreadContext context, Block block) {
private IRubyObject enumerateChars(ThreadContext context, String name, Block block, boolean wantarray) {
Ruby runtime = context.runtime;
RubyString str = this;
IRubyObject orig = str;
IRubyObject substr;
int i, len, n;
int len, n;
byte[] ptrBytes;
int ptr;
Encoding enc;
RubyArray ary = null;

str = strDup(runtime);
ByteList strByteList = str.getByteList();
ptrBytes = strByteList.unsafeBytes();
ptr = strByteList.begin();
len = strByteList.getRealSize();
enc = str.getEncoding();

if (block.isGiven()) {
if (wantarray) {
runtime.getWarnings().warning("passing a block to String#" + name + " is deprecated");
wantarray = false;
}
}
else {
if (wantarray)
ary = RubyArray.newArray(runtime, str.strLength());
else
return enumeratorizeWithSize(context, this, name, eachCharSizeFn());
else if (!wantarray) {
return enumeratorizeWithSize(context, str, name, eachCharSizeFn());
}

str = str.newFrozen();
ByteList strByteList = str.value;
ptrBytes = strByteList.unsafeBytes();
ptr = strByteList.begin();
len = strByteList.getRealSize();
enc = str.getEncoding();

IRubyObject[] ary = wantarray ? new IRubyObject[str.strLength()] : null; int a = 0;

switch (getCodeRange()) {
case CR_VALID:
case CR_7BIT:
for (i = 0; i < len; i += n) {
for (int i = 0; i < len; i += n) {
n = StringSupport.encFastMBCLen(ptrBytes, ptr + i, ptr + len, enc);
substr = str.substr(runtime, i, n);
if (wantarray)
ary.push(substr);
else
block.yield(context, substr);
IRubyObject substr = str.substr(runtime, i, n);
if (wantarray) ary[a++] = substr;
else block.yield(context, substr);
}
break;
default:
for (i = 0; i < len; i += n) {
for (int i = 0; i < len; i += n) {
n = StringSupport.length(enc, ptrBytes, ptr + i, ptr + len);
substr = str.substr(runtime, i, n);
if (wantarray)
ary.push(substr);
else
block.yield(context, substr);
IRubyObject substr = str.substr(runtime, i, n);
if (wantarray) ary[a++] = substr;
else block.yield(context, substr);
}
}
if (wantarray)
return ary;
else
return orig;

assert !wantarray || a == ary.length;

return wantarray ? RubyArray.newArrayNoCopy(runtime, ary) : this;
}

// MRI: rb_str_enumerate_codepoints
private IRubyObject enumerateCodepoints(ThreadContext context, String name, Block block, boolean wantarray) {
Ruby runtime = context.runtime;
RubyString str = this;
IRubyObject orig = str;
int n;
int c;
byte[] ptrBytes;
int ptr, end;
Encoding enc;
RubyArray ary = null;

if (singleByteOptimizable())
return enumerateBytes(context, name, block, wantarray);

str = RubyString.newString(runtime, str.getByteList().dup());
ByteList strByteList = str.getByteList();
ptrBytes = strByteList.unsafeBytes();
ptr = strByteList.begin();
end = ptr + strByteList.getRealSize();
enc = EncodingUtils.STR_ENC_GET(str);
if (singleByteOptimizable()) return enumerateBytes(context, name, block, wantarray);

if (block.isGiven()) {
if (wantarray) {
runtime.getWarnings().warning("passing a block to String#" + name + " is deprecated");
wantarray = false;
}
}
else {
if (wantarray)
ary = RubyArray.newArray(runtime, str.length());
else
return enumeratorizeWithSize(context, str, name, eachCodepointSizeFn());
else if (!wantarray) {
return enumeratorizeWithSize(context, str, name, eachCodepointSizeFn());
}

if (!str.isFrozen()) str.setByteListShared();
ByteList strByteList = str.value;
ptrBytes = strByteList.unsafeBytes();
ptr = strByteList.begin();
end = ptr + strByteList.getRealSize();
enc = EncodingUtils.getEncoding(strByteList);

RubyArray ary = wantarray ? RubyArray.newArray(runtime, str.strLength(strByteList, enc)) : null;

while (ptr < end) {
c = codePoint(runtime, enc, ptrBytes, ptr, end);
n = codeLength(enc, c);
if (wantarray)
ary.push(RubyFixnum.newFixnum(runtime, c));
else
block.yield(context, RubyFixnum.newFixnum(runtime, c));
int c = codePoint(runtime, enc, ptrBytes, ptr, end);
int n = codeLength(enc, c);
if (wantarray) ary.append(RubyFixnum.newFixnum(runtime, c));
else block.yield(context, RubyFixnum.newFixnum(runtime, c));
ptr += n;
}
if (wantarray)
return ary;
else
return orig;

return wantarray ? ary : this;
}

private IRubyObject enumerateBytes(ThreadContext context, String name, Block block, boolean wantarray) {
Ruby runtime = context.runtime;
RubyString str = this;
int i;
RubyArray ary = null;

if (block.isGiven()) {
if (wantarray) {
runtime.getWarnings().warning("passing a block to String#" + name + " is deprecated");
wantarray = false;
}
}
else {
if (wantarray)
ary = RubyArray.newBlankArray(runtime, str.size());
else
return enumeratorizeWithSize(context, str, name, eachByteSizeFn());
else if (!wantarray) {
return enumeratorizeWithSize(context, this, name, eachByteSizeFn());
}

for (i=0; i < str.size(); i++) {
RubyFixnum bite = RubyFixnum.newFixnum(runtime, str.getByteList().get(i) & 0xff);
if (wantarray)
ary.store(i, bite);
else
block.yield(context, bite);
IRubyObject[] ary = wantarray ? new IRubyObject[value.getRealSize()] : null;
// Check the length every iteration, since the block can modify this string.
for (int i=0; i < value.getRealSize(); i++) {
RubyFixnum bite = RubyFixnum.newFixnum(runtime, value.get(i) & 0xFF);
if (wantarray) ary[i] = bite;
else block.yield(context, bite);
}
if (wantarray)
return ary;
else
return str;

return wantarray ? RubyArray.newArrayNoCopy(runtime, ary) : this;
}

private SizeFn eachCodepointSizeFn() {
@@ -5778,7 +5758,7 @@ public IRubyObject size(IRubyObject[] args) {
};
}

private static ByteList GRAPHEME_CLUSTER_PATTERN = new ByteList(new byte[] {(byte)'\\', (byte)'X'});
private static final ByteList GRAPHEME_CLUSTER_PATTERN = new ByteList(new byte[] {(byte)'\\', (byte)'X'}, false);

private SizeFn eachGraphemeClusterSizeFn() {
return new SizeFn() {
@@ -5807,43 +5787,44 @@ public IRubyObject size(IRubyObject[] args) {
}

private IRubyObject enumerateGraphemeClusters(ThreadContext context, String name, Block block, boolean wantarray) {
Ruby runtime = context.runtime;
RubyString str = this;
RubyArray ary = null;
Ruby runtime = context.getRuntime();
Encoding enc = value.getEncoding();
if (!enc.isUnicode() || isSingleByteOptimizable(str, enc)) return enumerateChars(context, name, block, wantarray);
Encoding enc = str.getEncoding();
if (!enc.isUnicode() || isSingleByteOptimizable(str, enc)) {
return enumerateChars(context, name, block, wantarray);
}

if (block.isGiven()) {
if (wantarray) {
runtime.getWarnings().warning("passing a block to String#" + name + " is deprecated");
wantarray = false;
}
} else {
if (wantarray)
ary = RubyArray.newBlankArray(runtime, str.size());
else
return enumeratorizeWithSize(context, str, name, eachGraphemeClusterSizeFn());
}
else if (!wantarray) {
return enumeratorizeWithSize(context, str, name, eachGraphemeClusterSizeFn());
}

Regex reg = RubyRegexp.getRegexpFromCache(runtime, GRAPHEME_CLUSTER_PATTERN, enc, RegexpOptions.NULL_OPTIONS);

int beg = value.getBegin();
int end = beg + value.getRealSize();
byte[] bytes = value.getUnsafeBytes();
Matcher matcher = reg.matcher(bytes, beg, end);
if (!wantarray) str = str.newFrozen();
ByteList strByteList = str.value;
byte[] ptrBytes = strByteList.unsafeBytes();
int ptr = strByteList.begin();
int end = ptr + strByteList.getRealSize();
Matcher matcher = reg.matcher(ptrBytes, ptr, end);

while (beg < end) {
int len = matcher.match(beg, end, Option.DEFAULT);
RubyArray ary = wantarray ? RubyArray.newArray(runtime, end - ptr) : null;

while (ptr < end) {
int len = matcher.match(ptr, end, Option.DEFAULT);
if (len <= 0) break;
RubyString result = newStringShared(runtime, bytes, beg, len, enc);
if (wantarray)
ary.push(result);
else
block.yield(context, result);
beg += len;
RubyString result = newStringShared(runtime, ptrBytes, ptr, len, enc);
if (wantarray) ary.append(result);
else block.yield(context, result);
ptr += len;
}

return wantarray ? ary : str;
return wantarray ? ary : this;
}

@JRubyMethod
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import java.util.Map;

import static org.jruby.runtime.Visibility.PUBLIC;
import static org.jruby.util.StringSupport.startsWith;

/**
* Created by headius on 2/26/15.
@@ -201,12 +202,9 @@ static void assignAliases(final MethodInstaller installer,
// Add scala aliases for apply/update to roughly equivalent Ruby names
if (name.equals("apply")) {
addUnassignedAlias("[]", assignedNames, installer, Priority.ALIAS);
}
if (name.equals("update") && argCount == 2) {
} else if (argCount == 2 && name.equals("update")) {
addUnassignedAlias("[]=", assignedNames, installer, Priority.ALIAS);
}
// Scala aliases for $ method names
if (name.startsWith("$")) {
} else if (startsWith(name, '$')) { // Scala aliases for $ method names
addUnassignedAlias(ClassInitializer.fixScalaNames(name), assignedNames, installer, Priority.ALIAS);
}

4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/util/JarResource.java
Original file line number Diff line number Diff line change
@@ -31,12 +31,12 @@ else if (pathname.startsWith("file:")) {
String jarPath = pathname.substring(0, bang);
String entryPath = pathname.substring(bang + 1);
// normalize path -- issue #2017
if (entryPath.startsWith("//")) entryPath = entryPath.substring(1);
if (StringSupport.startsWith(entryPath, '/', '/')) entryPath = entryPath.substring(1);

// TODO: Do we really need to support both test.jar!foo/bar.rb and test.jar!/foo/bar.rb cases?
JarResource resource = createJarResource(jarPath, entryPath, false);

if (resource == null && entryPath.startsWith("/")) {
if (resource == null && StringSupport.startsWith(entryPath, '/')) {
resource = createJarResource(jarPath, entryPath.substring(1), true);
}

343 changes: 163 additions & 180 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -861,7 +861,6 @@ public static ByteList dumpCommon(Ruby runtime, ByteList bytelist) {
}

public static ByteList dumpCommon(Ruby runtime, ByteList byteList, boolean quoteOnlyIfNeeded) {
ByteList buf = null;
Encoding enc = byteList.getEncoding();
boolean includingsNonprintable = false;

@@ -920,58 +919,53 @@ public static ByteList dumpCommon(Ruby runtime, ByteList byteList, boolean quote
if ((quoteOnlyIfNeeded && includingsNonprintable) || !quoteOnlyIfNeeded) out[q++] = '"';
while (p < end) {
int c = bytes[p++] & 0xff;
if (c == '"' || c == '\\') {
out[q++] = '\\';
out[q++] = (byte)c;
} else if (c == '#') {
if (isEVStr(bytes, p, end)) out[q++] = '\\';
out[q++] = '#';
} else if (c == '\n') {
out[q++] = '\\';
out[q++] = 'n';
} else if (c == '\r') {
out[q++] = '\\';
out[q++] = 'r';
} else if (c == '\t') {
out[q++] = '\\';
out[q++] = 't';
} else if (c == '\f') {
out[q++] = '\\';
out[q++] = 'f';
} else if (c == '\013') {
out[q++] = '\\';
out[q++] = 'v';
} else if (c == '\010') {
out[q++] = '\\';
out[q++] = 'b';
} else if (c == '\007') {
out[q++] = '\\';
out[q++] = 'a';
} else if (c == '\033') {
out[q++] = '\\';
out[q++] = 'e';
} else if (ASCIIEncoding.INSTANCE.isPrint(c)) {
out[q++] = (byte)c;
} else {
out[q++] = '\\';
outBytes.setRealSize(q);
if (enc.isUTF8()) {
int n = preciseLength(enc, bytes, p - 1, end) - 1;
if (MBCLEN_CHARFOUND_LEN(n) > 0) {
int cc = codePoint(runtime, enc, bytes, p - 1, end);
switch (c) {
case '"': case '\\':
out[q++] = '\\'; out[q++] = (byte)c; break;
case '#':
if (isEVStr(bytes, p, end)) out[q++] = '\\';
out[q++] = '#';
break;
case '\n':
out[q++] = '\\'; out[q++] = 'n'; break;
case '\r':
out[q++] = '\\'; out[q++] = 'r'; break;
case '\t':
out[q++] = '\\'; out[q++] = 't'; break;
case '\f':
out[q++] = '\\'; out[q++] = 'f'; break;
case '\013':
out[q++] = '\\'; out[q++] = 'v'; break;
case '\010':
out[q++] = '\\'; out[q++] = 'b'; break;
case '\007':
out[q++] = '\\'; out[q++] = 'a'; break;
case '\033':
out[q++] = '\\'; out[q++] = 'e'; break;
default:
if (ASCIIEncoding.INSTANCE.isPrint(c)) {
out[q++] = (byte)c;
} else {
out[q++] = '\\';
outBytes.setRealSize(q);
p += n;
if (cc <= 0xFFFF) {
Sprintf.sprintf(runtime, outBytes, "u%04X", cc);
} else {
Sprintf.sprintf(runtime, outBytes, "u{%X}", cc);
if (enc.isUTF8()) {
int n = preciseLength(enc, bytes, p - 1, end) - 1;
if (MBCLEN_CHARFOUND_LEN(n) > 0) {
int cc = codePoint(runtime, enc, bytes, p - 1, end);
outBytes.setRealSize(q);
p += n;
if (cc <= 0xFFFF) {
Sprintf.sprintf(runtime, outBytes, "u%04X", cc);
} else {
Sprintf.sprintf(runtime, outBytes, "u{%X}", cc);
}
q = outBytes.getRealSize();
continue;
}
}
Sprintf.sprintf(runtime, outBytes, "x%02X", c);
q = outBytes.getRealSize();
continue;
}
}
Sprintf.sprintf(runtime, outBytes, "x%02X", c);
q = outBytes.getRealSize();
}
}
if ((quoteOnlyIfNeeded && includingsNonprintable) || !quoteOnlyIfNeeded) out[q++] = '"';
@@ -1089,6 +1083,15 @@ public static int strLengthFromRubyString(CodeRangeable string) {
return strLengthFromRubyStringFull(string, bytes, bytes.getEncoding());
}

public static int strLengthFromRubyString(CodeRangeable string, final ByteList bytes, final Encoding enc) {
if (isSingleByteOptimizable(string, enc)) return bytes.getRealSize();
// NOTE: strLengthFromRubyStringFull but without string.setCodeRange(..)
if (string.isCodeRangeValid() && enc.isUTF8()) return utf8Length(bytes);

long lencr = strLengthWithCodeRange(bytes, enc);
return unpackResult(lencr);
}

private static int strLengthFromRubyStringFull(CodeRangeable string, ByteList bytes, Encoding enc) {
if (string.isCodeRangeValid() && enc.isUTF8()) return utf8Length(bytes);

@@ -1777,31 +1780,24 @@ public static boolean areComparableViaCodeRange(CodeRangeable string, CodeRangea

public static IRubyObject rbStrEnumerateLines(RubyString str, ThreadContext context, String name, IRubyObject arg, Block block, boolean wantarray) {
IRubyObject opts = ArgsUtil.getOptionsArg(context.runtime, arg);
if (opts.isNil()) {
if (opts == context.nil) {
return rbStrEnumerateLines(str, context, name, arg, context.nil, block, wantarray);
} else {
return rbStrEnumerateLines(str, context, name, context.runtime.getGlobalVariables().get("$/"), opts, block, wantarray);
}
return rbStrEnumerateLines(str, context, name, context.runtime.getGlobalVariables().get("$/"), opts, block, wantarray);
}

private static int NULL_POINTER = -1;

public static IRubyObject rbStrEnumerateLines(RubyString str, ThreadContext context, String name, IRubyObject arg, IRubyObject opts, Block block, boolean wantarray) {
Ruby runtime = context.runtime;

Encoding enc;
IRubyObject line, rs, orig = str;
int ptr, pend, subptr, subend, rsptr, hit, adjusted;
int pos, len, rslen;
boolean paragraph_mode = false;
Encoding enc; IRubyObject line, orig = str;
int ptr, pend, subptr, subend, hit, adjusted;

boolean rsnewline = false;
boolean chomp = false;

IRubyObject ary = null;

rs = arg;

if (!opts.isNil()) {
if (opts != context.nil) {
IRubyObject _chomp = ArgsUtil.extractKeywordArg(context, "chomp", opts);
chomp = _chomp != null || _chomp.isTrue();
}
@@ -1811,155 +1807,142 @@ public static IRubyObject rbStrEnumerateLines(RubyString str, ThreadContext cont
// this code should be live in 3.0
if (false) { // #if STRING_ENUMERATORS_WANTARRAY
runtime.getWarnings().warn("given block not used");
ary = runtime.newEmptyArray();
} else {
runtime.getWarnings().warning("passing a block to String#lines is deprecated");
wantarray = false;
}
}
}
else {
if (wantarray) {
ary = runtime.newEmptyArray();
} else {
return enumeratorize(runtime, str, name, Helpers.arrayOf(arg, opts));
}
else if (!wantarray) {
return enumeratorize(runtime, str, name, Helpers.arrayOf(arg, opts));
}

if (rs.isNil()) {
if (wantarray) {
((RubyArray)ary).push(str);
return ary;
}
if (arg == context.nil) { // rs
if (wantarray) return RubyArray.newArray(runtime, str);
else {
block.yieldSpecific(context, str);
return orig;
}
}

// simulate jump to end
end: do {
str = str.newFrozen();
byte[] strBytes = str.getByteList().unsafeBytes();
ptr = subptr = str.getByteList().begin();
pend = ptr + str.size();
len = str.size();
rs = rs.convertToString();
rslen = ((RubyString) rs).size();
final RubyArray ary = wantarray ? RubyArray.newArray(runtime) : null;

IRubyObject defaultSep = context.runtime.getGlobalVariables().get("$/");
if (rs == defaultSep)
enc = str.getEncoding();
else
enc = str.checkEncoding((RubyString) rs);

byte[] rsbytes;
if (rslen == 0) {
/* paragraph mode */
int[] n = {0};
int eol = NULL_POINTER;
subend = subptr;
while (subend < pend) {
do {
if (EncodingUtils.encAscget(strBytes, subend, pend, n, enc) != '\r') {
n[0] = 0;
}
rslen = n[0] + length(enc, strBytes, subend + n[0], pend);
if (enc.isNewLine(strBytes, subend + n[0], pend)) {
if (eol == subend) break;
subend += rslen;
if (subptr != NULL_POINTER) eol = subend;
}
else {
if (subptr == NULL_POINTER) subptr = subend;
subend += rslen;
}
rslen = 0;
} while (subend < pend);
if (subptr == NULL_POINTER) break;
line = str.makeSharedString(runtime, subptr - ptr,
subend - subptr + (chomp ? 0 : rslen));
if (wantarray) {
((RubyArray) ary).push(line);
} else {
block.yield(context, line);
str.modifyCheck(strBytes, len);
}
subptr = eol = NULL_POINTER;
}
break end;
} else {
ByteList rsByteList = ((RubyString) rs).getByteList();
rsbytes = rsByteList.unsafeBytes();
rsptr = rsByteList.begin();
if (rsByteList.length() == enc.minLength() &&
enc.isNewLine(rsbytes, rsptr, rsByteList.length())) {
rsnewline = true;
}
}
final IRubyObject defaultSep = runtime.getGlobalVariables().get("$/");
RubyString rs = arg.convertToString();

if (rs == defaultSep && !enc.isAsciiCompatible()) {
rs = RubyString.newString(runtime, rsbytes, rsptr, rslen);
rs = EncodingUtils.rbStrEncode(context, rs, runtime.getEncodingService().convertEncodingToRubyEncoding(enc), 0, context.nil);
ByteList rsByteList = ((RubyString) rs).getByteList();
rsbytes = rsByteList.unsafeBytes();
rsptr = rsByteList.begin();
rslen = rsByteList.realSize();
}
str = str.newFrozen();
byte[] strBytes = str.getByteList().unsafeBytes();
ptr = subptr = str.getByteList().begin();
final int len = str.size();
pend = ptr + len;
int rslen = rs.size();

while (subptr < pend) {
pos = memsearch(rsbytes, rsptr, rslen, strBytes, subptr, pend - subptr, enc);
if (pos < 0) break;
hit = subptr + pos;
adjusted = enc.rightAdjustCharHead(strBytes, subptr, hit, pend);
if (hit != adjusted) {
subptr = adjusted;
continue;
}
subend = hit += rslen;
if (chomp) {
if (rsnewline) {
subend = chomp_newline(strBytes, subptr, subend, enc);
} else {
subend -= rslen;
}
}
line = str.substr(runtime, subptr - ptr, subend - subptr);
if (wantarray) {
((RubyArray) ary).push(line);
enc = (rs == defaultSep) ? str.getEncoding() : str.checkEncoding(rs);

if (rslen == 0) {
rbStrEnumerateLinesEmptySep(str, context, chomp, block, ary, strBytes, ptr, len, pend, enc, subptr);
return wantarray ? ary : orig; // end
}

ByteList rsByteList = rs.getByteList();
byte[] rsbytes = rsByteList.unsafeBytes();
int rsptr = rsByteList.begin();
if (rsByteList.length() == enc.minLength() && enc.isNewLine(rsbytes, rsptr, rsByteList.length())) {
rsnewline = true;
}

if (rs == defaultSep && !enc.isAsciiCompatible()) {
rs = RubyString.newString(runtime, rsbytes, rsptr, rslen);
rs = (RubyString) EncodingUtils.rbStrEncode(context, rs, runtime.getEncodingService().convertEncodingToRubyEncoding(enc), 0, context.nil);
rsByteList = rs.getByteList();
rsbytes = rsByteList.unsafeBytes();
rsptr = rsByteList.begin();
rslen = rsByteList.realSize();
}

while (subptr < pend) {
int pos = memsearch(rsbytes, rsptr, rslen, strBytes, subptr, pend - subptr, enc);
if (pos < 0) break;
hit = subptr + pos;
adjusted = enc.rightAdjustCharHead(strBytes, subptr, hit, pend);
if (hit != adjusted) {
subptr = adjusted;
continue;
}
subend = hit += rslen;
if (chomp) {
if (rsnewline) {
subend = chomp_newline(strBytes, subptr, subend, enc);
} else {
block.yieldSpecific(context, line);
str.modifyCheck(strBytes, len);
subend -= rslen;
}
subptr = hit;
}
line = str.substr(runtime, subptr - ptr, subend - subptr);
if (wantarray) ary.append(line);
else {
block.yieldSpecific(context, line);
str.modifyCheck(strBytes, len);
}
subptr = hit;
}

if (subptr != pend) {
if (chomp) {
pend = chomp_newline(strBytes, subptr, pend, enc);
} else if (pend - subptr >= rslen &&
ByteList.memcmp(strBytes, pend - rslen, rsbytes, rsptr, rslen) == 0) {
pend -= rslen;
}
line = str.substr(runtime, subptr - ptr, pend - subptr);
if (wantarray) {
((RubyArray) ary).push(line);
} else {
block.yieldSpecific(context, line);
}
if (subptr != pend) {
if (chomp) {
pend = chomp_newline(strBytes, subptr, pend, enc);
} else if (pend - subptr >= rslen &&
ByteList.memcmp(strBytes, pend - rslen, rsbytes, rsptr, rslen) == 0) {
pend -= rslen;
}
} while (false); // label "end"
line = str.substr(runtime, subptr - ptr, pend - subptr);
if (wantarray) ary.append(line);
else block.yieldSpecific(context, line);
}

return wantarray ? ary : orig;
}

private static void rbStrEnumerateLinesEmptySep(RubyString str,
ThreadContext context, final boolean chomp, Block block, RubyArray ary,
final byte[] strBytes, final int ptr, final int len,
final int pend, final Encoding enc, int subptr) {
/* paragraph mode */
int[] n = {0};
int eol = NULL_POINTER;
int subend = subptr;
while (subend < pend) {
int rslen;
do {
if (EncodingUtils.encAscget(strBytes, subend, pend, n, enc) != '\r') n[0] = 0;
rslen = n[0] + length(enc, strBytes, subend + n[0], pend);
if (enc.isNewLine(strBytes, subend + n[0], pend)) {
if (eol == subend) break;
subend += rslen;
if (subptr != NULL_POINTER) eol = subend;
}
else {
if (subptr == NULL_POINTER) subptr = subend;
subend += rslen;
}
rslen = 0;
} while (subend < pend);
if (subptr == NULL_POINTER) break;
RubyString line = str.makeSharedString(context.runtime, subptr - ptr,
subend - subptr + (chomp ? 0 : rslen));
if (ary != null) ary.append(line); // wantarray
else {
block.yield(context, line);
str.modifyCheck(strBytes, len);
}
subptr = eol = NULL_POINTER;
}
}

private static int chomp_newline(byte[] bytes, int p, int e, Encoding enc) {
int prev = enc.prevCharHead(bytes, p, e, e);
if (enc.isNewLine(bytes, prev, e)) {
e = prev;
prev = enc.prevCharHead(bytes, p, e, e);
if (prev != -1 && EncodingUtils.encAscget(bytes, prev, e, null, enc) == '\r')
e = prev;
if (prev != -1 && EncodingUtils.encAscget(bytes, prev, e, null, enc) == '\r') e = prev;
}
return e;
}