summaryrefslogtreecommitdiff
path: root/io.c
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-08-11 16:57:14 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-08-11 16:57:14 +0000
commit8f8de7782e93aab8e09c0727140fd1ff02d8ca6f (patch)
tree242a1a265a0ae94447562e3109c1031a741bbf00 /io.c
parent9492165db232bb3bee4c13ac03bf96eb0e76d6ad (diff)
* io.c (read_buffered_data): extracted from rb_io_fread.
(io_readpartial): new method IO#readpartial. [ruby-dev:24055] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@6756 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'io.c')
-rw-r--r--io.c157
1 files changed, 136 insertions, 21 deletions
diff --git a/io.c b/io.c
index 1df7057d30..4bb90f18e8 100644
--- a/io.c
+++ b/io.c
@@ -907,6 +907,27 @@ rb_io_to_io(io)
}
/* reading functions */
+static long
+read_buffered_data(ptr, len, f)
+ char *ptr;
+ long len;
+ FILE *f;
+{
+ long n;
+
+#ifdef READ_DATA_PENDING_COUNT
+ n = READ_DATA_PENDING_COUNT(f);
+ if (n <= 0) return 0;
+ if (n > len) n = len;
+ return fread(ptr, 1, n, f);
+#else
+ for (n = 0; n < len && READ_DATA_PENDING(f); ++n) {
+ *ptr++ = getc(f);
+ }
+ return n;
+#endif
+}
+
long
rb_io_fread(ptr, len, f)
char *ptr;
@@ -917,28 +938,12 @@ rb_io_fread(ptr, len, f)
int c;
while (n > 0) {
-#ifdef READ_DATA_PENDING_COUNT
- long i = READ_DATA_PENDING_COUNT(f);
- if (i <= 0) {
- rb_thread_wait_fd(fileno(f));
- i = READ_DATA_PENDING_COUNT(f);
- }
- if (i > 0) {
- if (i > n) i = n;
- TRAP_BEG;
- c = fread(ptr, 1, i, f);
- TRAP_END;
- if (c < 0) goto eof;
+ c = read_buffered_data(ptr, n, f);
+ if (c < 0) goto eof;
+ if (c > 0) {
ptr += c;
- n -= c;
- if (c < i) goto eof;
- continue;
+ if ((n -= c) <= 0) break;
}
-#else
- if (!READ_DATA_PENDING(f)) {
- rb_thread_wait_fd(fileno(f));
- }
-#endif
TRAP_BEG;
c = getc(f);
TRAP_END;
@@ -1042,6 +1047,113 @@ read_all(fptr, siz, str)
/*
* call-seq:
+ * ios.readpartial(integer[, outbuf]) => string, outbuf
+ *
+ * Reads at most <i>integer</i> bytes from the I/O stream but
+ * it blocks only if <em>ios</em> has no data immediately available.
+ * If the optional <i>outbuf</i> argument is present,
+ * it must reference a String, which will receive the data.
+ * It raises <code>EOFError</code> on end of file.
+ *
+ * readpartial is designed for streams such as pipe, socket, etc.
+ * It blocks only when no data immediately available.
+ * This means that it blocks only when following all conditions hold.
+ * * the stdio buffer in the IO object is empty.
+ * * the content of the stream is empty.
+ * * the stream is not reached to EOF.
+ *
+ * When readpartial blocks, it waits data or EOF on the stream.
+ * If some data is reached, readpartial returns with the data.
+ * If EOF is reached, readpartial raises EOFError.
+ *
+ * When readpartial doesn't blocks, it returns or raises immediately.
+ * If the stdio buffer is not empty, it returns the data in the buffer.
+ * Otherwise if the stream has some content,
+ * it returns the data in the stream.
+ * Otherwise if the stream is reached to EOF, it raises EOFError.
+ *
+ * r, w = IO.pipe # stdio buffer pipe content
+ * w << "abc" # "" "abc".
+ * r.readpartial(4096) #=> "abc" "" ""
+ * r.readpartial(4096) # blocks because buffer and pipe is empty.
+ *
+ * r, w = IO.pipe # stdio buffer pipe content
+ * w << "abc" # "" "abc"
+ * w.close # "" "abc" EOF
+ * r.readpartial(4096) #=> "abc" "" EOF
+ * r.readpartial(4096) # raises EOFError
+ *
+ * r, w = IO.pipe # stdio buffer pipe content
+ * w << "abc\ndef\n" # "" "abc\ndef\n"
+ * r.gets #=> "abc\n" "def\n" ""
+ * w << "ghi\n" # "def\n" "ghi\n"
+ * r.readpartial(4096) #=> "def\n" "" "ghi\n"
+ * r.readpartial(4096) #=> "ghi\n" "" ""
+ *
+ * Note that readpartial is nonblocking-flag insensitive.
+ * It blocks even if the nonblocking-flag is set.
+ *
+ * Also note that readpartial behaves similar to sysread in blocking mode.
+ * The behavior is identical when the stdio buffer is empty.
+ *
+ */
+
+static VALUE
+io_readpartial(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ OpenFile *fptr;
+ VALUE length, str;
+ long n, len;
+
+ rb_scan_args(argc, argv, "11", &length, &str);
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+
+ if ((len = NUM2LONG(length)) < 0) {
+ rb_raise(rb_eArgError, "negative length %ld given", len);
+ }
+
+ if (NIL_P(str)) {
+ str = rb_str_new(0, len);
+ }
+ else {
+ StringValue(str);
+ rb_str_modify(str);
+ rb_str_resize(str, len);
+ }
+ OBJ_TAINT(str);
+
+ if (len == 0)
+ return str;
+
+ READ_CHECK(fptr->f);
+ n = read_buffered_data(RSTRING(str)->ptr, len, fptr->f);
+ if (n <= 0) {
+ again:
+ TRAP_BEG;
+ n = read(fileno(fptr->f), RSTRING(str)->ptr, len);
+ TRAP_END;
+ if (n < 0) {
+ if (rb_io_wait_readable(fileno(fptr->f)))
+ goto again;
+ rb_str_resize(str, 0);
+ rb_sys_fail(fptr->path);
+ }
+ }
+ rb_str_resize(str, n);
+
+ if (n == 0)
+ rb_eof_error();
+ else
+ return str;
+}
+
+/*
+ * call-seq:
* ios.read([integer [, buffer]]) => string, buffer, or nil
*
* Reads at most <i>integer</i> bytes from the I/O stream, or to the
@@ -2072,11 +2184,13 @@ rb_io_syswrite(io, str)
/*
* call-seq:
- * ios.sysread(integer ) => string
+ * ios.sysread(integer[, outbuf]) => string
*
* Reads <i>integer</i> bytes from <em>ios</em> using a low-level
* read and returns them as a string. Do not mix with other methods
* that read from <em>ios</em> or you may get unpredictable results.
+ * If the optional <i>outbuf</i> argument is present, it must reference
+ * a String, which will receive the data.
* Raises <code>SystemCallError</code> on error and
* <code>EOFError</code> at end of file.
*
@@ -5439,6 +5553,7 @@ Init_IO()
rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
+ rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
rb_define_method(rb_cIO, "read", io_read, -1);
rb_define_method(rb_cIO, "write", io_write, 1);
rb_define_method(rb_cIO, "gets", rb_io_gets_m, -1);