implement IO::Splice.copy_stream
[ruby_io_splice.git] / lib / io / splice.rb
blobd7065e4941ba9f1cf15c461268aad9257483ae09
1 # -*- encoding: binary -*-
2 require 'io_splice_ext'
4 class IO
6   module Splice
8     # the version of IO::Splice, currently 0.1.0
9     VERSION = '1.0.0'
11     # The maximum capacity of the pipe in bytes.
12     # Under stock Linux, this is 65536 bytes as of 2.6.11, and 4096 before
13     # We detect this at runtime as it is easy to recompile the kernel
14     # and set a new value.
15     PIPE_CAPA = begin
16       rd, wr = IO.pipe
17       buf = ' ' * PIPE_BUF
18       n = 0
19       begin
20         n += wr.write_nonblock(buf)
21       rescue Errno::EAGAIN
22         break
23       end while true
24       wr.close
25       rd.close
26       n
27     end
29     # copies the contents of the IO object given by +src+ to +dst+
30     # If len is specified, then only len bytes are copied.  Otherwise
31     # the copy will be until EOF is reached on the +src+.
32     # +src+ and +dst+ must be IO objects or respond to +to_io+
33     def self.copy_stream(src, dst, len = nil)
34       src, dst = src.to_io, dst.to_io
35       rv = len
36       if src.stat.pipe? || dst.stat.pipe?
37         if len
38           while len > 0
39             nr = len > PIPE_CAPA ? PIPE_CAPA : len
40             nr = IO.splice(src, nil, dst, nil, nr, 0)
41             if nr == 0
42               raise EOFError, "unexpected EOF with #{len} bytes left"
43             end
44             len -= nr
45           end
46         else
47           rv = 0
48           begin
49             nr = IO.splice(src, nil, dst, nil, PIPE_CAPA, 0)
50             rv += nr
51           rescue EOFError
52             break
53           end while true
54         end
55       else
56         begin
57           r, w = IO.pipe
58           if len
59             while len > 0
60               nr = len > PIPE_CAPA ? PIPE_CAPA : len
61               nr_src = copy_stream(src, w, nr)
62               nr_src == nr or
63                 raise RuntimeError, "short splice from: #{nr_src} != #{nr}"
64               nr_dst = copy_stream(r, dst, nr)
65               nr_dst == nr or
66                 raise RuntimeError, "short splice to: #{nr_dst} != #{nr}"
67               len -= nr
68             end
69           else
70             rv = 0
71             begin
72               nr = IO.splice(src, nil, w, nil, PIPE_CAPA, 0)
73               rv += nr
74               begin
75                 nr -= IO.splice(r, nil, dst, nil, nr, 0)
76               end while nr > 0
77             rescue EOFError
78               break
79             end while true
80           end
81         ensure
82           r.close
83           w.close
84         end
85       end
87       rv
88     end
90   end
91 end