about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-05-01 13:29:51 -0700
committerEric Wong <normalperson@yhbt.net>2011-05-01 13:30:17 -0700
commitb5ec116e331c22c064bee46f63145e325785e33f (patch)
treed51e6693303ba4cc473d13209fc6d27785d3664a
parentb39448e121f8f060c274ee1ee3c473f7b20d92e1 (diff)
downloadruby_io_splice-b5ec116e331c22c064bee46f63145e325785e33f.tar.gz
This is for compatibility with IO.copy_stream and IO#sendfile,
both of which use pread()-semantics when given a source file
offset.   This allows multiple threads/processes to copy from
the same file handle.
-rw-r--r--lib/io/splice.rb22
-rw-r--r--test/test_io_splice.rb10
2 files changed, 21 insertions, 11 deletions
diff --git a/lib/io/splice.rb b/lib/io/splice.rb
index ab8efaa..2dd88b9 100644
--- a/lib/io/splice.rb
+++ b/lib/io/splice.rb
@@ -35,15 +35,14 @@ module IO::Splice
     dst.kind_of?(String) and close << (dst = File.open(dst, "w"))
     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
+        len -= full(src, dst, len, src_offset, select_args) until len == 0
       else
         rv = 0
-        while n = partial(src, dst, PIPE_CAPA, select_args)
+        while n = partial(src, dst, PIPE_CAPA, src_offset, select_args)
           rv += n
         end
       end
@@ -51,13 +50,13 @@ module IO::Splice
       r, w = tmp = IO.pipe
       close.concat(tmp)
       if len
-        while len != 0 && n = partial(src, w, len, select_args)
-          len -= full(r, dst, n, select_args)
+        while len != 0 && n = partial(src, w, len, src_offset, select_args)
+          len -= full(r, dst, n, nil, select_args)
         end
       else
         rv = 0
-        while n = partial(src, w, PIPE_CAPA, select_args)
-          rv += full(r, dst, n, select_args)
+        while n = partial(src, w, PIPE_CAPA, src_offset, select_args)
+          rv += full(r, dst, n, nil, select_args)
         end
       end
     end
@@ -76,10 +75,10 @@ module IO::Splice
   # 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 self.full(src, dst, len, _select_args = selectable(src, dst))
+  def self.full(src, dst, len, src_offset, _select_args = selectable(src, dst))
     nr = len
     while nr > 0
-      n = partial(src, dst, nr, _select_args) or
+      n = partial(src, dst, nr, src_offset, _select_args) or
                                      raise EOFError, "end of file reached"
       nr -= n
     end
@@ -94,9 +93,10 @@ module IO::Splice
   # 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 self.partial(src, dst, len, _select_args = selectable(src, dst))
+  def self.partial(src, dst, len, src_offset,
+                   _select_args = selectable(src, dst))
     begin
-      rv = IO.trysplice(src, nil, dst, nil, len, F_MOVE)
+      rv = IO.trysplice(src, src_offset, dst, nil, len, F_MOVE)
     end while rv == :EAGAIN and IO.select(*_select_args)
     rv
   end
diff --git a/test/test_io_splice.rb b/test/test_io_splice.rb
index eb4faeb..6943bf9 100644
--- a/test/test_io_splice.rb
+++ b/test/test_io_splice.rb
@@ -392,6 +392,16 @@ class Test_IO_Splice < Test::Unit::TestCase
     assert_equal 'world', b.read
   end
 
+  def test_splice_copy_stream_src_offset_unchanged
+    a = Tempfile.new('a')
+    b = Tempfile.new('a')
+    a.syswrite('hello world')
+    assert_equal 0, a.sysseek(0, IO::SEEK_SET)
+    IO::Splice.copy_stream(a, b.path, 5, 6)
+    assert_equal 'world', b.read
+    assert_equal 0, a.sysseek(0, IO::SEEK_CUR)
+  end
+
   def test_copy_stream_nonblock_src
     server = TCPServer.new('127.0.0.1', 0)
     port = server.addr[1]