about summary refs log tree commit homepage
path: root/ext/sleepy_penguin/cfr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/sleepy_penguin/cfr.c')
-rw-r--r--ext/sleepy_penguin/cfr.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/ext/sleepy_penguin/cfr.c b/ext/sleepy_penguin/cfr.c
new file mode 100644
index 0000000..ea17f82
--- /dev/null
+++ b/ext/sleepy_penguin/cfr.c
@@ -0,0 +1,68 @@
+#include "sleepy_penguin.h"
+#include "sp_copy.h"
+#include <unistd.h>
+
+#ifndef HAVE_COPY_FILE_RANGE
+#  include <sys/syscall.h>
+#  if !defined(__NR_copy_file_range) && \
+        (defined(__x86_64__) || defined(__i386__))
+#    define __NR_copy_file_range 285
+#  endif /* __NR_copy_file_range */
+#endif
+
+#ifdef __NR_copy_file_range
+static ssize_t my_cfr(int fd_in, off_t *off_in, int fd_out, off_t *off_out,
+                       size_t len, unsigned int flags)
+{
+        long n = syscall(__NR_copy_file_range,
+                        fd_in, off_in, fd_out, off_out, len, flags);
+
+        return (ssize_t)n;
+}
+#  define copy_file_range(fd_in,off_in,fd_out,off_out,len,flags) \
+                my_cfr((fd_in),(off_in),(fd_out),(off_out),(len),(flags))
+#endif
+
+static void *nogvl_cfr(void *ptr)
+{
+        struct copy_args *a = ptr;
+
+        return (void *)copy_file_range(a->fd_in, a->off_in,
+                                a->fd_out, a->off_out, a->len, a->flags);
+}
+
+static VALUE rb_cfr(int argc, VALUE *argv, VALUE mod)
+{
+        off_t i, o;
+        VALUE io_in, off_in, io_out, off_out, len, flags;
+        ssize_t bytes;
+        struct copy_args a;
+
+        rb_scan_args(argc, argv, "51",
+                     &io_in, &off_in, &io_out, &off_out, &len, &flags);
+
+        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);
+
+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();
+        }
+        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);
+}