libcdb-ruby 0.0.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ #ifndef __RCDB_READER_H__
2
+ #define __RCDB_READER_H__
3
+
4
+ #define Get_CDB_Reader(obj, var) {\
5
+ if (RTEST(rcdb_reader_closed_p(obj))) {\
6
+ rb_raise(rb_eRuntimeError, "closed stream");\
7
+ }\
8
+ else {\
9
+ Data_Get_Struct((obj), struct cdb, (var));\
10
+ }\
11
+ }
12
+
13
+ #define CALL_ITERATOR(iter) {\
14
+ VALUE self = rb_ary_shift(args);\
15
+ return rb_funcall2(self, rb_intern(iter),\
16
+ RARRAY_LEN(args), RARRAY_PTR(args));\
17
+ }
18
+
19
+ #define ITER_RESULT(method, block) {\
20
+ rb_iterate(method, rb_ary_new3(1, self), block, ary);\
21
+ return rb_ary_entry(ary, 0);\
22
+ }
23
+
24
+ #define VALUE_EQUAL(val) \
25
+ RTEST(rb_funcall((val), rb_intern("=="), 1, rb_ary_entry(ary, 1)))
26
+
27
+ VALUE cCDBReader;
28
+ void rcdb_init_reader(void);
29
+
30
+ #endif /* __RCDB_READER_H__ */
@@ -0,0 +1,301 @@
1
+ #include "ruby_libcdb.h"
2
+
3
+ static void
4
+ rcdb_writer_free(void *ptr) {
5
+ free(ptr);
6
+ }
7
+
8
+ static VALUE
9
+ rcdb_writer_alloc(VALUE klass) {
10
+ struct cdb_make *cdbm = ALLOC_N(struct cdb_make, 1);
11
+ return Data_Wrap_Struct(klass, NULL, rcdb_writer_free, cdbm);
12
+ }
13
+
14
+ /*
15
+ * call-seq:
16
+ * writer.closed? -> true | false
17
+ *
18
+ * Whether _writer_ is closed.
19
+ */
20
+ static VALUE
21
+ rcdb_writer_closed_p(VALUE self) {
22
+ return rb_attr_get(self, rb_intern("closed"));
23
+ }
24
+
25
+ /*
26
+ * call-seq:
27
+ * new(io) -> aWriter
28
+ *
29
+ * Creates a new Writer instance to interface with +io+. +io+ must be opened
30
+ * for writing (+w+); in addition, it must be opened for read-write (<tt>w+</tt>)
31
+ * if #insert or #replace are to be used.
32
+ */
33
+ static VALUE
34
+ rcdb_writer_initialize(VALUE self, VALUE io) {
35
+ struct cdb_make *cdbm = NULL;
36
+ rb_io_t *fptr;
37
+
38
+ Check_Type(io, T_FILE);
39
+ GetOpenFile(io, fptr);
40
+
41
+ rb_io_check_writable(fptr);
42
+ rb_iv_set(self, "@io", io);
43
+ rb_iv_set(self, "closed", Qfalse);
44
+
45
+ Get_CDB_Writer(self, cdbm);
46
+
47
+ if (cdb_make_start(cdbm, GetFileFD(fptr)) == -1) {
48
+ rb_sys_fail(0);
49
+ }
50
+
51
+ if (lseek(cdb_fileno(cdbm), 0, SEEK_SET) == -1) {
52
+ rb_sys_fail(0);
53
+ }
54
+
55
+ return self;
56
+ }
57
+
58
+ /* Helper method */
59
+ static int
60
+ rcdb_writer_push_pair(st_data_t key, st_data_t val, st_data_t ary) {
61
+ rb_ary_push((VALUE)ary, rb_ary_new3(2, (VALUE)key, (VALUE)val));
62
+ return ST_CONTINUE;
63
+ }
64
+
65
+ /* Helper method */
66
+ static void
67
+ rcdb_writer_put_pair(struct cdb_make *cdbm, VALUE key, VALUE val, enum cdb_put_mode mode) {
68
+ StringValue(key);
69
+ StringValue(val);
70
+
71
+ if (cdb_make_put(cdbm,
72
+ RSTRING_PTR(key), RSTRING_LEN(key),
73
+ RSTRING_PTR(val), RSTRING_LEN(val),
74
+ mode) == -1) {
75
+ rb_sys_fail(0);
76
+ }
77
+ }
78
+
79
+ /* Helper method */
80
+ static void
81
+ rcdb_writer_put_value(struct cdb_make *cdbm, VALUE key, VALUE val, enum cdb_put_mode mode) {
82
+ long i;
83
+
84
+ switch (TYPE(val)) {
85
+ case T_ARRAY:
86
+ switch (mode) {
87
+ case CDB_PUT_REPLACE:
88
+ /* remove any existing record */
89
+ cdb_make_find(cdbm,
90
+ RSTRING_PTR(key),
91
+ RSTRING_LEN(key),
92
+ CDB_FIND_REMOVE);
93
+
94
+ /* add all */
95
+ mode = CDB_PUT_ADD;
96
+
97
+ break;
98
+ case CDB_PUT_INSERT:
99
+ /* see if key already exists */
100
+ switch (cdb_make_exists(cdbm,
101
+ RSTRING_PTR(key),
102
+ RSTRING_LEN(key))) {
103
+ case 0:
104
+ /* doesn't exist, add all */
105
+ mode = CDB_PUT_ADD;
106
+ break;
107
+ case -1:
108
+ /* error */
109
+ rb_sys_fail(0);
110
+ break;
111
+ default:
112
+ /* ignore, won't add any */
113
+ break;
114
+ }
115
+
116
+ break;
117
+ default:
118
+ /* no need to touch mode */
119
+ break;
120
+ }
121
+
122
+ for (i = 0; i < RARRAY_LEN(val); i++) {
123
+ rcdb_writer_put_pair(cdbm, key, RARRAY_PTR(val)[i], mode);
124
+ }
125
+
126
+ break;
127
+ default:
128
+ rcdb_writer_put_pair(cdbm, key, val, mode);
129
+ break;
130
+ }
131
+ }
132
+
133
+ /* Helper method */
134
+ static VALUE
135
+ rcdb_writer_put(int argc, VALUE *argv, VALUE self, enum cdb_put_mode mode) {
136
+ struct cdb_make *cdbm = NULL;
137
+ VALUE arg, val;
138
+ long i;
139
+
140
+ Get_CDB_Writer(self, cdbm);
141
+
142
+ switch (argc) {
143
+ case 1:
144
+ arg = argv[0];
145
+
146
+ switch (TYPE(arg)) {
147
+ case T_ARRAY:
148
+ val = rb_str_new2("");
149
+
150
+ for (i = 0; i < RARRAY_LEN(arg); i++) {
151
+ rcdb_writer_put_pair(cdbm, RARRAY_PTR(arg)[i], val, mode);
152
+ }
153
+
154
+ break;
155
+ case T_HASH:
156
+ val = rb_ary_new();
157
+ st_foreach(RHASH_TBL(arg), rcdb_writer_push_pair, val);
158
+
159
+ for (i = 0; i < RARRAY_LEN(val); i++) {
160
+ rcdb_writer_put_value(cdbm,
161
+ rb_ary_entry(RARRAY_PTR(val)[i], 0),
162
+ rb_ary_entry(RARRAY_PTR(val)[i], 1),
163
+ mode);
164
+ }
165
+
166
+ break;
167
+ default:
168
+ rb_raise(rb_eTypeError,
169
+ "wrong argument type %s (expected Array or Hash)",
170
+ rb_obj_classname(arg));
171
+
172
+ break;
173
+ }
174
+
175
+ break;
176
+ case 2:
177
+ rcdb_writer_put_value(cdbm, argv[0], argv[1], mode);
178
+ break;
179
+ default:
180
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1-2)", argc);
181
+ break;
182
+ }
183
+
184
+ return self;
185
+ }
186
+
187
+ /*
188
+ * call-seq:
189
+ * writer.store(key, val) -> writer
190
+ * writer.store(key, [val, ...]) -> writer
191
+ * writer.store([key, ...]) -> writer
192
+ * writer.store({ key => val, ... }) -> writer
193
+ *
194
+ * Stores records in the database and returns _writer_. Records are stored
195
+ * unconditionally, so duplicate keys will produce multiple records.
196
+ *
197
+ * If a single key/value pair is given, a record with key +key+ and value
198
+ * +val+ is created; if +val+ is an array, one record per value is created
199
+ * for +key+. If an array of keys is given, one record per key with an empty
200
+ * value is created. If a hash of key/value pairs is given, one record per
201
+ * key/value pair is created; the same logic as for a single key/value pair
202
+ * applies.
203
+ */
204
+ static VALUE
205
+ rcdb_writer_store(int argc, VALUE *argv, VALUE self) {
206
+ return rcdb_writer_put(argc, argv, self, CDB_PUT_ADD);
207
+ }
208
+
209
+ /*
210
+ * call-seq:
211
+ * writer.replace(key, val) -> writer
212
+ * writer.replace(key, [val, ...]) -> writer
213
+ * writer.replace([key, ...]) -> writer
214
+ * writer.replace({ key => val, ... }) -> writer
215
+ *
216
+ * Stores records in the database and returns _writer_. Records with
217
+ * duplicate keys are replaced.
218
+ *
219
+ * The arguments are treated the same as in #store, so duplicate keys
220
+ * <em>in the arguments</em> will produce multiple records.
221
+ */
222
+ static VALUE
223
+ rcdb_writer_replace(int argc, VALUE *argv, VALUE self) {
224
+ return rcdb_writer_put(argc, argv, self, CDB_PUT_REPLACE);
225
+ }
226
+
227
+ /*
228
+ * call-seq:
229
+ * writer.insert(key, val) -> writer
230
+ * writer.insert(key, [val, ...]) -> writer
231
+ * writer.insert([key, ...]) -> writer
232
+ * writer.insert({ key => val, ... }) -> writer
233
+ *
234
+ * Stores records in the database and returns _writer_. Records will only
235
+ * be inserted if they don't already exist in the database.
236
+ *
237
+ * The arguments are treated the same as in #store, so duplicate keys
238
+ * <em>in the arguments</em> will produce multiple records.
239
+ */
240
+ static VALUE
241
+ rcdb_writer_insert(int argc, VALUE *argv, VALUE self) {
242
+ return rcdb_writer_put(argc, argv, self, CDB_PUT_INSERT);
243
+ }
244
+
245
+ /*
246
+ * call-seq:
247
+ * writer.close -> nil
248
+ *
249
+ * Closes _writer_, as well as the underlying IO object.
250
+ */
251
+ static VALUE
252
+ rcdb_writer_close(VALUE self) {
253
+ struct cdb_make *cdbm = NULL;
254
+
255
+ if (RTEST(rcdb_writer_closed_p(self))) {
256
+ return Qnil;
257
+ }
258
+
259
+ Get_CDB_Writer(self, cdbm);
260
+ rb_iv_set(self, "closed", Qtrue);
261
+
262
+ if (cdb_make_finish(cdbm) == -1) {
263
+ rb_sys_fail(0);
264
+ }
265
+
266
+ rb_io_close(rb_iv_get(self, "@io"));
267
+
268
+ return Qnil;
269
+ }
270
+
271
+ /* :nodoc: */
272
+ static VALUE
273
+ rcdb_writer_inspect(VALUE self) {
274
+ VALUE str = rb_call_super(0, NULL);
275
+
276
+ if (RTEST(rcdb_writer_closed_p(self))) {
277
+ rb_funcall(str,
278
+ rb_intern("insert"), 2, INT2FIX(-2), rb_str_new2(" (closed)"));
279
+ }
280
+
281
+ return str;
282
+ }
283
+
284
+ void rcdb_init_writer(void) {
285
+ /*
286
+ * The writer for creating CDB files. See Reader for reading them.
287
+ */
288
+ cCDBWriter = rb_define_class_under(cCDB, "Writer", rb_cObject);
289
+ rb_define_alloc_func(cCDBWriter, rcdb_writer_alloc);
290
+
291
+ rb_define_method(cCDBWriter, "close", rcdb_writer_close, 0);
292
+ rb_define_method(cCDBWriter, "closed?", rcdb_writer_closed_p, 0);
293
+ rb_define_method(cCDBWriter, "initialize", rcdb_writer_initialize, 1);
294
+ rb_define_method(cCDBWriter, "insert", rcdb_writer_insert, -1);
295
+ rb_define_method(cCDBWriter, "inspect", rcdb_writer_inspect, 0);
296
+ rb_define_method(cCDBWriter, "replace", rcdb_writer_replace, -1);
297
+ rb_define_method(cCDBWriter, "store", rcdb_writer_store, -1);
298
+
299
+ rb_define_alias(cCDBWriter, "[]=", "replace");
300
+ rb_define_alias(cCDBWriter, "add", "store");
301
+ }
@@ -0,0 +1,16 @@
1
+ #ifndef __RCDB_WRITER_H__
2
+ #define __RCDB_WRITER_H__
3
+
4
+ #define Get_CDB_Writer(obj, var) {\
5
+ if (RTEST(rcdb_writer_closed_p(obj))) {\
6
+ rb_raise(rb_eRuntimeError, "closed stream");\
7
+ }\
8
+ else {\
9
+ Data_Get_Struct((obj), struct cdb_make, (var));\
10
+ }\
11
+ }
12
+
13
+ VALUE cCDBWriter;
14
+ void rcdb_init_writer(void);
15
+
16
+ #endif /* __RCDB_WRITER_H__ */
@@ -0,0 +1,27 @@
1
+ #ifndef __RUBY_LIBCDB_H__
2
+ #define __RUBY_LIBCDB_H__
3
+
4
+ #include <ruby.h>
5
+ #if HAVE_RUBY_IO_H
6
+ #include <ruby/io.h>
7
+ #else
8
+ #include <rubyio.h>
9
+ #endif
10
+ #if HAVE_RUBY_ST_H
11
+ #include <ruby/st.h>
12
+ #else
13
+ #include <st.h>
14
+ #endif
15
+ #include <cdb.h>
16
+ #include <sys/types.h>
17
+ #include <sys/stat.h>
18
+ #include <unistd.h>
19
+ #include <fcntl.h>
20
+
21
+ #include "ruby_cdb.h"
22
+ #include "ruby_cdb_reader.h"
23
+ #include "ruby_cdb_writer.h"
24
+
25
+ VALUE mLibCDB;
26
+
27
+ #endif /* __RUBY_LIBCDB_H__ */
@@ -0,0 +1,2 @@
1
+ require 'libcdb'
2
+ include LibCDB
@@ -0,0 +1 @@
1
+ require 'libcdb'
@@ -0,0 +1,256 @@
1
+ begin
2
+ require "libcdb/#{RUBY_VERSION[/\d+.\d+/]}/libcdb_ruby"
3
+ rescue LoadError
4
+ require 'libcdb/libcdb_ruby'
5
+ end
6
+
7
+ require 'libcdb/version'
8
+ require 'forwardable'
9
+
10
+ module LibCDB
11
+
12
+ class CDB
13
+
14
+ extend Forwardable
15
+
16
+ MODE_READ = 'r' # :nodoc:
17
+ MODE_WRITE = 'w' # :nodoc:
18
+
19
+ class << self
20
+
21
+ # call-seq:
22
+ # CDB.open(path[, mode]) -> aReader | aWriter | aCDB
23
+ # CDB.open(path[, mode]) { |cdb| ... }
24
+ #
25
+ # Opens +path+ with +mode+. If a block is given, yields a _cdb_ object
26
+ # according to +mode+ (see below) and returns the return value of the
27
+ # block; the object is closed afterwards. Otherwise just returns the
28
+ # object.
29
+ #
30
+ # <tt>r</tt>:: Reader
31
+ # <tt>w</tt>:: Writer
32
+ # <tt>r+</tt>:: CDB (initially opened for reading)
33
+ # <tt>w+</tt>:: CDB (initially opened for writing)
34
+ def open(path, mode = MODE_READ)
35
+ klass, args = self, []
36
+
37
+ case mode
38
+ when 'r+' then args = [mode = MODE_READ]
39
+ when 'w+' then args = [ MODE_WRITE]
40
+ when MODE_READ then klass = Reader
41
+ when MODE_WRITE then klass, mode = Writer, 'w+'
42
+ else raise ArgumentError, "illegal access mode #{mode}"
43
+ end
44
+
45
+ cdb = begin
46
+ klass.new(io = File.open(path, mode), *args)
47
+ rescue
48
+ io.close if io
49
+ raise
50
+ end
51
+
52
+ if block_given?
53
+ begin
54
+ yield cdb
55
+ ensure
56
+ cdb.close
57
+ end
58
+ else
59
+ cdb
60
+ end
61
+ end
62
+
63
+ # call-seq:
64
+ # CDB.foreach(path) { |key, val| ... }
65
+ # CDB.foreach(path, key) { |val| ... }
66
+ #
67
+ # Opens +path+ for reading and iterates over each key/value pair,
68
+ # or, if +key+ is given, each value for +key+.
69
+ def foreach(path, *key)
70
+ open(path) { |cdb| cdb.each(*key) { |val| yield val } }
71
+ end
72
+
73
+ # call-seq:
74
+ # CDB.dump(path[, target[, separator]]) -> target
75
+ #
76
+ # Opens +path+ for reading and shovels each record dump into +target+
77
+ # (followed by +separator+, if present). Returns +target+.
78
+ def dump(path, target = '', separator = $\ || $/)
79
+ open(path) { |cdb|
80
+ cdb.each_dump { |dump|
81
+ target << dump
82
+ target << separator if separator
83
+ }
84
+
85
+ target
86
+ }
87
+ end
88
+
89
+ end
90
+
91
+ # call-seq:
92
+ # CDB.new(io[, mode]) -> aCDB
93
+ #
94
+ # Creates a new CDB object to interface with +io+. +mode+ must be the same
95
+ # mode +io+ was opened in, either +r+ or +w+. Responds to both Reader and
96
+ # Writer methods interchangeably by reopening +io+ in the corresponding mode
97
+ # and instantiating a new Reader or Writer object with it. Note that +io+
98
+ # will be truncated each time it's opened for writing.
99
+ def initialize(io, mode = MODE_WRITE)
100
+ @io, @mode = io, mode
101
+
102
+ case mode
103
+ when MODE_READ then open_read
104
+ when MODE_WRITE then open_write
105
+ else raise ArgumentError, "illegal access mode #{mode}"
106
+ end
107
+ end
108
+
109
+ # The underlying IO object.
110
+ attr_reader :io
111
+
112
+ # The current IO mode, either +r+ or +w+.
113
+ attr_reader :mode
114
+
115
+ # call-seq:
116
+ # cdb.reader -> aReader
117
+ #
118
+ # The Reader object associated with _cdb_.
119
+ def reader
120
+ @reader ||= open_read
121
+ end
122
+
123
+ # call-seq:
124
+ # cdb.writer -> aWriter
125
+ #
126
+ # The Writer object associated with _cdb_.
127
+ def writer
128
+ @writer ||= open_write
129
+ end
130
+
131
+ def_delegators :reader, :[], :dump, :each, :each_dump, :each_key,
132
+ :each_value, :empty?, :fetch, :fetch_all,
133
+ :fetch_first, :fetch_last, :get, :has_key?,
134
+ :has_value?, :include?, :key, :key?, :keys,
135
+ :length, :member?, :rget, :size, :to_a,
136
+ :to_h, :value?, :values, :values_at
137
+
138
+ def_delegators :writer, :[]=, :add, :insert, :replace, :store
139
+
140
+ # call-seq:
141
+ # cdb.read? -> true | false
142
+ #
143
+ # Whether _cdb_ is currently opened for reading.
144
+ def read?
145
+ !!@reader
146
+ end
147
+
148
+ # call-seq:
149
+ # cdb.write? -> true | false
150
+ #
151
+ # Whether _cdb_ is currently opened for writing.
152
+ def write?
153
+ !!@writer
154
+ end
155
+
156
+ # call-seq:
157
+ # cdb.open_read -> aReader
158
+ #
159
+ # Opens _cdb_ for reading and reopens #io accordingly.
160
+ # Closes #writer if open.
161
+ def open_read
162
+ close_write(false)
163
+ @reader = Reader.new(reopen(MODE_READ))
164
+ end
165
+
166
+ # call-seq:
167
+ # cdb.open_write -> aWriter
168
+ #
169
+ # Opens _cdb_ for writing and reopens #io accordingly.
170
+ # Closes #reader if open. Note that #io will be truncated.
171
+ def open_write
172
+ close_read(false)
173
+ @writer = Writer.new(reopen(MODE_WRITE))
174
+ end
175
+
176
+ # call-seq:
177
+ # cdb.close_read([strict]) -> nil
178
+ #
179
+ # If _cdb_ is currently opened for reading, closes the #reader (and #io
180
+ # with it). Otherwise, if +strict+ is true, raises an IOError.
181
+ def close_read(strict = true)
182
+ if read?
183
+ @reader.close
184
+ @reader = nil
185
+ elsif strict
186
+ raise IOError, 'not opened for reading'
187
+ end
188
+ end
189
+
190
+ # call-seq:
191
+ # cdb.close_write([strict]) -> nil
192
+ #
193
+ # If _cdb_ is currently opened for writing, closes the #writer (and #io
194
+ # with it). Otherwise, if +strict+ is true, raises an IOError.
195
+ def close_write(strict = true)
196
+ if write?
197
+ @writer.close
198
+ @writer = nil
199
+ elsif strict
200
+ raise IOError, 'not opened for writing'
201
+ end
202
+ end
203
+
204
+ # call-seq:
205
+ # cdb.close -> nil
206
+ #
207
+ # Closes both the #reader and the #writer, as well as #io. Doesn't raise
208
+ # an IOError if either of them is already closed.
209
+ def close
210
+ close_read(false)
211
+ close_write(false)
212
+ io.close unless io.closed?
213
+ end
214
+
215
+ # call-seq:
216
+ # cdb.closed_read? -> true | false | nil
217
+ #
218
+ # Whether #reader is closed if _cdb_ is currently opened for reading.
219
+ def closed_read?
220
+ reader.closed? if read?
221
+ end
222
+
223
+ # call-seq:
224
+ # cdb.closed_write? -> true | false | nil
225
+ #
226
+ # Whether #writer is closed if _cdb_ is currently opened for writing.
227
+ def closed_write?
228
+ writer.closed? if write?
229
+ end
230
+
231
+ # call-seq:
232
+ # cdb.closed? -> true | false | nil
233
+ #
234
+ # Whether _cdb_ is closed. See #closed_read? and #closed_write?.
235
+ def closed?
236
+ read? ? closed_read? : write? ? closed_write? : nil
237
+ end
238
+
239
+ private
240
+
241
+ # call-seq:
242
+ # cdb.reopen([mode]) -> anIO
243
+ #
244
+ # Reopens #io in +mode+ and returns it.
245
+ def reopen(new_mode = MODE_READ)
246
+ return io if mode == new_mode
247
+
248
+ @mode = new_mode
249
+ new_mode += '+' if new_mode == MODE_WRITE
250
+
251
+ io.reopen(io.path, new_mode)
252
+ end
253
+
254
+ end
255
+
256
+ end