Skip to content

Commit

Permalink
Port of the "slow" logic for gets, down to the letter. The fast port …
Browse files Browse the repository at this point in the history
…may come later, but I'm going for correctness right now.

git-svn-id: http://svn.codehaus.org/jruby/branches/headius_io@5805 961051c9-f516-0410-bf72-c9f7e237a7b7
  • Loading branch information
headius committed Feb 3, 2008
1 parent 4f5b85f commit 6c093a9
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 22 deletions.
165 changes: 145 additions & 20 deletions src/org/jruby/RubyIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -581,31 +581,89 @@ private ByteList getSeparatorForGets(IRubyObject[] args) {
return separator;
}

/** Read a line.
*
*/
// TODO: Most things loop over this and always pass it the same arguments
// meaning they are an invariant of the loop. Think about fixing this.
public IRubyObject getline(IRubyObject[] args) {
return getline(getSeparatorForGets(args));
}

public IRubyObject getline(ByteList separator) {
try {
openFile.checkReadable(getRuntime());

ByteList newLine = openFile.getMainStream().fgets(separator);
boolean isParagraph = separator == Stream.PARAGRAPH_DELIMETER;

if (isParagraph) {
swallow('\n');
}

if (separator == null) {
IRubyObject str = readAll(null);
if (((RubyString)str).getByteList().length() == 0) {
return getRuntime().getNil();
}
return str;
} else if (separator.length() == 1) {
return getlineFast(separator.get(0));
} else {
Stream readStream = openFile.getMainStream();
int c = -1;
int newline = separator.get(separator.length() - 1);

if (newLine != null) {
openFile.setLineNumber(openFile.getLineNumber() + 1);
getRuntime().getGlobalVariables().set("$.", getRuntime().newFixnum(openFile.getLineNumber()));
RubyString result = RubyString.newString(getRuntime(), newLine);
result.taint();
ByteList buf = new ByteList(1024);
boolean update = false;

while (true) {
do {
readCheck(readStream);
readStream.clearerr();

try {
c = readStream.fgetc();
} catch (EOFException e) {
c = -1;
}

if (c == -1) {
// TODO: clear error, wait for it to become readable
if (c == -1) {
if (update) {
return RubyString.newString(getRuntime(), buf);
}
break;
}
}

buf.append(c);

update = true;
} while (c != newline); // loop until we see the nth separator char

// if we hit EOF, we're done
if (c == -1) {
break;
}

// if we've found the last char of the separator,
// and we've found at least as many characters as separator length,
// and the last n characters of our buffer match the separator, we're done
if (c == newline && buf.length() >= separator.length() &&
0 == ByteList.memcmp(buf.unsafeBytes(), buf.begin + buf.realSize - separator.length(), separator.unsafeBytes(), separator.begin, separator.realSize)) {
break;
}
}

if (isParagraph) {
if (c != -1) {
swallow('\n');
}
}

if (!update) {
return getRuntime().getNil();
} else {
openFile.setLineNumber(openFile.getLineNumber() + 1);
// this is for a range check, near as I can tell
RubyNumeric.int2fix(getRuntime(), openFile.getLineNumber());
RubyString str = RubyString.newString(getRuntime(), buf);
str.setTaint(true);

return result;
return str;
}
}

return getRuntime().getNil();
} catch (PipeException ex) {
throw getRuntime().newErrnoEPIPEError();
} catch (InvalidValueException ex) {
Expand All @@ -618,6 +676,71 @@ public IRubyObject getline(ByteList separator) {
throw getRuntime().newIOError(e.getMessage());
}
}

protected boolean swallow(int term) throws IOException, BadDescriptorException {
Stream readStream = openFile.getMainStream();
int c;

do {
readCheck(readStream);

try {
c = readStream.fgetc();
} catch (EOFException e) {
c = -1;
}

if (c != term) {
return true;
}
} while (c != -1);

return false;
}

public IRubyObject getlineFast(int delim) throws IOException, BadDescriptorException {
Stream readStream = openFile.getMainStream();
int c = -1;

ByteList buf = new ByteList(1024);
boolean update = false;
do {
readCheck(readStream);
readStream.clearerr();

try {
c = readStream.fgetc();
} catch (EOFException e) {
c = -1;
}

if (c == -1) {
// TODO: clear error, wait for it to become readable
if (c == -1) {
if (update) {
return RubyString.newString(getRuntime(), buf);
}
break;
}
}

buf.append(c);

update = true;
} while (c != delim);

if (!update) {
return getRuntime().getNil();
} else {
openFile.setLineNumber(openFile.getLineNumber() + 1);
// this is for a range check, near as I can tell
RubyNumeric.int2fix(getRuntime(), openFile.getLineNumber());
RubyString str = RubyString.newString(getRuntime(), buf);
str.setTaint(true);

return str;
}
}
// IO class methods.

@JRubyMethod(name = {"new"}, rest = true, frame = true, meta = true)
Expand Down Expand Up @@ -1456,7 +1579,9 @@ public RubyIO flush() {
*/
@JRubyMethod(name = "gets", optional = 1)
public IRubyObject gets(IRubyObject[] args) {
IRubyObject result = getline(args);
ByteList separator = getSeparatorForGets(args);

IRubyObject result = getline(separator);

if (!result.isNil()) getRuntime().getCurrentContext().getCurrentFrame().setLastLine(result);

Expand Down
11 changes: 11 additions & 0 deletions src/org/jruby/util/ByteList.java
Original file line number Diff line number Diff line change
Expand Up @@ -730,4 +730,15 @@ public static int memcmp(final byte[] first, final int firstStart, final int fir
return firstLen == secondLen ? 0 : firstLen == len ? -1 : 1;

}

public static int memcmp(final byte[] first, final int firstStart, final byte[] second, final int secondStart, final int len) {
if (first == second) return 0;
int offset = -1;
for ( ; ++offset < len && first[firstStart + offset] == second[secondStart + offset]; ) ;
if (offset < len) {
return (first[firstStart + offset]&0xFF) > (second[secondStart + offset]&0xFF) ? 1 : -1;
}
return 0;

}
}
14 changes: 12 additions & 2 deletions src/org/jruby/util/io/ChannelStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,10 @@ private int bufferedRead() throws IOException, BadDescriptorException {
int read = descriptor.read(buffer);
buffer.flip();

if (read <= 0) return -1;
if (read <= 0) {
eof = true;
return -1;
}
}
return buffer.get() & 0xFF;
}
Expand Down Expand Up @@ -675,13 +678,19 @@ public void ungetc(int c) {
}

public synchronized int fgetc() throws IOException, BadDescriptorException {
if (eof) {
return -1;
}

checkReadable();

int c = read();

if (c == -1) {
eof = true;
return c;
}

return c & 0xff;
}

Expand Down Expand Up @@ -739,7 +748,8 @@ public synchronized int read() throws IOException, BadDescriptorException {
}

return bufferedRead();
} catch (EOFException eof) {
} catch (EOFException e) {
eof = true;
return -1;
}
}
Expand Down

0 comments on commit 6c093a9

Please sign in to comment.