io_splice 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -3
- data/LICENSE +2 -2
- data/Rakefile +2 -2
- data/examples/splice-cp.rb +1 -25
- data/examples/splice-tee.rb +2 -6
- data/ext/io_splice/extconf.rb +2 -0
- data/ext/io_splice/io_splice_ext.c +32 -5
- data/io_splice.gemspec +1 -1
- data/lib/io/splice.rb +70 -3
- data/test/test_io_splice.rb +59 -0
- metadata +2 -2
data/GIT-VERSION-GEN
CHANGED
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
|
-
|
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
|
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 => '
|
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',
|
147
|
+
:license => 'LGPL',
|
148
148
|
:description_style => 'Plain',
|
149
149
|
:description => desc,
|
150
150
|
:pass => password,
|
data/examples/splice-cp.rb
CHANGED
@@ -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)
|
data/examples/splice-tee.rb
CHANGED
@@ -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(
|
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(
|
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}"
|
data/ext/io_splice/extconf.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
357
|
-
|
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
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.
|
9
|
-
VERSION = '
|
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
|
data/test/test_io_splice.rb
CHANGED
@@ -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:
|
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
|
12
|
+
date: 2010-06-05 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|