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 }