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