Feature #4532 » 0001-add-IO-pread-and-IO-pwrite-methods.patch
configure.in | ||
---|---|---|
setsid telldir seekdir fchmod cosh sinh tanh log2 round\
|
||
setuid setgid daemon select_large_fdset setenv unsetenv\
|
||
mktime timegm gmtime_r clock_gettime gettimeofday\
|
||
pread sendfile shutdown sigaltstack dl_iterate_phdr)
|
||
pread pwrite sendfile shutdown sigaltstack dl_iterate_phdr)
|
||
AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value,
|
||
[AC_TRY_COMPILE([
|
io.c | ||
---|---|---|
return LONG2FIX(n);
|
||
}
|
||
#if defined(HAVE_PREAD) || defined(HAVE_PWRITE)
|
||
struct prdwr_args {
|
||
int fd;
|
||
void *buf;
|
||
size_t count;
|
||
off_t offset;
|
||
};
|
||
#endif
|
||
#if defined(HAVE_PREAD)
|
||
static VALUE
|
||
nogvl_pread(void *ptr)
|
||
{
|
||
struct prdwr_args *args = ptr;
|
||
return (VALUE)pread(args->fd, args->buf, args->count, args->offset);
|
||
}
|
||
/*
|
||
* 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 and <code>EOFError</code>
|
||
* at end of file.
|
||
* Not implemented on all platforms.
|
||
*
|
||
* f = File.new("testfile")
|
||
* f.pread(16, 0) #=> "This is line one"
|
||
*/
|
||
static VALUE
|
||
rb_io_pread(int argc, VALUE *argv, VALUE io)
|
||
{
|
||
VALUE len, offset, str;
|
||
rb_io_t *fptr;
|
||
ssize_t n;
|
||
struct prdwr_args args;
|
||
rb_scan_args(argc, argv, "21", &len, &offset, &str);
|
||
args.count = NUM2SIZET(len);
|
||
args.offset = NUM2OFFT(offset);
|
||
io_setstrbuf(&str, (long)args.count);
|
||
if (args.count == 0) return str;
|
||
args.buf = RSTRING_PTR(str);
|
||
GetOpenFile(io, fptr);
|
||
rb_io_check_byte_readable(fptr);
|
||
args.fd = fptr->fd;
|
||
rb_io_check_closed(fptr);
|
||
rb_str_locktmp(str);
|
||
n = (ssize_t)rb_thread_io_blocking_region(nogvl_pread, &args, fptr->fd);
|
||
rb_str_unlocktmp(str);
|
||
if (n == -1) {
|
||
rb_sys_fail_path(fptr->pathv);
|
||
}
|
||
rb_str_set_len(str, n);
|
||
if (n == 0 && args.count > 0) {
|
||
rb_eof_error();
|
||
}
|
||
rb_str_resize(str, n);
|
||
OBJ_TAINT(str);
|
||
return str;
|
||
}
|
||
#endif /* HAVE_PREAD */
|
||
#if defined(HAVE_PWRITE)
|
||
static VALUE
|
||
nogvl_pwrite(void *ptr)
|
||
{
|
||
struct prdwr_args *args = ptr;
|
||
return (VALUE)pwrite(args->fd, args->buf, args->count, args->offset);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* ios.pwrite(offset, string) -> 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.
|
||
*
|
||
* f = File.new("out", "w")
|
||
* f.pwrite(3, "ABCDEF") #=> 6
|
||
*/
|
||
static VALUE
|
||
rb_io_pwrite(VALUE io, VALUE offset, VALUE string)
|
||
{
|
||
rb_io_t *fptr;
|
||
ssize_t n;
|
||
struct prdwr_args args;
|
||
rb_secure(4);
|
||
string = rb_obj_as_string(string);
|
||
args.buf = RSTRING_PTR(string);
|
||
args.count = (size_t)RSTRING_LEN(string);
|
||
args.offset = NUM2OFFT(offset);
|
||
io = GetWriteIO(io);
|
||
GetOpenFile(io, fptr);
|
||
rb_io_check_writable(fptr);
|
||
args.fd = fptr->fd;
|
||
n = (ssize_t)rb_thread_io_blocking_region(nogvl_pwrite, &args, fptr->fd);
|
||
if (n == -1) rb_sys_fail_path(fptr->pathv);
|
||
return SSIZET2NUM(n);
|
||
}
|
||
#endif /* HAVE_PWRITE */
|
||
/*
|
||
* call-seq:
|
||
* ios.sysread(maxlen[, outbuf]) -> string
|
||
... | ... | |
rb_define_method(rb_cIO, "flush", rb_io_flush, 0);
|
||
rb_define_method(rb_cIO, "tell", rb_io_tell, 0);
|
||
rb_define_method(rb_cIO, "seek", rb_io_seek_m, -1);
|
||
#ifdef HAVE_PREAD
|
||
rb_define_method(rb_cIO, "pread", rb_io_pread, -1);
|
||
#endif
|
||
#ifdef HAVE_PWRITE
|
||
rb_define_method(rb_cIO, "pwrite", rb_io_pwrite, 2);
|
||
#endif
|
||
rb_define_const(rb_cIO, "SEEK_SET", INT2FIX(SEEK_SET));
|
||
rb_define_const(rb_cIO, "SEEK_CUR", INT2FIX(SEEK_CUR));
|
||
rb_define_const(rb_cIO, "SEEK_END", INT2FIX(SEEK_END));
|
test/ruby/test_io.rb | ||
---|---|---|
Process.waitpid2(pid)
|
||
end
|
||
end
|
||
def test_pread
|
||
t = make_tempfile
|
||
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_raises(EOFError) { f.pread(1, f.size) }
|
||
end
|
||
end if IO.method_defined?(:pread)
|
||
def test_pwrite
|
||
t = make_tempfile
|
||
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?(:pwrite) && IO.method_defined?(:pread)
|
||
end
|