Feature #4532 » 0001-Add-IO-pread-and-IO-pwrite-methods.patch
| configure.in | ||
|---|---|---|
| AC_CHECK_FUNCS(posix_memalign) | ||
| AC_CHECK_FUNCS(ppoll) | ||
| AC_CHECK_FUNCS(pread) | ||
| AC_CHECK_FUNCS(pwrite) | ||
| AC_CHECK_FUNCS(qsort_r) | ||
| AC_CHECK_FUNCS(qsort_s) | ||
| AC_CHECK_FUNCS(readlink) | ||
| io.c | ||
|---|---|---|
|     return str; | ||
| } | ||
| struct prdwr_internal_arg { | ||
|     int fd; | ||
|     void *buf; | ||
|     size_t count; | ||
|     off_t offset; | ||
|     ssize_t len; | ||
| }; | ||
| static VALUE | ||
| internal_pread_func(void *arg) | ||
| { | ||
|     struct prdwr_internal_arg *p = arg; | ||
|     return (VALUE)pread(p->fd, p->buf, p->count, p->offset); | ||
| } | ||
| static VALUE | ||
| pread_internal_call(VALUE arg) | ||
| { | ||
|     struct prdwr_internal_arg *p = (struct prdwr_internal_arg *)arg; | ||
|     p->len = (ssize_t)rb_thread_io_blocking_region(internal_pread_func, p, p->fd); | ||
|     return Qundef; | ||
| } | ||
| /* | ||
|  *  call-seq: | ||
|  *     ios.pread(maxlen, offset[, outbuf])    -> string | ||
|  * | ||
|  *  Reads <i>maxlen</i> bytes from <em>ios</em> using the pread system call | ||
|  *  and returns them as a string without modifying the underlying | ||
|  *  descriptor offset.  This is advantageous compared to combining IO#seek | ||
|  *  and IO#read in that it is atomic, allowing multiple threads/process to | ||
|  *  share the same IO object for reading the file at various locations. | ||
|  *  This bypasses any userspace buffering of the IO layer. | ||
|  *  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, <code>EOFError</code> | ||
|  *  at end of file and <code>NotImplementedError</code> if platform does not | ||
|  *  implement the system call. | ||
|  * | ||
|  *     f = File.new("testfile") | ||
|  *     f.read           #=> "This is line one\nThis is line two\n" | ||
|  *     f.pread(12, 0)   #=> "This is line" | ||
|  *     f.pread(9, 8)    #=> "line one\n" | ||
|  */ | ||
| static VALUE | ||
| rb_io_pread(int argc, VALUE *argv, VALUE io) | ||
| { | ||
|     VALUE len, offset, str; | ||
|     rb_io_t *fptr; | ||
|     ssize_t n; | ||
|     struct prdwr_internal_arg arg; | ||
| #if defined(HAVE_PREAD) | ||
|     rb_scan_args(argc, argv, "21", &len, &offset, &str); | ||
|     arg.count = NUM2SIZET(len); | ||
|     arg.offset = NUM2OFFT(offset); | ||
|     io_setstrbuf(&str, (long)arg.count); | ||
|     if (arg.count == 0) return str; | ||
|     arg.buf = RSTRING_PTR(str); | ||
|     GetOpenFile(io, fptr); | ||
|     rb_io_check_byte_readable(fptr); | ||
|     arg.fd = fptr->fd; | ||
|     rb_io_check_closed(fptr); | ||
|     rb_str_locktmp(str); | ||
|     rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str); | ||
|     n = arg.len; | ||
|     if (n == -1) { | ||
| 	rb_sys_fail_path(fptr->pathv); | ||
|     } | ||
|     io_set_read_length(str, n); | ||
|     if (n == 0 && arg.count > 0) { | ||
| 	rb_eof_error(); | ||
|     } | ||
|     OBJ_TAINT(str); | ||
|     return str; | ||
| #else | ||
|     rb_raise(rb_eNotImpError, "pread() function is unimplemented on this machine"); | ||
|     return Qnil; | ||
| #endif /* HAVE_PREAD */ | ||
| } | ||
| static VALUE | ||
| internal_pwrite_func(void *ptr) | ||
| { | ||
|     struct prdwr_internal_arg *arg = ptr; | ||
|     return (VALUE)pwrite(arg->fd, arg->buf, arg->count, arg->offset); | ||
| } | ||
| /* | ||
|  *  call-seq: | ||
|  *     ios.pwrite(string, offset)    -> integer | ||
|  * | ||
|  *  Writes the given string to <em>ios</em> at <i>offset</i> using pwrite() | ||
|  *  system call.  This is advantageous to combining IO#seek and IO#write | ||
|  *  in that it is atomic, allowing multiple threads/process to share the | ||
|  *  same IO object for reading the file at various locations. | ||
|  *  This bypasses any userspace buffering of the IO layer. | ||
|  *  Returns the number of bytes written. | ||
|  *  Raises <code>SystemCallError</code> on error and <code>NotImplementedError</code> | ||
|  *  if platform does not implement the system call. | ||
|  * | ||
|  *     f = File.new("out", "w") | ||
|  *     f.pwrite("ABCDEF", 3)   #=> 6 | ||
|  * | ||
|  *     File.read("out")        #=> "\u0000\u0000\u0000ABCDEF" | ||
|  */ | ||
| static VALUE | ||
| rb_io_pwrite(VALUE io, VALUE offset, VALUE str) | ||
| { | ||
|     rb_io_t *fptr; | ||
|     ssize_t n; | ||
|     struct prdwr_internal_arg arg; | ||
| #if defined(HAVE_PWRITE) | ||
|     if (!RB_TYPE_P(str, T_STRING)) | ||
| 	str = rb_obj_as_string(str); | ||
|     arg.buf = RSTRING_PTR(str); | ||
|     arg.count = (size_t)RSTRING_LEN(str); | ||
|     arg.offset = NUM2OFFT(offset); | ||
|     io = GetWriteIO(io); | ||
|     GetOpenFile(io, fptr); | ||
|     rb_io_check_writable(fptr); | ||
|     arg.fd = fptr->fd; | ||
|     n = (ssize_t)rb_thread_io_blocking_region(internal_pwrite_func, &arg, fptr->fd); | ||
|     RB_GC_GUARD(str); | ||
|     if (n == -1) rb_sys_fail_path(fptr->pathv); | ||
|     return LONG2FIX(n); | ||
| #else | ||
|     rb_raise(rb_eNotImpError, "pwrite() function is unimplemented on this machine"); | ||
|     return Qnil; | ||
| #endif /* HAVE_PWRITE */ | ||
| } | ||
| VALUE | ||
| rb_io_binmode(VALUE io) | ||
| { | ||
| ... | ... | |
|     rb_define_method(rb_cIO, "syswrite", rb_io_syswrite, 1); | ||
|     rb_define_method(rb_cIO, "sysread",  rb_io_sysread, -1); | ||
|     rb_define_method(rb_cIO, "pread", rb_io_pread, -1); | ||
|     rb_define_method(rb_cIO, "pwrite", rb_io_pwrite, 2); | ||
|     rb_define_method(rb_cIO, "fileno", rb_io_fileno, 0); | ||
|     rb_define_alias(rb_cIO, "to_i", "fileno"); | ||
|     rb_define_method(rb_cIO, "to_io", rb_io_to_io, 0); | ||
| test/ruby/test_io.rb | ||
|---|---|---|
|         end | ||
|       end | ||
|     end | ||
|     def test_pread | ||
|       make_tempfile { |t| | ||
|         open(t.path) do |f| | ||
|           assert_equal("bar", f.pread(3, 4)) | ||
|           buf = "asdf" | ||
|           assert_equal("bar", f.pread(3, 4, buf)) | ||
|           assert_equal("bar", buf) | ||
|           assert_raise(EOFError) { f.pread(1, f.size) } | ||
|         end | ||
|       } | ||
|     rescue NotImplementedError | ||
|       skip "pread(2) is not implemtented." | ||
|     end | ||
|     def test_pwrite | ||
|       make_tempfile { |t| | ||
|         open(t.path, IO::RDWR) do |f| | ||
|           assert_equal(3, f.pwrite(4, "ooo")) | ||
|           assert_equal("ooo", f.pread(3, 4)) | ||
|         end | ||
|       } | ||
|     rescue NotImplementedError | ||
|       skip "pwrite(2) and pread(2) are not implemtented." | ||
|     end | ||
|   end | ||
| end | ||