# -*- encoding: binary -*- require 'io_splice_ext' class IO module Splice # the version of IO::Splice, currently 2.2.0 VERSION = '2.2.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