vio 0.3.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/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
+