# IO.copy\_stream

**URL:** <https://www.ruby-forum.com/t/io-copy-stream/129421>\
**Category:** Development\
**Created:** [February 18, 2008, 1:53pm UTC](https://www.ruby-forum.com/t/io-copy-stream/129421 "2008-02-18T13:53:29Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 18, 2008, 1:53pm UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/1 "2008-02-18T13:53:29Z")

</div>

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](http://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);

# 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 19, 2008, 2:23am UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/2 "2008-02-19T02:23:29Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 19, 2008, 3:30pm UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/3 "2008-02-19T15:30:14Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Yukihiro M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 19, 2008, 7:56pm UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/4 "2008-02-19T19:56:45Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 20, 2008, 5:54am UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/5 "2008-02-20T05:54:23Z")

</div>

ÚÓ ò¹ìÔªÇª¹¡£

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

ÖÇª¨ªÐ

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

ªÇª¹¡£

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 22, 2008, 6:00am UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/6 "2008-02-22T06:00:49Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Yukihiro M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [February 20, 2008, 3:13am UTC](https://www.ruby-forum.com/t/io-copy-stream/129421/7 "2008-02-20T03:13:10Z")

</div>

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
