copy_stream: File.open encoding doesn't matter
[ruby_io_splice.git] / lib / io / splice.rb
blob0e332dd3f1799b33390f959b9e189f430f7d4723
1 # -*- encoding: binary -*-
2 require 'io_splice_ext'
4 module IO::Splice
6   # the version of IO::Splice, currently 2.2.0
7   VERSION = '2.2.0'
9   # The maximum default capacity of the pipe in bytes.
10   # Under stock Linux, this is 65536 bytes as of 2.6.11, and 4096 before
11   # We detect this at runtime as it is easy to recompile the kernel
12   # and set a new value.
13   # Starting with Linux 2.6.35, pipe capacity will be tunable
14   # and this will only represent the default capacity of a
15   # newly-created pipe.
16   PIPE_CAPA = begin
17     rd, wr = IO.pipe
18     buf = ' ' * PIPE_BUF
19     n = 0
20     begin
21       n += wr.write_nonblock(buf)
22     rescue Errno::EAGAIN
23       break
24     end while true
25     wr.close
26     rd.close
27     n
28   end
30   # copies the contents of the IO object given by +src+ to +dst+
31   # If +len+ is specified, then only +len+ bytes are copied and
32   # +EOFError+ is raised if fewer than +len+ bytes could be copied.
33   # Otherwise the copy will be until EOF is reached on the +src+.
34   # +src+ and +dst+ must be IO objects or respond to +to_io+
35   def self.copy_stream(src, dst, len = nil, src_offset = nil)
36     close = []
37     src.kind_of?(String) and close << (src = File.open(src))
38     dst.kind_of?(String) and close << (dst = File.open(dst, "w"))
39     src, dst = src.to_io, dst.to_io
40     rv = len
41     src.sysseek(src_offset) if src_offset
42     select_args = selectable(src, dst)
44     if src.stat.pipe? || dst.stat.pipe?
45       if len
46         len -= full(src, dst, len, select_args) until len == 0
47       else
48         rv = 0
49         while n = partial(src, dst, PIPE_CAPA, select_args)
50           rv += n
51         end
52       end
53     else
54       r, w = tmp = IO.pipe
55       close.concat(tmp)
56       if len
57         while len != 0 && n = partial(src, w, len, select_args)
58           len -= full(r, dst, n, select_args)
59         end
60       else
61         rv = 0
62         while n = partial(src, w, PIPE_CAPA, select_args)
63           rv += full(r, dst, n, select_args)
64         end
65       end
66     end
68     rv
69     ensure
70       close.each { |io| io.close }
71   end
73   # splice the full amount specified from +src+ to +dst+
74   # Either +dst+ or +src+ must be a pipe.  +dst+ and +src+
75   # may BOTH be pipes in Linux 2.6.31 or later.
76   # This will block and wait for IO completion of +len+
77   # Raises +EOFError+ if end of file is reached.
78   # bytes.  Returns the number of bytes actually spliced (always +len+)
79   # The +_select_args+ parameter is reserved for internal use and
80   # may be removed in future versions.  Do not write code that
81   # depends on +_select_args+.
82   def self.full(src, dst, len, _select_args = selectable(src, dst))
83     nr = len
84     while nr > 0
85       n = partial(src, dst, nr, _select_args) or
86                                      raise EOFError, "end of file reached"
87       nr -= n
88     end
89     len
90   end
92   # splice up to +len+ bytes from +src+ to +dst+.
93   # Either +dst+ or +src+ must be a pipe.  +dst+ and +src+
94   # may BOTH be pipes in Linux 2.6.31 or later.
95   # Returns the number of bytes actually spliced.
96   # Like IO#readpartial, this never returns Errno::EAGAIN
97   # The +_select_args+ parameter is reserved for internal use and
98   # may be removed in future versions.  Do not write code that
99   # depends on +_select_args+.
100   def self.partial(src, dst, len, _select_args = selectable(src, dst))
101     begin
102       rv = IO.trysplice(src, nil, dst, nil, len, F_MOVE)
103     end while rv == :EAGAIN and IO.select(*_select_args)
104     rv
105   end
107   # returns an array suitable for splat-ing to IO.select for blocking I/O
108   def self.selectable(src, dst)
109     rv = []
110     src.stat.pipe? or rv[0] = [ src ]
111     dst.stat.pipe? or rv[1] = [ dst ]
112     rv
113   end