about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2017-01-05 00:49:02 +0000
committerEric Wong <e@80x24.org>2017-01-05 08:39:07 +0000
commit56c4eb8a98957da88ef8efd42c6e39eaf0a1b69d (patch)
tree087aebe4bd96b3b997b07f78cafc6d430ffd9eae
parent1b355863e42164110a074a9313966ffebf880fb7 (diff)
downloadsleepy_penguin-56c4eb8a98957da88ef8efd42c6e39eaf0a1b69d.tar.gz
Keyword args allows for a smaller interface for common use,
while retaining the capability to use offsets for both input and
output.  The current (2.4) Ruby C API for keyword args is slow
and creates too many garbage objects.  As with our splice
wrapper, use a pure Ruby wrapper around an internal C function.
-rw-r--r--ext/sleepy_penguin/cfr.c39
-rw-r--r--lib/sleepy_penguin.rb5
-rw-r--r--lib/sleepy_penguin/cfr.rb6
-rw-r--r--test/test_cfr.rb10
4 files changed, 37 insertions, 23 deletions
diff --git a/ext/sleepy_penguin/cfr.c b/ext/sleepy_penguin/cfr.c
index e90d959..acece4f 100644
--- a/ext/sleepy_penguin/cfr.c
+++ b/ext/sleepy_penguin/cfr.c
@@ -34,38 +34,37 @@ static void *nogvl_cfr(void *ptr)
                                 a->fd_out, a->off_out, a->len, a->flags);
 }
 
-static VALUE rb_cfr(int argc, VALUE *argv, VALUE mod)
+/* :nodoc: */
+static VALUE rb_sp_cfr(VALUE mod, VALUE io_in, VALUE off_in,
+                        VALUE io_out, VALUE off_out,
+                        VALUE len, VALUE flags)
 {
-        off_t i, o;
-        VALUE io_in, off_in, io_out, off_out, len, flags;
-        ssize_t bytes;
+        off_t i = 0, o = 0;
         struct copy_args a;
-
-        rb_scan_args(argc, argv, "51",
-                     &io_in, &off_in, &io_out, &off_out, &len, &flags);
+        ssize_t bytes;
 
         a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
         a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
         a.len = NUM2SIZET(len);
-        a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags);
+        a.flags = NUM2UINT(flags);
 
-again:
-        a.fd_in = rb_sp_fileno(io_in);
-        a.fd_out = rb_sp_fileno(io_out);
-        bytes = (ssize_t)IO_RUN(nogvl_cfr, &a);
-        if (bytes < 0) {
-                if (errno == EINTR)
-                        goto again;
-                rb_sys_fail("copy_file_range");
-        } else if (bytes == 0) {
-                rb_eof_error();
+        for (;;) {
+                a.fd_in = rb_sp_fileno(io_in);
+                a.fd_out = rb_sp_fileno(io_out);
+                bytes = (ssize_t)IO_RUN(nogvl_cfr, &a);
+                if (bytes < 0) {
+                        switch (errno) {
+                        case EINTR: continue;
+                        default: rb_sys_fail("copy_file_range");
+                        }
+                }
+                return SSIZET2NUM(bytes);
         }
-        return SSIZET2NUM(bytes);
 }
 
 void sleepy_penguin_init_cfr(void)
 {
         VALUE mod = rb_define_module("SleepyPenguin");
 
-        rb_define_singleton_method(mod, "copy_file_range", rb_cfr, -1);
+        rb_define_singleton_method(mod, "__cfr", rb_sp_cfr, 6);
 }
diff --git a/lib/sleepy_penguin.rb b/lib/sleepy_penguin.rb
index 63f293d..07c431b 100644
--- a/lib/sleepy_penguin.rb
+++ b/lib/sleepy_penguin.rb
@@ -16,6 +16,7 @@ if defined?(SleepyPenguin::Inotify) &&
   end
 end
 
-if SleepyPenguin.respond_to?(:__splice) || SleepyPenguin.respond_to?(:__tee)
-  require_relative 'sleepy_penguin/splice'
+module SleepyPenguin
+  require_relative 'sleepy_penguin/splice' if respond_to?(:__splice)
+  require_relative 'sleepy_penguin/cfr' if respond_to?(:__cfr)
 end
diff --git a/lib/sleepy_penguin/cfr.rb b/lib/sleepy_penguin/cfr.rb
new file mode 100644
index 0000000..af94499
--- /dev/null
+++ b/lib/sleepy_penguin/cfr.rb
@@ -0,0 +1,6 @@
+module SleepyPenguin
+  def self.copy_file_range(io_in, io_out, len, flags = 0,
+                           off_in: nil, off_out: nil)
+    __cfr(io_in, off_in, io_out, off_out, len, flags)
+  end
+end
diff --git a/test/test_cfr.rb b/test/test_cfr.rb
index 3483c5a..0775c0f 100644
--- a/test/test_cfr.rb
+++ b/test/test_cfr.rb
@@ -13,7 +13,7 @@ class TestCfr < Test::Unit::TestCase
     assert_equal 5, src.syswrite(str)
     src.sysseek(0)
     begin
-      nr = SleepyPenguin.copy_file_range(src, nil, dst, nil, size, 0)
+      nr = SleepyPenguin.copy_file_range(src, dst, size)
     rescue Errno::EINVAL
       warn 'copy_file_range not supported (requires Linux 4.5+)'
       warn "We have: #{`uname -a`}"
@@ -22,6 +22,14 @@ class TestCfr < Test::Unit::TestCase
     assert_equal nr, 5
     dst.sysseek(0)
     assert_equal str, dst.sysread(5)
+
+    nr = SleepyPenguin.copy_file_range(src, dst, size, off_in: 1, off_out: 0)
+    assert_equal 4, nr
+    dst.sysseek(0)
+    assert_equal 'bcde', dst.sysread(4)
+
+    nr = SleepyPenguin.copy_file_range(src, dst, size, off_in: 9)
+    assert_equal 0, nr, 'no EOFError'
   ensure
     dst.close!
     src.close!