jio 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ .libs/*
2
+ .rbx/*
3
+ *.rbc
4
+ *.lo
5
+ *.la
6
+ *.lai
7
+ *.dylib.dSYM
8
+ *.dylib
9
+ *.o
10
+ *.a
11
+ *.log
12
+ tmp/*
13
+ true/*
14
+ *.bundle
15
+ *.gem
16
+ doc/*
17
+ .DS_Store
18
+ ext/libjio
19
+ pkg
20
+ scratch
21
+ ext/jio/dst
22
+ ext/jio/Makefile
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ rvm:
3
+ - rbx-18mode
4
+ - rbx-19mode
5
+ - ree
6
+ - 1.8.7
7
+ - 1.9.2
8
+ - 1.9.3
9
+ - ruby-head
10
+ script: "bundle exec rake"
11
+ gemfile:
12
+ - Gemfile
13
+ notifications:
14
+ recipients:
15
+ - lourens@methodmissing.com
16
+ branches:
17
+ only:
18
+ - master
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
@@ -0,0 +1,19 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jio (0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ rake (0.9.2.2)
10
+ rake-compiler (0.8.1)
11
+ rake
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ jio!
18
+ rake
19
+ rake-compiler (~> 0.8.1)
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ jio is copyrighted Free Software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them.
3
+
4
+ You can redistribute it and/or modify it under the terms of the GNU
5
+ Lesser General Public License as published by the Free Software Foundation,
6
+ version 2.1 or later {LGPLv2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt]
7
+ (see link:COPYING).
8
+
9
+ eio is distributed in the hope that it will be useful, but WITHOUT
10
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12
+ License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License
15
+ along with this library; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
@@ -0,0 +1,92 @@
1
+ = jio - transactional, journaled file I/O for Ruby {<img src="https://secure.travis-ci.org/methodmissing/jio.png" alt="Build Status" />}[http://travis-ci.org/methodmissing/jio]
2
+
3
+ (c) 2011 Lourens Naudé (methodmissing), with API guidance from the libjio (http://blitiri.com.ar/p/libjio/) Python extension
4
+
5
+ http://github.com/methodmissing/jio
6
+
7
+ == Why you may need this
8
+
9
+ Some problems don't map well to database systems and journaled flat files are often a good fit for append
10
+ logs and Event Sourcing implementations. The API is simple (modeled to known UNIX and libc APIs), there's no external dependencies (we vendor libjio) and is known to work on most POSIX systems. The library (libjio) and thus this Ruby extension guarantees file integrity even after unexpected crashes, never leaving your files in an inconsistent state. Crash recovery is fast and safe as well.
11
+
12
+ == How it works
13
+
14
+ On the disk, the file you work on is exactly like a regular one, but a special directory is created to store in-flight transactions (lock file and transaction in contents). For further details see http://blitiri.com.ar/p/libjio/doc/libjio.html
15
+
16
+ == Requirements
17
+
18
+ * A POSIX compliant OS, known to work well on Linux, BSD variants and Mac OS X
19
+ * Ruby MRI 1.8, 1.9 or Rubinius
20
+ * A C compiler
21
+
22
+ == Installation
23
+
24
+ Rubygems installation
25
+
26
+ gem install jio # pending upload to rubygems.org
27
+
28
+ Building from source
29
+
30
+ git clone git@github.com:methodmissing/jio.git
31
+ rake
32
+
33
+ Running tests
34
+
35
+ rake test
36
+
37
+ == Documentation
38
+
39
+ RDOC document pending.
40
+
41
+ == How to use
42
+
43
+ # libjio file handle
44
+ file = JIO.open("file.jio", JIO::RDWR | JIO::CREAT, 0600, JIO::J_LINGER)
45
+
46
+ # spawn a new lingering transaction with a write operation
47
+ trans = file.transaction(JIO::J_LINGER)
48
+ trans.write('COMMIT', 0)
49
+
50
+ # spawn 2 read operations
51
+ trans.read(2, 2)
52
+ trans.read(2, 4)
53
+
54
+ # Empty views - not committed yet
55
+ trans.views # []
56
+ # Commit write / read operations to / from disk
57
+ trans.commit # true
58
+
59
+ # View represents the 2 read operations requested earlier
60
+ trans.views %w(MM IT)
61
+ trans.committed? # true
62
+
63
+ # rollback and cleanup
64
+ trans.rollback
65
+ trans.release
66
+ file.close
67
+
68
+ # Assert journal integrity
69
+ JIO.check("file.jio", 0) # {"reapplied"=>1,
70
+ # "invalid"=>0,
71
+ # "corrupt"=>0,
72
+ # "total"=>1,
73
+ # "in_progress"=>0,
74
+ # "broken"=>0}
75
+
76
+ See the unit tests for further examples.
77
+
78
+ == Todo
79
+
80
+ * More intuitive API
81
+ * Release the GIL where appropriate
82
+ * Support vectored I/O readv and writev
83
+ * More examples
84
+ * Stress tests
85
+ * Better test coverage
86
+ * Gem release
87
+
88
+ == Contact, feedback and bugs
89
+
90
+ This project is still work in progress and I'm looking for guidance on API design, use cases and any outlier experiences. Please log bugs and suggestions at https://github.com/methodmissing/jio/issues
91
+
92
+ Thanks !
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems' unless defined?(Gem)
4
+ require 'rake' unless defined?(Rake)
5
+
6
+ require 'rake/extensiontask'
7
+ require 'rake/testtask'
8
+ begin
9
+ require 'rdoc/task'
10
+ rescue LoadError # fallback to older 1.8.7 rubies
11
+ require 'rake/rdoctask'
12
+ end
13
+
14
+ gemspec = eval(IO.read('jio.gemspec'))
15
+
16
+ Gem::PackageTask.new(gemspec) do |pkg|
17
+ end
18
+
19
+ Rake::ExtensionTask.new('jio', gemspec) do |ext|
20
+ ext.name = 'jio_ext'
21
+ ext.ext_dir = 'ext/jio'
22
+
23
+ CLEAN.include 'ext/jio/dst'
24
+ CLEAN.include 'ext/libjio'
25
+ CLEAN.include 'lib/**/jio_ext.*'
26
+ end
27
+
28
+ Rake::RDocTask.new do |rd|
29
+ files = FileList["README.rdoc", "lib/**/*.rb", "ext/jio/*.c"]
30
+ rd.title = "jio - transactional, journaled file I/O for Ruby"
31
+ rd.main = "README.rdoc"
32
+ rd.rdoc_dir = "doc"
33
+ rd.options << "--promiscuous"
34
+ rd.rdoc_files.include(files)
35
+ end
36
+
37
+ desc 'Run jio tests'
38
+ Rake::TestTask.new(:test) do |t|
39
+ t.test_files = Dir.glob("test/**/test_*.rb")
40
+ t.verbose = true
41
+ t.warning = true
42
+ end
43
+
44
+ task :test => :compile
45
+ task :default => :test
@@ -0,0 +1,123 @@
1
+ # encoding: utf-8
2
+
3
+ require 'mkmf'
4
+ require 'pathname'
5
+
6
+ def sys(cmd, err_msg)
7
+ p cmd
8
+ system(cmd) || fail(err_msg)
9
+ end
10
+
11
+ def fail(msg)
12
+ STDERR.puts msg
13
+ exit(1)
14
+ end
15
+
16
+ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
17
+
18
+ # XXX fallbacks specific to Darwin for JRuby (does not set these values in RbConfig::CONFIG)
19
+ LIBEXT = RbConfig::CONFIG['LIBEXT'] || 'a'
20
+ DLEXT = RbConfig::CONFIG['DLEXT'] || 'bundle'
21
+
22
+ cwd = Pathname(File.expand_path(File.dirname(__FILE__)))
23
+ dst_path = cwd + 'dst'
24
+ libs_path = dst_path + 'lib'
25
+ vendor_path = cwd + '..'
26
+ libjio_path = vendor_path + 'libjio'
27
+ libjio_include_path = libjio_path + 'libjio'
28
+
29
+ # Courtesy of EventMachine and @tmm1
30
+ def check_libs libs = [], fatal = false
31
+ libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
32
+ end
33
+
34
+ def check_heads heads = [], fatal = false
35
+ heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
36
+ end
37
+
38
+ case RUBY_PLATFORM
39
+ when /mswin32/, /mingw32/, /bccwin32/
40
+ check_heads(%w[windows.h winsock.h], true)
41
+ check_libs(%w[kernel32 rpcrt4 gdi32], true)
42
+
43
+ if GNU_CHAIN
44
+ CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++"
45
+ else
46
+ $defs.push "-EHs"
47
+ $defs.push "-GR"
48
+ end
49
+
50
+ when /solaris/
51
+ add_define 'OS_SOLARIS8'
52
+
53
+ if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler
54
+ # SUN CHAIN
55
+ add_define 'CC_SUNWspro'
56
+ $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
57
+ $CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
58
+ CONFIG['CCDLFLAGS'] = "-KPIC"
59
+ CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
60
+ else
61
+ # GNU CHAIN
62
+ # on Unix we need a g++ link, not gcc.
63
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
64
+ end
65
+
66
+ when /openbsd/
67
+ # OpenBSD branch contributed by Guillaume Sellier.
68
+
69
+ # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
70
+ CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
71
+ CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
72
+
73
+ when /darwin/
74
+ # on Unix we need a g++ link, not gcc.
75
+ # Ff line contributed by Daniel Harple.
76
+ CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
77
+
78
+ when /aix/
79
+ CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G -Wl,-brtl"
80
+
81
+ else
82
+ # on Unix we need a g++ link, not gcc.
83
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
84
+ end
85
+
86
+ # extract dependencies
87
+ unless File.directory?(libjio_path)
88
+ fail "The 'tar' (creates and manipulates streaming archive files) utility is required to extract dependencies" if `which tar`.strip.empty?
89
+ Dir.chdir(vendor_path) do
90
+ sys "tar xvzf libjio.tar.gz", "Could not extract the libjio archive!"
91
+ end
92
+ end
93
+
94
+ # build libjio
95
+ lib = libs_path + "libjio.#{LIBEXT}"
96
+ Dir.chdir libjio_path do
97
+ sys "make DEBUG=1 PREFIX=#{dst_path} install", "libjio compile error!"
98
+ end unless File.exist?(lib)
99
+
100
+ dir_config('jio')
101
+
102
+ have_func('rb_thread_blocking_region')
103
+
104
+ $INCFLAGS << " -I#{libjio_include_path}"
105
+
106
+ $LIBPATH << libs_path.to_s
107
+
108
+ # Special case to prevent Rubinius compile from linking system libjio if present
109
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/ && RUBY_PLATFORM =~ /linux/
110
+ CONFIG['LDSHARED'] = "#{CONFIG['LDSHARED']} -Wl,-rpath=#{libs_path.to_s}"
111
+ end
112
+
113
+ fail "Error compiling and linking libjio" unless have_library("jio")
114
+
115
+ $defs << "-pedantic"
116
+ # libjio requires large file support
117
+ $defs << "-D_LARGEFILE_SOURCE=1"
118
+ $defs << "-D_LARGEFILE64_SOURCE=1"
119
+
120
+ $CFLAGS << ' -Wall -funroll-loops'
121
+ $CFLAGS << ' -Wextra -O0 -ggdb3' if ENV['DEBUG']
122
+
123
+ create_makefile('jio_ext')
@@ -0,0 +1,488 @@
1
+ #include "jio_ext.h"
2
+
3
+ /*
4
+ * GC callbacks for JIO::File
5
+ */
6
+ static void rb_jio_free_file(void *ptr)
7
+ {
8
+ jio_jfs_wrapper *file = (jio_jfs_wrapper *)ptr;
9
+ if (file) {
10
+ if (file->fs != NULL && !(file->flags & JIO_FILE_CLOSED)) jclose(file->fs);
11
+ xfree(file);
12
+ }
13
+ }
14
+
15
+ /*
16
+ * call-seq:
17
+ * JIO.open("/path/file", JIO::CREAT | JIO::RDWR, 0600, JIO::J_LINGER) => JIO::File
18
+ *
19
+ * Returns a handle to a journaled file instance. Same semantics as the UNIX open(2) libc call, with
20
+ * an additional one for libjio specific flags.
21
+ *
22
+ * === Examples
23
+ * JIO.open("/path/file", JIO::CREAT | JIO::RDWR, 0600, JIO::J_LINGER) => JIO::File
24
+ *
25
+ */
26
+
27
+ static VALUE rb_jio_s_open(JIO_UNUSED VALUE jio, VALUE path, VALUE flags, VALUE mode, VALUE jflags)
28
+ {
29
+ VALUE obj;
30
+ jio_jfs_wrapper *file = NULL;
31
+ Check_Type(path, T_STRING);
32
+ Check_Type(flags, T_FIXNUM);
33
+ Check_Type(mode, T_FIXNUM);
34
+ Check_Type(jflags, T_FIXNUM);
35
+ obj = Data_Make_Struct(rb_cJioFile, jio_jfs_wrapper, 0, rb_jio_free_file, file);
36
+ TRAP_BEG;
37
+ file->fs = jopen(RSTRING_PTR(path), FIX2INT(flags), FIX2INT(mode), FIX2UINT(jflags));
38
+ TRAP_END;
39
+ if (file->fs == NULL) {
40
+ xfree(file);
41
+ rb_sys_fail("jopen");
42
+ }
43
+ file->flags = 0;
44
+ rb_obj_call_init(obj, 0, NULL);
45
+ return obj;
46
+ }
47
+
48
+
49
+ /*
50
+ * call-seq:
51
+ * file.sync => boolean
52
+ *
53
+ * Sync a file to disk. Makes sense only when using lingering transactions.
54
+ *
55
+ * === Examples
56
+ * file.sync => boolean
57
+ *
58
+ */
59
+
60
+ static VALUE rb_jio_file_sync(VALUE obj)
61
+ {
62
+ JioGetFile(obj);
63
+ TRAP_BEG;
64
+ return (jsync(file->fs) == 0) ? Qtrue : Qfalse;
65
+ TRAP_END;
66
+ }
67
+
68
+ /*
69
+ * call-seq:
70
+ * file.close => boolean
71
+ *
72
+ * After a call to this method, the memory allocated for the open file will be freed. If there was an
73
+ * autosync thread started for this file, it will be stopped.
74
+ *
75
+ * === Examples
76
+ * file.close => boolean
77
+ *
78
+ */
79
+
80
+ static VALUE rb_jio_file_close(VALUE obj)
81
+ {
82
+ JioGetFile(obj);
83
+ TRAP_BEG;
84
+ if (jclose(file->fs) != 0) return Qfalse;
85
+ TRAP_END;
86
+ file->flags |= JIO_FILE_CLOSED;
87
+ return Qtrue;
88
+ }
89
+
90
+ /*
91
+ * call-seq:
92
+ * file.move_journal("/path") => boolean
93
+ *
94
+ * Changes the location of the journal direction. The file cannot be in use at this time.
95
+ *
96
+ * === Examples
97
+ * file.move_journal("/path") => boolean
98
+ *
99
+ */
100
+
101
+ static VALUE rb_jio_file_move_journal(VALUE obj, VALUE path)
102
+ {
103
+ JioGetFile(obj);
104
+ Check_Type(path, T_STRING);
105
+ TRAP_BEG;
106
+ return (jmove_journal(file->fs, RSTRING_PTR(path)) == 0) ? Qtrue : Qfalse;
107
+ TRAP_END;
108
+ }
109
+
110
+ /*
111
+ * call-seq:
112
+ * file.autosync(5, 4000) => boolean
113
+ *
114
+ * Syncs to disk every X seconds, or every Y bytes written. Only one autosync thread per open file is
115
+ * allowed. Only makes sense with lingering transactions.
116
+ *
117
+ * === Examples
118
+ * file.autosync(5, 4000) => boolean
119
+ *
120
+ */
121
+
122
+ static VALUE rb_jio_file_autosync(VALUE obj, VALUE max_seconds, VALUE max_bytes)
123
+ {
124
+ JioGetFile(obj);
125
+ Check_Type(max_seconds, T_FIXNUM);
126
+ Check_Type(max_bytes, T_FIXNUM);
127
+ TRAP_BEG;
128
+ return (jfs_autosync_start(file->fs, (time_t)FIX2LONG(max_seconds), (size_t)FIX2LONG(max_bytes)) == 0) ? Qtrue : Qfalse;
129
+ TRAP_END;
130
+ }
131
+
132
+ /*
133
+ * call-seq:
134
+ * file.stop_autosync => boolean
135
+ *
136
+ * Stops a previously started autosync thread.
137
+ *
138
+ * === Examples
139
+ * file.stop_autosync => boolean
140
+ *
141
+ */
142
+
143
+ static VALUE rb_jio_file_stop_autosync(VALUE obj)
144
+ {
145
+ JioGetFile(obj);
146
+ TRAP_BEG;
147
+ return (jfs_autosync_stop(file->fs) == 0) ? Qtrue : Qfalse;
148
+ TRAP_END;
149
+ }
150
+
151
+ /*
152
+ * call-seq:
153
+ * file.read(10) => String
154
+ *
155
+ * Reads from a libjio file handle. Works just like UNIX read(2)
156
+ *
157
+ * === Examples
158
+ * file.read(10) => String
159
+ *
160
+ */
161
+
162
+ static VALUE rb_jio_file_read(VALUE obj, VALUE length)
163
+ {
164
+ ssize_t bytes;
165
+ char *buf = NULL;
166
+ ssize_t len;
167
+ JioGetFile(obj);
168
+ AssertLength(length);
169
+ len = (ssize_t)FIX2LONG(length);
170
+ buf = xmalloc(len + 1);
171
+ if (buf == NULL) rb_memerror();
172
+ TRAP_BEG;
173
+ bytes = jread(file->fs, buf, len);
174
+ TRAP_END;
175
+ if (bytes == -1) {
176
+ xfree(buf);
177
+ rb_sys_fail("jread");
178
+ }
179
+ return JioEncode(rb_str_new(buf, (long)len));
180
+ }
181
+
182
+ /*
183
+ * call-seq:
184
+ * file.pread(10, 10) => String
185
+ *
186
+ * Reads from a libjio file handle at a given offset. Works just like UNIX pread(2)
187
+ *
188
+ * === Examples
189
+ * file.pread(10, 10) => String
190
+ *
191
+ */
192
+
193
+ static VALUE rb_jio_file_pread(VALUE obj, VALUE length, VALUE offset)
194
+ {
195
+ ssize_t bytes;
196
+ char *buf = NULL;
197
+ ssize_t len;
198
+ JioGetFile(obj);
199
+ AssertLength(length);
200
+ AssertOffset(offset);
201
+ len = (ssize_t)FIX2LONG(length);
202
+ buf = xmalloc(len + 1);
203
+ if (buf == NULL) rb_memerror();
204
+ TRAP_BEG;
205
+ bytes = jpread(file->fs, buf, len, (off_t)NUM2OFFT(offset));
206
+ TRAP_END;
207
+ if (bytes == -1) {
208
+ xfree(buf);
209
+ rb_sys_fail("jpread");
210
+ }
211
+ return JioEncode(rb_str_new(buf, (long)len));
212
+ }
213
+
214
+ /*
215
+ * call-seq:
216
+ * file.write("buffer") => Fixnum
217
+ *
218
+ * Writes to a libjio file handle. Works just like UNIX write(2)
219
+ *
220
+ * === Examples
221
+ * file.write("buffer") => Fixnum
222
+ *
223
+ */
224
+
225
+ static VALUE rb_jio_file_write(VALUE obj, VALUE buf)
226
+ {
227
+ ssize_t bytes;
228
+ JioGetFile(obj);
229
+ Check_Type(buf, T_STRING);
230
+ TRAP_BEG;
231
+ bytes = jwrite(file->fs, RSTRING_PTR(buf), (size_t)RSTRING_LEN(buf));
232
+ TRAP_END;
233
+ if (bytes == -1) rb_sys_fail("jwrite");
234
+ return INT2NUM(bytes);
235
+ }
236
+
237
+ /*
238
+ * call-seq:
239
+ * file.pwrite("buffer", 10) => Fixnum
240
+ *
241
+ * Writes to a libjio file handle at a given offset. Works just like UNIX pwrite(2)
242
+ *
243
+ * === Examples
244
+ * file.pwrite("buffer", 10) => Fixnum
245
+ *
246
+ */
247
+
248
+ static VALUE rb_jio_file_pwrite(VALUE obj, VALUE buf, VALUE offset)
249
+ {
250
+ ssize_t bytes;
251
+ JioGetFile(obj);
252
+ Check_Type(buf, T_STRING);
253
+ AssertOffset(offset);
254
+ TRAP_BEG;
255
+ bytes = jpwrite(file->fs, RSTRING_PTR(buf), (size_t)RSTRING_LEN(buf), (off_t)NUM2OFFT(offset));
256
+ TRAP_END;
257
+ if (bytes == -1) rb_sys_fail("jpwrite");
258
+ return INT2NUM(bytes);
259
+ }
260
+
261
+ /*
262
+ * call-seq:
263
+ * file.lseek(10, JIO::SEEK_SET) => Fixnum
264
+ *
265
+ * Reposition the file pointer to the given offset, according to the whence directive.
266
+ *
267
+ * === Examples
268
+ * file.lseek(10, JIO::SEEK_SET) => Fixnum
269
+ *
270
+ */
271
+
272
+ static VALUE rb_jio_file_lseek(VALUE obj, VALUE offset, VALUE whence)
273
+ {
274
+ off_t off;
275
+ JioGetFile(obj);
276
+ AssertOffset(offset);
277
+ Check_Type(whence, T_FIXNUM);
278
+ TRAP_BEG;
279
+ off = jlseek(file->fs, (off_t)NUM2OFFT(offset), FIX2INT(whence));
280
+ TRAP_END;
281
+ if (off == -1) rb_sys_fail("jlseek");
282
+ return OFFT2NUM(off);
283
+ }
284
+
285
+ /*
286
+ * call-seq:
287
+ * file.truncate(10) => Fixnum
288
+ *
289
+ * Truncate the file to the given size.
290
+ *
291
+ * === Examples
292
+ * file.truncate(10) => Fixnum
293
+ *
294
+ */
295
+
296
+ static VALUE rb_jio_file_truncate(VALUE obj, VALUE length)
297
+ {
298
+ off_t len;
299
+ JioGetFile(obj);
300
+ AssertLength(length);
301
+ TRAP_BEG;
302
+ len = jtruncate(file->fs, (off_t)NUM2OFFT(length));
303
+ TRAP_END;
304
+ if (len == -1) rb_sys_fail("jtruncate");
305
+ return OFFT2NUM(len);
306
+ }
307
+
308
+ /*
309
+ * call-seq:
310
+ * file.fileno => Fixnum
311
+ *
312
+ * Return the file descriptor number for the file.
313
+ *
314
+ * === Examples
315
+ * file.fileno => Fixnum
316
+ *
317
+ */
318
+
319
+ static VALUE rb_jio_file_fileno(VALUE obj)
320
+ {
321
+ int fd;
322
+ JioGetFile(obj);
323
+ TRAP_BEG;
324
+ fd = jfileno(file->fs);
325
+ TRAP_END;
326
+ if (fd == -1) rb_sys_fail("jfileno");
327
+ return INT2NUM(fd);
328
+ }
329
+
330
+ /*
331
+ * call-seq:
332
+ * file.rewind => nil
333
+ *
334
+ * Adjusts the file so that the next I/O operation will take place at the beginning of the file.
335
+ *
336
+ * === Examples
337
+ * file.rewind => nil
338
+ *
339
+ */
340
+
341
+ static VALUE rb_jio_file_rewind(VALUE obj)
342
+ {
343
+ JioGetFile(obj);
344
+ TRAP_BEG;
345
+ jrewind(file->fs);
346
+ TRAP_END;
347
+ return Qnil;
348
+ }
349
+
350
+ /*
351
+ * call-seq:
352
+ * file.tell => Fixnum
353
+ *
354
+ * Determine the current file offset.
355
+ *
356
+ * === Examples
357
+ * file.tell => Fixnum
358
+ *
359
+ */
360
+
361
+ static VALUE rb_jio_file_tell(VALUE obj)
362
+ {
363
+ long size;
364
+ JioGetFile(obj);
365
+ TRAP_BEG;
366
+ size = jftell(file->fs);
367
+ TRAP_END;
368
+ if (size == -1) rb_sys_fail("jftell");
369
+ return INT2NUM(size);
370
+ }
371
+
372
+ /*
373
+ * call-seq:
374
+ * file.eof? => boolean
375
+ *
376
+ * Check for end-of-file.
377
+ *
378
+ * === Examples
379
+ * file.eof? => boolean
380
+ *
381
+ */
382
+
383
+ static VALUE rb_jio_file_eof_p(VALUE obj)
384
+ {
385
+ JioGetFile(obj);
386
+ TRAP_BEG;
387
+ return (jfeof(file->fs) != 0) ? Qtrue : Qfalse;
388
+ TRAP_END;
389
+ }
390
+
391
+ /*
392
+ * call-seq:
393
+ * file.error? => boolean
394
+ *
395
+ * Determines if an error condition has occurred.
396
+ *
397
+ * === Examples
398
+ * file.error? => boolean
399
+ *
400
+ */
401
+
402
+ static VALUE rb_jio_file_error_p(VALUE obj)
403
+ {
404
+ int res;
405
+ JioGetFile(obj);
406
+ TRAP_BEG;
407
+ res = jferror(file->fs);
408
+ TRAP_END;
409
+ if (res == 0) return Qfalse;
410
+ return INT2NUM(res);
411
+ }
412
+
413
+ /*
414
+ * call-seq:
415
+ * file.clearerr => nil
416
+ *
417
+ * Resets the error flag for this file, if any.
418
+ *
419
+ * === Examples
420
+ * file.clearerr => nil
421
+ *
422
+ */
423
+
424
+ static VALUE rb_jio_file_clearerr(VALUE obj)
425
+ {
426
+ JioGetFile(obj);
427
+ TRAP_BEG;
428
+ jclearerr(file->fs);
429
+ TRAP_END;
430
+ return Qnil;
431
+ }
432
+
433
+ /*
434
+ * call-seq:
435
+ * file.transaction(JIO::J_LINGER) => JIO::Transaction
436
+ *
437
+ * Creates a new low level transaction from a libjio file reference.
438
+ *
439
+ * === Examples
440
+ * file.transaction(JIO::J_LINGER) => JIO::Transaction
441
+ *
442
+ */
443
+
444
+ VALUE rb_jio_file_new_transaction(VALUE obj, VALUE flags)
445
+ {
446
+ VALUE transaction;
447
+ jio_jtrans_wrapper *trans = NULL;
448
+ JioGetFile(obj);
449
+ Check_Type(flags, T_FIXNUM);
450
+ transaction = Data_Make_Struct(rb_cJioTransaction, jio_jtrans_wrapper, rb_jio_mark_transaction, rb_jio_free_transaction, trans);
451
+ TRAP_BEG;
452
+ trans->trans = jtrans_new(file->fs, FIX2INT(flags));
453
+ TRAP_END;
454
+ if (trans->trans == NULL) {
455
+ xfree(trans);
456
+ rb_sys_fail("jtrans_new");
457
+ }
458
+ trans->views = Qnil;
459
+ trans->flags = 0;
460
+ rb_obj_call_init(transaction, 0, NULL);
461
+ return transaction;
462
+ }
463
+
464
+ void _init_rb_jio_file()
465
+ {
466
+ rb_define_module_function(mJio, "open", rb_jio_s_open, 4);
467
+
468
+ rb_cJioFile = rb_define_class_under(mJio, "File", rb_cObject);
469
+
470
+ rb_define_method(rb_cJioFile, "sync", rb_jio_file_sync, 0);
471
+ rb_define_method(rb_cJioFile, "close", rb_jio_file_close, 0);
472
+ rb_define_method(rb_cJioFile, "move_journal", rb_jio_file_move_journal, 1);
473
+ rb_define_method(rb_cJioFile, "autosync", rb_jio_file_autosync, 2);
474
+ rb_define_method(rb_cJioFile, "stop_autosync", rb_jio_file_stop_autosync, 0);
475
+ rb_define_method(rb_cJioFile, "read", rb_jio_file_read, 1);
476
+ rb_define_method(rb_cJioFile, "pread", rb_jio_file_pread, 2);
477
+ rb_define_method(rb_cJioFile, "write", rb_jio_file_write, 1);
478
+ rb_define_method(rb_cJioFile, "pwrite", rb_jio_file_pwrite, 2);
479
+ rb_define_method(rb_cJioFile, "lseek", rb_jio_file_lseek, 2);
480
+ rb_define_method(rb_cJioFile, "truncate", rb_jio_file_truncate, 1);
481
+ rb_define_method(rb_cJioFile, "fileno", rb_jio_file_fileno, 0);
482
+ rb_define_method(rb_cJioFile, "rewind", rb_jio_file_rewind, 0);
483
+ rb_define_method(rb_cJioFile, "tell", rb_jio_file_tell, 0);
484
+ rb_define_method(rb_cJioFile, "eof?", rb_jio_file_eof_p, 0);
485
+ rb_define_method(rb_cJioFile, "error?", rb_jio_file_error_p, 0);
486
+ rb_define_method(rb_cJioFile, "clearerr", rb_jio_file_clearerr, 0);
487
+ rb_define_method(rb_cJioFile, "transaction", rb_jio_file_new_transaction, 1);
488
+ }