diff --git a/configure.in b/configure.in
index 5dede8d..b630938 100644
--- a/configure.in
+++ b/configure.in
@@ -1265,7 +1265,7 @@ else
fi
AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot getcwd eaccess\
truncate ftruncate chsize times utimes utimensat fcntl lockf lstat\
- link symlink readlink readdir_r fsync fdatasync fchown\
+ link symlink readlink readdir_r fsync fdatasync fchown posix_fadvise\
setitimer setruid seteuid setreuid setresuid setproctitle socketpair\
setrgid setegid setregid setresgid issetugid pause lchown lchmod\
getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\
diff --git a/io.c b/io.c
index 8fa6613..86e4cf5 100644
--- a/io.c
+++ b/io.c
@@ -1,3 +1,4 @@
+
/**********************************************************************
io.c -
@@ -1413,6 +1414,111 @@ rb_io_fdatasync(VALUE io)
#define rb_io_fdatasync rb_f_notimplement
#endif
+static VALUE sym_normal, sym_sequential, sym_random,
+ sym_willneed, sym_dontneed, sym_noreuse;
+/*
+ * call-seq:
+ * ios.advise(advice, offset=0, len=0) -> nil
+ *
+ * Announce an intention to access data from the current file in a
+ * specific pattern. On platforms that do not support the
+ * posix_fadvise(2) system call, this method is a no-op.
+ *
+ * _advice_ is one of the following symbols:
+ *
+ * * :normal - No advice to give; the default assumption for an open file.
+ * * :sequential - The data will be accessed sequentially:
+ * with lower offsets read before higher ones.
+ * * :random - The data will be accessed in random order.
+ * * :willneed - The data will be accessed in the near future.
+ * * :dontneed - The data will not be accessed in the near future.
+ * * :noreuse - The data will only be accessed once.
+ *
+ * The semantics of a piece of advice are platform-dependent. See
+ * man 2 posix_fadvise for details.
+ *
+ * "data" means the region of the current file that begins at
+ * _offset_ and extends for _len_ bytes. If _len_ is 0, the region
+ * ends at the last byte of the file. By default, both _offset_ and
+ * _len_ are 0, meaning that the advice applies to the entire file.
+ *
+ * If an error occurs, one of the following exceptions will be raised:
+ *
+ * * IOError
- The IO
stream is closed.
+ * * Errno::EBADF
- The file descriptor of the current file is
+ invalid.
+ * * Errno::EINVAL
- An invalid value for _advice_ was given.
+ * * Errno::ESPIPE
- The file descriptor of the current
+ * * file refers to a FIFO or pipe. (Linux raises Errno::EINVAL
+ * * in this case).
+ * * TypeError
- Either _advice_ was not a Symbol, or one of the
+ other arguments was not an Integer
.
+ * * RangeError
- One of the arguments given was too big/small.
+ *
+ * This list is not exhaustive; other Errno:: exceptions are also possible.
+ */
+
+static VALUE
+rb_io_advise(int argc, VALUE *argv, VALUE io)
+{
+ off_t offset, len;
+ int advice, rv;
+ VALUE initadvice, initoffset, initlen;
+ rb_io_t *fptr;
+
+ rb_scan_args(argc, argv, "12", &initadvice, &initoffset, &initlen);
+ if (TYPE(initadvice) != T_SYMBOL)
+ rb_raise(rb_eTypeError, "advice must be a Symbol");
+
+ offset = NIL_P(initoffset) ? 0 : NUM2OFFT(initoffset);
+ len = NIL_P(initlen) ? 0 : NUM2OFFT(initlen);
+
+ if (initadvice == sym_normal) {
+#ifdef POSIX_FADV_NORMAL
+ advice = POSIX_FADV_NORMAL;
+#endif
+ }
+ else if (initadvice == sym_random) {
+#ifdef POSIX_FADV_RANDOM
+ advice = POSIX_FADV_RANDOM;
+#endif
+ }
+ else if (initadvice == sym_sequential) {
+#ifdef POSIX_FADV_SEQUENTIAL
+ advice = POSIX_FADV_SEQUENTIAL;
+#endif
+ }
+ else if (initadvice == sym_willneed) {
+#ifdef POSIX_FADV_WILLNEED
+ advice = POSIX_FADV_WILLNEED;
+#endif
+ }
+ else if (initadvice == sym_dontneed) {
+#ifdef POSIX_FADV_DONTNEED
+ advice = POSIX_FADV_DONTNEED;
+#endif
+ }
+ else if (initadvice == sym_noreuse) {
+#ifdef POSIX_FADV_NOREUSE
+ advice = POSIX_FADV_NOREUSE;
+#endif
+ }
+ else
+ rb_raise(rb_eArgError, "Invalid advice: :%s",
+ RSTRING_PTR(rb_id2str(SYM2ID(initadvice))));
+
+#ifdef HAVE_POSIX_FADVISE
+ io = GetWriteIO(io);
+ GetOpenFile(io, fptr);
+
+ if (rv = posix_fadvise(fptr->fd, offset, len, advice))
+ /* posix_fadvise(2) doesn't set errno. On success it returns 0; otherwise
+ it returns the error code. */
+ rb_syserr_fail(rv, RSTRING_PTR(fptr->pathv));
+#endif
+ return Qnil;
+}
+
/*
* call-seq:
* ios.fileno -> fixnum
@@ -10052,6 +10158,7 @@ Init_IO(void)
rb_define_method(rb_cIO, "binmode", rb_io_binmode_m, 0);
rb_define_method(rb_cIO, "binmode?", rb_io_binmode_p, 0);
rb_define_method(rb_cIO, "sysseek", rb_io_sysseek, -1);
+ rb_define_method(rb_cIO, "advise", rb_io_advise, -1);
rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1);
rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
@@ -10220,4 +10327,10 @@ Init_IO(void)
sym_textmode = ID2SYM(rb_intern("textmode"));
sym_binmode = ID2SYM(rb_intern("binmode"));
sym_autoclose = ID2SYM(rb_intern("autoclose"));
+ sym_normal = ID2SYM(rb_intern("normal"));
+ sym_sequential = ID2SYM(rb_intern("sequential"));
+ sym_random = ID2SYM(rb_intern("random"));
+ sym_willneed = ID2SYM(rb_intern("willneed"));
+ sym_dontneed = ID2SYM(rb_intern("dontneed"));
+ sym_noreuse = ID2SYM(rb_intern("noreuse"));
}
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 73a16de..119740e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1722,4 +1722,38 @@ End
end
end
end
+
+ def test_advise
+ t = make_tempfile
+ assert_raise(ArgumentError, "no arguments") { t.advise }
+ %w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |a|
+ [[0,0], [0, 20], [400, 2]].each do |l, o|
+ open(make_tempfile.path) do |t|
+ assert_equal(t.advise(a, l, o), nil)
+ assert_raise(ArgumentError, "superfluous arguments") do
+ t.advise(a, l, o, l)
+ end
+ assert_raise(TypeError, "wrong type for first argument") do
+ t.advise(a.to_s, l, o)
+ end
+ assert_raise(TypeError, "wrong type for last argument") do
+ t.advise(a, l, Array(o))
+ end
+ assert_raise(RangeError, "last argument too big") do
+ t.advise(a, l, 9999e99)
+ end
+ end
+ assert_raise(IOError, "closed file") do
+ make_tempfile.advise(a.to_sym, l, o)
+ end
+ end
+ end
+ %w{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |a|
+ [[0,0], [0, 20], [400, 2]].each do |l, o|
+ open(make_tempfile.path) do |t|
+ assert_raise(ArgumentError, "invalid advice") { t.advise(a, l, o) }
+ end
+ end
+ end
+ end
end