io_splice 2.2.0 → 2.2.0.18.g3025
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/.gitignore +2 -0
- data/.wrongdoc.yml +5 -0
- data/GNUmakefile +7 -193
- data/README +2 -2
- data/Rakefile +20 -106
- data/ext/io_splice/io_splice_ext.c +129 -36
- data/io_splice.gemspec +12 -24
- data/lib/io/splice.rb +95 -101
- data/pkg.mk +168 -0
- data/test/test_io_splice.rb +99 -50
- metadata +37 -13
data/.document
CHANGED
data/.gitignore
CHANGED
data/.wrongdoc.yml
ADDED
data/GNUmakefile
CHANGED
@@ -1,200 +1,14 @@
|
|
1
|
-
# use GNU Make to run tests in parallel, and without depending on RubyGems
|
2
1
|
all::
|
3
|
-
RUBY = ruby
|
4
|
-
RAKE = rake
|
5
|
-
GIT_URL = git://git.bogomips.org/ruby_io_splice.git
|
6
|
-
RSYNC = rsync
|
7
2
|
RCOV = rcov
|
8
|
-
|
9
|
-
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
10
|
-
@./GIT-VERSION-GEN
|
11
|
-
-include GIT-VERSION-FILE
|
12
|
-
-include local.mk
|
13
|
-
ifeq ($(DLEXT),) # "so" for Linux
|
14
|
-
DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]')
|
15
|
-
endif
|
16
|
-
ifeq ($(RUBY_VERSION),)
|
17
|
-
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
18
|
-
endif
|
19
|
-
|
20
|
-
install: $(bins)
|
21
|
-
$(prep_setup_rb)
|
22
|
-
$(RM) -r .install-tmp
|
23
|
-
mkdir .install-tmp
|
24
|
-
cp -p bin/* .install-tmp
|
25
|
-
$(RUBY) setup.rb all
|
26
|
-
$(RM) $^
|
27
|
-
mv .install-tmp/* bin/
|
28
|
-
$(RM) -r .install-tmp
|
29
|
-
$(prep_setup_rb)
|
30
|
-
|
31
|
-
setup_rb_files := .config InstalledFiles
|
32
|
-
prep_setup_rb := @-$(RM) $(setup_rb_files);$(MAKE) -C $(ext) clean
|
33
|
-
|
34
|
-
clean:
|
35
|
-
-$(MAKE) -C ext/io_splice clean
|
36
|
-
$(RM) $(setup_rb_files) ext/io_splice/Makefile
|
37
|
-
|
38
|
-
pkg_extra := GIT-VERSION-FILE NEWS ChangeLog
|
39
|
-
manifest: $(pkg_extra)
|
40
|
-
$(RM) .manifest
|
41
|
-
$(MAKE) .manifest
|
42
|
-
|
43
|
-
.manifest:
|
44
|
-
(git ls-files && \
|
45
|
-
for i in $@ $(pkg_extra); \
|
46
|
-
do echo $$i; done) | LC_ALL=C sort > $@+
|
47
|
-
cmp $@+ $@ || mv $@+ $@
|
48
|
-
$(RM) $@+
|
49
|
-
|
50
|
-
NEWS: GIT-VERSION-FILE
|
51
|
-
$(RAKE) -s news_rdoc > $@+
|
52
|
-
mv $@+ $@
|
53
|
-
|
54
|
-
SINCE = 1.0.0
|
55
|
-
ChangeLog: LOG_VERSION = \
|
56
|
-
$(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
|
57
|
-
echo $(GIT_VERSION) || git describe)
|
58
|
-
ifneq ($(SINCE),)
|
59
|
-
ChangeLog: log_range = v$(SINCE)..$(LOG_VERSION)
|
60
|
-
endif
|
61
|
-
ChangeLog: GIT-VERSION-FILE
|
62
|
-
@echo "ChangeLog from $(GIT_URL) ($(log_range))" > $@+
|
63
|
-
@echo >> $@+
|
64
|
-
git log $(log_range) | sed -e 's/^/ /' >> $@+
|
65
|
-
mv $@+ $@
|
66
|
-
|
67
|
-
news_atom := http://bogomips.org/ruby_io_splice/NEWS.atom.xml
|
68
|
-
cgit_atom := http://git.bogomips.org/cgit/ruby_io_splice.git/atom/?h=master
|
69
|
-
atom = <link rel="alternate" title="Atom feed" href="$(1)" \
|
70
|
-
type="application/atom+xml"/>
|
71
|
-
|
72
|
-
# using rdoc 2.5.x + workaround patch here:
|
73
|
-
# rubyforge.org/tracker/index.php?func=detail&aid=28230&group_id=627&atid=2472
|
74
|
-
doc: .document NEWS ChangeLog
|
75
|
-
rdoc -a -t "$(shell sed -ne '1s/^= //p' README)"
|
76
|
-
install -m644 COPYING doc/COPYING
|
77
|
-
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
78
|
-
cd doc && for i in $(base_bins); do \
|
79
|
-
html=$$(echo $$i | sed 's/\.rb/_rb/')_1.html; \
|
80
|
-
sed -e '/"documentation">/r man1/'$$i'.1.html' \
|
81
|
-
< $$html > tmp && mv tmp $$html; done
|
82
|
-
$(RUBY) -i -p -e \
|
83
|
-
'$$_.gsub!("</title>",%q{\&$(call atom,$(cgit_atom))})' \
|
84
|
-
doc/ChangeLog.html
|
85
|
-
$(RUBY) -i -p -e \
|
86
|
-
'$$_.gsub!("</title>",%q{\&$(call atom,$(news_atom))})' \
|
87
|
-
doc/NEWS.html doc/README.html
|
88
|
-
$(RAKE) -s news_atom > doc/NEWS.atom.xml
|
89
|
-
cd doc && ln README.html tmp && mv tmp index.html
|
90
|
-
|
91
|
-
latest: NEWS
|
92
|
-
@awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' $<
|
93
|
-
|
94
|
-
# publishes docs to http://bogomips.org/ruby_io_splice/,
|
95
|
-
publish_doc:
|
96
|
-
-git set-file-times
|
97
|
-
$(RM) -r doc ChangeLog NEWS
|
98
|
-
$(MAKE) doc LOG_VERSION=$(shell git tag -l | tail -1)
|
99
|
-
awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' \
|
100
|
-
NEWS > doc/LATEST
|
101
|
-
find doc/images doc/js -type f | \
|
102
|
-
TZ=UTC xargs touch -d '1970-01-01 00:00:01' doc/rdoc.css
|
103
|
-
$(MAKE) doc_gz
|
104
|
-
chmod 644 $$(find doc -type f)
|
105
|
-
$(RSYNC) -av doc/ bogomips.org:/srv/bogomips/ruby_io_splice/
|
106
|
-
git ls-files | xargs touch
|
107
|
-
|
108
|
-
# Create gzip variants of the same timestamp as the original so nginx
|
109
|
-
# "gzip_static on" can serve the gzipped versions directly.
|
110
|
-
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
111
|
-
doc_gz:
|
112
|
-
touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
|
113
|
-
for i in $(docs); do \
|
114
|
-
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
115
|
-
|
116
|
-
ifneq ($(VERSION),)
|
3
|
+
RSYNC_DEST := bogomips.org:/srv/bogomips/ruby_io_splice
|
117
4
|
rfproject := qrp
|
118
5
|
rfpackage := io_splice
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
release-notes: $(release_notes)
|
125
|
-
release-changes: $(release_changes)
|
126
|
-
$(release_changes):
|
127
|
-
$(RAKE) -s release_changes > $@+
|
128
|
-
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
129
|
-
$(release_notes):
|
130
|
-
GIT_URL=$(GIT_URL) $(RAKE) -s release_notes > $@+
|
131
|
-
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
132
|
-
|
133
|
-
# ensures we're actually on the tagged $(VERSION), only used for release
|
134
|
-
verify:
|
135
|
-
test x"$(shell umask)" = x0022
|
136
|
-
git rev-parse --verify refs/tags/v$(VERSION)^{}
|
137
|
-
git diff-index --quiet HEAD^0
|
138
|
-
test `git rev-parse --verify HEAD^0` = \
|
139
|
-
`git rev-parse --verify refs/tags/v$(VERSION)^{}`
|
140
|
-
|
141
|
-
fix-perms:
|
142
|
-
-git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
|
143
|
-
-git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
|
144
|
-
|
145
|
-
gem: $(pkggem)
|
146
|
-
|
147
|
-
install-gem: $(pkggem)
|
148
|
-
gem install $(CURDIR)/$<
|
149
|
-
|
150
|
-
$(pkggem): manifest fix-perms
|
151
|
-
gem build $(rfpackage).gemspec
|
152
|
-
mkdir -p pkg
|
153
|
-
mv $(@F) $@
|
154
|
-
|
155
|
-
$(pkgtgz): distdir = $(basename $@)
|
156
|
-
$(pkgtgz): HEAD = v$(VERSION)
|
157
|
-
$(pkgtgz): manifest fix-perms
|
158
|
-
@test -n "$(distdir)"
|
159
|
-
$(RM) -r $(distdir)
|
160
|
-
mkdir -p $(distdir)
|
161
|
-
tar c `cat .manifest` | (cd $(distdir) && tar x)
|
162
|
-
cd pkg && tar c $(basename $(@F)) | gzip -9 > $(@F)+
|
163
|
-
mv $@+ $@
|
164
|
-
|
165
|
-
package: $(pkgtgz) $(pkggem)
|
166
|
-
|
167
|
-
test-release: verify package $(release_notes) $(release_changes)
|
168
|
-
release: verify package $(release_notes) $(release_changes)
|
169
|
-
# make tgz release on RubyForge
|
170
|
-
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
171
|
-
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
172
|
-
# push gem to Gemcutter
|
173
|
-
gem push $(pkggem)
|
174
|
-
# in case of gem downloads from RubyForge releases page
|
175
|
-
-rubyforge add_file \
|
176
|
-
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
177
|
-
else
|
178
|
-
gem install-gem: GIT-VERSION-FILE
|
179
|
-
$(MAKE) $@ VERSION=$(GIT_VERSION)
|
6
|
+
include pkg.mk
|
7
|
+
ifneq ($(VERSION),)
|
8
|
+
release::
|
9
|
+
$(RAKE) raa_update VERSION=$(VERSION)
|
10
|
+
$(RAKE) publish_news VERSION=$(VERSION)
|
180
11
|
endif
|
181
|
-
|
182
|
-
ext := ext/io_splice/io_splice_ext.$(DLEXT)
|
183
|
-
ext/io_splice/Makefile: ext/io_splice/extconf.rb
|
184
|
-
cd $(@D) && $(RUBY) extconf.rb
|
185
|
-
$(ext): $(wildcard $(addprefix ext/io_splice/,*.c *.h)) ext/io_splice/Makefile
|
186
|
-
$(MAKE) -C $(@D)
|
187
|
-
|
188
|
-
all:: test
|
189
|
-
|
190
|
-
build: $(ext)
|
191
|
-
test: test-unit
|
192
12
|
rcov: build
|
193
13
|
$(RCOV) -I lib:ext/io_splice $(test_unit)
|
194
|
-
|
195
|
-
test_unit := $(wildcard test/test_*.rb)
|
196
|
-
$(test_unit): build
|
197
|
-
$(RUBY) -I lib:ext/io_splice $@ $(TEST_UNIT_OPTS)
|
198
|
-
test-unit: $(test_unit)
|
199
|
-
|
200
|
-
.PHONY: .FORCE-GIT-VERSION-FILE doc manifest man test $(test_unit)
|
14
|
+
.PHONY: rcov
|
data/README
CHANGED
@@ -66,13 +66,13 @@ standardized and available in non-Linux kernels some day.
|
|
66
66
|
|
67
67
|
You can get the latest source via git from the following locations:
|
68
68
|
|
69
|
-
git://
|
69
|
+
git://bogomips.org/ruby_io_splice.git
|
70
70
|
git://repo.or.cz/ruby_io_splice.git (mirror)
|
71
71
|
|
72
72
|
You may browse the code from the web and download the latest snapshot
|
73
73
|
tarballs here:
|
74
74
|
|
75
|
-
* http://
|
75
|
+
* http://bogomips.org/cgit/ruby_io_splice.git (cgit)
|
76
76
|
* http://repo.or.cz/w/ruby_io_splice.git (gitweb)
|
77
77
|
|
78
78
|
Inline patches (from "git format-patch") to the mailing list are
|
data/Rakefile
CHANGED
@@ -1,112 +1,26 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
require 'wrongdoc'
|
3
|
+
cgit_url = Wrongdoc.config[:cgit_url]
|
4
|
+
git_url = Wrongdoc.config[:git_url]
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
def tags
|
6
|
-
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
7
|
-
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
8
|
-
if %r{\Av[\d\.]+\z} =~ tag
|
9
|
-
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
10
|
-
header = header.split(/\n/)
|
11
|
-
tagger = header.grep(/\Atagger /).first
|
12
|
-
body ||= "initial"
|
13
|
-
{
|
14
|
-
:time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
|
15
|
-
:tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
|
16
|
-
:tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
|
17
|
-
:id => `git rev-parse refs/tags/#{tag}`.chomp!,
|
18
|
-
:tag => tag,
|
19
|
-
:subject => subject,
|
20
|
-
:body => body,
|
21
|
-
}
|
22
|
-
end
|
23
|
-
end.compact.sort { |a,b| b[:time] <=> a[:time] }
|
24
|
-
end
|
25
|
-
|
26
|
-
cgit_url = "http://git.bogomips.org/cgit/ruby_io_splice.git"
|
27
|
-
git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/ruby_io_splice.git'
|
28
|
-
web_url = "http://bogomips.org/ruby_io_splice/"
|
29
|
-
|
30
|
-
desc 'prints news as an Atom feed'
|
31
|
-
task :news_atom do
|
32
|
-
require 'nokogiri'
|
33
|
-
new_tags = tags[0,10]
|
34
|
-
puts(Nokogiri::XML::Builder.new do
|
35
|
-
feed :xmlns => "http://www.w3.org/2005/Atom" do
|
36
|
-
id! "#{web_url}NEWS.atom.xml"
|
37
|
-
title "Ruby io_splice news"
|
38
|
-
subtitle "splice and tee Linux syscalls from Ruby"
|
39
|
-
link! :rel => "alternate", :type => "text/html",
|
40
|
-
:href => "#{web_url}NEWS.html"
|
41
|
-
updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
|
42
|
-
new_tags.each do |tag|
|
43
|
-
entry do
|
44
|
-
title tag[:subject]
|
45
|
-
updated tag[:time]
|
46
|
-
published tag[:time]
|
47
|
-
author {
|
48
|
-
name tag[:tagger_name]
|
49
|
-
email tag[:tagger_email]
|
50
|
-
}
|
51
|
-
url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
|
52
|
-
link! :rel => "alternate", :type => "text/html", :href =>url
|
53
|
-
id! url
|
54
|
-
message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
|
55
|
-
content({:type =>:text}, message_only)
|
56
|
-
content(:type =>:xhtml) { pre tag[:body] }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end.to_xml)
|
61
|
-
end
|
62
|
-
|
63
|
-
desc 'prints RDoc-formatted news'
|
64
|
-
task :news_rdoc do
|
65
|
-
tags.each do |tag|
|
66
|
-
time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
|
67
|
-
puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
|
68
|
-
puts ""
|
69
|
-
|
70
|
-
body = tag[:body]
|
71
|
-
puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
|
72
|
-
puts ""
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
desc "print release changelog for Rubyforge"
|
77
|
-
task :release_changes do
|
78
|
-
version = ENV['VERSION'] or abort "VERSION= needed"
|
79
|
-
version = "v#{version}"
|
80
|
-
vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
|
81
|
-
prev = vtags[vtags.index(version) - 1]
|
82
|
-
if prev
|
83
|
-
system('git', 'diff', '--stat', prev, version) or abort $?
|
84
|
-
puts ""
|
85
|
-
system('git', 'log', "#{prev}..#{version}") or abort $?
|
86
|
-
else
|
87
|
-
system('git', 'log', version) or abort $?
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
desc "print release notes for Rubyforge"
|
92
|
-
task :release_notes do
|
93
|
-
spec = Gem::Specification.load('io_splice.gemspec')
|
94
|
-
puts spec.description.strip
|
95
|
-
puts ""
|
96
|
-
puts "* #{spec.homepage}"
|
97
|
-
puts "* #{spec.email}"
|
98
|
-
puts "* #{git_url}"
|
99
|
-
|
100
|
-
_, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
101
|
-
print "\nChanges:\n\n"
|
102
|
-
puts body
|
103
|
-
end
|
104
|
-
|
105
|
-
desc "read news article from STDIN and post to rubyforge"
|
6
|
+
desc "post news article to rubyforge"
|
106
7
|
task :publish_news do
|
107
8
|
require 'rubyforge'
|
108
|
-
|
109
|
-
|
9
|
+
spec = Gem::Specification.load('io_splice.gemspec')
|
10
|
+
tmp = Tempfile.new('rf-news')
|
11
|
+
_, subject, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
12
|
+
tmp.puts subject
|
13
|
+
tmp.puts
|
14
|
+
tmp.puts spec.description.strip
|
15
|
+
tmp.puts ""
|
16
|
+
tmp.puts "* #{spec.homepage}"
|
17
|
+
tmp.puts "* #{spec.email}"
|
18
|
+
tmp.puts "* #{git_url}"
|
19
|
+
tmp.print "\nChanges:\n\n"
|
20
|
+
tmp.puts body
|
21
|
+
tmp.flush
|
22
|
+
system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?"
|
23
|
+
msg = File.readlines(tmp.path)
|
110
24
|
subject = msg.shift
|
111
25
|
blank = msg.shift
|
112
26
|
blank == "\n" or abort "no newline after subject!"
|
@@ -144,7 +58,7 @@ task :raa_update do
|
|
144
58
|
:category_minor => 'System',
|
145
59
|
:url => s.homepage,
|
146
60
|
:download => 'http://rubyforge.org/frs/?group_id=5626',
|
147
|
-
:license =>
|
61
|
+
:license => "LGPL",
|
148
62
|
:description_style => 'Plain',
|
149
63
|
:description => desc,
|
150
64
|
:pass => password,
|
@@ -12,6 +12,8 @@
|
|
12
12
|
#include <alloca.h>
|
13
13
|
#include <sys/utsname.h>
|
14
14
|
|
15
|
+
static VALUE sym_EAGAIN;
|
16
|
+
|
15
17
|
#ifndef F_LINUX_SPECIFIC_BASE
|
16
18
|
# define F_LINUX_SPECIFIC_BASE 1024
|
17
19
|
#endif
|
@@ -88,8 +90,14 @@ rb_thread_blocking_region(
|
|
88
90
|
# define RARRAY_LEN(s) (RARRAY(s)->len)
|
89
91
|
#endif
|
90
92
|
|
93
|
+
static VALUE io_run(rb_blocking_function_t *fn, void *data)
|
94
|
+
{
|
95
|
+
return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
|
96
|
+
}
|
97
|
+
|
91
98
|
/*
|
92
99
|
* Releases GVL only iff blocking I/O is used.
|
100
|
+
* Only use this if all file descriptors in data are pipes.
|
93
101
|
* We'll trust programmers who use non-blocking I/O explicitly to
|
94
102
|
* want the fastest possible performance without resorting to threads,
|
95
103
|
* so releasing and them immediately reacquiring the GVL would be
|
@@ -99,7 +107,7 @@ static VALUE nb_io_run(rb_blocking_function_t *fn, void *data, unsigned flags)
|
|
99
107
|
{
|
100
108
|
if (flags & SPLICE_F_NONBLOCK)
|
101
109
|
return fn(data);
|
102
|
-
return
|
110
|
+
return io_run(fn, data);
|
103
111
|
}
|
104
112
|
|
105
113
|
struct splice_args {
|
@@ -119,8 +127,28 @@ static VALUE nogvl_splice(void *ptr)
|
|
119
127
|
a->len, a->flags);
|
120
128
|
}
|
121
129
|
|
130
|
+
static long do_splice(int argc, VALUE *argv, unsigned dflags)
|
131
|
+
{
|
132
|
+
off_t i, o;
|
133
|
+
VALUE fd_in, off_in, fd_out, off_out, len, flags;
|
134
|
+
struct splice_args a;
|
135
|
+
|
136
|
+
rb_scan_args(argc, argv, "51",
|
137
|
+
&fd_in, &off_in, &fd_out, &off_out, &len, &flags);
|
138
|
+
|
139
|
+
a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
|
140
|
+
a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
|
141
|
+
a.fd_in = my_fileno(fd_in);
|
142
|
+
a.fd_out = my_fileno(fd_out);
|
143
|
+
a.len = (size_t)NUM2ULONG(len);
|
144
|
+
a.flags = NIL_P(flags) ? dflags : NUM2UINT(flags) | dflags;
|
145
|
+
|
146
|
+
return (long)io_run(nogvl_splice, &a);
|
147
|
+
}
|
148
|
+
|
122
149
|
/*
|
123
150
|
* call-seq:
|
151
|
+
* IO.splice(fd_in, off_in, fd_out, off_out, len) => integer
|
124
152
|
* IO.splice(fd_in, off_in, fd_out, off_out, len, flags) => integer
|
125
153
|
*
|
126
154
|
* Splice +len+ bytes from/to a pipe. Either +fd_in+ or +fd_out+
|
@@ -130,6 +158,7 @@ static VALUE nogvl_splice(void *ptr)
|
|
130
158
|
* +off_in+ and +off_out+ if non-nil may be used to
|
131
159
|
* specify an offset for the non-pipe file descriptor.
|
132
160
|
*
|
161
|
+
* +flags+ defaults to zero if unspecified.
|
133
162
|
* +flags+ may be a bitmask of the following flags:
|
134
163
|
*
|
135
164
|
* IO::Splice::F_MOVE, IO::Splice::F_NONBLOCK, IO::Splice::F_MORE
|
@@ -152,26 +181,17 @@ static VALUE nogvl_splice(void *ptr)
|
|
152
181
|
* into account userspace buffering done by Ruby or stdio. It is
|
153
182
|
* also not subject to encoding/decoding filters under Ruby 1.9.
|
154
183
|
*
|
184
|
+
* Consider using IO.trysplice if you are using non-blocking I/O on
|
185
|
+
* both descriptors as it avoids the cost of raising common Errno::EAGAIN
|
186
|
+
* exceptions.
|
187
|
+
*
|
155
188
|
* See manpage for full documentation:
|
156
189
|
* http://kernel.org/doc/man-pages/online/pages/man2/splice.2.html
|
157
190
|
*/
|
158
|
-
static VALUE my_splice(VALUE self
|
159
|
-
VALUE fd_in, VALUE off_in,
|
160
|
-
VALUE fd_out, VALUE off_out,
|
161
|
-
VALUE len, VALUE flags)
|
191
|
+
static VALUE my_splice(int argc, VALUE *argv, VALUE self)
|
162
192
|
{
|
163
|
-
|
164
|
-
|
165
|
-
struct splice_args a = {
|
166
|
-
.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i),
|
167
|
-
.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o),
|
168
|
-
.fd_in = my_fileno(fd_in),
|
169
|
-
.fd_out = my_fileno(fd_out),
|
170
|
-
.len = (size_t)NUM2ULONG(len),
|
171
|
-
.flags = NUM2UINT(flags),
|
172
|
-
};
|
173
|
-
|
174
|
-
n = (long)rb_thread_blocking_region(nogvl_splice, &a, RUBY_UBF_IO, 0);
|
193
|
+
long n = do_splice(argc, argv, 0);
|
194
|
+
|
175
195
|
if (n == 0)
|
176
196
|
rb_eof_error();
|
177
197
|
if (n < 0)
|
@@ -179,6 +199,33 @@ static VALUE my_splice(VALUE self,
|
|
179
199
|
return LONG2NUM(n);
|
180
200
|
}
|
181
201
|
|
202
|
+
/*
|
203
|
+
* call-seq:
|
204
|
+
* IO.trysplice(fd_in, off_in, fd_out, off_out, len) => integer
|
205
|
+
* IO.trysplice(fd_in, off_in, fd_out, off_out, len, flags) => integer
|
206
|
+
*
|
207
|
+
* Exactly like IO.splice, except +:EAGAIN+ is returned when either
|
208
|
+
* the read or write end would block instead of raising Errno::EAGAIN.
|
209
|
+
*
|
210
|
+
* IO::Splice::F_NONBLOCK is always passed for the pipe descriptor,
|
211
|
+
* but this can still block if the non-pipe descriptor is blocking.
|
212
|
+
*
|
213
|
+
* See IO.splice documentation for more details.
|
214
|
+
*/
|
215
|
+
static VALUE trysplice(int argc, VALUE *argv, VALUE self)
|
216
|
+
{
|
217
|
+
long n = do_splice(argc, argv, SPLICE_F_NONBLOCK);
|
218
|
+
|
219
|
+
if (n == 0)
|
220
|
+
return Qnil;
|
221
|
+
if (n < 0) {
|
222
|
+
if (errno == EAGAIN)
|
223
|
+
return sym_EAGAIN;
|
224
|
+
rb_sys_fail("splice");
|
225
|
+
}
|
226
|
+
return LONG2NUM(n);
|
227
|
+
}
|
228
|
+
|
182
229
|
struct tee_args {
|
183
230
|
int fd_in;
|
184
231
|
int fd_out;
|
@@ -194,15 +241,30 @@ static VALUE nogvl_tee(void *ptr)
|
|
194
241
|
return (VALUE)tee(a->fd_in, a->fd_out, a->len, a->flags);
|
195
242
|
}
|
196
243
|
|
244
|
+
static long do_tee(int argc, VALUE *argv, unsigned dflags)
|
245
|
+
{
|
246
|
+
VALUE fd_in, fd_out, len, flags;
|
247
|
+
struct tee_args a;
|
248
|
+
|
249
|
+
rb_scan_args(argc, argv, "31", &fd_in, &fd_out, &len, &flags);
|
250
|
+
a.fd_in = my_fileno(fd_in);
|
251
|
+
a.fd_out = my_fileno(fd_out);
|
252
|
+
a.len = (size_t)NUM2ULONG(len);
|
253
|
+
a.flags = NIL_P(flags) ? dflags : NUM2UINT(flags) | dflags;
|
254
|
+
|
255
|
+
return (long)nb_io_run(nogvl_tee, &a, a.flags);
|
256
|
+
}
|
257
|
+
|
197
258
|
/*
|
198
259
|
* call-seq:
|
260
|
+
* IO.tee(fd_in, fd_out, len) => integer
|
199
261
|
* IO.tee(fd_in, fd_out, len, flags) => integer
|
200
262
|
*
|
201
263
|
* Copies up to +len+ bytes of data from +fd_in+ to +fd_out+. +fd_in+
|
202
264
|
* and +fd_out+ must both refer to pipe descriptors. +fd_in+ and +fd_out+
|
203
265
|
* may not be endpoints of the same pipe.
|
204
266
|
*
|
205
|
-
* +flags+ may be zero or IO::Splice::F_NONBLOCK
|
267
|
+
* +flags+ may be zero (the default) or IO::Splice::F_NONBLOCK
|
206
268
|
* Other IO::Splice flags are currently unimplemented or have no effect.
|
207
269
|
*
|
208
270
|
* Returns the number of bytes duplicated if successful.
|
@@ -210,22 +272,16 @@ static VALUE nogvl_tee(void *ptr)
|
|
210
272
|
* Raises Errno::EAGAIN when +fd_in+ is empty and/or +fd_out+ is full
|
211
273
|
* and +flags+ contains IO::Splice::F_NONBLOCK
|
212
274
|
*
|
275
|
+
* Consider using IO.trytee if you are using IO::Splice::F_NONBLOCK
|
276
|
+
* as it avoids the cost of raising common Errno::EAGAIN exceptions.
|
277
|
+
*
|
213
278
|
* See manpage for full documentation:
|
214
279
|
* http://kernel.org/doc/man-pages/online/pages/man2/tee.2.html
|
215
280
|
*/
|
216
|
-
static VALUE my_tee(VALUE self
|
217
|
-
VALUE fd_in, VALUE fd_out,
|
218
|
-
VALUE len, VALUE flags)
|
281
|
+
static VALUE my_tee(int argc, VALUE *argv, VALUE self)
|
219
282
|
{
|
220
|
-
long n;
|
221
|
-
|
222
|
-
.fd_in = my_fileno(fd_in),
|
223
|
-
.fd_out = my_fileno(fd_out),
|
224
|
-
.len = (size_t)NUM2ULONG(len),
|
225
|
-
.flags = NUM2UINT(flags),
|
226
|
-
};
|
227
|
-
|
228
|
-
n = (long)nb_io_run(nogvl_tee, &a, a.flags);
|
283
|
+
long n = do_tee(argc, argv, 0);
|
284
|
+
|
229
285
|
if (n == 0)
|
230
286
|
rb_eof_error();
|
231
287
|
if (n < 0)
|
@@ -234,6 +290,34 @@ static VALUE my_tee(VALUE self,
|
|
234
290
|
return LONG2NUM(n);
|
235
291
|
}
|
236
292
|
|
293
|
+
/*
|
294
|
+
* call-seq:
|
295
|
+
* IO.trytee(fd_in, fd_out, len) => integer
|
296
|
+
* IO.trytee(fd_in, fd_out, len, flags) => integer
|
297
|
+
*
|
298
|
+
* Exactly like IO.tee, except +:EAGAIN+ is returned when either
|
299
|
+
* the read or write end would block instead of raising Errno::EAGAIN.
|
300
|
+
*
|
301
|
+
* IO::Splice::F_NONBLOCK is always passed for the pipe descriptor,
|
302
|
+
* but this can still block if the non-pipe descriptor is blocking.
|
303
|
+
*
|
304
|
+
* See IO.tee documentation for more details.
|
305
|
+
*/
|
306
|
+
static VALUE trytee(int argc, VALUE *argv, VALUE self)
|
307
|
+
{
|
308
|
+
long n = do_tee(argc, argv, SPLICE_F_NONBLOCK);
|
309
|
+
|
310
|
+
if (n == 0)
|
311
|
+
return Qnil;
|
312
|
+
if (n < 0) {
|
313
|
+
if (errno == EAGAIN)
|
314
|
+
return sym_EAGAIN;
|
315
|
+
rb_sys_fail("tee");
|
316
|
+
}
|
317
|
+
|
318
|
+
return LONG2NUM(n);
|
319
|
+
}
|
320
|
+
|
237
321
|
struct vmsplice_args {
|
238
322
|
int fd;
|
239
323
|
struct iovec *iov;
|
@@ -272,7 +356,7 @@ do { \
|
|
272
356
|
static void advance_vmsplice_args(struct vmsplice_args *a, long n)
|
273
357
|
{
|
274
358
|
struct iovec *new_iov = a->iov;
|
275
|
-
|
359
|
+
unsigned long i;
|
276
360
|
|
277
361
|
/* skip over iovecs we've already written completely */
|
278
362
|
for (i = 0; i < a->nr_segs; i++, new_iov++) {
|
@@ -301,7 +385,9 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
|
|
301
385
|
|
302
386
|
/*
|
303
387
|
* call-seq:
|
388
|
+
* IO.vmsplice(fd, string_array) => integer
|
304
389
|
* IO.vmsplice(fd, string_array, flags) => integer
|
390
|
+
* IO.vmsplice(fd, string) => integer
|
305
391
|
* IO.vmsplice(fd, string, flags) => integer
|
306
392
|
*
|
307
393
|
* Transfers an array of strings into the pipe descriptor given by fd.
|
@@ -313,11 +399,14 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
|
|
313
399
|
* See manpage for full documentation:
|
314
400
|
* http://kernel.org/doc/man-pages/online/pages/man2/vmsplice.2.html
|
315
401
|
*/
|
316
|
-
static VALUE my_vmsplice(
|
402
|
+
static VALUE my_vmsplice(int argc, VALUE * argv, VALUE self)
|
317
403
|
{
|
318
404
|
long rv = 0;
|
319
405
|
ssize_t left;
|
320
406
|
struct vmsplice_args a;
|
407
|
+
VALUE fd, data, flags;
|
408
|
+
|
409
|
+
rb_scan_args(argc, argv, "21", &fd, &data, &flags);
|
321
410
|
|
322
411
|
switch (TYPE(data)) {
|
323
412
|
case T_STRING: {
|
@@ -338,7 +427,7 @@ static VALUE my_vmsplice(VALUE self, VALUE fd, VALUE data, VALUE flags)
|
|
338
427
|
rb_obj_classname(data));
|
339
428
|
}
|
340
429
|
a.fd = my_fileno(fd);
|
341
|
-
a.flags = NUM2UINT(flags);
|
430
|
+
a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags);
|
342
431
|
|
343
432
|
for (;;) {
|
344
433
|
long n = (long)nb_io_run(nogvl_vmsplice, &a, a.flags);
|
@@ -442,9 +531,11 @@ void Init_io_splice_ext(void)
|
|
442
531
|
VALUE mSplice = rb_define_module_under(rb_cIO, "Splice");
|
443
532
|
struct utsname utsname;
|
444
533
|
|
445
|
-
rb_define_singleton_method(rb_cIO, "splice", my_splice,
|
446
|
-
rb_define_singleton_method(rb_cIO, "
|
447
|
-
rb_define_singleton_method(rb_cIO, "
|
534
|
+
rb_define_singleton_method(rb_cIO, "splice", my_splice, -1);
|
535
|
+
rb_define_singleton_method(rb_cIO, "trysplice", trysplice, -1);
|
536
|
+
rb_define_singleton_method(rb_cIO, "tee", my_tee, -1);
|
537
|
+
rb_define_singleton_method(rb_cIO, "trytee", trytee, -1);
|
538
|
+
rb_define_singleton_method(rb_cIO, "vmsplice", my_vmsplice, -1);
|
448
539
|
|
449
540
|
/*
|
450
541
|
* Attempt to move pages instead of copying. This is only a hint
|
@@ -510,4 +601,6 @@ void Init_io_splice_ext(void)
|
|
510
601
|
rb_define_const(mSplice, "F_SETPIPE_SZ",
|
511
602
|
UINT2NUM(F_SETPIPE_SZ));
|
512
603
|
}
|
604
|
+
|
605
|
+
sym_EAGAIN = ID2SYM(rb_intern("EAGAIN"));
|
513
606
|
}
|