jio 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +22 -0
- data/.travis.yml +18 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +19 -0
- data/LICENSE +16 -0
- data/README.rdoc +92 -0
- data/Rakefile +45 -0
- data/ext/jio/extconf.rb +123 -0
- data/ext/jio/file.c +488 -0
- data/ext/jio/file.h +20 -0
- data/ext/jio/jio_ext.c +119 -0
- data/ext/jio/jio_ext.h +46 -0
- data/ext/jio/jio_prelude.h +22 -0
- data/ext/jio/jruby.h +20 -0
- data/ext/jio/rubinius.h +22 -0
- data/ext/jio/ruby18.h +43 -0
- data/ext/jio/ruby19.h +15 -0
- data/ext/jio/transaction.c +260 -0
- data/ext/jio/transaction.h +26 -0
- data/ext/libjio.tar.gz +0 -0
- data/jio.gemspec +22 -0
- data/lib/jio.rb +12 -0
- data/lib/jio/file.rb +24 -0
- data/lib/jio/version.rb +5 -0
- data/test/helper.rb +32 -0
- data/test/test_file.rb +116 -0
- data/test/test_jio.rb +23 -0
- data/test/test_transaction.rb +111 -0
- metadata +90 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -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.lock
ADDED
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
|
data/README.rdoc
ADDED
@@ -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 !
|
data/Rakefile
ADDED
@@ -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
|
data/ext/jio/extconf.rb
ADDED
@@ -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')
|
data/ext/jio/file.c
ADDED
@@ -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
|
+
}
|