vio 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,40 @@
1
+ Vectored I/O for Ruby MRI
2
+ (c) 2009 Lourens Naudé (methodmissing)
3
+
4
+ http://github.com/methodmissing/vio
5
+
6
+ Vectored I/O ?
7
+
8
+ POSIX 1003.1-2001 introduces reading or writing data into multiple buffers with a single procedure call.See http://en.wikipedia.org/wiki/Vectored_I/O for an overview.
9
+
10
+ Use cases :
11
+
12
+ * Write non-sequentially placed data in memory to disk with a single write operation.This fits very well with custom database/record formats for writing out indexes and data at once.
13
+ * Reducing syscall overheads
14
+ * Reading structured data (protocol dumps,db records) into distinct buffers
15
+
16
+ Examples :
17
+
18
+ 8=FIX.4.49=4535=049=TW56=ISLD34=352=20000426-12:05:0610=220
19
+
20
+ >>io = File.open('/path/to/fix.txt'))
21
+ => #<File://path/to/fix.txt>
22
+ >>io.read(9, 5, 5, 6, 8, 26, 8)
23
+ => ["8=FIX.4.4","\x019=45", "\x0135=0", "\x0149=TW", "\x0156=ISLD",
24
+ "\x0134=3\x0152=20000426-12:05:06", "\x0110=220\x01"]
25
+
26
+ Installation
27
+
28
+ sudo gem install methodmissing-vio
29
+
30
+ Todo :
31
+
32
+ * Error handling for rewind + sockets don't support lseek
33
+
34
+ To run the test suite:
35
+
36
+ rake
37
+
38
+ To run the benchmarks:
39
+
40
+ rake bench
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+ $:.unshift(File.expand_path('lib'))
5
+ VIO_ROOT = 'ext/vio'
6
+
7
+ desc 'Default: test'
8
+ task :default => :test
9
+
10
+ desc 'Run VIO tests.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs = [VIO_ROOT]
13
+ t.pattern = 'test/test_*.rb'
14
+ t.verbose = true
15
+ end
16
+ task :test => :build
17
+
18
+ namespace :build do
19
+ file "#{VIO_ROOT}/vio.c"
20
+ file "#{VIO_ROOT}/extconf.rb"
21
+ file "#{VIO_ROOT}/Makefile" => %W(#{VIO_ROOT}/vio.c #{VIO_ROOT}/extconf.rb) do
22
+ Dir.chdir(VIO_ROOT) do
23
+ ruby 'extconf.rb'
24
+ end
25
+ end
26
+
27
+ desc "generate makefile"
28
+ task :makefile => %W(#{VIO_ROOT}/Makefile #{VIO_ROOT}/vio.c)
29
+
30
+ dlext = Config::CONFIG['DLEXT']
31
+ file "#{VIO_ROOT}/vio.#{dlext}" => %W(#{VIO_ROOT}/Makefile #{VIO_ROOT}/vio.c) do
32
+ Dir.chdir(VIO_ROOT) do
33
+ sh 'make' # TODO - is there a config for which make somewhere?
34
+ end
35
+ end
36
+
37
+ desc "compile vio extension"
38
+ task :compile => "#{VIO_ROOT}/vio.#{dlext}"
39
+
40
+ task :clean do
41
+ Dir.chdir(VIO_ROOT) do
42
+ sh 'make clean'
43
+ end if File.exists?("#{VIO_ROOT}/Makefile")
44
+ end
45
+
46
+ CLEAN.include("#{VIO_ROOT}/Makefile")
47
+ CLEAN.include("#{VIO_ROOT}/vio.#{dlext}")
48
+ end
49
+
50
+ task :clean => %w(build:clean)
51
+
52
+ desc "compile"
53
+ task :build => %w(build:compile)
54
+
55
+ task :install do |t|
56
+ Dir.chdir(VIO_ROOT) do
57
+ sh 'sudo make install'
58
+ end
59
+ end
60
+
61
+ desc "clean build install"
62
+ task :setup => %w(clean build install)
63
+
64
+ namespace :bench do |t|
65
+ desc "bench reads"
66
+ task :read do
67
+ ruby "bench/read.rb"
68
+ end
69
+
70
+ desc "bench writes"
71
+ task :write do
72
+ ruby "bench/write.rb"
73
+ end
74
+ end
75
+ task :bench => :build
@@ -0,0 +1,30 @@
1
+ class Logger
2
+ def addv(msgs)
3
+ unless @logdev.nil?
4
+ @logdev.writev(msgs)
5
+ end
6
+ end
7
+
8
+ def blank_slate
9
+ @logdev.blank_slate
10
+ end
11
+
12
+ class LogDevice
13
+ def writev(messages)
14
+ @mutex.synchronize do
15
+ if @shift_age and @dev.respond_to?(:stat)
16
+ begin
17
+ check_shift_log
18
+ rescue
19
+ raise Logger::ShiftingError.new("Shifting failed. #{$!}")
20
+ end
21
+ end
22
+ @dev.writev(*messages)
23
+ end
24
+ end
25
+
26
+ def blank_slate
27
+ @dev.truncate(0)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ $:.unshift "."
2
+ def relative(path)
3
+ File.dirname(__FILE__) + path
4
+ end
5
+
6
+ require relative('/../ext/vio/vio')
7
+ require "benchmark"
8
+
9
+ FILE = relative("/../test/fixtures/fix.txt")
10
+ SEP = "\x01".freeze
11
+
12
+ source = File.open(FILE)
13
+ TESTS = 100_000
14
+ begin
15
+ puts "* Bench reads ..."
16
+ Benchmark.bmbm do |results|
17
+ results.report("IO.readv") { TESTS.times{ source.readv(9, 5, 5, 6, 8, 26, 8) } }
18
+ results.report("IO.read") { TESTS.times{ source.read.split(SEP) } }
19
+ end
20
+ ensure
21
+ source.close
22
+ end
@@ -0,0 +1,38 @@
1
+ $:.unshift "."
2
+ def relative(path)
3
+ File.dirname(__FILE__) + path
4
+ end
5
+
6
+ require relative('/../ext/vio/vio')
7
+ require "benchmark"
8
+ require "logger"
9
+ require relative('/patched_logger')
10
+
11
+ PAYLOAD = ['a' * 100,
12
+ 'b' * 200,
13
+ 'c' * 300,
14
+ 'd' * 400,
15
+ 'e' * 500,
16
+ 'f' * 600,
17
+ 'g' * 700,
18
+ 'h' * 800,
19
+ 'i' * 900,
20
+ 'j' * 1000]
21
+
22
+ FILE = relative("/../test/fixtures/writable.txt")
23
+
24
+ logger = Logger.new(FILE)
25
+ logger.blank_slate
26
+
27
+ TESTS = 100_000
28
+ begin
29
+ puts "* Bench log writes ..."
30
+ Benchmark.bmbm do |results|
31
+ results.report("IO.writev") { TESTS.times{ logger.addv(PAYLOAD) } }
32
+ logger.blank_slate
33
+ results.report("IO.write") { TESTS.times{ logger << PAYLOAD.join } }
34
+ end
35
+ ensure
36
+ logger.blank_slate
37
+ logger.close
38
+ end
@@ -0,0 +1,187 @@
1
+
2
+ SHELL = /bin/sh
3
+
4
+ #### Start of system configuration section. ####
5
+
6
+ srcdir = .
7
+ topdir = /Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1
8
+ hdrdir = /Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1
9
+ arch_hdrdir = /Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/$(arch)
10
+ VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
11
+ prefix = $(DESTDIR)/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1
12
+ rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
13
+ exec_prefix = $(prefix)
14
+ vendorhdrdir = $(rubyhdrdir)/vendor_ruby
15
+ sitehdrdir = $(rubyhdrdir)/site_ruby
16
+ rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
17
+ vendordir = $(rubylibprefix)/vendor_ruby
18
+ sitedir = $(rubylibprefix)/site_ruby
19
+ ridir = $(datarootdir)/$(RI_BASE_NAME)
20
+ mandir = $(datarootdir)/man
21
+ localedir = $(datarootdir)/locale
22
+ libdir = $(exec_prefix)/lib
23
+ psdir = $(docdir)
24
+ pdfdir = $(docdir)
25
+ dvidir = $(docdir)
26
+ htmldir = $(docdir)
27
+ infodir = $(datarootdir)/info
28
+ docdir = $(datarootdir)/doc/$(PACKAGE)
29
+ oldincludedir = $(DESTDIR)/usr/include
30
+ includedir = $(prefix)/include
31
+ localstatedir = $(prefix)/var
32
+ sharedstatedir = $(prefix)/com
33
+ sysconfdir = $(prefix)/etc
34
+ datadir = $(datarootdir)
35
+ datarootdir = $(prefix)/share
36
+ libexecdir = $(exec_prefix)/libexec
37
+ sbindir = $(exec_prefix)/sbin
38
+ bindir = $(exec_prefix)/bin
39
+ rubylibdir = $(rubylibprefix)/$(ruby_version)
40
+ archdir = $(rubylibdir)/$(arch)
41
+ sitelibdir = $(sitedir)/$(ruby_version)
42
+ sitearchdir = $(sitelibdir)/$(sitearch)
43
+ vendorlibdir = $(vendordir)/$(ruby_version)
44
+ vendorarchdir = $(vendorlibdir)/$(sitearch)
45
+
46
+ CC = gcc
47
+ CXX = g++
48
+ LIBRUBY = $(LIBRUBY_SO)
49
+ LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
50
+ LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
51
+ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
52
+ OUTFLAG = -o
53
+ COUTFLAG = -o
54
+
55
+ RUBY_EXTCONF_H =
56
+ cflags = $(optflags) $(debugflags) $(warnflags)
57
+ optflags = -O3
58
+ debugflags = -ggdb
59
+ warnflags = -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long
60
+ CFLAGS = -fno-common $(cflags) -fno-common -pipe
61
+ INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
62
+ DEFS =
63
+ CPPFLAGS = -DHAVE_RB_THREAD_BLOCKING_REGION -DRUBY19 -pedantic -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
64
+ CXXFLAGS = $(CFLAGS) $(cxxflags)
65
+ ldflags = -L. -L/usr/local/lib
66
+ dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace
67
+ ARCH_FLAG =
68
+ DLDFLAGS = $(ldflags) $(dldflags)
69
+ LDSHARED = $(CC) -dynamic -bundle
70
+ LDSHAREDXX = $(CXX) -dynamic -bundle
71
+ AR = ar
72
+ EXEEXT =
73
+
74
+ RUBY_BASE_NAME = ruby
75
+ RUBY_INSTALL_NAME = ruby
76
+ RUBY_SO_NAME = ruby.1.9.1
77
+ arch = x86_64-darwin10.4.0
78
+ sitearch = $(arch)
79
+ ruby_version = 1.9.1
80
+ ruby = /Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/bin/ruby
81
+ RUBY = $(ruby)
82
+ RM = rm -f
83
+ RM_RF = $(RUBY) -run -e rm -- -rf
84
+ RMDIRS = $(RUBY) -run -e rmdir -- -p
85
+ MAKEDIRS = mkdir -p
86
+ INSTALL = /usr/bin/install -c
87
+ INSTALL_PROG = $(INSTALL) -m 0755
88
+ INSTALL_DATA = $(INSTALL) -m 644
89
+ COPY = cp
90
+
91
+ #### End of system configuration section. ####
92
+
93
+ preload =
94
+
95
+ libpath = . $(libdir)
96
+ LIBPATH = -L. -L$(libdir)
97
+ DEFFILE =
98
+
99
+ CLEANFILES = mkmf.log
100
+ DISTCLEANFILES =
101
+ DISTCLEANDIRS =
102
+
103
+ extout =
104
+ extout_prefix =
105
+ target_prefix =
106
+ LOCAL_LIBS =
107
+ LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lobjc
108
+ SRCS = vio.c
109
+ OBJS = vio.o
110
+ TARGET = vio
111
+ DLLIB = $(TARGET).bundle
112
+ EXTSTATIC =
113
+ STATIC_LIB =
114
+
115
+ BINDIR = $(bindir)
116
+ RUBYCOMMONDIR = $(sitedir)$(target_prefix)
117
+ RUBYLIBDIR = $(sitelibdir)$(target_prefix)
118
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
119
+ HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
120
+ ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
121
+
122
+ TARGET_SO = $(DLLIB)
123
+ CLEANLIBS = $(TARGET).bundle
124
+ CLEANOBJS = *.o *.bak
125
+
126
+ all: $(DLLIB)
127
+ static: $(STATIC_LIB)
128
+ .PHONY: all install static install-so install-rb
129
+ .PHONY: clean clean-so clean-rb
130
+
131
+ clean-rb-default::
132
+ clean-rb::
133
+ clean-so::
134
+ clean: clean-so clean-rb-default clean-rb
135
+ @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
136
+
137
+ distclean-rb-default::
138
+ distclean-rb::
139
+ distclean-so::
140
+ distclean: clean distclean-so distclean-rb-default distclean-rb
141
+ @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
142
+ @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
143
+ @-$(RMDIRS) $(DISTCLEANDIRS)
144
+
145
+ realclean: distclean
146
+ install: install-so install-rb
147
+
148
+ install-so: $(RUBYARCHDIR)
149
+ install-so: $(RUBYARCHDIR)/$(DLLIB)
150
+ $(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
151
+ @-$(MAKEDIRS) $(@D)
152
+ $(INSTALL_PROG) $(DLLIB) $(@D)
153
+ install-rb: pre-install-rb install-rb-default
154
+ install-rb-default: pre-install-rb-default
155
+ pre-install-rb: Makefile
156
+ pre-install-rb-default: Makefile
157
+ $(RUBYARCHDIR):
158
+ $(MAKEDIRS) $@
159
+
160
+ site-install: site-install-so site-install-rb
161
+ site-install-so: install-so
162
+ site-install-rb: install-rb
163
+
164
+ .SUFFIXES: .c .m .cc .cxx .cpp .C .o
165
+
166
+ .cc.o:
167
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
168
+
169
+ .cxx.o:
170
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
171
+
172
+ .cpp.o:
173
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
174
+
175
+ .C.o:
176
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
177
+
178
+ .c.o:
179
+ $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
180
+
181
+ $(DLLIB): $(OBJS) Makefile
182
+ @-$(RM) $(@)
183
+ $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
184
+
185
+
186
+
187
+ $(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h
@@ -0,0 +1,13 @@
1
+ require 'mkmf'
2
+
3
+ def add_define(name)
4
+ $defs.push("-D#{name}")
5
+ end
6
+
7
+ dir_config('vio')
8
+ add_define 'RUBY19' if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
9
+ add_define 'RUBY18' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
10
+
11
+ $defs.push("-pedantic")
12
+
13
+ create_makefile('vio')
@@ -0,0 +1,62 @@
1
+ have_func: checking for rb_thread_blocking_region()... -------------------- yes
2
+
3
+ "gcc -o conftest -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/ruby/backward -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe conftest.c -L. -L/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/lib -L. -L/usr/local/lib -lruby.1.9.1-static -lpthread -ldl -lobjc "
4
+ checked program was:
5
+ /* begin */
6
+ 1: #include "ruby.h"
7
+ 2:
8
+ 3: int main() {return 0;}
9
+ /* end */
10
+
11
+ "gcc -o conftest -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/ruby/backward -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe conftest.c -L. -L/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/lib -L. -L/usr/local/lib -lruby.1.9.1-static -lpthread -ldl -lobjc "
12
+ checked program was:
13
+ /* begin */
14
+ 1: #include "ruby.h"
15
+ 2:
16
+ 3: /*top*/
17
+ 4: int main() {return 0;}
18
+ 5: int t() { void ((*volatile p)()); p = (void ((*)()))rb_thread_blocking_region; return 0; }
19
+ /* end */
20
+
21
+ --------------------
22
+
23
+ have_macro: checking for RUBY_UBF_IO in ruby.h... -------------------- yes
24
+
25
+ "gcc -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/ruby/backward -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe -c conftest.c"
26
+ checked program was:
27
+ /* begin */
28
+ 1: #include "ruby.h"
29
+ 2:
30
+ 3: #include <ruby.h>
31
+ 4: /*top*/
32
+ 5: #ifndef RUBY_UBF_IO
33
+ 6: # error
34
+ 7: >>>>>> RUBY_UBF_IO undefined <<<<<<
35
+ 8: #endif
36
+ /* end */
37
+
38
+ --------------------
39
+
40
+ have_var: checking for rb_trap_immediate in ruby.h,rubysig.h... -------------------- no
41
+
42
+ "gcc -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/x86_64-darwin10.4.0 -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/ruby/backward -I/Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-missing-field-initializers -Wshorten-64-to-32 -Wno-long-long -fno-common -pipe -c conftest.c"
43
+ In file included from conftest.c:4:
44
+ /Users/lourens/.rvm/rubies/ruby-1.9.2-rc1/include/ruby-1.9.1/ruby/backward/rubysig.h:14:2: warning: #warning rubysig.h is obsolete
45
+ conftest.c: In function ‘t’:
46
+ conftest.c:8: error: ‘rb_trap_immediate’ undeclared (first use in this function)
47
+ conftest.c:8: error: (Each undeclared identifier is reported only once
48
+ conftest.c:8: error: for each function it appears in.)
49
+ checked program was:
50
+ /* begin */
51
+ 1: #include "ruby.h"
52
+ 2:
53
+ 3: #include <ruby.h>
54
+ 4: #include <rubysig.h>
55
+ 5:
56
+ 6: /*top*/
57
+ 7: int main() {return 0;}
58
+ 8: int t() { const volatile void *volatile p; p = &(&rb_trap_immediate)[0]; return 0; }
59
+ /* end */
60
+
61
+ --------------------
62
+
Binary file
@@ -0,0 +1,164 @@
1
+ #include "ruby.h"
2
+ #include <sys/types.h>
3
+ #include <sys/uio.h>
4
+ #include <unistd.h>
5
+
6
+ #ifndef RSTRING_PTR
7
+ #define RSTRING_PTR(obj) RSTRING(obj)->ptr
8
+ #endif
9
+
10
+ #ifndef RSTRING_LEN
11
+ #define RSTRING_LEN(obj) RSTRING(obj)->len
12
+ #endif
13
+
14
+ #ifndef RARRAY_PTR
15
+ #define RARRAY_PTR(obj) RARRAY(obj)->ptr
16
+ #endif
17
+
18
+ #ifndef RARRAY_LEN
19
+ #define RARRAY_LEN(obj) RARRAY(obj)->len
20
+ #endif
21
+
22
+ #ifdef RUBY19
23
+ #include "ruby/io.h"
24
+ #define TRAP_BEG
25
+ #define TRAP_END
26
+ #define declare_fptr \
27
+ rb_io_t *fptr;
28
+ #define declare_fd \
29
+ fd = fptr->fd;
30
+ #else
31
+ #include "rubysig.h"
32
+ #include "rubyio.h"
33
+ #define declare_fptr \
34
+ OpenFile *fptr;
35
+ #define declare_fd \
36
+ fd = fileno(fptr->f);
37
+ #endif
38
+
39
+ #define vio_error(err) \
40
+ rb_raise(rb_eIOError, err);
41
+
42
+ static void
43
+ vio_read_error()
44
+ {
45
+ switch(errno){
46
+ case EAGAIN:
47
+ vio_error("The file was marked for non-blocking I/O, and no data were ready to be read.");
48
+ case EBADF:
49
+ vio_error("File descriptor is not a valid file or socket descriptor open for reading.");
50
+ case EFAULT:
51
+ vio_error("Buffer points outside the allocated address space.");
52
+ case EINTR:
53
+ vio_error("A read from a slow device was interrupted before any data arrived by the delivery of a signal.");
54
+ case EINVAL:
55
+ vio_error("The pointer associated with file descriptor was negative.");
56
+ case EIO:
57
+ vio_error("An I/O error occurred while reading from the file system.");
58
+ case EISDIR:
59
+ vio_error("An attempt is made to read a directory.");
60
+ case ENOBUFS:
61
+ vio_error("An attempt to allocate a memory buffer fails.");
62
+ case ENOMEM:
63
+ vio_error("Insufficient memory is available.");
64
+ case ENXIO:
65
+ vio_error("An action is requested of a device that does not exist.");
66
+ }
67
+ }
68
+
69
+ static void
70
+ vio_write_error()
71
+ {
72
+ switch(errno){
73
+ case EDQUOT:
74
+ vio_error("The user's quota of disk blocks on the file system containing the file is exhausted.");
75
+ case EFAULT:
76
+ vio_error("Buffer points outside the allocated address space.");
77
+ case EWOULDBLOCK:
78
+ vio_error("The file descriptor is for a socket, is marked O_NONBLOCK, and write would block.");
79
+ case EDESTADDRREQ:
80
+ vio_error("The destination is no longer available when writing to a UNIX domain datagram socket on which connect(2) had been used to set a destination address.");
81
+ case EINVAL:
82
+ vio_error("Iov count is less than or equal to 0, or greater than MAX_IOV");
83
+ case ENOBUFS:
84
+ vio_error("The mbuf pool has been completely exhausted when writing to a socket.");
85
+ }
86
+ }
87
+
88
+ static VALUE
89
+ vio_read(VALUE io, VALUE iov)
90
+ {
91
+ int fd, i, size, bytes_read, cnt;
92
+ int expected = 0;
93
+ struct iovec iovs[IOV_MAX];
94
+ VALUE results;
95
+ declare_fptr;
96
+ if (RARRAY_LEN(iov) == 0) vio_error("No buffer offsets given");
97
+ GetOpenFile(io, fptr);
98
+ rb_io_check_readable(fptr);
99
+ declare_fd;
100
+ /* XXX Todo: Error handling */
101
+ lseek(fd, 0L, SEEK_SET);
102
+ fptr->lineno = 0;
103
+ cnt = RARRAY_LEN(iov);
104
+ results = rb_ary_new2(cnt);
105
+ for (i=0; i < cnt; i++) {
106
+ VALUE size_el = RARRAY_PTR(iov)[i];
107
+ Check_Type(size_el, T_FIXNUM);
108
+ size = FIX2INT(size_el);
109
+ expected = expected + size;
110
+ iovs[i].iov_len = size;
111
+ iovs[i].iov_base = calloc(1,size);
112
+ }
113
+ retry:
114
+ TRAP_BEG;
115
+ bytes_read = readv(fd,iovs,cnt);
116
+ TRAP_END;
117
+ if (bytes_read < expected && bytes_read > 0) goto retry;
118
+ vio_read_error();
119
+ for (i=0; i < cnt; i++) {
120
+ rb_ary_push(results, rb_tainted_str_new((char *)iovs[i].iov_base, iovs[i].iov_len));
121
+ }
122
+ return results;
123
+ }
124
+
125
+ static VALUE
126
+ vio_write(VALUE io, VALUE iov)
127
+ {
128
+ int i, size, bytes_written, fd, cnt;
129
+ int expected = 0;
130
+ VALUE results;
131
+ struct iovec iovs[IOV_MAX];
132
+ declare_fptr;
133
+ Check_Type(iov, T_ARRAY);
134
+ cnt = RARRAY_LEN(iov);
135
+ if (cnt == 0) vio_error("No buffers to write given");
136
+ GetOpenFile(io, fptr);
137
+ rb_io_check_writable(fptr);
138
+ declare_fd;
139
+ results = rb_ary_new2(cnt);
140
+ for (i=0; i < cnt; i++) {
141
+ VALUE str = RARRAY_PTR(iov)[i];
142
+ Check_Type(str, T_STRING);
143
+ size = RSTRING_LEN(str);
144
+ expected = expected + size;
145
+ iovs[i].iov_len = size;
146
+ iovs[i].iov_base = RSTRING_PTR(str);
147
+ }
148
+ retry:
149
+ TRAP_BEG;
150
+ bytes_written = writev(fd,iovs,cnt);
151
+ TRAP_END;
152
+ if (bytes_written < expected && bytes_written > 0) goto retry;
153
+ vio_write_error();
154
+ for (i=0; i < cnt; i++) {
155
+ rb_ary_push(results, INT2FIX(iovs[i].iov_len));
156
+ }
157
+ return results;
158
+ }
159
+
160
+ void Init_vio()
161
+ {
162
+ rb_define_method(rb_cIO, "readv", vio_read, -2);
163
+ rb_define_method(rb_cIO, "writev", vio_write, -2);
164
+ }
Binary file
@@ -0,0 +1 @@
1
+ 8=FIX.4.49=4535=049=TW56=ISLD34=352=20000426-12:05:0610=220
File without changes
@@ -0,0 +1,13 @@
1
+ require 'fileutils'
2
+ require 'test/unit'
3
+ require 'vio'
4
+
5
+ FIXTURES = File.dirname(__FILE__) + "/fixtures"
6
+
7
+ def fixtures(*files)
8
+ files.map{|f| File.join(FIXTURES,f) }
9
+ end
10
+
11
+ def fixture(file)
12
+ file =~ /\// ? file : fixtures(*file).first
13
+ end
@@ -0,0 +1,54 @@
1
+ $:.unshift "."
2
+ require File.dirname(__FILE__) + '/helper'
3
+
4
+ class TestVectoredIO < Test::Unit::TestCase
5
+ CONTENT = [ "8=FIX.4.4", "\x019=45", "\x0135=0", "\x0149=TW", "\x0156=ISLD",
6
+ "\x0134=3\x0152=20000426-12:05:06", "\x0110=220\x01" ]
7
+ LENGTHS = [9, 5, 5, 6, 8, 26, 8]
8
+
9
+ def test_readv_arguments
10
+ io = File.open(fixture('fix.txt'))
11
+ assert_raises TypeError do
12
+ io.readv nil
13
+ end
14
+ assert_raises TypeError do
15
+ io.readv 'string'
16
+ end
17
+ assert_raises IOError do
18
+ io.readv
19
+ end
20
+ assert_raises IOError do
21
+ io.readv -2
22
+ end
23
+ io.close
24
+ end
25
+
26
+ def test_readv
27
+ io = File.open(fixture('fix.txt'))
28
+ expected = CONTENT
29
+ assert_equal expected, io.readv(*LENGTHS)
30
+ io.close
31
+ end
32
+
33
+ def test_writev_arguments
34
+ io = File.open(fixture('writable.txt'), 'w')
35
+ assert_raises TypeError do
36
+ io.writev nil
37
+ end
38
+ assert_raises TypeError do
39
+ io.writev 2
40
+ end
41
+ assert_raises IOError do
42
+ io.writev
43
+ end
44
+ io.close
45
+ end
46
+
47
+ def test_writev
48
+ io = File.open(fixture('writable.txt'), 'w')
49
+ expected = LENGTHS
50
+ assert_equal expected, io.writev(*CONTENT.dup)
51
+ io.truncate(0)
52
+ io.close
53
+ end
54
+ end
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "vio"
3
+ s.version = "0.3.0"
4
+ s.date = "2009-09-19"
5
+ s.summary = "Vectored I/O extension for Ruby"
6
+ s.email = "lourens@methodmissing.com"
7
+ s.homepage = "http://github.com/methodmissing/vio"
8
+ s.description = "Vectored I/O extension for Ruby MRI (1.8.{6,7} and 1.9.2)"
9
+ s.has_rdoc = true
10
+ s.authors = ["Lourens Naudé (methodmissing)"]
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = Dir.glob("{bench,ext,test}/**/*") + %w[README Rakefile ext/vio/extconf.rb ext/vio/vio.c vio.gemspec]
13
+ s.rdoc_options = ["--main", "README"]
14
+ s.extra_rdoc_files = ["README"]
15
+ s.extensions << "ext/vio/extconf.rb"
16
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vio
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
+ platform: ruby
11
+ authors:
12
+ - "Lourens Naud\xC3\xA9 (methodmissing)"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2009-09-19 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Vectored I/O extension for Ruby MRI (1.8.{6,7} and 1.9.2)
22
+ email: lourens@methodmissing.com
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/vio/extconf.rb
27
+ extra_rdoc_files:
28
+ - README
29
+ files:
30
+ - bench/patched_logger.rb
31
+ - bench/read.rb
32
+ - bench/write.rb
33
+ - ext/vio/extconf.rb
34
+ - ext/vio/Makefile
35
+ - ext/vio/mkmf.log
36
+ - ext/vio/vio.bundle
37
+ - ext/vio/vio.c
38
+ - ext/vio/vio.o
39
+ - test/fixtures/fix.txt
40
+ - test/fixtures/writable.txt
41
+ - test/helper.rb
42
+ - test/test_vio.rb
43
+ - README
44
+ - Rakefile
45
+ - vio.gemspec
46
+ has_rdoc: true
47
+ homepage: http://github.com/methodmissing/vio
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --main
53
+ - README
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Vectored I/O extension for Ruby
79
+ test_files: []
80
+