00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00036 #include "OW_config.h"
00037 #include "OW_Index.hpp"
00038 #include "OW_FileSystem.hpp"
00039 #include "OW_Exception.hpp"
00040 #include "OW_Format.hpp"
00041 #include "OW_ExceptionIds.hpp"
00042
00043 extern "C"
00044 {
00045 #ifndef OW_WIN32
00046 #include <unistd.h>
00047 #endif
00048 #include <string.h>
00049 #include <sys/types.h>
00050 #include <sys/stat.h>
00051 #include <fcntl.h>
00052 #include <errno.h>
00053 #include "db.h"
00054 }
00055
00056 extern "C" {
00057 static int recCompare(const DBT* key1, const DBT* key2);
00058 }
00059
00060 namespace OW_NAMESPACE
00061 {
00062
00063 OW_DECLARE_EXCEPTION(Index);
00064 OW_DEFINE_EXCEPTION_WITH_ID(Index);
00065
00067 class IndexImpl : public Index
00068 {
00069 public:
00070 IndexImpl();
00071 virtual ~IndexImpl();
00072 virtual void open(const char* fileName, EDuplicateKeysFlag allowDuplicates = E_NO_DUPLICATES);
00073 virtual void close();
00074 virtual IndexEntry findFirst(const char* key=NULL);
00075 virtual IndexEntry findNext();
00076 virtual IndexEntry findPrev();
00077 virtual IndexEntry find(const char* key);
00078 virtual bool add(const char* key, Int32 offset);
00079 virtual bool remove(const char* key, Int32 offset=-1L);
00080 virtual bool update(const char* key, Int32 newOffset);
00081 virtual void flush();
00082 void reopen();
00083 void openIfClosed();
00084 private:
00085 DB* m_pDB;
00086 String m_dbFileName;
00087 };
00089
00090 IndexRef
00091 Index::createIndexObject()
00092 {
00093 return IndexRef(new IndexImpl);
00094 }
00096 IndexImpl::IndexImpl() :
00097 m_pDB(NULL), m_dbFileName()
00098 {
00099 }
00101 IndexImpl::~IndexImpl()
00102 {
00103 try
00104 {
00105 close();
00106 }
00107 catch (...)
00108 {
00109
00110 }
00111 }
00113 void
00114 IndexImpl::open(const char* fileName, EDuplicateKeysFlag allowDuplicates)
00115 {
00116 close();
00117 BTREEINFO dbinfo;
00118 m_dbFileName = fileName;
00119 m_dbFileName += ".ndx";
00120 ::memset(&dbinfo, 0, sizeof(dbinfo));
00121 dbinfo.flags = (allowDuplicates == E_ALLDUPLICATES) ? R_DUP : 0;
00122 dbinfo.compare = recCompare;
00123 if (FileSystem::canRead(m_dbFileName)
00124 && FileSystem::canWrite(m_dbFileName))
00125 {
00126 #ifdef OW_WIN32
00127 m_pDB = dbopen(m_dbFileName.c_str(), O_RDWR, _S_IREAD | _S_IWRITE,
00128 DB_BTREE, &dbinfo);
00129 #else
00130 m_pDB = dbopen(m_dbFileName.c_str(), O_RDWR, S_IRUSR | S_IWUSR,
00131 DB_BTREE, &dbinfo);
00132 #endif
00133 if (m_pDB == NULL)
00134 {
00135 OW_THROW(IndexException, Format("Failed to open index file: %1, errno =%2(%3)", m_dbFileName, errno, strerror(errno)).c_str());
00136 }
00137 }
00138 else
00139 {
00140 #ifdef OW_WIN32
00141 m_pDB = dbopen(m_dbFileName.c_str(), O_TRUNC | O_RDWR | O_CREAT,
00142 _S_IREAD | _S_IWRITE, DB_BTREE, &dbinfo);
00143 #else
00144 m_pDB = dbopen(m_dbFileName.c_str(), O_TRUNC | O_RDWR | O_CREAT,
00145 S_IRUSR | S_IWUSR, DB_BTREE, &dbinfo);
00146 #endif
00147 if (m_pDB == NULL)
00148 {
00149 OW_THROW(IndexException, Format("Failed to create index file: %1, errno =%2(%3)", m_dbFileName, errno, strerror(errno)).c_str());
00150 }
00151 }
00152 }
00154 void
00155 IndexImpl::reopen()
00156 {
00157 BTREEINFO dbinfo;
00158 close();
00159 ::memset(&dbinfo, 0, sizeof(dbinfo));
00160 dbinfo.compare = recCompare;
00161 #ifdef OW_WIN32
00162 m_pDB = dbopen(m_dbFileName.c_str(), O_RDWR, _S_IREAD | _S_IWRITE,
00163 DB_BTREE, &dbinfo);
00164 #else
00165 m_pDB = dbopen(m_dbFileName.c_str(), O_RDWR, S_IRUSR | S_IWUSR,
00166 DB_BTREE, &dbinfo);
00167 #endif
00168 if (m_pDB == NULL)
00169 {
00170 String msg = "Failed to re-open index file: ";
00171 msg += m_dbFileName;
00172 OW_THROW(IndexException, msg.c_str());
00173 }
00174 }
00176 void
00177 IndexImpl::openIfClosed()
00178 {
00179 if (!m_pDB)
00180 {
00181 BTREEINFO dbinfo;
00182 ::memset(&dbinfo, 0, sizeof(dbinfo));
00183 dbinfo.compare = recCompare;
00184 #ifdef OW_WIN32
00185 m_pDB = dbopen(m_dbFileName.c_str(), O_RDWR, _S_IREAD | _S_IWRITE,
00186 DB_BTREE, &dbinfo);
00187 #else
00188 m_pDB = dbopen(m_dbFileName.c_str(), O_RDWR, S_IRUSR | S_IWUSR,
00189 DB_BTREE, &dbinfo);
00190 #endif
00191 if (m_pDB == NULL)
00192 {
00193 String msg = "Failed to re-open index file: ";
00194 msg += m_dbFileName;
00195 OW_THROW(IndexException, msg.c_str());
00196 }
00197 }
00198 }
00200 void
00201 IndexImpl::close()
00202 {
00203 if (m_pDB != NULL)
00204 {
00205 m_pDB->close(m_pDB);
00206 m_pDB = NULL;
00207 }
00208 }
00210 void
00211 IndexImpl::flush()
00212 {
00213 if (m_pDB != NULL)
00214 {
00215 reopen();
00216
00217 }
00218 }
00219 namespace
00220 {
00221 class OpenCloser
00222 {
00223 public:
00224 OpenCloser(IndexImpl* pIndex) : m_pIndex(pIndex)
00225 {
00226 m_pIndex->openIfClosed();
00227 }
00228 ~OpenCloser()
00229 {
00230 m_pIndex->close();
00231 }
00232 private:
00233 IndexImpl* m_pIndex;
00234 };
00235 }
00237
00238 IndexEntry
00239 IndexImpl::find(const char* key)
00240 {
00241 openIfClosed();
00242 if (m_pDB == NULL)
00243 {
00244 OW_THROW(IndexException, "Index file hasn't been opened");
00245 }
00246 DBT theKey, theRec;
00247 theKey.data = const_cast<void*>(static_cast<const void*>(key));
00248 theKey.size = ::strlen(key)+1;
00249 if (m_pDB->seq(m_pDB, &theKey, &theRec, R_CURSOR) == 0)
00250 {
00251 if (!::strcmp(reinterpret_cast<const char*>(theKey.data), key))
00252 {
00253 Int32 tmp;
00254 memcpy(&tmp, theRec.data, sizeof(tmp));
00255 return IndexEntry(reinterpret_cast<const char*>(theKey.data), tmp);
00256 }
00257 }
00258 return IndexEntry();
00259 }
00261 bool
00262 IndexImpl::add(const char* key, Int32 offset)
00263 {
00264 OpenCloser oc(this);
00265 if (m_pDB == NULL)
00266 {
00267 OW_THROW(IndexException, "Index file hasn't been opened");
00268 }
00269 DBT theRec, theKey;
00270 theRec.data = &offset;
00271 theRec.size = sizeof(offset);
00272 theKey.data = const_cast<void*>(static_cast<const void*>(key));
00273 theKey.size = ::strlen(key)+1;
00274 return (m_pDB->put(m_pDB, &theKey, &theRec, 0) == 0);
00275 }
00277 bool
00278 IndexImpl::remove(const char* key, Int32 offset)
00279 {
00280 OpenCloser oc(this);
00281 if (m_pDB == NULL)
00282 {
00283 OW_THROW(IndexException, "Index file hasn't been opened");
00284 }
00285 DBT theKey;
00286 theKey.data = const_cast<void*>(static_cast<const void*>(key));
00287 theKey.size = ::strlen(key)+1;
00288 IndexEntry ientry = findFirst(key);
00289 while (ientry)
00290 {
00291 if (!ientry.key.equals(key))
00292 {
00293 break;
00294 }
00295 if (offset == -1L || ientry.offset == offset)
00296 {
00297 return (m_pDB->del(m_pDB, &theKey, R_CURSOR) == 0);
00298 }
00299 ientry = findNext();
00300 }
00301 return false;
00302 }
00304 bool
00305 IndexImpl::update(const char* key, Int32 newOffset)
00306 {
00307 OpenCloser oc(this);
00308 if (m_pDB == NULL)
00309 {
00310 OW_THROW(IndexException, "Index file hasn't been opened");
00311 }
00312 if (!find(key))
00313 {
00314 return false;
00315 }
00316 DBT theRec, theKey;
00317 theRec.data = &newOffset;
00318 theRec.size = sizeof(newOffset);
00319 theKey.data = const_cast<void*>(static_cast<const void*>(key));
00320 theKey.size = ::strlen(key)+1;
00321 return (m_pDB->put(m_pDB, &theKey, &theRec, R_CURSOR) == 0);
00322 }
00324 IndexEntry
00325 IndexImpl::findFirst(const char* key)
00326 {
00327 openIfClosed();
00328 if (m_pDB == NULL)
00329 {
00330 OW_THROW(IndexException, "Index file hasn't been opened");
00331 }
00332 DBT theRec, theKey;
00333 memset(&theKey, 0, sizeof(theKey));
00334 memset(&theRec, 0, sizeof(theRec));
00335 int op = R_FIRST;
00336 if (key != NULL)
00337 {
00338 op = R_CURSOR;
00339 theKey.data = const_cast<void*>(static_cast<const void*>(key));
00340 theKey.size = ::strlen(key)+1;
00341 }
00342 int cc = m_pDB->seq(m_pDB, &theKey, &theRec, op);
00343 if (cc == 0)
00344 {
00345 Int32 tmp;
00346 memcpy(&tmp, theRec.data, sizeof(tmp));
00347 return IndexEntry(reinterpret_cast<const char*>(theKey.data), tmp);
00348 }
00349 return IndexEntry();
00350 }
00352 IndexEntry
00353 IndexImpl::findNext()
00354 {
00355 openIfClosed();
00356 if (m_pDB == NULL)
00357 {
00358 OW_THROW(IndexException, "Index file hasn't been opened");
00359 }
00360 DBT theRec, theKey;
00361 if (m_pDB->seq(m_pDB, &theKey, &theRec, R_NEXT) == 0)
00362 {
00363 Int32 tmp;
00364 memcpy(&tmp, theRec.data, sizeof(tmp));
00365 return IndexEntry(reinterpret_cast<const char*>(theKey.data), tmp);
00366 }
00367 return IndexEntry();
00368 }
00370 IndexEntry
00371 IndexImpl::findPrev()
00372 {
00373 openIfClosed();
00374 if (m_pDB == NULL)
00375 {
00376 OW_THROW(IndexException, "Index file hasn't been opened");
00377 }
00378 DBT theRec, theKey;
00379 if (m_pDB->seq(m_pDB, &theKey, &theRec, R_PREV) == 0)
00380 {
00381 Int32 tmp;
00382 memcpy(&tmp, theRec.data, sizeof(tmp));
00383 return IndexEntry(reinterpret_cast<const char*>(theKey.data), tmp);
00384 }
00385 return IndexEntry();
00386 }
00387
00388 }
00389
00391 extern "C" {
00392 static int
00393 recCompare(const DBT* key1, const DBT* key2)
00394 {
00395 if (key1->data && key2->data)
00396 {
00397 return strcmp(reinterpret_cast<const char*>(key1->data),
00398 reinterpret_cast<const char*>(key2->data));
00399 }
00400 else if (key1->data && !key2->data)
00401 {
00402 return 1;
00403 }
00404 else if (!key1->data && key2->data)
00405 {
00406 return -1;
00407 }
00408 else
00409 {
00410 return 0;
00411 }
00412 }
00413 }