io_splice 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.1.0.GIT
4
+ DEF_VER=v2.0.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -188,7 +188,10 @@ all:: test
188
188
 
189
189
  build: $(ext)
190
190
  test: test-unit
191
- test-unit: build
192
- $(RUBY) -I lib:ext/io_splice test/test_io_splice.rb
193
191
 
194
- .PHONY: .FORCE-GIT-VERSION-FILE doc manifest man test
192
+ test_unit := $(wildcard test/test_*.rb)
193
+ $(test_unit): build
194
+ $(RUBY) -I lib:ext/io_splice $@
195
+ test-unit: $(test_unit)
196
+
197
+ .PHONY: .FORCE-GIT-VERSION-FILE doc manifest man test $(test_unit)
data/LICENSE CHANGED
@@ -3,8 +3,8 @@ revision control for names and email addresses of all of them.
3
3
 
4
4
  You can redistribute it and/or modify it under the terms of the GNU
5
5
  Lesser General Public License as published by the Free Software Foundation,
6
- version 3.0 {LGPLv3}[http://www.gnu.org/licenses/lgpl-3.0.txt] (see
7
- link:COPYING).
6
+ version 2.1 or later {LGPLv2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt]
7
+ (see link:COPYING).
8
8
 
9
9
  io_splice is distributed in the hope that it will be useful, but WITHOUT
10
10
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
data/Rakefile CHANGED
@@ -137,14 +137,14 @@ task :raa_update do
137
137
  :name => s.name,
138
138
  :short_description => s.summary,
139
139
  :version => s.version.to_s,
140
- :status => 'experimental',
140
+ :status => 'stable',
141
141
  :owner => s.authors.first,
142
142
  :email => s.email,
143
143
  :category_major => 'Library',
144
144
  :category_minor => 'System',
145
145
  :url => s.homepage,
146
146
  :download => 'http://rubyforge.org/frs/?group_id=5626',
147
- :license => 'LGPL', # LGPLv3, actually, but RAA is ancient...
147
+ :license => 'LGPL',
148
148
  :description_style => 'Plain',
149
149
  :description => desc,
150
150
  :pass => password,
@@ -10,28 +10,4 @@ require 'io/splice'
10
10
  usage = "#$0 SOURCE DEST"
11
11
  source = ARGV.shift or abort usage
12
12
  dest = ARGV.shift or abort usage
13
-
14
- source = File.open(source, 'rb')
15
- dest = File.open(dest, 'wb')
16
- source_fd, dest_fd = source.fileno, dest.fileno
17
-
18
- # We use a pipe as a ring buffer in kernel space.
19
- # pipes may store up to IO::Splice::PIPE_CAPA bytes
20
- pipe = IO.pipe
21
- rfd, wfd = pipe.map { |io| io.fileno }
22
-
23
- begin
24
- nread = begin
25
- # first pull as many bytes as possible into the pipe
26
- IO.splice(source_fd, nil, wfd, nil, IO::Splice::PIPE_CAPA, 0)
27
- rescue EOFError
28
- break
29
- end
30
-
31
- # now move the contents of the pipe buffer into the destination file
32
- # the copied data never enters userspace
33
- nwritten = IO.splice(rfd, nil, dest_fd, nil, nread, 0)
34
-
35
- nwritten == nread or
36
- abort "short splice to destination file: #{nwritten} != #{nread}"
37
- end while true
13
+ IO::Splice.copy_stream(source, dest)
@@ -11,21 +11,17 @@ $stdin.stat.pipe? or abort "stdin must be a pipe"
11
11
  $stdout.stat.pipe? or abort "stdout must be a pipe"
12
12
 
13
13
  dest = File.open(dest, 'wb')
14
- out_fd = dest.fileno
15
-
16
- stdin_fd = $stdin.fileno
17
- stdout_fd = $stdout.fileno
18
14
 
19
15
  begin
20
16
  nread = begin
21
17
  # "copy" data from stdin to stdout, without consuming stdin
22
- IO.tee(stdin_fd, stdout_fd, IO::Splice::PIPE_CAPA, 0)
18
+ IO.tee($stdin, $stdout, IO::Splice::PIPE_CAPA, 0)
23
19
  rescue EOFError
24
20
  break
25
21
  end
26
22
 
27
23
  # sends data to the file, consumes stdin
28
- nwritten = IO.splice(stdin_fd, nil, out_fd, nil, nread, 0)
24
+ nwritten = IO.splice($stdin, nil, dest, nil, nread, 0)
29
25
 
30
26
  nwritten == nread or
31
27
  abort "short splice to file: #{nwritten} != #{nread}"
@@ -4,6 +4,8 @@ $CPPFLAGS << ' -D_GNU_SOURCE=1'
4
4
  have_func('splice', %w(fcntl.h)) or abort 'splice(2) not defined'
5
5
  have_func('tee', %w(fcntl.h)) or abort 'tee(2) not defined'
6
6
  have_func('rb_thread_blocking_region')
7
+ have_macro('F_GETPIPE_SZ', %w(fcntl.h))
8
+ have_macro('F_SETPIPE_SZ', %w(fcntl.h))
7
9
 
8
10
  dir_config('io_splice')
9
11
  create_makefile('io_splice_ext')
@@ -4,6 +4,7 @@
4
4
  #else
5
5
  # include "rubyio.h"
6
6
  #endif
7
+ #include <errno.h>
7
8
  #include <fcntl.h>
8
9
  #include <assert.h>
9
10
  #include <sys/uio.h>
@@ -225,7 +226,7 @@ static VALUE my_tee(VALUE self,
225
226
 
226
227
  struct vmsplice_args {
227
228
  int fd;
228
- struct iovec *iov;
229
+ struct iovec *iov;
229
230
  unsigned long nr_segs;
230
231
  unsigned flags;
231
232
  };
@@ -243,7 +244,6 @@ do { \
243
244
  VALUE *cur; \
244
245
  struct iovec *tmp; \
245
246
  long n; \
246
- Check_Type(ary, T_ARRAY); \
247
247
  cur = RARRAY_PTR(ary); \
248
248
  n = RARRAY_LEN(ary); \
249
249
  if (n > IOV_MAX) \
@@ -292,6 +292,7 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
292
292
  /*
293
293
  * call-seq:
294
294
  * IO.vmsplice(fd, string_array, flags) => integer
295
+ * IO.vmsplice(fd, string, flags) => integer
295
296
  *
296
297
  * Transfers an array of strings into the pipe descriptor given by fd.
297
298
  * +fd+ must be the writable end of a pipe.
@@ -308,7 +309,24 @@ static VALUE my_vmsplice(VALUE self, VALUE fd, VALUE data, VALUE flags)
308
309
  ssize_t left;
309
310
  struct vmsplice_args a;
310
311
 
311
- ARY2IOVEC(a.iov, a.nr_segs, left, data);
312
+ switch (TYPE(data)) {
313
+ case T_STRING: {
314
+ struct iovec iov;
315
+
316
+ iov.iov_base = RSTRING_PTR(data);
317
+ iov.iov_len = (size_t)(left = (ssize_t)RSTRING_LEN(data));
318
+ a.iov = &iov;
319
+ a.nr_segs = 1;
320
+ }
321
+ break;
322
+ case T_ARRAY:
323
+ ARY2IOVEC(a.iov, a.nr_segs, left, data);
324
+ break;
325
+ default:
326
+ rb_raise(rb_eTypeError, "wrong argument type %s "
327
+ "(expected a String or Array of strings)",
328
+ rb_obj_classname(data));
329
+ }
312
330
  a.fd = my_fileno(fd);
313
331
  a.flags = NUM2UINT(flags);
314
332
 
@@ -353,8 +371,8 @@ void Init_io_splice_ext(void)
353
371
 
354
372
  /*
355
373
  * Attempt to move pages instead of copying. This is only a hint
356
- * and support for it was removed in Linux 2.6.21 and has not been
357
- * readded as of 2.6.30.
374
+ * and support for it was removed in Linux 2.6.21. It will be
375
+ * re-added for FUSE filesystems only in Linux 2.6.35.
358
376
  */
359
377
  rb_define_const(mSplice, "F_MOVE", UINT2NUM(SPLICE_F_MOVE));
360
378
 
@@ -384,6 +402,15 @@ void Init_io_splice_ext(void)
384
402
  */
385
403
  rb_define_const(mSplice, "F_GIFT", UINT2NUM(SPLICE_F_GIFT));
386
404
 
405
+ #ifdef F_GETPIPE_SZ
406
+ /* :nodoc: */
407
+ rb_define_const(mSplice, "F_GETPIPE_SZ", UINT2NUM(F_GETPIPE_SZ));
408
+ #endif
409
+ #ifdef F_SETPIPE_SZ
410
+ /* :nodoc: */
411
+ rb_define_const(mSplice, "F_SETPIPE_SZ", UINT2NUM(F_SETPIPE_SZ));
412
+ #endif
413
+
387
414
  /*
388
415
  * The maximum size of an atomic write to a pipe
389
416
  * POSIX requires this to be at least 512 bytes.
data/io_splice.gemspec CHANGED
@@ -34,5 +34,5 @@ Gem::Specification.new do |s|
34
34
 
35
35
  s.test_files = test_files
36
36
 
37
- # s.licenses = %w(LGPLv3) # accessor not compatible with older RubyGems
37
+ # s.licenses = %w(LGPL) # accessor not compatible with older RubyGems
38
38
  end
data/lib/io/splice.rb CHANGED
@@ -5,13 +5,16 @@ class IO
5
5
 
6
6
  module Splice
7
7
 
8
- # the version of IO::Splice, currently 0.1.0
9
- VERSION = '1.0.0'
8
+ # the version of IO::Splice, currently 2.0.0
9
+ VERSION = '2.0.0'
10
10
 
11
- # The maximum capacity of the pipe in bytes.
11
+ # The maximum default capacity of the pipe in bytes.
12
12
  # Under stock Linux, this is 65536 bytes as of 2.6.11, and 4096 before
13
13
  # We detect this at runtime as it is easy to recompile the kernel
14
14
  # and set a new value.
15
+ # Starting with Linux 2.6.35, pipe capacity will be tunable
16
+ # and this will only represent the default capacity of a
17
+ # newly-created pipe.
15
18
  PIPE_CAPA = begin
16
19
  rd, wr = IO.pipe
17
20
  buf = ' ' * PIPE_BUF
@@ -26,5 +29,69 @@ class IO
26
29
  n
27
30
  end
28
31
 
32
+ # copies the contents of the IO object given by +src+ to +dst+
33
+ # If len is specified, then only len bytes are copied. Otherwise
34
+ # the copy will be until EOF is reached on the +src+.
35
+ # +src+ and +dst+ must be IO objects or respond to +to_io+
36
+ def self.copy_stream(src, dst, len = nil, src_offset = nil)
37
+ src.kind_of?(String) and src = File.open(src, 'rb')
38
+ dst.kind_of?(String) and dst = File.open(dst, 'wb')
39
+ src, dst = src.to_io, dst.to_io
40
+ rv = len
41
+ src.sysseek(src_offset) if src_offset
42
+ if src.stat.pipe? || dst.stat.pipe?
43
+ if len
44
+ while len > 0
45
+ nr = len > PIPE_CAPA ? PIPE_CAPA : len
46
+ nr = IO.splice(src, nil, dst, nil, nr, F_MOVE)
47
+ if nr == 0
48
+ raise EOFError, "unexpected EOF with #{len} bytes left"
49
+ end
50
+ len -= nr
51
+ end
52
+ else
53
+ rv = 0
54
+ begin
55
+ nr = IO.splice(src, nil, dst, nil, PIPE_CAPA, F_MOVE)
56
+ rv += nr
57
+ rescue EOFError
58
+ break
59
+ end while true
60
+ end
61
+ else
62
+ begin
63
+ r, w = IO.pipe
64
+ if len
65
+ while len > 0
66
+ nr = len > PIPE_CAPA ? PIPE_CAPA : len
67
+ nr_src = copy_stream(src, w, nr)
68
+ nr_src == nr or
69
+ raise RuntimeError, "short splice from: #{nr_src} != #{nr}"
70
+ nr_dst = copy_stream(r, dst, nr)
71
+ nr_dst == nr or
72
+ raise RuntimeError, "short splice to: #{nr_dst} != #{nr}"
73
+ len -= nr
74
+ end
75
+ else
76
+ rv = 0
77
+ begin
78
+ nr = IO.splice(src, nil, w, nil, PIPE_CAPA, F_MOVE)
79
+ rv += nr
80
+ begin
81
+ nr -= IO.splice(r, nil, dst, nil, nr, F_MOVE)
82
+ end while nr > 0
83
+ rescue EOFError
84
+ break
85
+ end while true
86
+ end
87
+ ensure
88
+ r.close
89
+ w.close
90
+ end
91
+ end
92
+
93
+ rv
94
+ end
95
+
29
96
  end
30
97
  end
@@ -213,6 +213,12 @@ class Test_IO_Splice < Test::Unit::TestCase
213
213
  assert_equal data.join(''), r.readpartial(16384)
214
214
  end
215
215
 
216
+ def test_vmsplice_string
217
+ r, w = IO.pipe
218
+ assert_equal 5, IO.vmsplice(w, 'hello', 0)
219
+ assert_equal 'hello', r.read(5)
220
+ end
221
+
216
222
  def test_vmsplice_array_io
217
223
  data = %w(hello world how are you today)
218
224
  r, w = IO.pipe
@@ -272,4 +278,57 @@ class Test_IO_Splice < Test::Unit::TestCase
272
278
  assert IO::Splice::PIPE_CAPA >= IO::Splice::PIPE_BUF
273
279
  end
274
280
 
281
+ def test_splice_copy_stream_file_to_file_small
282
+ a, b = Tempfile.new('a'), Tempfile.new('b')
283
+ a.syswrite 'hello world'
284
+ a.sysseek(0)
285
+ IO::Splice.copy_stream(a, b)
286
+ b.rewind
287
+ assert_equal 'hello world', b.read
288
+ end
289
+
290
+ def test_splice_copy_stream_file_to_file_big
291
+ buf = ('ab' * IO::Splice::PIPE_CAPA) + 'hi'
292
+ a, b = Tempfile.new('a'), Tempfile.new('b')
293
+ a.syswrite buf
294
+ a.sysseek(0)
295
+ IO::Splice.copy_stream(a, b)
296
+ b.rewind
297
+ assert_equal buf, b.read
298
+ end
299
+
300
+ def test_splice_copy_stream_file_to_file_len
301
+ a, b = Tempfile.new('a'), Tempfile.new('b')
302
+ a.syswrite 'hello world'
303
+ a.sysseek(0)
304
+ IO::Splice.copy_stream(a, b, 5)
305
+ b.rewind
306
+ assert_equal 'hello', b.read
307
+ end
308
+
309
+ def test_splice_copy_stream_pipe_to_file_len
310
+ a = Tempfile.new('a')
311
+ r, w = IO.pipe
312
+ w.syswrite 'hello world'
313
+ IO::Splice.copy_stream(r, a, 5)
314
+ a.rewind
315
+ assert_equal 'hello', a.read
316
+ end
317
+
318
+ def test_splice_copy_stream_paths
319
+ a = Tempfile.new('a')
320
+ b = Tempfile.new('a')
321
+ a.syswrite('hello world')
322
+ IO::Splice.copy_stream(a.path, b.path, 5)
323
+ assert_equal 'hello', b.read
324
+ end
325
+
326
+ def test_splice_copy_stream_src_offset
327
+ a = Tempfile.new('a')
328
+ b = Tempfile.new('a')
329
+ a.syswrite('hello world')
330
+ IO::Splice.copy_stream(a.path, b.path, 5, 6)
331
+ assert_equal 'world', b.read
332
+ end
333
+
275
334
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io_splice
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - io_splice hackers
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-05-27 00:00:00 +00:00
12
+ date: 2010-06-05 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15