diff --git a/NEWS b/NEWS
index 0a53aa2ee0..26b9401a4a 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,11 @@ with all sufficient information, see the ChangeLog file or Redmine
* Integer.sqrt [Feature #13219]
+* IO
+
+ * IO#pread [Feature #4532]
+ * IO#pwrite [Feature #4532]
+
* Regexp
* Update Onigmo 6.1.1.
* Support absent operator https://github.com/k-takata/Onigmo/issues/82
diff --git a/configure.in b/configure.in
index dcd56ba2f7..48a10811a0 100644
--- a/configure.in
+++ b/configure.in
@@ -2427,6 +2427,7 @@ AC_CHECK_FUNCS(posix_fadvise)
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)
diff --git a/doc/contributors.rdoc b/doc/contributors.rdoc
index eaec30d7d0..ec567cd35f 100644
--- a/doc/contributors.rdoc
+++ b/doc/contributors.rdoc
@@ -37,6 +37,9 @@ arton
* He is the distributor of ActiveScriptRuby and experimental 1.9.0-x installers for win32.
* Wrote patches for win32ole, gc.c, tmpdir.rb
+Sergey Avseyev
+* Added IO#pread and IO#pwrite.
+
== B
Daniel Berger
diff --git a/io.c b/io.c
index 085d7df2c9..3ac6b97a0e 100644
--- a/io.c
+++ b/io.c
@@ -4832,6 +4832,148 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io)
return str;
}
+#if defined(HAVE_PREAD) || defined(HAVE_PWRITE)
+struct prdwr_internal_arg {
+ int fd;
+ void *buf;
+ size_t count;
+ off_t offset;
+};
+#endif /* HAVE_PREAD || HAVE_PWRITE */
+
+#if defined(HAVE_PREAD)
+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;
+ return rb_thread_io_blocking_region(internal_pread_func, p, p->fd);
+}
+
+/*
+ * call-seq:
+ * ios.pread(maxlen, offset[, outbuf]) -> string
+ *
+ * Reads maxlen bytes from ios 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 outbuf argument is present, it must
+ * reference a String, which will receive the data.
+ * Raises SystemCallError
on error, EOFError
+ * at end of file and NotImplementedError
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;
+
+ 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);
+ n = (ssize_t)rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);
+
+ 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
+# define rb_io_pread rb_f_notimplement
+#endif /* HAVE_PREAD */
+
+#if defined(HAVE_PWRITE)
+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 ios at offset 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 SystemCallError
on error and NotImplementedError
+ * 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 (!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 SSIZET2NUM(n);
+}
+#else
+# define rb_io_pwrite rb_f_notimplement
+#endif /* HAVE_PWRITE */
+
VALUE
rb_io_binmode(VALUE io)
{
@@ -12464,6 +12606,9 @@ Init_IO(void)
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);
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index ca3f1e2d3b..d6cbf205e9 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -3525,5 +3525,26 @@ __END__
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
+ }
+ end if IO.method_defined?(:pread)
+
+ 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
+ }
+ end if IO.method_defined?(:pread) and IO.method_defined?(:pwrite)
end
end