lib/io/splice.rb in io_splice-2.0.0 vs lib/io/splice.rb in io_splice-2.1.0
- old
+ new
@@ -3,12 +3,12 @@
class IO
module Splice
- # the version of IO::Splice, currently 2.0.0
- VERSION = '2.0.0'
+ # 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.
@@ -31,66 +31,89 @@
# 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 self.copy_stream(src, dst, len = nil, src_offset = nil)
- src.kind_of?(String) and src = File.open(src, 'rb')
- dst.kind_of?(String) and dst = File.open(dst, 'wb')
+ 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
- while len > 0
- nr = len > PIPE_CAPA ? PIPE_CAPA : len
- nr = IO.splice(src, nil, dst, nil, nr, F_MOVE)
- if nr == 0
- raise EOFError, "unexpected EOF with #{len} bytes left"
- end
- len -= nr
- end
+ len -= full(src, dst, len, select_args) until len == 0
else
rv = 0
begin
- nr = IO.splice(src, nil, dst, nil, PIPE_CAPA, F_MOVE)
- rv += nr
+ rv += partial(src, dst, PIPE_CAPA, select_args)
rescue EOFError
break
end while true
end
else
- begin
- r, w = IO.pipe
- if len
- while len > 0
- nr = len > PIPE_CAPA ? PIPE_CAPA : len
- nr_src = copy_stream(src, w, nr)
- nr_src == nr or
- raise RuntimeError, "short splice from: #{nr_src} != #{nr}"
- nr_dst = copy_stream(r, dst, nr)
- nr_dst == nr or
- raise RuntimeError, "short splice to: #{nr_dst} != #{nr}"
- len -= nr
- end
- else
- rv = 0
- begin
- nr = IO.splice(src, nil, w, nil, PIPE_CAPA, F_MOVE)
- rv += nr
- begin
- nr -= IO.splice(r, nil, dst, nil, nr, F_MOVE)
- end while nr > 0
- rescue EOFError
- break
- end while true
+ 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
- ensure
- r.close
- w.close
+ 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