kccachedb.h   kccachedb.h 
skipping to change at line 84 skipping to change at line 84
*/ */
class Cursor : public BasicDB::Cursor { class Cursor : public BasicDB::Cursor {
friend class CacheDB; friend class CacheDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(CacheDB* db) : db_(db), sidx_(-1), rec_(NULL) { explicit Cursor(CacheDB* db) : db_(db), sidx_(-1), rec_(NULL) {
_assert_(db); _assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true); _assert_(true);
if (!db_) return; if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.remove(this); db_->curs_.remove(this);
} }
/** /**
* Accept a visitor to the current record. * Accept a visitor to the current record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation. * @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. * @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and othe r threads accessing * @note The operation for each record is performed atomically and othe r threads accessing
* the same record are blocked. To avoid deadlock, any explicit databa se operation must not * the same record are blocked. To avoid deadlock, any explicit databa se operation must not
* be performed in this function. * be performed in this function.
*/ */
bool accept(Visitor* visitor, bool writable = true, bool step = false) { bool accept(Visitor* visitor, bool writable = true, bool step = false) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->omode_ & OWRITER)) { if (writable && !(db_->omode_ & OWRITER)) {
db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (sidx_ < 0 || !rec_) { if (sidx_ < 0 || !rec_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
skipping to change at line 159 skipping to change at line 159
if (step) step_impl(); if (step) step_impl();
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record for forward scan. * Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump() { bool jump() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
Slot* slot = db_->slots_ + i; Slot* slot = db_->slots_ + i;
if (slot->first) { if (slot->first) {
sidx_ = i; sidx_ = i;
rec_ = slot->first; rec_ = slot->first;
return true; return true;
skipping to change at line 185 skipping to change at line 185
return false; return false;
} }
/** /**
* Jump the cursor to a record for forward scan. * Jump the cursor to a record for forward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump(const char* kbuf, size_t ksiz) { bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (ksiz > KSIZMAX) ksiz = KSIZMAX; if (ksiz > KSIZMAX) ksiz = KSIZMAX;
uint64_t hash = db_->hash_record(kbuf, ksiz); uint64_t hash = db_->hash_record(kbuf, ksiz);
int32_t sidx = hash % SLOTNUM; int32_t sidx = hash % SLOTNUM;
hash /= SLOTNUM; hash /= SLOTNUM;
Slot* slot = db_->slots_ + sidx; Slot* slot = db_->slots_ + sidx;
size_t bidx = hash % slot->bnum; size_t bidx = hash % slot->bnum;
skipping to change at line 243 skipping to change at line 243
bool jump(const std::string& key) { bool jump(const std::string& key) {
_assert_(true); _assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), key.size());
} }
/** /**
* Jump the cursor to the last record for backward scan. * Jump the cursor to the last record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back() { bool jump_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const char* kbuf, size_t ksiz) { bool jump_back(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const std::string& key) { bool jump_back(const std::string& key) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Step the cursor to the next record. * Step the cursor to the next record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step() { bool step() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (sidx_ < 0 || !rec_) { if (sidx_ < 0 || !rec_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
bool err = false; bool err = false;
if (!step_impl()) err = true; if (!step_impl()) err = true;
return !err; return !err;
} }
/** /**
* Step the cursor to the previous record. * Step the cursor to the previous record.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool step_back() { bool step_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Get the database object. * Get the database object.
* @return the database object. * @return the database object.
skipping to change at line 410 skipping to change at line 410
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and other threads accessing the * @note The operation for each record is performed atomically and other threads accessing the
* same record are blocked. To avoid deadlock, any explicit database ope ration must not be * same record are blocked. To avoid deadlock, any explicit database ope ration must not be
* performed in this function. * performed in this function.
*/ */
bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) { bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (ksiz > KSIZMAX) ksiz = KSIZMAX; if (ksiz > KSIZMAX) ksiz = KSIZMAX;
uint64_t hash = hash_record(kbuf, ksiz); uint64_t hash = hash_record(kbuf, ksiz);
skipping to change at line 442 skipping to change at line 442
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operations for specified records are performed atomically an d other threads * @note The operations for specified records are performed atomically an d other threads
* accessing the same records are blocked. To avoid deadlock, any explic it database operation * accessing the same records are blocked. To avoid deadlock, any explic it database operation
* must not be performed in this function. * must not be performed in this function.
*/ */
bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor,
bool writable = true) { bool writable = true) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
size_t knum = keys.size(); size_t knum = keys.size();
skipping to change at line 506 skipping to change at line 506
* Iterate to accept a visitor for each record. * Iterate to accept a visitor for each record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @param checker a progress checker object. If it is NULL, no checking is performed. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads ar e blocked. To avoid * @note The whole iteration is performed atomically and other threads ar e blocked. To avoid
* deadlock, any explicit database operation must not be performed in thi s function. * deadlock, any explicit database operation must not be performed in thi s function.
*/ */
bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) { bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
int64_t allcnt = count_impl(); int64_t allcnt = count_impl();
skipping to change at line 615 skipping to change at line 615
* CacheDB::OTRYLOCK, which means locking is performed without blocking, CacheDB::ONOREPAIR, * CacheDB::OTRYLOCK, which means locking is performed without blocking, CacheDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is * which means the database file is not repaired implicitly even if file destruction is
* detected. * detected.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Every opened database must be closed by the CacheDB::close metho d when it is no * @note Every opened database must be closed by the CacheDB::close metho d when it is no
* longer in use. It is not allowed for two or more database objects in the same process to * 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. * keep their connections to the same database file at the same time.
*/ */
bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str());
omode_ = mode; omode_ = mode;
path_.append(path); path_.append(path);
size_t bnum = nearbyprime(bnum_ / SLOTNUM); size_t bnum = nearbyprime(bnum_ / SLOTNUM);
size_t capcnt = capcnt_ > 0 ? capcnt_ / SLOTNUM + 1 : (1ULL << (sizeof( capcnt) * 8 - 1)); size_t capcnt = capcnt_ > 0 ? capcnt_ / SLOTNUM + 1 : (1ULL << (sizeof( capcnt) * 8 - 1));
size_t capsiz = capsiz_ > 0 ? capsiz_ / SLOTNUM + 1 : (1ULL << (sizeof( capsiz) * 8 - 1)); size_t capsiz = capsiz_ > 0 ? capsiz_ / SLOTNUM + 1 : (1ULL << (sizeof( capsiz) * 8 - 1));
skipping to change at line 642 skipping to change at line 642
std::memset(opaque_, 0, sizeof(opaque_)); std::memset(opaque_, 0, sizeof(opaque_));
trigger_meta(MetaTrigger::OPEN, "open"); trigger_meta(MetaTrigger::OPEN, "open");
return true; return true;
} }
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close() { bool close() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str()); report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str());
tran_ = false; tran_ = false;
for (int32_t i = SLOTNUM - 1; i >= 0; i--) { for (int32_t i = SLOTNUM - 1; i >= 0; i--) {
destroy_slot(slots_ + i); destroy_slot(slots_ + i);
} }
path_.clear(); path_.clear();
skipping to change at line 671 skipping to change at line 671
* @param proc a postprocessor object. If it is NULL, no postprocessing is performed. * @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. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the postprocessor is performed atomically and o ther threads accessing * @note The operation of the postprocessor is performed atomically and o ther threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool synchronize(bool hard = false, FileProcessor* proc = NULL, bool synchronize(bool hard = false, FileProcessor* proc = NULL,
ProgressChecker* checker = NULL) { ProgressChecker* checker = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
bool err = false; bool err = false;
if ((omode_ & OWRITER) && checker && if ((omode_ & OWRITER) && checker &&
!checker->check("synchronize", "nothing to be synchronized", -1, -1 )) { !checker->check("synchronize", "nothing to be synchronized", -1, -1 )) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
return false; return false;
} }
skipping to change at line 706 skipping to change at line 706
* Occupy database by locking and do something meanwhile. * Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock. * @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 perfo rmed. * @param proc a processor object. If it is NULL, no processing is perfo rmed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing * @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 * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool occupy(bool writable = true, FileProcessor* proc = NULL) { bool occupy(bool writable = true, FileProcessor* proc = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, writable); ScopedRWLock lock(&mlock_, writable);
bool err = false; bool err = false;
if (proc && !proc->process(path_, count_impl(), size_impl())) { if (proc && !proc->process(path_, count_impl(), size_impl())) {
set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); set_error(_KCCODELINE_, Error::LOGIC, "processing failed");
err = true; err = true;
} }
trigger_meta(MetaTrigger::OCCUPY, "occupy"); trigger_meta(MetaTrigger::OCCUPY, "occupy");
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
skipping to change at line 786 skipping to change at line 786
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* End transaction. * End transaction.
* @param commit true to commit the transaction, or false to abort the tr ansaction. * @param commit true to commit the transaction, or false to abort the tr ansaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool end_transaction(bool commit = true) { bool end_transaction(bool commit = true) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); set_error(_KCCODELINE_, Error::INVALID, "not in transaction");
return false; return false;
} }
if (!commit) disable_cursors(); if (!commit) disable_cursors();
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
skipping to change at line 811 skipping to change at line 811
tran_ = false; tran_ = false;
trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction");
return true; return true;
} }
/** /**
* Remove all records. * Remove all records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clear() { bool clear() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
disable_cursors(); disable_cursors();
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
Slot* slot = slots_ + i; Slot* slot = slots_ + i;
clear_slot(slot); clear_slot(slot);
} }
std::memset(opaque_, 0, sizeof(opaque_)); std::memset(opaque_, 0, sizeof(opaque_));
trigger_meta(MetaTrigger::CLEAR, "clear"); trigger_meta(MetaTrigger::CLEAR, "clear");
return true; return true;
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count() { int64_t count() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return count_impl(); return count_impl();
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure. * @return the size of the database file in bytes, or -1 on failure.
*/ */
int64_t size() { int64_t size() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return size_impl(); return size_impl();
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
std::string path() { std::string path() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return ""; return "";
} }
return path_; return path_;
} }
/** /**
* Get the miscellaneous status information. * Get the miscellaneous status information.
* @param strmap a string map to contain the result. * @param strmap a string map to contain the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool status(std::map<std::string, std::string>* strmap) { bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap); _assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = strprintf("%u", (unsigned)TYPECACHE); (*strmap)["type"] = strprintf("%u", (unsigned)TYPECACHE);
(*strmap)["realtype"] = strprintf("%u", (unsigned)type_); (*strmap)["realtype"] = strprintf("%u", (unsigned)type_);
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
(*strmap)["libver"] = strprintf("%u", LIBVER); (*strmap)["libver"] = strprintf("%u", LIBVER);
(*strmap)["librev"] = strprintf("%u", LIBREV); (*strmap)["librev"] = strprintf("%u", LIBREV);
(*strmap)["fmtver"] = strprintf("%u", FMTVER); (*strmap)["fmtver"] = strprintf("%u", FMTVER);
skipping to change at line 929 skipping to change at line 929
* @param file the file name of the program source code. * @param file the file name of the program source code.
* @param line the line number 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 func the function name of the program source code.
* @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal * @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal
* information, Logger::WARN for warning, and Logger::ERROR for fatal err or. * information, Logger::WARN for warning, and Logger::ERROR for fatal err or.
* @param message the supplement message. * @param message the supplement message.
*/ */
void log(const char* file, int32_t line, const char* func, Logger::Kind k ind, void log(const char* file, int32_t line, const char* func, Logger::Kind k ind,
const char* message) { const char* message) {
_assert_(file && line > 0 && func && message); _assert_(file && line > 0 && func && message);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (!logger_) return; if (!logger_) return;
logger_->log(file, line, func, kind, message); logger_->log(file, line, func, kind, message);
} }
/** /**
* Set the internal logger. * Set the internal logger.
* @param logger the logger object. * @param logger the logger object.
* @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging,
* Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal * Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal
* error. * error.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) { bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) {
_assert_(logger); _assert_(logger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
logger_ = logger; logger_ = logger;
logkinds_ = kinds; logkinds_ = kinds;
return true; return true;
} }
/** /**
* Set the internal meta operation trigger. * Set the internal meta operation trigger.
* @param trigger the trigger object. * @param trigger the trigger object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_meta_trigger(MetaTrigger* trigger) { bool tune_meta_trigger(MetaTrigger* trigger) {
_assert_(trigger); _assert_(trigger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
mtrigger_ = trigger; mtrigger_ = trigger;
return true; return true;
} }
/** /**
* Set the optional features. * Set the optional features.
* @param opts the optional features by bitwise-or: DirDB::TCOMPRESS to c ompress each record. * @param opts the optional features by bitwise-or: DirDB::TCOMPRESS to c ompress each record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_options(int8_t opts) { bool tune_options(int8_t opts) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
opts_ = opts; opts_ = opts;
return true; return true;
} }
/** /**
* Set the number of buckets of the hash table. * Set the number of buckets of the hash table.
* @param bnum the number of buckets of the hash table. * @param bnum the number of buckets of the hash table.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_buckets(int64_t bnum) { bool tune_buckets(int64_t bnum) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum >= 0 ? bnum : DEFBNUM; bnum_ = bnum >= 0 ? bnum : DEFBNUM;
return true; return true;
} }
/** /**
* Set the data compressor. * Set the data compressor.
* @param comp the data compressor object. * @param comp the data compressor object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_compressor(Compressor* comp) { bool tune_compressor(Compressor* comp) {
_assert_(comp); _assert_(comp);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
embcomp_ = comp; embcomp_ = comp;
return true; return true;
} }
/** /**
* Set the capacity by record number. * Set the capacity by record number.
* @param count the maximum number of records. * @param count the maximum number of records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool cap_count(int64_t count) { bool cap_count(int64_t count) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
capcnt_ = count; capcnt_ = count;
return true; return true;
} }
/** /**
* Set the capacity by memory usage. * Set the capacity by memory usage.
* @param size the maximum size of memory usage. * @param size the maximum size of memory usage.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool cap_size(int64_t size) { bool cap_size(int64_t size) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
capsiz_ = size; capsiz_ = size;
return true; return true;
} }
/** /**
* Get the opaque data. * Get the opaque data.
* @return the pointer to the opaque data region, whose size is 16 bytes. * @return the pointer to the opaque data region, whose size is 16 bytes.
*/ */
char* opaque() { char* opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return opaque_; return opaque_;
} }
/** /**
* Synchronize the opaque data. * Synchronize the opaque data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_opaque() { bool synchronize_opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
return true; return true;
} }
skipping to change at line 1153 skipping to change at line 1153
_assert_(message); _assert_(message);
if (mtrigger_) mtrigger_->trigger(kind, message); if (mtrigger_) mtrigger_->trigger(kind, message);
} }
/** /**
* Set the database type. * Set the database type.
* @param type the database type. * @param type the database type.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_type(int8_t type) { bool tune_type(int8_t type) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
type_ = type; type_ = type;
return true; return true;
} }
/** /**
* Get the library version. * Get the library version.
* @return the library version, or 0 on failure. * @return the library version, or 0 on failure.
*/ */
uint8_t libver() { uint8_t libver() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return LIBVER; return LIBVER;
} }
/** /**
* Get the library revision. * Get the library revision.
* @return the library revision, or 0 on failure. * @return the library revision, or 0 on failure.
*/ */
uint8_t librev() { uint8_t librev() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return LIBREV; return LIBREV;
} }
/** /**
* Get the format version. * Get the format version.
* @return the format version, or 0 on failure. * @return the format version, or 0 on failure.
*/ */
uint8_t fmtver() { uint8_t fmtver() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return FMTVER; return FMTVER;
} }
/** /**
* Get the module checksum. * Get the module checksum.
* @return the module checksum, or 0 on failure. * @return the module checksum, or 0 on failure.
*/ */
uint8_t chksum() { uint8_t chksum() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return 0xff; return 0xff;
} }
/** /**
* Get the database type. * Get the database type.
* @return the database type, or 0 on failure. * @return the database type, or 0 on failure.
*/ */
uint8_t type() { uint8_t type() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return type_; return type_;
} }
/** /**
* Get the options. * Get the options.
* @return the options, or 0 on failure. * @return the options, or 0 on failure.
*/ */
uint8_t opts() { uint8_t opts() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return opts_; return opts_;
} }
/** /**
* Get the data compressor. * Get the data compressor.
* @return the data compressor, or NULL on failure. * @return the data compressor, or NULL on failure.
*/ */
Compressor* comp() { Compressor* comp() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return comp_; return comp_;
} }
/** /**
* Check whether the database was recovered or not. * Check whether the database was recovered or not.
* @return true if recovered, or false if not. * @return true if recovered, or false if not.
*/ */
bool recovered() { bool recovered() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return false; return false;
} }
/** /**
* Check whether the database was reorganized or not. * Check whether the database was reorganized or not.
* @return true if reorganized, or false if not. * @return true if reorganized, or false if not.
*/ */
bool reorganized() { bool reorganized() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return false; return false;
} }
private: private:
/** /**
* Set the power of the alignment of record size. * Set the power of the alignment of record size.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
skipping to change at line 1389 skipping to change at line 1389
} }
/** constructor for an empty record */ /** constructor for an empty record */
explicit TranLog(const char* kbuf, size_t ksiz) : full(false), key(kbuf , ksiz) { explicit TranLog(const char* kbuf, size_t ksiz) : full(false), key(kbuf , ksiz) {
_assert_(true); _assert_(true);
} }
}; };
/** /**
* Slot table. * Slot table.
*/ */
struct Slot { struct Slot {
SpinLock lock; ///< lock Mutex lock; ///< lock
Record** buckets; ///< bucket array Record** buckets; ///< bucket array
size_t bnum; ///< number of buckets size_t bnum; ///< number of buckets
size_t capcnt; ///< cap of record number size_t capcnt; ///< cap of record number
size_t capsiz; ///< cap of memory usage size_t capsiz; ///< cap of memory usage
Record* first; ///< first record Record* first; ///< first record
Record* last; ///< last record Record* last; ///< last record
size_t count; ///< number of records size_t count; ///< number of records
size_t size; ///< total size of records size_t size; ///< total size of records
TranLogList trlogs; ///< transaction logs TranLogList trlogs; ///< transaction logs
size_t trsize; ///< size before transaction size_t trsize; ///< size before transaction
skipping to change at line 1658 skipping to change at line 1658
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count_impl() { int64_t count_impl() {
_assert_(true); _assert_(true);
int64_t sum = 0; int64_t sum = 0;
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
Slot* slot = slots_ + i; Slot* slot = slots_ + i;
ScopedSpinLock lock(&slot->lock); ScopedMutex lock(&slot->lock);
sum += slot->count; sum += slot->count;
} }
return sum; return sum;
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes. * @return the size of the database file in bytes.
*/ */
int64_t size_impl() { int64_t size_impl() {
_assert_(true); _assert_(true);
int64_t sum = sizeof(*this); int64_t sum = sizeof(*this);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
Slot* slot = slots_ + i; Slot* slot = slots_ + i;
ScopedSpinLock lock(&slot->lock); ScopedMutex lock(&slot->lock);
sum += slot->bnum * sizeof(Record*); sum += slot->bnum * sizeof(Record*);
sum += slot->size; sum += slot->size;
} }
return sum; return sum;
} }
/** /**
* Initialize a slot table. * Initialize a slot table.
* @param slot the slot table. * @param slot the slot table.
* @param bnum the number of buckets. * @param bnum the number of buckets.
* @param capcnt the capacity of record number. * @param capcnt the capacity of record number.
skipping to change at line 1836 skipping to change at line 1836
_assert_(abuf && asiz <= MEMMAXSIZ && bbuf && bsiz <= MEMMAXSIZ); _assert_(abuf && asiz <= MEMMAXSIZ && bbuf && bsiz <= MEMMAXSIZ);
if (asiz != bsiz) return (int32_t)asiz - (int32_t)bsiz; if (asiz != bsiz) return (int32_t)asiz - (int32_t)bsiz;
return std::memcmp(abuf, bbuf, asiz); return std::memcmp(abuf, bbuf, asiz);
} }
/** /**
* Escape cursors on a shifted or removed records. * Escape cursors on a shifted or removed records.
* @param rec the record. * @param rec the record.
*/ */
void escape_cursors(Record* rec) { void escape_cursors(Record* rec) {
_assert_(rec); _assert_(rec);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
if (curs_.empty()) return; if (curs_.empty()) return;
CursorList::const_iterator cit = curs_.begin(); CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end(); CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
if (cur->rec_ == rec) cur->step_impl(); if (cur->rec_ == rec) cur->step_impl();
++cit; ++cit;
} }
} }
/** /**
* Adjust cursors on re-allocated records. * Adjust cursors on re-allocated records.
* @param orec the old address. * @param orec the old address.
* @param nrec the new address. * @param nrec the new address.
*/ */
void adjust_cursors(Record* orec, Record* nrec) { void adjust_cursors(Record* orec, Record* nrec) {
_assert_(orec && nrec); _assert_(orec && nrec);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
if (curs_.empty()) return; if (curs_.empty()) return;
CursorList::const_iterator cit = curs_.begin(); CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end(); CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
if (cur->rec_ == orec) cur->rec_ = nrec; if (cur->rec_ == orec) cur->rec_ = nrec;
++cit; ++cit;
} }
} }
/** /**
* Disable all cursors. * Disable all cursors.
*/ */
void disable_cursors() { void disable_cursors() {
_assert_(true); _assert_(true);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
CursorList::const_iterator cit = curs_.begin(); CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end(); CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
cur->sidx_ = -1; cur->sidx_ = -1;
cur->rec_ = NULL; cur->rec_ = NULL;
++cit; ++cit;
} }
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
CacheDB(const CacheDB&); CacheDB(const CacheDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
CacheDB& operator =(const CacheDB&); CacheDB& operator =(const CacheDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; RWLock mlock_;
/** The file lock. */ /** The file lock. */
SpinLock flock_; Mutex flock_;
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The internal logger. */ /** The internal logger. */
Logger* logger_; Logger* logger_;
/** The kinds of logged messages. */ /** The kinds of logged messages. */
uint32_t logkinds_; uint32_t logkinds_;
/** The internal meta operation trigger. */ /** The internal meta operation trigger. */
MetaTrigger* mtrigger_; MetaTrigger* mtrigger_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
 End of changes. 51 change blocks. 
51 lines changed or deleted 51 lines changed or added


 kcdirdb.h   kcdirdb.h 
skipping to change at line 86 skipping to change at line 86
*/ */
class Cursor : public BasicDB::Cursor { class Cursor : public BasicDB::Cursor {
friend class DirDB; friend class DirDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(DirDB* db) : db_(db), dir_(), alive_(false), name_("") { explicit Cursor(DirDB* db) : db_(db), dir_(), alive_(false), name_("") {
_assert_(db); _assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true); _assert_(true);
if (!db_) return; if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.remove(this); db_->curs_.remove(this);
} }
/** /**
* Accept a visitor to the current record. * Accept a visitor to the current record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation. * @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. * @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and othe r threads accessing * @note The operation for each record is performed atomically and othe r threads accessing
* the same record are blocked. To avoid deadlock, any explicit databa se operation must not * the same record are blocked. To avoid deadlock, any explicit databa se operation must not
* be performed in this function. * be performed in this function.
*/ */
bool accept(Visitor* visitor, bool writable = true, bool step = false) { bool accept(Visitor* visitor, bool writable = true, bool step = false) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->writer_)) { if (writable && !(db_->writer_)) {
db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (!alive_) { if (!alive_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
skipping to change at line 176 skipping to change at line 176
} }
} }
return !err; return !err;
} }
/** /**
* Jump the cursor to the first record for forward scan. * Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump() { bool jump() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (alive_ && !disable()) return false; if (alive_ && !disable()) return false;
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!dir_.open(db_->path_)) { if (!dir_.open(db_->path_)) {
db_->set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory fa iled"); db_->set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory fa iled");
return false; return false;
} }
alive_ = true; alive_ = true;
skipping to change at line 204 skipping to change at line 204
return true; return true;
} }
/** /**
* Jump the cursor to a record for forward scan. * Jump the cursor to a record for forward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump(const char* kbuf, size_t ksiz) { bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (alive_ && !disable()) return false; if (alive_ && !disable()) return false;
if (!dir_.open(db_->path_)) { if (!dir_.open(db_->path_)) {
db_->set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory fa iled"); db_->set_error(_KCCODELINE_, Error::SYSTEM, "opening a directory fa iled");
return false; return false;
} }
alive_ = true; alive_ = true;
while (true) { while (true) {
if (!dir_.read(&name_)) { if (!dir_.read(&name_)) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
disable(); disable();
skipping to change at line 248 skipping to change at line 248
bool jump(const std::string& key) { bool jump(const std::string& key) {
_assert_(true); _assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), key.size());
} }
/** /**
* Jump the cursor to the last record for backward scan. * Jump the cursor to the last record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back() { bool jump_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const char* kbuf, size_t ksiz) { bool jump_back(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const std::string& key) { bool jump_back(const std::string& key) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Step the cursor to the next record. * Step the cursor to the next record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step() { bool step() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!alive_) { if (!alive_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
do { do {
if (!dir_.read(&name_)) { if (!dir_.read(&name_)) {
skipping to change at line 314 skipping to change at line 314
} }
} while (*name_.c_str() == *KCDDBMAGICFILE); } while (*name_.c_str() == *KCDDBMAGICFILE);
return true; return true;
} }
/** /**
* Step the cursor to the previous record. * Step the cursor to the previous record.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool step_back() { bool step_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Get the database object. * Get the database object.
* @return the database object. * @return the database object.
skipping to change at line 416 skipping to change at line 416
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and other threads accessing the * @note The operation for each record is performed atomically and other threads accessing the
* same record are blocked. To avoid deadlock, any explicit database ope ration must not be * same record are blocked. To avoid deadlock, any explicit database ope ration must not be
* performed in this function. * performed in this function.
*/ */
bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) { bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
char name[NUMBUFSIZ]; char name[NUMBUFSIZ];
skipping to change at line 450 skipping to change at line 450
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operations for specified records are performed atomically an d other threads * @note The operations for specified records are performed atomically an d other threads
* accessing the same records are blocked. To avoid deadlock, any explic it database operation * accessing the same records are blocked. To avoid deadlock, any explic it database operation
* must not be performed in this function. * must not be performed in this function.
*/ */
bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor,
bool writable = true) { bool writable = true) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
size_t knum = keys.size(); size_t knum = keys.size();
skipping to change at line 514 skipping to change at line 514
* Iterate to accept a visitor for each record. * Iterate to accept a visitor for each record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @param checker a progress checker object. If it is NULL, no checking is performed. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads ar e blocked. To avoid * @note The whole iteration is performed atomically and other threads ar e blocked. To avoid
* deadlock, any explicit database operation must not be performed in thi s function. * deadlock, any explicit database operation must not be performed in thi s function.
*/ */
bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) { bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
bool err = false; bool err = false;
skipping to change at line 578 skipping to change at line 578
* DirDB::OTRYLOCK, which means locking is performed without blocking, Di rDB::ONOREPAIR, * DirDB::OTRYLOCK, which means locking is performed without blocking, Di rDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is * which means the database file is not repaired implicitly even if file destruction is
* detected. * detected.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Every opened database must be closed by the DirDB::close method when it is no * @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 * 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. * keep their connections to the same database file at the same time.
*/ */
bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str());
writer_ = false; writer_ = false;
autotran_ = false; autotran_ = false;
autosync_ = false; autosync_ = false;
recov_ = false; recov_ = false;
reorg_ = false; reorg_ = false;
skipping to change at line 781 skipping to change at line 781
load_opaque(); load_opaque();
trigger_meta(MetaTrigger::OPEN, "open"); trigger_meta(MetaTrigger::OPEN, "open");
return true; return true;
} }
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close() { bool close() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str()); report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str());
bool err = false; bool err = false;
if (tran_ && !abort_transaction()) err = true; if (tran_ && !abort_transaction()) err = true;
if (!disable_cursors()) err = true; if (!disable_cursors()) err = true;
if (writer_) { if (writer_) {
if (!dump_magic()) err = true; if (!dump_magic()) err = true;
skipping to change at line 816 skipping to change at line 816
* @param proc a postprocessor object. If it is NULL, no postprocessing is performed. * @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. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the postprocessor is performed atomically and o ther threads accessing * @note The operation of the postprocessor is performed atomically and o ther threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool synchronize(bool hard = false, FileProcessor* proc = NULL, bool synchronize(bool hard = false, FileProcessor* proc = NULL,
ProgressChecker* checker = NULL) { ProgressChecker* checker = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
rlock_.lock_reader_all(); rlock_.lock_reader_all();
bool err = false; bool err = false;
if (!synchronize_impl(hard, proc, checker)) err = true; if (!synchronize_impl(hard, proc, checker)) err = true;
trigger_meta(MetaTrigger::SYNCHRONIZE, "synchronize"); trigger_meta(MetaTrigger::SYNCHRONIZE, "synchronize");
rlock_.unlock_all(); rlock_.unlock_all();
return !err; return !err;
skipping to change at line 839 skipping to change at line 839
* Occupy database by locking and do something meanwhile. * Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock. * @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 perfo rmed. * @param proc a processor object. If it is NULL, no processing is perfo rmed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing * @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 * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool occupy(bool writable = true, FileProcessor* proc = NULL) { bool occupy(bool writable = true, FileProcessor* proc = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, writable); ScopedRWLock lock(&mlock_, writable);
bool err = false; bool err = false;
if (proc && !proc->process(path_, count_, size_impl())) { if (proc && !proc->process(path_, count_, size_impl())) {
set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); set_error(_KCCODELINE_, Error::LOGIC, "processing failed");
err = true; err = true;
} }
trigger_meta(MetaTrigger::OCCUPY, "occupy"); trigger_meta(MetaTrigger::OCCUPY, "occupy");
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
skipping to change at line 929 skipping to change at line 929
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* End transaction. * End transaction.
* @param commit true to commit the transaction, or false to abort the tr ansaction. * @param commit true to commit the transaction, or false to abort the tr ansaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool end_transaction(bool commit = true) { bool end_transaction(bool commit = true) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); set_error(_KCCODELINE_, Error::INVALID, "not in transaction");
return false; return false;
} }
bool err = false; bool err = false;
if (commit) { if (commit) {
skipping to change at line 954 skipping to change at line 954
tran_ = false; tran_ = false;
trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction");
return !err; return !err;
} }
/** /**
* Remove all records. * Remove all records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clear() { bool clear() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (!disable_cursors()) err = true; if (!disable_cursors()) err = true;
skipping to change at line 1009 skipping to change at line 1009
size_ = 0; size_ = 0;
trigger_meta(MetaTrigger::CLEAR, "clear"); trigger_meta(MetaTrigger::CLEAR, "clear");
return !err; return !err;
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count() { int64_t count() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return count_; return count_;
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure. * @return the size of the database file in bytes, or -1 on failure.
*/ */
int64_t size() { int64_t size() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return size_impl(); return size_impl();
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
std::string path() { std::string path() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return ""; return "";
} }
return path_; return path_;
} }
/** /**
* Get the miscellaneous status information. * Get the miscellaneous status information.
* @param strmap a string map to contain the result. * @param strmap a string map to contain the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool status(std::map<std::string, std::string>* strmap) { bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap); _assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = strprintf("%u", (unsigned)TYPEDIR); (*strmap)["type"] = strprintf("%u", (unsigned)TYPEDIR);
(*strmap)["realtype"] = strprintf("%u", (unsigned)type_); (*strmap)["realtype"] = strprintf("%u", (unsigned)type_);
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
(*strmap)["libver"] = strprintf("%u", libver_); (*strmap)["libver"] = strprintf("%u", libver_);
(*strmap)["librev"] = strprintf("%u", librev_); (*strmap)["librev"] = strprintf("%u", librev_);
(*strmap)["fmtver"] = strprintf("%u", fmtver_); (*strmap)["fmtver"] = strprintf("%u", fmtver_);
skipping to change at line 1093 skipping to change at line 1093
* @param file the file name of the program source code. * @param file the file name of the program source code.
* @param line the line number 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 func the function name of the program source code.
* @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal * @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal
* information, Logger::WARN for warning, and Logger::ERROR for fatal err or. * information, Logger::WARN for warning, and Logger::ERROR for fatal err or.
* @param message the supplement message. * @param message the supplement message.
*/ */
void log(const char* file, int32_t line, const char* func, Logger::Kind k ind, void log(const char* file, int32_t line, const char* func, Logger::Kind k ind,
const char* message) { const char* message) {
_assert_(file && line > 0 && func && message); _assert_(file && line > 0 && func && message);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (!logger_) return; if (!logger_) return;
logger_->log(file, line, func, kind, message); logger_->log(file, line, func, kind, message);
} }
/** /**
* Set the internal logger. * Set the internal logger.
* @param logger the logger object. * @param logger the logger object.
* @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging,
* Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal * Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal
* error. * error.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) { bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) {
_assert_(logger); _assert_(logger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
logger_ = logger; logger_ = logger;
logkinds_ = kinds; logkinds_ = kinds;
return true; return true;
} }
/** /**
* Set the internal meta operation trigger. * Set the internal meta operation trigger.
* @param trigger the trigger object. * @param trigger the trigger object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_meta_trigger(MetaTrigger* trigger) { bool tune_meta_trigger(MetaTrigger* trigger) {
_assert_(trigger); _assert_(trigger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
mtrigger_ = trigger; mtrigger_ = trigger;
return true; return true;
} }
/** /**
* Set the optional features. * Set the optional features.
* @param opts the optional features by bitwise-or: DirDB::TCOMPRESS to c ompress each record. * @param opts the optional features by bitwise-or: DirDB::TCOMPRESS to c ompress each record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_options(int8_t opts) { bool tune_options(int8_t opts) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
opts_ = opts; opts_ = opts;
return true; return true;
} }
/** /**
* Set the data compressor. * Set the data compressor.
* @param comp the data compressor object. * @param comp the data compressor object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_compressor(Compressor* comp) { bool tune_compressor(Compressor* comp) {
_assert_(comp); _assert_(comp);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
embcomp_ = comp; embcomp_ = comp;
return true; return true;
} }
/** /**
* Get the opaque data. * Get the opaque data.
* @return the pointer to the opaque data region, whose size is 16 bytes. * @return the pointer to the opaque data region, whose size is 16 bytes.
*/ */
char* opaque() { char* opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return opaque_; return opaque_;
} }
/** /**
* Synchronize the opaque data. * Synchronize the opaque data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_opaque() { bool synchronize_opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (!dump_opaque()) err = true; if (!dump_opaque()) err = true;
return !err; return !err;
} }
/** /**
* Get the status flags. * Get the status flags.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
uint8_t flags() { uint8_t flags() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return 0; return 0;
} }
protected: protected:
/** /**
* Report a message for debugging. * Report a message for debugging.
* @param file the file name of the program source code. * @param file the file name of the program source code.
skipping to change at line 1287 skipping to change at line 1287
_assert_(message); _assert_(message);
if (mtrigger_) mtrigger_->trigger(kind, message); if (mtrigger_) mtrigger_->trigger(kind, message);
} }
/** /**
* Set the database type. * Set the database type.
* @param type the database type. * @param type the database type.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_type(int8_t type) { bool tune_type(int8_t type) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
type_ = type; type_ = type;
return true; return true;
} }
/** /**
* Get the library version. * Get the library version.
* @return the library version, or 0 on failure. * @return the library version, or 0 on failure.
*/ */
uint8_t libver() { uint8_t libver() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return libver_; return libver_;
} }
/** /**
* Get the library revision. * Get the library revision.
* @return the library revision, or 0 on failure. * @return the library revision, or 0 on failure.
*/ */
uint8_t librev() { uint8_t librev() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return librev_; return librev_;
} }
/** /**
* Get the format version. * Get the format version.
* @return the format version, or 0 on failure. * @return the format version, or 0 on failure.
*/ */
uint8_t fmtver() { uint8_t fmtver() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return fmtver_; return fmtver_;
} }
/** /**
* Get the module checksum. * Get the module checksum.
* @return the module checksum, or 0 on failure. * @return the module checksum, or 0 on failure.
*/ */
uint8_t chksum() { uint8_t chksum() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return chksum_; return chksum_;
} }
/** /**
* Get the database type. * Get the database type.
* @return the database type, or 0 on failure. * @return the database type, or 0 on failure.
*/ */
uint8_t type() { uint8_t type() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return type_; return type_;
} }
/** /**
* Get the options. * Get the options.
* @return the options, or 0 on failure. * @return the options, or 0 on failure.
*/ */
uint8_t opts() { uint8_t opts() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return opts_; return opts_;
} }
/** /**
* Get the data compressor. * Get the data compressor.
* @return the data compressor, or NULL on failure. * @return the data compressor, or NULL on failure.
*/ */
Compressor* comp() { Compressor* comp() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return comp_; return comp_;
} }
/** /**
* Check whether the database was recovered or not. * Check whether the database was recovered or not.
* @return true if recovered, or false if not. * @return true if recovered, or false if not.
*/ */
bool recovered() { bool recovered() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return recov_; return recov_;
} }
/** /**
* Check whether the database was reorganized or not. * Check whether the database was reorganized or not.
* @return true if reorganized, or false if not. * @return true if reorganized, or false if not.
*/ */
bool reorganized() { bool reorganized() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return reorg_; return reorg_;
} }
private: private:
/** /**
* Set the power of the alignment of record size. * Set the power of the alignment of record size.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
skipping to change at line 2223 skipping to change at line 2223
* @return the size of the database file in bytes. * @return the size of the database file in bytes.
*/ */
int64_t size_impl() { int64_t size_impl() {
return size_ + count_ * RECUNITSIZ; return size_ + count_ * RECUNITSIZ;
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
DirDB(const DirDB&); DirDB(const DirDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
DirDB& operator =(const DirDB&); DirDB& operator =(const DirDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; RWLock mlock_;
/** The record locks. */ /** The record locks. */
SlottedRWLock rlock_; SlottedRWLock rlock_;
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The internal logger. */ /** The internal logger. */
Logger* logger_; Logger* logger_;
/** The kinds of logged messages. */ /** The kinds of logged messages. */
uint32_t logkinds_; uint32_t logkinds_;
/** The internal meta operation trigger. */ /** The internal meta operation trigger. */
MetaTrigger* mtrigger_; MetaTrigger* mtrigger_;
 End of changes. 42 change blocks. 
42 lines changed or deleted 42 lines changed or added


 kchashdb.h   kchashdb.h 
skipping to change at line 138 skipping to change at line 138
*/ */
class Cursor : public BasicDB::Cursor { class Cursor : public BasicDB::Cursor {
friend class HashDB; friend class HashDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(HashDB* db) : db_(db), off_(0), end_(0) { explicit Cursor(HashDB* db) : db_(db), off_(0), end_(0) {
_assert_(db); _assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true); _assert_(true);
if (!db_) return; if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.remove(this); db_->curs_.remove(this);
} }
/** /**
* Accept a visitor to the current record. * Accept a visitor to the current record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation. * @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. * @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and othe r threads accessing * @note The operation for each record is performed atomically and othe r threads accessing
* the same record are blocked. To avoid deadlock, any explicit databa se operation must not * the same record are blocked. To avoid deadlock, any explicit databa se operation must not
* be performed in this function. * be performed in this function.
*/ */
bool accept(Visitor* visitor, bool writable = true, bool step = false) { bool accept(Visitor* visitor, bool writable = true, bool step = false) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable) { if (writable) {
if (!db_->writer_) { if (!db_->writer_) {
db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (!(db_->flags_ & FOPEN) && !db_->autotran_ && !db_->tran_ && if (!(db_->flags_ & FOPEN) && !db_->autotran_ && !db_->tran_ &&
skipping to change at line 281 skipping to change at line 281
db_->frgcnt_ -= db_->dfunit_; db_->frgcnt_ -= db_->dfunit_;
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record for forward scan. * Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump() { bool jump() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
off_ = 0; off_ = 0;
if (db_->lsiz_ <= db_->roff_) { if (db_->lsiz_ <= db_->roff_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
off_ = db_->roff_; off_ = db_->roff_;
skipping to change at line 303 skipping to change at line 303
return true; return true;
} }
/** /**
* Jump the cursor to a record for forward scan. * Jump the cursor to a record for forward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump(const char* kbuf, size_t ksiz) { bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
off_ = 0; off_ = 0;
uint64_t hash = db_->hash_record(kbuf, ksiz); uint64_t hash = db_->hash_record(kbuf, ksiz);
uint32_t pivot = db_->fold_hash(hash); uint32_t pivot = db_->fold_hash(hash);
int64_t bidx = hash % db_->bnum_; int64_t bidx = hash % db_->bnum_;
int64_t off = db_->get_bucket(bidx); int64_t off = db_->get_bucket(bidx);
if (off < 0) return false; if (off < 0) return false;
skipping to change at line 367 skipping to change at line 367
bool jump(const std::string& key) { bool jump(const std::string& key) {
_assert_(true); _assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), key.size());
} }
/** /**
* Jump the cursor to the last record for backward scan. * Jump the cursor to the last record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back() { bool jump_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const char* kbuf, size_t ksiz) { bool jump_back(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const std::string& key) { bool jump_back(const std::string& key) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Step the cursor to the next record. * Step the cursor to the next record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step() { bool step() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (off_ < 1) { if (off_ < 1) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
bool err = false; bool err = false;
Record rec; Record rec;
skipping to change at line 434 skipping to change at line 434
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Step the cursor to the previous record. * Step the cursor to the previous record.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool step_back() { bool step_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Get the database object. * Get the database object.
* @return the database object. * @return the database object.
skipping to change at line 585 skipping to change at line 585
uint32_t pivot = fold_hash(hash); uint32_t pivot = fold_hash(hash);
int64_t bidx = hash % bnum_; int64_t bidx = hash % bnum_;
size_t lidx = bidx % RLOCKSLOT; size_t lidx = bidx % RLOCKSLOT;
if (writable) { if (writable) {
rlock_.lock_writer(lidx); rlock_.lock_writer(lidx);
} else { } else {
rlock_.lock_reader(lidx); rlock_.lock_reader(lidx);
} }
if (!accept_impl(kbuf, ksiz, visitor, bidx, pivot, false)) err = true; if (!accept_impl(kbuf, ksiz, visitor, bidx, pivot, false)) err = true;
rlock_.unlock(lidx); rlock_.unlock(lidx);
if (!err && dfunit_ > 0 && frgcnt_ >= dfunit_ && mlock_.promote()) { mlock_.unlock();
if (!err && dfunit_ > 0 && frgcnt_ >= dfunit_ && mlock_.lock_writer_try
()) {
int64_t unit = frgcnt_; int64_t unit = frgcnt_;
if (unit >= dfunit_) { if (unit >= dfunit_) {
if (unit > DFRGMAX) unit = DFRGMAX; if (unit > DFRGMAX) unit = DFRGMAX;
if (!defrag_impl(unit * DFRGCEF)) err = true; if (!defrag_impl(unit * DFRGCEF)) err = true;
frgcnt_ -= unit; frgcnt_ -= unit;
} }
mlock_.unlock();
} }
mlock_.unlock();
return !err; return !err;
} }
/** /**
* Accept a visitor to multiple records at once. * Accept a visitor to multiple records at once.
* @param keys specifies a string vector of the keys. * @param keys specifies a string vector of the keys.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operations for specified records are performed atomically an d other threads * @note The operations for specified records are performed atomically an d other threads
* accessing the same records are blocked. To avoid deadlock, any explic it database operation * accessing the same records are blocked. To avoid deadlock, any explic it database operation
skipping to change at line 676 skipping to change at line 677
break; break;
} }
} }
lit = lidxs.begin(); lit = lidxs.begin();
litend = lidxs.end(); litend = lidxs.end();
while (lit != litend) { while (lit != litend) {
rlock_.unlock(*lit); rlock_.unlock(*lit);
++lit; ++lit;
} }
delete[] rkeys; delete[] rkeys;
if (!err && dfunit_ > 0 && frgcnt_ >= dfunit_ && mlock_.promote()) { visitor->visit_after();
mlock_.unlock();
if (!err && dfunit_ > 0 && frgcnt_ >= dfunit_ && mlock_.lock_writer_try
()) {
int64_t unit = frgcnt_; int64_t unit = frgcnt_;
if (unit >= dfunit_) { if (unit >= dfunit_) {
if (unit > DFRGMAX) unit = DFRGMAX; if (unit > DFRGMAX) unit = DFRGMAX;
if (!defrag_impl(unit * DFRGCEF)) err = true; if (!defrag_impl(unit * DFRGCEF)) err = true;
frgcnt_ -= unit; frgcnt_ -= unit;
} }
mlock_.unlock();
} }
visitor->visit_after();
mlock_.unlock();
return !err; return !err;
} }
/** /**
* Iterate to accept a visitor for each record. * Iterate to accept a visitor for each record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @param checker a progress checker object. If it is NULL, no checking is performed. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads ar e blocked. To avoid * @note The whole iteration is performed atomically and other threads ar e blocked. To avoid
* deadlock, any explicit database operation must not be performed in thi s function. * deadlock, any explicit database operation must not be performed in thi s function.
*/ */
bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) { bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable) { if (writable) {
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (!(flags_ & FOPEN) && !autotran_ && !tran_ && !set_flag(FOPEN, tru e)) { if (!(flags_ & FOPEN) && !autotran_ && !tran_ && !set_flag(FOPEN, tru e)) {
skipping to change at line 769 skipping to change at line 771
* HashDB::OTRYLOCK, which means locking is performed without blocking, H ashDB::ONOREPAIR, * HashDB::OTRYLOCK, which means locking is performed without blocking, H ashDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is * which means the database file is not repaired implicitly even if file destruction is
* detected. * detected.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Every opened database must be closed by the HashDB::close method when it is no * @note Every opened database must be closed by the HashDB::close method when it is no
* longer in use. It is not allowed for two or more database objects in the same process to * 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. * keep their connections to the same database file at the same time.
*/ */
bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str());
writer_ = false; writer_ = false;
autotran_ = false; autotran_ = false;
autosync_ = false; autosync_ = false;
reorg_ = false; reorg_ = false;
trim_ = false; trim_ = false;
skipping to change at line 903 skipping to change at line 905
omode_ = mode; omode_ = mode;
trigger_meta(MetaTrigger::OPEN, "open"); trigger_meta(MetaTrigger::OPEN, "open");
return true; return true;
} }
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close() { bool close() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str()); report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str());
bool err = false; bool err = false;
if (tran_ && !abort_transaction()) err = true; if (tran_ && !abort_transaction()) err = true;
disable_cursors(); disable_cursors();
if (writer_) { if (writer_) {
if (!dump_free_blocks()) err = true; if (!dump_free_blocks()) err = true;
skipping to change at line 940 skipping to change at line 942
* @param proc a postprocessor object. If it is NULL, no postprocessing is performed. * @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. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the postprocessor is performed atomically and o ther threads accessing * @note The operation of the postprocessor is performed atomically and o ther threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool synchronize(bool hard = false, FileProcessor* proc = NULL, bool synchronize(bool hard = false, FileProcessor* proc = NULL,
ProgressChecker* checker = NULL) { ProgressChecker* checker = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
rlock_.lock_reader_all(); rlock_.lock_reader_all();
bool err = false; bool err = false;
if (!synchronize_impl(hard, proc, checker)) err = true; if (!synchronize_impl(hard, proc, checker)) err = true;
trigger_meta(MetaTrigger::SYNCHRONIZE, "synchronize"); trigger_meta(MetaTrigger::SYNCHRONIZE, "synchronize");
rlock_.unlock_all(); rlock_.unlock_all();
return !err; return !err;
skipping to change at line 963 skipping to change at line 965
* Occupy database by locking and do something meanwhile. * Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock. * @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 perfo rmed. * @param proc a processor object. If it is NULL, no processing is perfo rmed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing * @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 * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool occupy(bool writable = true, FileProcessor* proc = NULL) { bool occupy(bool writable = true, FileProcessor* proc = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, writable); ScopedRWLock lock(&mlock_, writable);
bool err = false; bool err = false;
if (proc && !proc->process(path_, count_, lsiz_)) { if (proc && !proc->process(path_, count_, lsiz_)) {
set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); set_error(_KCCODELINE_, Error::LOGIC, "processing failed");
err = true; err = true;
} }
trigger_meta(MetaTrigger::OCCUPY, "occupy"); trigger_meta(MetaTrigger::OCCUPY, "occupy");
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
skipping to change at line 1053 skipping to change at line 1055
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* End transaction. * End transaction.
* @param commit true to commit the transaction, or false to abort the tr ansaction. * @param commit true to commit the transaction, or false to abort the tr ansaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool end_transaction(bool commit = true) { bool end_transaction(bool commit = true) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); set_error(_KCCODELINE_, Error::INVALID, "not in transaction");
return false; return false;
} }
bool err = false; bool err = false;
if (commit) { if (commit) {
skipping to change at line 1078 skipping to change at line 1080
tran_ = false; tran_ = false;
trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction");
return !err; return !err;
} }
/** /**
* Remove all records. * Remove all records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clear() { bool clear() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
disable_cursors(); disable_cursors();
if (!file_.truncate(HEADSIZ)) { if (!file_.truncate(HEADSIZ)) {
skipping to change at line 1118 skipping to change at line 1120
if (!autotran_ && !set_flag(FOPEN, true)) err = true; if (!autotran_ && !set_flag(FOPEN, true)) err = true;
trigger_meta(MetaTrigger::CLEAR, "clear"); trigger_meta(MetaTrigger::CLEAR, "clear");
return true; return true;
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count() { int64_t count() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return count_; return count_;
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure. * @return the size of the database file in bytes, or -1 on failure.
*/ */
int64_t size() { int64_t size() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return lsiz_; return lsiz_;
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
std::string path() { std::string path() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return ""; return "";
} }
return path_; return path_;
} }
/** /**
* Get the miscellaneous status information. * Get the miscellaneous status information.
* @param strmap a string map to contain the result. * @param strmap a string map to contain the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool status(std::map<std::string, std::string>* strmap) { bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap); _assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = strprintf("%u", (unsigned)TYPEHASH); (*strmap)["type"] = strprintf("%u", (unsigned)TYPEHASH);
(*strmap)["realtype"] = strprintf("%u", (unsigned)type_); (*strmap)["realtype"] = strprintf("%u", (unsigned)type_);
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
(*strmap)["libver"] = strprintf("%u", libver_); (*strmap)["libver"] = strprintf("%u", libver_);
(*strmap)["librev"] = strprintf("%u", librev_); (*strmap)["librev"] = strprintf("%u", librev_);
(*strmap)["fmtver"] = strprintf("%u", fmtver_); (*strmap)["fmtver"] = strprintf("%u", fmtver_);
skipping to change at line 1226 skipping to change at line 1228
* @param file the file name of the program source code. * @param file the file name of the program source code.
* @param line the line number 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 func the function name of the program source code.
* @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal * @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal
* information, Logger::WARN for warning, and Logger::ERROR for fatal err or. * information, Logger::WARN for warning, and Logger::ERROR for fatal err or.
* @param message the supplement message. * @param message the supplement message.
*/ */
void log(const char* file, int32_t line, const char* func, Logger::Kind k ind, void log(const char* file, int32_t line, const char* func, Logger::Kind k ind,
const char* message) { const char* message) {
_assert_(file && line > 0 && func && message); _assert_(file && line > 0 && func && message);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (!logger_) return; if (!logger_) return;
logger_->log(file, line, func, kind, message); logger_->log(file, line, func, kind, message);
} }
/** /**
* Set the internal logger. * Set the internal logger.
* @param logger the logger object. * @param logger the logger object.
* @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging,
* Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal * Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal
* error. * error.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) { bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) {
_assert_(logger); _assert_(logger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
logger_ = logger; logger_ = logger;
logkinds_ = kinds; logkinds_ = kinds;
return true; return true;
} }
/** /**
* Set the internal meta operation trigger. * Set the internal meta operation trigger.
* @param trigger the trigger object. * @param trigger the trigger object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_meta_trigger(MetaTrigger* trigger) { bool tune_meta_trigger(MetaTrigger* trigger) {
_assert_(trigger); _assert_(trigger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
mtrigger_ = trigger; mtrigger_ = trigger;
return true; return true;
} }
/** /**
* Set the power of the alignment of record size. * Set the power of the alignment of record size.
* @param apow the power of the alignment of record size. * @param apow the power of the alignment of record size.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_alignment(int8_t apow) { bool tune_alignment(int8_t apow) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
apow_ = apow >= 0 ? apow : DEFAPOW; apow_ = apow >= 0 ? apow : DEFAPOW;
if (apow_ > MAXAPOW) apow_ = MAXAPOW; if (apow_ > MAXAPOW) apow_ = MAXAPOW;
return true; return true;
} }
/** /**
* Set the power of the capacity of the free block pool. * Set the power of the capacity of the free block pool.
* @param fpow the power of the capacity of the free block pool. * @param fpow the power of the capacity of the free block pool.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_fbp(int8_t fpow) { bool tune_fbp(int8_t fpow) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
fpow_ = fpow >= 0 ? fpow : DEFFPOW; fpow_ = fpow >= 0 ? fpow : DEFFPOW;
if (fpow_ > MAXFPOW) fpow_ = MAXFPOW; if (fpow_ > MAXFPOW) fpow_ = MAXFPOW;
return true; return true;
} }
/** /**
* Set the optional features. * Set the optional features.
* @param opts the optional features by bitwise-or: HashDB::TSMALL to use 32-bit addressing, * @param opts the optional features by bitwise-or: HashDB::TSMALL to use 32-bit addressing,
* HashDB::TLINEAR to use linear collision chaining, HashDB::TCOMPRESS to compress each record. * HashDB::TLINEAR to use linear collision chaining, HashDB::TCOMPRESS to compress each record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_options(int8_t opts) { bool tune_options(int8_t opts) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
opts_ = opts; opts_ = opts;
return true; return true;
} }
/** /**
* Set the number of buckets of the hash table. * Set the number of buckets of the hash table.
* @param bnum the number of buckets of the hash table. * @param bnum the number of buckets of the hash table.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_buckets(int64_t bnum) { bool tune_buckets(int64_t bnum) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum > 0 ? bnum : DEFBNUM; bnum_ = bnum > 0 ? bnum : DEFBNUM;
if (bnum_ > INT16MAX) bnum_ = nearbyprime(bnum_); if (bnum_ > INT16MAX) bnum_ = nearbyprime(bnum_);
return true; return true;
} }
/** /**
* Set the size of the internal memory-mapped region. * Set the size of the internal memory-mapped region.
* @param msiz the size of the internal memory-mapped region. * @param msiz the size of the internal memory-mapped region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_map(int64_t msiz) { bool tune_map(int64_t msiz) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
msiz_ = msiz >= 0 ? msiz : DEFMSIZ; msiz_ = msiz >= 0 ? msiz : DEFMSIZ;
return true; return true;
} }
/** /**
* Set the unit step number of auto defragmentation. * Set the unit step number of auto defragmentation.
* @param dfunit the unit step number of auto defragmentation. * @param dfunit the unit step number of auto defragmentation.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_defrag(int64_t dfunit) { bool tune_defrag(int64_t dfunit) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
dfunit_ = dfunit > 0 ? dfunit : 0; dfunit_ = dfunit > 0 ? dfunit : 0;
return true; return true;
} }
/** /**
* Set the data compressor. * Set the data compressor.
* @param comp the data compressor object. * @param comp the data compressor object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_compressor(Compressor* comp) { bool tune_compressor(Compressor* comp) {
_assert_(comp); _assert_(comp);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
embcomp_ = comp; embcomp_ = comp;
return true; return true;
} }
/** /**
* Get the opaque data. * Get the opaque data.
* @return the pointer to the opaque data region, whose size is 16 bytes. * @return the pointer to the opaque data region, whose size is 16 bytes.
*/ */
char* opaque() { char* opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return opaque_; return opaque_;
} }
/** /**
* Synchronize the opaque data. * Synchronize the opaque data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_opaque() { bool synchronize_opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (!dump_opaque()) err = true; if (!dump_opaque()) err = true;
return !err; return !err;
} }
/** /**
* Perform defragmentation of the file. * Perform defragmentation of the file.
* @param step the number of steps. If it is not more than 0, the whole region is defraged. * @param step the number of steps. If it is not more than 0, the whole region is defraged.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool defrag(int64_t step = 0) { bool defrag(int64_t step = 0) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (step > 0) { if (step > 0) {
skipping to change at line 1437 skipping to change at line 1439
} }
frgcnt_ = 0; frgcnt_ = 0;
return !err; return !err;
} }
/** /**
* Get the status flags. * Get the status flags.
* @return the status flags, or 0 on failure. * @return the status flags, or 0 on failure.
*/ */
uint8_t flags() { uint8_t flags() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return flags_; return flags_;
} }
protected: protected:
/** /**
* Report a message for debugging. * Report a message for debugging.
* @param file the file name of the program source code. * @param file the file name of the program source code.
skipping to change at line 1525 skipping to change at line 1527
_assert_(message); _assert_(message);
if (mtrigger_) mtrigger_->trigger(kind, message); if (mtrigger_) mtrigger_->trigger(kind, message);
} }
/** /**
* Set the database type. * Set the database type.
* @param type the database type. * @param type the database type.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_type(int8_t type) { bool tune_type(int8_t type) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
type_ = type; type_ = type;
return true; return true;
} }
/** /**
* Get the library version. * Get the library version.
* @return the library version, or 0 on failure. * @return the library version, or 0 on failure.
*/ */
uint8_t libver() { uint8_t libver() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return libver_; return libver_;
} }
/** /**
* Get the library revision. * Get the library revision.
* @return the library revision, or 0 on failure. * @return the library revision, or 0 on failure.
*/ */
uint8_t librev() { uint8_t librev() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return librev_; return librev_;
} }
/** /**
* Get the format version. * Get the format version.
* @return the format version, or 0 on failure. * @return the format version, or 0 on failure.
*/ */
uint8_t fmtver() { uint8_t fmtver() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return fmtver_; return fmtver_;
} }
/** /**
* Get the module checksum. * Get the module checksum.
* @return the module checksum, or 0 on failure. * @return the module checksum, or 0 on failure.
*/ */
uint8_t chksum() { uint8_t chksum() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return chksum_; return chksum_;
} }
/** /**
* Get the database type. * Get the database type.
* @return the database type, or 0 on failure. * @return the database type, or 0 on failure.
*/ */
uint8_t type() { uint8_t type() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return type_; return type_;
} }
/** /**
* Get the alignment power. * Get the alignment power.
* @return the alignment power, or 0 on failure. * @return the alignment power, or 0 on failure.
*/ */
uint8_t apow() { uint8_t apow() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return apow_; return apow_;
} }
/** /**
* Get the free block pool power. * Get the free block pool power.
* @return the free block pool power, or 0 on failure. * @return the free block pool power, or 0 on failure.
*/ */
uint8_t fpow() { uint8_t fpow() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return fpow_; return fpow_;
} }
/** /**
* Get the options. * Get the options.
* @return the options, or 0 on failure. * @return the options, or 0 on failure.
*/ */
uint8_t opts() { uint8_t opts() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return opts_; return opts_;
} }
/** /**
* Get the bucket number. * Get the bucket number.
* @return the bucket number, or 0 on failure. * @return the bucket number, or 0 on failure.
*/ */
int64_t bnum() { int64_t bnum() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return bnum_; return bnum_;
} }
/** /**
* Get the size of the internal memory-mapped region. * Get the size of the internal memory-mapped region.
* @return the size of the internal memory-mapped region, or 0 on failure . * @return the size of the internal memory-mapped region, or 0 on failure .
*/ */
int64_t msiz() { int64_t msiz() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return msiz_; return msiz_;
} }
/** /**
* Get the unit step number of auto defragmentation. * Get the unit step number of auto defragmentation.
* @return the unit step number of auto defragmentation, or 0 on failure. * @return the unit step number of auto defragmentation, or 0 on failure.
*/ */
int64_t dfunit() { int64_t dfunit() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return dfunit_; return dfunit_;
} }
/** /**
* Get the data compressor. * Get the data compressor.
* @return the data compressor, or NULL on failure. * @return the data compressor, or NULL on failure.
*/ */
Compressor* comp() { Compressor* comp() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return comp_; return comp_;
} }
/** /**
* Check whether the database was recovered or not. * Check whether the database was recovered or not.
* @return true if recovered, or false if not. * @return true if recovered, or false if not.
*/ */
bool recovered() { bool recovered() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return file_.recovered(); return file_.recovered();
} }
/** /**
* Check whether the database was reorganized or not. * Check whether the database was reorganized or not.
* @return true if reorganized, or false if not. * @return true if reorganized, or false if not.
*/ */
bool reorganized() { bool reorganized() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return reorg_; return reorg_;
} }
private: private:
/** /**
* Record data. * Record data.
*/ */
skipping to change at line 2262 skipping to change at line 2264
} }
if (writer_ && !autotran_ && !set_flag(FOPEN, true)) err = true; if (writer_ && !autotran_ && !set_flag(FOPEN, true)) err = true;
return !err; return !err;
} }
/** /**
* Synchronize meta data with the file and the device. * Synchronize meta data with the file and the device.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_meta() { bool synchronize_meta() {
_assert_(true); _assert_(true);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
bool err = false; bool err = false;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!file_.synchronize(true)) { if (!file_.synchronize(true)) {
set_error(_KCCODELINE_, Error::SYSTEM, file_.error()); set_error(_KCCODELINE_, Error::SYSTEM, file_.error());
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Perform defragmentation. * Perform defragmentation.
skipping to change at line 3185 skipping to change at line 3187
} }
return true; return true;
} }
/** /**
* Insert a free block to the free block pool. * Insert a free block to the free block pool.
* @param off the offset of the free block. * @param off the offset of the free block.
* @param rsiz the size of the free block. * @param rsiz the size of the free block.
*/ */
void insert_free_block(int64_t off, size_t rsiz) { void insert_free_block(int64_t off, size_t rsiz) {
_assert_(off >= 0); _assert_(off >= 0);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
escape_cursors(off, off + rsiz); escape_cursors(off, off + rsiz);
if (fbpnum_ < 1) return; if (fbpnum_ < 1) return;
if (fbp_.size() >= (size_t)fbpnum_) { if (fbp_.size() >= (size_t)fbpnum_) {
FBP::const_iterator it = fbp_.begin(); FBP::const_iterator it = fbp_.begin();
if (rsiz <= it->rsiz) return; if (rsiz <= it->rsiz) return;
fbp_.erase(it); fbp_.erase(it);
} }
FreeBlock fb = { off, rsiz }; FreeBlock fb = { off, rsiz };
fbp_.insert(fb); fbp_.insert(fb);
} }
/** /**
* Fetch the free block pool from a decent sized block. * Fetch the free block pool from a decent sized block.
* @param rsiz the minimum size of the block. * @param rsiz the minimum size of the block.
* @param res the structure for the result. * @param res the structure for the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool fetch_free_block(size_t rsiz, FreeBlock* res) { bool fetch_free_block(size_t rsiz, FreeBlock* res) {
_assert_(res); _assert_(res);
if (fbpnum_ < 1) return false; if (fbpnum_ < 1) return false;
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
FreeBlock fb = { INT64MAX, rsiz }; FreeBlock fb = { INT64MAX, rsiz };
FBP::const_iterator it = fbp_.upper_bound(fb); FBP::const_iterator it = fbp_.upper_bound(fb);
if (it == fbp_.end()) return false; if (it == fbp_.end()) return false;
res->off = it->off; res->off = it->off;
res->rsiz = it->rsiz; res->rsiz = it->rsiz;
fbp_.erase(it); fbp_.erase(it);
escape_cursors(res->off, res->off + res->rsiz); escape_cursors(res->off, res->off + res->rsiz);
return true; return true;
} }
/** /**
skipping to change at line 3589 skipping to change at line 3591
disable_cursors(); disable_cursors();
fbp_.clear(); fbp_.clear();
atlock_.unlock(); atlock_.unlock();
return !err; return !err;
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
HashDB(const HashDB&); HashDB(const HashDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
HashDB& operator =(const HashDB&); HashDB& operator =(const HashDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; RWLock mlock_;
/** The record locks. */ /** The record locks. */
SlottedRWLock rlock_; SlottedRWLock rlock_;
/** The file lock. */ /** The file lock. */
SpinLock flock_; Mutex flock_;
/** The auto transaction lock. */ /** The auto transaction lock. */
Mutex atlock_; Mutex atlock_;
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The internal logger. */ /** The internal logger. */
Logger* logger_; Logger* logger_;
/** The kinds of logged messages. */ /** The kinds of logged messages. */
uint32_t logkinds_; uint32_t logkinds_;
/** The internal meta operation trigger. */ /** The internal meta operation trigger. */
MetaTrigger* mtrigger_; MetaTrigger* mtrigger_;
 End of changes. 61 change blocks. 
60 lines changed or deleted 64 lines changed or added


 kcplantdb.h   kcplantdb.h 
skipping to change at line 403 skipping to change at line 403
*/ */
bool set_position(int64_t id) { bool set_position(int64_t id) {
_assert_(true); _assert_(true);
while (id > 0) { while (id > 0) {
LeafNode* node = db_->load_leaf_node(id, false); LeafNode* node = db_->load_leaf_node(id, false);
if (!node) { if (!node) {
db_->set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node"); db_->set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node");
db_->db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long )id); db_->db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long )id);
return false; return false;
} }
ScopedSpinRWLock lock(&node->lock, false); ScopedRWLock lock(&node->lock, false);
RecordArray& recs = node->recs; RecordArray& recs = node->recs;
if (!recs.empty()) { if (!recs.empty()) {
set_position(recs.front(), id); set_position(recs.front(), id);
return true; return true;
} else { } else {
id = node->next; id = node->next;
} }
} }
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
skipping to change at line 429 skipping to change at line 429
*/ */
bool set_position_back(int64_t id) { bool set_position_back(int64_t id) {
_assert_(true); _assert_(true);
while (id > 0) { while (id > 0) {
LeafNode* node = db_->load_leaf_node(id, false); LeafNode* node = db_->load_leaf_node(id, false);
if (!node) { if (!node) {
db_->set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node"); db_->set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node");
db_->db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long )id); db_->db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long )id);
return false; return false;
} }
ScopedSpinRWLock lock(&node->lock, false); ScopedRWLock lock(&node->lock, false);
RecordArray& recs = node->recs; RecordArray& recs = node->recs;
if (!recs.empty()) { if (!recs.empty()) {
set_position(recs.back(), id); set_position(recs.back(), id);
return true; return true;
} else { } else {
id = node->prev; id = node->prev;
} }
} }
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
skipping to change at line 1346 skipping to change at line 1346
if (!writer_) { if (!writer_) {
if (!db_.close()) return false; if (!db_.close()) return false;
uint32_t tmode = (mode & ~OREADER) | OWRITER; uint32_t tmode = (mode & ~OREADER) | OWRITER;
if (!db_.open(path, tmode)) return false; if (!db_.open(path, tmode)) return false;
} }
if (!recalc_count()) return false; if (!recalc_count()) return false;
if (!writer_) { if (!writer_) {
if (!db_.close()) return false; if (!db_.close()) return false;
if (!db_.open(path, mode)) return false; if (!db_.open(path, mode)) return false;
} }
if (count_ == INT64MAX && !reorganize_file(mode)) return false;
} }
if (writer_ && db_.count() < 1) { if (writer_ && db_.count() < 1) {
root_ = 0; root_ = 0;
first_ = 0; first_ = 0;
last_ = 0; last_ = 0;
count_ = 0; count_ = 0;
create_leaf_cache(); create_leaf_cache();
create_inner_cache(); create_inner_cache();
lcnt_ = 0; lcnt_ = 0;
create_leaf_node(0, 0); create_leaf_node(0, 0);
skipping to change at line 2129 skipping to change at line 2130
_assert_(true); _assert_(true);
char* akbuf = (char*)a + sizeof(*a); char* akbuf = (char*)a + sizeof(*a);
char* bkbuf = (char*)b + sizeof(*b); char* bkbuf = (char*)b + sizeof(*b);
return comp->compare(akbuf, a->ksiz, bkbuf, b->ksiz) < 0; return comp->compare(akbuf, a->ksiz, bkbuf, b->ksiz) < 0;
} }
}; };
/** /**
* Leaf node of B+ tree. * Leaf node of B+ tree.
*/ */
struct LeafNode { struct LeafNode {
SpinRWLock lock; ///< lock RWLock lock; ///< lock
int64_t id; ///< page ID number int64_t id; ///< page ID number
RecordArray recs; ///< sorted array of records RecordArray recs; ///< sorted array of records
int64_t size; ///< total size of records int64_t size; ///< total size of records
int64_t prev; ///< previous leaf node int64_t prev; ///< previous leaf node
int64_t next; ///< next leaf node int64_t next; ///< next leaf node
bool hot; ///< whether in the hot cache bool hot; ///< whether in the hot cache
bool dirty; ///< whether to be written back bool dirty; ///< whether to be written back
bool dead; ///< whether to be removed bool dead; ///< whether to be removed
}; };
/** /**
skipping to change at line 2167 skipping to change at line 2168
_assert_(true); _assert_(true);
char* akbuf = (char*)a + sizeof(*a); char* akbuf = (char*)a + sizeof(*a);
char* bkbuf = (char*)b + sizeof(*b); char* bkbuf = (char*)b + sizeof(*b);
return comp->compare(akbuf, a->ksiz, bkbuf, b->ksiz) < 0; return comp->compare(akbuf, a->ksiz, bkbuf, b->ksiz) < 0;
} }
}; };
/** /**
* Inner node of B+ tree. * Inner node of B+ tree.
*/ */
struct InnerNode { struct InnerNode {
SpinRWLock lock; ///< lock RWLock lock; ///< lock
int64_t id; ///< page ID numger int64_t id; ///< page ID numger
int64_t heir; ///< child before the first link int64_t heir; ///< child before the first link
LinkArray links; ///< sorted array of links LinkArray links; ///< sorted array of links
int64_t size; ///< total size of links int64_t size; ///< total size of links
bool dirty; ///< whether to be written back bool dirty; ///< whether to be written back
bool dead; ///< whether to be removed bool dead; ///< whether to be removed
}; };
/** /**
* Slot cache of leaf nodes. * Slot cache of leaf nodes.
*/ */
struct LeafSlot { struct LeafSlot {
Mutex lock; ///< lock Mutex lock; ///< lock
LeafCache* hot; ///< hot cache LeafCache* hot; ///< hot cache
LeafCache* warm; ///< warm cache LeafCache* warm; ///< warm cache
}; };
/** /**
* Slot cache of inner nodes. * Slot cache of inner nodes.
*/ */
struct InnerSlot { struct InnerSlot {
SpinLock lock; ///< lock Mutex lock; ///< lock
InnerCache* warm; ///< warm cache InnerCache* warm; ///< warm cache
}; };
/** /**
* Scoped visitor. * Scoped visitor.
*/ */
class ScopedVisitor { class ScopedVisitor {
public: public:
/** constructor */ /** constructor */
explicit ScopedVisitor(Visitor* visitor) : visitor_(visitor) { explicit ScopedVisitor(Visitor* visitor) : visitor_(visitor) {
_assert_(visitor); _assert_(visitor);
skipping to change at line 2379 skipping to change at line 2380
delete node; delete node;
return !err; return !err;
} }
/** /**
* Save a leaf node. * Save a leaf node.
* @param node the leaf node. * @param node the leaf node.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool save_leaf_node(LeafNode* node) { bool save_leaf_node(LeafNode* node) {
_assert_(node); _assert_(node);
ScopedSpinRWLock lock(&node->lock, false); ScopedRWLock lock(&node->lock, false);
if (!node->dirty) return true; if (!node->dirty) return true;
bool err = false; bool err = false;
char hbuf[NUMBUFSIZ]; char hbuf[NUMBUFSIZ];
size_t hsiz = std::sprintf(hbuf, "%c%llX", LNPREFIX, (long long)node->i d); size_t hsiz = std::sprintf(hbuf, "%c%llX", LNPREFIX, (long long)node->i d);
if (node->dead) { if (node->dead) {
if (!db_.remove(hbuf, hsiz) && db_.error().code() != Error::NOREC) er r = true; if (!db_.remove(hbuf, hsiz) && db_.error().code() != Error::NOREC) er r = true;
} else { } else {
char* rbuf = new char[node->size]; char* rbuf = new char[node->size];
char* wp = rbuf; char* wp = rbuf;
wp += writevarnum(wp, node->prev); wp += writevarnum(wp, node->prev);
skipping to change at line 2696 skipping to change at line 2697
} }
/** /**
* Clean all of the inner cache. * Clean all of the inner cache.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clean_inner_cache() { bool clean_inner_cache() {
_assert_(true); _assert_(true);
bool err = false; bool err = false;
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
InnerSlot* slot = islots_ + i; InnerSlot* slot = islots_ + i;
ScopedSpinLock lock(&slot->lock); ScopedMutex lock(&slot->lock);
typename InnerCache::Iterator it = slot->warm->begin(); typename InnerCache::Iterator it = slot->warm->begin();
typename InnerCache::Iterator itend = slot->warm->end(); typename InnerCache::Iterator itend = slot->warm->end();
while (it != itend) { while (it != itend) {
InnerNode* node = it.value(); InnerNode* node = it.value();
if (!save_inner_node(node)) err = true; if (!save_inner_node(node)) err = true;
++it; ++it;
} }
} }
return !err; return !err;
} }
skipping to change at line 2795 skipping to change at line 2796
} }
/** /**
* Load an inner node. * Load an inner node.
* @param id the ID number of the inner node. * @param id the ID number of the inner node.
* @return the loaded inner node. * @return the loaded inner node.
*/ */
InnerNode* load_inner_node(int64_t id) { InnerNode* load_inner_node(int64_t id) {
_assert_(id > 0); _assert_(id > 0);
int32_t sidx = id % SLOTNUM; int32_t sidx = id % SLOTNUM;
InnerSlot* slot = islots_ + sidx; InnerSlot* slot = islots_ + sidx;
ScopedSpinLock lock(&slot->lock); ScopedMutex lock(&slot->lock);
InnerNode** np = slot->warm->get(id, InnerCache::MLAST); InnerNode** np = slot->warm->get(id, InnerCache::MLAST);
if (np) return *np; if (np) return *np;
char hbuf[NUMBUFSIZ]; char hbuf[NUMBUFSIZ];
size_t hsiz = std::sprintf(hbuf, "%c%llX", INPREFIX, (long long)(id - I NIDBASE)); size_t hsiz = std::sprintf(hbuf, "%c%llX", INPREFIX, (long long)(id - I NIDBASE));
class VisitorImpl : public DB::Visitor { class VisitorImpl : public DB::Visitor {
public: public:
explicit VisitorImpl() : node_(NULL) {} explicit VisitorImpl() : node_(NULL) {}
InnerNode* pop() { InnerNode* pop() {
return node_; return node_;
} }
skipping to change at line 3346 skipping to change at line 3347
return !err; return !err;
} }
/** /**
* Recalculate the count data. * Recalculate the count data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool recalc_count() { bool recalc_count() {
_assert_(true); _assert_(true);
if (!load_meta()) return false; if (!load_meta()) return false;
bool err = false; bool err = false;
std::set<int64_t> ids;
std::set<int64_t> prevs;
std::set<int64_t> nexts;
class VisitorImpl : public DB::Visitor { class VisitorImpl : public DB::Visitor {
public: public:
explicit VisitorImpl() : count_(0) {} explicit VisitorImpl(std::set<int64_t>* ids,
std::set<int64_t>* prevs, std::set<int64_t>* nex
ts) :
ids_(ids), prevs_(prevs), nexts_(nexts), count_(0) {}
int64_t count() { int64_t count() {
return count_; return count_;
} }
private: private:
const char* visit_full(const char* kbuf, size_t ksiz, const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) { const char* vbuf, size_t vsiz, size_t* sp) {
if (ksiz < 2 || kbuf[0] != LNPREFIX) return NOP; if (ksiz < 2 || ksiz >= NUMBUFSIZ || kbuf[0] != LNPREFIX) return NO
P;
kbuf++;
ksiz--;
char tkbuf[NUMBUFSIZ];
memcpy(tkbuf, kbuf, ksiz);
tkbuf[ksiz] = '\0';
int64_t id = atoih(tkbuf);
uint64_t prev; uint64_t prev;
size_t step = readvarnum(vbuf, vsiz, &prev); size_t step = readvarnum(vbuf, vsiz, &prev);
if (step < 1) return NOP; if (step < 1) return NOP;
vbuf += step; vbuf += step;
vsiz -= step; vsiz -= step;
uint64_t next; uint64_t next;
step = readvarnum(vbuf, vsiz, &next); step = readvarnum(vbuf, vsiz, &next);
if (step < 1) return NOP; if (step < 1) return NOP;
vbuf += step; vbuf += step;
vsiz -= step; vsiz -= step;
ids_->insert(id);
if (prev > 0) prevs_->insert(prev);
if (next > 0) nexts_->insert(next);
while (vsiz > 1) { while (vsiz > 1) {
uint64_t rksiz; uint64_t rksiz;
step = readvarnum(vbuf, vsiz, &rksiz); step = readvarnum(vbuf, vsiz, &rksiz);
if (step < 1) break; if (step < 1) break;
vbuf += step; vbuf += step;
vsiz -= step; vsiz -= step;
uint64_t rvsiz; uint64_t rvsiz;
step = readvarnum(vbuf, vsiz, &rvsiz); step = readvarnum(vbuf, vsiz, &rvsiz);
if (step < 1) break; if (step < 1) break;
vbuf += step; vbuf += step;
vsiz -= step; vsiz -= step;
if (vsiz < rksiz + rvsiz) break; if (vsiz < rksiz + rvsiz) break;
vbuf += rksiz; vbuf += rksiz;
vsiz -= rksiz; vsiz -= rksiz;
vbuf += rvsiz; vbuf += rvsiz;
vsiz -= rvsiz; vsiz -= rvsiz;
count_++; count_++;
} }
return NOP; return NOP;
} }
std::set<int64_t>* ids_;
std::set<int64_t>* prevs_;
std::set<int64_t>* nexts_;
int64_t count_; int64_t count_;
} visitor; } visitor(&ids, &prevs, &nexts);
if (!db_.iterate(&visitor, false)) err = true; if (!db_.iterate(&visitor, false)) err = true;
int64_t count = visitor.count(); int64_t count = visitor.count();
db_.report(_KCCODELINE_, Logger::WARN, "recalculated the record count f rom %lld to %lld", db_.report(_KCCODELINE_, Logger::WARN, "recalculated the record count f rom %lld to %lld",
(long long)count_, (long long)count); (long long)count_, (long long)count);
std::set<int64_t>::iterator iitend = ids.end();
std::set<int64_t>::iterator nit = nexts.begin();
std::set<int64_t>::iterator nitend = nexts.end();
while (nit != nitend) {
if (ids.find(*nit) == ids.end()) {
db_.report(_KCCODELINE_, Logger::WARN, "detected missing leaf: %lld
", (long long)*nit);
count = INT64MAX;
}
++nit;
}
std::set<int64_t>::iterator pit = prevs.begin();
std::set<int64_t>::iterator pitend = prevs.end();
while (pit != pitend) {
if (ids.find(*pit) == iitend) {
db_.report(_KCCODELINE_, Logger::WARN, "detected missing leaf: %lld
", (long long)*pit);
count = INT64MAX;
}
++pit;
}
count_ = count; count_ = count;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
return !err; return !err;
} }
/** /**
* Reorganize the database file. * Reorganize the database file.
* @param mode the connection mode of the internal database. * @param mode the connection mode of the internal database.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool reorganize_file(uint32_t mode) { bool reorganize_file(uint32_t mode) {
 End of changes. 16 change blocks. 
11 lines changed or deleted 52 lines changed or added


 kcprotodb.h   kcprotodb.h 
skipping to change at line 73 skipping to change at line 73
*/ */
class Cursor : public BasicDB::Cursor { class Cursor : public BasicDB::Cursor {
friend class ProtoDB; friend class ProtoDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(ProtoDB* db) : db_(db), it_(db->recs_.end()) { explicit Cursor(ProtoDB* db) : db_(db), it_(db->recs_.end()) {
_assert_(db); _assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true); _assert_(true);
if (!db_) return; if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.remove(this); db_->curs_.remove(this);
} }
/** /**
* Accept a visitor to the current record. * Accept a visitor to the current record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation. * @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. * @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and othe r threads accessing * @note The operation for each record is performed atomically and othe r threads accessing
* the same record are blocked. To avoid deadlock, any explicit databa se operation must not * the same record are blocked. To avoid deadlock, any explicit databa se operation must not
* be performed in this function. * be performed in this function.
*/ */
bool accept(Visitor* visitor, bool writable = true, bool step = false) { bool accept(Visitor* visitor, bool writable = true, bool step = false) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->omode_ & OWRITER)) { if (writable && !(db_->omode_ & OWRITER)) {
db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
skipping to change at line 151 skipping to change at line 151
if (step) it_++; if (step) it_++;
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record for forward scan. * Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump() { bool jump() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
it_ = db_->recs_.begin(); it_ = db_->recs_.begin();
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
return true; return true;
} }
/** /**
* Jump the cursor to a record for forward scan. * Jump the cursor to a record for forward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump(const char* kbuf, size_t ksiz) { bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
std::string key(kbuf, ksiz); std::string key(kbuf, ksiz);
search(key); search(key);
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
skipping to change at line 198 skipping to change at line 198
bool jump(const std::string& key) { bool jump(const std::string& key) {
_assert_(true); _assert_(true);
return jump(key.data(), key.size()); return jump(key.data(), key.size());
} }
/** /**
* Jump the cursor to the last record for backward scan. * Jump the cursor to the last record for backward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump_back() { bool jump_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
it_ = db_->recs_.end(); it_ = db_->recs_.end();
if (it_ == db_->recs_.begin()) { if (it_ == db_->recs_.begin()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
if (!iter_back()) { if (!iter_back()) {
skipping to change at line 222 skipping to change at line 222
return true; return true;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump_back(const char* kbuf, size_t ksiz) { bool jump_back(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
std::string key(kbuf, ksiz); std::string key(kbuf, ksiz);
search(key); search(key);
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
if (it_ == db_->recs_.begin()) { if (it_ == db_->recs_.begin()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
skipping to change at line 270 skipping to change at line 270
bool jump_back(const std::string& key) { bool jump_back(const std::string& key) {
_assert_(true); _assert_(true);
return jump_back(key.data(), key.size()); return jump_back(key.data(), key.size());
} }
/** /**
* Step the cursor to the next record. * Step the cursor to the next record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step() { bool step() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
it_++; it_++;
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
skipping to change at line 292 skipping to change at line 292
return false; return false;
} }
return true; return true;
} }
/** /**
* Step the cursor to the previous record. * Step the cursor to the previous record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step_back() { bool step_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (it_ == db_->recs_.begin()) { if (it_ == db_->recs_.begin()) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
it_ = db_->recs_.end(); it_ = db_->recs_.end();
return false; return false;
} }
if (!iter_back()) { if (!iter_back()) {
skipping to change at line 377 skipping to change at line 377
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and other threads accessing the * @note The operation for each record is performed atomically and other threads accessing the
* same record are blocked. To avoid deadlock, any explicit database ope ration must not be * same record are blocked. To avoid deadlock, any explicit database ope ration must not be
* performed in this function. * performed in this function.
*/ */
bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) { bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
if (writable) { if (writable) {
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
std::string key(kbuf, ksiz); std::string key(kbuf, ksiz);
typename STRMAP::iterator it = recs_.find(key); typename STRMAP::iterator it = recs_.find(key);
skipping to change at line 430 skipping to change at line 430
if (tran_) { if (tran_) {
TranLog log(key, value); TranLog log(key, value);
trlogs_.push_back(log); trlogs_.push_back(log);
} }
size_ -= value.size(); size_ -= value.size();
size_ += vsiz; size_ += vsiz;
it->second = std::string(vbuf, vsiz); it->second = std::string(vbuf, vsiz);
} }
} }
} else { } else {
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
std::string key(kbuf, ksiz); std::string key(kbuf, ksiz);
const STRMAP& rrecs = recs_; const STRMAP& rrecs = recs_;
typename STRMAP::const_iterator it = rrecs.find(key); typename STRMAP::const_iterator it = rrecs.find(key);
if (it == rrecs.end()) { if (it == rrecs.end()) {
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_empty(kbuf, ksiz, &vsiz); const char* vbuf = visitor->visit_empty(kbuf, ksiz, &vsiz);
skipping to change at line 470 skipping to change at line 470
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operations for specified records are performed atomically an d other threads * @note The operations for specified records are performed atomically an d other threads
* accessing the same records are blocked. To avoid deadlock, any explic it database operation * accessing the same records are blocked. To avoid deadlock, any explic it database operation
* must not be performed in this function. * must not be performed in this function.
*/ */
bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor,
bool writable = true) { bool writable = true) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
if (keys.empty()) return true; if (keys.empty()) return true;
skipping to change at line 543 skipping to change at line 543
* Iterate to accept a visitor for each record. * Iterate to accept a visitor for each record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @param checker a progress checker object. If it is NULL, no checking is performed. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads ar e blocked. To avoid * @note The whole iteration is performed atomically and other threads ar e blocked. To avoid
* deadlock, any explicit database operation must not be performed in thi s function. * deadlock, any explicit database operation must not be performed in thi s function.
*/ */
bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) { bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
int64_t allcnt = recs_.size(); int64_t allcnt = recs_.size();
skipping to change at line 639 skipping to change at line 639
* BasicDB::OTRYLOCK, which means locking is performed without blocking, BasicDB::ONOREPAIR, * BasicDB::OTRYLOCK, which means locking is performed without blocking, BasicDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is * which means the database file is not repaired implicitly even if file destruction is
* detected. * detected.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Every opened database must be closed by the BasicDB::close metho d when it is no * @note Every opened database must be closed by the BasicDB::close metho d when it is no
* longer in use. It is not allowed for two or more database objects in the same process to * 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. * keep their connections to the same database file at the same time.
*/ */
bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str());
omode_ = mode; omode_ = mode;
path_.append(path); path_.append(path);
std::memset(opaque_, 0, sizeof(opaque_)); std::memset(opaque_, 0, sizeof(opaque_));
trigger_meta(MetaTrigger::OPEN, "open"); trigger_meta(MetaTrigger::OPEN, "open");
return true; return true;
} }
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close() { bool close() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str()); report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str());
tran_ = false; tran_ = false;
trlogs_.clear(); trlogs_.clear();
recs_.clear(); recs_.clear();
if (!curs_.empty()) { if (!curs_.empty()) {
typename CursorList::const_iterator cit = curs_.begin(); typename CursorList::const_iterator cit = curs_.begin();
skipping to change at line 694 skipping to change at line 694
* @param proc a postprocessor object. If it is NULL, no postprocessing is performed. * @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. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the postprocessor is performed atomically and o ther threads accessing * @note The operation of the postprocessor is performed atomically and o ther threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool synchronize(bool hard = false, FileProcessor* proc = NULL, bool synchronize(bool hard = false, FileProcessor* proc = NULL,
ProgressChecker* checker = NULL) { ProgressChecker* checker = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
bool err = false; bool err = false;
if ((omode_ & OWRITER) && checker && if ((omode_ & OWRITER) && checker &&
!checker->check("synchronize", "nothing to be synchronized", -1, -1 )) { !checker->check("synchronize", "nothing to be synchronized", -1, -1 )) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
return false; return false;
} }
skipping to change at line 729 skipping to change at line 729
* Occupy database by locking and do something meanwhile. * Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock. * @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 perfo rmed. * @param proc a processor object. If it is NULL, no processing is perfo rmed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing * @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 * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool occupy(bool writable = true, FileProcessor* proc = NULL) { bool occupy(bool writable = true, FileProcessor* proc = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, writable); ScopedRWLock lock(&mlock_, writable);
bool err = false; bool err = false;
if (proc && !proc->process(path_, recs_.size(), size_)) { if (proc && !proc->process(path_, recs_.size(), size_)) {
set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); set_error(_KCCODELINE_, Error::LOGIC, "processing failed");
err = true; err = true;
} }
trigger_meta(MetaTrigger::OCCUPY, "occupy"); trigger_meta(MetaTrigger::OCCUPY, "occupy");
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
skipping to change at line 811 skipping to change at line 811
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* End transaction. * End transaction.
* @param commit true to commit the transaction, or false to abort the tr ansaction. * @param commit true to commit the transaction, or false to abort the tr ansaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool end_transaction(bool commit = true) { bool end_transaction(bool commit = true) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); set_error(_KCCODELINE_, Error::INVALID, "not in transaction");
return false; return false;
} }
if (!commit) { if (!commit) {
if (!curs_.empty()) { if (!curs_.empty()) {
skipping to change at line 854 skipping to change at line 854
tran_ = false; tran_ = false;
trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction");
return true; return true;
} }
/** /**
* Remove all records. * Remove all records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clear() { bool clear() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
recs_.clear(); recs_.clear();
if (!curs_.empty()) { if (!curs_.empty()) {
typename CursorList::const_iterator cit = curs_.begin(); typename CursorList::const_iterator cit = curs_.begin();
typename CursorList::const_iterator citend = curs_.end(); typename CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
skipping to change at line 879 skipping to change at line 879
std::memset(opaque_, 0, sizeof(opaque_)); std::memset(opaque_, 0, sizeof(opaque_));
trigger_meta(MetaTrigger::CLEAR, "clear"); trigger_meta(MetaTrigger::CLEAR, "clear");
return true; return true;
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count() { int64_t count() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return recs_.size(); return recs_.size();
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure. * @return the size of the database file in bytes, or -1 on failure.
*/ */
int64_t size() { int64_t size() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return size_; return size_;
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
std::string path() { std::string path() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return ""; return "";
} }
return path_; return path_;
} }
/** /**
* Get the miscellaneous status information. * Get the miscellaneous status information.
* @param strmap a string map to contain the result. * @param strmap a string map to contain the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool status(std::map<std::string, std::string>* strmap) { bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap); _assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = strprintf("%u", (unsigned)DBTYPE); (*strmap)["type"] = strprintf("%u", (unsigned)DBTYPE);
(*strmap)["realtype"] = strprintf("%u", (unsigned)DBTYPE); (*strmap)["realtype"] = strprintf("%u", (unsigned)DBTYPE);
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
if (strmap->count("opaque") > 0) if (strmap->count("opaque") > 0)
(*strmap)["opaque"] = std::string(opaque_, sizeof(opaque_)); (*strmap)["opaque"] = std::string(opaque_, sizeof(opaque_));
(*strmap)["count"] = strprintf("%lld", (long long)recs_.size()); (*strmap)["count"] = strprintf("%lld", (long long)recs_.size());
skipping to change at line 955 skipping to change at line 955
* @param file the file name of the program source code. * @param file the file name of the program source code.
* @param line the line number 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 func the function name of the program source code.
* @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal * @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal
* information, Logger::WARN for warning, and Logger::ERROR for fatal err or. * information, Logger::WARN for warning, and Logger::ERROR for fatal err or.
* @param message the supplement message. * @param message the supplement message.
*/ */
void log(const char* file, int32_t line, const char* func, Logger::Kind k ind, void log(const char* file, int32_t line, const char* func, Logger::Kind k ind,
const char* message) { const char* message) {
_assert_(file && line > 0 && func && message); _assert_(file && line > 0 && func && message);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (!logger_) return; if (!logger_) return;
logger_->log(file, line, func, kind, message); logger_->log(file, line, func, kind, message);
} }
/** /**
* Set the internal logger. * Set the internal logger.
* @param logger the logger object. * @param logger the logger object.
* @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging,
* Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal * Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal
* error. * error.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) { bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) {
_assert_(logger); _assert_(logger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
logger_ = logger; logger_ = logger;
logkinds_ = kinds; logkinds_ = kinds;
return true; return true;
} }
/** /**
* Set the internal meta operation trigger. * Set the internal meta operation trigger.
* @param trigger the trigger object. * @param trigger the trigger object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_meta_trigger(MetaTrigger* trigger) { bool tune_meta_trigger(MetaTrigger* trigger) {
_assert_(trigger); _assert_(trigger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
mtrigger_ = trigger; mtrigger_ = trigger;
return true; return true;
} }
/** /**
* Get the opaque data. * Get the opaque data.
* @return the pointer to the opaque data region, whose size is 16 bytes. * @return the pointer to the opaque data region, whose size is 16 bytes.
*/ */
char* opaque() { char* opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return opaque_; return opaque_;
} }
/** /**
* Synchronize the opaque data. * Synchronize the opaque data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_opaque() { bool synchronize_opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
return true; return true;
} }
skipping to change at line 1142 skipping to change at line 1142
}; };
/** /**
* Tune the internal map object. * Tune the internal map object.
*/ */
void map_tune(); void map_tune();
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
ProtoDB(const ProtoDB&); ProtoDB(const ProtoDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
ProtoDB& operator =(const ProtoDB&); ProtoDB& operator =(const ProtoDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; RWLock mlock_;
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The internal logger. */ /** The internal logger. */
Logger* logger_; Logger* logger_;
/** The kinds of logged messages. */ /** The kinds of logged messages. */
uint32_t logkinds_; uint32_t logkinds_;
/** The internal meta operation trigger. */ /** The internal meta operation trigger. */
MetaTrigger* mtrigger_; MetaTrigger* mtrigger_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
 End of changes. 29 change blocks. 
29 lines changed or deleted 29 lines changed or added


 kcstashdb.h   kcstashdb.h 
skipping to change at line 78 skipping to change at line 78
*/ */
class Cursor : public BasicDB::Cursor { class Cursor : public BasicDB::Cursor {
friend class StashDB; friend class StashDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(StashDB* db) : db_(db), bidx_(-1), rbuf_(NULL) { explicit Cursor(StashDB* db) : db_(db), bidx_(-1), rbuf_(NULL) {
_assert_(db); _assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true); _assert_(true);
if (!db_) return; if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.remove(this); db_->curs_.remove(this);
} }
/** /**
* Accept a visitor to the current record. * Accept a visitor to the current record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation. * @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. * @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and othe r threads accessing * @note The operation for each record is performed atomically and othe r threads accessing
* the same record are blocked. To avoid deadlock, any explicit databa se operation must not * the same record are blocked. To avoid deadlock, any explicit databa se operation must not
* be performed in this function. * be performed in this function.
*/ */
bool accept(Visitor* visitor, bool writable = true, bool step = false) { bool accept(Visitor* visitor, bool writable = true, bool step = false) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->omode_ & OWRITER)) { if (writable && !(db_->omode_ & OWRITER)) {
db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
if (bidx_ < 0) { if (bidx_ < 0) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
skipping to change at line 136 skipping to change at line 136
if (step && rbuf_) step_impl(); if (step && rbuf_) step_impl();
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record for forward scan. * Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump() { bool jump() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
bidx_ = 0; bidx_ = 0;
rbuf_ = NULL; rbuf_ = NULL;
while (bidx_ < (int64_t)db_->bnum_) { while (bidx_ < (int64_t)db_->bnum_) {
if (db_->buckets_[bidx_]) { if (db_->buckets_[bidx_]) {
rbuf_ = db_->buckets_[bidx_]; rbuf_ = db_->buckets_[bidx_];
return true; return true;
skipping to change at line 162 skipping to change at line 162
return false; return false;
} }
/** /**
* Jump the cursor to a record for forward scan. * Jump the cursor to a record for forward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump(const char* kbuf, size_t ksiz) { bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
bidx_ = -1; bidx_ = -1;
rbuf_ = NULL; rbuf_ = NULL;
size_t bidx = db_->hash_record(kbuf, ksiz) % db_->bnum_; size_t bidx = db_->hash_record(kbuf, ksiz) % db_->bnum_;
char* rbuf = db_->buckets_[bidx]; char* rbuf = db_->buckets_[bidx];
while (rbuf) { while (rbuf) {
Record rec(rbuf); Record rec(rbuf);
skipping to change at line 197 skipping to change at line 197
bool jump(const std::string& key) { bool jump(const std::string& key) {
_assert_(true); _assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), key.size());
} }
/** /**
* Jump the cursor to the last record for backward scan. * Jump the cursor to the last record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back() { bool jump_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const char* kbuf, size_t ksiz) { bool jump_back(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool jump_back(const std::string& key) { bool jump_back(const std::string& key) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Step the cursor to the next record. * Step the cursor to the next record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step() { bool step() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (bidx_ < 0) { if (bidx_ < 0) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
return false; return false;
} }
bool err = false; bool err = false;
if (!step_impl()) err = true; if (!step_impl()) err = true;
return !err; return !err;
} }
/** /**
* Step the cursor to the previous record. * Step the cursor to the previous record.
* @note This is a dummy implementation for compatibility. * @note This is a dummy implementation for compatibility.
*/ */
bool step_back() { bool step_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented"); db_->set_error(_KCCODELINE_, Error::NOIMPL, "not implemented");
return false; return false;
} }
/** /**
* Get the database object. * Get the database object.
* @return the database object. * @return the database object.
skipping to change at line 348 skipping to change at line 348
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and other threads accessing the * @note The operation for each record is performed atomically and other threads accessing the
* same record are blocked. To avoid deadlock, any explicit database ope ration must not be * same record are blocked. To avoid deadlock, any explicit database ope ration must not be
* performed in this function. * performed in this function.
*/ */
bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) { bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writabl e = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
size_t bidx = hash_record(kbuf, ksiz) % bnum_; size_t bidx = hash_record(kbuf, ksiz) % bnum_;
size_t lidx = bidx % RLOCKSLOT; size_t lidx = bidx % RLOCKSLOT;
skipping to change at line 381 skipping to change at line 381
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operations for specified records are performed atomically an d other threads * @note The operations for specified records are performed atomically an d other threads
* accessing the same records are blocked. To avoid deadlock, any explic it database operation * accessing the same records are blocked. To avoid deadlock, any explic it database operation
* must not be performed in this function. * must not be performed in this function.
*/ */
bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor,
bool writable = true) { bool writable = true) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
size_t knum = keys.size(); size_t knum = keys.size();
skipping to change at line 442 skipping to change at line 442
* Iterate to accept a visitor for each record. * Iterate to accept a visitor for each record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @param checker a progress checker object. If it is NULL, no checking is performed. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads ar e blocked. To avoid * @note The whole iteration is performed atomically and other threads ar e blocked. To avoid
* deadlock, any explicit database operation must not be performed in thi s function. * deadlock, any explicit database operation must not be performed in thi s function.
*/ */
bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) { bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
int64_t allcnt = count_; int64_t allcnt = count_;
skipping to change at line 535 skipping to change at line 535
* StashDB::OTRYLOCK, which means locking is performed without blocking, StashDB::ONOREPAIR, * StashDB::OTRYLOCK, which means locking is performed without blocking, StashDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is * which means the database file is not repaired implicitly even if file destruction is
* detected. * detected.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Every opened database must be closed by the StashDB::close metho d when it is no * @note Every opened database must be closed by the StashDB::close metho d when it is no
* longer in use. It is not allowed for two or more database objects in the same process to * 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. * keep their connections to the same database file at the same time.
*/ */
bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str());
omode_ = mode; omode_ = mode;
path_.append(path); path_.append(path);
if (bnum_ >= MAPZMAPBNUM) { if (bnum_ >= MAPZMAPBNUM) {
buckets_ = (char**)mapalloc(sizeof(*buckets_) * bnum_); buckets_ = (char**)mapalloc(sizeof(*buckets_) * bnum_);
} else { } else {
skipping to change at line 561 skipping to change at line 561
std::memset(opaque_, 0, sizeof(opaque_)); std::memset(opaque_, 0, sizeof(opaque_));
trigger_meta(MetaTrigger::OPEN, "open"); trigger_meta(MetaTrigger::OPEN, "open");
return true; return true;
} }
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close() { bool close() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str()); report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath_.c_str());
tran_ = false; tran_ = false;
trlogs_.clear(); trlogs_.clear();
for (size_t i = 0; i < bnum_; i++) { for (size_t i = 0; i < bnum_; i++) {
char* rbuf = buckets_[i]; char* rbuf = buckets_[i];
while (rbuf) { while (rbuf) {
skipping to change at line 602 skipping to change at line 602
* @param proc a postprocessor object. If it is NULL, no postprocessing is performed. * @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. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the postprocessor is performed atomically and o ther threads accessing * @note The operation of the postprocessor is performed atomically and o ther threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool synchronize(bool hard = false, FileProcessor* proc = NULL, bool synchronize(bool hard = false, FileProcessor* proc = NULL,
ProgressChecker* checker = NULL) { ProgressChecker* checker = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
bool err = false; bool err = false;
if ((omode_ & OWRITER) && checker && if ((omode_ & OWRITER) && checker &&
!checker->check("synchronize", "nothing to be synchronized", -1, -1 )) { !checker->check("synchronize", "nothing to be synchronized", -1, -1 )) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
return false; return false;
} }
skipping to change at line 637 skipping to change at line 637
* Occupy database by locking and do something meanwhile. * Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock. * @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 perfo rmed. * @param proc a processor object. If it is NULL, no processing is perfo rmed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing * @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 * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool occupy(bool writable = true, FileProcessor* proc = NULL) { bool occupy(bool writable = true, FileProcessor* proc = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, writable); ScopedRWLock lock(&mlock_, writable);
bool err = false; bool err = false;
if (proc && !proc->process(path_, count_, size_impl())) { if (proc && !proc->process(path_, count_, size_impl())) {
set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); set_error(_KCCODELINE_, Error::LOGIC, "processing failed");
err = true; err = true;
} }
trigger_meta(MetaTrigger::OCCUPY, "occupy"); trigger_meta(MetaTrigger::OCCUPY, "occupy");
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
skipping to change at line 721 skipping to change at line 721
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* End transaction. * End transaction.
* @param commit true to commit the transaction, or false to abort the tr ansaction. * @param commit true to commit the transaction, or false to abort the tr ansaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool end_transaction(bool commit = true) { bool end_transaction(bool commit = true) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); set_error(_KCCODELINE_, Error::INVALID, "not in transaction");
return false; return false;
} }
if (!commit) { if (!commit) {
disable_cursors(); disable_cursors();
skipping to change at line 747 skipping to change at line 747
tran_ = false; tran_ = false;
trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction");
return true; return true;
} }
/** /**
* Remove all records. * Remove all records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clear() { bool clear() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
disable_cursors(); disable_cursors();
if (count_ > 0) { if (count_ > 0) {
for (size_t i = 0; i < bnum_; i++) { for (size_t i = 0; i < bnum_; i++) {
char* rbuf = buckets_[i]; char* rbuf = buckets_[i];
while (rbuf) { while (rbuf) {
Record rec(rbuf); Record rec(rbuf);
skipping to change at line 777 skipping to change at line 777
std::memset(opaque_, 0, sizeof(opaque_)); std::memset(opaque_, 0, sizeof(opaque_));
trigger_meta(MetaTrigger::CLEAR, "clear"); trigger_meta(MetaTrigger::CLEAR, "clear");
return true; return true;
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count() { int64_t count() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return count_; return count_;
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure. * @return the size of the database file in bytes, or -1 on failure.
*/ */
int64_t size() { int64_t size() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return size_impl(); return size_impl();
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
std::string path() { std::string path() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return ""; return "";
} }
return path_; return path_;
} }
/** /**
* Get the miscellaneous status information. * Get the miscellaneous status information.
* @param strmap a string map to contain the result. * @param strmap a string map to contain the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool status(std::map<std::string, std::string>* strmap) { bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap); _assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = strprintf("%u", (unsigned)TYPESTASH); (*strmap)["type"] = strprintf("%u", (unsigned)TYPESTASH);
(*strmap)["realtype"] = strprintf("%u", (unsigned)TYPESTASH); (*strmap)["realtype"] = strprintf("%u", (unsigned)TYPESTASH);
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
if (strmap->count("opaque") > 0) if (strmap->count("opaque") > 0)
(*strmap)["opaque"] = std::string(opaque_, sizeof(opaque_)); (*strmap)["opaque"] = std::string(opaque_, sizeof(opaque_));
if (strmap->count("bnum_used") > 0) { if (strmap->count("bnum_used") > 0) {
skipping to change at line 860 skipping to change at line 860
* @param file the file name of the program source code. * @param file the file name of the program source code.
* @param line the line number 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 func the function name of the program source code.
* @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal * @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal
* information, Logger::WARN for warning, and Logger::ERROR for fatal err or. * information, Logger::WARN for warning, and Logger::ERROR for fatal err or.
* @param message the supplement message. * @param message the supplement message.
*/ */
void log(const char* file, int32_t line, const char* func, Logger::Kind k ind, void log(const char* file, int32_t line, const char* func, Logger::Kind k ind,
const char* message) { const char* message) {
_assert_(file && line > 0 && func && message); _assert_(file && line > 0 && func && message);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (!logger_) return; if (!logger_) return;
logger_->log(file, line, func, kind, message); logger_->log(file, line, func, kind, message);
} }
/** /**
* Set the internal logger. * Set the internal logger.
* @param logger the logger object. * @param logger the logger object.
* @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging,
* Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal * Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal
* error. * error.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) { bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) {
_assert_(logger); _assert_(logger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
logger_ = logger; logger_ = logger;
logkinds_ = kinds; logkinds_ = kinds;
return true; return true;
} }
/** /**
* Set the internal meta operation trigger. * Set the internal meta operation trigger.
* @param trigger the trigger object. * @param trigger the trigger object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_meta_trigger(MetaTrigger* trigger) { bool tune_meta_trigger(MetaTrigger* trigger) {
_assert_(trigger); _assert_(trigger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
mtrigger_ = trigger; mtrigger_ = trigger;
return true; return true;
} }
/** /**
* Set the number of buckets of the hash table. * Set the number of buckets of the hash table.
* @param bnum the number of buckets of the hash table. * @param bnum the number of buckets of the hash table.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_buckets(int64_t bnum) { bool tune_buckets(int64_t bnum) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum >= 0 ? bnum : DEFBNUM; bnum_ = bnum >= 0 ? bnum : DEFBNUM;
if (bnum_ > (size_t)INT16MAX) bnum_ = nearbyprime(bnum_); if (bnum_ > (size_t)INT16MAX) bnum_ = nearbyprime(bnum_);
return true; return true;
} }
/** /**
* Get the opaque data. * Get the opaque data.
* @return the pointer to the opaque data region, whose size is 16 bytes. * @return the pointer to the opaque data region, whose size is 16 bytes.
*/ */
char* opaque() { char* opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return opaque_; return opaque_;
} }
/** /**
* Synchronize the opaque data. * Synchronize the opaque data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_opaque() { bool synchronize_opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
return true; return true;
} }
skipping to change at line 1190 skipping to change at line 1190
char* rbuf = buckets_[bidx]; char* rbuf = buckets_[bidx];
char** entp = buckets_ + bidx; char** entp = buckets_ + bidx;
while (rbuf) { while (rbuf) {
Record rec(rbuf); Record rec(rbuf);
if (rec.ksiz_ == ksiz && !std::memcmp(rec.kbuf_, kbuf, ksiz)) { if (rec.ksiz_ == ksiz && !std::memcmp(rec.kbuf_, kbuf, ksiz)) {
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_full(rec.kbuf_, rec.ksiz_, const char* vbuf = visitor->visit_full(rec.kbuf_, rec.ksiz_,
rec.vbuf_, rec.vsiz_, &vsiz) ; rec.vbuf_, rec.vsiz_, &vsiz) ;
if (vbuf == Visitor::REMOVE) { if (vbuf == Visitor::REMOVE) {
if (tran_) { if (tran_) {
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
TranLog log(rec.kbuf_, rec.ksiz_, rec.vbuf_, rec.vsiz_); TranLog log(rec.kbuf_, rec.ksiz_, rec.vbuf_, rec.vsiz_);
trlogs_.push_back(log); trlogs_.push_back(log);
} }
count_ -= 1; count_ -= 1;
size_ -= rec.ksiz_ + rec.vsiz_; size_ -= rec.ksiz_ + rec.vsiz_;
escape_cursors(rbuf); escape_cursors(rbuf);
*entp = rec.child_; *entp = rec.child_;
delete[] rbuf; delete[] rbuf;
} else if (vbuf != Visitor::NOP) { } else if (vbuf != Visitor::NOP) {
if (tran_) { if (tran_) {
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
TranLog log(rec.kbuf_, rec.ksiz_, rec.vbuf_, rec.vsiz_); TranLog log(rec.kbuf_, rec.ksiz_, rec.vbuf_, rec.vsiz_);
trlogs_.push_back(log); trlogs_.push_back(log);
} }
int32_t oh = (int32_t)sizevarnum(vsiz) - (int32_t)sizevarnum(rec. vsiz_); int32_t oh = (int32_t)sizevarnum(vsiz) - (int32_t)sizevarnum(rec. vsiz_);
int64_t diff = (int64_t)rec.vsiz_ - (int64_t)(vsiz + oh); int64_t diff = (int64_t)rec.vsiz_ - (int64_t)(vsiz + oh);
size_ += (int64_t)vsiz - (int64_t)rec.vsiz_; size_ += (int64_t)vsiz - (int64_t)rec.vsiz_;
if (diff >= 0) { if (diff >= 0) {
rec.overwrite(rbuf, vbuf, vsiz); rec.overwrite(rbuf, vbuf, vsiz);
} else { } else {
Record nrec(rec.child_, kbuf, ksiz, vbuf, vsiz); Record nrec(rec.child_, kbuf, ksiz, vbuf, vsiz);
skipping to change at line 1227 skipping to change at line 1227
} }
return; return;
} }
entp = (char**)rbuf; entp = (char**)rbuf;
rbuf = rec.child_; rbuf = rec.child_;
} }
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_empty(kbuf, ksiz, &vsiz); const char* vbuf = visitor->visit_empty(kbuf, ksiz, &vsiz);
if (vbuf != Visitor::REMOVE && vbuf != Visitor::NOP) { if (vbuf != Visitor::REMOVE && vbuf != Visitor::NOP) {
if (tran_) { if (tran_) {
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
TranLog log(kbuf, ksiz); TranLog log(kbuf, ksiz);
trlogs_.push_back(log); trlogs_.push_back(log);
} }
Record nrec(NULL, kbuf, ksiz, vbuf, vsiz); Record nrec(NULL, kbuf, ksiz, vbuf, vsiz);
*entp = nrec.serialize(); *entp = nrec.serialize();
count_ += 1; count_ += 1;
size_ += ksiz + vsiz; size_ += ksiz + vsiz;
} }
} }
/** /**
skipping to change at line 1261 skipping to change at line 1261
int64_t size_impl() { int64_t size_impl() {
_assert_(true); _assert_(true);
return bnum_ * sizeof(*buckets_) + count_ * (4 + sizeof(void*)) + size_ ; return bnum_ * sizeof(*buckets_) + count_ * (4 + sizeof(void*)) + size_ ;
} }
/** /**
* Escape cursors on a shifted or removed records. * Escape cursors on a shifted or removed records.
* @param rbuf the record buffer. * @param rbuf the record buffer.
*/ */
void escape_cursors(char* rbuf) { void escape_cursors(char* rbuf) {
_assert_(rbuf); _assert_(rbuf);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
if (curs_.empty()) return; if (curs_.empty()) return;
CursorList::const_iterator cit = curs_.begin(); CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end(); CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
if (cur->rbuf_ == rbuf) cur->step_impl(); if (cur->rbuf_ == rbuf) cur->step_impl();
++cit; ++cit;
} }
} }
/** /**
* Adjust cursors on re-allocated records. * Adjust cursors on re-allocated records.
* @param obuf the old address. * @param obuf the old address.
* @param nbuf the new address. * @param nbuf the new address.
*/ */
void adjust_cursors(char* obuf, char* nbuf) { void adjust_cursors(char* obuf, char* nbuf) {
_assert_(obuf && nbuf); _assert_(obuf && nbuf);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
if (curs_.empty()) return; if (curs_.empty()) return;
CursorList::const_iterator cit = curs_.begin(); CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end(); CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
if (cur->rbuf_ == obuf) cur->rbuf_ = nbuf; if (cur->rbuf_ == obuf) cur->rbuf_ = nbuf;
++cit; ++cit;
} }
} }
/** /**
* Disable all cursors. * Disable all cursors.
*/ */
void disable_cursors() { void disable_cursors() {
_assert_(true); _assert_(true);
ScopedSpinLock lock(&flock_); ScopedMutex lock(&flock_);
CursorList::const_iterator cit = curs_.begin(); CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end(); CursorList::const_iterator citend = curs_.end();
while (cit != citend) { while (cit != citend) {
Cursor* cur = *cit; Cursor* cur = *cit;
cur->bidx_ = -1; cur->bidx_ = -1;
cur->rbuf_ = NULL; cur->rbuf_ = NULL;
++cit; ++cit;
} }
} }
/** /**
skipping to change at line 1331 skipping to change at line 1331
Remover remover; Remover remover;
accept_impl(kbuf, ksiz, &remover, bidx); accept_impl(kbuf, ksiz, &remover, bidx);
} }
} }
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
StashDB(const StashDB&); StashDB(const StashDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
StashDB& operator =(const StashDB&); StashDB& operator =(const StashDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; RWLock mlock_;
/** The record locks. */ /** The record locks. */
SlottedSpinRWLock rlock_; SlottedRWLock rlock_;
/** The file lock. */ /** The file lock. */
SpinLock flock_; Mutex flock_;
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The internal logger. */ /** The internal logger. */
Logger* logger_; Logger* logger_;
/** The kinds of logged messages. */ /** The kinds of logged messages. */
uint32_t logkinds_; uint32_t logkinds_;
/** The internal meta operation trigger. */ /** The internal meta operation trigger. */
MetaTrigger* mtrigger_; MetaTrigger* mtrigger_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
 End of changes. 38 change blocks. 
38 lines changed or deleted 38 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/