Ruby io_splice 2.0.0
[ruby_io_splice.git] / lib / io / splice.rb
blob2c5f5ac1f04cb078060e58f083c98004e22c3ab8
1 # -*- encoding: binary -*-
2 require 'io_splice_ext'
4 class IO
6   module Splice
8     # the version of IO::Splice, currently 2.0.0
9     VERSION = '2.0.0'
11     # The maximum default 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     # Starting with Linux 2.6.35, pipe capacity will be tunable
16     # and this will only represent the default capacity of a
17     # newly-created pipe.
18     PIPE_CAPA = begin
19       rd, wr = IO.pipe
20       buf = ' ' * PIPE_BUF
21       n = 0
22       begin
23         n += wr.write_nonblock(buf)
24       rescue Errno::EAGAIN
25         break
26       end while true
27       wr.close
28       rd.close
29       n
30     end
32     # copies the contents of the IO object given by +src+ to +dst+
33     # If len is specified, then only len bytes are copied.  Otherwise
34     # the copy will be until EOF is reached on the +src+.
35     # +src+ and +dst+ must be IO objects or respond to +to_io+
36     def self.copy_stream(src, dst, len = nil, src_offset = nil)
37       src.kind_of?(String) and src = File.open(src, 'rb')
38       dst.kind_of?(String) and dst = File.open(dst, 'wb')
39       src, dst = src.to_io, dst.to_io
40       rv = len
41       src.sysseek(src_offset) if src_offset
42       if src.stat.pipe? || dst.stat.pipe?
43         if len
44           while len > 0
45             nr = len > PIPE_CAPA ? PIPE_CAPA : len
46             nr = IO.splice(src, nil, dst, nil, nr, F_MOVE)
47             if nr == 0
48               raise EOFError, "unexpected EOF with #{len} bytes left"
49             end
50             len -= nr
51           end
52         else
53           rv = 0
54           begin
55             nr = IO.splice(src, nil, dst, nil, PIPE_CAPA, F_MOVE)
56             rv += nr
57           rescue EOFError
58             break
59           end while true
60         end
61       else
62         begin
63           r, w = IO.pipe
64           if len
65             while len > 0
66               nr = len > PIPE_CAPA ? PIPE_CAPA : len
67               nr_src = copy_stream(src, w, nr)
68               nr_src == nr or
69                 raise RuntimeError, "short splice from: #{nr_src} != #{nr}"
70               nr_dst = copy_stream(r, dst, nr)
71               nr_dst == nr or
72                 raise RuntimeError, "short splice to: #{nr_dst} != #{nr}"
73               len -= nr
74             end
75           else
76             rv = 0
77             begin
78               nr = IO.splice(src, nil, w, nil, PIPE_CAPA, F_MOVE)
79               rv += nr
80               begin
81                 nr -= IO.splice(r, nil, dst, nil, nr, F_MOVE)
82               end while nr > 0
83             rescue EOFError
84               break
85             end while true
86           end
87         ensure
88           r.close
89           w.close
90         end
91       end
93       rv
94     end
96   end
97 end