ext/gdbm/gdbm.c

Go to the documentation of this file.
00001 /************************************************
00002 
00003   gdbm.c -
00004 
00005   $Author: nobu $
00006   modified at: Mon Jan 24 15:59:52 JST 1994
00007 
00008   Documentation by Peter Adolphs < futzilogik at users dot sourceforge dot net >
00009 
00010 ************************************************/
00011 
00012 #include "ruby.h"
00013 
00014 #include <gdbm.h>
00015 #include <fcntl.h>
00016 #include <errno.h>
00017 
00018 /*
00019  * Document-class: GDBM
00020  *
00021  * == Summary
00022  *
00023  * Ruby extension for GNU dbm (gdbm) -- a simple database engine for storing
00024  * key-value pairs on disk.
00025  *
00026  * == Description
00027  *
00028  * GNU dbm is a library for simple databases. A database is a file that stores
00029  * key-value pairs. Gdbm allows the user to store, retrieve, and delete data by
00030  * key. It furthermore allows a non-sorted traversal of all key-value pairs.
00031  * A gdbm database thus provides the same functionality as a hash. As
00032  * with objects of the Hash class, elements can be accessed with <tt>[]</tt>.
00033  * Furthermore, GDBM mixes in the Enumerable module, thus providing convenient
00034  * methods such as #find, #collect, #map, etc.
00035  *
00036  * A process is allowed to open several different databases at the same time.
00037  * A process can open a database as a "reader" or a "writer". Whereas a reader
00038  * has only read-access to the database, a writer has read- and write-access.
00039  * A database can be accessed either by any number of readers or by exactly one
00040  * writer at the same time.
00041  *
00042  * == Examples
00043  *
00044  * 1. Opening/creating a database, and filling it with some entries:
00045  *
00046  *      require 'gdbm'
00047  *
00048  *      gdbm = GDBM.new("fruitstore.db")
00049  *      gdbm["ananas"]    = "3"
00050  *      gdbm["banana"]    = "8"
00051  *      gdbm["cranberry"] = "4909"
00052  *      gdbm.close
00053  *
00054  * 2. Reading out a database:
00055  *
00056  *      require 'gdbm'
00057  *
00058  *      gdbm = GDBM.new("fruitstore.db")
00059  *      gdbm.each_pair do |key, value|
00060  *        print "#{key}: #{value}\n"
00061  *      end
00062  *      gdbm.close
00063  *
00064  *    produces
00065  *
00066  *      banana: 8
00067  *      ananas: 3
00068  *      cranberry: 4909
00069  *
00070  * == Links
00071  *
00072  * * http://www.gnu.org/software/gdbm/
00073  */
00074 static VALUE rb_cGDBM, rb_eGDBMError, rb_eGDBMFatalError;
00075 
00076 #define RUBY_GDBM_RW_BIT 0x20000000
00077 
00078 #define MY_BLOCK_SIZE (2048)
00079 #define MY_FATAL_FUNC rb_gdbm_fatal
00080 static void
00081 rb_gdbm_fatal(char *msg)
00082 {
00083     rb_raise(rb_eGDBMFatalError, "%s", msg);
00084 }
00085 
00086 struct dbmdata {
00087     int  di_size;
00088     GDBM_FILE di_dbm;
00089 };
00090 
00091 static void
00092 closed_dbm(void)
00093 {
00094     rb_raise(rb_eRuntimeError, "closed GDBM file");
00095 }
00096 
00097 #define GetDBM(obj, dbmp) do {\
00098     Data_Get_Struct(obj, struct dbmdata, dbmp);\
00099     if (dbmp == 0) closed_dbm();\
00100     if (dbmp->di_dbm == 0) closed_dbm();\
00101 } while (0)
00102 
00103 #define GetDBM2(obj, data, dbm) {\
00104     GetDBM(obj, data);\
00105     (dbm) = dbmp->di_dbm;\
00106 }
00107 
00108 static void
00109 free_dbm(struct dbmdata *dbmp)
00110 {
00111     if (dbmp) {
00112         if (dbmp->di_dbm) gdbm_close(dbmp->di_dbm);
00113         xfree(dbmp);
00114     }
00115 }
00116 
00117 /*
00118  * call-seq:
00119  *     gdbm.close -> nil
00120  *
00121  * Closes the associated database file.
00122  */
00123 static VALUE
00124 fgdbm_close(VALUE obj)
00125 {
00126     struct dbmdata *dbmp;
00127 
00128     GetDBM(obj, dbmp);
00129     gdbm_close(dbmp->di_dbm);
00130     dbmp->di_dbm = 0;
00131 
00132     return Qnil;
00133 }
00134 
00135 /*
00136  * call-seq:
00137  *     gdbm.closed?  -> true or false
00138  *
00139  * Returns true if the associated database file has been closed.
00140  */
00141 static VALUE
00142 fgdbm_closed(VALUE obj)
00143 {
00144     struct dbmdata *dbmp;
00145 
00146     Data_Get_Struct(obj, struct dbmdata, dbmp);
00147     if (dbmp == 0)
00148         return Qtrue;
00149     if (dbmp->di_dbm == 0)
00150         return Qtrue;
00151 
00152     return Qfalse;
00153 }
00154 
00155 static VALUE
00156 fgdbm_s_alloc(VALUE klass)
00157 {
00158     return Data_Wrap_Struct(klass, 0, free_dbm, 0);
00159 }
00160 
00161 /*
00162  * call-seq:
00163  *      GDBM.new(filename, mode = 0666, flags = nil)
00164  *
00165  * Creates a new GDBM instance by opening a gdbm file named _filename_.
00166  * If the file does not exist, a new file with file mode _mode_ will be
00167  * created. _flags_ may be one of the following:
00168  * * *READER*  - open as a reader
00169  * * *WRITER*  - open as a writer
00170  * * *WRCREAT* - open as a writer; if the database does not exist, create a new one
00171  * * *NEWDB*   - open as a writer; overwrite any existing databases
00172  *
00173  * The values *WRITER*, *WRCREAT* and *NEWDB* may be combined with the following
00174  * values by bitwise or:
00175  * * *SYNC*    - cause all database operations to be synchronized to the disk
00176  * * *NOLOCK*  - do not lock the database file
00177  *
00178  * If no _flags_ are specified, the GDBM object will try to open the database
00179  * file as a writer and will create it if it does not already exist
00180  * (cf. flag <tt>WRCREAT</tt>). If this fails (for instance, if another process
00181  * has already opened the database as a reader), it will try to open the
00182  * database file as a reader (cf. flag <tt>READER</tt>).
00183  */
00184 static VALUE
00185 fgdbm_initialize(int argc, VALUE *argv, VALUE obj)
00186 {
00187     VALUE file, vmode, vflags;
00188     GDBM_FILE dbm;
00189     struct dbmdata *dbmp;
00190     int mode, flags = 0;
00191 
00192     if (rb_scan_args(argc, argv, "12", &file, &vmode, &vflags) == 1) {
00193         mode = 0666;            /* default value */
00194     }
00195     else if (NIL_P(vmode)) {
00196         mode = -1;              /* return nil if DB does not exist */
00197     }
00198     else {
00199         mode = NUM2INT(vmode);
00200     }
00201 
00202     if (!NIL_P(vflags))
00203         flags = NUM2INT(vflags);
00204 
00205     SafeStringValue(file);
00206 
00207     if (flags & RUBY_GDBM_RW_BIT) {
00208         flags &= ~RUBY_GDBM_RW_BIT;
00209         dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00210                         flags, mode, MY_FATAL_FUNC);
00211     }
00212     else {
00213         dbm = 0;
00214         if (mode >= 0)
00215             dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00216                             GDBM_WRCREAT|flags, mode, MY_FATAL_FUNC);
00217         if (!dbm)
00218             dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00219                             GDBM_WRITER|flags, 0, MY_FATAL_FUNC);
00220         if (!dbm)
00221             dbm = gdbm_open(RSTRING_PTR(file), MY_BLOCK_SIZE,
00222                             GDBM_READER|flags, 0, MY_FATAL_FUNC);
00223     }
00224 
00225     if (!dbm) {
00226         if (mode == -1) return Qnil;
00227 
00228         if (gdbm_errno == GDBM_FILE_OPEN_ERROR ||
00229             gdbm_errno == GDBM_CANT_BE_READER ||
00230             gdbm_errno == GDBM_CANT_BE_WRITER)
00231             rb_sys_fail(RSTRING_PTR(file));
00232         else
00233             rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00234     }
00235 
00236     dbmp = ALLOC(struct dbmdata);
00237     free_dbm(DATA_PTR(obj));
00238     DATA_PTR(obj) = dbmp;
00239     dbmp->di_dbm = dbm;
00240     dbmp->di_size = -1;
00241 
00242     return obj;
00243 }
00244 
00245 /*
00246  * call-seq:
00247  *      GDBM.open(filename, mode = 0666, flags = nil)
00248  *      GDBM.open(filename, mode = 0666, flags = nil) { |gdbm| ... }
00249  *
00250  * If called without a block, this is synonymous to GDBM::new.
00251  * If a block is given, the new GDBM instance will be passed to the block
00252  * as a parameter, and the corresponding database file will be closed
00253  * after the execution of the block code has been finished.
00254  *
00255  * Example for an open call with a block:
00256  *
00257  *   require 'gdbm'
00258  *   GDBM.open("fruitstore.db") do |gdbm|
00259  *     gdbm.each_pair do |key, value|
00260  *       print "#{key}: #{value}\n"
00261  *     end
00262  *   end
00263  */
00264 static VALUE
00265 fgdbm_s_open(int argc, VALUE *argv, VALUE klass)
00266 {
00267     VALUE obj = Data_Wrap_Struct(klass, 0, free_dbm, 0);
00268 
00269     if (NIL_P(fgdbm_initialize(argc, argv, obj))) {
00270         return Qnil;
00271     }
00272 
00273     if (rb_block_given_p()) {
00274         return rb_ensure(rb_yield, obj, fgdbm_close, obj);
00275     }
00276 
00277     return obj;
00278 }
00279 
00280 static VALUE
00281 rb_gdbm_fetch(GDBM_FILE dbm, datum key)
00282 {
00283     datum val;
00284     VALUE str;
00285 
00286     val = gdbm_fetch(dbm, key);
00287     if (val.dptr == 0)
00288         return Qnil;
00289 
00290     str = rb_str_new(val.dptr, val.dsize);
00291     free(val.dptr);
00292     OBJ_TAINT(str);
00293     return str;
00294 }
00295 
00296 static VALUE
00297 rb_gdbm_fetch2(GDBM_FILE dbm, VALUE keystr)
00298 {
00299     datum key;
00300 
00301     StringValue(keystr);
00302     key.dptr = RSTRING_PTR(keystr);
00303     key.dsize = RSTRING_LEN(keystr);
00304 
00305     return rb_gdbm_fetch(dbm, key);
00306 }
00307 
00308 static VALUE
00309 rb_gdbm_fetch3(VALUE obj, VALUE keystr)
00310 {
00311     struct dbmdata *dbmp;
00312     GDBM_FILE dbm;
00313 
00314     GetDBM2(obj, dbmp, dbm);
00315     return rb_gdbm_fetch2(dbm, keystr);
00316 }
00317 
00318 static VALUE
00319 rb_gdbm_firstkey(GDBM_FILE dbm)
00320 {
00321     datum key;
00322     VALUE str;
00323 
00324     key = gdbm_firstkey(dbm);
00325     if (key.dptr == 0)
00326         return Qnil;
00327 
00328     str = rb_str_new(key.dptr, key.dsize);
00329     free(key.dptr);
00330     OBJ_TAINT(str);
00331     return str;
00332 }
00333 
00334 static VALUE
00335 rb_gdbm_nextkey(GDBM_FILE dbm, VALUE keystr)
00336 {
00337     datum key, key2;
00338     VALUE str;
00339 
00340     key.dptr = RSTRING_PTR(keystr);
00341     key.dsize = RSTRING_LEN(keystr);
00342     key2 = gdbm_nextkey(dbm, key);
00343     if (key2.dptr == 0)
00344         return Qnil;
00345 
00346     str = rb_str_new(key2.dptr, key2.dsize);
00347     free(key2.dptr);
00348     OBJ_TAINT(str);
00349     return str;
00350 }
00351 
00352 static VALUE
00353 fgdbm_fetch(VALUE obj, VALUE keystr, VALUE ifnone)
00354 {
00355     VALUE valstr;
00356 
00357     valstr = rb_gdbm_fetch3(obj, keystr);
00358     if (NIL_P(valstr)) {
00359         if (ifnone == Qnil && rb_block_given_p())
00360             return rb_yield(keystr);
00361         return ifnone;
00362     }
00363     return valstr;
00364 }
00365 
00366 /*
00367  * call-seq:
00368  *      gdbm[key] -> value
00369  *
00370  * Retrieves the _value_ corresponding to _key_.
00371  */
00372 static VALUE
00373 fgdbm_aref(VALUE obj, VALUE keystr)
00374 {
00375     return rb_gdbm_fetch3(obj, keystr);
00376 }
00377 
00378 /*
00379  * call-seq:
00380  *      gdbm.fetch(key [, default]) -> value
00381  *
00382  * Retrieves the _value_ corresponding to _key_. If there is no value
00383  * associated with _key_, _default_ will be returned instead.
00384  */
00385 static VALUE
00386 fgdbm_fetch_m(int argc, VALUE *argv, VALUE obj)
00387 {
00388     VALUE keystr, valstr, ifnone;
00389 
00390     rb_scan_args(argc, argv, "11", &keystr, &ifnone);
00391     valstr = fgdbm_fetch(obj, keystr, ifnone);
00392     if (argc == 1 && !rb_block_given_p() && NIL_P(valstr))
00393         rb_raise(rb_eIndexError, "key not found");
00394 
00395     return valstr;
00396 }
00397 
00398 /*
00399  * call-seq:
00400  *      gdbm.key(value) -> key
00401  *
00402  * Returns the _key_ for a given _value_. If several keys may map to the
00403  * same value, the key that is found first will be returned.
00404  */
00405 static VALUE
00406 fgdbm_key(VALUE obj, VALUE valstr)
00407 {
00408     struct dbmdata *dbmp;
00409     GDBM_FILE dbm;
00410     VALUE keystr, valstr2;
00411 
00412     StringValue(valstr);
00413     GetDBM2(obj, dbmp, dbm);
00414     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00415          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00416 
00417         valstr2 = rb_gdbm_fetch2(dbm, keystr);
00418         if (!NIL_P(valstr2) &&
00419             RSTRING_LEN(valstr) == RSTRING_LEN(valstr2) &&
00420             memcmp(RSTRING_PTR(valstr), RSTRING_PTR(valstr2),
00421                    RSTRING_LEN(valstr)) == 0) {
00422             return keystr;
00423         }
00424     }
00425     return Qnil;
00426 }
00427 
00428 /* :nodoc: */
00429 static VALUE
00430 fgdbm_index(VALUE obj, VALUE value)
00431 {
00432     rb_warn("GDBM#index is deprecated; use GDBM#key");
00433     return fgdbm_key(obj, value);
00434 }
00435 
00436 /*
00437  * call-seq:
00438  *      gdbm.select { |key, value| block } -> array
00439  *
00440  * Returns a new array of all key-value pairs of the database for which _block_
00441  * evaluates to true.
00442  */
00443 static VALUE
00444 fgdbm_select(VALUE obj)
00445 {
00446     VALUE new = rb_ary_new();
00447     GDBM_FILE dbm;
00448     struct dbmdata *dbmp;
00449     VALUE keystr;
00450 
00451     GetDBM2(obj, dbmp, dbm);
00452     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00453          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00454         VALUE assoc = rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr));
00455         VALUE v = rb_yield(assoc);
00456 
00457         if (RTEST(v)) {
00458             rb_ary_push(new, assoc);
00459         }
00460         GetDBM2(obj, dbmp, dbm);
00461     }
00462 
00463     return new;
00464 }
00465 
00466 /*
00467  * call-seq:
00468  *      gdbm.values_at(key, ...) -> array
00469  *
00470  * Returns an array of the values associated with each specified _key_.
00471  */
00472 static VALUE
00473 fgdbm_values_at(int argc, VALUE *argv, VALUE obj)
00474 {
00475     VALUE new = rb_ary_new2(argc);
00476     int i;
00477 
00478     for (i=0; i<argc; i++) {
00479         rb_ary_push(new, rb_gdbm_fetch3(obj, argv[i]));
00480     }
00481 
00482     return new;
00483 }
00484 
00485 static void
00486 rb_gdbm_modify(VALUE obj)
00487 {
00488     rb_secure(4);
00489     if (OBJ_FROZEN(obj)) rb_error_frozen("GDBM");
00490 }
00491 
00492 static VALUE
00493 rb_gdbm_delete(VALUE obj, VALUE keystr)
00494 {
00495     datum key;
00496     struct dbmdata *dbmp;
00497     GDBM_FILE dbm;
00498 
00499     rb_gdbm_modify(obj);
00500     StringValue(keystr);
00501     key.dptr = RSTRING_PTR(keystr);
00502     key.dsize = RSTRING_LEN(keystr);
00503 
00504     GetDBM2(obj, dbmp, dbm);
00505     if (!gdbm_exists(dbm, key)) {
00506         return Qnil;
00507     }
00508 
00509     if (gdbm_delete(dbm, key)) {
00510         dbmp->di_size = -1;
00511         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00512     }
00513     else if (dbmp->di_size >= 0) {
00514         dbmp->di_size--;
00515     }
00516     return obj;
00517 }
00518 
00519 /*
00520  * call-seq:
00521  *      gdbm.delete(key) -> value or nil
00522  *
00523  * Removes the key-value-pair with the specified _key_ from this database and
00524  * returns the corresponding _value_. Returns nil if the database is empty.
00525  */
00526 static VALUE
00527 fgdbm_delete(VALUE obj, VALUE keystr)
00528 {
00529     VALUE valstr;
00530 
00531     valstr = fgdbm_fetch(obj, keystr, Qnil);
00532     rb_gdbm_delete(obj, keystr);
00533     return valstr;
00534 }
00535 
00536 /*
00537  * call-seq:
00538  *      gdbm.shift -> (key, value) or nil
00539  *
00540  * Removes a key-value-pair from this database and returns it as a
00541  * two-item array [ _key_, _value_ ]. Returns nil if the database is empty.
00542  */
00543 static VALUE
00544 fgdbm_shift(VALUE obj)
00545 {
00546     struct dbmdata *dbmp;
00547     GDBM_FILE dbm;
00548     VALUE keystr, valstr;
00549 
00550     rb_gdbm_modify(obj);
00551     GetDBM2(obj, dbmp, dbm);
00552     keystr = rb_gdbm_firstkey(dbm);
00553     if (NIL_P(keystr)) return Qnil;
00554     valstr = rb_gdbm_fetch2(dbm, keystr);
00555     rb_gdbm_delete(obj, keystr);
00556 
00557     return rb_assoc_new(keystr, valstr);
00558 }
00559 
00560 /*
00561  * call-seq:
00562  *      gdbm.delete_if { |key, value| block } -> gdbm
00563  *      gdbm.reject! { |key, value| block } -> gdbm
00564  *
00565  * Deletes every key-value pair from _gdbm_ for which _block_ evaluates to true.
00566  */
00567 static VALUE
00568 fgdbm_delete_if(VALUE obj)
00569 {
00570     struct dbmdata *dbmp;
00571     GDBM_FILE dbm;
00572     VALUE keystr, valstr;
00573     VALUE ret, ary = rb_ary_new();
00574     int i, status = 0, n;
00575 
00576     rb_gdbm_modify(obj);
00577     GetDBM2(obj, dbmp, dbm);
00578     n = dbmp->di_size;
00579     dbmp->di_size = -1;
00580 
00581     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00582          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00583 
00584         valstr = rb_gdbm_fetch2(dbm, keystr);
00585         ret = rb_protect(rb_yield, rb_assoc_new(keystr, valstr), &status);
00586         if (status != 0) break;
00587         if (RTEST(ret)) rb_ary_push(ary, keystr);
00588         GetDBM2(obj, dbmp, dbm);
00589     }
00590 
00591     for (i = 0; i < RARRAY_LEN(ary); i++)
00592         rb_gdbm_delete(obj, RARRAY_PTR(ary)[i]);
00593     if (status) rb_jump_tag(status);
00594     if (n > 0) dbmp->di_size = n - RARRAY_LEN(ary);
00595 
00596     return obj;
00597 }
00598 
00599 /*
00600  * call-seq:
00601  *      gdbm.clear -> gdbm
00602  *
00603  * Removes all the key-value pairs within _gdbm_.
00604  */
00605 static VALUE
00606 fgdbm_clear(VALUE obj)
00607 {
00608     datum key, nextkey;
00609     struct dbmdata *dbmp;
00610     GDBM_FILE dbm;
00611 
00612     rb_gdbm_modify(obj);
00613     GetDBM2(obj, dbmp, dbm);
00614     dbmp->di_size = -1;
00615 
00616 #if 0
00617     while (key = gdbm_firstkey(dbm), key.dptr) {
00618         if (gdbm_delete(dbm, key)) {
00619             free(key.dptr);
00620             rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00621         }
00622         free(key.dptr);
00623     }
00624 #else
00625     while (key = gdbm_firstkey(dbm), key.dptr) {
00626         for (; key.dptr; key = nextkey) {
00627             nextkey = gdbm_nextkey(dbm, key);
00628             if (gdbm_delete(dbm, key)) {
00629                 free(key.dptr);
00630                 if (nextkey.dptr) free(nextkey.dptr);
00631                 rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00632             }
00633             free(key.dptr);
00634         }
00635     }
00636 #endif
00637     dbmp->di_size = 0;
00638 
00639     return obj;
00640 }
00641 
00642 /*
00643  * call-seq:
00644  *     gdbm.invert  -> hash
00645  *
00646  * Returns a hash created by using _gdbm_'s values as keys, and the keys
00647  * as values.
00648  */
00649 static VALUE
00650 fgdbm_invert(VALUE obj)
00651 {
00652     struct dbmdata *dbmp;
00653     GDBM_FILE dbm;
00654     VALUE keystr, valstr;
00655     VALUE hash = rb_hash_new();
00656 
00657     GetDBM2(obj, dbmp, dbm);
00658     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00659          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00660         valstr = rb_gdbm_fetch2(dbm, keystr);
00661 
00662         rb_hash_aset(hash, valstr, keystr);
00663     }
00664     return hash;
00665 }
00666 
00667 /*
00668  * call-seq:
00669  *      gdbm[key]= value -> value
00670  *      gdbm.store(key, value) -> value
00671  *
00672  * Associates the value _value_ with the specified _key_.
00673  */
00674 static VALUE
00675 fgdbm_store(VALUE obj, VALUE keystr, VALUE valstr)
00676 {
00677     datum key, val;
00678     struct dbmdata *dbmp;
00679     GDBM_FILE dbm;
00680 
00681     rb_gdbm_modify(obj);
00682     StringValue(keystr);
00683     StringValue(valstr);
00684 
00685     key.dptr = RSTRING_PTR(keystr);
00686     key.dsize = RSTRING_LEN(keystr);
00687 
00688     val.dptr = RSTRING_PTR(valstr);
00689     val.dsize = RSTRING_LEN(valstr);
00690 
00691     GetDBM2(obj, dbmp, dbm);
00692     dbmp->di_size = -1;
00693     if (gdbm_store(dbm, key, val, GDBM_REPLACE)) {
00694         if (errno == EPERM) rb_sys_fail(0);
00695         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
00696     }
00697 
00698     return valstr;
00699 }
00700 
00701 static VALUE
00702 update_i(VALUE pair, VALUE dbm)
00703 {
00704     Check_Type(pair, T_ARRAY);
00705     if (RARRAY_LEN(pair) < 2) {
00706         rb_raise(rb_eArgError, "pair must be [key, value]");
00707     }
00708     fgdbm_store(dbm, RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1]);
00709     return Qnil;
00710 }
00711 
00712 /*
00713  * call-seq:
00714  *     gdbm.update(other) -> gdbm
00715  *
00716  * Adds the key-value pairs of _other_ to _gdbm_, overwriting entries with
00717  * duplicate keys with those from _other_. _other_ must have an each_pair
00718  * method.
00719  */
00720 static VALUE
00721 fgdbm_update(VALUE obj, VALUE other)
00722 {
00723     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00724     return obj;
00725 }
00726 
00727 /*
00728  * call-seq:
00729  *     gdbm.replace(other) -> gdbm
00730  *
00731  * Replaces the content of _gdbm_ with the key-value pairs of _other_.
00732  * _other_ must have an each_pair method.
00733  */
00734 static VALUE
00735 fgdbm_replace(VALUE obj, VALUE other)
00736 {
00737     fgdbm_clear(obj);
00738     rb_block_call(other, rb_intern("each_pair"), 0, 0, update_i, obj);
00739     return obj;
00740 }
00741 
00742 /*
00743  * call-seq:
00744  *      gdbm.length -> fixnum
00745  *      gdbm.size -> fixnum
00746  *
00747  * Returns the number of key-value pairs in this database.
00748  */
00749 static VALUE
00750 fgdbm_length(VALUE obj)
00751 {
00752     datum key, nextkey;
00753     struct dbmdata *dbmp;
00754     GDBM_FILE dbm;
00755     int i = 0;
00756 
00757     GetDBM2(obj, dbmp, dbm);
00758     if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size);
00759 
00760     for (key = gdbm_firstkey(dbm); key.dptr; key = nextkey) {
00761         nextkey = gdbm_nextkey(dbm, key);
00762         free(key.dptr);
00763         i++;
00764     }
00765     dbmp->di_size = i;
00766 
00767     return INT2FIX(i);
00768 }
00769 
00770 /*
00771  * call-seq:
00772  *      gdbm.empty? -> true or false
00773  *
00774  * Returns true if the database is empty.
00775  */
00776 static VALUE
00777 fgdbm_empty_p(VALUE obj)
00778 {
00779     datum key;
00780     struct dbmdata *dbmp;
00781     GDBM_FILE dbm;
00782 
00783     GetDBM(obj, dbmp);
00784     if (dbmp->di_size < 0) {
00785         dbm = dbmp->di_dbm;
00786 
00787         key = gdbm_firstkey(dbm);
00788         if (key.dptr) {
00789             free(key.dptr);
00790             return Qfalse;
00791         }
00792         return Qtrue;
00793     }
00794 
00795     if (dbmp->di_size == 0) return Qtrue;
00796     return Qfalse;
00797 }
00798 
00799 /*
00800  * call-seq:
00801  *      gdbm.each_value { |value| block } -> gdbm
00802  *
00803  * Executes _block_ for each key in the database, passing the corresponding
00804  * _value_ as a parameter.
00805  */
00806 static VALUE
00807 fgdbm_each_value(VALUE obj)
00808 {
00809     struct dbmdata *dbmp;
00810     GDBM_FILE dbm;
00811     VALUE keystr;
00812 
00813     RETURN_ENUMERATOR(obj, 0, 0);
00814 
00815     GetDBM2(obj, dbmp, dbm);
00816     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00817          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00818 
00819         rb_yield(rb_gdbm_fetch2(dbm, keystr));
00820         GetDBM2(obj, dbmp, dbm);
00821     }
00822     return obj;
00823 }
00824 
00825 /*
00826  * call-seq:
00827  *      gdbm.each_key { |key| block } -> gdbm
00828  *
00829  * Executes _block_ for each key in the database, passing the
00830  * _key_ as a parameter.
00831  */
00832 static VALUE
00833 fgdbm_each_key(VALUE obj)
00834 {
00835     struct dbmdata *dbmp;
00836     GDBM_FILE dbm;
00837     VALUE keystr;
00838 
00839     RETURN_ENUMERATOR(obj, 0, 0);
00840 
00841     GetDBM2(obj, dbmp, dbm);
00842     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00843          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00844 
00845         rb_yield(keystr);
00846         GetDBM2(obj, dbmp, dbm);
00847     }
00848     return obj;
00849 }
00850 
00851 /*
00852  * call-seq:
00853  *      gdbm.each_pair { |key, value| block } -> gdbm
00854  *
00855  * Executes _block_ for each key in the database, passing the _key_ and the
00856  * correspoding _value_ as a parameter.
00857  */
00858 static VALUE
00859 fgdbm_each_pair(VALUE obj)
00860 {
00861     GDBM_FILE dbm;
00862     struct dbmdata *dbmp;
00863     VALUE keystr;
00864 
00865     RETURN_ENUMERATOR(obj, 0, 0);
00866 
00867     GetDBM2(obj, dbmp, dbm);
00868     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00869          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00870 
00871         rb_yield(rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr)));
00872         GetDBM2(obj, dbmp, dbm);
00873     }
00874 
00875     return obj;
00876 }
00877 
00878 /*
00879  * call-seq:
00880  *      gdbm.keys -> array
00881  *
00882  * Returns an array of all keys of this database.
00883  */
00884 static VALUE
00885 fgdbm_keys(VALUE obj)
00886 {
00887     struct dbmdata *dbmp;
00888     GDBM_FILE dbm;
00889     VALUE keystr, ary;
00890 
00891     GetDBM2(obj, dbmp, dbm);
00892     ary = rb_ary_new();
00893     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00894          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00895 
00896         rb_ary_push(ary, keystr);
00897     }
00898 
00899     return ary;
00900 }
00901 
00902 /*
00903  * call-seq:
00904  *      gdbm.values -> array
00905  *
00906  * Returns an array of all values of this database.
00907  */
00908 static VALUE
00909 fgdbm_values(VALUE obj)
00910 {
00911     datum key, nextkey;
00912     struct dbmdata *dbmp;
00913     GDBM_FILE dbm;
00914     VALUE valstr, ary;
00915 
00916     GetDBM2(obj, dbmp, dbm);
00917     ary = rb_ary_new();
00918     for (key = gdbm_firstkey(dbm); key.dptr; key = nextkey) {
00919         nextkey = gdbm_nextkey(dbm, key);
00920         valstr = rb_gdbm_fetch(dbm, key);
00921         free(key.dptr);
00922         rb_ary_push(ary, valstr);
00923     }
00924 
00925     return ary;
00926 }
00927 
00928 /*
00929  * call-seq:
00930  *      gdbm.has_key?(k) -> true or false
00931  *      gdbm.key?(k) -> true or false
00932  *
00933  * Returns true if the given key _k_ exists within the database.
00934  * Returns false otherwise.
00935  */
00936 static VALUE
00937 fgdbm_has_key(VALUE obj, VALUE keystr)
00938 {
00939     datum key;
00940     struct dbmdata *dbmp;
00941     GDBM_FILE dbm;
00942 
00943     StringValue(keystr);
00944     key.dptr = RSTRING_PTR(keystr);
00945     key.dsize = RSTRING_LEN(keystr);
00946 
00947     GetDBM2(obj, dbmp, dbm);
00948     if (gdbm_exists(dbm, key))
00949         return Qtrue;
00950     return Qfalse;
00951 }
00952 
00953 /*
00954  * call-seq:
00955  *      gdbm.has_value?(v) -> true or false
00956  *      gdbm.value?(v) -> true or false
00957  *
00958  * Returns true if the given value _v_ exists within the database.
00959  * Returns false otherwise.
00960  */
00961 static VALUE
00962 fgdbm_has_value(VALUE obj, VALUE valstr)
00963 {
00964     struct dbmdata *dbmp;
00965     GDBM_FILE dbm;
00966     VALUE keystr, valstr2;
00967 
00968     StringValue(valstr);
00969     GetDBM2(obj, dbmp, dbm);
00970     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
00971          keystr = rb_gdbm_nextkey(dbm, keystr)) {
00972 
00973         valstr2 = rb_gdbm_fetch2(dbm, keystr);
00974 
00975         if (!NIL_P(valstr2) &&
00976             RSTRING_LEN(valstr) == RSTRING_LEN(valstr2) &&
00977             memcmp(RSTRING_PTR(valstr), RSTRING_PTR(valstr2),
00978                    RSTRING_LEN(valstr)) == 0) {
00979             return Qtrue;
00980         }
00981     }
00982     return Qfalse;
00983 }
00984 
00985 /*
00986  * call-seq:
00987  *     gdbm.to_a -> array
00988  *
00989  * Returns an array of all key-value pairs contained in the database.
00990  */
00991 static VALUE
00992 fgdbm_to_a(VALUE obj)
00993 {
00994     struct dbmdata *dbmp;
00995     GDBM_FILE dbm;
00996     VALUE keystr, ary;
00997 
00998     GetDBM2(obj, dbmp, dbm);
00999     ary = rb_ary_new();
01000     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01001          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01002 
01003         rb_ary_push(ary, rb_assoc_new(keystr, rb_gdbm_fetch2(dbm, keystr)));
01004     }
01005 
01006     return ary;
01007 }
01008 
01009 /*
01010  * call-seq:
01011  *     gdbm.reorganize -> gdbm
01012  *
01013  * Reorganizes the database file. This operation removes reserved space of
01014  * elements that have already been deleted. It is only useful after a lot of
01015  * deletions in the database.
01016  */
01017 static VALUE
01018 fgdbm_reorganize(VALUE obj)
01019 {
01020     struct dbmdata *dbmp;
01021     GDBM_FILE dbm;
01022 
01023     rb_gdbm_modify(obj);
01024     GetDBM2(obj, dbmp, dbm);
01025     gdbm_reorganize(dbm);
01026     return obj;
01027 }
01028 
01029 /*
01030  * call-seq:
01031  *     gdbm.sync -> gdbm
01032  *
01033  * Unless the _gdbm_ object has been opened with the *SYNC* flag, it is not
01034  * guarenteed that database modification operations are immediately applied to
01035  * the database file. This method ensures that all recent modifications
01036  * to the database are written to the file. Blocks until all writing operations
01037  * to the disk have been finished.
01038  */
01039 static VALUE
01040 fgdbm_sync(VALUE obj)
01041 {
01042     struct dbmdata *dbmp;
01043     GDBM_FILE dbm;
01044 
01045     rb_gdbm_modify(obj);
01046     GetDBM2(obj, dbmp, dbm);
01047     gdbm_sync(dbm);
01048     return obj;
01049 }
01050 
01051 /*
01052  * call-seq:
01053  *     gdbm.cachesize = size -> size
01054  *
01055  * Sets the size of the internal bucket cache to _size_.
01056  */
01057 static VALUE
01058 fgdbm_set_cachesize(VALUE obj, VALUE val)
01059 {
01060     struct dbmdata *dbmp;
01061     GDBM_FILE dbm;
01062     int optval;
01063 
01064     GetDBM2(obj, dbmp, dbm);
01065     optval = FIX2INT(val);
01066     if (gdbm_setopt(dbm, GDBM_CACHESIZE, &optval, sizeof(optval)) == -1) {
01067         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01068     }
01069     return val;
01070 }
01071 
01072 /*
01073  * call-seq:
01074  *     gdbm.fastmode = boolean -> boolean
01075  *
01076  * Turns the database's fast mode on or off. If fast mode is turned on, gdbm
01077  * does not wait for writes to be flushed to the disk before continuing.
01078  *
01079  * This option is obsolete for gdbm >= 1.8 since fast mode is turned on by
01080  * default. See also: #syncmode=
01081  */
01082 static VALUE
01083 fgdbm_set_fastmode(VALUE obj, VALUE val)
01084 {
01085     struct dbmdata *dbmp;
01086     GDBM_FILE dbm;
01087     int optval;
01088 
01089     GetDBM2(obj, dbmp, dbm);
01090     optval = 0;
01091     if (RTEST(val))
01092         optval = 1;
01093 
01094     if (gdbm_setopt(dbm, GDBM_FASTMODE, &optval, sizeof(optval)) == -1) {
01095         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01096     }
01097     return val;
01098 }
01099 
01100 /*
01101  * call-seq:
01102  *     gdbm.syncmode = boolean -> boolean
01103  *
01104  * Turns the database's synchronization mode on or off. If the synchronization
01105  * mode is turned on, the database's in-memory state will be synchronized to
01106  * disk after every database modification operation. If the synchronization
01107  * mode is turned off, GDBM does not wait for writes to be flushed to the disk
01108  * before continuing.
01109  *
01110  * This option is only available for gdbm >= 1.8 where syncmode is turned off
01111  * by default. See also: #fastmode=
01112  */
01113 static VALUE
01114 fgdbm_set_syncmode(VALUE obj, VALUE val)
01115 {
01116 #if !defined(GDBM_SYNCMODE)
01117     fgdbm_set_fastmode(obj, RTEST(val) ? Qfalse : Qtrue);
01118     return val;
01119 #else
01120     struct dbmdata *dbmp;
01121     GDBM_FILE dbm;
01122     int optval;
01123 
01124     GetDBM2(obj, dbmp, dbm);
01125     optval = 0;
01126     if (RTEST(val))
01127         optval = 1;
01128 
01129     if (gdbm_setopt(dbm, GDBM_FASTMODE, &optval, sizeof(optval)) == -1) {
01130         rb_raise(rb_eGDBMError, "%s", gdbm_strerror(gdbm_errno));
01131     }
01132     return val;
01133 #endif
01134 }
01135 
01136 /*
01137  * call-seq:
01138  *     gdbm.to_hash -> hash
01139  *
01140  * Returns a hash of all key-value pairs contained in the database.
01141  */
01142 static VALUE
01143 fgdbm_to_hash(VALUE obj)
01144 {
01145     struct dbmdata *dbmp;
01146     GDBM_FILE dbm;
01147     VALUE keystr, hash;
01148 
01149     GetDBM2(obj, dbmp, dbm);
01150     hash = rb_hash_new();
01151     for (keystr = rb_gdbm_firstkey(dbm); RTEST(keystr);
01152          keystr = rb_gdbm_nextkey(dbm, keystr)) {
01153 
01154         rb_hash_aset(hash, keystr, rb_gdbm_fetch2(dbm, keystr));
01155     }
01156 
01157     return hash;
01158 }
01159 
01160 /*
01161  * call-seq:
01162  *      gdbm.reject { |key, value| block } -> hash
01163  *
01164  * Returns a hash copy of _gdbm_ where all key-value pairs from _gdbm_ for
01165  * which _block_ evaluates to true are removed. See also: #delete_if
01166  */
01167 static VALUE
01168 fgdbm_reject(VALUE obj)
01169 {
01170     return rb_hash_delete_if(fgdbm_to_hash(obj));
01171 }
01172 
01173 void
01174 Init_gdbm(void)
01175 {
01176     rb_cGDBM = rb_define_class("GDBM", rb_cObject);
01177     rb_eGDBMError = rb_define_class("GDBMError", rb_eStandardError);
01178     rb_eGDBMFatalError = rb_define_class("GDBMFatalError", rb_eException);
01179     rb_include_module(rb_cGDBM, rb_mEnumerable);
01180 
01181     rb_define_alloc_func(rb_cGDBM, fgdbm_s_alloc);
01182     rb_define_singleton_method(rb_cGDBM, "open", fgdbm_s_open, -1);
01183 
01184     rb_define_method(rb_cGDBM, "initialize", fgdbm_initialize, -1);
01185     rb_define_method(rb_cGDBM, "close", fgdbm_close, 0);
01186     rb_define_method(rb_cGDBM, "closed?", fgdbm_closed, 0);
01187     rb_define_method(rb_cGDBM, "[]", fgdbm_aref, 1);
01188     rb_define_method(rb_cGDBM, "fetch", fgdbm_fetch_m, -1);
01189     rb_define_method(rb_cGDBM, "[]=", fgdbm_store, 2);
01190     rb_define_method(rb_cGDBM, "store", fgdbm_store, 2);
01191     rb_define_method(rb_cGDBM, "index",  fgdbm_index, 1);
01192     rb_define_method(rb_cGDBM, "key",  fgdbm_key, 1);
01193     rb_define_method(rb_cGDBM, "select",  fgdbm_select, 0);
01194     rb_define_method(rb_cGDBM, "values_at",  fgdbm_values_at, -1);
01195     rb_define_method(rb_cGDBM, "length", fgdbm_length, 0);
01196     rb_define_method(rb_cGDBM, "size", fgdbm_length, 0);
01197     rb_define_method(rb_cGDBM, "empty?", fgdbm_empty_p, 0);
01198     rb_define_method(rb_cGDBM, "each", fgdbm_each_pair, 0);
01199     rb_define_method(rb_cGDBM, "each_value", fgdbm_each_value, 0);
01200     rb_define_method(rb_cGDBM, "each_key", fgdbm_each_key, 0);
01201     rb_define_method(rb_cGDBM, "each_pair", fgdbm_each_pair, 0);
01202     rb_define_method(rb_cGDBM, "keys", fgdbm_keys, 0);
01203     rb_define_method(rb_cGDBM, "values", fgdbm_values, 0);
01204     rb_define_method(rb_cGDBM, "shift", fgdbm_shift, 0);
01205     rb_define_method(rb_cGDBM, "delete", fgdbm_delete, 1);
01206     rb_define_method(rb_cGDBM, "delete_if", fgdbm_delete_if, 0);
01207     rb_define_method(rb_cGDBM, "reject!", fgdbm_delete_if, 0);
01208     rb_define_method(rb_cGDBM, "reject", fgdbm_reject, 0);
01209     rb_define_method(rb_cGDBM, "clear", fgdbm_clear, 0);
01210     rb_define_method(rb_cGDBM, "invert", fgdbm_invert, 0);
01211     rb_define_method(rb_cGDBM, "update", fgdbm_update, 1);
01212     rb_define_method(rb_cGDBM, "replace", fgdbm_replace, 1);
01213     rb_define_method(rb_cGDBM, "reorganize", fgdbm_reorganize, 0);
01214     rb_define_method(rb_cGDBM, "sync", fgdbm_sync, 0);
01215     /* rb_define_method(rb_cGDBM, "setopt", fgdbm_setopt, 2); */
01216     rb_define_method(rb_cGDBM, "cachesize=", fgdbm_set_cachesize, 1);
01217     rb_define_method(rb_cGDBM, "fastmode=", fgdbm_set_fastmode, 1);
01218     rb_define_method(rb_cGDBM, "syncmode=", fgdbm_set_syncmode, 1);
01219 
01220     rb_define_method(rb_cGDBM, "include?", fgdbm_has_key, 1);
01221     rb_define_method(rb_cGDBM, "has_key?", fgdbm_has_key, 1);
01222     rb_define_method(rb_cGDBM, "member?", fgdbm_has_key, 1);
01223     rb_define_method(rb_cGDBM, "has_value?", fgdbm_has_value, 1);
01224     rb_define_method(rb_cGDBM, "key?", fgdbm_has_key, 1);
01225     rb_define_method(rb_cGDBM, "value?", fgdbm_has_value, 1);
01226 
01227     rb_define_method(rb_cGDBM, "to_a", fgdbm_to_a, 0);
01228     rb_define_method(rb_cGDBM, "to_hash", fgdbm_to_hash, 0);
01229 
01230     /* flag for #new and #open: open database as a reader */
01231     rb_define_const(rb_cGDBM, "READER",  INT2FIX(GDBM_READER|RUBY_GDBM_RW_BIT));
01232     /* flag for #new and #open: open database as a writer */
01233     rb_define_const(rb_cGDBM, "WRITER",  INT2FIX(GDBM_WRITER|RUBY_GDBM_RW_BIT));
01234     /* flag for #new and #open: open database as a writer; if the database does not exist, create a new one */
01235     rb_define_const(rb_cGDBM, "WRCREAT", INT2FIX(GDBM_WRCREAT|RUBY_GDBM_RW_BIT));
01236     /* flag for #new and #open: open database as a writer; overwrite any existing databases  */
01237     rb_define_const(rb_cGDBM, "NEWDB",   INT2FIX(GDBM_NEWDB|RUBY_GDBM_RW_BIT));
01238 
01239     /* flag for #new and #open. this flag is obsolete for gdbm >= 1.8 */
01240     rb_define_const(rb_cGDBM, "FAST", INT2FIX(GDBM_FAST));
01241     /* this flag is obsolete in gdbm 1.8.
01242        On gdbm 1.8, fast mode is default behavior. */
01243 
01244     /* gdbm version 1.8 specific */
01245 #if defined(GDBM_SYNC)
01246     /* flag for #new and #open. only for gdbm >= 1.8 */
01247     rb_define_const(rb_cGDBM, "SYNC",    INT2FIX(GDBM_SYNC));
01248 #endif
01249 #if defined(GDBM_NOLOCK)
01250     /* flag for #new and #open */
01251     rb_define_const(rb_cGDBM, "NOLOCK",  INT2FIX(GDBM_NOLOCK));
01252 #endif
01253     /* version of the gdbm library*/
01254     rb_define_const(rb_cGDBM, "VERSION",  rb_str_new2(gdbm_version));
01255 }
01256 

Generated on Wed Aug 10 09:17:00 2011 for Ruby by  doxygen 1.4.7