# -*- encoding: binary -*- require 'io_splice_ext' require 'io/wait' module IO::Splice # 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 def self.need_open?(obj) # :nodoc: return false if obj.respond_to?(:to_io) obj.respond_to?(:to_path) || obj.kind_of?(String) end # copies the contents of the IO object given by +src+ to +dst+ # If +len+ is specified, then only +len+ bytes are copied and # +EOFError+ is raised if fewer than +len+ bytes could be 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+ # # This is nearly a drop-in replacement for IO.copy_stream (in Ruby 1.9) # but does not take into account userspace I/O buffers nor IO-like # objects with no underlying file descriptor (e.g. StringIO). def self.copy_stream(src, dst, len = nil, src_offset = nil) close = [] need_open?(src) and close << (src = File.open(src)) need_open?(dst) and close << (dst = File.open(dst, "w")) rv = len src, dst = src.to_io, dst.to_io if src.stat.pipe? || dst.stat.pipe? if len len -= full(src, dst, len, src_offset) until len == 0 else rv = 0 while n = partial(src, dst, PIPE_CAPA, src_offset) rv += n src_offset += n if src_offset end end else r, w = tmp = IO.pipe close.concat(tmp) if len while len != 0 && n = partial(src, w, len, src_offset) src_offset += n if src_offset len -= full(r, dst, n, nil) end else rv = 0 while n = partial(src, w, PIPE_CAPA, src_offset) src_offset += n if src_offset rv += full(r, dst, n, nil) end 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+ # Raises +EOFError+ if end of file is reached. # bytes. Returns the number of bytes actually spliced (always +len+) # unless +src+ does not have +len+ bytes to read. # # Do not use this method to splice a socket +src+ into a pipe +dst+ # unless there is another process or native thread doing a blocking # read on the other end of the +dst+ pipe. # # This method is safe for splicing a pipe +src+ into any type of +dst+ IO. def self.full(src, dst, len, src_offset) IO.splice(src, src_offset, dst, nil, len, F_MOVE | WAITALL) 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 def self.partial(src, dst, len, src_offset) case rv = IO.trysplice(src, src_offset, dst, nil, len, F_MOVE) when :EAGAIN src.to_io.wait IO.select(nil, [dst]) when Integer return rv else return nil end while true end end if (! defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") && RUBY_VERSION.to_f <= 1.8 require "io/splice/mri_18" end