IO.copy_stream

IO.copy_stream e$B$r$D$1$k$N$O$I$&$G$7$g$&$+!#e(B

IO.copy_stream(src, dst) e$B$Ge(B src e$B$+$ie(B dst
e$B$K%3%T!<$7$^$9!#e(B
src, dst e$B$O%U%!%$%kL>$+e(B IO e$B%*%V%8%'%/%H$G$9!#e(B

e$B$?$H$($P!"e(BIO.copy_stream(STDIN, STDOUT) e$B$N$h$&$K;H$($^$9!#e(B

e$B$3$NA:n$O%U%!%$%k$N%3%T!<$H$+e(B Web e$B%5!<%P$G%U%!%$%k$r%=%1%Ce(B e$B%H$KAw$j9~$$H$+$1$C$3$&$d$k$3$H$,$"$k$N$G$9$,!"e(BRuby e$BK\BN$He(B
e$B$7$F$O$9$0$K;H$($k%a%=%C%I$ODs6!$7$F$*$i$:!“e(B
FileUtils.copy_stream e$B$r;H$&$J$I$NI,MW$,$”$j$^$9!#e(B

FileUtils.copy_stream e$B$KHf$Y$Fe(B IO.copy_stream e$B$NMxE@$K$O0J2<e(B
e$B$N$b$N$,$"$j$^$9!#e(B

  • e$B%3%T!<Cfe(B global interpreter lock e$B$r30$;$ke(B
    e$B%3%T!<$r$7$F$$$k:GCf$Oe(B Ruby e$B$N%*%V%8%'%/%H$K?($kI,MW$,$J$$e(B
    e$B$N$G!“e(BGIL e$B$r30$;$F!”$=$l$K$h$j$=$N$"$$$@B>$N%9%l%C%I$,F0$1e(B
    e$B$^$9e(B

  • e$B%7%9%F%$H>u67$K$h$C$F$Oe(B sendfile e$B$r;H$($ke(B e$B$3$l$K$h$j!"%3%T!<$r%+!<%M%k$KG$$;$C@Z$j$K$G$-$F!"%3%s%F%-e(B e$B%9%H%9%$%C%A$d%+!<%M%k6u4V$H%f!<%66u4V$N4V$N%3%T!<$r:o8:$Ge(B e$B$-$^$9!#e(B e$B$J$*!"e(Bsendfile e$B$O8=;~E@$G$Oe(B GNU/Linux e$B$N$b$N$7$+07$C$F$$$^e(B e$B$;$s!#e(B(Apache Portable Runtime e$B$rD4$Y$F$_$k$H!"$1$C$3$&$$e(B e$B$m$$$m$J%7%9%F%$,%5%]!<%H$7$F$$$k$h$&$G$9$,!#e(B) e$B$“$H!“e(Bsrc
    e$B$,%U%!%$%k$Ge(B dst e$B$,%=%1%C%H$G$”$k$H$$$&>r7o$,$”$j$^$9!#e(B

e$B$^$?!“>JN,2DG=0z?t$,e(B 2e$B$D$”$C$F!"%3%T!<$9$kD9$5$He(B src
e$B$K$*$1e(B
e$B$k%3%T!<3+;O0LCV$r;XDj$G$-$^$9!#e(B
IO.copy_stream(src, dst, copy_length, src_offset)

src e$B$,e(B (e$B%U%!%$%kL>$G$J$/e(B) IO e$B$G!"$+$D!"e(Bsrc_offset
e$B$r;XDj$7$?e(B
e$B;~$K$Oe(B src e$B$N%U%!%$%k%%U%;%C%H$OF0$+$7$^$;$s!#e(Bsendfile
e$B$,$Je(B
e$B$$>l9g!"$3$l$Oe(B SUS e$B$GDj5A$5$l$F$$$ke(B pread
e$B$G<B8=$7$^$9!#e(B
pread e$B$b$J$1$l$P$$$^$N$H$3$m%(%i!<$G$9!#%(%i!<$8$c$J$/$Fe(B
lseek e$B$G%U%!%$%k%
%U%;%C%H$rLa$7$F$b$$$$$+$b$7$l$^$;$s$,!#e(B

Index: configure.in

— configure.in (revision 15543)
+++ configure.in (working copy)
@@ -582,7 +582,7 @@ AC_CHECK_HEADERS(stdlib.h string.h unist
fcntl.h sys/fcntl.h sys/select.h sys/time.h sys/times.h
sys/param.h
syscall.h pwd.h grp.h a.out.h utime.h memory.h direct.h
sys/resource.h
sys/mkdev.h sys/utime.h xti.h netinet/in_systm.h float.h ieeefp.h
pthread.h \

  • ucontext.h intrinsics.h langinfo.h locale.h)
    
  • ucontext.h intrinsics.h langinfo.h locale.h sys/sendfile.h)
    

dnl Check additional types.
AC_CHECK_SIZEOF(rlim_t, 0, [
@@ -661,7 +661,8 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid
dlopen sigprocmask sigaction sigsetjmp _setjmp vsnprintf
snprintf
setsid telldir seekdir fchmod cosh sinh tanh log2 round
setuid setgid daemon select_large_fdset setenv unsetenv\

  •    mktime timegm clock_gettime gettimeofday)
    
  •          mktime timegm clock_gettime gettimeofday\
    
  •          pread sendfile)
    

AC_ARG_ENABLE(setreuid,
[ --enable-setreuid use setreuid()/setregid() according
to need even if obsolete.],
[use_setreuid=$enableval])
Index: io.c

— io.c (revision 15543)
+++ io.c (working copy)
@@ -6011,6 +6011,480 @@ rb_io_s_read(int argc, VALUE *argv, VALU
return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}

+struct copy_stream_struct {

  • VALUE src;
  • VALUE dst;
  • int src_fd;
  • int dst_fd;
  • off_t copy_length;
  • off_t src_offset;
  • int close_src;
  • int close_dst;
  • off_t total;
  • char *syserr;
  • int error_no;
  • char *notimp;
  • rb_fdset_t fds;
    +};

+static void
+copy_stream_rbuf_to_dst(struct copy_stream_struct *stp,

  •    rb_io_t *src_fptr, rb_io_t *dst_fptr, const char *dst_path)
    

+{

  • ssize_t r;
  • int len;
    +retry:
  • len = src_fptr->rbuf_len;
  • if (stp->copy_length != (off_t)-1 && stp->copy_length < len) {
  •    len = stp->copy_length;
    
  • }
  • if (len == 0)
  •    return;
    
  • r = rb_write_internal(dst_fptr->fd, src_fptr->rbuf +
    src_fptr->rbuf_off, len);
  • if (len == r) {
  •    src_fptr->rbuf_len -= len;
    
  •    if (src_fptr->rbuf_len < 0) src_fptr->rbuf_len = 0;
    
  •    if (stp->copy_length != (off_t)-1) stp->copy_length -= len;
    
  •    stp->total += len;
    
  •    return;
    
  • }
  • else if (0 <= r) {
  •    src_fptr->rbuf_off += r;
    
  •    src_fptr->rbuf_len -= r;
    
  •    if (stp->copy_length != (off_t)-1) stp->copy_length -= r;
    
  •    stp->total += r;
    
  •    errno = EAGAIN;
    
  • }
  • if (rb_io_wait_writable(dst_fptr->fd)) {
  •    rb_io_check_closed(dst_fptr);
    
  •    if (src_fptr->rbuf_len)
    
  •        goto retry;
    
  • }
  • rb_sys_fail(dst_path);
    +}

+static int
+copy_stream_wait_read(struct copy_stream_struct *stp)
+{

  • int ret;
    +retry:
  • rb_fd_zero(&stp->fds);
  • rb_fd_set(stp->src_fd, &stp->fds);
  • ret = rb_fd_select(rb_fd_max(&stp->fds), &stp->fds, NULL, NULL,
    NULL);
  • if (ret == -1) {
  •    if (errno == EINTR)
    
  •        goto retry;
    
  •    stp->syserr = "select";
    
  •    stp->error_no = errno;
    
  •    return -1;
    
  • }
  • return 0;
    +}

+static int
+copy_stream_wait_write(struct copy_stream_struct *stp)
+{

  • int ret;
    +retry:
  • rb_fd_zero(&stp->fds);
  • rb_fd_set(stp->dst_fd, &stp->fds);
  • ret = rb_fd_select(rb_fd_max(&stp->fds), NULL, &stp->fds, NULL,
    NULL);
  • if (ret == -1) {
  •    if (errno == EINTR)
    
  •        goto retry;
    
  •    stp->syserr = "select";
    
  •    stp->error_no = errno;
    
  •    return -1;
    
  • }
  • return 0;
    +}

+#ifdef HAVE_SENDFILE
+
+#ifdef linux
+#define USE_SENDFILE
+
+#ifdef HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+static ssize_t
+simple_sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
+{

  • return sendfile(out_fd, in_fd, offset, count);
    +}

+#endif
+
+#endif
+
+#ifdef USE_SENDFILE
+static int
+copy_stream_sendfile(struct copy_stream_struct *stp)
+{

  • struct stat src_stat, dst_stat;
  • ssize_t ss;
  • int ret;
  • off_t copy_length;
  • off_t src_offset;
  • int use_pread;
  • ret = fstat(stp->src_fd, &src_stat);
  • if (ret == -1) {
  •    stp->syserr = "fstat";
    
  •    stp->error_no = errno;
    
  •    return -1;
    
  • }
  • if (!S_ISREG(src_stat.st_mode))
  •    return 0;
    
  • ret = fstat(stp->dst_fd, &dst_stat);
  • if (ret == -1) {
  •    stp->syserr = "fstat";
    
  •    stp->error_no = errno;
    
  •    return -1;
    
  • }
  • if ((dst_stat.st_mode & S_IFMT) != S_IFSOCK)
  •    return 0;
    
  • src_offset = stp->src_offset;
  • use_pread = src_offset != (off_t)-1;
  • copy_length = stp->copy_length;
  • if (copy_length == (off_t)-1) {
  •    if (use_pread)
    
  •        copy_length = src_stat.st_size - src_offset;
    
  •    else {
    
  •        off_t cur = lseek(stp->src_fd, 0, SEEK_CUR);
    
  •        if (cur == (off_t)-1) {
    
  •            stp->syserr = "lseek";
    
  •            stp->error_no = errno;
    
  •            return -1;
    
  •        }
    
  •        copy_length = src_stat.st_size - cur;
    
  •    }
    
  • }

+retry_sendfile:

  • if (use_pread) {
  •    ss = simple_sendfile(stp->dst_fd, stp->src_fd, &src_offset, 
    

copy_length);

  • }
  • else {
  •    ss = simple_sendfile(stp->dst_fd, stp->src_fd, NULL, 
    

copy_length);

  • }
  • if (0 < ss) {
  •    stp->total += ss;
    
  •    copy_length -= ss;
    
  •    if (0 < copy_length) {
    
  •        ss = -1;
    
  •        errno = EAGAIN;
    
  •    }
    
  • }
  • if (ss == -1) {
  •    if (errno == EINVAL || errno == ENOSYS)
    
  •        return 0;
    
  •    if (errno == EINTR)
    
  •        goto retry_sendfile;
    
  •    if (errno == EAGAIN || errno == EWOULDBLOCK) {
    
  •        if (copy_stream_wait_write(stp) == -1)
    
  •            return -1;
    
  •        goto retry_sendfile;
    
  •    }
    
  •    stp->syserr = "sendfile";
    
  •    stp->error_no = errno;
    
  •    return -1;
    
  • }
  • return 1;
    +}
    +#endif

+static ssize_t
+copy_stream_read(struct copy_stream_struct *stp, char *buf, int len,
off_t offset)
+{

  • ssize_t ss;
    +retry_read:
  • if (offset == (off_t)-1)
  •    ss = read(stp->src_fd, buf, len);
    
  • else {
    +#ifdef HAVE_PREAD
  •    ss = pread(stp->src_fd, buf, len, offset);
    

+#else

  •    stp->notimp = "pread";
    
  •    return -1;
    

+#endif

  • }
  • if (ss == 0) {
  •    return 0;
    
  • }
  • if (ss == -1) {
  •    if (errno == EINTR)
    
  •        goto retry_read;
    
  •    if (errno == EAGAIN || errno == EWOULDBLOCK) {
    
  •        if (copy_stream_wait_read(stp) == -1)
    
  •            return -1;
    
  •        goto retry_read;
    
  •    }
    
  •    if (errno == ENOSYS) {
    
  •        stp->notimp = "pread";
    
  •        return -1;
    
  •    }
    
  •    stp->syserr = offset == (off_t)-1 ?  "read" : "pread";
    
  •    stp->error_no = errno;
    
  •    return -1;
    
  • }
  • return ss;
    +}

+static int
+copy_stream_write(struct copy_stream_struct *stp, char *buf, int len)
+{

  • ssize_t ss;
  • int off = 0;
  • while (len) {
  •    ss = write(stp->dst_fd, buf+off, len);
    
  •    if (ss == -1) {
    
  •        if (errno == EINTR)
    
  •            continue;
    
  •        if (errno == EAGAIN || errno == EWOULDBLOCK) {
    
  •            if (copy_stream_wait_write(stp) == -1)
    
  •                return -1;
    
  •            continue;
    
  •        }
    
  •        stp->syserr = "write";
    
  •        stp->error_no = errno;
    
  •        return -1;
    
  •    }
    
  •    off += ss;
    
  •    len -= ss;
    
  •    stp->total += ss;
    
  • }
  • return 0;
    +}

+static void
+copy_stream_read_write(struct copy_stream_struct *stp)
+{

  • char buf[1024*16];
  • int len;
  • ssize_t ss;
  • int ret;
  • off_t copy_length;
  • int use_eof;
  • off_t src_offset;
  • int use_pread;
  • copy_length = stp->copy_length;
  • use_eof = copy_length == (off_t)-1;
  • src_offset = stp->src_offset;
  • use_pread = src_offset != (off_t)-1;
  • if (use_pread && stp->close_src) {
  •    off_t r;
    
  •    r = lseek(stp->src_fd, src_offset, SEEK_SET);
    
  •    if (r == (off_t)-1) {
    
  •        stp->syserr = "lseek";
    
  •        stp->error_no = errno;
    
  •        return;
    
  •    }
    
  •    src_offset = (off_t)-1;
    
  •    use_pread = 0;
    
  • }
  • while (use_eof || 0 < copy_length) {
  •    if (!use_eof && copy_length < sizeof(buf)) {
    
  •        len = copy_length;
    
  •    }
    
  •    else {
    
  •        len = sizeof(buf);
    
  •    }
    
  •    if (use_pread) {
    
  •        ss = copy_stream_read(stp, buf, len, src_offset);
    
  •        if (0 < ss)
    
  •            src_offset += ss;
    
  •    }
    
  •    else {
    
  •        ss = copy_stream_read(stp, buf, len, (off_t)-1);
    
  •    }
    
  •    if (ss <= 0) /* EOF or error */
    
  •        return;
    
  •    ret = copy_stream_write(stp, buf, ss);
    
  •    if (ret < 0)
    
  •        return;
    
  •    if (!use_eof)
    
  •        copy_length -= ss;
    
  • }
    +}

+static VALUE
+copy_stream_func(void *arg)
+{

  • struct copy_stream_struct *stp = (struct copy_stream_struct *)arg;
  • int ret;

+#ifdef USE_SENDFILE

  • ret = copy_stream_sendfile(stp);
  • if (ret != 0)
  •    goto finish; /* error or success */
    

+#endif
+

  • copy_stream_read_write(stp);

+finish:

  • return Qnil;
    +}

+static VALUE
+copy_stream_body(VALUE arg)
+{

  • struct copy_stream_struct *stp = (struct copy_stream_struct *)arg;
  • VALUE src_io, dst_io;
  • rb_io_t *src_fptr, *dst_fptr;
  • int src_fd, dst_fd;
  • char *src_path = 0, *dst_path = 0;
  • src_io = rb_check_convert_type(stp->src, T_FILE, “IO”, “to_io”);
  • if (!NIL_P(src_io)) {
  •    GetOpenFile(src_io, src_fptr);
    
  •    src_fd = src_fptr->fd;
    
  • }
  • else {
  •    src_fptr = 0;
    
  •    FilePathValue(stp->src);
    
  •    src_path = StringValueCStr(stp->src);
    
  •    src_fd = rb_sysopen_internal(src_path, O_RDONLY|O_NOCTTY, 0);
    
  •    if (src_fd == -1) { rb_sys_fail(src_path); }
    
  •    stp->close_src = 1;
    
  • }
  • stp->src_fd = src_fd;
  • dst_io = rb_check_convert_type(stp->dst, T_FILE, “IO”, “to_io”);
  • if (!NIL_P(dst_io)) {
  •    dst_io = GetWriteIO(dst_io);
    
  •    GetOpenFile(dst_io, dst_fptr);
    
  •    dst_fd = dst_fptr->fd;
    
  • }
  • else {
  •    dst_fptr = 0;
    
  •    FilePathValue(stp->dst);
    
  •    dst_path = StringValueCStr(stp->dst);
    
  •    dst_fd = rb_sysopen_internal(dst_path, 
    

O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0600);

  •    if (dst_fd == -1) { rb_sys_fail(dst_path); }
    
  •    stp->close_dst = 1;
    
  • }
  • stp->dst_fd = dst_fd;
  • stp->total = 0;
  • if (src_fptr && dst_fptr && src_fptr->rbuf_len &&
    dst_fptr->wbuf_len) {
  •    long len = src_fptr->rbuf_len;
    
  •    VALUE str;
    
  •    if (stp->copy_length != (off_t)-1 && stp->copy_length < len) {
    
  •        len = stp->copy_length;
    
  •    }
    
  •    str = rb_str_buf_new(len);
    
  •    rb_str_resize(str,len);
    
  •    read_buffered_data(RSTRING_PTR(str), len, src_fptr);
    
  •    io_fwrite(str, dst_fptr);
    
  •    stp->total += len;
    
  •    if (stp->copy_length != (off_t)-1)
    
  •        stp->copy_length -= len;
    
  • }
  • if (dst_fptr && io_fflush(dst_fptr) < 0) {
  • rb_raise(rb_eIOError, “flush failed”);
  • }
  • if (src_fptr) {
  •    copy_stream_rbuf_to_dst(stp, src_fptr, dst_fptr, dst_path);
    
  • }
  • if (stp->copy_length == 0)
  •    return Qnil;
    
  • rb_fd_init(&stp->fds);
  • rb_fd_set(src_fd, &stp->fds);
  • rb_fd_set(dst_fd, &stp->fds);
  • return rb_thread_blocking_region(copy_stream_func, (void*)stp,
    RB_UBF_DFL, 0);
    +}

+static VALUE
+copy_stream_finalize(VALUE arg)
+{

  • struct copy_stream_struct *stp = (struct copy_stream_struct *)arg;
  • if (stp->close_src)
  •    close(stp->src_fd);
    
  • if (stp->close_dst)
  •    close(stp->dst_fd);
    
  • rb_fd_term(&stp->fds);
  • if (stp->syserr) {
  •    errno = stp->error_no;
    
  •    rb_sys_fail(stp->syserr);
    
  • }
  • if (stp->notimp) {
  • rb_raise(rb_eNotImpError, “%s() not implemented”, stp->notimp);
  • }
  • return Qnil;
    +}

+/*

    • call-seq:
    • IO.copy_stream(src, dst)
      
    • IO.copy_stream(src, dst, copy_length)
      
    • IO.copy_stream(src, dst, copy_length, src_offset)
      
    • IO.copy_stream copies src to dst.
    • src and dst is either a filename or an IO.
    • This method returns the number of bytes copied.
    • If optional arguments are not given,
    • the start position of the copy is
    • the beginning of the filename or
    • the current file offset of the IO.
    • The end position of the copy is the end of file.
    • If copy_length is given,
    • No more than copy_length bytes are copied.
    • If src_offset is given,
    • it specifies the start position of the copy.
    • When src_offset is specified and
    • src is an IO,
    • IO.copy_stream doesn’t move the current file offset.
  • */
    +static VALUE
    +rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
    +{
  • VALUE src, dst, length, src_offset;
  • struct copy_stream_struct st;
  • MEMZERO(&st, struct copy_stream_struct, 1);
  • rb_scan_args(argc, argv, “22”, &src, &dst, &length, &src_offset);
  • if (NIL_P(length))
  •    st.copy_length = (off_t)-1;
    
  • else
  •    st.copy_length = NUM2OFFT(length);
    
  • if (NIL_P(src_offset))
  •    st.src_offset = (off_t)-1;
    
  • else
  •    st.src_offset = NUM2OFFT(src_offset);
    
  • st.src = src;
  • st.dst = dst;
  • rb_ensure(copy_stream_body, (VALUE)&st, copy_stream_finalize,
    (VALUE)&st);
  • return OFFT2NUM(st.total);
    +}

/*

  • call-seq:
    @@ -6595,6 +7069,7 @@ Init_IO(void)
    rb_define_singleton_method(rb_cIO, “select”, rb_f_select, -1);
    rb_define_singleton_method(rb_cIO, “pipe”, rb_io_s_pipe, -1);
    rb_define_singleton_method(rb_cIO, “try_convert”,
    rb_io_s_try_convert, 1);
  • rb_define_singleton_method(rb_cIO, “copy_stream”,
    rb_io_s_copy_stream, -1);

    rb_define_method(rb_cIO, “initialize”, rb_io_initialize, -1);

Index: thread.c

— thread.c (revision 15543)
+++ thread.c (working copy)
@@ -1702,6 +1702,25 @@ rb_fd_copy(rb_fdset_t *dst, const fd_set
memcpy(dst->fdset, src, size);
}

+int
+rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds,
rb_fdset_t *exceptfds, struct timeval *timeout)
+{

  • fd_set *r = NULL, *w = NULL, *e = NULL;
  • if (readfds) {
  •    rb_fd_resize(n - 1, readfds);
    
  •    r = rb_fd_ptr(readfds);
    
  • }
  • if (writefds) {
  •    rb_fd_resize(n - 1, writefds);
    
  •    w = rb_fd_ptr(writefds);
    
  • }
  • if (exceptfds) {
  •    rb_fd_resize(n - 1, exceptfds);
    
  •    e = rb_fd_ptr(exceptfds);
    
  • }
  • return select(n, r, w, e, timeout);
    +}

#undef FD_ZERO
#undef FD_SET
#undef FD_CLR
Index: test/ruby/test_io.rb

— test/ruby/test_io.rb (revision 15543)
+++ test/ruby/test_io.rb (working copy)
@@ -1,4 +1,7 @@
require ‘test/unit’
+require ‘tmpdir’
+require ‘io/nonblock’
+require ‘socket’

class TestIO < Test::Unit::TestCase
def test_gets_rs
@@ -55,4 +58,284 @@ class TestIO < Test::Unit::TestCase
ensure
r.close
end
+

  • def with_pipe
  • r, w = IO.pipe
  • begin
  •  yield r, w
    
  • ensure
  •  r.close unless r.closed?
    
  •  w.close unless w.closed?
    
  • end
  • end
  • def with_read_pipe(content)
  • r, w = IO.pipe
  • w << content
  • w.close
  • begin
  •  yield r
    
  • ensure
  •  r.close
    
  • end
  • end
  • def test_copy_stream
  • Dir.mktmpdir {|d|
  •  Dir.chdir d
    
  •  content = "foobar"
    
  •  File.open("src", "w") {|f| f << content }
    
  •  IO.copy_stream("src", "dst")
    
  •  assert_equal(content, File.read("dst"))
    
  •  # overwrite by smaller file.
    
  •  content = "baz"
    
  •  File.open("src", "w") {|f| f << content }
    
  •  IO.copy_stream("src", "dst")
    
  •  assert_equal(content, File.read("dst"))
    
  •  IO.copy_stream("src", "dst", 2)
    
  •  assert_equal(content[0,2], File.read("dst"))
    
  •  IO.copy_stream("src", "dst", 0)
    
  •  assert_equal("", File.read("dst"))
    
  •  IO.copy_stream("src", "dst", nil, 1)
    
  •  assert_equal(content[1..-1], File.read("dst"))
    
  •  assert_raise(Errno::ENOENT) {
    
  •    IO.copy_stream("nodir/foo", "dst")
    
  •  }
    
  •  assert_raise(Errno::ENOENT) {
    
  •    IO.copy_stream("src", "nodir/bar")
    
  •  }
    
  •  with_pipe {|r, w|
    
  •    IO.copy_stream("src", w)
    
  •    w.close
    
  •    assert_equal(content, r.read)
    
  •  }
    
  •  with_pipe {|r, w|
    
  •    w.close
    
  •    assert_raise(IOError) { IO.copy_stream("src", w) }
    
  •  }
    
  •  pipe_content = "abc"
    
  •  with_read_pipe(pipe_content) {|r|
    
  •    IO.copy_stream(r, "dst")
    
  •    assert_equal(pipe_content, File.read("dst"))
    
  •  }
    
  •  with_read_pipe("abc") {|r1|
    
  •    assert_equal("a", r1.getc)
    
  •    with_pipe {|r2, w2|
    
  •      w2.sync = false
    
  •      w2 << "def"
    
  •      IO.copy_stream(r1, w2)
    
  •      w2.close
    
  •      assert_equal("defbc", r2.read)
    
  •    }
    
  •  }
    
  •  with_read_pipe("abc") {|r1|
    
  •    assert_equal("a", r1.getc)
    
  •    with_pipe {|r2, w2|
    
  •      w2.sync = false
    
  •      w2 << "def"
    
  •      IO.copy_stream(r1, w2, 1)
    
  •      w2.close
    
  •      assert_equal("defb", r2.read)
    
  •    }
    
  •  }
    
  •  with_read_pipe("abc") {|r1|
    
  •    assert_equal("a", r1.getc)
    
  •    with_pipe {|r2, w2|
    
  •      IO.copy_stream(r1, w2)
    
  •      w2.close
    
  •      assert_equal("bc", r2.read)
    
  •    }
    
  •  }
    
  •  with_read_pipe("abc") {|r1|
    
  •    assert_equal("a", r1.getc)
    
  •    with_pipe {|r2, w2|
    
  •      IO.copy_stream(r1, w2, 1)
    
  •      w2.close
    
  •      assert_equal("b", r2.read)
    
  •    }
    
  •  }
    
  •  with_read_pipe("abc") {|r1|
    
  •    assert_equal("a", r1.getc)
    
  •    with_pipe {|r2, w2|
    
  •      IO.copy_stream(r1, w2, 0)
    
  •      w2.close
    
  •      assert_equal("", r2.read)
    
  •    }
    
  •  }
    
  •  with_pipe {|r1, w1|
    
  •    w1 << "abc"
    
  •    assert_equal("a", r1.getc)
    
  •    with_pipe {|r2, w2|
    
  •      w1 << "def"
    
  •      w1.close
    
  •      IO.copy_stream(r1, w2)
    
  •      w2.close
    
  •      assert_equal("bcdef", r2.read)
    
  •    }
    
  •  }
    
  •  with_pipe {|r, w|
    
  •    IO.copy_stream("src", w, 1, 1)
    
  •    w.close
    
  •    assert_equal(content[1,1], r.read)
    
  •  }
    
  •  bigcontent = "abc" * 123456
    
  •  File.open("bigsrc", "w") {|f| f << bigcontent }
    
  •  IO.copy_stream("bigsrc", "bigdst")
    
  •  assert_equal(bigcontent, File.read("bigdst"))
    
  •  File.unlink("bigdst")
    
  •  IO.copy_stream("bigsrc", "bigdst", nil, 100)
    
  •  assert_equal(bigcontent[100..-1], File.read("bigdst"))
    
  •  File.unlink("bigdst")
    
  •  IO.copy_stream("bigsrc", "bigdst", 30000, 100)
    
  •  assert_equal(bigcontent[100, 30000], File.read("bigdst"))
    
  •  File.open("bigsrc") {|f|
    
  •    assert_equal(0, f.pos)
    
  •    IO.copy_stream(f, "bigdst", nil, 10)
    
  •    assert_equal(bigcontent[10..-1], File.read("bigdst"))
    
  •    assert_equal(0, f.pos)
    
  •    IO.copy_stream(f, "bigdst", 40, 30)
    
  •    assert_equal(bigcontent[30, 40], File.read("bigdst"))
    
  •    assert_equal(0, f.pos)
    
  •  }
    
  •  with_pipe {|r, w|
    
  •    w.close
    
  •    assert_raise(IOError) { IO.copy_stream("src", w) }
    
  •  }
    
  • }
  • end
  • def with_socketpair
  • s1, s2 = UNIXSocket.pair
  • begin
  •  yield s1, s2
    
  • ensure
  •  s1.close unless s1.closed?
    
  •  s2.close unless s2.closed?
    
  • end
  • end
  • def test_copy_stream_socket
  • Dir.mktmpdir {|d|
  •  Dir.chdir d
    
  •  content = "foobar"
    
  •  File.open("src", "w") {|f| f << content }
    
  •  with_socketpair {|s1, s2|
    
  •    IO.copy_stream("src", s1)
    
  •    s1.close
    
  •    assert_equal(content, s2.read)
    
  •  }
    
  •  bigcontent = "abc" * 123456
    
  •  File.open("bigsrc", "w") {|f| f << bigcontent }
    
  •  with_socketpair {|s1, s2|
    
  •    t = Thread.new { s2.read }
    
  •    IO.copy_stream("bigsrc", s1)
    
  •    s1.close
    
  •    result = t.value
    
  •    assert_equal(bigcontent, result)
    
  •  }
    
  •  with_socketpair {|s1, s2|
    
  •    t = Thread.new { s2.read }
    
  •    IO.copy_stream("bigsrc", s1, 10000)
    
  •    s1.close
    
  •    result = t.value
    
  •    assert_equal(bigcontent[0,10000], result)
    
  •  }
    
  •  File.open("bigsrc") {|f|
    
  •    assert_equal(0, f.pos)
    
  •    with_socketpair {|s1, s2|
    
  •      t = Thread.new { s2.read }
    
  •      IO.copy_stream(f, s1, nil, 100)
    
  •      assert_equal(0, f.pos)
    
  •      s1.close
    
  •      result = t.value
    
  •      assert_equal(bigcontent[100..-1], result)
    
  •    }
    
  •  }
    
  •  File.open("bigsrc") {|f|
    
  •    assert_equal(bigcontent[0,100], f.read(100))
    
  •    assert_equal(100, f.pos)
    
  •    with_socketpair {|s1, s2|
    
  •      t = Thread.new { s2.read }
    
  •      IO.copy_stream(f, s1)
    
  •      assert_equal(bigcontent.length, f.pos)
    
  •      s1.close
    
  •      result = t.value
    
  •      assert_equal(bigcontent[100..-1], result)
    
  •    }
    
  •  }
    
  •  megacontent = "abc" * 1234567
    
  •  File.open("megasrc", "w") {|f| f << megacontent }
    
  •  with_socketpair {|s1, s2|
    
  •    t = Thread.new { s2.read }
    
  •    s1.nonblock = true
    
  •    IO.copy_stream("megasrc", s1)
    
  •    s1.close
    
  •    result = t.value
    
  •    assert_equal(megacontent, result)
    
  •  }
    
  •  with_pipe {|r1, w1|
    
  •    with_pipe {|r2, w2|
    
  •      t1 = Thread.new { w1 << megacontent; w1.close }
    
  •      t2 = Thread.new { r2.read }
    
  •      r1.nonblock = true
    
  •      w2.nonblock = true
    
  •      IO.copy_stream(r1, w2)
    
  •      w2.close
    
  •      t1.join
    
  •      assert_equal(megacontent, t2.value)
    
  •    }
    
  •  }
    
  •  with_pipe {|r1, w1|
    
  •    with_pipe {|r2, w2|
    
  •      t1 = Thread.new { w1 << megacontent; w1.close }
    
  •      t2 = Thread.new { r2.read }
    
  •      IO.copy_stream(r1, w2)
    
  •      w2.close
    
  •      t1.join
    
  •      assert_equal(megacontent, t2.value)
    
  •    }
    
  •  }
    
  •  with_pipe {|r, w|
    
  •    t = Thread.new { r.read }
    
  •    IO.copy_stream("megasrc", w)
    
  •    w.close
    
  •    assert_equal(megacontent, t.value)
    
  •  }
    
  • }
  • end
    end

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:33843] IO.copy_stream”
on Mon, 18 Feb 2008 21:52:22 +0900, Tanaka A. [email protected]
writes:

|IO.copy_stream e$B$r$D$1$k$N$O$I$&$G$7$g$&$+!#e(B

e$B$=$N$h$&$J:rF|$rF3F~$9$k$3$H$KH?BP$O$7$^$;$s!#e(B
e$B:NMQ$9$kA0$KL>A0$K4X$9$k5DO@$,I,MW$@$H;W$$$^$9!#e(B

In article [email protected],
Yukihiro M. [email protected] writes:

|IO.copy_stream e$B$r$D$1$k$N$O$I$&$G$7$g$&$+!#e(B

e$B$=$N$h$&$J:rF|$rF3F~$9$k$3$H$KH?BP$O$7$^$;$s!#e(B
e$B:NMQ$9$kA0$KL>A0$K4X$9$k5DO@$,I,MW$@$H;W$$$^$9!#e(B

e$B$?$7$+$K!"%U%!%$%kL>$r<u$1IU$1$k$N$Ke(B stream e$B$O$A$g$C$HJQ$H$Oe(B
e$B;W$&$N$G!"e(BIO.copy e$B$C$F$$$&$N$O$I$&$G$9$+$M$'!#e(B

ftools e$B$,e(B File.copy e$B$rDs6!$7$F$$$?$N$G=E$J$C$F$$$J$/$b$J$$$He(B
e$B$$$&$N$O$“$j$^$9$,!#$^$!!”$=$s$J$K:$$i$J$$$+$J!#e(B

e$B$“$H$OK\BN$8$c$J$/$F3HD%%i%$%V%i%j$K!”$H$$$&OC$b$"$C$F$$$$$+e(B
e$B$b$7$l$^$;$s!#e(B

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:33866] Re: IO.copy_stream”
on Tue, 19 Feb 2008 23:29:43 +0900, Tanaka A. [email protected]
writes:
|> |IO.copy_stream e$B$r$D$1$k$N$O$I$&$G$7$g$&$+!#e(B

|> e$B:NMQ$9$kA0$KL>A0$K4X$9$k5DO@$,I,MW$@$H;W$$$^$9!#e(B
|
|e$B$?$7$+$K!"%U%!%$%kL>$r<u$1IU$1$k$N$Ke(B stream e$B$O$A$g$C$HJQ$H$Oe(B
|e$B;W$&$N$G!"e(BIO.copy e$B$C$F$$$&$N$O$I$&$G$9$+$M$'!#e(B

IO.copye$B$@$He(BIOe$B%*%V%8%'%/%H$r%3%T!<$9$k$$?$$$KJ9$3$($J$/$b$Je(B
e$B$$$G$9$M$(!#C1$J$ke(Bcopye$B$h$j$Oe(Bcopy_streame$B$NJ}$,9%$
$G$9!#$J$se(B
e$B$i$+$NJ}K!$G%G!<%?$r%3%T!<$9$k$H$+!"FbMF$r%3%T!<$9$k$H$+$rL@e(B
e$B<($7$?$$$b$N$G$9!#e(B

|e$B$“$H$OK\BN$8$c$J$/$F3HD%%i%$%V%i%j$K!”$H$$$&OC$b$"$C$F$$$$$+e(B
|e$B$b$7$l$^$;$s!#e(B

e$BK\BN$G$$$$$s$8$c$J$$$G$7$g$&$+!#e(B

ÚÓ ò¹ìԪǪ¹¡£

IO#copy ªÈå몦ªÎªÏª¤ª«ª¬ªÇª·ªçª¦ª«¡£

ÖǪ¨ªÐ

a = File.open(‘a.txt’)
b = File.open(‘b.txt’, ‘w’)
b.copy(a)

ªÇª¹¡£

In article [email protected],
Yukihiro M. [email protected] writes:

IO.copye$B$@$He(BIOe$B%*%V%8%'%/%H$r%3%T!<$9$k$$?$$$KJ9$3$($J$/$b$Je(B
e$B$$$G$9$M$(!#C1$J$ke(Bcopye$B$h$j$Oe(Bcopy_streame$B$NJ}$,9%$
$G$9!#$J$se(B
e$B$i$+$NJ}K!$G%G!<%?$r%3%T!<$9$k$H$+!"FbMF$r%3%T!<$9$k$H$+$rL@e(B
e$B<($7$?$$$b$N$G$9!#e(B

e$B%G!<%?$H$9$k$He(B IO.copy_data e$B$G$9$,!"$3$l$O$A$g$C$H%J%K$G$9$+e(B
e$B$M!#%3%s%T%e!<%?$G07$&$N$O$@$$$?$$%G!<%?$G$9$7!#e(B

e$B$"$H$O0J2<$,=P$^$7$?$,!"8D?ME*$J463P$H$7$F$Oe(B IO.copy_stream
e$B$G$=$N<!$,e(B IO.copy_content e$B$+$J$!!#e(B

IO.copy_stream
IO.copy_content
IO.copy
IO#copy

IO#copy e$B$K$D$$$F$O!“%3%T!<85$K%U%!%$%kL>$r;H$($J$/$J$kE@$,e(B
e$B$A$g$C$H1F6A$,$”$j$^$9!#Bhe(B4e$B0z?te(B offset
e$B$KCM$r;XDj$7$?>l9g%U%!e(B
e$B%$%k%%U%;%C%H$rF0$+$5$J$$$H$$$&;EMM$K$7$F$$$k$s$G$9$,!"%U%!e(B
e$B%$%kL>$r;XDj$7$?>l9g%U%!%$%k%
%U%;%C%H$O30$K8+$($J$$$N$Ge(B
e$B<BAu$H$7$F$OF0$+$7$F$bF0$+$5$J$/$F$b4X78$"$j$^$;$s!#$=$N<+M3e(B
e$BEY$K$h$jF0$+$5$J$$<BAu$,L5M}$J;~$G$bF0:n$9$k$h$&$K$J$C$F$$$ke(B
e$B$N$G$9$,$=$N:Y9)$,L5M}$K$J$j$^$9!#$^$!!"F0$+$9$+$I$&$+$re(B
offset e$B$8$c$J$/$FJL$K;XDj$9$k$h$&$K$9$l$P2r7h$O$G$-$^$9$,!#e(B

e$B$3$s$K$A$O!#%4%H%&$H?=$7$^$9!#e(B

|> e$B:NMQ$9$kA0$KL>A0$K4X$9$k5DO@$,I,MW$@$H;W$$$^$9!#e(B
|
|e$B$?$7$+$K!"%U%!%$%kL>$r<u$1IU$1$k$N$Ke(B stream e$B$O$A$g$C$HJQ$H$Oe(B
|e$B;W$&$N$G!"e(BIO.copy e$B$C$F$$$&$N$O$I$&$G$9$+$M$’!#e(B

IO.copye$B$@$He(BIOe$B%*%V%8%’%/%H$r%3%T!<$9$k$$?$$$KJ9$3$($J$/$b$Je(B
e$B$$$G$9$M$(!#C1$J$ke(Bcopye$B$h$j$Oe(Bcopy_streame$B$NJ}$,9%$
$G$9!#$J$se(B
e$B$i$+$NJ}K!$G%G!<%?$r%3%T!<$9$k$H$+!"FbMF$r%3%T!<$9$k$H$+$rL@e(B
e$B<($7$?$$$b$N$G$9!#e(B

IOe$B%%V%8%’%/%H!J%U%!%$%kL>!K$N;X$7<($9@h!JFbMF!K$r%3%T!<$9$k$N$G!"e(B
IO.copy_content e$B$H$+8D?ME
$K$OJ,$+$j$d$9$$$+$J$!$H;W$C$?$j$7$^$9!#e(B

e$B$$$+$,$G$7$g$&$+!#e(B