libcdb-ruby 0.0.1-x86-mswin32-60

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.
@@ -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__ */
data/lib/cdb.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'libcdb'
2
+ include LibCDB
@@ -0,0 +1 @@
1
+ require 'libcdb'
data/lib/libcdb.rb ADDED
@@ -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