Permalink
Browse files

Port of the "slow" logic for gets, down to the letter. The fast port …

…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...
1 parent 4f5b85f commit 6c093a953550aec45e2e1a7411ca422210cd6373 @headius headius committed Feb 3, 2008
Showing with 168 additions and 22 deletions.
  1. +145 −20 src/org/jruby/RubyIO.java
  2. +11 −0 src/org/jruby/util/ByteList.java
  3. +12 −2 src/org/jruby/util/io/ChannelStream.java
View
@@ -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) {
@@ -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)
@@ -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);
@@ -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;
+
+ }
}
@@ -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;
}
@@ -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;
}
@@ -739,7 +748,8 @@ public synchronized int read() throws IOException, BadDescriptorException {
}
return bufferedRead();
- } catch (EOFException eof) {
+ } catch (EOFException e) {
+ eof = true;
return -1;
}
}

0 comments on commit 6c093a9

Please sign in to comment.