Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvbdbhash.cc

Go to the documentation of this file.
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2003 Net Integration Technologies, Inc. 00004 * 00005 * A hash table container backed by a Berkeley DB (libdb) database. 00006 * See wvbdbhash.h. 00007 */ 00008 #include "wvautoconf.h" 00009 00010 #ifdef WITH_BDB 00011 00012 #include "wvbdbhash.h" 00013 #include <fcntl.h> 00014 #include <errno.h> 00015 #include <unistd.h> 00016 00017 #ifdef HAVE_DB_H 00018 #include <db.h> 00019 #else 00020 #ifdef HAVE_DB_185_H 00021 #include <db_185.h> 00022 #endif 00023 #endif 00024 00025 #include "wvlog.h" 00026 00027 int comparefunc(const DBT *a, const DBT *b) 00028 { 00029 if (a == NULL && b == NULL) return 0; 00030 if (a == NULL) return 1; 00031 if (b == NULL) return -1; 00032 00033 size_t minlen = (a->size > b->size) ? b->size : a->size; 00034 int ret = memcmp(a->data, b->data, minlen); 00035 if (ret != 0) return ret; 00036 if (a->size > b->size) return 1; 00037 if (a->size < b->size) return -1; 00038 return 0; 00039 } 00040 00041 00042 WvBdbHashBase::WvBdbHashBase(WvStringParm _dbfile, bool _persist) 00043 { 00044 dbf = NULL; 00045 opendb(_dbfile, _persist); 00046 } 00047 00048 00049 WvBdbHashBase::~WvBdbHashBase() 00050 { 00051 closedb(); 00052 } 00053 00054 00055 void WvBdbHashBase::opendb(WvStringParm _dbfile, bool _persist) 00056 { 00057 closedb(); 00058 00059 noerr(); 00060 00061 dbfile = _dbfile; 00062 persist_dbfile = _persist; 00063 00064 BTREEINFO info; 00065 memset(&info, 0, sizeof(info)); 00066 info.compare = comparefunc; 00067 00068 int mode = O_CREAT | O_RDWR; 00069 if (!persist_dbfile) mode |= O_TRUNC; 00070 00071 dbf = dbopen(!!dbfile ? dbfile.cstr() : NULL, mode, 0666, DB_BTREE, 00072 &info); 00073 if (!dbf) seterr(errno); 00074 } 00075 00076 00077 void WvBdbHashBase::closedb() 00078 { 00079 if (dbf) 00080 { 00081 if (!dbf->close(dbf)) seterr(errno); 00082 if (!persist_dbfile && !!dbfile) 00083 ::unlink(dbfile); 00084 dbf = NULL; 00085 } 00086 seterr("The db is closed."); // this won't overwrite an earlier err 00087 } 00088 00089 00090 void WvBdbHashBase::add(const datum &key, const datum &data, bool replace) 00091 { 00092 if (!isok()) return; 00093 int r = dbf->put(dbf, (DBT *)&key, (DBT *)&data, 00094 !replace ? R_NOOVERWRITE : 0); 00095 if (r == 1) 00096 seterr("Must set the replace flag to replace existing elements."); 00097 else if (r != 0) 00098 seterr(errno); 00099 } 00100 00101 00102 void WvBdbHashBase::remove(const datum &key) 00103 { 00104 if (!isok()) return; 00105 00106 datum newkey, data; 00107 newkey = key; 00108 00109 int ret = dbf->seq(dbf, (DBT *)&newkey, (DBT *)&data, R_CURSOR); 00110 if (!ret) 00111 { 00112 ret = dbf->del(dbf, (DBT *)&newkey, R_CURSOR); 00113 } 00114 00115 if (ret == 1) seterr("Strange: seq found a key that del didn't recognize"); 00116 else if (ret) seterr(errno); 00117 } 00118 00119 00120 WvBdbHashBase::datum WvBdbHashBase::find(const datum &key) 00121 { 00122 datum ret = {0, 0}; 00123 if (!isok()) return ret; 00124 00125 int r = dbf->get(dbf, (DBT *)&key, (DBT *)&ret, 0); 00126 if (r == 1) 00127 { 00128 // not found - make sure we return an empty datum 00129 ret.dptr = NULL; 00130 } 00131 else if (r != 0) 00132 { 00133 ret.dptr = NULL; 00134 seterr(errno); 00135 } 00136 return ret; 00137 } 00138 00139 00140 bool WvBdbHashBase::exists(const datum &key) 00141 { 00142 if (!isok()) return false; 00143 00144 datum ret = {0, 0}; 00145 int r = dbf->get(dbf, (DBT *)&key, (DBT *)&ret, 0); 00146 00147 // return true on success 00148 if (r == 0) return true; 00149 00150 // return false on 1 (not found) or -1 (error) 00151 if (r != 1) seterr(errno); 00152 return false; 00153 } 00154 00155 00156 void WvBdbHashBase::zap() 00157 { 00158 if (!dbfile) 00159 { 00160 // we're broken - nothing we can do 00161 if (!isok()) 00162 { 00163 closedb(); 00164 return; 00165 } 00166 00167 // super-slow version 00168 datum key, value; 00169 int r; 00170 while ((r = dbf->seq(dbf, (DBT *)&key, (DBT *)&value, R_FIRST)) == 0) 00171 { 00172 int r2 = dbf->del(dbf, (DBT *)&key, R_CURSOR); 00173 if (r2 == 1) seterr("Strange: seq found a key that del didn't recognize"); 00174 else if (r2 != 0) seterr(errno); 00175 } 00176 if (r != 1) seterr(errno); // 1 = not found 00177 } 00178 else // delete the database file and reopen - much quicker! 00179 { 00180 if (dbf) 00181 { 00182 dbf->close(dbf); // don't care if it fails 00183 dbf = NULL; 00184 } 00185 int fd = open(dbfile, O_RDWR | O_TRUNC); 00186 if (fd >= 0) ::close(fd); 00187 opendb(dbfile, persist_dbfile); 00188 } 00189 } 00190 00191 00192 WvBdbHashBase::IterBase::IterBase(WvBdbHashBase &_bdbhash) 00193 : bdbhash(_bdbhash) 00194 { 00195 rewindto.dsize = 0; 00196 rewindto.dptr = NULL; 00197 } 00198 00199 00200 WvBdbHashBase::IterBase::~IterBase() 00201 { 00202 free(rewindto.dptr); 00203 } 00204 00205 00206 void WvBdbHashBase::IterBase::rewind() 00207 { 00208 free(rewindto.dptr); 00209 rewindto.dptr = NULL; 00210 } 00211 00212 00213 void WvBdbHashBase::IterBase::rewind(const datum &firstkey, datum &curkey, 00214 datum &curdata) 00215 { 00216 // save the firstkey and clear the current one 00217 free(rewindto.dptr); 00218 rewindto.dsize = firstkey.dsize; 00219 rewindto.dptr = malloc(rewindto.dsize); 00220 memcpy(rewindto.dptr, firstkey.dptr, rewindto.dsize); 00221 curkey.dptr = curdata.dptr = NULL; 00222 } 00223 00224 00225 void WvBdbHashBase::IterBase::next(datum &curkey, datum &curdata) 00226 { 00227 if (!bdbhash.isok()) return; 00228 00229 // check if this is the first next() after a rewind() 00230 bool first = !curkey.dptr; 00231 datum wanted = { 0, 0 }; 00232 if (first) 00233 { 00234 if (rewindto.dptr) 00235 { 00236 curkey = rewindto; 00237 first = false; 00238 } 00239 } 00240 else 00241 { 00242 wanted.dsize = curkey.dsize; 00243 wanted.dptr = malloc(wanted.dsize); 00244 memcpy(wanted.dptr, curkey.dptr, wanted.dsize); 00245 } 00246 00247 // always seek for the saved cursor we were just passed, to work around 00248 // bugs in libdb1's seq with btrees. (As a bonus, this gives us multiple 00249 // iterators for free!) 00250 int r = bdbhash.dbf->seq(bdbhash.dbf, (DBT *)&curkey, (DBT *)&curdata, 00251 first ? R_FIRST : R_CURSOR); 00252 if (r == 1) 00253 { 00254 // current key gone, and none higher left: done 00255 curkey.dptr = curdata.dptr = NULL; 00256 } 00257 else if (r != 0) 00258 bdbhash.seterr(errno); 00259 00260 else if (!first) 00261 { 00262 while (comparefunc((DBT *)&wanted, (DBT *)&curkey) >= 0) 00263 { 00264 // found the exact key or earlier than requested: move forward one 00265 // (yes, libbdb1 can return a key less than requested, despite 00266 // the documentation's claims!) 00267 // This algorithm definitely makes it so inserting the same key 00268 // more than once doesn't work at all. 00269 r = bdbhash.dbf->seq(bdbhash.dbf, (DBT *)&curkey, (DBT *)&curdata, 00270 R_NEXT); 00271 00272 if (r == 1) 00273 { 00274 // nothing left? Fine, we're done 00275 curkey.dptr = curdata.dptr = NULL; 00276 break; 00277 } 00278 else if (r != 0) 00279 bdbhash.seterr(errno); 00280 } 00281 } 00282 00283 // otherwise, curkey is now sitting on the key after the requested one (or 00284 // the very first key), as expected. (Also, if rewindto is set it should 00285 // be either filled in with the matching btree data or cleared.) Unless, 00286 // of course, the whole db is borked. 00287 assert(!bdbhash.isok() || !rewindto.dptr || curkey.dptr != rewindto.dptr); 00288 free(wanted.dptr); 00289 } 00290 00291 00292 void WvBdbHashBase::IterBase::xunlink(const datum &curkey) 00293 { 00294 bdbhash.remove(curkey); 00295 } 00296 00297 00298 void WvBdbHashBase::IterBase::update(const datum &curkey, const datum &data) 00299 { 00300 bdbhash.add(curkey, data, true); 00301 } 00302 00303 #endif 00304

Generated on Tue Oct 5 01:09:19 2004 for WvStreams by doxygen 1.3.7