diff options
author | George Hazan <george.hazan@gmail.com> | 2015-04-02 17:28:07 +0000 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2015-04-02 17:28:07 +0000 |
commit | 2e511ab1b1ff3d78c695874e3b28ff4ce7680cc8 (patch) | |
tree | 9c3588c82da7ad3e326f51d899800ad183f0d826 /plugins/Dbx_kyoto/src/kyotocabinet/kcdirdb.h | |
parent | 0f73f1572a03e5bae2664c1b2bb2cd18a1e33fca (diff) |
kyotocabinet based db driver
first version that compiles
DO NOT USE IT, dragons live there
git-svn-id: http://svn.miranda-ng.org/main/trunk@12580 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Dbx_kyoto/src/kyotocabinet/kcdirdb.h')
-rw-r--r-- | plugins/Dbx_kyoto/src/kyotocabinet/kcdirdb.h | 2437 |
1 files changed, 2437 insertions, 0 deletions
diff --git a/plugins/Dbx_kyoto/src/kyotocabinet/kcdirdb.h b/plugins/Dbx_kyoto/src/kyotocabinet/kcdirdb.h new file mode 100644 index 0000000000..c80ba2d143 --- /dev/null +++ b/plugins/Dbx_kyoto/src/kyotocabinet/kcdirdb.h @@ -0,0 +1,2437 @@ +/************************************************************************************************* + * Directory hash database + * Copyright (C) 2009-2012 FAL Labs + * This file is part of Kyoto Cabinet. + * This program is free software: you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation, either version + * 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License along with this program. + * If not, see <http://www.gnu.org/licenses/>. + *************************************************************************************************/ + + +#ifndef _KCDIRDB_H // duplication check +#define _KCDIRDB_H + +#include <kccommon.h> +#include <kcutil.h> +#include <kcthread.h> +#include <kcfile.h> +#include <kccompress.h> +#include <kccompare.h> +#include <kcmap.h> +#include <kcregex.h> +#include <kcdb.h> +#include <kcplantdb.h> + +#define KCDDBMAGICFILE "__KCDIR__" ///< magic file of the directory +#define KCDDBMETAFILE "__meta__" ///< meta data file of the directory +#define KCDDBOPAQUEFILE "__opq__" ///< opaque file of the directory +#define KCDDBATRANPREFIX "_x" ///< prefix of files for auto transaction +#define KCDDBCHKSUMSEED "__kyotocabinet__" ///< seed of the module checksum +#define KCDDBMAGICEOF "_EOF_" ///< magic data for the end of file +#define KCDDBWALPATHEXT "wal" ///< extension of the WAL directory +#define KCDDBTMPPATHEXT "tmp" ///< extension of the temporary directory + +namespace kyotocabinet { // common namespace + + +/** + * Directory hash database. + * @note This class is a concrete class to operate a hash database in a directory. This class + * can be inherited but overwriting methods is forbidden. Before every database operation, it is + * necessary to call the DirDB::open method in order to open a database file and connect the + * database object to it. To avoid data missing or corruption, it is important to close every + * database file by the DirDB::close method when the database is no longer in use. It is + * forbidden for multible database objects in a process to open the same database at the same + * time. It is forbidden to share a database object with child processes. + */ +class DirDB : public BasicDB { + friend class PlantDB<DirDB, BasicDB::TYPEFOREST>; + public: + class Cursor; + private: + struct Record; + class ScopedVisitor; + /** An alias of list of cursors. */ + typedef std::list<Cursor*> CursorList; + /** An alias of vector of strings. */ + typedef std::vector<std::string> StringVector; + /** The size of the meta data buffer. */ + static const int64_t METABUFSIZ = 128; + /** The magic data for record. */ + static const uint8_t RECMAGIC = 0xcc; + /** The number of slots of the record lock. */ + static const int32_t RLOCKSLOT = 2048; + /** The unit size of a record. */ + static const int32_t RECUNITSIZ = 32; + /** The size of the opaque buffer. */ + static const size_t OPAQUESIZ = 16; + /** The threshold of busy loop and sleep for locking. */ + static const uint32_t LOCKBUSYLOOP = 8192; + public: + /** + * Cursor to indicate a record. + */ + class Cursor : public BasicDB::Cursor { + friend class DirDB; + public: + /** + * Constructor. + * @param db the container database object. + */ + explicit Cursor(DirDB* db) : db_(db), dir_(), alive_(false), name_("") { + _assert_(db); + ScopedRWLock lock(&db_->mlock_, true); + db_->curs_.push_back(this); + } + /** + * Destructor. + */ + virtual ~Cursor() { + _assert_(true); + if (!db_) return; + ScopedRWLock lock(&db_->mlock_, true); + db_->curs_.remove(this); + } + /** + * Accept a visitor to the current record. + * @param visitor a visitor object. + * @param writable true for writable operation, or false for read-only operation. + * @param step true to move the cursor to the next record, or false for no move. + * @return true on success, or false on failure. + * @note The operation for each record is performed atomically and other threads accessing + * the same record are blocked. To avoid deadlock, any explicit database operation must not + * be performed in this function. + */ + bool accept(Visitor* visitor, bool writable = true, bool step = false) { + _assert_(visitor); + ScopedRWLock lock(&db_->mlock_, true); + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (writable && !(db_->writer_)) { + db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + return false; + } + if (!alive_) { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + return false; + } + bool err = false; + const std::string& rpath = db_->path_ + File::PATHCHR + name_; + int64_t cnt = db_->count_; + Record rec; + if (db_->read_record(rpath, &rec)) { + if (!db_->accept_visit_full(rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz, rec.rsiz, + visitor, rpath, name_.c_str())) err = true; + delete[] rec.rbuf; + if (alive_ && step && db_->count_ == cnt) { + do { + if (!dir_.read(&name_)) { + if (!disable()) err = true; + break; + } + } while (*name_.c_str() == *KCDDBMAGICFILE); + } + } else { + while (true) { + if (!dir_.read(&name_)) { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + disable(); + break; + } + if (*name_.c_str() == *KCDDBMAGICFILE) continue; + const std::string& npath = db_->path_ + File::PATHCHR + name_; + if (!File::status(npath)) continue; + if (db_->read_record(npath, &rec)) { + if (!db_->accept_visit_full(rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz, rec.rsiz, + visitor, npath, name_.c_str())) err = true; + delete[] rec.rbuf; + if (alive_ && step && db_->count_ == cnt) { + do { + if (!dir_.read(&name_)) { + if (!disable()) err = true; + break; + } + } while (*name_.c_str() == *KCDDBMAGICFILE); + } + } else { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + err = true; + } + break; + } + } + return !err; + } + /** + * Jump the cursor to the first record for forward scan. + * @return true on success, or false on failure. + */ + bool jump() { + _assert_(true); + ScopedRWLock lock(&db_->mlock_, true); + if (alive_ && !disable()) return false; + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (!dir_.open(db_->path_)) { + db_->set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + return false; + } + alive_ = true; + do { + if (!dir_.read(&name_)) { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + disable(); + return false; + } + } while (*name_.c_str() == *KCDDBMAGICFILE); + return true; + } + /** + * Jump the cursor to a record for forward scan. + * @param kbuf the pointer to the key region. + * @param ksiz the size of the key region. + * @return true on success, or false on failure. + */ + bool jump(const char* kbuf, size_t ksiz) { + _assert_(kbuf && ksiz <= MEMMAXSIZ); + ScopedRWLock lock(&db_->mlock_, true); + if (alive_ && !disable()) return false; + if (!dir_.open(db_->path_)) { + db_->set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + return false; + } + alive_ = true; + while (true) { + if (!dir_.read(&name_)) { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + disable(); + return false; + } + if (*name_.c_str() == *KCDDBMAGICFILE) continue; + const std::string& rpath = db_->path_ + File::PATHCHR + name_; + Record rec; + if (db_->read_record(rpath, &rec)) { + if (rec.ksiz == ksiz && !std::memcmp(rec.kbuf, kbuf, ksiz)) { + delete[] rec.rbuf; + break; + } + delete[] rec.rbuf; + } else { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + disable(); + return false; + } + } + return true; + } + /** + * Jump the cursor to a record for forward scan. + * @note Equal to the original Cursor::jump method except that the parameter is std::string. + */ + bool jump(const std::string& key) { + _assert_(true); + return jump(key.c_str(), key.size()); + } + /** + * Jump the cursor to the last record for backward scan. + * @note This is a dummy implementation for compatibility. + */ + bool jump_back() { + _assert_(true); + ScopedRWLock lock(&db_->mlock_, true); + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); + return false; + } + /** + * Jump the cursor to a record for backward scan. + * @note This is a dummy implementation for compatibility. + */ + bool jump_back(const char* kbuf, size_t ksiz) { + _assert_(kbuf && ksiz <= MEMMAXSIZ); + ScopedRWLock lock(&db_->mlock_, true); + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); + return false; + } + /** + * Jump the cursor to a record for backward scan. + * @note This is a dummy implementation for compatibility. + */ + bool jump_back(const std::string& key) { + _assert_(true); + ScopedRWLock lock(&db_->mlock_, true); + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); + return false; + } + /** + * Step the cursor to the next record. + * @return true on success, or false on failure. + */ + bool step() { + _assert_(true); + ScopedRWLock lock(&db_->mlock_, true); + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (!alive_) { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + return false; + } + do { + if (!dir_.read(&name_)) { + db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); + disable(); + return false; + } + } while (*name_.c_str() == *KCDDBMAGICFILE); + return true; + } + /** + * Step the cursor to the previous record. + * @note This is a dummy implementation for compatibility. + */ + bool step_back() { + _assert_(true); + ScopedRWLock lock(&db_->mlock_, true); + if (db_->omode_ == 0) { + db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); + return false; + } + /** + * Get the database object. + * @return the database object. + */ + DirDB* db() { + _assert_(true); + return db_; + } + private: + /** + * Disable the cursor. + * @return true on success, or false on failure. + */ + bool disable() { + bool err = false; + if (!dir_.close()) { + db_->set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + alive_ = false; + return !err; + } + /** Dummy constructor to forbid the use. */ + Cursor(const Cursor&); + /** Dummy Operator to forbid the use. */ + Cursor& operator =(const Cursor&); + /** The inner database. */ + DirDB* db_; + /** The inner directory stream. */ + DirStream dir_; + /** The flag if alive. */ + bool alive_; + /** The current name. */ + std::string name_; + }; + /** + * Tuning options. + */ + enum Option { + TSMALL = 1 << 0, ///< dummy for compatibility + TLINEAR = 1 << 1, ///< dummy for compatibility + TCOMPRESS = 1 << 2 ///< compress each record + }; + /** + * Status flags. + */ + enum Flag { + FOPEN = 1 << 0, ///< dummy for compatibility + FFATAL = 1 << 1 ///< dummy for compatibility + }; + /** + * Default constructor. + */ + explicit DirDB() : + mlock_(), rlock_(RLOCKSLOT), error_(), + logger_(NULL), logkinds_(0), mtrigger_(NULL), + omode_(0), writer_(false), autotran_(false), autosync_(false), + recov_(false), reorg_(false), + file_(), curs_(), path_(""), + libver_(LIBVER), librev_(LIBREV), fmtver_(FMTVER), chksum_(0), type_(TYPEDIR), + flags_(0), opts_(0), count_(0), size_(0), opaque_(), embcomp_(ZLIBRAWCOMP), comp_(NULL), + tran_(false), trhard_(false), trcount_(0), trsize_(0), walpath_(""), tmppath_("") { + _assert_(true); + } + /** + * Destructor. + * @note If the database is not closed, it is closed implicitly. + */ + virtual ~DirDB() { + _assert_(true); + if (omode_ != 0) close(); + if (!curs_.empty()) { + CursorList::const_iterator cit = curs_.begin(); + CursorList::const_iterator citend = curs_.end(); + while (cit != citend) { + Cursor* cur = *cit; + cur->db_ = NULL; + ++cit; + } + } + } + /** + * Accept a visitor to a record. + * @param kbuf the pointer to the key region. + * @param ksiz the size of the key region. + * @param visitor a visitor object. + * @param writable true for writable operation, or false for read-only operation. + * @return true on success, or false on failure. + * @note The operation for each record is performed atomically and other threads accessing the + * same record are blocked. To avoid deadlock, any explicit database operation must not be + * performed in this function. + */ + bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writable = true) { + _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (writable && !writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + return false; + } + bool err = false; + char name[NUMBUFSIZ]; + size_t lidx = hashpath(kbuf, ksiz, name) % RLOCKSLOT; + if (writable) { + rlock_.lock_writer(lidx); + } else { + rlock_.lock_reader(lidx); + } + if (!accept_impl(kbuf, ksiz, visitor, name)) err = true; + rlock_.unlock(lidx); + return !err; + } + /** + * Accept a visitor to multiple records at once. + * @param keys specifies a string vector of the keys. + * @param visitor a visitor object. + * @param writable true for writable operation, or false for read-only operation. + * @return true on success, or false on failure. + * @note The operations for specified records are performed atomically and other threads + * accessing the same records are blocked. To avoid deadlock, any explicit database operation + * must not be performed in this function. + */ + bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, + bool writable = true) { + _assert_(visitor); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (writable && !writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + return false; + } + ScopedVisitor svis(visitor); + size_t knum = keys.size(); + if (knum < 1) return true; + bool err = false; + struct RecordKey { + const char* kbuf; + size_t ksiz; + char name[NUMBUFSIZ]; + }; + RecordKey* rkeys = new RecordKey[knum]; + std::set<size_t> lidxs; + for (size_t i = 0; i < knum; i++) { + const std::string& key = keys[i]; + RecordKey* rkey = rkeys + i; + rkey->kbuf = key.data(); + rkey->ksiz = key.size(); + lidxs.insert(hashpath(rkey->kbuf, rkey->ksiz, rkey->name) % RLOCKSLOT); + } + std::set<size_t>::iterator lit = lidxs.begin(); + std::set<size_t>::iterator litend = lidxs.end(); + while (lit != litend) { + if (writable) { + rlock_.lock_writer(*lit); + } else { + rlock_.lock_reader(*lit); + } + ++lit; + } + for (size_t i = 0; i < knum; i++) { + RecordKey* rkey = rkeys + i; + if (!accept_impl(rkey->kbuf, rkey->ksiz, visitor, rkey->name)) { + err = true; + break; + } + } + lit = lidxs.begin(); + litend = lidxs.end(); + while (lit != litend) { + rlock_.unlock(*lit); + ++lit; + } + delete[] rkeys; + return !err; + } + /** + * Iterate to accept a visitor for each record. + * @param visitor a visitor object. + * @param writable true for writable operation, or false for read-only operation. + * @param checker a progress checker object. If it is NULL, no checking is performed. + * @return true on success, or false on failure. + * @note The whole iteration is performed atomically and other threads are blocked. To avoid + * deadlock, any explicit database operation must not be performed in this function. + */ + bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* checker = NULL) { + _assert_(visitor); + ScopedRWLock lock(&mlock_, true); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (writable && !writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + return false; + } + ScopedVisitor svis(visitor); + bool err = false; + if (!iterate_impl(visitor, checker)) err = true; + trigger_meta(MetaTrigger::ITERATE, "iterate"); + return !err; + } + /** + * Scan each record in parallel. + * @param visitor a visitor object. + * @param thnum the number of worker threads. + * @param checker a progress checker object. If it is NULL, no checking is performed. + * @return true on success, or false on failure. + * @note This function is for reading records and not for updating ones. The return value of + * the visitor is just ignored. To avoid deadlock, any explicit database operation must not + * be performed in this function. + */ + bool scan_parallel(Visitor *visitor, size_t thnum, ProgressChecker* checker = NULL) { + _assert_(visitor && thnum <= MEMMAXSIZ); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (thnum < 1) thnum = 0; + if (thnum > (size_t)INT8MAX) thnum = INT8MAX; + ScopedVisitor svis(visitor); + rlock_.lock_reader_all(); + bool err = false; + if (!scan_parallel_impl(visitor, thnum, checker)) err = true; + rlock_.unlock_all(); + trigger_meta(MetaTrigger::ITERATE, "scan_parallel"); + return !err; + } + /** + * Get the last happened error. + * @return the last happened error. + */ + Error error() const { + _assert_(true); + return error_; + } + /** + * Set the error information. + * @param file the file name of the program source code. + * @param line the line number of the program source code. + * @param func the function name of the program source code. + * @param code an error code. + * @param message a supplement message. + */ + void set_error(const char* file, int32_t line, const char* func, + Error::Code code, const char* message) { + _assert_(file && line > 0 && func && message); + error_->set(code, message); + if (code == Error::BROKEN || code == Error::SYSTEM) flags_ |= FFATAL; + if (logger_) { + Logger::Kind kind = code == Error::BROKEN || code == Error::SYSTEM ? + Logger::ERROR : Logger::INFO; + if (kind & logkinds_) + report(file, line, func, kind, "%d: %s: %s", code, Error::codename(code), message); + } + } + /** + * Open a database file. + * @param path the path of a database file. + * @param mode the connection mode. DirDB::OWRITER as a writer, DirDB::OREADER as a + * reader. The following may be added to the writer mode by bitwise-or: DirDB::OCREATE, + * which means it creates a new database if the file does not exist, DirDB::OTRUNCATE, which + * means it creates a new database regardless if the file exists, DirDB::OAUTOTRAN, which + * means each updating operation is performed in implicit transaction, DirDB::OAUTOSYNC, + * which means each updating operation is followed by implicit synchronization with the file + * system. The following may be added to both of the reader mode and the writer mode by + * bitwise-or: DirDB::ONOLOCK, which means it opens the database file without file locking, + * DirDB::OTRYLOCK, which means locking is performed without blocking, DirDB::ONOREPAIR, + * which means the database file is not repaired implicitly even if file destruction is + * detected. + * @return true on success, or false on failure. + * @note Every opened database must be closed by the DirDB::close method when it is no + * longer in use. It is not allowed for two or more database objects in the same process to + * keep their connections to the same database file at the same time. + */ + bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ != 0) { + set_error(_KCCODELINE_, Error::INVALID, "already opened"); + return false; + } + report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", path.c_str()); + writer_ = false; + autotran_ = false; + autosync_ = false; + recov_ = false; + reorg_ = false; + uint32_t fmode = File::OREADER; + if (mode & OWRITER) { + writer_ = true; + fmode = File::OWRITER; + if (mode & OCREATE) fmode |= File::OCREATE; + if (mode & OTRUNCATE) fmode |= File::OTRUNCATE; + if (mode & OAUTOTRAN) autotran_ = true; + if (mode & OAUTOSYNC) autosync_ = true; + } + if (mode & ONOLOCK) fmode |= File::ONOLOCK; + if (mode & OTRYLOCK) fmode |= File::OTRYLOCK; + size_t psiz = path.size(); + while (psiz > 0 && path[psiz-1] == File::PATHCHR) { + psiz--; + } + const std::string& cpath = path.substr(0, psiz); + const std::string& magicpath = cpath + File::PATHCHR + KCDDBMAGICFILE; + const std::string& metapath = cpath + File::PATHCHR + KCDDBMETAFILE; + const std::string& opqpath = cpath + File::PATHCHR + KCDDBOPAQUEFILE; + const std::string& walpath = cpath + File::EXTCHR + KCDDBWALPATHEXT; + const std::string& tmppath = cpath + File::EXTCHR + KCDDBTMPPATHEXT; + bool hot = false; + if (writer_ && (mode & OTRUNCATE) && File::status(magicpath)) { + if (!file_.open(magicpath, fmode)) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + return false; + } + if (!remove_files(cpath)) { + file_.close(); + return false; + } + if (File::status(walpath)) { + remove_files(walpath); + File::remove_directory(walpath); + } + if (!file_.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + return false; + } + const std::string& buf = format_magic(0, 0); + if (!File::write_file(magicpath, buf.c_str(), buf.size())) { + set_error(_KCCODELINE_, Error::SYSTEM, "writing a file failed"); + return false; + } + if (File::status(metapath) && !File::remove(metapath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + return false; + } + if (File::status(opqpath) && !File::remove(opqpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + return false; + } + hot = true; + } + File::Status sbuf; + if (File::status(cpath, &sbuf)) { + if (!sbuf.isdir) { + set_error(_KCCODELINE_, Error::NOPERM, "invalid path (not directory)"); + return false; + } + if (!File::status(magicpath)) { + set_error(_KCCODELINE_, Error::BROKEN, "invalid magic data"); + return false; + } + if (!file_.open(magicpath, fmode)) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + return false; + } + } else if (writer_ && (mode & OCREATE)) { + hot = true; + if (!File::make_directory(cpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "making a directory failed"); + return false; + } + if (!file_.open(magicpath, fmode)) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + return false; + } + } else { + set_error(_KCCODELINE_, Error::NOREPOS, "open failed (file not found)"); + return false; + } + if (hot) { + count_ = 0; + size_ = 0; + comp_ = (opts_ & TCOMPRESS) ? embcomp_ : NULL; + libver_ = LIBVER; + librev_ = LIBREV; + fmtver_ = FMTVER; + chksum_ = calc_checksum(); + if (!dump_meta(metapath)) { + file_.close(); + return false; + } + std::memset(opaque_, 0, sizeof(opaque_)); + if (autosync_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + file_.close(); + return false; + } + } else { + if (File::status(walpath, &sbuf)) { + if (writer_) { + file_.truncate(0); + } else { + File::write_file(magicpath, "", 0); + file_.refresh(); + } + DirStream dir; + if (dir.open(walpath)) { + std::string name; + while (dir.read(&name)) { + const std::string& srcpath = walpath + File::PATHCHR + name; + const std::string& destpath = cpath + File::PATHCHR + name; + File::Status sbuf; + if (File::status(srcpath, &sbuf)) { + if (sbuf.size > 1) { + File::rename(srcpath, destpath); + } else { + if (File::remove(destpath) || !File::status(destpath)) File::remove(srcpath); + } + } + } + dir.close(); + File::remove_directory(walpath); + recov_ = true; + report(_KCCODELINE_, Logger::WARN, "recovered by the WAL directory"); + } + } + if (!load_meta(metapath)) { + file_.close(); + return false; + } + comp_ = (opts_ & TCOMPRESS) ? embcomp_ : NULL; + uint8_t chksum = calc_checksum(); + if (chksum != chksum_) { + set_error(_KCCODELINE_, Error::INVALID, "invalid module checksum"); + report(_KCCODELINE_, Logger::WARN, "saved=%02X calculated=%02X", + (unsigned)chksum_, (unsigned)chksum); + file_.close(); + return false; + } + if (!load_magic()) { + if (!calc_magic(cpath)) { + file_.close(); + return false; + } + reorg_ = true; + if (!writer_ && !(mode & ONOLOCK)) { + const std::string& buf = format_magic(count_, size_); + if (!File::write_file(magicpath, buf.c_str(), buf.size())) { + set_error(_KCCODELINE_, Error::SYSTEM, "writing a file failed"); + file_.close(); + return false; + } + if (!file_.refresh()) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + file_.close(); + return false; + } + } + report(_KCCODELINE_, Logger::WARN, "re-calculated magic data"); + } + } + if (writer_ && !file_.truncate(0)) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + file_.close(); + return false; + } + if (File::status(walpath)) { + remove_files(walpath); + File::remove_directory(walpath); + } + if (File::status(tmppath)) { + remove_files(tmppath); + File::remove_directory(tmppath); + } + omode_ = mode; + path_ = cpath; + tran_ = false; + walpath_ = walpath; + tmppath_ = tmppath; + load_opaque(); + trigger_meta(MetaTrigger::OPEN, "open"); + return true; + } + /** + * Close the database file. + * @return true on success, or false on failure. + */ + bool close() { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", path_.c_str()); + bool err = false; + if (tran_ && !abort_transaction()) err = true; + if (!disable_cursors()) err = true; + if (writer_) { + if (!dump_magic()) err = true; + if (!dump_opaque()) err = true; + } + if (!file_.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + err = true; + } + omode_ = 0; + trigger_meta(MetaTrigger::CLOSE, "close"); + return !err; + } + /** + * Synchronize updated contents with the file and the device. + * @param hard true for physical synchronization with the device, or false for logical + * synchronization with the file system. + * @param proc a postprocessor object. If it is NULL, no postprocessing is performed. + * @param checker a progress checker object. If it is NULL, no checking is performed. + * @return true on success, or false on failure. + * @note The operation of the postprocessor is performed atomically and other threads accessing + * the same record are blocked. To avoid deadlock, any explicit database operation must not + * be performed in this function. + */ + bool synchronize(bool hard = false, FileProcessor* proc = NULL, + ProgressChecker* checker = NULL) { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + rlock_.lock_reader_all(); + bool err = false; + if (!synchronize_impl(hard, proc, checker)) err = true; + trigger_meta(MetaTrigger::SYNCHRONIZE, "synchronize"); + rlock_.unlock_all(); + return !err; + } + /** + * Occupy database by locking and do something meanwhile. + * @param writable true to use writer lock, or false to use reader lock. + * @param proc a processor object. If it is NULL, no processing is performed. + * @return true on success, or false on failure. + * @note The operation of the processor is performed atomically and other threads accessing + * the same record are blocked. To avoid deadlock, any explicit database operation must not + * be performed in this function. + */ + bool occupy(bool writable = true, FileProcessor* proc = NULL) { + _assert_(true); + ScopedRWLock lock(&mlock_, writable); + bool err = false; + if (proc && !proc->process(path_, count_, size_impl())) { + set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); + err = true; + } + trigger_meta(MetaTrigger::OCCUPY, "occupy"); + return !err; + } + /** + * Begin transaction. + * @param hard true for physical synchronization with the device, or false for logical + * synchronization with the file system. + * @return true on success, or false on failure. + */ + bool begin_transaction(bool hard = false) { + _assert_(true); + uint32_t wcnt = 0; + while (true) { + mlock_.lock_writer(); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + mlock_.unlock(); + return false; + } + if (!writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + mlock_.unlock(); + return false; + } + if (!tran_) break; + mlock_.unlock(); + if (wcnt >= LOCKBUSYLOOP) { + Thread::chill(); + } else { + Thread::yield(); + wcnt++; + } + } + trhard_ = hard; + if (!begin_transaction_impl()) { + mlock_.unlock(); + return false; + } + tran_ = true; + trigger_meta(MetaTrigger::BEGINTRAN, "begin_transaction"); + mlock_.unlock(); + return true; + } + /** + * Try to begin transaction. + * @param hard true for physical synchronization with the device, or false for logical + * synchronization with the file system. + * @return true on success, or false on failure. + */ + bool begin_transaction_try(bool hard = false) { + _assert_(true); + mlock_.lock_writer(); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + mlock_.unlock(); + return false; + } + if (!writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + mlock_.unlock(); + return false; + } + if (tran_) { + set_error(_KCCODELINE_, Error::LOGIC, "competition avoided"); + mlock_.unlock(); + return false; + } + trhard_ = hard; + if (!begin_transaction_impl()) { + mlock_.unlock(); + return false; + } + tran_ = true; + trigger_meta(MetaTrigger::BEGINTRAN, "begin_transaction_try"); + mlock_.unlock(); + return true; + } + /** + * End transaction. + * @param commit true to commit the transaction, or false to abort the transaction. + * @return true on success, or false on failure. + */ + bool end_transaction(bool commit = true) { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (!tran_) { + set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); + return false; + } + bool err = false; + if (commit) { + if (!commit_transaction()) err = true; + } else { + if (!abort_transaction()) err = true; + } + tran_ = false; + trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); + return !err; + } + /** + * Remove all records. + * @return true on success, or false on failure. + */ + bool clear() { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (!writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + return false; + } + bool err = false; + if (!disable_cursors()) err = true; + if (tran_) { + DirStream dir; + if (dir.open(path_)) { + std::string name; + while (dir.read(&name)) { + if (*name.c_str() == *KCDDBMAGICFILE) continue; + const std::string& rpath = path_ + File::PATHCHR + name; + const std::string& walpath = walpath_ + File::PATHCHR + name; + if (File::status(walpath)) { + if (!File::remove(rpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + err = true; + } + } else if (!File::rename(rpath, walpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a file failed"); + err = true; + } + } + if (!dir.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + } else { + set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + err = true; + } + } else { + if (!remove_files(path_)) err = true; + } + recov_ = false; + reorg_ = false; + flags_ = 0; + std::memset(opaque_, 0, sizeof(opaque_)); + count_ = 0; + size_ = 0; + trigger_meta(MetaTrigger::CLEAR, "clear"); + return !err; + } + /** + * Get the number of records. + * @return the number of records, or -1 on failure. + */ + int64_t count() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return -1; + } + return count_; + } + /** + * Get the size of the database file. + * @return the size of the database file in bytes, or -1 on failure. + */ + int64_t size() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return -1; + } + return size_impl(); + } + /** + * Get the path of the database file. + * @return the path of the database file, or an empty string on failure. + */ + std::string path() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return ""; + } + return path_; + } + /** + * Get the miscellaneous status information. + * @param strmap a string map to contain the result. + * @return true on success, or false on failure. + */ + bool status(std::map<std::string, std::string>* strmap) { + _assert_(strmap); + ScopedRWLock lock(&mlock_, true); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + (*strmap)["type"] = strprintf("%u", (unsigned)TYPEDIR); + (*strmap)["realtype"] = strprintf("%u", (unsigned)type_); + (*strmap)["path"] = path_; + (*strmap)["libver"] = strprintf("%u", libver_); + (*strmap)["librev"] = strprintf("%u", librev_); + (*strmap)["fmtver"] = strprintf("%u", fmtver_); + (*strmap)["chksum"] = strprintf("%u", chksum_); + (*strmap)["flags"] = strprintf("%u", flags_); + (*strmap)["opts"] = strprintf("%u", opts_); + (*strmap)["recovered"] = strprintf("%d", recov_); + (*strmap)["reorganized"] = strprintf("%d", reorg_); + if (strmap->count("opaque") > 0) + (*strmap)["opaque"] = std::string(opaque_, sizeof(opaque_)); + (*strmap)["count"] = strprintf("%lld", (long long)count_); + (*strmap)["size"] = strprintf("%lld", (long long)size_impl()); + return true; + } + /** + * Create a cursor object. + * @return the return value is the created cursor object. + * @note Because the object of the return value is allocated by the constructor, it should be + * released with the delete operator when it is no longer in use. + */ + Cursor* cursor() { + _assert_(true); + return new Cursor(this); + } + /** + * Write a log message. + * @param file the file name of the program source code. + * @param line the line number of the program source code. + * @param func the function name of the program source code. + * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal + * information, Logger::WARN for warning, and Logger::ERROR for fatal error. + * @param message the supplement message. + */ + void log(const char* file, int32_t line, const char* func, Logger::Kind kind, + const char* message) { + _assert_(file && line > 0 && func && message); + ScopedRWLock lock(&mlock_, false); + if (!logger_) return; + logger_->log(file, line, func, kind, message); + } + /** + * Set the internal logger. + * @param logger the logger object. + * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, + * Logger::INFO for normal information, Logger::WARN for warning, and Logger::ERROR for fatal + * error. + * @return true on success, or false on failure. + */ + bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger::ERROR) { + _assert_(logger); + ScopedRWLock lock(&mlock_, true); + if (omode_ != 0) { + set_error(_KCCODELINE_, Error::INVALID, "already opened"); + return false; + } + logger_ = logger; + logkinds_ = kinds; + return true; + } + /** + * Set the internal meta operation trigger. + * @param trigger the trigger object. + * @return true on success, or false on failure. + */ + bool tune_meta_trigger(MetaTrigger* trigger) { + _assert_(trigger); + ScopedRWLock lock(&mlock_, true); + if (omode_ != 0) { + set_error(_KCCODELINE_, Error::INVALID, "already opened"); + return false; + } + mtrigger_ = trigger; + return true; + } + /** + * Set the optional features. + * @param opts the optional features by bitwise-or: DirDB::TCOMPRESS to compress each record. + * @return true on success, or false on failure. + */ + bool tune_options(int8_t opts) { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ != 0) { + set_error(_KCCODELINE_, Error::INVALID, "already opened"); + return false; + } + opts_ = opts; + return true; + } + /** + * Set the data compressor. + * @param comp the data compressor object. + * @return true on success, or false on failure. + */ + bool tune_compressor(Compressor* comp) { + _assert_(comp); + ScopedRWLock lock(&mlock_, true); + if (omode_ != 0) { + set_error(_KCCODELINE_, Error::INVALID, "already opened"); + return false; + } + embcomp_ = comp; + return true; + } + /** + * Get the opaque data. + * @return the pointer to the opaque data region, whose size is 16 bytes. + */ + char* opaque() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return NULL; + } + return opaque_; + } + /** + * Synchronize the opaque data. + * @return true on success, or false on failure. + */ + bool synchronize_opaque() { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + if (!writer_) { + set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); + return false; + } + bool err = false; + if (!dump_opaque()) err = true; + return !err; + } + /** + * Get the status flags. + * @note This is a dummy implementation for compatibility. + */ + uint8_t flags() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return 0; + } + protected: + /** + * Report a message for debugging. + * @param file the file name of the program source code. + * @param line the line number of the program source code. + * @param func the function name of the program source code. + * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal + * information, Logger::WARN for warning, and Logger::ERROR for fatal error. + * @param format the printf-like format string. + * @param ... used according to the format string. + */ + void report(const char* file, int32_t line, const char* func, Logger::Kind kind, + const char* format, ...) { + _assert_(file && line > 0 && func && format); + if (!logger_ || !(kind & logkinds_)) return; + std::string message; + strprintf(&message, "%s: ", path_.empty() ? "-" : path_.c_str()); + va_list ap; + va_start(ap, format); + vstrprintf(&message, format, ap); + va_end(ap); + logger_->log(file, line, func, kind, message.c_str()); + } + /** + * Report a message for debugging with variable number of arguments. + * @param file the file name of the program source code. + * @param line the line number of the program source code. + * @param func the function name of the program source code. + * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal + * information, Logger::WARN for warning, and Logger::ERROR for fatal error. + * @param format the printf-like format string. + * @param ap used according to the format string. + */ + void report_valist(const char* file, int32_t line, const char* func, Logger::Kind kind, + const char* format, va_list ap) { + _assert_(file && line > 0 && func && format); + if (!logger_ || !(kind & logkinds_)) return; + std::string message; + strprintf(&message, "%s: ", path_.empty() ? "-" : path_.c_str()); + vstrprintf(&message, format, ap); + logger_->log(file, line, func, kind, message.c_str()); + } + /** + * Report the content of a binary buffer for debugging. + * @param file the file name of the epicenter. + * @param line the line number of the epicenter. + * @param func the function name of the program source code. + * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal + * information, Logger::WARN for warning, and Logger::ERROR for fatal error. + * @param name the name of the information. + * @param buf the binary buffer. + * @param size the size of the binary buffer + */ + void report_binary(const char* file, int32_t line, const char* func, Logger::Kind kind, + const char* name, const char* buf, size_t size) { + _assert_(file && line > 0 && func && name && buf && size <= MEMMAXSIZ); + if (!logger_) return; + char* hex = hexencode(buf, size); + report(file, line, func, kind, "%s=%s", name, hex); + delete[] hex; + } + /** + * Trigger a meta database operation. + * @param kind the kind of the event. MetaTrigger::OPEN for opening, MetaTrigger::CLOSE for + * closing, MetaTrigger::CLEAR for clearing, MetaTrigger::ITERATE for iteration, + * MetaTrigger::SYNCHRONIZE for synchronization, MetaTrigger::BEGINTRAN for beginning + * transaction, MetaTrigger::COMMITTRAN for committing transaction, MetaTrigger::ABORTTRAN + * for aborting transaction, and MetaTrigger::MISC for miscellaneous operations. + * @param message the supplement message. + */ + void trigger_meta(MetaTrigger::Kind kind, const char* message) { + _assert_(message); + if (mtrigger_) mtrigger_->trigger(kind, message); + } + /** + * Set the database type. + * @param type the database type. + * @return true on success, or false on failure. + */ + bool tune_type(int8_t type) { + _assert_(true); + ScopedRWLock lock(&mlock_, true); + if (omode_ != 0) { + set_error(_KCCODELINE_, Error::INVALID, "already opened"); + return false; + } + type_ = type; + return true; + } + /** + * Get the library version. + * @return the library version, or 0 on failure. + */ + uint8_t libver() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return libver_; + } + /** + * Get the library revision. + * @return the library revision, or 0 on failure. + */ + uint8_t librev() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return librev_; + } + /** + * Get the format version. + * @return the format version, or 0 on failure. + */ + uint8_t fmtver() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return fmtver_; + } + /** + * Get the module checksum. + * @return the module checksum, or 0 on failure. + */ + uint8_t chksum() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return chksum_; + } + /** + * Get the database type. + * @return the database type, or 0 on failure. + */ + uint8_t type() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return type_; + } + /** + * Get the options. + * @return the options, or 0 on failure. + */ + uint8_t opts() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return 0; + } + return opts_; + } + /** + * Get the data compressor. + * @return the data compressor, or NULL on failure. + */ + Compressor* comp() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return NULL; + } + return comp_; + } + /** + * Check whether the database was recovered or not. + * @return true if recovered, or false if not. + */ + bool recovered() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + return recov_; + } + /** + * Check whether the database was reorganized or not. + * @return true if reorganized, or false if not. + */ + bool reorganized() { + _assert_(true); + ScopedRWLock lock(&mlock_, false); + if (omode_ == 0) { + set_error(_KCCODELINE_, Error::INVALID, "not opened"); + return false; + } + return reorg_; + } + private: + /** + * Set the power of the alignment of record size. + * @note This is a dummy implementation for compatibility. + */ + bool tune_alignment(int8_t apow) { + return true; + } + /** + * Set the power of the capacity of the free block pool. + * @note This is a dummy implementation for compatibility. + */ + bool tune_fbp(int8_t fpow) { + return true; + } + /** + * Set the number of buckets of the hash table. + * @note This is a dummy implementation for compatibility. + */ + bool tune_buckets(int64_t bnum) { + return true; + } + /** + * Set the size of the internal memory-mapped region. + * @note This is a dummy implementation for compatibility. + */ + bool tune_map(int64_t msiz) { + return true; + } + /** + * Set the unit step number of auto defragmentation. + * @note This is a dummy implementation for compatibility. + */ + bool tune_defrag(int64_t dfunit) { + return true; + } + /** + * Perform defragmentation of the file. + * @note This is a dummy implementation for compatibility. + */ + bool defrag(int64_t step = 0) { + return true; + } + /** + * Get the alignment power. + * @note This is a dummy implementation for compatibility. + */ + uint8_t apow() { + return 0; + } + /** + * Get the free block pool power. + * @note This is a dummy implementation for compatibility. + */ + uint8_t fpow() { + return 0; + } + /** + * Get the bucket number. + * @note This is a dummy implementation for compatibility. + */ + int64_t bnum() { + return 1; + } + /** + * Get the size of the internal memory-mapped region. + * @note This is a dummy implementation for compatibility. + */ + int64_t msiz() { + return 0; + } + /** + * Get the unit step number of auto defragmentation. + * @note This is a dummy implementation for compatibility. + */ + int64_t dfunit() { + return 0; + } + private: + /** + * Record data. + */ + struct Record { + char* rbuf; ///< record buffer + size_t rsiz; ///< record size + const char* kbuf; ///< key buffer + size_t ksiz; ///< key size + const char* vbuf; ///< value buffer + size_t vsiz; ///< value size + }; + /** + * Scoped visitor. + */ + class ScopedVisitor { + public: + /** constructor */ + explicit ScopedVisitor(Visitor* visitor) : visitor_(visitor) { + _assert_(visitor); + visitor_->visit_before(); + } + /** destructor */ + ~ScopedVisitor() { + _assert_(true); + visitor_->visit_after(); + } + private: + Visitor* visitor_; ///< visitor + }; + /** + * Dump the magic data into the file. + * @return true on success, or false on failure. + */ + bool dump_magic() { + _assert_(true); + const std::string& buf = format_magic(count_, size_); + if (!file_.write(0, buf.c_str(), buf.size())) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + return false; + } + return true; + } + /** + * Format the magic data. + * @return the result string. + */ + std::string format_magic(int64_t count, int64_t size) { + return strprintf("%lld\n%lld\n%s\n", (long long)count, (long long)size, KCDDBMAGICEOF); + } + /** + * Load the magic data from the file. + * @return true on success, or false on failure. + */ + bool load_magic() { + _assert_(true); + char buf[NUMBUFSIZ*3]; + size_t len = file_.size(); + if (len > sizeof(buf) - 1) len = sizeof(buf) - 1; + if (!file_.read(0, buf, len)) return false; + buf[len] = '\0'; + char* rp = buf; + int64_t count = atoi(rp); + char* pv = std::strchr(rp, '\n'); + if (!pv) return false; + rp = pv + 1; + int64_t size = atoi(rp); + pv = std::strchr(rp, '\n'); + if (!pv) return false; + rp = pv + 1; + if (std::strlen(rp) < sizeof(KCDDBMAGICEOF) - 1 || + std::memcmp(rp, KCDDBMAGICEOF, sizeof(KCDDBMAGICEOF) - 1)) return false; + flags_ = 0; + count_ = count; + size_ = size; + return true; + } + /** + * Calculate magic data. + * @param cpath the path of the database file. + * @return true on success, or false on failure. + */ + bool calc_magic(const std::string& cpath) { + _assert_(true); + count_ = 0; + size_ = 0; + DirStream dir; + if (!dir.open(cpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + return false; + } + bool err = false; + std::string name; + while (dir.read(&name)) { + if (*name.c_str() == *KCDDBMAGICFILE) continue; + const std::string& rpath = cpath + File::PATHCHR + name; + File::Status sbuf; + if (File::status(rpath, &sbuf)) { + if (sbuf.size >= 4) { + count_ += 1; + size_ += sbuf.size - 4; + } else { + File::remove(rpath); + } + } else { + set_error(_KCCODELINE_, Error::SYSTEM, "checking the status of a file failed"); + err = true; + } + } + if (!dir.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + return !err; + } + /** + * Calculate the module checksum. + * @return the module checksum. + */ + uint8_t calc_checksum() { + _assert_(true); + const char* kbuf = KCDDBCHKSUMSEED; + size_t ksiz = sizeof(KCDDBCHKSUMSEED) - 1; + char* zbuf = NULL; + size_t zsiz = 0; + if (comp_) { + zbuf = comp_->compress(kbuf, ksiz, &zsiz); + if (!zbuf) return 0; + kbuf = zbuf; + ksiz = zsiz; + } + char name[NUMBUFSIZ]; + uint32_t hash = hashpath(kbuf, ksiz, name); + hash += hashmurmur(name, std::strlen(name)); + delete[] zbuf; + return hash; + } + /** + * Dump the meta data into the file. + * @param metapath the path of the meta data file. + * @return true on success, or false on failure. + */ + bool dump_meta(const std::string& metapath) { + _assert_(true); + bool err = false; + char buf[METABUFSIZ]; + char* wp = buf; + wp += std::sprintf(wp, "%u\n", libver_); + wp += std::sprintf(wp, "%u\n", librev_); + wp += std::sprintf(wp, "%u\n", fmtver_); + wp += std::sprintf(wp, "%u\n", chksum_); + wp += std::sprintf(wp, "%u\n", type_); + wp += std::sprintf(wp, "%u\n", opts_); + wp += std::sprintf(wp, "%s\n", KCDDBMAGICEOF); + if (!File::write_file(metapath, buf, wp - buf)) { + set_error(_KCCODELINE_, Error::SYSTEM, "writing a file failed"); + err = true; + } + return !err; + } + /** + * Load the meta data from the file. + * @param metapath the path of the meta data file. + * @return true on success, or false on failure. + */ + bool load_meta(const std::string& metapath) { + _assert_(true); + int64_t size; + char* buf = File::read_file(metapath, &size, METABUFSIZ); + if (!buf) { + set_error(_KCCODELINE_, Error::SYSTEM, "reading a file failed"); + return false; + } + std::string str(buf, size); + delete[] buf; + std::vector<std::string> elems; + if (strsplit(str, '\n', &elems) < 7 || elems[6] != KCDDBMAGICEOF) { + set_error(_KCCODELINE_, Error::BROKEN, "invalid meta data file"); + return false; + } + libver_ = atoi(elems[0].c_str()); + librev_ = atoi(elems[1].c_str()); + fmtver_ = atoi(elems[2].c_str()); + chksum_ = atoi(elems[3].c_str()); + type_ = atoi(elems[4].c_str()); + opts_ = atoi(elems[5].c_str()); + return true; + } + /** + * Dump the opaque data into the file. + * @return true on success, or false on failure. + */ + bool dump_opaque() { + _assert_(true); + bool err = false; + const std::string& opath = path_ + File::PATHCHR + KCDDBOPAQUEFILE; + if (!File::write_file(opath, opaque_, sizeof(opaque_))) { + set_error(_KCCODELINE_, Error::SYSTEM, "writing a file failed"); + err = true; + } + return !err; + } + /** + * Load the opaque data from the file. + * @return true on success, or false on failure. + */ + void load_opaque() { + _assert_(true); + std::memset(opaque_, 0, sizeof(opaque_)); + const std::string& opath = path_ + File::PATHCHR + KCDDBOPAQUEFILE; + int64_t size; + char* buf = File::read_file(opath, &size, sizeof(opaque_)); + if (buf) { + std::memcpy(opaque_, buf, size); + delete[] buf; + } + } + /** + * Remove inner files. + * @param cpath the path of the database file. + * @return true on success, or false on failure. + */ + bool remove_files(const std::string& cpath) { + _assert_(true); + DirStream dir; + if (!dir.open(cpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + return false; + } + bool err = false; + std::string name; + while (dir.read(&name)) { + if (*name.c_str() == *KCDDBMAGICFILE) continue; + const std::string& rpath = cpath + File::PATHCHR + name; + if (!File::remove(rpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + err = true; + } + } + if (!dir.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + return !err; + } + /** + * Read a record. + * @param rpath the path of the record. + * @param rec the record structure. + * @return true on success, or false on failure. + */ + bool read_record(const std::string& rpath, Record* rec) { + _assert_(rec); + int64_t rsiz; + char* rbuf = File::read_file(rpath, &rsiz); + if (!rbuf) return false; + rec->rsiz = rsiz; + if (comp_) { + size_t zsiz; + char* zbuf = comp_->decompress(rbuf, rsiz, &zsiz); + if (!zbuf) { + set_error(_KCCODELINE_, Error::SYSTEM, "data decompression failed"); + delete[] rbuf; + return false; + } + delete[] rbuf; + rbuf = zbuf; + rsiz = zsiz; + } + const char* rp = rbuf; + if (rsiz < 4 || *(const unsigned char*)rp != RECMAGIC) { + set_error(_KCCODELINE_, Error::BROKEN, "invalid magic data of a record"); + report(_KCCODELINE_, Logger::WARN, "rpath=%s", rpath.c_str()); + report_binary(_KCCODELINE_, Logger::WARN, "rbuf", rbuf, rsiz); + delete[] rbuf; + return false; + } + rp++; + uint64_t num; + size_t step = readvarnum(rp, rsiz, &num); + rp += step; + rsiz -= step; + size_t ksiz = num; + if (rsiz < 2) { + report(_KCCODELINE_, Logger::WARN, "rpath=%s", rpath.c_str()); + delete[] rbuf; + return false; + } + step = readvarnum(rp, rsiz, &num); + rp += step; + rsiz -= step; + size_t vsiz = num; + if (rsiz < 1 + (int64_t)ksiz + (int64_t)vsiz || + ((const unsigned char*)rp)[ksiz+vsiz] != RECMAGIC) { + set_error(_KCCODELINE_, Error::BROKEN, "too short record"); + report(_KCCODELINE_, Logger::WARN, "rpath=%s", rpath.c_str()); + delete[] rbuf; + return false; + } + rec->rbuf = rbuf; + rec->kbuf = rp; + rec->ksiz = ksiz; + rec->vbuf = rp + ksiz; + rec->vsiz = vsiz; + return true; + } + /** + * Write a record. + * @param rpath the path of the record. + * @param name the file name of the record. + * @param kbuf the pointer to the key region. + * @param ksiz the size of the key region. + * @param vbuf the pointer to the value region. + * @param vsiz the size of the value region. + * @param wsp the pointer to the variable into which the size of the written record is + * assigned. + * @return true on success, or false on failure. + */ + bool write_record(const std::string& rpath, const char* name, const char* kbuf, size_t ksiz, + const char* vbuf, size_t vsiz, size_t* wsp) { + _assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ && wsp); + bool err = false; + char* rbuf = new char[NUMBUFSIZ*2+ksiz+vsiz]; + char* wp = rbuf; + *(wp++) = RECMAGIC; + wp += writevarnum(wp, ksiz); + wp += writevarnum(wp, vsiz); + std::memcpy(wp, kbuf, ksiz); + wp += ksiz; + std::memcpy(wp, vbuf, vsiz); + wp += vsiz; + *(wp++) = RECMAGIC; + size_t rsiz = wp - rbuf; + if (comp_) { + size_t zsiz; + char* zbuf = comp_->compress(rbuf, rsiz, &zsiz); + if (!zbuf) { + set_error(_KCCODELINE_, Error::SYSTEM, "data compression failed"); + delete[] rbuf; + *wsp = 0; + return false; + } + delete[] rbuf; + rbuf = zbuf; + rsiz = zsiz; + } + if (autotran_ && !tran_) { + const std::string& tpath = path_ + File::PATHCHR + KCDDBATRANPREFIX + name; + if (!File::write_file(tpath, rbuf, rsiz)) { + set_error(_KCCODELINE_, Error::SYSTEM, "writing a file failed"); + err = true; + } + if (!File::rename(tpath, rpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a file failed"); + err = true; + File::remove(tpath); + } + } else { + if (!File::write_file(rpath, rbuf, rsiz)) { + set_error(_KCCODELINE_, Error::SYSTEM, "writing a file failed"); + err = true; + } + } + delete[] rbuf; + *wsp = rsiz; + return !err; + } + /** + * Disable all cursors. + * @return true on success, or false on failure. + */ + bool disable_cursors() { + _assert_(true); + if (curs_.empty()) return true; + bool err = false; + CursorList::const_iterator cit = curs_.begin(); + CursorList::const_iterator citend = curs_.end(); + while (cit != citend) { + Cursor* cur = *cit; + if (cur->alive_ && !cur->disable()) err = true; + ++cit; + } + return !err; + } + /** + * Escape cursors on a free block. + * @param rpath the file path of the record. + * @param name the file name of the record. + * @return true on success, or false on failure. + */ + bool escape_cursors(const std::string& rpath, const char* name) { + bool err = false; + if (curs_.empty()) return true; + CursorList::const_iterator cit = curs_.begin(); + CursorList::const_iterator citend = curs_.end(); + while (cit != citend) { + Cursor* cur = *cit; + if (cur->alive_ && cur->name_ == name) { + do { + if (!cur->dir_.read(&cur->name_)) { + if (!cur->disable()) err = true; + break; + } + } while (*cur->name_.c_str() == *KCDDBMAGICFILE); + } + ++cit; + } + return !err; + } + /** + * Accept a visitor to a record. + * @param kbuf the pointer to the key region. + * @param ksiz the size of the key region. + * @param visitor a visitor object. + * @param name the encoded key. + * @return true on success, or false on failure. + */ + bool accept_impl(const char* kbuf, size_t ksiz, Visitor* visitor, const char* name) { + _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor && name); + bool err = false; + const std::string& rpath = path_ + File::PATHCHR + name; + Record rec; + if (read_record(rpath, &rec)) { + if (rec.ksiz == ksiz || !std::memcmp(rec.kbuf, kbuf, ksiz)) { + if (!accept_visit_full(kbuf, ksiz, rec.vbuf, rec.vsiz, rec.rsiz, + visitor, rpath, name)) err = true; + } else { + set_error(_KCCODELINE_, Error::LOGIC, "collision of the hash values"); + err = true; + } + delete[] rec.rbuf; + } else { + if (!accept_visit_empty(kbuf, ksiz, visitor, rpath, name)) err = true; + } + return !err; + } + /** + * Accept the visit_full method. + * @param kbuf the pointer to the key region. + * @param ksiz the size of the key region. + * @param vbuf the pointer to the value region. + * @param vsiz the size of the value region. + * @param osiz the old size of the record. + * @param visitor a visitor object. + * @param rpath the file path of the record. + * @param name the file name of the record. + * @return true on success, or false on failure. + */ + bool accept_visit_full(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz, + size_t osiz, Visitor *visitor, const std::string& rpath, + const char* name) { + _assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ && visitor); + bool err = false; + size_t rsiz; + const char* rbuf = visitor->visit_full(kbuf, ksiz, vbuf, vsiz, &rsiz); + if (rbuf == Visitor::REMOVE) { + if (tran_) { + const std::string& walpath = walpath_ + File::PATHCHR + name; + if (File::status(walpath)) { + if (!File::remove(rpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + err = true; + } + } else if (!File::rename(rpath, walpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a file failed"); + err = true; + } + } else { + if (!File::remove(rpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + err = true; + } + } + if (!escape_cursors(rpath, name)) err = true; + count_ -= 1; + size_ -= osiz; + if (autosync_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + err = true; + } + } else if (rbuf != Visitor::NOP) { + if (tran_) { + const std::string& walpath = walpath_ + File::PATHCHR + name; + if (!File::status(walpath) && !File::rename(rpath, walpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a file failed"); + err = true; + } + } + size_t wsiz; + if (!write_record(rpath, name, kbuf, ksiz, rbuf, rsiz, &wsiz)) err = true; + size_ += (int64_t)wsiz - (int64_t)osiz; + if (autosync_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + err = true; + } + } + return !err; + } + /** + * Accept the visit_empty method. + * @param kbuf the pointer to the key region. + * @param ksiz the size of the key region. + * @param visitor a visitor object. + * @param rpath the file path of the record. + * @param name the file name of the record. + * @return true on success, or false on failure. + */ + bool accept_visit_empty(const char* kbuf, size_t ksiz, + Visitor *visitor, const std::string& rpath, const char* name) { + _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); + bool err = false; + size_t rsiz; + const char* rbuf = visitor->visit_empty(kbuf, ksiz, &rsiz); + if (rbuf != Visitor::NOP && rbuf != Visitor::REMOVE) { + if (tran_) { + const std::string& walpath = walpath_ + File::PATHCHR + name; + if (!File::status(walpath) && !File::write_file(walpath, "", 0)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a file failed"); + err = true; + } + } + size_t wsiz; + if (!write_record(rpath, name, kbuf, ksiz, rbuf, rsiz, &wsiz)) err = true; + count_ += 1; + size_ += wsiz; + if (autosync_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + err = true; + } + } + return !err; + } + /** + * Iterate to accept a visitor for each record. + * @param visitor a visitor object. + * @param checker a progress checker object. + * @return true on success, or false on failure. + */ + bool iterate_impl(Visitor* visitor, ProgressChecker* checker) { + _assert_(visitor); + int64_t allcnt = count_; + if (checker && !checker->check("iterate", "beginning", 0, allcnt)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + return false; + } + DirStream dir; + if (!dir.open(path_)) { + set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + return false; + } + bool err = false; + std::string name; + int64_t curcnt = 0; + while (dir.read(&name)) { + if (*name.c_str() == *KCDDBMAGICFILE) continue; + const std::string& rpath = path_ + File::PATHCHR + name; + Record rec; + if (read_record(rpath, &rec)) { + if (!accept_visit_full(rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz, rec.rsiz, + visitor, rpath, name.c_str())) err = true; + delete[] rec.rbuf; + } else { + set_error(_KCCODELINE_, Error::BROKEN, "missing record"); + err = true; + } + curcnt++; + if (checker && !checker->check("iterate", "processing", curcnt, allcnt)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + err = true; + break; + } + } + if (!dir.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + if (checker && !checker->check("iterate", "ending", -1, allcnt)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + err = true; + } + return !err; + } + /** + * Scan each record in parallel. + * @param visitor a visitor object. + * @param thnum the number of worker threads. + * @param checker a progress checker object. + * @return true on success, or false on failure. + */ + bool scan_parallel_impl(Visitor *visitor, size_t thnum, ProgressChecker* checker) { + _assert_(visitor && thnum <= MEMMAXSIZ); + int64_t allcnt = count_; + if (checker && !checker->check("scan_parallel", "beginning", -1, allcnt)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + return false; + } + DirStream dir; + if (!dir.open(path_)) { + set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + return false; + } + class ThreadImpl : public Thread { + public: + explicit ThreadImpl() : + db_(NULL), visitor_(NULL), checker_(NULL), allcnt_(0), + dir_(NULL), itmtx_(NULL), error_() {} + void init(DirDB* db, Visitor* visitor, ProgressChecker* checker, int64_t allcnt, + DirStream* dir, Mutex* itmtx) { + db_ = db; + visitor_ = visitor; + checker_ = checker; + allcnt_ = allcnt; + dir_ = dir; + itmtx_ = itmtx; + } + const Error& error() { + return error_; + } + private: + void run() { + DirDB* db = db_; + Visitor* visitor = visitor_; + ProgressChecker* checker = checker_; + int64_t allcnt = allcnt_; + DirStream* dir = dir_; + Mutex* itmtx = itmtx_; + const std::string& path = db->path_; + while (true) { + itmtx->lock(); + std::string name; + if (!dir->read(&name)) { + itmtx->unlock(); + break; + } + itmtx->unlock(); + if (*name.c_str() == *KCDDBMAGICFILE) continue; + const std::string& rpath = path + File::PATHCHR + name; + Record rec; + if (db->read_record(rpath, &rec)) { + size_t vsiz; + visitor->visit_full(rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz, &vsiz); + delete[] rec.rbuf; + } else { + error_ = db->error(); + break; + } + if (checker && !checker->check("scan_parallel", "processing", -1, allcnt)) { + db->set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + error_ = db->error(); + break; + } + } + } + DirDB* db_; + Visitor* visitor_; + ProgressChecker* checker_; + int64_t allcnt_; + DirStream* dir_; + Mutex* itmtx_; + Error error_; + }; + bool err = false; + Mutex itmtx; + ThreadImpl* threads = new ThreadImpl[thnum]; + for (size_t i = 0; i < thnum; i++) { + ThreadImpl* thread = threads + i; + thread->init(this, visitor, checker, allcnt, &dir, &itmtx); + } + for (size_t i = 0; i < thnum; i++) { + ThreadImpl* thread = threads + i; + thread->start(); + } + for (size_t i = 0; i < thnum; i++) { + ThreadImpl* thread = threads + i; + thread->join(); + if (thread->error() != Error::SUCCESS) { + *error_ = thread->error(); + err = true; + } + } + delete[] threads; + if (!dir.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + if (checker && !checker->check("scan_parallel", "ending", -1, allcnt)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + err = true; + } + return !err; + } + /** + * Synchronize updated contents with the file and the device. + * @param hard true for physical synchronization with the device, or false for logical + * synchronization with the file system. + * @param proc a postprocessor object. + * @param checker a progress checker object. + * @return true on success, or false on failure. + */ + bool synchronize_impl(bool hard, FileProcessor* proc, ProgressChecker* checker) { + _assert_(true); + bool err = false; + if (writer_) { + if (checker && !checker->check("synchronize", "dumping the magic data", -1, -1)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + return false; + } + if (!dump_magic()) err = true; + if (checker && !checker->check("synchronize", "synchronizing the directory", -1, -1)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + return false; + } + if (hard && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + err = true; + } + } + if (proc) { + if (checker && !checker->check("synchronize", "running the post processor", -1, -1)) { + set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); + return false; + } + if (!proc->process(path_, count_, size_impl())) { + set_error(_KCCODELINE_, Error::LOGIC, "postprocessing failed"); + err = true; + } + } + if (writer_ && !file_.truncate(0)) { + set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); + err = true; + } + return !err; + } + /** + * Begin transaction. + * @return true on success, or false on failure. + */ + bool begin_transaction_impl() { + _assert_(true); + if (!File::make_directory(walpath_)) { + set_error(_KCCODELINE_, Error::SYSTEM, "making a directory failed"); + return false; + } + if (trhard_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + return false; + } + trcount_ = count_; + trsize_ = size_; + return true; + } + /** + * Commit transaction. + * @return true on success, or false on failure. + */ + bool commit_transaction() { + _assert_(true); + bool err = false; + if (!File::rename(walpath_, tmppath_)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a directory failed"); + err = true; + } + if (!remove_files(tmppath_)) err = true; + if (!File::remove_directory(tmppath_)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a directory failed"); + return false; + } + if (trhard_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + err = true; + } + return !err; + } + /** + * Abort transaction. + * @return true on success, or false on failure. + */ + bool abort_transaction() { + _assert_(true); + bool err = false; + if (!disable_cursors()) err = true; + DirStream dir; + if (dir.open(walpath_)) { + std::string name; + while (dir.read(&name)) { + const std::string& srcpath = walpath_ + File::PATHCHR + name; + const std::string& destpath = path_ + File::PATHCHR + name; + File::Status sbuf; + if (File::status(srcpath, &sbuf)) { + if (sbuf.size > 1) { + if (!File::rename(srcpath, destpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "renaming a file failed"); + err = true; + } + } else { + if (File::remove(destpath) || !File::status(destpath)) { + if (!File::remove(srcpath)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + err = true; + } + } else { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a file failed"); + err = true; + } + } + } else { + set_error(_KCCODELINE_, Error::SYSTEM, "checking a file failed"); + err = true; + } + } + if (!dir.close()) { + set_error(_KCCODELINE_, Error::SYSTEM, "closing a directory failed"); + err = true; + } + if (!File::remove_directory(walpath_)) { + set_error(_KCCODELINE_, Error::SYSTEM, "removing a directory failed"); + err = true; + } + } else { + set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory failed"); + err = true; + } + count_ = trcount_; + size_ = trsize_; + if (trhard_ && !File::synchronize_whole()) { + set_error(_KCCODELINE_, Error::SYSTEM, "synchronizing the file system failed"); + err = true; + } + return !err; + } + /** + * Get the size of the database file. + * @return the size of the database file in bytes. + */ + int64_t size_impl() { + return size_ + count_ * RECUNITSIZ; + } + /** Dummy constructor to forbid the use. */ + DirDB(const DirDB&); + /** Dummy Operator to forbid the use. */ + DirDB& operator =(const DirDB&); + /** The method lock. */ + RWLock mlock_; + /** The record locks. */ + SlottedRWLock rlock_; + /** The last happened error. */ + TSD<Error> error_; + /** The internal logger. */ + Logger* logger_; + /** The kinds of logged messages. */ + uint32_t logkinds_; + /** The internal meta operation trigger. */ + MetaTrigger* mtrigger_; + /** The open mode. */ + uint32_t omode_; + /** The flag for writer. */ + bool writer_; + /** The flag for auto transaction. */ + bool autotran_; + /** The flag for auto synchronization. */ + bool autosync_; + /** The flag for recovered. */ + bool recov_; + /** The flag for reorganized. */ + bool reorg_; + /** The file for magic data. */ + File file_; + /** The cursor objects. */ + CursorList curs_; + /** The path of the database file. */ + std::string path_; + /** The library version. */ + uint8_t libver_; + /** The library revision. */ + uint8_t librev_; + /** The format revision. */ + uint8_t fmtver_; + /** The module checksum. */ + uint8_t chksum_; + /** The database type. */ + uint8_t type_; + /** The status flags. */ + uint8_t flags_; + /** The options. */ + uint8_t opts_; + /** The record number. */ + AtomicInt64 count_; + /** The total size of records. */ + AtomicInt64 size_; + /** The opaque data. */ + char opaque_[OPAQUESIZ]; + /** The embedded data compressor. */ + Compressor* embcomp_; + /** The data compressor. */ + Compressor* comp_; + /** The compression checksum. */ + bool tran_; + /** The flag whether hard transaction. */ + bool trhard_; + /** The old count before transaction. */ + int64_t trcount_; + /** The old size before transaction. */ + int64_t trsize_; + /** The WAL directory for transaction. */ + std::string walpath_; + /** The temporary directory. */ + std::string tmppath_; +}; + + +/** An alias of the directory tree database. */ +typedef PlantDB<DirDB, BasicDB::TYPEFOREST> ForestDB; + + +} // common namespace + +#endif // duplication check + +// END OF FILE |