kccachedb.h   kccachedb.h 
skipping to change at line 69 skipping to change at line 69
* Cursor to indicate a record. * Cursor to indicate a record.
*/ */
class Cursor : public FileDB::Cursor { class Cursor : public FileDB::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);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true);
if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock 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
* the same record are blocked.
*/ */
virtual bool accept(Visitor* visitor, bool writable, bool step) { virtual bool accept(Visitor* visitor, bool writable = true, bool step =
false) {
_assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->omode_ & OWRITER)) { if (writable && !(db_->omode_ & OWRITER)) {
db_->set_error(Error::INVALID, "permission denied"); db_->set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
if (sidx_ < 0 || !rec_) { if (sidx_ < 0 || !rec_) {
db_->set_error(Error::NOREC, "no record"); db_->set_error(Error::NOREC, "no record");
return false; return false;
} }
uint32_t rksiz = rec_->ksiz & CDBKSIZMAX; uint32_t rksiz = rec_->ksiz & CDBKSIZMAX;
char* dbuf = (char*)rec_ + sizeof(*rec_); char* dbuf = (char*)rec_ + sizeof(*rec_);
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_full(dbuf, rksiz, dbuf + rksiz, rec _->vsiz, &vsiz); const char* vbuf = visitor->visit_full(dbuf, rksiz, dbuf + rksiz, rec _->vsiz, &vsiz);
skipping to change at line 125 skipping to change at line 131
db_->accept_impl(slot, hash, dbuf, rksiz, &repeater, true); db_->accept_impl(slot, hash, dbuf, rksiz, &repeater, true);
if (step) step_impl(); if (step) step_impl();
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record. * Jump the cursor to the first record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool jump() { virtual bool jump() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
for (int32_t i = 0; i < CDBSLOTNUM; i++) { for (int32_t i = 0; i < CDBSLOTNUM; 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;
} }
} }
db_->set_error(Error::NOREC, "no record"); db_->set_error(Error::NOREC, "no record");
sidx_ = -1; sidx_ = -1;
rec_ = NULL; rec_ = NULL;
return true; return false;
} }
/** /**
* Jump the cursor onto a record. * Jump the cursor onto a record.
* @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.
*/ */
virtual bool jump(const char* kbuf, size_t ksiz) { virtual bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (ksiz > CDBKSIZMAX) ksiz = CDBKSIZMAX; if (ksiz > CDBKSIZMAX) ksiz = CDBKSIZMAX;
uint64_t hash = db_->hash_record(kbuf, ksiz); uint64_t hash = db_->hash_record(kbuf, ksiz);
int32_t sidx = hash % CDBSLOTNUM; int32_t sidx = hash % CDBSLOTNUM;
hash /= CDBSLOTNUM; hash /= CDBSLOTNUM;
Slot* slot = db_->slots_ + sidx; Slot* slot = db_->slots_ + sidx;
skipping to change at line 199 skipping to change at line 207
db_->set_error(Error::NOREC, "no record"); db_->set_error(Error::NOREC, "no record");
sidx_ = -1; sidx_ = -1;
rec_ = NULL; rec_ = NULL;
return false; return false;
} }
/** /**
* Jump the cursor to a record. * Jump the cursor to a record.
* @note Equal to the original Cursor::jump method except that the para meter is std::string. * @note Equal to the original Cursor::jump method except that the para meter is std::string.
*/ */
virtual bool jump(const std::string& key) { virtual bool jump(const std::string& key) {
_assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), 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.
*/ */
virtual bool step() { virtual bool step() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (sidx_ < 0 || !rec_) { if (sidx_ < 0 || !rec_) {
db_->set_error(Error::NOREC, "no record"); db_->set_error(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;
} }
/**
* Get the database object.
* @return the database object.
*/
virtual CacheDB* db() {
_assert_(true);
return db_;
}
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
Cursor(const Cursor&); Cursor(const Cursor&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
Cursor& operator =(const Cursor&); Cursor& operator =(const Cursor&);
/** /**
* 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_impl() { bool step_impl() {
skipping to change at line 256 skipping to change at line 274
/** The inner database. */ /** The inner database. */
CacheDB* db_; CacheDB* db_;
/** The index of the current slot. */ /** The index of the current slot. */
int32_t sidx_; int32_t sidx_;
/** The current record. */ /** The current record. */
Record* rec_; Record* rec_;
}; };
/** /**
* Default constructor. * Default constructor.
*/ */
CacheDB() : explicit CacheDB() :
mlock_(), flock_(), error_(), omode_(0), curs_(), path_(""), mlock_(), flock_(), error_(), omode_(0), curs_(), path_(""),
bnum_(CDBDEFBNUM), capcnt_(-1), capsiz_(-1), slots_(), tran_(false) {} bnum_(CDBDEFBNUM), capcnt_(-1), capsiz_(-1), slots_(), tran_(false) {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
* @note If the database is not closed, it is closed implicitly. * @note If the database is not closed, it is closed implicitly.
*/ */
virtual ~CacheDB() { virtual ~CacheDB() {
_assert_(true);
if (omode_ != 0) close(); if (omode_ != 0) close();
if (curs_.size() > 0) {
CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end();
while (cit != citend) {
Cursor* cur = *cit;
cur->db_ = NULL;
cit++;
}
}
} }
/** /**
* Accept a visitor to a record. * Accept a visitor to a record.
* @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.
* @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
* same record are blocked.
*/ */
virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool
writable) { writable = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
if (ksiz > CDBKSIZMAX) ksiz = CDBKSIZMAX; if (ksiz > CDBKSIZMAX) ksiz = CDBKSIZMAX;
uint64_t hash = hash_record(kbuf, ksiz); uint64_t hash = hash_record(kbuf, ksiz);
int32_t sidx = hash % CDBSLOTNUM; int32_t sidx = hash % CDBSLOTNUM;
hash /= CDBSLOTNUM; hash /= CDBSLOTNUM;
Slot* slot = slots_ + sidx; Slot* slot = slots_ + sidx;
slot->lock.lock(); slot->lock.lock();
accept_impl(slot, hash, kbuf, ksiz, visitor, false); accept_impl(slot, hash, kbuf, ksiz, visitor, false);
slot->lock.unlock(); slot->lock.unlock();
return true; return true;
} }
/** /**
* 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.
* @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.
*/ */
virtual bool iterate(Visitor *visitor, bool writable) { virtual bool iterate(Visitor *visitor, bool writable = true) {
_assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
for (int32_t i = 0; i < CDBSLOTNUM; i++) { for (int32_t i = 0; i < CDBSLOTNUM; i++) {
Slot* slot = slots_ + i; Slot* slot = slots_ + i;
Record* rec = slot->first; Record* rec = slot->first;
while (rec) { while (rec) {
Record* next = rec->next; Record* next = rec->next;
uint32_t rksiz = rec->ksiz & CDBKSIZMAX; uint32_t rksiz = rec->ksiz & CDBKSIZMAX;
char* dbuf = (char*)rec + sizeof(*rec); char* dbuf = (char*)rec + sizeof(*rec);
size_t vsiz; size_t vsiz;
skipping to change at line 338 skipping to change at line 373
rec = next; rec = next;
} }
} }
return true; return true;
} }
/** /**
* Get the last happened error. * Get the last happened error.
* @return the last happened error. * @return the last happened error.
*/ */
virtual Error error() const { virtual Error error() const {
_assert_(true);
return error_; return error_;
} }
/** /**
* Set the error information. * Set the error information.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
virtual void set_error(Error::Code code, const char* message) { virtual void set_error(Error::Code code, const char* message) {
_assert_(message);
error_->set(code, message); error_->set(code, message);
} }
/** /**
* Open a database file. * Open a database file.
* @param path the path of a database file. * @param path the path of a database file.
* @param mode the connection mode. FileDB::OWRITER as a writer, FileDB: * @param mode the connection mode. CacheDB::OWRITER as a writer, CacheD
:OREADER as a reader. B::OREADER as a
* The following may be added to the writer mode by bitwise-or: FileDB::O * reader. The following may be added to the writer mode by bitwise-or:
CREATE, which means CacheDB::OCREATE,
* it creates a new database if the file does not exist, FileDB::OTRUNCAT * which means it creates a new database if the file does not exist, Cach
E, which means it eDB::OTRUNCATE, which
* creates a new database regardless if the file exists, FileDB::OAUTOTRA * means it creates a new database regardless if the file exists, CacheDB
N, which means each ::OAUTOTRAN, which
* updating operation is performed in implicit transaction, FileDB::OAUTO * means each updating operation is performed in implicit transaction, Ca
SYNC, which means cheDB::OAUTOSYNC,
* each updating operation is followed by implicit synchronization with t * which means each updating operation is followed by implicit synchroniz
he file system. The ation with the file
* following may be added to both of the reader mode and the writer mode * system. The following may be added to both of the reader mode and the
by bitwise-or: writer mode by
* FileDB::ONOLOCK, which means it opens the database file without file l * bitwise-or: CacheDB::ONOLOCK, which means it opens the database file w
ocking, ithout file locking,
* FileDB::OTRYLOCK, which means locking is performed without blocking, F * CacheDB::OTRYLOCK, which means locking is performed without blocking,
ile::ONOREPAIR, which CacheDB::ONOREPAIR,
* means the database file is not repaired implicitly even if file destru * which means the database file is not repaired implicitly even if file
ction is detected. destruction is
* 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
* longer in use.
*/ */
virtual bool open(const std::string& path, uint32_t mode) { virtual bool open(const std::string& path, uint32_t mode = OWRITER | OCRE
ATE) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(Error::INVALID, "already opened"); set_error(Error::INVALID, "already opened");
return false; return false;
} }
omode_ = mode; omode_ = mode;
path_.append(path); path_.append(path);
size_t bnum = nearbyprime(bnum_ / CDBSLOTNUM); size_t bnum = nearbyprime(bnum_ / CDBSLOTNUM);
size_t capcnt = capcnt_ > 0 ? capcnt_ / CDBSLOTNUM + 1 : (1ULL << (size of(capcnt) * 8 - 1)); size_t capcnt = capcnt_ > 0 ? capcnt_ / CDBSLOTNUM + 1 : (1ULL << (size of(capcnt) * 8 - 1));
size_t capsiz = capsiz_ > 0 ? capsiz_ / CDBSLOTNUM + 1 : (1ULL << (size of(capsiz) * 8 - 1)); size_t capsiz = capsiz_ > 0 ? capsiz_ / CDBSLOTNUM + 1 : (1ULL << (size of(capsiz) * 8 - 1));
skipping to change at line 386 skipping to change at line 427
for (int32_t i = 0; i < CDBSLOTNUM; i++) { for (int32_t i = 0; i < CDBSLOTNUM; i++) {
initialize_slot(slots_ + i, bnum, capcnt, capsiz); initialize_slot(slots_ + i, bnum, capcnt, capsiz);
} }
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.
*/ */
virtual bool close() { virtual bool close() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
tran_ = false; tran_ = false;
for (int32_t i = CDBSLOTNUM - 1; i >= 0; i--) { for (int32_t i = CDBSLOTNUM - 1; i >= 0; i--) {
destroy_slot(slots_ + i); destroy_slot(slots_ + i);
} }
path_.clear(); path_.clear();
omode_ = 0; omode_ = 0;
return true; return true;
} }
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool synchronize(bool hard, FileProcessor* proc) { virtual bool synchronize(bool hard = false, FileProcessor* proc = NULL) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (proc && !proc->process(path_)) { if (proc && !proc->process(path_, count_impl(), size_impl())) {
set_error(Error::MISC, "postprocessing failed"); set_error(Error::MISC, "postprocessing failed");
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool begin_transaction(bool hard) { virtual bool begin_transaction(bool hard = false) {
_assert_(true);
for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) { for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) {
mlock_.lock_writer(); mlock_.lock_writer();
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!tran_) break; if (!tran_) break;
mlock_.unlock(); mlock_.unlock();
if (wsec > 1.0) wsec = 1.0; if (wsec > 1.0) wsec = 1.0;
Thread::sleep(wsec); Thread::sleep(wsec);
} }
tran_ = true; tran_ = true;
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* Commit transaction. * Try to begin transaction.
* @param hard true for physical synchronization with the device, or fals
e for logical
* synchronization with the file system.
* @return true on success, or false on failure.
*/
virtual bool begin_transaction_try(bool hard = false) {
_assert_(true);
mlock_.lock_writer();
if (omode_ == 0) {
set_error(Error::INVALID, "not opened");
mlock_.unlock();
return false;
}
if (!(omode_ & OWRITER)) {
set_error(Error::NOPERM, "permission denied");
mlock_.unlock();
return false;
}
if (tran_) {
set_error(Error::LOGIC, "competition avoided");
mlock_.unlock();
return false;
}
tran_ = true;
mlock_.unlock();
return true;
}
/**
* 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.
*/ */
virtual bool end_transaction(bool commit) { virtual bool end_transaction(bool commit = true) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(Error::INVALID, "not in transaction"); set_error(Error::INVALID, "not in transaction");
return false; return false;
} }
if (!commit) disable_cursors(); if (!commit) disable_cursors();
skipping to change at line 480 skipping to change at line 553
adjust_slot_capacity(slots_ + i); adjust_slot_capacity(slots_ + i);
} }
tran_ = false; tran_ = false;
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.
*/ */
virtual bool clear() { virtual bool clear() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
disable_cursors(); disable_cursors();
for (int32_t i = 0; i < CDBSLOTNUM; i++) { for (int32_t i = 0; i < CDBSLOTNUM; i++) {
Slot* slot = slots_ + i; Slot* slot = slots_ + i;
clear_slot(slot); clear_slot(slot);
} }
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.
*/ */
virtual int64_t count() { virtual int64_t count() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(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.
*/ */
virtual int64_t size() { virtual int64_t size() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(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 in bytes, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
virtual std::string path() { virtual std::string path() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(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.
*/ */
virtual bool status(std::map<std::string, std::string>* strmap) { virtual bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = "CacheDB"; (*strmap)["type"] = "CacheDB";
(*strmap)["realtype"] = strprintf("%u", (unsigned)TYPECACHE);
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
(*strmap)["count"] = strprintf("%lld", (long long)count_impl()); (*strmap)["count"] = strprintf("%lld", (long long)count_impl());
(*strmap)["size"] = strprintf("%lld", (long long)size_impl()); (*strmap)["size"] = strprintf("%lld", (long long)size_impl());
return true; return true;
} }
/** /**
* Create a cursor object. * Create a cursor object.
* @return the return value is the cursor object. * @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the const ructor, it should be * @note Because the object of the return value is allocated by the const ructor, it should be
* released with the delete operator when it is no longer in use. * released with the delete operator when it is no longer in use.
*/ */
virtual Cursor* cursor() { virtual Cursor* cursor() {
_assert_(true);
return new Cursor(this); return new Cursor(this);
} }
/** /**
* 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.
*/ */
virtual bool tune_buckets(int64_t bnum) { virtual bool tune_buckets(int64_t bnum) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(Error::INVALID, "already opened"); set_error(Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum >= 0 ? bnum : CDBDEFBNUM; bnum_ = bnum >= 0 ? bnum : CDBDEFBNUM;
return true; return true;
} }
/** /**
* Set the cap of record number. * Set the capacity by record number.
* @param count the muximum 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.
*/ */
virtual bool cap_count(int64_t count) { virtual bool cap_count(int64_t count) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(Error::INVALID, "already opened"); set_error(Error::INVALID, "already opened");
return false; return false;
} }
capcnt_ = count; capcnt_ = count;
return true; return true;
} }
/** /**
* Set the cap of memory usage. * Set the capacity by memory usage.
* @param size the muximum 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.
*/ */
virtual bool cap_size(int64_t size) { virtual bool cap_size(int64_t size) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(Error::INVALID, "already opened"); set_error(Error::INVALID, "already opened");
return false; return false;
} }
capsiz_ = size; capsiz_ = size;
return true; return true;
} }
private: private:
/** /**
skipping to change at line 616 skipping to change at line 699
Record* next; ///< next record Record* next; ///< next record
}; };
/** /**
* Transaction log. * Transaction log.
*/ */
struct TranLog { struct TranLog {
bool full; ///< flag whether full bool full; ///< flag whether full
std::string key; ///< old key std::string key; ///< old key
std::string value; ///< old value std::string value; ///< old value
/** constructor for a full record */ /** constructor for a full record */
TranLog(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) : explicit TranLog(const char* kbuf, size_t ksiz, const char* vbuf, size_ t vsiz) :
full(true), key(kbuf, ksiz), value(vbuf, vsiz) {} full(true), key(kbuf, ksiz), value(vbuf, vsiz) {}
/** constructor for an empty record */ /** constructor for an empty record */
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) {}
}; };
/** /**
* Slot table. * Slot table.
*/ */
struct Slot { struct Slot {
SpinLock lock; ///< lock SpinLock 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
skipping to change at line 852 skipping to change at line 935
for (int32_t i = 0; i < CDBSLOTNUM; i++) { for (int32_t i = 0; i < CDBSLOTNUM; i++) {
Slot* slot = slots_ + i; Slot* slot = slots_ + i;
ScopedSpinLock lock(&slot->lock); ScopedSpinLock 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 the slot table. * @param slot the slot table.
* @param bnum the number of buckets. * @param bnum the number of buckets.
* @param capcnt the cap of record number. * @param capcnt the capacity of record number.
* @param capsiz the cap of memory usage. * @param capsiz the capacity of memory usage.
*/ */
void initialize_slot(Slot* slot, size_t bnum, size_t capcnt, size_t capsi z) { void initialize_slot(Slot* slot, size_t bnum, size_t capcnt, size_t capsi z) {
Record** buckets; Record** buckets;
if (bnum >= CDBZMAPBNUM) { if (bnum >= CDBZMAPBNUM) {
buckets = (Record**)mapalloc(sizeof(*buckets) * bnum); buckets = (Record**)mapalloc(sizeof(*buckets) * bnum);
} else { } else {
buckets = new Record*[bnum]; buckets = new Record*[bnum];
for (size_t i = 0; i < bnum; i++) { for (size_t i = 0; i < bnum; i++) {
buckets[i] = NULL; buckets[i] = NULL;
} }
skipping to change at line 878 skipping to change at line 961
slot->bnum = bnum; slot->bnum = bnum;
slot->capcnt = capcnt; slot->capcnt = capcnt;
slot->capsiz = capsiz; slot->capsiz = capsiz;
slot->first = NULL; slot->first = NULL;
slot->last = NULL; slot->last = NULL;
slot->count = 0; slot->count = 0;
slot->size = 0; slot->size = 0;
} }
/** /**
* Destroy a slot table. * Destroy a slot table.
* @param the slot table. * @param slot the slot table.
*/ */
void destroy_slot(Slot* slot) { void destroy_slot(Slot* slot) {
slot->trlogs.clear(); slot->trlogs.clear();
Record* rec = slot->last; Record* rec = slot->last;
while (rec) { while (rec) {
Record* prev = rec->prev; Record* prev = rec->prev;
xfree(rec); xfree(rec);
rec = prev; rec = prev;
} }
if (slot->bnum >= CDBZMAPBNUM) { if (slot->bnum >= CDBZMAPBNUM) {
mapfree(slot->buckets); mapfree(slot->buckets);
} else { } else {
delete[] slot->buckets; delete[] slot->buckets;
} }
} }
/** /**
* Clear a slot table. * Clear a slot table.
* @param the slot table. * @param slot the slot table.
*/ */
void clear_slot(Slot* slot) { void clear_slot(Slot* slot) {
Record* rec = slot->last; Record* rec = slot->last;
while (rec) { while (rec) {
Record* prev = rec->prev; Record* prev = rec->prev;
xfree(rec); xfree(rec);
rec = prev; rec = prev;
} }
Record** buckets = slot->buckets; Record** buckets = slot->buckets;
size_t bnum = slot->bnum; size_t bnum = slot->bnum;
for (size_t i = 0; i < bnum; i++) { for (size_t i = 0; i < bnum; i++) {
buckets[i] = NULL; buckets[i] = NULL;
} }
slot->first = NULL; slot->first = NULL;
slot->last = NULL; slot->last = NULL;
slot->count = 0; slot->count = 0;
slot->size = 0; slot->size = 0;
} }
/** /**
* Apply transaction logs of a slot table. * Apply transaction logs of a slot table.
* @param the slot table. * @param slot the slot table.
*/ */
void apply_slot_trlogs(Slot* slot) { void apply_slot_trlogs(Slot* slot) {
const TranLogList& logs = slot->trlogs; const TranLogList& logs = slot->trlogs;
TranLogList::const_iterator it = logs.end(); TranLogList::const_iterator it = logs.end();
TranLogList::const_iterator itbeg = logs.begin(); TranLogList::const_iterator itbeg = logs.begin();
while (it != itbeg) { while (it != itbeg) {
it--; it--;
const char* kbuf = it->key.c_str(); const char* kbuf = it->key.c_str();
size_t ksiz = it->key.size(); size_t ksiz = it->key.size();
const char* vbuf = it->value.c_str(); const char* vbuf = it->value.c_str();
skipping to change at line 941 skipping to change at line 1024
Setter setter(vbuf, vsiz); Setter setter(vbuf, vsiz);
accept_impl(slot, hash, kbuf, ksiz, &setter, true); accept_impl(slot, hash, kbuf, ksiz, &setter, true);
} else { } else {
Remover remover; Remover remover;
accept_impl(slot, hash, kbuf, ksiz, &remover, true); accept_impl(slot, hash, kbuf, ksiz, &remover, true);
} }
} }
} }
/** /**
* Addjust a slot table to the capacity. * Addjust a slot table to the capacity.
* @param the slot table. * @param slot the slot table.
*/ */
void adjust_slot_capacity(Slot* slot) { void adjust_slot_capacity(Slot* slot) {
if ((slot->count > slot->capcnt || slot->size > slot->capsiz) && slot-> first) { if ((slot->count > slot->capcnt || slot->size > slot->capsiz) && slot-> first) {
Record* rec = slot->first; Record* rec = slot->first;
uint32_t rksiz = rec->ksiz & CDBKSIZMAX; uint32_t rksiz = rec->ksiz & CDBKSIZMAX;
char* dbuf = (char*)rec + sizeof(*rec); char* dbuf = (char*)rec + sizeof(*rec);
char stack[CDBRECBUFSIZ]; char stack[CDBRECBUFSIZ];
char* kbuf = rksiz > sizeof(stack) ? new char[rksiz] : stack; char* kbuf = rksiz > sizeof(stack) ? new char[rksiz] : stack;
std::memcpy(kbuf, dbuf, rksiz); std::memcpy(kbuf, dbuf, rksiz);
uint64_t hash = hash_record(kbuf, rksiz) / CDBSLOTNUM; uint64_t hash = hash_record(kbuf, rksiz) / CDBSLOTNUM;
skipping to change at line 978 skipping to change at line 1061
* @param hash the hash number. * @param hash the hash number.
* @return the result number. * @return the result number.
*/ */
uint32_t fold_hash(uint64_t hash) { uint32_t fold_hash(uint64_t hash) {
return ((hash & 0xffffffff00000000ULL) >> 32) ^ ((hash & 0x0000ffffffff 0000ULL) >> 16) ^ return ((hash & 0xffffffff00000000ULL) >> 32) ^ ((hash & 0x0000ffffffff 0000ULL) >> 16) ^
((hash & 0x000000000000ffffULL) << 16) ^ ((hash & 0x00000000ffff0000U LL) >> 0); ((hash & 0x000000000000ffffULL) << 16) ^ ((hash & 0x00000000ffff0000U LL) >> 0);
} }
/** /**
* Compare two keys in lexical order. * Compare two keys in lexical order.
* @param abuf one key. * @param abuf one key.
* @param abuf the size of the one key. * @param asiz the size of the one key.
* @param bbuf the other key. * @param bbuf the other key.
* @param bbuf the size of the other key. * @param bsiz the size of the other key.
* @return positive if the former is big, or negative if the latter is bi g, or 0 if both are * @return positive if the former is big, or negative if the latter is bi g, or 0 if both are
* equivalent. * equivalent.
*/ */
int32_t compare_keys(const char* abuf, size_t asiz, const char* bbuf, siz e_t bsiz) { int32_t compare_keys(const char* abuf, size_t asiz, const char* bbuf, siz e_t bsiz) {
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.
skipping to change at line 1051 skipping to change at line 1134
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
/** The cursor objects. */ /** The cursor objects. */
CursorList curs_; CursorList curs_;
/** The path of the database file. */ /** The path of the database file. */
std::string path_; std::string path_;
/** The bucket number. */ /** The bucket number. */
int64_t bnum_; int64_t bnum_;
/** The cap of record number. */ /** The capacity of record number. */
int64_t capcnt_; int64_t capcnt_;
/** The cap of memory usage. */ /** The capacity of memory usage. */
int64_t capsiz_; int64_t capsiz_;
/** The slot tables. */ /** The slot tables. */
Slot slots_[CDBSLOTNUM]; Slot slots_[CDBSLOTNUM];
/** The flag whether in transaction. */ /** The flag whether in transaction. */
bool tran_; bool tran_;
}; };
} // common namespace } // common namespace
#endif // duplication check #endif // duplication check
 End of changes. 60 change blocks. 
57 lines changed or deleted 146 lines changed or added


 kccommon.h   kccommon.h 
skipping to change at line 18 skipping to change at line 18
* This program is distributed in the hope that it will be useful, but WITH OUT ANY WARRANTY; * This program is distributed in the hope that it will be useful, but WITH OUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PA RTICULAR PURPOSE. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PA RTICULAR PURPOSE.
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program. * You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
************************************************************************** ***********************/ ************************************************************************** ***********************/
#ifndef _KCCOMMON_H // duplication check #ifndef _KCCOMMON_H // duplication check
#define _KCCOMMON_H #define _KCCOMMON_H
#define __STDC_LIMIT_MACROS 1 #undef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1 ///< enable limit macros
extern "C" { extern "C" {
#include <inttypes.h>
#include <stdint.h> #include <stdint.h>
} }
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <cfloat> #include <cfloat>
#include <climits> #include <climits>
#include <clocale> #include <clocale>
#include <cmath> #include <cmath>
#include <csetjmp> #include <csetjmp>
#include <cstdarg> #include <cstdarg>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <csignal> #include <csignal>
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <cwchar>
#include <exception>
#include <stdexcept> #include <stdexcept>
#include <exception>
#include <limits>
#include <new> #include <new>
#include <typeinfo>
#include <utility>
#include <functional>
#include <memory>
#include <iterator> #include <iterator>
#include <algorithm>
#include <locale>
#include <string> #include <string>
#include <vector> #include <vector>
#include <list> #include <list>
#include <set>
#include <map> #include <map>
#include <utility> #include <set>
#include <algorithm>
#include <functional> #include <ios>
#include <iostream> #include <iostream>
#include <streambuf>
#include <fstream> #include <fstream>
#include <sstream>
#if defined(_MSC_VER)
#define snprintf _snprintf
#endif
namespace std { namespace std {
using ::nan;
using ::modfl; using ::modfl;
using ::snprintf;
} }
#include <tr1/unordered_set> #if __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined
(_MSC_VER)
#include <unordered_map>
#include <unordered_set>
#else
#include <tr1/unordered_map> #include <tr1/unordered_map>
#include <tr1/unordered_set>
namespace std { namespace std {
using tr1::hash; using tr1::hash;
using tr1::unordered_map; using tr1::unordered_map;
using tr1::unordered_set; using tr1::unordered_set;
} }
#endif
#if defined(_KCUYIELD)
#if defined(_MSC_VER)
#include <windows.h>
#define _yield_() ::Sleep(0)
#else
#include <sched.h>
#define _yield_() ::sched_yield()
#endif
#define _testyield_() \
do { \
static uint32_t _KC_seed = 725; \
_KC_seed = _KC_seed * 123456761 + 211; \
if (_KC_seed % 0x100 == 0) _yield_(); \
} while(false)
#define _assert_(KC_a) \
do { \
_testyield_(); \
assert(KC_a); \
} while(false)
#elif defined(_KCDEBUG)
#define _yield_()
#define _testyield_()
#define _assert_(KC_a) assert(KC_a)
#else
#define _yield_() ///< for debugging
#define _testyield_() ///< for debugging
#define _assert_(KC_a) ///< for debugging
#endif
#endif // duplication check #endif // duplication check
// END OF FILE // END OF FILE
 End of changes. 16 change blocks. 
9 lines changed or deleted 63 lines changed or added


 kccompare.h   kccompare.h 
/************************************************************************** *********************** /************************************************************************** ***********************
* Data mapping structures * Comparator functions
* Copyright (C) 2009- 2010 Mikio Hirabayashi * Copyright (C) 2009- 2010 Mikio Hirabayashi
* This file is part of Kyoto Cabinet. * This file is part of Kyoto Cabinet.
* This program is free software: you can redistribute it and/or modify it under the terms of * This program is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundat ion, either version * the GNU General Public License as published by the Free Software Foundat ion, either version
* 3 of the License, or any later version. * 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful, but WITH OUT ANY WARRANTY; * This program is distributed in the hope that it will be useful, but WITH OUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PA RTICULAR PURPOSE. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PA RTICULAR PURPOSE.
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program. * You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
skipping to change at line 50 skipping to change at line 50
*/ */
virtual int32_t compare(const char* akbuf, size_t aksiz, const char* bkbu f, size_t bksiz) = 0; virtual int32_t compare(const char* akbuf, size_t aksiz, const char* bkbu f, size_t bksiz) = 0;
}; };
/** /**
* Comparator in the lexical order. * Comparator in the lexical order.
*/ */
class LexicalComparator : public Comparator { class LexicalComparator : public Comparator {
public: public:
int32_t compare(const char* akbuf, size_t aksiz, const char* bkbuf, size_ t bksiz) { int32_t compare(const char* akbuf, size_t aksiz, const char* bkbuf, size_ t bksiz) {
_assert_(akbuf && bkbuf);
size_t msiz = aksiz < bksiz ? aksiz : bksiz; size_t msiz = aksiz < bksiz ? aksiz : bksiz;
for (size_t i = 0; i < msiz; i++) { for (size_t i = 0; i < msiz; i++) {
if (((uint8_t*)akbuf)[i] != ((uint8_t*)bkbuf)[i]) if (((uint8_t*)akbuf)[i] != ((uint8_t*)bkbuf)[i])
return ((uint8_t*)akbuf)[i] - ((uint8_t*)bkbuf)[i]; return ((uint8_t*)akbuf)[i] - ((uint8_t*)bkbuf)[i];
} }
return (int32_t)aksiz - (int32_t)bksiz; return (int32_t)aksiz - (int32_t)bksiz;
} }
}; };
/** /**
* Comparator in the decimal order. * Comparator in the decimal order.
*/ */
class DecimalComparator : public Comparator { class DecimalComparator : public Comparator {
int32_t compare(const char* akbuf, size_t aksiz, const char* bkbuf, size_ t bksiz) { int32_t compare(const char* akbuf, size_t aksiz, const char* bkbuf, size_ t bksiz) {
_assert_(akbuf && bkbuf);
const int32_t LDBLCOLMAX = 16; const int32_t LDBLCOLMAX = 16;
const unsigned char* arp = (unsigned char*)akbuf; const unsigned char* arp = (unsigned char*)akbuf;
int32_t alen = aksiz; int32_t alen = aksiz;
while (alen > 0 && (*arp <= ' ' || *arp == 0x7f)) { while (alen > 0 && (*arp <= ' ' || *arp == 0x7f)) {
arp++; arp++;
alen--; alen--;
} }
int64_t anum = 0; int64_t anum = 0;
int32_t asign = 1; int32_t asign = 1;
if (alen > 0 && *arp == '-') { if (alen > 0 && *arp == '-') {
 End of changes. 3 change blocks. 
1 lines changed or deleted 3 lines changed or added


 kccompress.h   kccompress.h 
skipping to change at line 105 skipping to change at line 105
* delete[] operator when it is no longer in use. * delete[] operator when it is no longer in use.
*/ */
static char* decompress(Mode mode, const void* buf, size_t size, size_t* sp); static char* decompress(Mode mode, const void* buf, size_t size, size_t* sp);
}; };
/** /**
* Compressor with the Zlib raw mode. * Compressor with the Zlib raw mode.
*/ */
class ZlibRawCompressor : public Compressor { class ZlibRawCompressor : public Compressor {
char* compress(const void* buf, size_t size, size_t* sp) { char* compress(const void* buf, size_t size, size_t* sp) {
_assert_(buf && sp);
return Zlib::compress(Zlib::RAW, buf, size, sp); return Zlib::compress(Zlib::RAW, buf, size, sp);
} }
char* decompress(const void* buf, size_t size, size_t* sp) { char* decompress(const void* buf, size_t size, size_t* sp) {
_assert_(buf && sp);
return Zlib::decompress(Zlib::RAW, buf, size, sp); return Zlib::decompress(Zlib::RAW, buf, size, sp);
} }
}; };
/** /**
* Compressor with the Zlib deflate mode. * Compressor with the Zlib deflate mode.
*/ */
class ZlibDeflateCompressor : public Compressor { class ZlibDeflateCompressor : public Compressor {
char* compress(const void* buf, size_t size, size_t* sp) { char* compress(const void* buf, size_t size, size_t* sp) {
_assert_(buf && sp);
return Zlib::compress(Zlib::DEFLATE, buf, size, sp); return Zlib::compress(Zlib::DEFLATE, buf, size, sp);
} }
char* decompress(const void* buf, size_t size, size_t* sp) { char* decompress(const void* buf, size_t size, size_t* sp) {
_assert_(buf && sp);
return Zlib::decompress(Zlib::DEFLATE, buf, size, sp); return Zlib::decompress(Zlib::DEFLATE, buf, size, sp);
} }
}; };
/** /**
* Compressor with the Zlib gzip mode. * Compressor with the Zlib gzip mode.
*/ */
class ZlibGzipCompressor : public Compressor { class ZlibGzipCompressor : public Compressor {
char* compress(const void* buf, size_t size, size_t* sp) { char* compress(const void* buf, size_t size, size_t* sp) {
_assert_(buf && sp);
return Zlib::compress(Zlib::GZIP, buf, size, sp); return Zlib::compress(Zlib::GZIP, buf, size, sp);
} }
char* decompress(const void* buf, size_t size, size_t* sp) { char* decompress(const void* buf, size_t size, size_t* sp) {
_assert_(buf && sp);
return Zlib::decompress(Zlib::GZIP, buf, size, sp); return Zlib::decompress(Zlib::GZIP, buf, size, sp);
} }
}; };
/** /**
* Prepared variable of the compressor with the Zlib raw mode. * Prepared variable of the compressor with the Zlib raw mode.
*/ */
extern ZlibRawCompressor ZLIBRAWCOMP; extern ZlibRawCompressor ZLIBRAWCOMP;
/** /**
 End of changes. 6 change blocks. 
0 lines changed or deleted 6 lines changed or added


 kcdb.h   kcdb.h 
skipping to change at line 24 skipping to change at line 24
#ifndef _KCDB_H // duplication check #ifndef _KCDB_H // duplication check
#define _KCDB_H #define _KCDB_H
#include <kccommon.h> #include <kccommon.h>
#include <kcutil.h> #include <kcutil.h>
namespace kyotocabinet { // common namespace namespace kyotocabinet { // common namespace
/** /**
* Constants for implementation.
*/
namespace {
const char DBSSMAGICDATA[] = "KCSS\n"; ///< magic data of the file
const size_t DBIOBUFSIZ = 8192; ///< size of the IO buffer
}
/**
* Interface of database abstraction. * Interface of database abstraction.
*/ */
class DB { class DB {
public: public:
/** /**
* Database types. * Database types.
*/ */
enum Type { enum Type {
TYPEPROTO = 0x01, ///< prototype database TYPEVOID = 0x00, ///< void database
TYPECACHE = 0x02, ///< cache database TYPEPHASH = 0x01, ///< prototype hash database
TYPEPTREE = 0x02, ///< prototype tree database
TYPEPMISC = 0x08, ///< miscellaneous prototype datab
ase
TYPECACHE = 0x09, ///< cache database
TYPEHASH = 0x11, ///< file hash database TYPEHASH = 0x11, ///< file hash database
TYPETREE = 0x12 ///< file tree database TYPETREE = 0x12, ///< file tree database
TYPEMISC = 0x20 ///< miscellaneous database
}; };
/** /**
* Get the string of a database type. * Get the string of a database type.
* @param type the database type. * @param type the database type.
* @return the string of the type name. * @return the string of the type name.
*/ */
static const char* typestring(uint32_t type) { static const char* typestring(uint32_t type) {
_assert_(true);
switch (type) { switch (type) {
case TYPEPROTO: return "prototype database"; case TYPEVOID: return "void";
case TYPEPHASH: return "prototype hash database";
case TYPEPTREE: return "prototype tree database";
case TYPEPMISC: return "miscellaneous prototype database";
case TYPECACHE: return "cache database"; case TYPECACHE: return "cache database";
case TYPEHASH: return "file hash database"; case TYPEHASH: return "file hash database";
case TYPETREE: return "file tree database"; case TYPETREE: return "file tree database";
case TYPEMISC: return "miscellaneous database";
} }
return "unknown"; return "unknown";
} }
/** /**
* Interface to access a record. * Interface to access a record.
*/ */
class Visitor { class Visitor {
public: public:
/** Special pointer for no operation. */ /** Special pointer for no operation. */
static const char* const NOP; static const char* const NOP;
/** Special pointer to remove the record. */ /** Special pointer to remove the record. */
static const char* const REMOVE; static const char* const REMOVE;
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Visitor() {} virtual ~Visitor() {
_assert_(true);
}
/** /**
* Visit a record. * Visit a record.
* @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.
* @param vbuf the pointer to the value region. * @param vbuf the pointer to the value region.
* @param vsiz the size of the value region. * @param vsiz the size of the value region.
* @param sp the pointer to the variable into which the size of the reg ion of the return * @param sp the pointer to the variable into which the size of the reg ion of the return
* value is assigned. * value is assigned.
* @return If it is the pointer to a region, the value is replaced by t he content. If it * @return If it is the pointer to a region, the value is replaced by t he content. If it
* is Visitor::NOP, nothing is modified. If it is Visitor::REMOVE, the record is removed. * is Visitor::NOP, nothing is modified. If it is Visitor::REMOVE, the record is removed.
*/ */
virtual const char* visit_full(const char* kbuf, size_t ksiz, virtual const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* s p) { const char* vbuf, size_t vsiz, size_t* s p) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ && sp );
return NOP; return NOP;
} }
/** /**
* Visit a empty record space. * Visit a empty record space.
* @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.
* @param sp the pointer to the variable into which the size of the reg ion of the return * @param sp the pointer to the variable into which the size of the reg ion of the return
* value is assigned. * value is assigned.
* @return If it is the pointer to a region, the value is replaced by t he content. If it * @return If it is the pointer to a region, the value is replaced by t he content. If it
* is Visitor::NOP or Visitor::REMOVE, nothing is modified. * is Visitor::NOP or Visitor::REMOVE, nothing is modified.
*/ */
virtual const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) { virtual const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && sp);
return NOP; return NOP;
} }
}; };
/** /**
* Interface of cursor to indicate a record. * Interface of cursor to indicate a record.
*/ */
class Cursor { class Cursor {
public: public:
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() {} virtual ~Cursor() {
_assert_(true);
}
/** /**
* 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.
*/ */
virtual bool accept(Visitor* visitor, bool writable, bool step) = 0; virtual bool accept(Visitor* visitor, bool writable = true, bool step =
false) = 0;
/**
* Set the value of the current record.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @param step true to move the cursor to the next record, or false for
no move.
* @return true on success, or false on failure.
*/
virtual bool set_value(const char* vbuf, size_t vsiz, bool step = false
) = 0;
/**
* Set the value of the current record.
* @note Equal to the original Cursor::set_value method except that the
parameter is
* std::string.
*/
virtual bool set_value(const std::string& value, bool step = false) = 0
;
/**
* Remove the current record.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned. The c
ursor is moved to the
* next record implicitly.
*/
virtual bool remove() = 0;
/** /**
* Get the key of the current record. * Get the key of the current record.
* @param sp the pointer to the variable into which the size of the reg ion of the return * @param sp the pointer to the variable into which the size of the reg ion of the return
* value is assigned. * value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the key region of the current record, or NULL on failure. * @return the pointer to the key region of the current record, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero * @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero
* code is appended at the end of the region of the return value, the r eturn value can be * code is appended at the end of the region of the return value, the r eturn value can be
* treated as a C-style string. Because the region of the return value is allocated with the * treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer * the new[] operator, it should be released with the delete[] operator when it is no longer
* in use. * in use.
*/ */
virtual char* get_key(size_t* sp) = 0; virtual char* get_key(size_t* sp, bool step = false) = 0;
/** /**
* Get the key of the current record. * Get the key of the current record.
* @note Equal to the original Cursor::get_key method except that the p arameter and the * @note Equal to the original Cursor::get_key method except that the p arameter and the
* return value are std::string. The return value should be deleted ex plicitly by the * return value are std::string. The return value should be deleted ex plicitly by the
* caller. * caller.
*/ */
virtual std::string* get_key() = 0; virtual std::string* get_key(bool step = false) = 0;
/** /**
* Get the value of the current record. * Get the value of the current record.
* @param sp the pointer to the variable into which the size of the reg ion of the return * @param sp the pointer to the variable into which the size of the reg ion of the return
* value is assigned. * value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the value region of the current record, or NU LL on failure. * @return the pointer to the value region of the current record, or NU LL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero * @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero
* code is appended at the end of the region of the return value, the r eturn value can be * code is appended at the end of the region of the return value, the r eturn value can be
* treated as a C-style string. Because the region of the return value is allocated with the * treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer * the new[] operator, it should be released with the delete[] operator when it is no longer
* in use. * in use.
*/ */
virtual char* get_value(size_t* sp) = 0; virtual char* get_value(size_t* sp, bool step = false) = 0;
/** /**
* Get the value of the current record. * Get the value of the current record.
* @note Equal to the original Cursor::get_value method except that the parameter and the * @note Equal to the original Cursor::get_value method except that the parameter and the
* return value are std::string. The return value should be deleted ex plicitly by the * return value are std::string. The return value should be deleted ex plicitly by the
* caller. * caller.
*/ */
virtual std::string* get_value() = 0; virtual std::string* get_value(bool step = false) = 0;
/** /**
* Get a pair of the key and the value of the current record. * Get a pair of the key and the value of the current record.
* @param ksp the pointer to the variable into which the size of the re gion of the return * @param ksp the pointer to the variable into which the size of the re gion of the return
* value is assigned. * value is assigned.
* @param vbp the pointer to the variable into which the pointer to the value region is * @param vbp the pointer to the variable into which the pointer to the value region is
* assigned. * assigned.
* @param vsp the pointer to the variable into which the size of the va lue region is * @param vsp the pointer to the variable into which the size of the va lue region is
* assigned. * assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the pair of the key region, or NULL on failur e. * @return the pointer to the pair of the key region, or NULL on failur e.
* @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero code is * @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero code is
* appended at the end of each region of the key and the value, each re gion can be treated * appended at the end of each region of the key and the value, each re gion can be treated
* as a C-style string. The return value should be deleted explicitly by the caller with * as a C-style string. The return value should be deleted explicitly by the caller with
* the detele[] operator. * the detele[] operator.
*/ */
virtual char* get(size_t* ksp, const char** vbp, size_t* vsp) = 0; virtual char* get(size_t* ksp, const char** vbp, size_t* vsp, bool step = false) = 0;
/** /**
* Get a pair of the key and the value of the current record. * Get a pair of the key and the value of the current record.
* @return the pointer to the pair of the key and the value, or NULL on failure. * @return the pointer to the pair of the key and the value, or NULL on failure.
* @note Equal to the original Cursor::get method except that the retur n value is std::pair. * @note Equal to the original Cursor::get method except that the retur n value is std::pair.
* The return value should be deleted explicitly by the caller. * The return value should be deleted explicitly by the caller.
* @note If the cursor is invalidated, NULL is returned. The return va lue should be deleted * @note If the cursor is invalidated, NULL is returned. The return va lue should be deleted
* explicitly by the caller. * explicitly by the caller.
*/ */
virtual std::pair<std::string, std::string>* get_pair() = 0; virtual std::pair<std::string, std::string>* get_pair(bool step = false
/** ) = 0;
* Remove the current record.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned. The c
ursor is moved to the
* next record implicitly.
*/
virtual bool remove() = 0;
/** /**
* Jump the cursor to the first record. * Jump the cursor to the first record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool jump() = 0; virtual bool jump() = 0;
/** /**
* Jump the cursor to a record. * Jump the cursor to a record.
* @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.
skipping to change at line 200 skipping to change at line 240
/** /**
* Jump the cursor to a record. * Jump the cursor to a record.
* @note Equal to the original Cursor::set method except that the param eter is std::string. * @note Equal to the original Cursor::set method except that the param eter is std::string.
*/ */
virtual bool jump(const std::string& key) = 0; virtual bool jump(const std::string& key) = 0;
/** /**
* 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.
*/ */
virtual bool step() = 0; virtual bool step() = 0;
/**
* Get the database object.
* @return the database object.
*/
virtual DB* db() = 0;
}; };
/** /**
* Default constructor.
*/
explicit DB() {}
/**
* Destructor. * Destructor.
*/ */
virtual ~DB() {} virtual ~DB() {
_assert_(true);
}
/** /**
* Accept a visitor to a record. * Accept a visitor to a record.
* @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.
* @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
* same record are blocked.
*/ */
virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writable) = 0; virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writable = true) = 0;
/** /**
* 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.
* @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.
*/ */
virtual bool iterate(Visitor *visitor, bool writable) = 0; virtual bool iterate(Visitor *visitor, bool writable = true) = 0;
/** /**
* Set the value of a record. * Set the value of a record.
* @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.
* @param vbuf the pointer to the value region. * @param vbuf the pointer to the value region.
* @param vsiz the size of the value region. * @param vsiz the size of the value region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. I f the corresponding * @note If no record corresponds to the key, a new record is created. I f the corresponding
* record exists, the value is overwritten. * record exists, the value is overwritten.
*/ */
skipping to change at line 289 skipping to change at line 335
*/ */
virtual int64_t increment(const char* kbuf, size_t ksiz, int64_t num) = 0 ; virtual int64_t increment(const char* kbuf, size_t ksiz, int64_t num) = 0 ;
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the param eter is std::string. * @note Equal to the original DB::increment method except that the param eter is std::string.
*/ */
virtual int64_t increment(const std::string& key, int64_t num) = 0; virtual int64_t increment(const std::string& key, int64_t num) = 0;
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the param eter and the return * @note Equal to the original DB::increment method except that the param eter and the return
* value are double. * value are double. Not-a-number is returned on failure.
*/ */
virtual double increment(const char* kbuf, size_t ksiz, double num) = 0; virtual double increment(const char* kbuf, size_t ksiz, double num) = 0;
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the param eter is std::string * @note Equal to the original DB::increment method except that the param eter is std::string
* and the return value is double. * and the return value is double. Not-a-number is returned on failure.
*/ */
virtual double increment(const std::string& key, double num) = 0; virtual double increment(const std::string& key, double num) = 0;
/** /**
* Perform compare-and-swap. * Perform compare-and-swap.
* @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.
* @param ovbuf the pointer to the old value region. NULL means that no record corresponds. * @param ovbuf the pointer to the old value region. NULL means that no record corresponds.
* @param ovsiz the size of the old value region. * @param ovsiz the size of the old value region.
* @param nvbuf the pointer to the new value region. NULL means that the record is removed. * @param nvbuf the pointer to the new value region. NULL means that the record is removed.
* @param nvsiz the size of new old value region. * @param nvsiz the size of new old value region.
skipping to change at line 371 skipping to change at line 417
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool clear() = 0; virtual bool clear() = 0;
/** /**
* 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.
*/ */
virtual int64_t count() = 0; virtual int64_t count() = 0;
/** /**
* Create a cursor object. * Create a cursor object.
* @return the return value is the cursor object. * @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the const ructor, it should be * @note Because the object of the return value is allocated by the const ructor, it should be
* released with the delete operator when it is no longer in use. * released with the delete operator when it is no longer in use.
*/ */
virtual Cursor* cursor() = 0; virtual Cursor* cursor() = 0;
}; };
/** /**
* Basic implementation for file database. * Basic implementation for file database.
* @note Before every database operation, it is necessary to call the open * @note Before every database operation, it is necessary to call the FileD
method in order to B::open method in
* open a database file and connect the database object to it. To avoid da * order to open a database file and connect the database object to it. To
ta missing or avoid data missing
* corruption, it is important to close every database file by the close me * or corruption, it is important to close every database file by the FileD
thod when the B::close method when
* database is no longer in use. It is forbidden for multible database obj * the database is no longer in use. It is forbidden for multible database
ects in a process objects in a process
* to open the same database at the same time. * to open the same database at the same time.
*/ */
class FileDB : public DB { class FileDB : public DB {
public: public:
class Error;
class Cursor;
public:
/** /**
* Interface of cursor to indicate a record. * Interface of cursor to indicate a record.
*/ */
class Cursor : public DB::Cursor { class Cursor : public DB::Cursor {
public: public:
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() {} virtual ~Cursor() {
_assert_(true);
}
/**
* Set the value of the current record.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @param step true to move the cursor to the next record, or false for
no move.
* @return true on success, or false on failure.
*/
virtual bool set_value(const char* vbuf, size_t vsiz, bool step = false
) {
_assert_(vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
*sp = vsiz_;
return vbuf_;
}
const char* vbuf_;
size_t vsiz_;
bool ok_;
};
VisitorImpl visitor(vbuf, vsiz);
if (!accept(&visitor, true, step)) return false;
if (!visitor.ok()) return false;
return true;
}
/**
* Set the value of the current record.
* @note Equal to the original Cursor::set_value method except that the
parameter is
* std::string.
*/
virtual bool set_value(const std::string& value, bool step = false) {
_assert_(true);
return set_value(value.c_str(), value.size(), step);
}
/**
* Remove the current record.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned. The c
ursor is moved to the
* next record implicitly.
*/
virtual bool remove() {
_assert_(true);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
return REMOVE;
}
bool ok_;
};
VisitorImpl visitor;
if (!accept(&visitor, true, false)) return false;
if (!visitor.ok()) return false;
return true;
}
/** /**
* Get the key of the current record. * Get the key of the current record.
* @param sp the pointer to the variable into which the size of the reg ion of the return * @param sp the pointer to the variable into which the size of the reg ion of the return
* value is assigned. * value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the key region of the current record, or NULL on failure. * @return the pointer to the key region of the current record, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero * @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero
* code is appended at the end of the region of the return value, the r eturn value can be * code is appended at the end of the region of the return value, the r eturn value can be
* treated as a C-style string. Because the region of the return value is allocated with the * treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer * the new[] operator, it should be released with the delete[] operator when it is no longer
* in use. * in use.
*/ */
virtual char* get_key(size_t* sp) { virtual char* get_key(size_t* sp, bool step = false) {
_assert_(sp);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl() : kbuf_(NULL), ksiz_(0) {} explicit VisitorImpl() : kbuf_(NULL), ksiz_(0) {}
char* pop(size_t* sp) { char* pop(size_t* sp) {
*sp = ksiz_; *sp = ksiz_;
return kbuf_; return kbuf_;
} }
void clear() { void clear() {
delete[] kbuf_; delete[] kbuf_;
} }
skipping to change at line 432 skipping to change at line 553
kbuf_ = new char[ksiz+1]; kbuf_ = new char[ksiz+1];
std::memcpy(kbuf_, kbuf, ksiz); std::memcpy(kbuf_, kbuf, ksiz);
kbuf_[ksiz] = '\0'; kbuf_[ksiz] = '\0';
ksiz_ = ksiz; ksiz_ = ksiz;
return NOP; return NOP;
} }
char* kbuf_; char* kbuf_;
size_t ksiz_; size_t ksiz_;
}; };
VisitorImpl visitor; VisitorImpl visitor;
if (!accept(&visitor, false, false)) { if (!accept(&visitor, false, step)) {
visitor.clear(); visitor.clear();
*sp = 0; *sp = 0;
return NULL; return NULL;
} }
size_t ksiz; size_t ksiz;
char* kbuf = visitor.pop(&ksiz); char* kbuf = visitor.pop(&ksiz);
if (!kbuf) { if (!kbuf) {
*sp = 0; *sp = 0;
return NULL; return NULL;
} }
*sp = ksiz; *sp = ksiz;
return kbuf; return kbuf;
} }
/** /**
* Get the key of the current record. * Get the key of the current record.
* @note Equal to the original Cursor::key method except that the param eter and the return * @note Equal to the original Cursor::key method except that the param eter and the return
* value are std::string. * value are std::string.
*/ */
virtual std::string* get_key() { virtual std::string* get_key(bool step = false) {
_assert_(true);
size_t ksiz; size_t ksiz;
char* kbuf = get_key(&ksiz); char* kbuf = get_key(&ksiz, step);
if (!kbuf) return NULL; if (!kbuf) return NULL;
std::string* key = new std::string(kbuf, ksiz); std::string* key = new std::string(kbuf, ksiz);
delete[] kbuf; delete[] kbuf;
return key; return key;
} }
/** /**
* Get the value of the current record. * Get the value of the current record.
* @param sp the pointer to the variable into which the size of the reg ion of the return * @param sp the pointer to the variable into which the size of the reg ion of the return
* value is assigned. * value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the value region of the current record, or NU LL on failure. * @return the pointer to the value region of the current record, or NU LL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero * @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero
* code is appended at the end of the region of the return value, the r eturn value can be * code is appended at the end of the region of the return value, the r eturn value can be
* treated as a C-style string. Because the region of the return value is allocated with the * treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer * the new[] operator, it should be released with the delete[] operator when it is no longer
* in use. * in use.
*/ */
virtual char* get_value(size_t* sp) { virtual char* get_value(size_t* sp, bool step = false) {
_assert_(sp);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {} explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* sp) { char* pop(size_t* sp) {
*sp = vsiz_; *sp = vsiz_;
return vbuf_; return vbuf_;
} }
void clear() { void clear() {
delete[] vbuf_; delete[] vbuf_;
} }
skipping to change at line 494 skipping to change at line 618
vbuf_ = new char[vsiz+1]; vbuf_ = new char[vsiz+1];
std::memcpy(vbuf_, vbuf, vsiz); std::memcpy(vbuf_, vbuf, vsiz);
vbuf_[vsiz] = '\0'; vbuf_[vsiz] = '\0';
vsiz_ = vsiz; vsiz_ = vsiz;
return NOP; return NOP;
} }
char* vbuf_; char* vbuf_;
size_t vsiz_; size_t vsiz_;
}; };
VisitorImpl visitor; VisitorImpl visitor;
if (!accept(&visitor, false, false)) { if (!accept(&visitor, false, step)) {
visitor.clear(); visitor.clear();
*sp = 0; *sp = 0;
return NULL; return NULL;
} }
size_t vsiz; size_t vsiz;
char* vbuf = visitor.pop(&vsiz); char* vbuf = visitor.pop(&vsiz);
if (!vbuf) { if (!vbuf) {
*sp = 0; *sp = 0;
return NULL; return NULL;
} }
*sp = vsiz; *sp = vsiz;
return vbuf; return vbuf;
} }
/** /**
* Get the value of the current record. * Get the value of the current record.
* @note Equal to the original Cursor::value method except that the par ameter and the return * @note Equal to the original Cursor::value method except that the par ameter and the return
* value are std::string. * value are std::string.
*/ */
virtual std::string* get_value() { virtual std::string* get_value(bool step = false) {
_assert_(true);
size_t vsiz; size_t vsiz;
char* vbuf = get_value(&vsiz); char* vbuf = get_value(&vsiz, step);
if (!vbuf) return NULL; if (!vbuf) return NULL;
std::string* value = new std::string(vbuf, vsiz); std::string* value = new std::string(vbuf, vsiz);
delete[] vbuf; delete[] vbuf;
return value; return value;
} }
/** /**
* Get a pair of the key and the value of the current record. * Get a pair of the key and the value of the current record.
* @param ksp the pointer to the variable into which the size of the re gion of the return * @param ksp the pointer to the variable into which the size of the re gion of the return
* value is assigned. * value is assigned.
* @param vbp the pointer to the variable into which the pointer to the value region is * @param vbp the pointer to the variable into which the pointer to the value region is
* assigned. * assigned.
* @param vsp the pointer to the variable into which the size of the va lue region is * @param vsp the pointer to the variable into which the size of the va lue region is
* assigned. * assigned.
* @return the pointer to the pair of the key region, or NULL on failur e. * @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the pair of the key region, or NULL on failur e. * @return the pointer to the pair of the key region, or NULL on failur e.
* @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero code is * @note If the cursor is invalidated, NULL is returned. Because an ad ditional zero code is
* appended at the end of each region of the key and the value, each re gion can be treated * appended at the end of each region of the key and the value, each re gion can be treated
* as a C-style string. The return value should be deleted explicitly by the caller with * as a C-style string. The return value should be deleted explicitly by the caller with
* the detele[] operator. * the detele[] operator.
*/ */
virtual char* get(size_t* ksp, const char** vbp, size_t* vsp) { virtual char* get(size_t* ksp, const char** vbp, size_t* vsp, bool step
= false) {
_assert_(ksp && vbp && vsp);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl() : kbuf_(NULL), ksiz_(0), vbuf_(NULL), vsiz_( 0) {} explicit VisitorImpl() : kbuf_(NULL), ksiz_(0), vbuf_(NULL), vsiz_( 0) {}
char* pop(size_t* ksp, const char** vbp, size_t* vsp) { char* pop(size_t* ksp, const char** vbp, size_t* vsp) {
*ksp = ksiz_; *ksp = ksiz_;
*vbp = vbuf_; *vbp = vbuf_;
*vsp = vsiz_; *vsp = vsiz_;
return kbuf_; return kbuf_;
} }
void clear() { void clear() {
skipping to change at line 569 skipping to change at line 695
vbuf_[vsiz] = '\0'; vbuf_[vsiz] = '\0';
vsiz_ = vsiz; vsiz_ = vsiz;
return NOP; return NOP;
} }
char* kbuf_; char* kbuf_;
size_t ksiz_; size_t ksiz_;
char* vbuf_; char* vbuf_;
size_t vsiz_; size_t vsiz_;
}; };
VisitorImpl visitor; VisitorImpl visitor;
if (!accept(&visitor, false, false)) { if (!accept(&visitor, false, step)) {
visitor.clear(); visitor.clear();
*ksp = 0; *ksp = 0;
*vbp = NULL; *vbp = NULL;
*vsp = 0; *vsp = 0;
return NULL; return NULL;
} }
return visitor.pop(ksp, vbp, vsp); return visitor.pop(ksp, vbp, vsp);
} }
/** /**
* Get a pair of the key and the value of the current record. * Get a pair of the key and the value of the current record.
* @return the pointer to the pair of the key and the value, or NULL on failure. * @return the pointer to the pair of the key and the value, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. The return va lue should be deleted * @note If the cursor is invalidated, NULL is returned. The return va lue should be deleted
* explicitly by the caller. * explicitly by the caller.
*/ */
virtual std::pair<std::string, std::string>* get_pair() { virtual std::pair<std::string, std::string>* get_pair(bool step = false
) {
_assert_(true);
typedef std::pair<std::string, std::string> Record; typedef std::pair<std::string, std::string> Record;
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl() : rec_(NULL) {} explicit VisitorImpl() : rec_(NULL) {}
Record* pop() { Record* pop() {
return rec_; return rec_;
} }
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) {
std::string key(kbuf, ksiz); std::string key(kbuf, ksiz);
std::string value(vbuf, vsiz); std::string value(vbuf, vsiz);
rec_ = new Record(key, value); rec_ = new Record(key, value);
return NOP; return NOP;
} }
Record* rec_; Record* rec_;
}; };
VisitorImpl visitor; VisitorImpl visitor;
if (!accept(&visitor, false, false)) return NULL; if (!accept(&visitor, false, step)) return NULL;
return visitor.pop(); return visitor.pop();
} }
/** /**
* Remove the current record. * Get the database object.
* @return true on success, or false on failure. * @return the database object.
* @note If no record corresponds to the key, false is returned. The c
ursor is moved to the
* next record implicitly.
*/ */
virtual bool remove() { virtual FileDB* db() = 0;
class VisitorImpl : public Visitor { /**
public: * Get the last happened error.
explicit VisitorImpl() : ok_(false) {} * @return the last happened error.
bool ok() const { */
return ok_; virtual Error error() {
} _assert_(true);
private: return db()->error();
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
return REMOVE;
}
bool ok_;
};
VisitorImpl visitor;
if (!accept(&visitor, true, false)) return false;
if (!visitor.ok()) return false;
return true;
} }
}; };
/** /**
* Error data. * Error data.
*/ */
class Error { class Error {
public: public:
/** /**
* Error codes. * Error codes.
*/ */
skipping to change at line 657 skipping to change at line 771
BROKEN, ///< broken file BROKEN, ///< broken file
DUPREC, ///< record duplication DUPREC, ///< record duplication
NOREC, ///< no record NOREC, ///< no record
LOGIC, ///< logical inconsistency LOGIC, ///< logical inconsistency
SYSTEM, ///< system error SYSTEM, ///< system error
MISC = 15 ///< miscellaneous error MISC = 15 ///< miscellaneous error
}; };
/** /**
* Default constructor. * Default constructor.
*/ */
explicit Error() : code_(SUCCESS), message_("no error") {} explicit Error() : code_(SUCCESS), message_("no error") {
_assert_(true);
}
/** /**
* Constructor. * Constructor.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
explicit Error(Code code, const char* message) : code_(code), message_( explicit Error(Code code, const char* message) : code_(code), message_(
message) {} message) {
_assert_(message);
}
/** /**
* Destructor. * Destructor.
*/ */
~Error() {} ~Error() {
_assert_(true);
}
/** /**
* Set the error information. * Set the error information.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
void set(Code code, const char* message) { void set(Code code, const char* message) {
code_ = code; code_ = code;
message_ = message; message_ = message;
} }
/** /**
* Get the error code. * Get the error code.
* @return the error code. * @return the error code.
*/ */
Code code() const { Code code() const {
return code_; return code_;
} }
/** /**
* Get the error message string. * Get the readable string of the code.
* @return the error message string. * @return the readable string of the code.
*/ */
std::string string() const { const char* name() const {
switch (code_) { return codename(code_);
case SUCCESS: return std::string("success: ").append(message_); }
case NOIMPL: return std::string("not implemented: ").append(message /**
_); * Get the supplement message.
case INVALID: return std::string("invalid operation: ").append(mess * @return the supplement message.
age_); */
case NOFILE: return std::string("file not found: ").append(message_ const char* message() const {
); return message_;
case NOPERM: return std::string("no permission: ").append(message_) }
; /**
case BROKEN: return std::string("broken file: ").append(message_); * Get the readable string of an error code.
case DUPREC: return std::string("record duplication: ").append(mess * @param code the error code.
age_); * @return the readable string of the error code.
case NOREC: return std::string("no record: ").append(message_); */
case LOGIC: return std::string("logical inconsistency: ").append(me static const char* codename(Code code) {
ssage_); switch (code) {
case SYSTEM: return std::string("system error: ").append(message_); case SUCCESS: return "success";
case NOIMPL: return "not implemented";
case INVALID: return "invalid operation";
case NOFILE: return "file not found";
case NOPERM: return "no permission";
case BROKEN: return "broken file";
case DUPREC: return "record duplication";
case NOREC: return "no record";
case LOGIC: return "logical inconsistency";
case SYSTEM: return "system error";
default: break; default: break;
} }
return std::string("miscellaneous error: ").append(message_); return "miscellaneous error";
}
/**
* Cast operator to integer.
* @return the error code.
*/
operator int32_t() {
return code_;
} }
private: private:
/** Error code. */ /** The error code. */
Code code_; Code code_;
/** Supplement message. */ /** The supplement message. */
const char* message_; const char* message_;
}; };
/** /**
* Interface to process the database file. * Interface to process the database file.
*/ */
class FileProcessor { class FileProcessor {
public: public:
/** /**
* Destructor. * Destructor.
*/ */
virtual ~FileProcessor() {} virtual ~FileProcessor() {
_assert_(true);
}
/** /**
* Process the database file. * Process the database file.
* @param path the path of the database file. * @param path the path of the database file.
* @param count the number of records.
* @param size the size of the available region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool process(const std::string& path) = 0; virtual bool process(const std::string& path, int64_t count, int64_t si ze) = 0;
}; };
/** /**
* Open modes. * Open modes.
*/ */
enum OpenMode { enum OpenMode {
OREADER = 1 << 0, ///< open as a reader OREADER = 1 << 0, ///< open as a reader
OWRITER = 1 << 1, ///< open as a writer OWRITER = 1 << 1, ///< open as a writer
OCREATE = 1 << 2, ///< writer creating OCREATE = 1 << 2, ///< writer creating
OTRUNCATE = 1 << 3, ///< writer truncating OTRUNCATE = 1 << 3, ///< writer truncating
OAUTOTRAN = 1 << 4, ///< auto transaction OAUTOTRAN = 1 << 4, ///< auto transaction
OAUTOSYNC = 1 << 5, ///< auto synchronization OAUTOSYNC = 1 << 5, ///< auto synchronization
ONOLOCK = 1 << 6, ///< open without locking ONOLOCK = 1 << 6, ///< open without locking
OTRYLOCK = 1 << 7, ///< lock without blocking OTRYLOCK = 1 << 7, ///< lock without blocking
ONOREPAIR = 1 << 8 ///< open without auto repair ONOREPAIR = 1 << 8 ///< open without auto repair
}; };
/** /**
* Destructor. * Destructor.
* @note If the database is not closed, it is closed implicitly. * @note If the database is not closed, it is closed implicitly.
*/ */
virtual ~FileDB() {} virtual ~FileDB() {
_assert_(true);
}
/** /**
* Get the last happened error. * Get the last happened error.
* @return the last happened error. * @return the last happened error.
*/ */
virtual Error error() const = 0; virtual Error error() const = 0;
/** /**
* Set the error information. * Set the error information.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
skipping to change at line 770 skipping to change at line 918
* The following may be added to the writer mode by bitwise-or: FileDB::O CREATE, which means * The following may be added to the writer mode by bitwise-or: FileDB::O CREATE, which means
* it creates a new database if the file does not exist, FileDB::OTRUNCAT E, which means it * it creates a new database if the file does not exist, FileDB::OTRUNCAT E, which means it
* creates a new database regardless if the file exists, FileDB::OAUTOTRA N, which means each * creates a new database regardless if the file exists, FileDB::OAUTOTRA N, which means each
* updating operation is performed in implicit transaction, FileDB::OAUTO SYNC, which means * updating operation is performed in implicit transaction, FileDB::OAUTO SYNC, which means
* each updating operation is followed by implicit synchronization with t he file system. The * each updating operation is followed by implicit synchronization with t he file system. The
* following may be added to both of the reader mode and the writer mode by bitwise-or: * following may be added to both of the reader mode and the writer mode by bitwise-or:
* FileDB::ONOLOCK, which means it opens the database file without file l ocking, * FileDB::ONOLOCK, which means it opens the database file without file l ocking,
* FileDB::OTRYLOCK, which means locking is performed without blocking, F ile::ONOREPAIR, which * FileDB::OTRYLOCK, which means locking is performed without blocking, F ile::ONOREPAIR, which
* means the database file is not repaired implicitly even if file destru ction is detected. * means the database file is not repaired implicitly even if file destru ction is 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 FileDB::close method
when it is no longer
* in use.
*/ */
virtual bool open(const std::string& path, uint32_t mode) = 0; virtual bool open(const std::string& path, uint32_t mode = OWRITER | OCRE ATE) = 0;
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool close() = 0; virtual bool close() = 0;
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool synchronize(bool hard, FileProcessor* proc) = 0; virtual bool synchronize(bool hard = false, FileProcessor* proc = NULL) =
0;
/**
* Create a copy of the database file.
* @param dest the path of the destination file.
* @return true on success, or false on failure.
*/
virtual bool copy(const std::string& dest) {
_assert_(true);
class FileProcessorImpl : public FileProcessor {
public:
FileProcessorImpl(const std::string& dest) : dest_(dest) {}
private:
bool process(const std::string& path, int64_t count, int64_t size) {
std::ofstream ofs;
ofs.open(dest_.c_str(),
std::ios_base::out | std::ios_base::binary | std::ios_base
::trunc);
if (!ofs) return false;
bool err = false;
std::ifstream ifs;
ifs.open(path.c_str(), std::ios_base::in | std::ios_base::binary);
if (ifs) {
char buf[DBIOBUFSIZ];
while (!ifs.eof()) {
size_t n = ifs.read(buf, sizeof(buf)).gcount();
if (n > 0) {
ofs.write(buf, n);
if (!ofs) {
err = true;
break;
}
}
}
ifs.close();
if (ifs.bad()) err = true;
} else {
err = true;
}
ofs.close();
if (!ofs) err = true;
return !err;
}
const std::string& dest_;
};
FileProcessorImpl proc(dest);
return synchronize(false, &proc);
}
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool begin_transaction(bool hard) = 0; virtual bool begin_transaction(bool hard = false) = 0;
/** /**
* Commit transaction. * Try to begin transaction.
* @param hard true for physical synchronization with the device, or fals
e for logical
* synchronization with the file system.
* @return true on success, or false on failure.
*/
virtual bool begin_transaction_try(bool hard = false) = 0;
/**
* 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.
*/ */
virtual bool end_transaction(bool commit) = 0; virtual bool end_transaction(bool commit = true) = 0;
/** /**
* 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.
*/ */
virtual int64_t size() = 0; virtual int64_t size() = 0;
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file in bytes, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
virtual std::string path() = 0; virtual std::string path() = 0;
/** /**
* 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.
*/ */
virtual bool status(std::map<std::string, std::string>* strmap) = 0; virtual bool status(std::map<std::string, std::string>* strmap) = 0;
/** /**
* Set the value of a record. * Set the value of a record.
* @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.
* @param vbuf the pointer to the value region. * @param vbuf the pointer to the value region.
* @param vsiz the size of the value region. * @param vsiz the size of the value region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. I f the corresponding * @note If no record corresponds to the key, a new record is created. I f the corresponding
* record exists, the value is overwritten. * record exists, the value is overwritten.
*/ */
virtual bool set(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) { virtual bool set(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) : vbuf_(vbuf), vs iz_(vsiz) {} explicit VisitorImpl(const char* vbuf, size_t vsiz) : vbuf_(vbuf), vs iz_(vsiz) {}
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) {
*sp = vsiz_; *sp = vsiz_;
return vbuf_; return vbuf_;
} }
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) { const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
skipping to change at line 850 skipping to change at line 1053
}; };
VisitorImpl visitor(vbuf, vsiz); VisitorImpl visitor(vbuf, vsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false; if (!accept(kbuf, ksiz, &visitor, true)) return false;
return true; return true;
} }
/** /**
* Set the value of a record. * Set the value of a record.
* @note Equal to the original DB::set method except that the parameters are std::string. * @note Equal to the original DB::set method except that the parameters are std::string.
*/ */
virtual bool set(const std::string& key, const std::string& value) { virtual bool set(const std::string& key, const std::string& value) {
_assert_(true);
return set(key.c_str(), key.size(), value.c_str(), value.size()); return set(key.c_str(), key.size(), value.c_str(), value.size());
} }
/** /**
* Add a record. * Add a record.
* @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.
* @param vbuf the pointer to the value region. * @param vbuf the pointer to the value region.
* @param vsiz the size of the value region. * @param vsiz the size of the value region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. I f the corresponding * @note If no record corresponds to the key, a new record is created. I f the corresponding
* record exists, the record is not modified and false is returned. * record exists, the record is not modified and false is returned.
*/ */
virtual bool add(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) { virtual bool add(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) : explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), ok_(false) {} vbuf_(vbuf), vsiz_(vsiz), ok_(false) {}
bool ok() const { bool ok() const {
return ok_; return ok_;
} }
private: private:
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) { const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
ok_ = true; ok_ = true;
skipping to change at line 893 skipping to change at line 1098
set_error(Error::DUPREC, "record duplication"); set_error(Error::DUPREC, "record duplication");
return false; return false;
} }
return true; return true;
} }
/** /**
* Set the value of a record. * Set the value of a record.
* @note Equal to the original DB::add method except that the parameters are std::string. * @note Equal to the original DB::add method except that the parameters are std::string.
*/ */
virtual bool add(const std::string& key, const std::string& value) { virtual bool add(const std::string& key, const std::string& value) {
_assert_(true);
return add(key.c_str(), key.size(), value.c_str(), value.size()); return add(key.c_str(), key.size(), value.c_str(), value.size());
} }
/** /**
* Append the value of a record. * Append the value of a record.
* @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.
* @param vbuf the pointer to the value region. * @param vbuf the pointer to the value region.
* @param vsiz the size of the value region. * @param vsiz the size of the value region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. I f the corresponding * @note If no record corresponds to the key, a new record is created. I f the corresponding
* record exists, the given value is appended at the end of the existing value. * record exists, the given value is appended at the end of the existing value.
*/ */
virtual bool append(const char* kbuf, size_t ksiz, const char* vbuf, size _t vsiz) { virtual bool append(const char* kbuf, size_t ksiz, const char* vbuf, size _t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) : explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), nbuf_(NULL) {} vbuf_(vbuf), vsiz_(vsiz), nbuf_(NULL) {}
~VisitorImpl() { ~VisitorImpl() {
if (nbuf_) delete[] nbuf_; if (nbuf_) delete[] nbuf_;
} }
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) {
skipping to change at line 940 skipping to change at line 1147
}; };
VisitorImpl visitor(vbuf, vsiz); VisitorImpl visitor(vbuf, vsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false; if (!accept(kbuf, ksiz, &visitor, true)) return false;
return true; return true;
} }
/** /**
* Set the value of a record. * Set the value of a record.
* @note Equal to the original DB::append method except that the paramete rs are std::string. * @note Equal to the original DB::append method except that the paramete rs are std::string.
*/ */
virtual bool append(const std::string& key, const std::string& value) { virtual bool append(const std::string& key, const std::string& value) {
_assert_(true);
return append(key.c_str(), key.size(), value.c_str(), value.size()); return append(key.c_str(), key.size(), value.c_str(), value.size());
} }
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @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.
* @param num the additional number. * @param num the additional number.
* @return the result value, or INT64_MIN on failure. * @return the result value, or INT64_MIN on failure.
*/ */
virtual int64_t increment(const char* kbuf, size_t ksiz, int64_t num) { virtual int64_t increment(const char* kbuf, size_t ksiz, int64_t num) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(int64_t num) : num_(num), big_(0) {} explicit VisitorImpl(int64_t num) : num_(num), big_(0) {}
int64_t num() { int64_t num() {
return num_; return num_;
} }
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 (vsiz != sizeof(num_)) { if (vsiz != sizeof(num_)) {
skipping to change at line 997 skipping to change at line 1206
set_error(Error::LOGIC, "logical inconsistency"); set_error(Error::LOGIC, "logical inconsistency");
return num; return num;
} }
return num; return num;
} }
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the param eter is std::string. * @note Equal to the original DB::increment method except that the param eter is std::string.
*/ */
virtual int64_t increment(const std::string& key, int64_t num) { virtual int64_t increment(const std::string& key, int64_t num) {
_assert_(true);
return increment(key.c_str(), key.size(), num); return increment(key.c_str(), key.size(), num);
} }
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the param eter and the return * @note Equal to the original DB::increment method except that the param eter and the return
* value are double. * value are double.
*/ */
virtual double increment(const char* kbuf, size_t ksiz, double num) { virtual double increment(const char* kbuf, size_t ksiz, double num) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(double num) : DECUNIT(1000000000000000LL), num_( num), buf_() {} explicit VisitorImpl(double num) : DECUNIT(1000000000000000LL), num_( num), buf_() {}
double num() { double num() {
return num_; return num_;
} }
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 (vsiz != sizeof(buf_)) { if (vsiz != sizeof(buf_)) {
num_ = std::nan(""); num_ = nan();
return NOP; return NOP;
} }
int64_t linteg, lfract; int64_t linteg, lfract;
std::memcpy(&linteg, vbuf, sizeof(linteg)); std::memcpy(&linteg, vbuf, sizeof(linteg));
linteg = ntoh64(linteg); linteg = ntoh64(linteg);
std::memcpy(&lfract, vbuf + sizeof(linteg), sizeof(lfract)); std::memcpy(&lfract, vbuf + sizeof(linteg), sizeof(lfract));
lfract = ntoh64(lfract); lfract = ntoh64(lfract);
if (lfract == INT64_MIN && linteg == INT64_MIN) { if (lfract == INT64_MIN && linteg == INT64_MIN) {
num_ = std::nan(""); num_ = nan();
return NOP; return NOP;
} else if (linteg == INT64_MAX) { } else if (linteg == INT64_MAX) {
num_ = HUGE_VAL; num_ = HUGE_VAL;
return NOP; return NOP;
} else if (linteg == INT64_MIN) { } else if (linteg == INT64_MIN) {
num_ = -HUGE_VAL; num_ = -HUGE_VAL;
return NOP; return NOP;
} }
if (num_ == 0.0) { if (num_ == 0.0) {
num_ = linteg + (double)lfract / DECUNIT; num_ = linteg + (double)lfract / DECUNIT;
return NOP; return NOP;
} }
long double dinteg; long double dinteg;
long double dfract = std::modfl(num_, &dinteg); long double dfract = std::modfl(num_, &dinteg);
if (std::isnormal(dinteg) || dinteg == 0) { if (chknan(dinteg)) {
linteg += dinteg; linteg = INT64_MIN;
lfract += dfract * DECUNIT; lfract = INT64_MIN;
num_ = nan();
} else if (chkinf(dinteg)) {
linteg = dinteg > 0 ? INT64_MAX : INT64_MIN;
lfract = 0;
num_ = dinteg;
} else {
linteg += (int64_t)dinteg;
lfract += (int64_t)(dfract * DECUNIT);
if (lfract >= DECUNIT) { if (lfract >= DECUNIT) {
linteg += 1; linteg += 1;
lfract -= DECUNIT; lfract -= DECUNIT;
} }
num_ = linteg + (double)lfract / DECUNIT; num_ = linteg + (double)lfract / DECUNIT;
} else if (std::isinf(dinteg)) {
linteg = dinteg > 0 ? INT64_MAX : INT64_MIN;
lfract = 0;
num_ = dinteg;
} else {
linteg = INT64_MIN;
lfract = INT64_MIN;
num_ = std::nan("");
} }
linteg = hton64(linteg); linteg = hton64(linteg);
std::memcpy(buf_, &linteg, sizeof(linteg)); std::memcpy(buf_, &linteg, sizeof(linteg));
lfract = hton64(lfract); lfract = hton64(lfract);
std::memcpy(buf_ + sizeof(linteg), &lfract, sizeof(lfract)); std::memcpy(buf_ + sizeof(linteg), &lfract, sizeof(lfract));
*sp = sizeof(buf_); *sp = sizeof(buf_);
return buf_; return buf_;
} }
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) { const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
long double dinteg; long double dinteg;
long double dfract = std::modfl(num_, &dinteg); long double dfract = std::modfl(num_, &dinteg);
int64_t linteg, lfract; int64_t linteg, lfract;
if (std::isnormal(dinteg) || dinteg == 0) { if (chknan(dinteg)) {
linteg = dinteg; linteg = INT64_MIN;
lfract = dfract * DECUNIT; lfract = INT64_MIN;
} else if (std::isinf(dinteg)) { } else if (chkinf(dinteg)) {
linteg = dinteg > 0 ? INT64_MAX : INT64_MIN; linteg = dinteg > 0 ? INT64_MAX : INT64_MIN;
lfract = 0; lfract = 0;
} else { } else {
linteg = INT64_MIN; linteg = (int64_t)dinteg;
lfract = INT64_MIN; lfract = (int64_t)(dfract * DECUNIT);
} }
linteg = hton64(linteg); linteg = hton64(linteg);
std::memcpy(buf_, &linteg, sizeof(linteg)); std::memcpy(buf_, &linteg, sizeof(linteg));
lfract = hton64(lfract); lfract = hton64(lfract);
std::memcpy(buf_ + sizeof(linteg), &lfract, sizeof(lfract)); std::memcpy(buf_ + sizeof(linteg), &lfract, sizeof(lfract));
*sp = sizeof(buf_); *sp = sizeof(buf_);
return buf_; return buf_;
} }
const int64_t DECUNIT; const int64_t DECUNIT;
double num_; double num_;
char buf_[sizeof(int64_t)*2]; char buf_[sizeof(int64_t)*2];
}; };
VisitorImpl visitor(num); VisitorImpl visitor(num);
if (!accept(kbuf, ksiz, &visitor, true)) return std::nan(""); if (!accept(kbuf, ksiz, &visitor, true)) return nan();
num = visitor.num(); num = visitor.num();
if (std::isnan(num)) { if (chknan(num)) {
set_error(Error::LOGIC, "logical inconsistency"); set_error(Error::LOGIC, "logical inconsistency");
return std::nan(""); return nan();
} }
return num; return num;
} }
/** /**
* Add a number to the numeric value of a record. * Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the param eter is std::string * @note Equal to the original DB::increment method except that the param eter is std::string
* and the return value is double. * and the return value is double.
*/ */
virtual double increment(const std::string& key, double num) { virtual double increment(const std::string& key, double num) {
_assert_(true);
return increment(key.c_str(), key.size(), num); return increment(key.c_str(), key.size(), num);
} }
/** /**
* Perform compare-and-swap. * Perform compare-and-swap.
* @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.
* @param ovbuf the pointer to the old value region. NULL means that no record corresponds. * @param ovbuf the pointer to the old value region. NULL means that no record corresponds.
* @param ovsiz the size of the old value region. * @param ovsiz the size of the old value region.
* @param nvbuf the pointer to the new value region. NULL means that the record is removed. * @param nvbuf the pointer to the new value region. NULL means that the record is removed.
* @param nvsiz the size of new old value region. * @param nvsiz the size of new old value region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool cas(const char* kbuf, size_t ksiz, virtual bool cas(const char* kbuf, size_t ksiz,
const char* ovbuf, size_t ovsiz, const char* nvbuf, size _t nvsiz) { const char* ovbuf, size_t ovsiz, const char* nvbuf, size _t nvsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(const char* ovbuf, size_t ovsiz, const char* nvb uf, size_t nvsiz) : explicit VisitorImpl(const char* ovbuf, size_t ovsiz, const char* nvb uf, size_t nvsiz) :
ovbuf_(ovbuf), ovsiz_(ovsiz), nvbuf_(nvbuf), nvsiz_(nvsiz), ok_(fal se) {} ovbuf_(ovbuf), ovsiz_(ovsiz), nvbuf_(nvbuf), nvsiz_(nvsiz), ok_(fal se) {}
bool ok() const { bool ok() const {
return ok_; return ok_;
} }
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) {
skipping to change at line 1160 skipping to change at line 1373
return false; return false;
} }
return true; return true;
} }
/** /**
* Perform compare-and-swap. * Perform compare-and-swap.
* @note Equal to the original DB::cas method except that the parameters are std::string. * @note Equal to the original DB::cas method except that the parameters are std::string.
*/ */
virtual bool cas(const std::string& key, virtual bool cas(const std::string& key,
const std::string& ovalue, const std::string& nvalue) { const std::string& ovalue, const std::string& nvalue) {
_assert_(true);
return cas(key.c_str(), key.size(), return cas(key.c_str(), key.size(),
ovalue.c_str(), ovalue.size(), nvalue.c_str(), nvalue.size() ); ovalue.c_str(), ovalue.size(), nvalue.c_str(), nvalue.size() );
} }
/** /**
* Remove a record. * Remove a record.
* @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.
* @note If no record corresponds to the key, false is returned. * @note If no record corresponds to the key, false is returned.
*/ */
virtual bool remove(const char* kbuf, size_t ksiz) { virtual bool remove(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl() : ok_(false) {} explicit VisitorImpl() : ok_(false) {}
bool ok() const { bool ok() const {
return ok_; return ok_;
} }
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) {
ok_ = true; ok_ = true;
skipping to change at line 1198 skipping to change at line 1413
set_error(Error::NOREC, "no record"); set_error(Error::NOREC, "no record");
return false; return false;
} }
return true; return true;
} }
/** /**
* Remove a record. * Remove a record.
* @note Equal to the original DB::remove method except that the paramete r is std::string. * @note Equal to the original DB::remove method except that the paramete r is std::string.
*/ */
virtual bool remove(const std::string& key) { virtual bool remove(const std::string& key) {
_assert_(true);
return remove(key.c_str(), key.size()); return remove(key.c_str(), key.size());
} }
/** /**
* Retrieve the value of a record. * Retrieve the value of a record.
* @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.
* @param sp the pointer to the variable into which the size of the regio n of the return * @param sp the pointer to the variable into which the size of the regio n of the return
* value is assigned. * value is assigned.
* @return the pointer to the value region of the corresponding record, o r NULL on failure. * @return the pointer to the value region of the corresponding record, o r NULL on failure.
* @note If no record corresponds to the key, NULL is returned. Because an additional zero * @note If no record corresponds to the key, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the ret urn value can be * code is appended at the end of the region of the return value, the ret urn value can be
* treated as a C-style string. Because the region of the return value i s allocated with the * treated as a C-style string. Because the region of the return value i s allocated with the
* the new[] operator, it should be released with the delete[] operator w hen it is no longer * the new[] operator, it should be released with the delete[] operator w hen it is no longer
* in use. * in use.
*/ */
virtual char* get(const char* kbuf, size_t ksiz, size_t* sp) { virtual char* get(const char* kbuf, size_t ksiz, size_t* sp) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && sp);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {} explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* sp) { char* pop(size_t* sp) {
*sp = vsiz_; *sp = vsiz_;
return vbuf_; return vbuf_;
} }
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) {
skipping to change at line 1254 skipping to change at line 1471
} }
*sp = vsiz; *sp = vsiz;
return vbuf; return vbuf;
} }
/** /**
* Retrieve the value of a record. * Retrieve the value of a record.
* @note Equal to the original DB::get method except that the parameter a nd the return value * @note Equal to the original DB::get method except that the parameter a nd the return value
* are std::string. The return value should be deleted explicitly by the caller. * are std::string. The return value should be deleted explicitly by the caller.
*/ */
virtual std::string* get(const std::string& key) { virtual std::string* get(const std::string& key) {
_assert_(true);
size_t vsiz; size_t vsiz;
char* vbuf = get(key.c_str(), key.size(), &vsiz); char* vbuf = get(key.c_str(), key.size(), &vsiz);
if (!vbuf) return NULL; if (!vbuf) return NULL;
std::string* value = new std::string(vbuf, vsiz); std::string* value = new std::string(vbuf, vsiz);
delete[] vbuf; delete[] vbuf;
return value; return value;
} }
/** /**
* Retrieve the value of a record. * Retrieve the value of a record.
* @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.
* @param vbuf the pointer to the buffer into which the value of the corr esponding record is * @param vbuf the pointer to the buffer into which the value of the corr esponding record is
* written. * written.
* @param max the size of the buffer. * @param max the size of the buffer.
* @return the size of the value, or -1 on failure. * @return the size of the value, or -1 on failure.
*/ */
virtual int32_t get(const char* kbuf, size_t ksiz, char* vbuf, size_t max ) { virtual int32_t get(const char* kbuf, size_t ksiz, char* vbuf, size_t max ) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf);
class VisitorImpl : public Visitor { class VisitorImpl : public Visitor {
public: public:
explicit VisitorImpl(char* vbuf, size_t max) : vbuf_(vbuf), max_(max) , vsiz_(-1) {} explicit VisitorImpl(char* vbuf, size_t max) : vbuf_(vbuf), max_(max) , vsiz_(-1) {}
int32_t vsiz() { int32_t vsiz() {
return vsiz_; return vsiz_;
} }
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) {
vsiz_ = vsiz; vsiz_ = vsiz;
skipping to change at line 1298 skipping to change at line 1517
}; };
VisitorImpl visitor(vbuf, max); VisitorImpl visitor(vbuf, max);
if (!accept(kbuf, ksiz, &visitor, false)) return -1; if (!accept(kbuf, ksiz, &visitor, false)) return -1;
int32_t vsiz = visitor.vsiz(); int32_t vsiz = visitor.vsiz();
if (vsiz < 0) { if (vsiz < 0) {
set_error(Error::NOREC, "no record"); set_error(Error::NOREC, "no record");
return -1; return -1;
} }
return vsiz; return vsiz;
} }
/**
* Dump records into a data stream.
* @param dest the destination stream.
* @return true on success, or false on failure.
*/
virtual bool dump_snapshot(std::ostream* dest) {
_assert_(dest);
if (dest->fail()) {
set_error(Error::MISC, "invalid stream");
return false;
}
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::ostream* dest) : dest_(dest), stack_() {}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
char* wp = stack_;
*(wp++) = 0x00;
wp += writevarnum(wp, ksiz);
wp += writevarnum(wp, vsiz);
dest_->write(stack_, wp - stack_);
dest_->write(kbuf, ksiz);
dest_->write(vbuf, vsiz);
return NOP;
}
std::ostream* dest_;
char stack_[NUMBUFSIZ*2];
};
VisitorImpl visitor(dest);
bool err = false;
dest->write(DBSSMAGICDATA, sizeof(DBSSMAGICDATA));
if (iterate(&visitor, false)) {
unsigned char c = 0xff;
dest->write((char*)&c, 1);
if (dest->fail()) {
set_error(Error::MISC, "stream output error");
err = true;
}
} else {
err = true;
}
return !err;
}
/**
* Dump records into a file.
* @param dest the path of the destination file.
* @return true on success, or false on failure.
*/
virtual bool dump_snapshot(const std::string& dest) {
_assert_(true);
std::ofstream ofs;
ofs.open(dest.c_str(), std::ios_base::out | std::ios_base::binary | std
::ios_base::trunc);
if (!ofs) {
set_error(Error::MISC, "open failed");
return false;
}
bool err = false;
if (!dump_snapshot(&ofs)) err = true;
ofs.close();
if (!ofs) {
set_error(Error::MISC, "close failed");
err = true;
}
return !err;
}
/**
* Load records from a data stream.
* @param src the source stream.
* @return true on success, or false on failure.
*/
virtual bool load_snapshot(std::istream* src) {
_assert_(src);
if (src->fail()) {
set_error(Error::MISC, "invalid stream");
return false;
}
char buf[DBIOBUFSIZ];
src->read(buf, sizeof(DBSSMAGICDATA));
if (src->fail()) {
set_error(Error::MISC, "stream input error");
return false;
}
if (std::memcmp(buf, DBSSMAGICDATA, sizeof(DBSSMAGICDATA))) {
set_error(Error::MISC, "invalid magic data of input stream");
return false;
}
bool err = false;
while (true) {
int32_t c = src->get();
if (src->fail()) {
set_error(Error::MISC, "stream input error");
err = true;
break;
}
if (c == 0xff) break;
if (c == 0x00) {
size_t ksiz = 0;
do {
c = src->get();
ksiz = (ksiz << 7) + (c & 0x7f);
} while (c >= 0x80);
size_t vsiz = 0;
do {
c = src->get();
vsiz = (vsiz << 7) + (c & 0x7f);
} while (c >= 0x80);
size_t rsiz = ksiz + vsiz;
char* rbuf = rsiz > sizeof(buf) ? new char[rsiz] : buf;
src->read(rbuf, ksiz + vsiz);
if (src->fail()) {
set_error(Error::MISC, "stream input error");
err = true;
if (rbuf != buf) delete[] rbuf;
break;
}
if (!set(rbuf, ksiz, rbuf + ksiz, vsiz)) {
err = true;
if (rbuf != buf) delete[] rbuf;
break;
}
if (rbuf != buf) delete[] rbuf;
} else {
set_error(Error::MISC, "invalid magic data of input stream");
err = true;
break;
}
}
return !err;
}
/**
* Load records from a file.
* @param src the path of the source file.
* @return true on success, or false on failure.
*/
virtual bool load_snapshot(const std::string& src) {
_assert_(true);
std::ifstream ifs;
ifs.open(src.c_str(), std::ios_base::in | std::ios_base::binary);
if (!ifs) {
set_error(Error::MISC, "open failed");
return false;
}
bool err = false;
if (!load_snapshot(&ifs)) err = true;
ifs.close();
if (ifs.bad()) {
set_error(Error::MISC, "close failed");
return false;
}
return !err;
}
/**
* Create a cursor object.
* @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the const
ructor, it should be
* released with the delete operator when it is no longer in use.
*/
virtual Cursor* cursor() = 0;
}; };
} // common namespace } // common namespace
#endif // duplication check #endif // duplication check
// END OF FILE // END OF FILE
 End of changes. 96 change blocks. 
135 lines changed or deleted 526 lines changed or added


 kcfile.h   kcfile.h 
skipping to change at line 86 skipping to change at line 86
* @param path the path of a file. * @param path the path of a file.
* @param mode the connection mode. File::OWRITER as a writer, File::ORE ADER as a reader. * @param mode the connection mode. File::OWRITER as a writer, File::ORE ADER as a reader.
* The following may be added to the writer mode by bitwise-or: File::OCR EATE, which means it * The following may be added to the writer mode by bitwise-or: File::OCR EATE, which means it
* creates a new file if the file does not exist, File::OTRUNCATE, which means it creates a * creates a new file if the file does not exist, File::OTRUNCATE, which means it creates a
* new file regardless if the file exists. The following may be added to both of the reader * new file regardless if the file exists. The following may be added to both of the reader
* mode and the writer mode by bitwise-or: File::ONOLOCK, which means it opens the file * mode and the writer mode by bitwise-or: File::ONOLOCK, which means it opens the file
* without file locking, File::TRYLOCK, which means locking is performed without blocking. * without file locking, File::TRYLOCK, which means locking is performed without blocking.
* @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 open(const std::string& path, uint32_t mode, int64_t msiz); bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE, int 64_t msiz = 0);
/** /**
* Close the file. * Close the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close(); bool close();
/** /**
* Write data. * Write data.
* @param off the offset of the destination. * @param off the offset of the destination.
* @param buf the pointer to the data region. * @param buf the pointer to the data region.
* @param size the size of the data region. * @param size the size of the data region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool write(int64_t off, const void* buf, size_t size); bool write(int64_t off, const void* buf, size_t size);
/** /**
* Write data. * Write data.
* @note Equal to the original File::write method except that the sigunat ure is different. * @note Equal to the original File::write method except that the sigunat ure is different.
*/ */
bool write(int64_t off, const std::string& str) { bool write(int64_t off, const std::string& str) {
_assert_(off >= 0);
return write(off, str.c_str(), str.size()); return write(off, str.c_str(), str.size());
} }
/** /**
* Write data with assuring the region does not spill from the file size. * Write data with assuring the region does not spill from the file size.
* @param off the offset of the destination. * @param off the offset of the destination.
* @param buf the pointer to the data region. * @param buf the pointer to the data region.
* @param size the size of the data region. * @param size the size of the data region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool write_fast(int64_t off, const void* buf, size_t size); bool write_fast(int64_t off, const void* buf, size_t size);
/** /**
* Write data with assuring the region does not spill from the file size. * Write data with assuring the region does not spill from the file size.
* @note Equal to the original File::write_fast method except that the si gunature is different. * @note Equal to the original File::write_fast method except that the si gunature is different.
*/ */
bool write_fast(int64_t off, const std::string& str) { bool write_fast(int64_t off, const std::string& str) {
_assert_(off >= 0);
return write_fast(off, str.c_str(), str.size()); return write_fast(off, str.c_str(), str.size());
} }
/** /**
* Write data at the end of the file. * Write data at the end of the file.
* @param buf the pointer to the data region. * @param buf the pointer to the data region.
* @param size the size of the data region. * @param size the size of the data region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool append(const void* buf, size_t size); bool append(const void* buf, size_t size);
/** /**
* Write data at the end of the file. * Write data at the end of the file.
* @note Equal to the original File::append method except that the siguna ture is different. * @note Equal to the original File::append method except that the siguna ture is different.
*/ */
bool append(const std::string& str) { bool append(const std::string& str) {
_assert_(true);
return append(str.c_str(), str.size()); return append(str.c_str(), str.size());
} }
/** /**
* Read data. * Read data.
* @param off the offset of the source. * @param off the offset of the source.
* @param buf the pointer to the destination region. * @param buf the pointer to the destination region.
* @param size the size of the data to be read. * @param size the size of the data to be read.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool read(int64_t off, void* buf, size_t size); bool read(int64_t off, void* buf, size_t size);
/** /**
* Read data. * Read data.
* @note Equal to the original File::read method except that the sigunatu re is different. * @note Equal to the original File::read method except that the sigunatu re is different.
*/ */
bool read(int64_t off, std::string* buf, size_t size) { bool read(int64_t off, std::string* buf, size_t size) {
_assert_(off >= 0 && buf);
char* tbuf = new char[size]; char* tbuf = new char[size];
if (!read(off, tbuf, size)) { if (!read(off, tbuf, size)) {
delete tbuf; delete tbuf;
return false; return false;
} }
buf->append(std::string(tbuf, size)); buf->append(std::string(tbuf, size));
delete tbuf; delete tbuf;
return true; return true;
} }
/** /**
skipping to change at line 171 skipping to change at line 175
* @param buf the pointer to the destination region. * @param buf the pointer to the destination region.
* @param size the size of the data to be read. * @param size the size of the data to be read.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool read_fast(int64_t off, void* buf, size_t size); bool read_fast(int64_t off, void* buf, size_t size);
/** /**
* Read data. * Read data.
* @note Equal to the original File::read method except that the sigunatu re is different. * @note Equal to the original File::read method except that the sigunatu re is different.
*/ */
bool read_fast(int64_t off, std::string* buf, size_t size) { bool read_fast(int64_t off, std::string* buf, size_t size) {
_assert_(off >= 0 && buf);
char* tbuf = new char[size]; char* tbuf = new char[size];
if (!read_fast(off, tbuf, size)) { if (!read_fast(off, tbuf, size)) {
delete tbuf; delete tbuf;
return false; return false;
} }
buf->append(std::string(tbuf, size)); buf->append(std::string(tbuf, size));
delete tbuf; delete tbuf;
return true; return true;
} }
/** /**
skipping to change at line 207 skipping to change at line 212
bool refresh(); bool refresh();
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @param off the beginning offset of the guarded region * @param off the beginning offset of the guarded region
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool begin_transaction(bool hard, int64_t off); bool begin_transaction(bool hard, int64_t off);
/** /**
* Commit 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); bool end_transaction(bool commit);
/** /**
* Write a WAL message of transaction explicitly. * Write a WAL message of transaction explicitly.
* @param off the offset of the source. * @param off the offset of the source.
* @param size the size of the data to be read. * @param size the size of the data to be read.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
skipping to change at line 230 skipping to change at line 235
* Get the size of the file. * Get the size of the file.
* @return the size of the file, or 0 on failure. * @return the size of the file, or 0 on failure.
*/ */
int64_t size() const; int64_t size() const;
/** /**
* Get the path of the file. * Get the path of the file.
* @return the path of the file in bytes, or an empty string on failure. * @return the path of the file in bytes, or an empty string on failure.
*/ */
std::string path() const; std::string path() const;
/** /**
* Check whether the file was recovered or not.
* @return true if recovered, or false if not.
*/
bool recovered() const;
/**
* Get the status information of a file. * Get the status information of a file.
* @param path the path of a file. * @param path the path of a file.
* @param buf a structure of status information. * @param buf a structure of status information.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
static bool status(const std::string& path, Status* buf); static bool status(const std::string& path, Status* buf);
/** /**
* Get the absolute path of a file.
* @param path the path of a file.
* @return the absolute path of the file, or an empty string on failure.
*/
static std::string absolute_path(const std::string& path);
/**
* Remove a file. * Remove a file.
* @param path the path of a file. * @param path the path of a file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
static bool remove(const std::string& path); static bool remove(const std::string& path);
/**
* Change the name or location of a file.
* @param opath the old path of a file.
* @param npath the new path of a file.
* @return true on success, or false on failure.
*/
static bool rename(const std::string& opath, const std::string& npath);
/**
* Read a directory.
* @param path the path of a directory.
* @param strvec a string list to contain the result.
* @return true on success, or false on failure.
*/
static bool read_directory(const std::string& path, std::vector<std::stri
ng>* strvec);
/**
* Make a directory.
* @param path the path of a directory.
* @return true on success, or false on failure.
*/
static bool make_directory(const std::string& path);
/**
* Remove a directory.
* @param path the path of a directory.
* @return true on success, or false on failure.
*/
static bool remove_directory(const std::string& path);
/**
* Get the path of the current working directory.
* @return the path of the current working directory, or an empty string
on failure.
*/
static std::string get_current_directory();
/**
* Set the current working directory.
* @param path the path of a directory.
* @return true on success, or false on failure.
*/
static bool set_current_directory(const std::string& path);
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
File(const File&); File(const File&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
File& operator =(const File&); File& operator =(const File&);
/** Opaque pointer. */ /** Opaque pointer. */
void* opq_; void* opq_;
}; };
} // common namespace } // common namespace
 End of changes. 10 change blocks. 
2 lines changed or deleted 57 lines changed or added


 kchashdb.h   kchashdb.h 
skipping to change at line 66 skipping to change at line 66
const uint8_t HDBMAXAPOW = 15; ///< maximum alignment power const uint8_t HDBMAXAPOW = 15; ///< maximum alignment power
const uint8_t HDBDEFFPOW = 10; ///< default free block pool power const uint8_t HDBDEFFPOW = 10; ///< default free block pool power
const uint8_t HDBMAXFPOW = 20; ///< maximum free block pool power const uint8_t HDBMAXFPOW = 20; ///< maximum free block pool power
const int64_t HDBDEFBNUM = 1048583LL; ///< default bucket number const int64_t HDBDEFBNUM = 1048583LL; ///< default bucket number
const int64_t HDBDEFMSIZ = 64LL << 20; ///< default size of the memory-ma pped region const int64_t HDBDEFMSIZ = 64LL << 20; ///< default size of the memory-ma pped region
const uint8_t HDBRECMAGIC = 0xcc; ///< magic data for record const uint8_t HDBRECMAGIC = 0xcc; ///< magic data for record
const uint8_t HDBPADMAGIC = 0xee; ///< magic data for padding const uint8_t HDBPADMAGIC = 0xee; ///< magic data for padding
const uint8_t HDBFBMAGIC = 0xdd; ///< magic data for free block const uint8_t HDBFBMAGIC = 0xdd; ///< magic data for free block
const int32_t HDBDFRGMAX = 512; ///< maximum unit of auto defragme ntation const int32_t HDBDFRGMAX = 512; ///< maximum unit of auto defragme ntation
const int32_t HDBDFRGCEF = 2; ///< coefficient of auto defragmen tation const int32_t HDBDFRGCEF = 2; ///< coefficient of auto defragmen tation
const char* HDBTMPPATHEXT = "tmp"; ///< extension of the WAL file const char* HDBTMPPATHEXT = "tmpkch"; ///< extension of the temporary fi le
} }
/** /**
* File hash database. * File hash database.
*/ */
class HashDB : public FileDB { class HashDB : public FileDB {
public: public:
class Cursor; class Cursor;
private: private:
struct Record; struct Record;
skipping to change at line 98 skipping to change at line 98
* Cursor to indicate a record. * Cursor to indicate a record.
*/ */
class Cursor : public FileDB::Cursor { class Cursor : public FileDB::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);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true);
if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock 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
* the same record are blocked.
*/ */
virtual bool accept(Visitor* visitor, bool writable, bool step) { virtual bool accept(Visitor* visitor, bool writable = true, bool step =
false) {
_assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->writer_)) { if (writable && !(db_->writer_)) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "permission deni ed"); db_->set_error(__FILE__, __LINE__, Error::NOPERM, "permission denie d");
return false; return false;
} }
if (off_ < 1) { if (off_ < 1) {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record"); db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false; return false;
} }
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
if (!step_impl(&rec, rbuf, 0)) return false; if (!step_impl(&rec, rbuf, 0)) return false;
if (!rec.vbuf && !db_->read_record_body(&rec)) { if (!rec.vbuf && !db_->read_record_body(&rec)) {
skipping to change at line 228 skipping to change at line 234
if (!db_->defrag_impl(db_->dfunit_ * HDBDFRGCEF)) return false; if (!db_->defrag_impl(db_->dfunit_ * HDBDFRGCEF)) return false;
db_->frgcnt_ -= db_->dfunit_; db_->frgcnt_ -= db_->dfunit_;
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record. * Jump the cursor to the first record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool jump() { virtual bool jump() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
off_ = 0;
if (db_->lsiz_ <= db_->roff_) {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false;
}
off_ = db_->roff_; off_ = db_->roff_;
end_ = db_->lsiz_; end_ = db_->lsiz_;
return true; return true;
} }
/** /**
* Jump the cursor onto a record. * Jump the cursor onto a record.
* @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.
*/ */
virtual bool jump(const char* kbuf, size_t ksiz) { virtual bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); db_->set_error(__FILE__, __LINE__, 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;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
while (off > 0) { while (off > 0) {
rec.off = off; rec.off = off;
if (!db_->read_record(&rec, rbuf)) return false; if (!db_->read_record(&rec, rbuf)) return false;
if (rec.psiz == UINT16_MAX) { if (rec.psiz == UINT16_MAX) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the chain"); db_->set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the chain");
db_->report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%l
d",
(long)db_->psiz_, (long)rec.off, (long)db_->file_.siz
e());
return false; return false;
} }
uint32_t tpivot = db_->linear_ ? pivot : uint32_t tpivot = db_->linear_ ? pivot :
db_->fold_hash(db_->hash_record(rec.kbuf, rec.ksiz)); db_->fold_hash(db_->hash_record(rec.kbuf, rec.ksiz));
if (pivot > tpivot) { if (pivot > tpivot) {
delete[] rec.bbuf; delete[] rec.bbuf;
off = rec.left; off = rec.left;
} else if (pivot < tpivot) { } else if (pivot < tpivot) {
delete[] rec.bbuf; delete[] rec.bbuf;
off = rec.right; off = rec.right;
skipping to change at line 297 skipping to change at line 312
} }
} }
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record"); db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false; return false;
} }
/** /**
* Jump the cursor to a record. * Jump the cursor to a record.
* @note Equal to the original Cursor::jump method except that the para meter is std::string. * @note Equal to the original Cursor::jump method except that the para meter is std::string.
*/ */
virtual bool jump(const std::string& key) { virtual bool jump(const std::string& key) {
_assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), 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.
*/ */
virtual bool step() { virtual bool step() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (off_ < 1) { if (off_ < 1) {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record"); db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false; return false;
} }
bool err = false; bool err = false;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
if (step_impl(&rec, rbuf, 1)) { if (step_impl(&rec, rbuf, 1)) {
delete[] rec.bbuf; delete[] rec.bbuf;
} else { } else {
err = true; err = true;
} }
return !err; return !err;
} }
/**
* Get the database object.
* @return the database object.
*/
virtual HashDB* db() {
_assert_(true);
return db_;
}
private: private:
/** /**
* Step the cursor to the next record. * Step the cursor to the next record.
* @param rec the record structure. * @param rec the record structure.
* @param rbuf the working buffer. * @param rbuf the working buffer.
* @param skip the number of skipping blocks. * @param skip the number of skipping blocks.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool step_impl(Record* rec, char* rbuf, int64_t skip) { bool step_impl(Record* rec, char* rbuf, int64_t skip) {
_assert_(rec && rbuf && skip >= 0);
if (off_ >= end_) { if (off_ >= end_) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "cursor after the end"); db_->set_error(__FILE__, __LINE__, Error::BROKEN, "cursor after the end");
db_->report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld"
,
(long)db_->psiz_, (long)rec->off, (long)db_->file_.size
());
return false; return false;
} }
while (off_ < end_) { while (off_ < end_) {
rec->off = off_; rec->off = off_;
if (!db_->read_record(rec, rbuf)) return false; if (!db_->read_record(rec, rbuf)) return false;
skip--; skip--;
if (rec->psiz == UINT16_MAX) { if (rec->psiz == UINT16_MAX) {
off_ += rec->rsiz; off_ += rec->rsiz;
} else { } else {
if (skip < 0) return true; if (skip < 0) return true;
skipping to change at line 383 skipping to change at line 411
*/ */
enum Flag { enum Flag {
FOPEN = 1 << 0, ///< whether opened FOPEN = 1 << 0, ///< whether opened
FFATAL = 1 << 1 ///< whether with fatal error FFATAL = 1 << 1 ///< whether with fatal error
}; };
/** /**
* Default constructor. * Default constructor.
*/ */
explicit HashDB() : explicit HashDB() :
mlock_(), rlock_(), flock_(), atlock_(), error_(), erstrm_(NULL), ervbs _(false), mlock_(), rlock_(), flock_(), atlock_(), error_(), erstrm_(NULL), ervbs _(false),
omode_(0), writer_(false), autotran_(false), autosync_(false), omode_(0), writer_(false), autotran_(false), autosync_(false), reorg_(f alse), trim_(false),
file_(), fbp_(), curs_(), path_(""), file_(), fbp_(), curs_(), path_(""),
libver_(LIBVER), librev_(LIBREV), fmtver_(FMTVER), chksum_(0), type_(TY PEHASH), libver_(LIBVER), librev_(LIBREV), fmtver_(FMTVER), chksum_(0), type_(TY PEHASH),
apow_(HDBDEFAPOW), fpow_(HDBDEFFPOW), opts_(0), bnum_(HDBDEFBNUM), apow_(HDBDEFAPOW), fpow_(HDBDEFFPOW), opts_(0), bnum_(HDBDEFBNUM),
flags_(0), flagopen_(false), count_(0), lsiz_(0), psiz_(0), opaque_(), flags_(0), flagopen_(false), count_(0), lsiz_(0), psiz_(0), opaque_(),
msiz_(HDBDEFMSIZ), dfunit_(0), embcomp_(&ZLIBRAWCOMP), msiz_(HDBDEFMSIZ), dfunit_(0), embcomp_(&ZLIBRAWCOMP),
align_(0), fbpnum_(0), width_(0), linear_(false), align_(0), fbpnum_(0), width_(0), linear_(false),
comp_(NULL), rhsiz_(0), boff_(0), roff_(0), dfcur_(0), frgcnt_(0), comp_(NULL), rhsiz_(0), boff_(0), roff_(0), dfcur_(0), frgcnt_(0),
tran_(false), trhard_(false), trfbp_() {} tran_(false), trhard_(false), trfbp_() {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
* @note If the database is not closed, it is closed implicitly. * @note If the database is not closed, it is closed implicitly.
*/ */
virtual ~HashDB() { virtual ~HashDB() {
_assert_(true);
if (omode_ != 0) close(); if (omode_ != 0) close();
if (curs_.size() > 0) {
CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end();
while (cit != citend) {
Cursor* cur = *cit;
cur->db_ = NULL;
cit++;
}
}
} }
/** /**
* Accept a visitor to a record. * Accept a visitor to a record.
* @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.
* @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
* same record are blocked.
*/ */
virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool
writable) { writable = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
mlock_.lock_reader(); mlock_.lock_reader();
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
uint64_t hash = hash_record(kbuf, ksiz); uint64_t hash = hash_record(kbuf, ksiz);
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 % HDBRLOCKSLOT; size_t lidx = bidx % HDBRLOCKSLOT;
if (writable) { if (writable) {
rlock_.lock_writer(lidx); rlock_.lock_writer(lidx);
} else { } else {
skipping to change at line 447 skipping to change at line 490
} }
} }
mlock_.unlock(); 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.
* @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.
*/ */
virtual bool iterate(Visitor *visitor, bool writable) { virtual bool iterate(Visitor *visitor, bool writable = true) {
_assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (!iterate_impl(visitor)) err = true; if (!iterate_impl(visitor)) err = true;
return !err; return !err;
} }
/** /**
* Get the last happened error. * Get the last happened error.
* @return the last happened error. * @return the last happened error.
*/ */
virtual Error error() const { virtual Error error() const {
_assert_(true);
return error_; return error_;
} }
/** /**
* Set the error information. * Set the error information.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
virtual void set_error(Error::Code code, const char* message) { virtual void set_error(Error::Code code, const char* message) {
_assert_(message);
error_->set(code, message); error_->set(code, message);
if (code == Error::BROKEN || code == Error::SYSTEM) flags_ |= FFATAL; if (code == Error::BROKEN || code == Error::SYSTEM) flags_ |= FFATAL;
} }
/** /**
* Open a database file. * Open a database file.
* @param path the path of a database file. * @param path the path of a database file.
* @param mode the connection mode. FileDB::OWRITER as a writer, FileDB: * @param mode the connection mode. HashDB::OWRITER as a writer, HashDB:
:OREADER as a reader. :OREADER as a
* The following may be added to the writer mode by bitwise-or: FileDB::O * reader. The following may be added to the writer mode by bitwise-or:
CREATE, which means HashDB::OCREATE,
* it creates a new database if the file does not exist, FileDB::OTRUNCAT * which means it creates a new database if the file does not exist, Hash
E, which means it DB::OTRUNCATE, which
* creates a new database regardless if the file exists, FileDB::OAUTOTRA * means it creates a new database regardless if the file exists, HashDB:
N, which means each :OAUTOTRAN, which
* updating operation is performed in implicit transaction, FileDB::OAUTO * means each updating operation is performed in implicit transaction, Ha
SYNC, which means shDB::OAUTOSYNC,
* each updating operation is followed by implicit synchronization with t * which means each updating operation is followed by implicit synchroniz
he file system. The ation with the file
* following may be added to both of the reader mode and the writer mode * system. The following may be added to both of the reader mode and the
by bitwise-or: writer mode by
* FileDB::ONOLOCK, which means it opens the database file without file l * bitwise-or: HashDB::ONOLOCK, which means it opens the database file wi
ocking, thout file locking,
* FileDB::OTRYLOCK, which means locking is performed without blocking, F * HashDB::OTRYLOCK, which means locking is performed without blocking, H
ile::ONOREPAIR, which ashDB::ONOREPAIR,
* means the database file is not repaired implicitly even if file destru * which means the database file is not repaired implicitly even if file
ction is detected. destruction is
* 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
* longer in use.
*/ */
virtual bool open(const std::string& path, uint32_t mode) { virtual bool open(const std::string& path, uint32_t mode = OWRITER | OCRE
ATE) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
writer_ = false; writer_ = false;
autotran_ = false; autotran_ = false;
autosync_ = false; autosync_ = false;
reorg_ = false;
trim_ = false;
uint32_t fmode = File::OREADER; uint32_t fmode = File::OREADER;
if (mode & OWRITER) { if (mode & OWRITER) {
writer_ = true; writer_ = true;
fmode = File::OWRITER; fmode = File::OWRITER;
if (mode & OCREATE) fmode |= File::OCREATE; if (mode & OCREATE) fmode |= File::OCREATE;
if (mode & OTRUNCATE) fmode |= File::OTRUNCATE; if (mode & OTRUNCATE) fmode |= File::OTRUNCATE;
if (mode & OAUTOTRAN) autotran_ = true; if (mode & OAUTOTRAN) autotran_ = true;
if (mode & OAUTOSYNC) autosync_ = true; if (mode & OAUTOSYNC) autosync_ = true;
} }
if (mode & ONOLOCK) fmode |= File::ONOLOCK; if (mode & ONOLOCK) fmode |= File::ONOLOCK;
if (mode & OTRYLOCK) fmode |= File::OTRYLOCK; if (mode & OTRYLOCK) fmode |= File::OTRYLOCK;
if (!file_.open(path, fmode, msiz_)) { if (!file_.open(path, fmode, msiz_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
if (file_.recovered()) report(__FILE__, __LINE__, "info", "recovered by the WAL file");
if ((mode & OWRITER) && file_.size() < 1) { if ((mode & OWRITER) && file_.size() < 1) {
calc_meta(); calc_meta();
chksum_ = calc_checksum(); chksum_ = calc_checksum();
lsiz_ = roff_; lsiz_ = roff_;
if (!file_.truncate(lsiz_)) { if (!file_.truncate(lsiz_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
file_.close(); file_.close();
return false; return false;
} }
if (!dump_meta()) { if (!dump_meta()) {
file_.close(); file_.close();
return false; return false;
} }
} }
if (!load_meta()) { if (!load_meta()) {
file_.close(); file_.close();
return false; return false;
} }
calc_meta(); calc_meta();
if (chksum_ != calc_checksum()) { uint8_t chksum = calc_checksum();
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid module checksum if (chksum != chksum_) {
"); set_error(__FILE__, __LINE__, Error::INVALID, "invalid module checksu
m");
report(__FILE__, __LINE__, "info", "saved=%02X calculated=%02X",
(unsigned)chksum, (unsigned)chksum_);
file_.close();
return false;
}
if (((flags_ & FOPEN) || (flags_ & FFATAL)) && !(mode & ONOREPAIR) && !
(mode & ONOLOCK) &&
!reorganize_file(path)) {
file_.close(); file_.close();
return false; return false;
} }
if (((flags_ & FOPEN) || (flags_ & FFATAL)) && !(mode & ONOREPAIR) &&
!reorganize_file(path)) return false;
if (type_ == 0 || apow_ > HDBMAXAPOW || fpow_ > HDBMAXFPOW || if (type_ == 0 || apow_ > HDBMAXAPOW || fpow_ > HDBMAXFPOW ||
bnum_ < 1 || count_ < 0 || lsiz_ < roff_) { bnum_ < 1 || count_ < 0 || lsiz_ < roff_) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid meta data"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid meta data");
report(__FILE__, __LINE__, "info", "type=0x%02X apow=%d fpow=%d bnum=
%ld count=%ld"
" lsiz=%ld fsiz=%ld", (unsigned)type_, (int)apow_, (int)fpow_,
(long)bnum_,
(long)count_, (long)lsiz_, (long)file_.size());
file_.close(); file_.close();
return false; return false;
} }
if (file_.size() < lsiz_) { if (file_.size() < lsiz_) {
set_error(__FILE__, __LINE__, Error::BROKEN, "inconsistent file size" ); set_error(__FILE__, __LINE__, Error::BROKEN, "inconsistent file size" );
report(__FILE__, __LINE__, "info", "lsiz=%ld fsiz=%ld", (long)lsiz_,
(long)file_.size());
file_.close();
return false;
}
if (file_.size() != lsiz_ && !(mode & ONOREPAIR) && !(mode & ONOLOCK) &
& !trim_file(path)) {
file_.close(); file_.close();
return false; return false;
} }
if (mode & OWRITER) { if (mode & OWRITER) {
if (!(flags_ & FOPEN) && !(flags_ & FFATAL) && !load_free_blocks()) { if (!(flags_ & FOPEN) && !(flags_ & FFATAL) && !load_free_blocks()) {
file_.close(); file_.close();
return false; return false;
} }
if (!dump_empty_free_blocks()) { if (!dump_empty_free_blocks()) {
file_.close(); file_.close();
skipping to change at line 577 skipping to change at line 645
} }
path_.append(path); path_.append(path);
omode_ = mode; omode_ = mode;
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.
*/ */
virtual bool close() { virtual bool close() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
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 605 skipping to change at line 674
path_.clear(); path_.clear();
return !err; return !err;
} }
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool synchronize(bool hard, FileProcessor* proc) { virtual bool synchronize(bool hard = false, FileProcessor* proc = NULL) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
return false; return false;
} }
rlock_.lock_reader_all(); rlock_.lock_reader_all();
bool err = false; bool err = false;
if (!synchronize_impl(hard, proc)) err = true; if (!synchronize_impl(hard, proc)) err = true;
rlock_.unlock_all(); rlock_.unlock_all();
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool begin_transaction(bool hard) { virtual bool begin_transaction(bool hard = false) {
_assert_(true);
for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) { for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) {
mlock_.lock_writer(); mlock_.lock_writer();
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!tran_) break; if (!tran_) break;
mlock_.unlock(); mlock_.unlock();
if (wsec > 1.0) wsec = 1.0; if (wsec > 1.0) wsec = 1.0;
Thread::sleep(wsec); Thread::sleep(wsec);
} }
trhard_ = hard; trhard_ = hard;
if (!begin_transaction_impl()) { if (!begin_transaction_impl()) {
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
tran_ = true; tran_ = true;
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* Commit transaction. * Try to begin transaction.
* @param hard true for physical synchronization with the device, or fals
e for logical
* synchronization with the file system.
* @return true on success, or false on failure.
*/
virtual bool begin_transaction_try(bool hard = false) {
_assert_(true);
mlock_.lock_writer();
if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock();
return false;
}
if (!writer_) {
set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock();
return false;
}
if (tran_) {
set_error(__FILE__, __LINE__, Error::LOGIC, "competition avoided");
mlock_.unlock();
return false;
}
trhard_ = hard;
if (!begin_transaction_impl()) {
mlock_.unlock();
return false;
}
tran_ = true;
mlock_.unlock();
return true;
}
/**
* 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.
*/ */
virtual bool end_transaction(bool commit) { virtual bool end_transaction(bool commit = true) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(__FILE__, __LINE__, Error::INVALID, "not in transaction"); set_error(__FILE__, __LINE__, Error::INVALID, "not in transaction");
return false; return false;
} }
bool err = false; bool err = false;
skipping to change at line 683 skipping to change at line 788
if (!abort_transaction()) err = true; if (!abort_transaction()) err = true;
} }
tran_ = false; tran_ = false;
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.
*/ */
virtual bool clear() { virtual bool clear() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
return false; return false;
} }
disable_cursors(); disable_cursors();
if (!file_.truncate(HDBHEADSIZ)) { if (!file_.truncate(HDBHEADSIZ)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
fbp_.clear(); fbp_.clear();
bool err = false; bool err = false;
flags_ = 0; flags_ = 0;
skipping to change at line 719 skipping to change at line 825
} }
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!set_flag(FOPEN, true)) err = true; if (!set_flag(FOPEN, true)) err = true;
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.
*/ */
virtual int64_t count() { virtual int64_t count() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual int64_t size() { virtual int64_t size() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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 in bytes, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
virtual std::string path() { virtual std::string path() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual bool status(std::map<std::string, std::string>* strmap) { virtual bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = "HashDB"; (*strmap)["type"] = "HashDB";
(*strmap)["realtype"] = strprintf("%u", type_); (*strmap)["realtype"] = strprintf("%u", 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_);
(*strmap)["chksum"] = strprintf("0x%02X", chksum_); (*strmap)["chksum"] = strprintf("%u", chksum_);
(*strmap)["flags"] = strprintf("%u", flags_); (*strmap)["flags"] = strprintf("%u", flags_);
(*strmap)["apow"] = strprintf("%u", apow_); (*strmap)["apow"] = strprintf("%u", apow_);
(*strmap)["fpow"] = strprintf("%u", fpow_); (*strmap)["fpow"] = strprintf("%u", fpow_);
(*strmap)["opts"] = strprintf("%u", opts_); (*strmap)["opts"] = strprintf("%u", opts_);
(*strmap)["bnum"] = strprintf("%lld", (long long)bnum_); (*strmap)["bnum"] = strprintf("%lld", (long long)bnum_);
(*strmap)["msiz"] = strprintf("%lld", (long long)msiz_); (*strmap)["msiz"] = strprintf("%lld", (long long)msiz_);
(*strmap)["dfunit"] = strprintf("%lld", (long long)dfunit_); (*strmap)["dfunit"] = strprintf("%lld", (long long)dfunit_);
(*strmap)["frgcnt"] = strprintf("%lld", (long long)(frgcnt_ > 0 ? (int6 4_t)frgcnt_ : 0)); (*strmap)["frgcnt"] = strprintf("%lld", (long long)(frgcnt_ > 0 ? (int6 4_t)frgcnt_ : 0));
(*strmap)["realsize"] = strprintf("%lld", (long long)file_.size()); (*strmap)["realsize"] = strprintf("%lld", (long long)file_.size());
(*strmap)["recovered"] = strprintf("%d", file_.recovered());
(*strmap)["reorganized"] = strprintf("%d", reorg_);
if (strmap->count("fbpnum_used") > 0) { if (strmap->count("fbpnum_used") > 0) {
if (writer_) { if (writer_) {
(*strmap)["fbpnum_used"] = strprintf("%lld", (long long)fbp_.size() ); (*strmap)["fbpnum_used"] = strprintf("%lld", (long long)fbp_.size() );
} else { } else {
if (!load_free_blocks()) return false; if (!load_free_blocks()) return false;
(*strmap)["fbpnum_used"] = strprintf("%lld", (long long)fbp_.size() ); (*strmap)["fbpnum_used"] = strprintf("%lld", (long long)fbp_.size() );
fbp_.clear(); fbp_.clear();
} }
} }
if (strmap->count("bnum_used") > 0) { if (strmap->count("bnum_used") > 0) {
skipping to change at line 801 skipping to change at line 913
(*strmap)["bnum_used"] = strprintf("%lld", (long long)cnt); (*strmap)["bnum_used"] = strprintf("%lld", (long long)cnt);
} }
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)count_); (*strmap)["count"] = strprintf("%lld", (long long)count_);
(*strmap)["size"] = strprintf("%lld", (long long)lsiz_); (*strmap)["size"] = strprintf("%lld", (long long)lsiz_);
return true; return true;
} }
/** /**
* Create a cursor object. * Create a cursor object.
* @return the return value is the cursor object. * @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the const ructor, it should be * @note Because the object of the return value is allocated by the const ructor, it should be
* released with the delete operator when it is no longer in use. * released with the delete operator when it is no longer in use.
*/ */
virtual Cursor* cursor() { virtual Cursor* cursor() {
_assert_(true);
return new Cursor(this); return new Cursor(this);
} }
/** /**
* Set the internal error reporter. * Set the internal error reporter.
* @param erstrm a stream object into which internal error messages are s tored. * @param erstrm a stream object into which internal error messages are s tored.
* @param ervbs true to report all errors, or false to report fatal error s only. * @param ervbs true to report all errors, or false to report fatal error s only.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_error_reporter(std::ostream* erstrm, bool ervbs) { virtual bool tune_error_reporter(std::ostream* erstrm, bool ervbs) {
_assert_(erstrm);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
erstrm_ = erstrm; erstrm_ = erstrm;
ervbs_ = ervbs; ervbs_ = ervbs;
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.
*/ */
virtual bool tune_alignment(int8_t apow) { virtual bool tune_alignment(int8_t apow) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
apow_ = apow >= 0 ? apow : HDBDEFAPOW; apow_ = apow >= 0 ? apow : HDBDEFAPOW;
if (apow_ > HDBMAXAPOW) apow_ = HDBMAXAPOW; if (apow_ > HDBMAXAPOW) apow_ = HDBMAXAPOW;
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.
*/ */
virtual bool tune_fbp(int8_t fpow) { virtual bool tune_fbp(int8_t fpow) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
fpow_ = fpow >= 0 ? fpow : HDBDEFFPOW; fpow_ = fpow >= 0 ? fpow : HDBDEFFPOW;
if (fpow_ > HDBMAXFPOW) fpow_ = HDBMAXFPOW; if (fpow_ > HDBMAXFPOW) fpow_ = HDBMAXFPOW;
return true; return true;
} }
/** /**
* Set the optional features. * Set the optional features.
* @param opts the optional features. * @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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_options(int8_t opts) { virtual bool tune_options(int8_t opts) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual bool tune_buckets(int64_t bnum) { virtual bool tune_buckets(int64_t bnum) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum > 0 ? bnum : HDBDEFBNUM; bnum_ = bnum > 0 ? bnum : HDBDEFBNUM;
if (bnum_ > INT16_MAX) bnum_ = nearbyprime(bnum_); if (bnum_ > INT16_MAX) 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.
*/ */
virtual bool tune_map(int64_t msiz) { virtual bool tune_map(int64_t msiz) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
msiz_ = msiz >= 0 ? msiz : HDBDEFMSIZ; msiz_ = msiz >= 0 ? msiz : HDBDEFMSIZ;
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.
*/ */
virtual bool tune_defrag(int64_t dfunit) { virtual bool tune_defrag(int64_t dfunit) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual bool tune_compressor(Compressor* comp) { virtual bool tune_compressor(Compressor* comp) {
_assert_(comp);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual char* opaque() { virtual char* opaque() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual bool synchronize_opaque() { virtual bool synchronize_opaque() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return NULL; 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.
*/ */
virtual bool defrag(int64_t step) { virtual bool defrag(int64_t step) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (step > 0) { if (step > 0) {
if (!defrag_impl(step)) err = true; if (!defrag_impl(step)) err = true;
} else { } else {
dfcur_ = roff_; dfcur_ = roff_;
if (!defrag_impl(INT64_MAX)) err = true; if (!defrag_impl(INT64_MAX)) err = true;
} }
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.
*/ */
virtual uint8_t flags() { virtual uint8_t flags() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return 0; return 0;
} }
return flags_; return flags_;
} }
protected: protected:
/** /**
* Set the error information. * Set the error information.
* @param file the file name of the epicenter. * @param file the file name of the epicenter.
* @param line the line number of the epicenter. * @param line the line number of the epicenter.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
virtual void set_error(const char* file, int32_t line, virtual void set_error(const char* file, int32_t line,
Error::Code code, const char* message) { Error::Code code, const char* message) {
_assert_(file && message);
set_error(code, message); set_error(code, message);
if (ervbs_ || code == Error::BROKEN || code == Error::SYSTEM)
report(file, line, "error", "%d: %s: %s", code, Error::codename(code)
, message);
}
/**
* Report a message for debugging.
* @param file the file name of the epicenter.
* @param line the line number of the epicenter.
* @param type the type string.
* @param format the printf-like format string.
* @param ... used according to the format string.
*/
virtual void report(const char* file, int32_t line, const char* type,
const char* format, ...) {
_assert_(file && line > 0 && type && format);
if (!erstrm_) return; if (!erstrm_) return;
if (!ervbs_ && code != Error::BROKEN && code != Error::SYSTEM) return; const std::string& path = path_.size() > 0 ? path_ : "-";
*erstrm_ << "Error: " << path_ << ": " << file << ": " << line; std::string message;
*erstrm_ << ": " << code << ": " << message << std::endl; va_list ap;
va_start(ap, format);
strprintf(&message, format, ap);
va_end(ap);
*erstrm_ << "[" << type << "]: " << path << ": " << file << ": " << lin
e;
*erstrm_ << ": " << message << std::endl;
}
/**
* Report the content of a binary buffer for debugging.
* @param file the file name of the epicenter.
* @param line the line number of the epicenter.
* @param type the type string.
* @param name the name of the information.
* @param buf the binary buffer.
* @param size the size of the binary buffer
*/
virtual void report_binary(const char* file, int32_t line, const char* ty
pe,
const char* name, const char* buf, size_t size
) {
_assert_(file && line > 0 && type && name && buf && size <= MEMMAXSIZ);
if (!erstrm_) return;
char* hex = hexencode(buf, size);
report(file, line, type, "%s=%s", name, hex);
delete[] hex;
} }
/** /**
* 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.
*/ */
virtual bool tune_type(int8_t type) { virtual bool tune_type(int8_t type) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t libver() { virtual uint8_t libver() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t librev() { virtual uint8_t librev() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t fmtver() { virtual uint8_t fmtver() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t chksum() { virtual uint8_t chksum() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t type() { virtual uint8_t type() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t apow() { virtual uint8_t apow() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t fpow() { virtual uint8_t fpow() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual uint8_t opts() { virtual uint8_t opts() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual int64_t bnum() { virtual int64_t bnum() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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 .
*/ */
virtual int64_t msiz() { virtual int64_t msiz() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual int64_t dfunit() { virtual int64_t dfunit() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual Compressor *comp() { virtual Compressor *comp() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return comp_; return comp_;
} }
/**
* Check whether the database was recovered or not.
* @return true if recovered, or false if not.
*/
virtual bool recovered() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false;
}
return file_.recovered();
}
/**
* Check whether the database was reorganized or not.
* @return true if recovered, or false if not.
*/
virtual bool reorganized() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false;
}
return reorg_;
}
private: private:
/** /**
* Record data. * Record data.
*/ */
struct Record { struct Record {
int64_t off; ///< offset int64_t off; ///< offset
size_t rsiz; ///< whole size size_t rsiz; ///< whole size
size_t psiz; ///< size of the padding size_t psiz; ///< size of the padding
size_t ksiz; ///< size of the key size_t ksiz; ///< size of the key
size_t vsiz; ///< size of the value size_t vsiz; ///< size of the value
skipping to change at line 1186 skipping to change at line 1388
int64_t boff; ///< offset of the body int64_t boff; ///< offset of the body
char* bbuf; ///< buffer of the body char* bbuf; ///< buffer of the body
}; };
/** /**
* Free block data. * Free block data.
*/ */
struct FreeBlock { struct FreeBlock {
int64_t off; ///< offset int64_t off; ///< offset
size_t rsiz; ///< record size size_t rsiz; ///< record size
/** comparing operator */ /** comparing operator */
bool operator<(const FreeBlock& obj) const { bool operator <(const FreeBlock& obj) const {
_assert_(true);
if (rsiz < obj.rsiz) return true; if (rsiz < obj.rsiz) return true;
if (rsiz == obj.rsiz && off > obj.off) return true; if (rsiz == obj.rsiz && off > obj.off) return true;
return false; return false;
} }
}; };
/** /**
* Comparator for free blocks. * Comparator for free blocks.
*/ */
struct FreeBlockComparator { struct FreeBlockComparator {
/** comparing operator */ /** comparing operator */
bool operator()(const FreeBlock& a, const FreeBlock& b) const { bool operator ()(const FreeBlock& a, const FreeBlock& b) const {
_assert_(true);
return a.off < b.off; return a.off < b.off;
} }
}; };
/** /**
* Repeating visitor. * Repeating visitor.
*/ */
class Repeater : public Visitor { class Repeater : public Visitor {
public: public:
explicit Repeater(const char* vbuf, size_t vsiz) : vbuf_(vbuf), vsiz_(v explicit Repeater(const char* vbuf, size_t vsiz) : vbuf_(vbuf), vsiz_(v
siz) {} siz) {
_assert_(vbuf);
}
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) {
_assert_(kbuf && vbuf && sp);
*sp = vsiz_; *sp = vsiz_;
return vbuf_; return vbuf_;
} }
const char* vbuf_; const char* vbuf_;
size_t vsiz_; size_t vsiz_;
}; };
/** 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&);
skipping to change at line 1232 skipping to change at line 1439
* @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.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param bidx the bucket index. * @param bidx the bucket index.
* @param pivot the second hash value. * @param pivot the second hash value.
@ @param isiter true for iterator use, or false for direct use. @ @param isiter true for iterator use, or false for direct use.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool accept_impl(const char* kbuf, size_t ksiz, Visitor* visitor, bool accept_impl(const char* kbuf, size_t ksiz, Visitor* visitor,
int64_t bidx, uint32_t pivot, bool isiter) { int64_t bidx, uint32_t pivot, bool isiter) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor && bidx >= 0);
int64_t off = get_bucket(bidx); int64_t off = get_bucket(bidx);
if (off < 0) return false; if (off < 0) return false;
int64_t entoff = 0; int64_t entoff = 0;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
while (off > 0) { while (off > 0) {
rec.off = off; rec.off = off;
if (!read_record(&rec, rbuf)) return false; if (!read_record(&rec, rbuf)) return false;
if (rec.psiz == UINT16_MAX) { if (rec.psiz == UINT16_MAX) {
set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the cha in"); set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the cha in");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)rec.off, (long)file_.size());
return false; return false;
} }
uint32_t tpivot = linear_ ? pivot : fold_hash(hash_record(rec.kbuf, r ec.ksiz)); uint32_t tpivot = linear_ ? pivot : fold_hash(hash_record(rec.kbuf, r ec.ksiz));
if (pivot > tpivot) { if (pivot > tpivot) {
delete[] rec.bbuf; delete[] rec.bbuf;
off = rec.left; off = rec.left;
entoff = rec.off + sizeof(uint16_t); entoff = rec.off + sizeof(uint16_t);
} else if (pivot < tpivot) { } else if (pivot < tpivot) {
delete[] rec.bbuf; delete[] rec.bbuf;
off = rec.right; off = rec.right;
skipping to change at line 1490 skipping to change at line 1700
} }
} }
return true; return true;
} }
/** /**
* 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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool iterate_impl(Visitor* visitor) { bool iterate_impl(Visitor* visitor) {
size_t vsiz; _assert_(visitor);
const char* vbuf = visitor->visit_empty("", 0, &vsiz);
int64_t off = roff_; int64_t off = roff_;
int64_t end = lsiz_; int64_t end = lsiz_;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
while (off > 0 && off < end) { while (off > 0 && off < end) {
rec.off = off; rec.off = off;
if (!read_record(&rec, rbuf)) return false; if (!read_record(&rec, rbuf)) return false;
if (rec.psiz == UINT16_MAX) { if (rec.psiz == UINT16_MAX) {
off += rec.rsiz; off += rec.rsiz;
} else { } else {
if (!rec.vbuf && !read_record_body(&rec)) { if (!rec.vbuf && !read_record_body(&rec)) {
delete[] rec.bbuf; delete[] rec.bbuf;
return false; return false;
} }
vbuf = rec.vbuf; const char* vbuf = rec.vbuf;
vsiz = rec.vsiz; size_t vsiz = rec.vsiz;
char* zbuf = NULL; char* zbuf = NULL;
size_t zsiz = 0; size_t zsiz = 0;
if (comp_) { if (comp_) {
zbuf = comp_->decompress(vbuf, vsiz, &zsiz); zbuf = comp_->decompress(vbuf, vsiz, &zsiz);
if (!zbuf) { if (!zbuf) {
set_error(__FILE__, __LINE__, Error::SYSTEM, "data decompressio n failed"); set_error(__FILE__, __LINE__, Error::SYSTEM, "data decompressio n failed");
delete[] rec.bbuf; delete[] rec.bbuf;
return false; return false;
} }
vbuf = zbuf; vbuf = zbuf;
skipping to change at line 1576 skipping to change at line 1785
delete[] rec.bbuf; delete[] rec.bbuf;
return false; return false;
} }
delete[] zbuf; delete[] zbuf;
delete[] rec.bbuf; delete[] rec.bbuf;
} }
} }
off += rec.rsiz; off += rec.rsiz;
} }
} }
visitor->visit_empty("", 0, &vsiz);
return true; return true;
} }
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @param proc a postprocessor object. * @param proc a postprocessor object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_impl(bool hard, FileProcessor* proc) { bool synchronize_impl(bool hard, FileProcessor* proc) {
_assert_(true);
bool err = false; bool err = false;
if (hard && !dump_free_blocks()) err = true; if (hard && !dump_free_blocks()) err = true;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!file_.synchronize(hard)) { if (!file_.synchronize(hard)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
if (proc && !proc->process(path_)) { if (proc && !proc->process(path_, count_, lsiz_)) {
set_error(__FILE__, __LINE__, Error::MISC, "postprocessing failed"); set_error(__FILE__, __LINE__, Error::MISC, "postprocessing failed");
err = 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);
ScopedSpinLock lock(&flock_); ScopedSpinLock 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(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Perform defragmentation. * Perform defragmentation.
* @param step the number of steps. * @param step the number of steps.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool defrag_impl(int64_t step) { bool defrag_impl(int64_t step) {
_assert_(step >= 0);
int64_t end = lsiz_; int64_t end = lsiz_;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
while (true) { while (true) {
if (dfcur_ >= end) { if (dfcur_ >= end) {
dfcur_ = roff_; dfcur_ = roff_;
return true; return true;
} }
if (step-- < 1) return true; if (step-- < 1) return true;
rec.off = dfcur_; rec.off = dfcur_;
skipping to change at line 1681 skipping to change at line 1892
if (!write_free_block(dest, rsiz, rbuf)) return false; if (!write_free_block(dest, rsiz, rbuf)) return false;
insert_free_block(dest, rsiz); insert_free_block(dest, rsiz);
dfcur_ = dest; dfcur_ = dest;
} }
return true; return true;
} }
/** /**
* Calculate meta data with saved ones. * Calculate meta data with saved ones.
*/ */
void calc_meta() { void calc_meta() {
_assert_(true);
align_ = 1 << apow_; align_ = 1 << apow_;
fbpnum_ = fpow_ > 0 ? 1 << fpow_ : 0; fbpnum_ = fpow_ > 0 ? 1 << fpow_ : 0;
width_ = (opts_ & TSMALL) ? HDBWIDTHSMALL : HDBWIDTHLARGE; width_ = (opts_ & TSMALL) ? HDBWIDTHSMALL : HDBWIDTHLARGE;
linear_ = (opts_ & TLINEAR) ? true : false; linear_ = (opts_ & TLINEAR) ? true : false;
comp_ = (opts_ & TCOMPRESS) ? embcomp_ : NULL; comp_ = (opts_ & TCOMPRESS) ? embcomp_ : NULL;
rhsiz_ = sizeof(uint16_t) + sizeof(uint8_t) * 2; rhsiz_ = sizeof(uint16_t) + sizeof(uint8_t) * 2;
rhsiz_ += linear_ ? width_ : width_ * 2; rhsiz_ += linear_ ? width_ : width_ * 2;
boff_ = HDBHEADSIZ + HDBFBPWIDTH * fbpnum_; boff_ = HDBHEADSIZ + HDBFBPWIDTH * fbpnum_;
if (fbpnum_ > 0) boff_ += width_ * 2 + sizeof(uint8_t) * 2; if (fbpnum_ > 0) boff_ += width_ * 2 + sizeof(uint8_t) * 2;
roff_ = boff_ + width_ * bnum_; roff_ = boff_ + width_ * bnum_;
int64_t rem = roff_ % align_; int64_t rem = roff_ % align_;
if (rem > 0) roff_ += align_ - rem; if (rem > 0) roff_ += align_ - rem;
dfcur_ = roff_; dfcur_ = roff_;
frgcnt_ = 0; frgcnt_ = 0;
tran_ = false; tran_ = false;
} }
/** /**
* Calculate the module checksum. * Calculate the module checksum.
*/ */
uint8_t calc_checksum() { uint8_t calc_checksum() {
_assert_(true);
const char* kbuf = HDBCHKSUMSEED; const char* kbuf = HDBCHKSUMSEED;
size_t ksiz = sizeof(HDBCHKSUMSEED) - 1; size_t ksiz = sizeof(HDBCHKSUMSEED) - 1;
char* zbuf = NULL; char* zbuf = NULL;
size_t zsiz = 0; size_t zsiz = 0;
if (comp_) { if (comp_) {
zbuf = comp_->compress(kbuf, ksiz, &zsiz); zbuf = comp_->compress(kbuf, ksiz, &zsiz);
if (!zbuf) return 0; if (!zbuf) return 0;
kbuf = zbuf; kbuf = zbuf;
ksiz = zsiz; ksiz = zsiz;
} }
uint32_t hash = fold_hash(hash_record(kbuf, ksiz)); uint32_t hash = fold_hash(hash_record(kbuf, ksiz));
delete[] zbuf; delete[] zbuf;
return (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ (hash >> 0); return (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ (hash >> 0);
} }
/** /**
* Dump the meta data into the file. * Dump the meta data into the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool dump_meta() { bool dump_meta() {
_assert_(true);
char head[HDBHEADSIZ]; char head[HDBHEADSIZ];
std::memset(head, 0, sizeof(head)); std::memset(head, 0, sizeof(head));
std::memcpy(head, HDBMAGICDATA, sizeof(HDBMAGICDATA)); std::memcpy(head, HDBMAGICDATA, sizeof(HDBMAGICDATA));
std::memcpy(head + HDBMOFFLIBVER, &libver_, sizeof(libver_)); std::memcpy(head + HDBMOFFLIBVER, &libver_, sizeof(libver_));
std::memcpy(head + HDBMOFFLIBREV, &librev_, sizeof(librev_)); std::memcpy(head + HDBMOFFLIBREV, &librev_, sizeof(librev_));
std::memcpy(head + HDBMOFFFMTVER, &fmtver_, sizeof(fmtver_)); std::memcpy(head + HDBMOFFFMTVER, &fmtver_, sizeof(fmtver_));
std::memcpy(head + HDBMOFFCHKSUM, &chksum_, sizeof(chksum_)); std::memcpy(head + HDBMOFFCHKSUM, &chksum_, sizeof(chksum_));
std::memcpy(head + HDBMOFFTYPE, &type_, sizeof(type_)); std::memcpy(head + HDBMOFFTYPE, &type_, sizeof(type_));
std::memcpy(head + HDBMOFFAPOW, &apow_, sizeof(apow_)); std::memcpy(head + HDBMOFFAPOW, &apow_, sizeof(apow_));
std::memcpy(head + HDBMOFFFPOW, &fpow_, sizeof(fpow_)); std::memcpy(head + HDBMOFFFPOW, &fpow_, sizeof(fpow_));
skipping to change at line 1752 skipping to change at line 1966
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
return true; return true;
} }
/** /**
* Dump the meta data into the file. * Dump the meta data into the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool dump_auto_meta() { bool dump_auto_meta() {
_assert_(true);
const int64_t hsiz = HDBMOFFOPAQUE - HDBMOFFCOUNT; const int64_t hsiz = HDBMOFFOPAQUE - HDBMOFFCOUNT;
char head[hsiz]; char head[hsiz];
std::memset(head, 0, hsiz); std::memset(head, 0, hsiz);
uint64_t num = hton64(count_); uint64_t num = hton64(count_);
std::memcpy(head, &num, sizeof(num)); std::memcpy(head, &num, sizeof(num));
num = hton64(lsiz_); num = hton64(lsiz_);
std::memcpy(head + HDBMOFFSIZE - HDBMOFFCOUNT, &num, sizeof(num)); std::memcpy(head + HDBMOFFSIZE - HDBMOFFCOUNT, &num, sizeof(num));
if (!file_.write_fast(HDBMOFFCOUNT, head, sizeof(head))) { if (!file_.write_fast(HDBMOFFCOUNT, head, sizeof(head))) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
return true; return true;
} }
/** /**
* Dump the opaque data into the file. * Dump the opaque data into the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool dump_opaque() { bool dump_opaque() {
_assert_(true);
if (!file_.write_fast(HDBMOFFOPAQUE, opaque_, sizeof(opaque_))) { if (!file_.write_fast(HDBMOFFOPAQUE, opaque_, sizeof(opaque_))) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
return true; return true;
} }
/** /**
* Load the meta data from the file. * Load the meta data from the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool load_meta() { bool load_meta() {
_assert_(true);
char head[HDBHEADSIZ]; char head[HDBHEADSIZ];
if (file_.size() < (int64_t)sizeof(head)) {
set_error(__FILE__, __LINE__, Error::INVALID, "missing magic data of
the file");
return false;
}
if (!file_.read(0, head, sizeof(head))) { if (!file_.read(0, head, sizeof(head))) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)0, (long)file_.size());
return false; return false;
} }
if (std::memcmp(head, HDBMAGICDATA, sizeof(HDBMAGICDATA))) { if (std::memcmp(head, HDBMAGICDATA, sizeof(HDBMAGICDATA))) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic data of t he file"); set_error(__FILE__, __LINE__, Error::INVALID, "invalid magic data of the file");
return false; return false;
} }
std::memcpy(&libver_, head + HDBMOFFLIBVER, sizeof(libver_)); std::memcpy(&libver_, head + HDBMOFFLIBVER, sizeof(libver_));
std::memcpy(&librev_, head + HDBMOFFLIBREV, sizeof(librev_)); std::memcpy(&librev_, head + HDBMOFFLIBREV, sizeof(librev_));
std::memcpy(&fmtver_, head + HDBMOFFFMTVER, sizeof(fmtver_)); std::memcpy(&fmtver_, head + HDBMOFFFMTVER, sizeof(fmtver_));
std::memcpy(&chksum_, head + HDBMOFFCHKSUM, sizeof(chksum_)); std::memcpy(&chksum_, head + HDBMOFFCHKSUM, sizeof(chksum_));
std::memcpy(&type_, head + HDBMOFFTYPE, sizeof(type_)); std::memcpy(&type_, head + HDBMOFFTYPE, sizeof(type_));
std::memcpy(&apow_, head + HDBMOFFAPOW, sizeof(apow_)); std::memcpy(&apow_, head + HDBMOFFAPOW, sizeof(apow_));
std::memcpy(&fpow_, head + HDBMOFFFPOW, sizeof(fpow_)); std::memcpy(&fpow_, head + HDBMOFFFPOW, sizeof(fpow_));
std::memcpy(&opts_, head + HDBMOFFOPTS, sizeof(opts_)); std::memcpy(&opts_, head + HDBMOFFOPTS, sizeof(opts_));
skipping to change at line 1818 skipping to change at line 2041
std::memcpy(opaque_, head + HDBMOFFOPAQUE, sizeof(opaque_)); std::memcpy(opaque_, head + HDBMOFFOPAQUE, sizeof(opaque_));
return true; return true;
} }
/** /**
* Set a status flag. * Set a status flag.
* @param flag the flag kind. * @param flag the flag kind.
* @param sign whether to set or unset. * @param sign whether to set or unset.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool set_flag(uint8_t flag, bool sign) { bool set_flag(uint8_t flag, bool sign) {
_assert_(true);
uint8_t flags; uint8_t flags;
if (!file_.read(HDBMOFFFLAGS, &flags, sizeof(flags))) { if (!file_.read(HDBMOFFFLAGS, &flags, sizeof(flags))) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)HDBMOFFFLAGS, (long)file_.size());
return false; return false;
} }
if (sign) { if (sign) {
flags |= flag; flags |= flag;
} else { } else {
flags &= ~flag; flags &= ~flag;
} }
if (!file_.write(HDBMOFFFLAGS, &flags, sizeof(flags))) { if (!file_.write(HDBMOFFFLAGS, &flags, sizeof(flags))) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
flags_ = flags; flags_ = flags;
return true; return true;
} }
/** /**
* Reorganize the whole file. * Reorganize the whole file.
* @param path the path of the database file. * @param path the path of the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool reorganize_file(const std::string& path) { bool reorganize_file(const std::string& path) {
_assert_(true);
bool err = false; bool err = false;
HashDB db; HashDB db;
db.tune_type(type_); db.tune_type(type_);
db.tune_alignment(apow_); db.tune_alignment(apow_);
db.tune_fbp(fpow_); db.tune_fbp(fpow_);
db.tune_options(opts_); db.tune_options(opts_);
db.tune_buckets(bnum_); db.tune_buckets(bnum_);
db.tune_map(msiz_); db.tune_map(msiz_);
if (embcomp_) db.tune_compressor(embcomp_); if (embcomp_) db.tune_compressor(embcomp_);
std::string npath = path + File::EXTCHR + HDBTMPPATHEXT; const std::string& npath = path + File::EXTCHR + HDBTMPPATHEXT;
if (db.open(npath, OWRITER | OCREATE | OTRUNCATE)) { if (db.open(npath, OWRITER | OCREATE | OTRUNCATE)) {
report(__FILE__, __LINE__, "info", "reorganizing the database");
lsiz_ = file_.size(); lsiz_ = file_.size();
psiz_ = lsiz_; psiz_ = lsiz_;
if (copy_records(&db)) { if (copy_records(&db)) {
if (db.close()) { if (db.close()) {
File src; File src;
if (src.open(npath, File::OREADER | File::File::ONOLOCK, 0)) { if (src.open(npath, File::OREADER | File::ONOLOCK, 0)) {
File* dest = writer_ ? &file_ : new File(); File* dest = writer_ ? &file_ : new File();
if (dest == &file_ || dest->open(path, File::OWRITER | File::Fi if (dest == &file_ || dest->open(path, File::OWRITER | File::ON
le::ONOLOCK, 0)) { OLOCK, 0)) {
// hoge
std::cerr << "[reorganize]" << std::endl;
Thread::sleep(0.5);
int64_t off = 0; int64_t off = 0;
int64_t size = src.size(); int64_t size = src.size();
char buf[PAGESIZE]; char buf[HDBIOBUFSIZ*4];
while (off < size) { while (off < size) {
int32_t psiz = size - off; int32_t psiz = size - off;
if (psiz > PAGESIZE) psiz = PAGESIZE; if (psiz > (int32_t)sizeof(buf)) psiz = sizeof(buf);
if (src.read(off, buf, psiz)) { if (src.read(off, buf, psiz)) {
if (!dest->write(off, buf, psiz)) { if (!dest->write(off, buf, psiz)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->erro r()); set_error(__FILE__, __LINE__, Error::SYSTEM, dest->erro r());
err = true; err = true;
break; break;
} }
} else { } else {
set_error(__FILE__, __LINE__, Error::SYSTEM, src.error()) ; set_error(__FILE__, __LINE__, Error::SYSTEM, src.error()) ;
err = true; err = true;
break; break;
} }
off += PAGESIZE; off += psiz;
} }
if (!dest->truncate(size)) { if (!dest->truncate(size)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error()) ; set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error()) ;
err = true; err = true;
} }
if (dest != &file_) { if (dest != &file_) {
if (!dest->close()) { if (!dest->close()) {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error( )); set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error( ));
err = true; err = true;
} }
if (!file_.refresh()) { if (!file_.refresh()) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error( )); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error( ));
err = true; err = true;
} }
} }
if (!load_meta()) err = true; if (!load_meta()) err = true;
calc_meta(); calc_meta();
reorg_ = true;
if (dest != &file_) delete dest; if (dest != &file_) delete dest;
} else { } else {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error()); set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error());
err = true; err = true;
} }
src.close(); src.close();
} else { } else {
set_error(__FILE__, __LINE__, Error::SYSTEM, src.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, src.error());
err = true; err = true;
} }
skipping to change at line 1931 skipping to change at line 2155
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Copy all records to another database. * Copy all records to another database.
* @param dest the destination database. * @param dest the destination database.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool copy_records(HashDB* dest) { bool copy_records(HashDB* dest) {
_assert_(dest);
std::ostream* erstrm = erstrm_;
erstrm_ = NULL;
int64_t off = roff_; int64_t off = roff_;
int64_t end = psiz_; int64_t end = psiz_;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
while (off > 0 && off < end) { while (off > 0 && off < end) {
rec.off = off; rec.off = off;
if (!read_record(&rec, rbuf)) break; if (!read_record(&rec, rbuf)) break;
if (rec.psiz == UINT16_MAX) { if (rec.psiz == UINT16_MAX) {
off += rec.rsiz; off += rec.rsiz;
} else { } else {
skipping to change at line 1969 skipping to change at line 2196
if (!dest->set(rec.kbuf, rec.ksiz, vbuf, vsiz)) { if (!dest->set(rec.kbuf, rec.ksiz, vbuf, vsiz)) {
delete[] zbuf; delete[] zbuf;
delete[] rec.bbuf; delete[] rec.bbuf;
break; break;
} }
delete[] zbuf; delete[] zbuf;
delete[] rec.bbuf; delete[] rec.bbuf;
off += rec.rsiz; off += rec.rsiz;
} }
} }
erstrm_ = erstrm;
return true; return true;
} }
/** /**
* Trim the file size.
* @param path the path of the database file.
* @return true on success, or false on failure.
*/
bool trim_file(const std::string& path) {
_assert_(true);
bool err = false;
report(__FILE__, __LINE__, "info", "trimming the database");
File* dest = writer_ ? &file_ : new File();
if (dest == &file_ || dest->open(path, File::OWRITER | File::ONOLOCK, 0
)) {
if (!dest->truncate(lsiz_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error());
err = true;
}
if (dest != &file_) {
if (!dest->close()) {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error());
err = true;
}
if (!file_.refresh()) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true;
}
}
trim_ = true;
} else {
set_error(__FILE__, __LINE__, Error::SYSTEM, dest->error());
err = true;
}
if (dest != &file_) delete dest;
return !err;
}
/**
* Get the hash value of a record. * Get the hash value of a record.
* @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 the hash value. * @return the hash value.
*/ */
uint64_t hash_record(const char* kbuf, size_t ksiz) { uint64_t hash_record(const char* kbuf, size_t ksiz) {
_assert_(kbuf);
return hashmurmur(kbuf, ksiz); return hashmurmur(kbuf, ksiz);
} }
/** /**
* Fold a hash value into a small number. * Fold a hash value into a small number.
* @param hash the hash number. * @param hash the hash number.
* @return the result number. * @return the result number.
*/ */
uint32_t fold_hash(uint64_t hash) { uint32_t fold_hash(uint64_t hash) {
_assert_(true);
return (((hash & 0xffff000000000000ULL) >> 48) | ((hash & 0x0000ffff000 00000ULL) >> 16)) ^ return (((hash & 0xffff000000000000ULL) >> 48) | ((hash & 0x0000ffff000 00000ULL) >> 16)) ^
(((hash & 0x000000000000ffffULL) << 16) | ((hash & 0x00000000ffff0000 ULL) >> 16)); (((hash & 0x000000000000ffffULL) << 16) | ((hash & 0x00000000ffff0000 ULL) >> 16));
} }
/** /**
* Compare two keys in lexical order. * Compare two keys in lexical order.
* @param abuf one key. * @param abuf one key.
* @param abuf the size of the one key. * @param asiz the size of the one key.
* @param bbuf the other key. * @param bbuf the other key.
* @param bbuf the size of the other key. * @param bsiz the size of the other key.
* @return positive if the former is big, or negative if the latter is bi g, or 0 if both are * @return positive if the former is big, or negative if the latter is bi g, or 0 if both are
* equivalent. * equivalent.
*/ */
int32_t compare_keys(const char* abuf, size_t asiz, const char* bbuf, siz e_t bsiz) { int32_t compare_keys(const char* abuf, size_t asiz, const char* bbuf, siz e_t bsiz) {
_assert_(abuf && bbuf);
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);
} }
/** /**
* Set an address into a bucket. * Set an address into a bucket.
* @param bidx the index of the bucket. * @param bidx the index of the bucket.
* @param off the address. * @param off the address.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool set_bucket(int64_t bidx, int64_t off) { bool set_bucket(int64_t bidx, int64_t off) {
_assert_(bidx >= 0 && off >= 0);
char buf[sizeof(uint64_t)]; char buf[sizeof(uint64_t)];
writefixnum(buf, off >> apow_, width_); writefixnum(buf, off >> apow_, width_);
if (!file_.write_fast(boff_ + bidx * width_, buf, width_)) { if (!file_.write_fast(boff_ + bidx * width_, buf, width_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
return true; return true;
} }
/** /**
* Get an address from a bucket. * Get an address from a bucket.
* @param bidx the index of the bucket. * @param bidx the index of the bucket.
* @return the address, or -1 on failure. * @return the address, or -1 on failure.
*/ */
int64_t get_bucket(int64_t bidx) { int64_t get_bucket(int64_t bidx) {
_assert_(bidx >= 0);
char buf[sizeof(uint64_t)]; char buf[sizeof(uint64_t)];
if (!file_.read_fast(boff_ + bidx * width_, buf, width_)) { if (!file_.read_fast(boff_ + bidx * width_, buf, width_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)boff_ + bidx * width_, (long)file_.size());
return -1; return -1;
} }
return readfixnum(buf, width_) << apow_; return readfixnum(buf, width_) << apow_;
} }
/** /**
* Set an address into a chain slot. * Set an address into a chain slot.
* @param entoff the address of the chain slot. * @param entoff the address of the chain slot.
* @param off the destination address. * @param off the destination address.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool set_chain(int64_t entoff, int64_t off) { bool set_chain(int64_t entoff, int64_t off) {
_assert_(entoff >= 0 && off >= 0);
char buf[sizeof(uint64_t)]; char buf[sizeof(uint64_t)];
writefixnum(buf, off >> apow_, width_); writefixnum(buf, off >> apow_, width_);
if (!file_.write_fast(entoff, buf, width_)) { if (!file_.write_fast(entoff, buf, width_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
return true; return true;
} }
/** /**
* Read a record from the file. * Read a record from the file.
* @param rec the record structure. * @param rec the record structure.
* @param rbuf the working buffer. * @param rbuf the working buffer.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool read_record(Record* rec, char* rbuf) { bool read_record(Record* rec, char* rbuf) {
_assert_(rec && rbuf);
if (rec->off < roff_) { if (rec->off < roff_) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid record offset") ; set_error(__FILE__, __LINE__, Error::BROKEN, "invalid record offset") ;
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)rec->off, (long)file_.size());
return false; return false;
} }
size_t rsiz = psiz_ - rec->off; size_t rsiz = psiz_ - rec->off;
if (rsiz > HDBRECBUFSIZ) { if (rsiz > HDBRECBUFSIZ) {
rsiz = HDBRECBUFSIZ; rsiz = HDBRECBUFSIZ;
} else { } else {
if (rsiz < rhsiz_) { if (rsiz < rhsiz_) {
set_error(__FILE__, __LINE__, Error::BROKEN, "too short record regi on"); set_error(__FILE__, __LINE__, Error::BROKEN, "too short record regi on");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=
%ld",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size())
;
return false; return false;
} }
rsiz = rhsiz_; rsiz = rhsiz_;
} }
if (!file_.read_fast(rec->off, rbuf, rsiz)) { if (!file_.read_fast(rec->off, rbuf, rsiz)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=%l
d",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size());
return false; return false;
} }
const char* rp = rbuf; const char* rp = rbuf;
uint16_t snum; uint16_t snum;
if (*(uint8_t*)rp == HDBRECMAGIC) { if (*(uint8_t*)rp == HDBRECMAGIC) {
((uint8_t*)&snum)[0] = 0; ((uint8_t*)&snum)[0] = 0;
((uint8_t*)&snum)[1] = *(uint8_t*)(rp + 1); ((uint8_t*)&snum)[1] = *(uint8_t*)(rp + 1);
} else if (*(uint8_t*)rp >= 0x80) { } else if (*(uint8_t*)rp >= 0x80) {
if (*(uint8_t*)(rp++) != HDBFBMAGIC || *(uint8_t*)(rp++) != HDBFBMAGI C) { if (*(uint8_t*)(rp++) != HDBFBMAGIC || *(uint8_t*)(rp++) != HDBFBMAGI C) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic data of a free block"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic data of a free block");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=
%ld",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size())
;
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
return false; return false;
} }
rec->rsiz = readfixnum(rp, width_) << apow_; rec->rsiz = readfixnum(rp, width_) << apow_;
rp += width_; rp += width_;
if (*(uint8_t*)(rp++) != HDBPADMAGIC || *(uint8_t*)(rp++) != HDBPADMA GIC) { if (*(uint8_t*)(rp++) != HDBPADMAGIC || *(uint8_t*)(rp++) != HDBPADMA GIC) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic data of a free block"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic data of a free block");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=
%ld",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size())
;
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
return false; return false;
} }
if (rec->rsiz < rhsiz_) { if (rec->rsiz < rhsiz_) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid size of a fre e block"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid size of a fre e block");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=
%ld",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size())
;
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
return false; return false;
} }
rec->psiz = UINT16_MAX; rec->psiz = UINT16_MAX;
rec->ksiz = 0; rec->ksiz = 0;
rec->vsiz = 0; rec->vsiz = 0;
rec->left = 0; rec->left = 0;
rec->right = 0; rec->right = 0;
rec->kbuf = NULL; rec->kbuf = NULL;
rec->vbuf = NULL; rec->vbuf = NULL;
rec->boff = 0; rec->boff = 0;
rec->bbuf = NULL; rec->bbuf = NULL;
return true; return true;
} else if (*rp == 0) { } else if (*rp == 0) {
set_error(__FILE__, __LINE__, Error::BROKEN, "nullified region"); set_error(__FILE__, __LINE__, Error::BROKEN, "nullified region");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=%l
// hoge d",
printf("psiz=%d off=%d rsiz=%d fsiz=%d snum=%04X\n", (long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size());
(int)psiz_, (int)rec->off, (int)rsiz, (int)file_.size(), snum) report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
;
for (size_t i = 0; i < rsiz; i++) {
printf(" %02X", ((uint8_t*)rp)[i]);
}
printf("\n");
return false; return false;
} else { } else {
std::memcpy(&snum, rp, sizeof(snum)); std::memcpy(&snum, rp, sizeof(snum));
} }
rp += sizeof(snum); rp += sizeof(snum);
rsiz -= sizeof(snum); rsiz -= sizeof(snum);
rec->psiz = ntoh16(snum); rec->psiz = ntoh16(snum);
rec->left = readfixnum(rp, width_) << apow_; rec->left = readfixnum(rp, width_) << apow_;
rp += width_; rp += width_;
rsiz -= width_; rsiz -= width_;
skipping to change at line 2132 skipping to change at line 2411
rec->right = 0; rec->right = 0;
} else { } else {
rec->right = readfixnum(rp, width_) << apow_; rec->right = readfixnum(rp, width_) << apow_;
rp += width_; rp += width_;
rsiz -= width_; rsiz -= width_;
} }
uint64_t num; uint64_t num;
size_t step = readvarnum(rp, rsiz, &num); size_t step = readvarnum(rp, rsiz, &num);
if (step < 1) { if (step < 1) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid key length"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid key length");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=%l
d snum=%04X",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size(), s
num);
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
return false; return false;
} }
rec->ksiz = num; rec->ksiz = num;
rp += step; rp += step;
rsiz -= step; rsiz -= step;
step = readvarnum(rp, rsiz, &num); step = readvarnum(rp, rsiz, &num);
if (step < 1) { if (step < 1) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid value length"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid value length");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=%l
d snum=%04X",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.size(), s
num);
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
return false; return false;
} }
rec->vsiz = num; rec->vsiz = num;
rp += step; rp += step;
rsiz -= step; rsiz -= step;
size_t hsiz = rp - rbuf; size_t hsiz = rp - rbuf;
rec->rsiz = hsiz + rec->ksiz + rec->vsiz + rec->psiz; rec->rsiz = hsiz + rec->ksiz + rec->vsiz + rec->psiz;
rec->kbuf = NULL; rec->kbuf = NULL;
rec->vbuf = NULL; rec->vbuf = NULL;
rec->boff = rec->off + hsiz; rec->boff = rec->off + hsiz;
skipping to change at line 2162 skipping to change at line 2447
rec->kbuf = rp; rec->kbuf = rp;
rp += rec->ksiz; rp += rec->ksiz;
rsiz -= rec->ksiz; rsiz -= rec->ksiz;
if (rsiz >= rec->vsiz) { if (rsiz >= rec->vsiz) {
rec->vbuf = rp; rec->vbuf = rp;
if (rec->psiz > 0) { if (rec->psiz > 0) {
rp += rec->vsiz; rp += rec->vsiz;
rsiz -= rec->vsiz; rsiz -= rec->vsiz;
if (rsiz > 0 && *(uint8_t*)rp != HDBPADMAGIC) { if (rsiz > 0 && *(uint8_t*)rp != HDBPADMAGIC) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic dat a of a record"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid magic dat a of a record");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld f
siz=%ld snum=%04X",
(long)psiz_, (long)rec->off, (long)rsiz, (long)file_.siz
e(), snum);
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rsiz);
return false; return false;
} }
} }
} }
} else if (!read_record_body(rec)) { } else if (!read_record_body(rec)) {
return false; return false;
} }
return true; return true;
} }
/** /**
* Read the body of a record from the file. * Read the body of a record from the file.
* @param rec the record structure. * @param rec the record structure.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool read_record_body(Record* rec) { bool read_record_body(Record* rec) {
_assert_(rec);
size_t bsiz = rec->ksiz + rec->vsiz; size_t bsiz = rec->ksiz + rec->vsiz;
char* bbuf = new char[bsiz]; char* bbuf = new char[bsiz];
if (!file_.read_fast(rec->boff, bbuf, bsiz)) { if (!file_.read_fast(rec->boff, bbuf, bsiz)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)rec->boff, (long)file_.size());
delete[] bbuf; delete[] bbuf;
return false; return false;
} }
rec->bbuf = bbuf; rec->bbuf = bbuf;
rec->kbuf = rec->bbuf; rec->kbuf = rec->bbuf;
rec->vbuf = rec->bbuf + rec->ksiz; rec->vbuf = rec->bbuf + rec->ksiz;
return true; return true;
} }
/** /**
* Write a record into the file. * Write a record into the file.
* @param rec the record structure. * @param rec the record structure.
* @param over true for overwriting, or false for new record. * @param over true for overwriting, or false for new record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool write_record(Record* rec, bool over) { bool write_record(Record* rec, bool over) {
_assert_(rec);
char stack[HDBIOBUFSIZ]; char stack[HDBIOBUFSIZ];
char* rbuf = rec->rsiz > sizeof(stack) ? new char[rec->rsiz] : stack; char* rbuf = rec->rsiz > sizeof(stack) ? new char[rec->rsiz] : stack;
char* wp = rbuf; char* wp = rbuf;
uint16_t snum = hton16(rec->psiz); uint16_t snum = hton16(rec->psiz);
std::memcpy(wp, &snum, sizeof(snum)); std::memcpy(wp, &snum, sizeof(snum));
if (rec->psiz < 0x100) *wp = HDBRECMAGIC; if (rec->psiz < 0x100) *wp = HDBRECMAGIC;
wp += sizeof(snum); wp += sizeof(snum);
writefixnum(wp, rec->left >> apow_, width_); writefixnum(wp, rec->left >> apow_, width_);
wp += width_; wp += width_;
if (!linear_) { if (!linear_) {
skipping to change at line 2241 skipping to change at line 2533
} }
if (rbuf != stack) delete[] rbuf; if (rbuf != stack) delete[] rbuf;
return !err; return !err;
} }
/** /**
* Adjust the padding of a record. * Adjust the padding of a record.
* @param rec the record structure. * @param rec the record structure.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool adjust_record(Record* rec) { bool adjust_record(Record* rec) {
_assert_(rec);
if (rec->psiz > INT16_MAX || rec->psiz > rec->rsiz / 2) { if (rec->psiz > INT16_MAX || rec->psiz > rec->rsiz / 2) {
size_t nsiz = (rec->psiz >> apow_) << apow_; size_t nsiz = (rec->psiz >> apow_) << apow_;
if (nsiz < rhsiz_) return true; if (nsiz < rhsiz_) return true;
rec->rsiz -= nsiz; rec->rsiz -= nsiz;
rec->psiz -= nsiz; rec->psiz -= nsiz;
int64_t noff = rec->off + rec->rsiz; int64_t noff = rec->off + rec->rsiz;
char nbuf[HDBRECBUFSIZ]; char nbuf[HDBRECBUFSIZ];
if (!write_free_block(noff, nsiz, nbuf)) return false; if (!write_free_block(noff, nsiz, nbuf)) return false;
insert_free_block(noff, nsiz); insert_free_block(noff, nsiz);
} }
return true; return true;
} }
/** /**
* Calculate the size of a record. * Calculate the size of a record.
* @param ksiz the size of the key. * @param ksiz the size of the key.
* @param vsiz the size of the value. * @param vsiz the size of the value.
* @return the size of the record. * @return the size of the record.
*/ */
size_t calc_record_size(size_t ksiz, size_t vsiz) { size_t calc_record_size(size_t ksiz, size_t vsiz) {
_assert_(true);
size_t rsiz = sizeof(uint16_t) + width_; size_t rsiz = sizeof(uint16_t) + width_;
if (!linear_) rsiz += width_; if (!linear_) rsiz += width_;
if (ksiz < (1ULL << 7)) { if (ksiz < (1ULL << 7)) {
rsiz += 1; rsiz += 1;
} else if (ksiz < (1ULL << 14)) { } else if (ksiz < (1ULL << 14)) {
rsiz += 2; rsiz += 2;
} else if (ksiz < (1ULL << 21)) { } else if (ksiz < (1ULL << 21)) {
rsiz += 3; rsiz += 3;
} else if (ksiz < (1ULL << 28)) { } else if (ksiz < (1ULL << 28)) {
rsiz += 4; rsiz += 4;
skipping to change at line 2294 skipping to change at line 2588
rsiz += ksiz; rsiz += ksiz;
rsiz += vsiz; rsiz += vsiz;
return rsiz; return rsiz;
} }
/** /**
* Calculate the padding size of a record. * Calculate the padding size of a record.
* @param rsiz the size of the record. * @param rsiz the size of the record.
* @return the size of the padding. * @return the size of the padding.
*/ */
size_t calc_record_padding(size_t rsiz) { size_t calc_record_padding(size_t rsiz) {
_assert_(true);
size_t diff = rsiz & (align_ - 1); size_t diff = rsiz & (align_ - 1);
return diff > 0 ? align_ - diff : 0; return diff > 0 ? align_ - diff : 0;
} }
/** /**
* Shift a record to another place. * Shift a record to another place.
* @param orec the original record structure. * @param orec the original record structure.
* @param dest the destination offset. * @param dest the destination offset.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool shift_record(Record* orec, int64_t dest) { bool shift_record(Record* orec, int64_t dest) {
_assert_(orec && dest >= 0);
uint64_t hash = hash_record(orec->kbuf, orec->ksiz); uint64_t hash = hash_record(orec->kbuf, orec->ksiz);
uint32_t pivot = fold_hash(hash); uint32_t pivot = fold_hash(hash);
int64_t bidx = hash % bnum_; int64_t bidx = hash % bnum_;
int64_t off = get_bucket(bidx); int64_t off = get_bucket(bidx);
if (off < 0) return false; if (off < 0) return false;
if (off == orec->off) { if (off == orec->off) {
orec->off = dest; orec->off = dest;
if (!write_record(orec, true)) return false; if (!write_record(orec, true)) return false;
if (!set_bucket(bidx, dest)) return false; if (!set_bucket(bidx, dest)) return false;
return true; return true;
} }
int64_t entoff = 0; int64_t entoff = 0;
Record rec; Record rec;
char rbuf[HDBRECBUFSIZ]; char rbuf[HDBRECBUFSIZ];
while (off > 0) { while (off > 0) {
rec.off = off; rec.off = off;
if (!read_record(&rec, rbuf)) return false; if (!read_record(&rec, rbuf)) return false;
if (rec.psiz == UINT16_MAX) { if (rec.psiz == UINT16_MAX) {
set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the cha in"); set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the cha in");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)rec.off, (long)file_.size());
return false; return false;
} }
uint32_t tpivot = linear_ ? pivot : fold_hash(hash_record(rec.kbuf, r ec.ksiz)); uint32_t tpivot = linear_ ? pivot : fold_hash(hash_record(rec.kbuf, r ec.ksiz));
if (pivot > tpivot) { if (pivot > tpivot) {
delete[] rec.bbuf; delete[] rec.bbuf;
off = rec.left; off = rec.left;
entoff = rec.off + sizeof(uint16_t); entoff = rec.off + sizeof(uint16_t);
} else if (pivot < tpivot) { } else if (pivot < tpivot) {
delete[] rec.bbuf; delete[] rec.bbuf;
off = rec.right; off = rec.right;
skipping to change at line 2359 skipping to change at line 2657
if (entoff > 0) { if (entoff > 0) {
if (!set_chain(entoff, dest)) return false; if (!set_chain(entoff, dest)) return false;
} else { } else {
if (!set_bucket(bidx, dest)) return false; if (!set_bucket(bidx, dest)) return false;
} }
return true; return true;
} }
} }
} }
set_error(__FILE__, __LINE__, Error::BROKEN, "no record to shift"); set_error(__FILE__, __LINE__, Error::BROKEN, "no record to shift");
report(__FILE__, __LINE__, "info", "psiz=%ld fsiz=%ld", (long)psiz_, (l ong)file_.size());
return false; return false;
} }
/** /**
* Write a free block into the file. * Write a free block into the file.
* @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.
* @param rbuf the working buffer. * @param rbuf the working buffer.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool write_free_block(int64_t off, size_t rsiz, char* rbuf) { bool write_free_block(int64_t off, size_t rsiz, char* rbuf) {
_assert_(off >= 0 && rbuf);
char* wp = rbuf; char* wp = rbuf;
*(wp++) = HDBFBMAGIC; *(wp++) = HDBFBMAGIC;
*(wp++) = HDBFBMAGIC; *(wp++) = HDBFBMAGIC;
writefixnum(wp, rsiz >> apow_, width_); writefixnum(wp, rsiz >> apow_, width_);
wp += width_; wp += width_;
*(wp++) = HDBPADMAGIC; *(wp++) = HDBPADMAGIC;
*(wp++) = HDBPADMAGIC; *(wp++) = HDBPADMAGIC;
if (!file_.write_fast(off, rbuf, wp - rbuf)) { if (!file_.write_fast(off, rbuf, wp - rbuf)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
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);
ScopedSpinLock lock(&flock_); ScopedSpinLock 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);
if (fbpnum_ < 1) return false; if (fbpnum_ < 1) return false;
ScopedSpinLock lock(&flock_); ScopedSpinLock lock(&flock_);
FreeBlock fb = { INT64_MAX, rsiz }; FreeBlock fb = { INT64_MAX, 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;
} }
/** /**
* Trim invalid free blocks. * Trim invalid free blocks.
* @param begin the beginning offset. * @param begin the beginning offset.
* @param end the end offset. * @param end the end offset.
*/ */
void trim_free_blocks(int64_t begin, int64_t end) { void trim_free_blocks(int64_t begin, int64_t end) {
_assert_(begin >= 0 && end >= 0);
FBP::const_iterator it = fbp_.begin(); FBP::const_iterator it = fbp_.begin();
FBP::const_iterator itend = fbp_.end(); FBP::const_iterator itend = fbp_.end();
while (it != itend) { while (it != itend) {
if (it->off >= begin && it->off < end) { if (it->off >= begin && it->off < end) {
fbp_.erase(it++); fbp_.erase(it++);
} else { } else {
it++; it++;
} }
} }
} }
/** /**
* Dump all free blocks into the file. * Dump all free blocks into the file.
* @param rsiz the minimum size of the block.
* @param res the structure for the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool dump_free_blocks() { bool dump_free_blocks() {
_assert_(true);
if (fbpnum_ < 1) return true; if (fbpnum_ < 1) return true;
size_t size = boff_ - HDBHEADSIZ; size_t size = boff_ - HDBHEADSIZ;
char* rbuf = new char[size]; char* rbuf = new char[size];
char* wp = rbuf; char* wp = rbuf;
char* end = rbuf + size - width_ * 2 - sizeof(uint8_t) * 2; char* end = rbuf + size - width_ * 2 - sizeof(uint8_t) * 2;
size_t num = fbp_.size(); size_t num = fbp_.size();
if (num > 0) { if (num > 0) {
FreeBlock* blocks = new FreeBlock[num]; FreeBlock* blocks = new FreeBlock[num];
size_t cnt = 0; size_t cnt = 0;
FBP::const_iterator it = fbp_.begin(); FBP::const_iterator it = fbp_.begin();
skipping to change at line 2480 skipping to change at line 2782
err = true; err = true;
} }
delete[] rbuf; delete[] rbuf;
return !err; return !err;
} }
/** /**
* Dump an empty set of free blocks into the file. * Dump an empty set of free blocks into the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool dump_empty_free_blocks() { bool dump_empty_free_blocks() {
_assert_(true);
if (fbpnum_ < 1) return true; if (fbpnum_ < 1) return true;
char rbuf[2]; char rbuf[2];
char* wp = rbuf; char* wp = rbuf;
*(wp++) = 0; *(wp++) = 0;
*(wp++) = 0; *(wp++) = 0;
bool err = false; bool err = false;
if (!file_.write(HDBHEADSIZ, rbuf, wp - rbuf)) { if (!file_.write(HDBHEADSIZ, rbuf, wp - rbuf)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Load all free blocks from from the file. * Load all free blocks from from the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool load_free_blocks() { bool load_free_blocks() {
_assert_(true);
if (fbpnum_ < 1) return true; if (fbpnum_ < 1) return true;
size_t size = boff_ - HDBHEADSIZ; size_t size = boff_ - HDBHEADSIZ;
char* rbuf = new char[size]; char* rbuf = new char[size];
if (!file_.read(HDBHEADSIZ, rbuf, size)) { if (!file_.read(HDBHEADSIZ, rbuf, size)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)HDBHEADSIZ, (long)file_.size());
delete[] rbuf; delete[] rbuf;
return false; return false;
} }
const char* rp = rbuf; const char* rp = rbuf;
FreeBlock* blocks = new FreeBlock[fbpnum_]; FreeBlock* blocks = new FreeBlock[fbpnum_];
int32_t num = 0; int32_t num = 0;
while (num < fbpnum_ && size > 1 && *rp != '\0') { while (num < fbpnum_ && size > 1 && *rp != '\0') {
uint64_t off; uint64_t off;
size_t step = readvarnum(rp, size, &off); size_t step = readvarnum(rp, size, &off);
if (step < 1 || off < 1) { if (step < 1 || off < 1) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid free block of fset"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid free block of fset");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)off, (long)file_.size());
delete[] rbuf; delete[] rbuf;
delete[] blocks; delete[] blocks;
return false; return false;
} }
rp += step; rp += step;
size -= step; size -= step;
uint64_t rsiz; uint64_t rsiz;
step = readvarnum(rp, size, &rsiz); step = readvarnum(rp, size, &rsiz);
if (step < 1 || rsiz < 1) { if (step < 1 || rsiz < 1) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid free block si ze"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid free block si ze");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld rsiz=%ld fsiz=
%ld",
(long)psiz_, (long)off, (long)rsiz, (long)file_.size());
delete[] rbuf; delete[] rbuf;
delete[] blocks; delete[] blocks;
return false; return false;
} }
rp += step; rp += step;
size -= step; size -= step;
blocks[num].off = off << apow_; blocks[num].off = off << apow_;
blocks[num].rsiz = rsiz << apow_; blocks[num].rsiz = rsiz << apow_;
num++; num++;
} }
skipping to change at line 2548 skipping to change at line 2858
fbp_.insert(fb); fbp_.insert(fb);
} }
delete[] blocks; delete[] blocks;
delete[] rbuf; delete[] rbuf;
return true; return true;
} }
/** /**
* Disable all cursors. * Disable all cursors.
*/ */
void disable_cursors() { void disable_cursors() {
_assert_(true);
if (curs_.size() < 1) return; if (curs_.size() < 1) 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;
cur->off_ = 0; cur->off_ = 0;
cit++; cit++;
} }
} }
/** /**
* Escape cursors on a free block. * Escape cursors on a free block.
* @param off the offset of the free block. * @param off the offset of the free block.
* @param dest the destination offset. * @param dest the destination offset.
*/ */
void escape_cursors(int64_t off, int64_t dest) { void escape_cursors(int64_t off, int64_t dest) {
_assert_(off >= 0 && dest >= 0);
if (curs_.size() < 1) return; if (curs_.size() < 1) 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->end_ == off) { if (cur->end_ == off) {
cur->end_ = dest; cur->end_ = dest;
if (cur->off_ >= cur->end_) cur->off_ = 0; if (cur->off_ >= cur->end_) cur->off_ = 0;
} }
if (cur->off_ == off) { if (cur->off_ == off) {
cur->off_ = dest; cur->off_ = dest;
if (cur->off_ >= cur->end_) cur->off_ = 0; if (cur->off_ >= cur->end_) cur->off_ = 0;
} }
cit++; cit++;
} }
} }
/** /**
* Trim invalid cursors. * Trim invalid cursors.
*/ */
void trim_cursors() { void trim_cursors() {
_assert_(true);
if (curs_.size() < 1) return; if (curs_.size() < 1) return;
int64_t end = lsiz_; int64_t end = lsiz_;
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->off_ >= end) { if (cur->off_ >= end) {
cur->off_ = 0; cur->off_ = 0;
} else if (cur->end_ > end) { } else if (cur->end_ > end) {
cur->end_ = end; cur->end_ = end;
skipping to change at line 2606 skipping to change at line 2919
} }
/** /**
* Remove a record from a bucket chain. * Remove a record from a bucket chain.
* @param rec the record structure. * @param rec the record structure.
* @param rbuf the working buffer. * @param rbuf the working buffer.
* @param bidx the bucket index. * @param bidx the bucket index.
* @param entoff the offset of the entry pointer. * @param entoff the offset of the entry pointer.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool cut_chain(Record* rec, char* rbuf, int64_t bidx, int64_t entoff) { bool cut_chain(Record* rec, char* rbuf, int64_t bidx, int64_t entoff) {
_assert_(rec && rbuf && bidx >= 0 && entoff >= 0);
int64_t child; int64_t child;
if (rec->left > 0 && rec->right < 1) { if (rec->left > 0 && rec->right < 1) {
child = rec->left; child = rec->left;
} else if (rec->left < 1 && rec->right > 0) { } else if (rec->left < 1 && rec->right > 0) {
child = rec->right; child = rec->right;
} else if (rec->left < 1) { } else if (rec->left < 1) {
child = 0; child = 0;
} else { } else {
Record prec; Record prec;
prec.off = rec->left; prec.off = rec->left;
if (!read_record(&prec, rbuf)) return false; if (!read_record(&prec, rbuf)) return false;
if (prec.psiz == UINT16_MAX) { if (prec.psiz == UINT16_MAX) {
set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the cha in"); set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the cha in");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)prec.off, (long)file_.size());
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rhsiz_);
return false; return false;
} }
delete[] prec.bbuf; delete[] prec.bbuf;
if (prec.right > 0) { if (prec.right > 0) {
int64_t off = prec.right; int64_t off = prec.right;
int64_t pentoff = prec.off + sizeof(uint16_t) + width_; int64_t pentoff = prec.off + sizeof(uint16_t) + width_;
while (true) { while (true) {
prec.off = off; prec.off = off;
if (!read_record(&prec, rbuf)) return false; if (!read_record(&prec, rbuf)) return false;
if (prec.psiz == UINT16_MAX) { if (prec.psiz == UINT16_MAX) {
set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the chain"); set_error(__FILE__, __LINE__, Error::BROKEN, "free block in the chain");
report(__FILE__, __LINE__, "info", "psiz=%ld off=%ld fsiz=%ld",
(long)psiz_, (long)prec.off, (long)file_.size());
report_binary(__FILE__, __LINE__, "info", "rbuf", rbuf, rhsiz_)
;
return false; return false;
} }
delete[] prec.bbuf; delete[] prec.bbuf;
if (prec.right < 1) break; if (prec.right < 1) break;
off = prec.right; off = prec.right;
pentoff = prec.off + sizeof(uint16_t) + width_; pentoff = prec.off + sizeof(uint16_t) + width_;
} }
child = off; child = off;
if (!set_chain(pentoff, prec.left)) return false; if (!set_chain(pentoff, prec.left)) return false;
if (!set_chain(off + sizeof(uint16_t), rec->left)) return false; if (!set_chain(off + sizeof(uint16_t), rec->left)) return false;
skipping to change at line 2658 skipping to change at line 2978
} else { } else {
if (!set_bucket(bidx, child)) return false; if (!set_bucket(bidx, child)) return false;
} }
return true; return true;
} }
/** /**
* Begin transaction. * Begin transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool begin_transaction_impl() { bool begin_transaction_impl() {
_assert_(true);
if (!dump_meta()) return false; if (!dump_meta()) return false;
// TBD: hoge: really?
/*
if (!file_.truncate(lsiz_)) { if (!file_.truncate(lsiz_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
psiz_ = lsiz_; psiz_ = lsiz_;
*/
if (!file_.begin_transaction(trhard_, boff_)) { if (!file_.begin_transaction(trhard_, boff_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
return false; return false;
} }
if (!file_.write_transaction(HDBMOFFBNUM, HDBHEADSIZ - HDBMOFFBNUM)) { if (!file_.write_transaction(HDBMOFFBNUM, HDBHEADSIZ - HDBMOFFBNUM)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
file_.end_transaction(false); file_.end_transaction(false);
return false; return false;
} }
if (fbpnum_ > 0) { if (fbpnum_ > 0) {
skipping to change at line 2689 skipping to change at line 3015
trfbp_.insert(*it); trfbp_.insert(*it);
} }
} }
return true; return true;
} }
/** /**
* Begin auto transaction. * Begin auto transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool begin_auto_transaction() { bool begin_auto_transaction() {
_assert_(true);
atlock_.lock(); atlock_.lock();
if (!file_.begin_transaction(autosync_, boff_)) { if (!file_.begin_transaction(autosync_, boff_)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
atlock_.unlock(); atlock_.unlock();
return false; return false;
} }
if (!file_.write_transaction(HDBMOFFCOUNT, HDBMOFFOPAQUE - HDBMOFFCOUNT )) { if (!file_.write_transaction(HDBMOFFCOUNT, HDBMOFFOPAQUE - HDBMOFFCOUNT )) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
file_.end_transaction(false); file_.end_transaction(false);
atlock_.unlock(); atlock_.unlock();
return false; return false;
} }
return true; return true;
} }
/** /**
* Commit transaction. * Commit transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool commit_transaction() { bool commit_transaction() {
_assert_(true);
bool err = false; bool err = false;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!file_.end_transaction(true)) { if (!file_.end_transaction(true)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
trfbp_.clear(); trfbp_.clear();
return !err; return !err;
} }
/** /**
* Commit auto transaction. * Commit auto transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool commit_auto_transaction() { bool commit_auto_transaction() {
_assert_(true);
bool err = false; bool err = false;
if (!dump_auto_meta()) err = true; if (!dump_auto_meta()) err = true;
if (!file_.end_transaction(true)) { if (!file_.end_transaction(true)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
atlock_.unlock(); atlock_.unlock();
return !err; return !err;
} }
/** /**
* Abort transaction. * Abort transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool abort_transaction() { bool abort_transaction() {
_assert_(true);
bool err = false; bool err = false;
if (!file_.end_transaction(false)) { if (!file_.end_transaction(false)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
if (!load_meta()) err = true; if (!load_meta()) err = true;
calc_meta(); calc_meta();
disable_cursors(); disable_cursors();
fbp_.swap(trfbp_); fbp_.swap(trfbp_);
trfbp_.clear(); trfbp_.clear();
return !err; return !err;
} }
/** /**
* Abort auto transaction. * Abort auto transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool abort_auto_transaction() { bool abort_auto_transaction() {
_assert_(true);
bool err = false; bool err = false;
if (!file_.end_transaction(false)) { if (!file_.end_transaction(false)) {
set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error()); set_error(__FILE__, __LINE__, Error::SYSTEM, file_.error());
err = true; err = true;
} }
if (!load_meta()) err = true; if (!load_meta()) err = true;
calc_meta(); calc_meta();
disable_cursors(); disable_cursors();
fbp_.clear(); fbp_.clear();
atlock_.unlock(); atlock_.unlock();
skipping to change at line 2787 skipping to change at line 3118
/** The flag to report all errors. */ /** The flag to report all errors. */
bool ervbs_; bool ervbs_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
/** The flag for writer. */ /** The flag for writer. */
bool writer_; bool writer_;
/** The flag for auto transaction. */ /** The flag for auto transaction. */
bool autotran_; bool autotran_;
/** The flag for auto synchronization. */ /** The flag for auto synchronization. */
bool autosync_; bool autosync_;
/** The flag for reorganized. */
bool reorg_;
/** The flag for trimmed. */
bool trim_;
/** The file for data. */ /** The file for data. */
File file_; File file_;
/** The free block pool. */ /** The free block pool. */
FBP fbp_; FBP fbp_;
/** The cursor objects. */ /** The cursor objects. */
CursorList curs_; CursorList curs_;
/** The path of the database file. */ /** The path of the database file. */
std::string path_; std::string path_;
/** The library version. */ /** The library version. */
uint8_t libver_; uint8_t libver_;
 End of changes. 177 change blocks. 
89 lines changed or deleted 464 lines changed or added


 kcmap.h   kcmap.h 
skipping to change at line 55 skipping to change at line 55
* Iterator of records. * Iterator of records.
*/ */
class Iterator { class Iterator {
private: private:
friend class LinkedHashMap; friend class LinkedHashMap;
public: public:
/** /**
* Copy constructor. * Copy constructor.
* @param src the source object. * @param src the source object.
*/ */
Iterator(const Iterator& src) : map_(src.map_), rec_(src.rec_) {} Iterator(const Iterator& src) : map_(src.map_), rec_(src.rec_) {
_assert_(true);
}
/** /**
* Get the key. * Get the key.
*/ */
const KEY& key() { const KEY& key() {
_assert_(true);
return rec_->key; return rec_->key;
} }
/** /**
* Get the value. * Get the value.
*/ */
VALUE& value() { VALUE& value() {
_assert_(true);
return rec_->value; return rec_->value;
} }
/** /**
* Assignment operator from the self type. * Assignment operator from the self type.
* @param right the right operand. * @param right the right operand.
* @return the reference to itself. * @return the reference to itself.
*/ */
Iterator& operator =(const Iterator& right) { Iterator& operator =(const Iterator& right) {
_assert_(true);
map_ = right.map_; map_ = right.map_;
rec_ = right.rec_; rec_ = right.rec_;
return *this; return *this;
} }
/** /**
* Equality operator with the self type. * Equality operator with the self type.
* @param right the right operand. * @param right the right operand.
* @return true if the both are equal, or false if not. * @return true if the both are equal, or false if not.
*/ */
bool operator==(const Iterator& right) const { bool operator ==(const Iterator& right) const {
_assert_(true);
return map_ == right.map_ && rec_ == right.rec_; return map_ == right.map_ && rec_ == right.rec_;
} }
/** /**
* Non-equality operator with the self type. * Non-equality operator with the self type.
* @param right the right operand. * @param right the right operand.
* @return false if the both are equal, or true if not. * @return false if the both are equal, or true if not.
*/ */
bool operator!=(const Iterator& right) const { bool operator !=(const Iterator& right) const {
_assert_(true);
return map_ != right.map_ || rec_ != right.rec_; return map_ != right.map_ || rec_ != right.rec_;
} }
/** /**
* Preposting increment operator. * Preposting increment operator.
* @return the iterator itself. * @return the iterator itself.
*/ */
Iterator& operator++() { Iterator& operator ++() {
_assert_(true);
rec_ = rec_->next; rec_ = rec_->next;
return *this; return *this;
} }
/** /**
* Postpositive increment operator. * Postpositive increment operator.
* @return an iterator of the old position. * @return an iterator of the old position.
*/ */
Iterator operator++(int) { Iterator operator ++(int) {
_assert_(true);
Iterator old(*this); Iterator old(*this);
rec_ = rec_->next; rec_ = rec_->next;
return old; return old;
} }
/** /**
* Preposting decrement operator. * Preposting decrement operator.
* @return the iterator itself. * @return the iterator itself.
*/ */
Iterator& operator--() { Iterator& operator --() {
_assert_(true);
if (rec_) { if (rec_) {
rec_ = rec_->prev; rec_ = rec_->prev;
} else { } else {
rec_ = map_->last_; rec_ = map_->last_;
} }
return *this; return *this;
} }
/** /**
* Postpositive decrement operator. * Postpositive decrement operator.
* @return an iterator of the old position. * @return an iterator of the old position.
*/ */
Iterator operator--(int) { Iterator operator --(int) {
_assert_(true);
Iterator old(*this); Iterator old(*this);
if (rec_) { if (rec_) {
rec_ = rec_->prev; rec_ = rec_->prev;
} else { } else {
rec_ = map_->last_; rec_ = map_->last_;
} }
return old; return old;
} }
private: private:
/** /**
* Constructor. * Constructor.
* @param map the container. * @param map the container.
* @param rec the pointer to the current record. * @param rec the pointer to the current record.
*/ */
explicit Iterator(LinkedHashMap* map, Record* rec) : map_(map), rec_(re explicit Iterator(LinkedHashMap* map, Record* rec) : map_(map), rec_(re
c) {} c) {
_assert_(map);
}
/** The container. */ /** The container. */
LinkedHashMap* map_; LinkedHashMap* map_;
/** The current record. */ /** The current record. */
Record* rec_; Record* rec_;
}; };
/** /**
* Moving Modes. * Moving Modes.
*/ */
enum MoveMode { enum MoveMode {
MCURRENT, ///< keep the current position MCURRENT, ///< keep the current position
MFIRST, ///< move to the first MFIRST, ///< move to the first
MLAST ///< move to the last MLAST ///< move to the last
}; };
/** /**
* Default constructor. * Default constructor.
*/ */
explicit LinkedHashMap() : explicit LinkedHashMap() :
buckets_(NULL), bnum_(LHMDEFBNUM), first_(NULL), last_(NULL), count_(0) { buckets_(NULL), bnum_(LHMDEFBNUM), first_(NULL), last_(NULL), count_(0) {
_assert_(true);
initialize(); initialize();
} }
/** /**
* Constructor. * Constructor.
* @param bnum the number of buckets of the hash table. * @param bnum the number of buckets of the hash table.
*/ */
explicit LinkedHashMap(size_t bnum) : explicit LinkedHashMap(size_t bnum) :
buckets_(NULL), bnum_(bnum), first_(NULL), last_(NULL), count_(0) { buckets_(NULL), bnum_(bnum), first_(NULL), last_(NULL), count_(0) {
_assert_(true);
if (bnum_ < 1) bnum_ = LHMDEFBNUM; if (bnum_ < 1) bnum_ = LHMDEFBNUM;
initialize(); initialize();
} }
/** /**
* Destructor. * Destructor.
*/ */
~LinkedHashMap() { ~LinkedHashMap() {
_assert_(true);
destroy(); destroy();
} }
/** /**
* Store a record. * Store a record.
* @param key the key. * @param key the key.
* @param value the value. * @param value the value.
* @param mode the moving mode. * @param mode the moving mode.
* @return the pointer to the value of the stored record. * @return the pointer to the value of the stored record.
*/ */
VALUE *set(const KEY& key, const VALUE& value, MoveMode mode) { VALUE *set(const KEY& key, const VALUE& value, MoveMode mode) {
_assert_(true);
size_t bidx = hash_(key) % bnum_; size_t bidx = hash_(key) % bnum_;
Record* rec = buckets_[bidx]; Record* rec = buckets_[bidx];
Record** entp = buckets_ + bidx; Record** entp = buckets_ + bidx;
while (rec) { while (rec) {
if (equalto_(rec->key, key)) { if (equalto_(rec->key, key)) {
rec->value = value; rec->value = value;
switch (mode) { switch (mode) {
default: { default: {
break; break;
} }
skipping to change at line 254 skipping to change at line 271
*entp = rec; *entp = rec;
count_++; count_++;
return &rec->value; return &rec->value;
} }
/** /**
* Remove a record. * Remove a record.
* @param key the key. * @param key the key.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool remove(const KEY& key) { bool remove(const KEY& key) {
_assert_(true);
size_t bidx = hash_(key) % bnum_; size_t bidx = hash_(key) % bnum_;
Record* rec = buckets_[bidx]; Record* rec = buckets_[bidx];
Record** entp = buckets_ + bidx; Record** entp = buckets_ + bidx;
while (rec) { while (rec) {
if (equalto_(rec->key, key)) { if (equalto_(rec->key, key)) {
if (rec->prev) rec->prev->next = rec->next; if (rec->prev) rec->prev->next = rec->next;
if (rec->next) rec->next->prev = rec->prev; if (rec->next) rec->next->prev = rec->prev;
if (rec == first_) first_ = rec->next; if (rec == first_) first_ = rec->next;
if (rec == last_) last_ = rec->prev; if (rec == last_) last_ = rec->prev;
*entp = rec->child; *entp = rec->child;
skipping to change at line 282 skipping to change at line 300
return false; return false;
} }
/** /**
* Migrate a record to another map. * Migrate a record to another map.
* @param key the key. * @param key the key.
* @param dist the destination map. * @param dist the destination map.
* @param mode the moving mode. * @param mode the moving mode.
* @return the pointer to the value of the migrated record, or NULL on fa ilure. * @return the pointer to the value of the migrated record, or NULL on fa ilure.
*/ */
VALUE* migrate(const KEY& key, LinkedHashMap* dist, MoveMode mode) { VALUE* migrate(const KEY& key, LinkedHashMap* dist, MoveMode mode) {
_assert_(dist);
size_t hash = hash_(key); size_t hash = hash_(key);
size_t bidx = hash % bnum_; size_t bidx = hash % bnum_;
Record* rec = buckets_[bidx]; Record* rec = buckets_[bidx];
Record** entp = buckets_ + bidx; Record** entp = buckets_ + bidx;
while (rec) { while (rec) {
if (equalto_(rec->key, key)) { if (equalto_(rec->key, key)) {
if (rec->prev) rec->prev->next = rec->next; if (rec->prev) rec->prev->next = rec->next;
if (rec->next) rec->next->prev = rec->prev; if (rec->next) rec->next->prev = rec->prev;
if (rec == first_) first_ = rec->next; if (rec == first_) first_ = rec->next;
if (rec == last_) last_ = rec->prev; if (rec == last_) last_ = rec->prev;
skipping to change at line 383 skipping to change at line 402
} }
return NULL; return NULL;
} }
/** /**
* Retrieve a record. * Retrieve a record.
* @param key the key. * @param key the key.
* @param mode the moving mode. * @param mode the moving mode.
* @return the pointer to the value of the corresponding record, or NULL on failure. * @return the pointer to the value of the corresponding record, or NULL on failure.
*/ */
VALUE* get(const KEY& key, MoveMode mode) { VALUE* get(const KEY& key, MoveMode mode) {
_assert_(true);
size_t bidx = hash_(key) % bnum_; size_t bidx = hash_(key) % bnum_;
Record* rec = buckets_[bidx]; Record* rec = buckets_[bidx];
while (rec) { while (rec) {
if (equalto_(rec->key, key)) { if (equalto_(rec->key, key)) {
switch (mode) { switch (mode) {
default: { default: {
break; break;
} }
case MFIRST: { case MFIRST: {
if (first_ != rec) { if (first_ != rec) {
skipping to change at line 427 skipping to change at line 447
} else { } else {
rec = rec->child; rec = rec->child;
} }
} }
return NULL; return NULL;
} }
/** /**
* Remove all records. * Remove all records.
*/ */
void clear() { void clear() {
_assert_(true);
if (count_ < 1) return; if (count_ < 1) return;
Record* rec = last_; Record* rec = last_;
while (rec) { while (rec) {
Record* prev = rec->prev; Record* prev = rec->prev;
delete rec; delete rec;
rec = prev; rec = prev;
} }
for (size_t i = 0; i < bnum_; i++) { for (size_t i = 0; i < bnum_; i++) {
buckets_[i] = NULL; buckets_[i] = NULL;
} }
first_ = NULL; first_ = NULL;
last_ = NULL; last_ = NULL;
count_ = 0; count_ = 0;
} }
/** /**
* Get the number of records. * Get the number of records.
*/ */
size_t count() { size_t count() {
_assert_(true);
return count_; return count_;
} }
/** /**
* Get an iterator at the first record. * Get an iterator at the first record.
*/ */
Iterator begin() { Iterator begin() {
_assert_(true);
return Iterator(this, first_); return Iterator(this, first_);
} }
/** /**
* Get an iterator of the end sentry. * Get an iterator of the end sentry.
*/ */
Iterator end() { Iterator end() {
_assert_(true);
return Iterator(this, NULL); return Iterator(this, NULL);
} }
/** /**
* Get an iterator at a record. * Get an iterator at a record.
* @param key the key. * @param key the key.
* @return the pointer to the value of the corresponding record, or NULL on failure. * @return the pointer to the value of the corresponding record, or NULL on failure.
*/ */
Iterator find(const KEY& key) { Iterator find(const KEY& key) {
_assert_(true);
size_t bidx = hash_(key) % bnum_; size_t bidx = hash_(key) % bnum_;
Record* rec = buckets_[bidx]; Record* rec = buckets_[bidx];
while (rec) { while (rec) {
if (equalto_(rec->key, key)) { if (equalto_(rec->key, key)) {
return Iterator(this, rec); return Iterator(this, rec);
} else { } else {
rec = rec->child; rec = rec->child;
} }
} }
return Iterator(this, NULL); return Iterator(this, NULL);
} }
/** /**
* Get the reference of the key of the first record. * Get the reference of the key of the first record.
* @return the reference of the key of the first record. * @return the reference of the key of the first record.
*/ */
const KEY& first_key() { const KEY& first_key() {
_assert_(true);
return first_->key; return first_->key;
} }
/** /**
* Get the reference of the value of the first record. * Get the reference of the value of the first record.
* @return the reference of the value of the first record. * @return the reference of the value of the first record.
*/ */
VALUE& first_value() { VALUE& first_value() {
_assert_(true);
return first_->value; return first_->value;
} }
/** /**
* Get the reference of the key of the last record. * Get the reference of the key of the last record.
* @return the reference of the key of the last record. * @return the reference of the key of the last record.
*/ */
const KEY& last_key() { const KEY& last_key() {
_assert_(true);
return last_->key; return last_->key;
} }
/** /**
* Get the reference of the value of the last record. * Get the reference of the value of the last record.
* @return the reference of the value of the last record. * @return the reference of the value of the last record.
*/ */
VALUE& last_value() { VALUE& last_value() {
_assert_(true);
return last_->value; return last_->value;
} }
private: private:
/** /**
* Record data. * Record data.
*/ */
struct Record { struct Record {
KEY key; ///< key KEY key; ///< key
VALUE value; ///< value VALUE value; ///< value
Record* child; ///< child record Record* child; ///< child record
Record* prev; ///< previous record Record* prev; ///< previous record
Record* next; ///< next record Record* next; ///< next record
/** constructor */ /** constructor */
Record(const KEY& k, const VALUE& v) : explicit Record(const KEY& k, const VALUE& v) :
key(k), value(v), child(NULL), prev(NULL), next(NULL) {} key(k), value(v), child(NULL), prev(NULL), next(NULL) {
_assert_(true);
}
}; };
/** /**
* Initialize fields. * Initialize fields.
*/ */
void initialize() { void initialize() {
_assert_(true);
if (bnum_ >= LHMZMAPBNUM) { if (bnum_ >= LHMZMAPBNUM) {
buckets_ = (Record**)mapalloc(sizeof(*buckets_) * bnum_); buckets_ = (Record**)mapalloc(sizeof(*buckets_) * bnum_);
} else { } else {
buckets_ = new Record*[bnum_]; buckets_ = new Record*[bnum_];
for (size_t i = 0; i < bnum_; i++) { for (size_t i = 0; i < bnum_; i++) {
buckets_[i] = NULL; buckets_[i] = NULL;
} }
} }
} }
/** /**
* Clean up fields. * Clean up fields.
*/ */
void destroy() { void destroy() {
_assert_(true);
Record* rec = last_; Record* rec = last_;
while (rec) { while (rec) {
Record* prev = rec->prev; Record* prev = rec->prev;
delete rec; delete rec;
rec = prev; rec = prev;
} }
if (bnum_ >= LHMZMAPBNUM) { if (bnum_ >= LHMZMAPBNUM) {
mapfree(buckets_); mapfree(buckets_);
} else { } else {
delete[] buckets_; delete[] buckets_;
 End of changes. 30 change blocks. 
11 lines changed or deleted 44 lines changed or added


 kcprotodb.h   kcprotodb.h 
skipping to change at line 57 skipping to change at line 57
if (it != map->end()) return it; if (it != map->end()) return it;
return map->upper_bound(key); return map->upper_bound(key);
} }
template <class STRMAP> template <class STRMAP>
void map_tune(STRMAP* map) {} void map_tune(STRMAP* map) {}
template <> template <>
void map_tune(StringHashMap* map) { void map_tune(StringHashMap* map) {
map->rehash(PDBBNUM); map->rehash(PDBBNUM);
map->max_load_factor(FLT_MAX); map->max_load_factor(FLT_MAX);
} }
template <class STRMAP>
DB::Type map_type(STRMAP* map) {
return DB::TYPEPMISC;
}
template <>
DB::Type map_type(StringHashMap* map) {
return DB::TYPEPHASH;
}
template <>
DB::Type map_type(StringTreeMap* map) {
return DB::TYPEPTREE;
}
} }
/** /**
* Prototype implementation of file database with STL. * Prototype implementation of file database with STL.
* @param STRMAP a map compatible class of STL. * @param STRMAP a map compatible class of STL.
* @note The class ProtoHashDB is the instance using std::unordered_map. T he class ProtoTreeDB * @note The class ProtoHashDB is the instance using std::unordered_map. T he class ProtoTreeDB
* is the instance using std::map. * is the instance using std::map.
*/ */
template <class STRMAP> template <class STRMAP>
class ProtoDB : public FileDB { class ProtoDB : public FileDB {
skipping to change at line 87 skipping to change at line 99
* Cursor to indicate a record. * Cursor to indicate a record.
*/ */
class Cursor : public FileDB::Cursor { class Cursor : public FileDB::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);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true);
if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock 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
* the same record are blocked.
*/ */
virtual bool accept(Visitor* visitor, bool writable, bool step) { virtual bool accept(Visitor* visitor, bool writable = true, bool step =
false) {
_assert_(visitor);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(db_->omode_ & OWRITER)) { if (writable && !(db_->omode_ & OWRITER)) {
db_->set_error(Error::INVALID, "permission denied"); db_->set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
db_->set_error(Error::NOREC, "no record"); db_->set_error(Error::NOREC, "no record");
return false; return false;
} }
const std::string& key = it_->first; const std::string& key = it_->first;
const std::string& value = it_->second; const std::string& value = it_->second;
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_full(key.c_str(), key.size(), const char* vbuf = visitor->visit_full(key.c_str(), key.size(),
skipping to change at line 158 skipping to change at line 176
it_->second = std::string(vbuf, vsiz); it_->second = std::string(vbuf, vsiz);
if (step) it_++; if (step) it_++;
} }
return true; return true;
} }
/** /**
* Jump the cursor to the first record. * Jump the cursor to the first record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool jump() { virtual bool jump() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
it_ = db_->recs_.begin(); it_ = db_->recs_.begin();
if (it_ == db_->recs_.end()) {
db_->set_error(Error::NOREC, "no record");
return false;
}
return true; return true;
} }
/** /**
* Jump the cursor onto a record. * Jump the cursor onto a record.
* @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.
*/ */
virtual bool jump(const char* kbuf, size_t ksiz) { virtual bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
std::string key(kbuf, ksiz); std::string key(kbuf, ksiz);
it_ = map_find(&db_->recs_, key); it_ = map_find(&db_->recs_, key);
if (it_ == db_->recs_.end()) {
db_->set_error(Error::NOREC, "no record");
return false;
}
return true; return true;
} }
/** /**
* Jump the cursor to a record. * Jump the cursor to a record.
* @note Equal to the original Cursor::jump method except that the para meter is std::string. * @note Equal to the original Cursor::jump method except that the para meter is std::string.
*/ */
virtual bool jump(const std::string& key) { virtual bool jump(const std::string& key) {
_assert_(true);
return jump(key.c_str(), key.size()); return jump(key.c_str(), 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.
*/ */
virtual bool step() { virtual bool step() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(Error::INVALID, "not opened"); db_->set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (it_ == db_->recs_.end()) { if (it_ == db_->recs_.end()) {
db_->set_error(Error::NOREC, "no record"); db_->set_error(Error::NOREC, "no record");
return false; return false;
} }
it_++; it_++;
return true; return true;
} }
/**
* Get the database object.
* @return the database object.
*/
virtual ProtoDB* db() {
_assert_(true);
return db_;
}
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
Cursor(const Cursor&); Cursor(const Cursor&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
Cursor& operator =(const Cursor&); Cursor& operator =(const Cursor&);
/** The inner database. */ /** The inner database. */
ProtoDB* db_; ProtoDB* db_;
/** The inner iterator. */ /** The inner iterator. */
typename STRMAP::iterator it_; typename STRMAP::iterator it_;
}; };
/** /**
* Default constructor. * Default constructor.
*/ */
explicit ProtoDB() : mlock_(), error_(), omode_(0), recs_(), explicit ProtoDB() : mlock_(), error_(), omode_(0), recs_(),
curs_(), path_(""), size_(0), tran_(false), trlogs_( ), trsize_(0) { curs_(), path_(""), size_(0), tran_(false), trlogs_( ), trsize_(0) {
_assert_(true);
map_tune(&recs_); map_tune(&recs_);
} }
/** /**
* Destructor. * Destructor.
* @note If the database is not closed, it is closed implicitly. * @note If the database is not closed, it is closed implicitly.
*/ */
virtual ~ProtoDB() { virtual ~ProtoDB() {
_assert_(true);
if (omode_ != 0) close(); if (omode_ != 0) close();
if (curs_.size() > 0) {
typename CursorList::const_iterator cit = curs_.begin();
typename CursorList::const_iterator citend = curs_.end();
while (cit != citend) {
Cursor* cur = *cit;
cur->db_ = NULL;
cit++;
}
}
} }
/** /**
* Accept a visitor to a record. * Accept a visitor to a record.
* @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.
* @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
* same record are blocked.
*/ */
virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool
writable) { writable = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
if (writable) { if (writable) {
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(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);
if (it == recs_.end()) { if (it == recs_.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);
if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) { if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) {
if (tran_) { if (tran_) {
TranLog log(key); TranLog log(key);
skipping to change at line 305 skipping to change at line 357
set_error(Error::INVALID, "not opened"); set_error(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);
if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) { if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
} else { } else {
const std::string& value = it->second; const std::string& value = it->second;
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_full(kbuf, ksiz, value.c_str(), v alue.size(), &vsiz); const char* vbuf = visitor->visit_full(kbuf, ksiz, value.c_str(), v alue.size(), &vsiz);
if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) { if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
} }
} }
return true; return true;
} }
/** /**
* 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.
* @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.
*/ */
virtual bool iterate(Visitor *visitor, bool writable) { virtual bool iterate(Visitor *visitor, bool writable = true) {
_assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !(omode_ & OWRITER)) { if (writable && !(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
size_t vsiz;
const char*vbuf = visitor->visit_empty("", 0, &vsiz);
typename STRMAP::iterator it = recs_.begin(); typename STRMAP::iterator it = recs_.begin();
typename STRMAP::iterator itend = recs_.end(); typename STRMAP::iterator itend = recs_.end();
while (it != itend) { while (it != itend) {
const std::string& key = it->first; const std::string& key = it->first;
const std::string& value = it->second; const std::string& value = it->second;
vbuf = visitor->visit_full(key.c_str(), key.size(), value.c_str(), va size_t vsiz;
lue.size(), &vsiz); const char* vbuf = visitor->visit_full(key.c_str(), key.size(),
value.c_str(), value.size(), &
vsiz);
if (vbuf == Visitor::REMOVE) { if (vbuf == Visitor::REMOVE) {
size_ -= key.size() + value.size(); size_ -= key.size() + value.size();
recs_.erase(it++); recs_.erase(it++);
} else if (vbuf == Visitor::NOP) { } else if (vbuf == Visitor::NOP) {
it++; it++;
} else { } else {
size_ -= value.size(); size_ -= value.size();
size_ += vsiz; size_ += vsiz;
it->second = std::string(vbuf, vsiz); it->second = std::string(vbuf, vsiz);
it++; it++;
} }
} }
visitor->visit_empty("", 0, &vsiz);
return true; return true;
} }
/** /**
* Get the last happened error. * Get the last happened error.
* @return the last happened error. * @return the last happened error.
*/ */
virtual Error error() const { virtual Error error() const {
_assert_(true);
return error_; return error_;
} }
/** /**
* Set the error information. * Set the error information.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
virtual void set_error(Error::Code code, const char* message) { virtual void set_error(Error::Code code, const char* message) {
_assert_(message);
error_->set(code, message); error_->set(code, message);
} }
/** /**
* Open a database file. * Open a database file.
* @param path the path of a database file. * @param path the path of a database file.
* @param mode the connection mode. FileDB::OWRITER as a writer, FileDB: * @param mode the connection mode. FileDB::OWRITER as a writer, FileDB:
:OREADER as a reader. :OREADER as a
* The following may be added to the writer mode by bitwise-or: FileDB::O * reader. The following may be added to the writer mode by bitwise-or:
CREATE, which means FileDB::OCREATE,
* it creates a new database if the file does not exist, FileDB::OTRUNCAT * which means it creates a new database if the file does not exist, File
E, which means it DB::OTRUNCATE, which
* creates a new database regardless if the file exists, FileDB::OAUTOTRA * means it creates a new database regardless if the file exists, FileDB:
N, which means each :OAUTOTRAN, which
* updating operation is performed in implicit transaction, FileDB::OAUTO * means each updating operation is performed in implicit transaction, Fi
SYNC, which means leDB::OAUTOSYNC,
* each updating operation is followed by implicit synchronization with t * which means each updating operation is followed by implicit synchroniz
he file system. The ation with the file
* following may be added to both of the reader mode and the writer mode * system. The following may be added to both of the reader mode and the
by bitwise-or: writer mode by
* FileDB::ONOLOCK, which means it opens the database file without file l * bitwise-or: FileDB::ONOLOCK, which means it opens the database file wi
ocking, thout file locking,
* FileDB::OTRYLOCK, which means locking is performed without blocking, F * FileDB::OTRYLOCK, which means locking is performed without blocking, F
ile::ONOREPAIR, which ileDB::ONOREPAIR,
* means the database file is not repaired implicitly even if file destru * which means the database file is not repaired implicitly even if file
ction is detected. destruction is
* 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 FileDB::close method
when it is no
* longer in use.
*/ */
virtual bool open(const std::string& path, uint32_t mode) { virtual bool open(const std::string& path, uint32_t mode = OWRITER | OCRE
ATE) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(Error::INVALID, "already opened"); set_error(Error::INVALID, "already opened");
return false; return false;
} }
omode_ = mode; omode_ = mode;
path_.append(path); path_.append(path);
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.
*/ */
virtual bool close() { virtual bool close() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
tran_ = false; tran_ = false;
trlogs_.clear(); trlogs_.clear();
recs_.clear(); recs_.clear();
if (curs_.size() > 0) { if (curs_.size() > 0) {
typename CursorList::const_iterator cit = curs_.begin(); typename CursorList::const_iterator cit = curs_.begin();
skipping to change at line 432 skipping to change at line 492
omode_ = 0; omode_ = 0;
return true; return true;
} }
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool synchronize(bool hard, FileProcessor* proc) { virtual bool synchronize(bool hard = false, FileProcessor* proc = NULL) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
if (proc && !proc->process(path_)) { if (proc && !proc->process(path_, recs_.size(), size_)) {
set_error(Error::MISC, "postprocessing failed"); set_error(Error::MISC, "postprocessing failed");
err = true; err = true;
} }
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool begin_transaction(bool hard) { virtual bool begin_transaction(bool hard = false) {
_assert_(true);
for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) { for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) {
mlock_.lock_writer(); mlock_.lock_writer();
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!(omode_ & OWRITER)) { if (!(omode_ & OWRITER)) {
set_error(Error::INVALID, "permission denied"); set_error(Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!tran_) break; if (!tran_) break;
mlock_.unlock(); mlock_.unlock();
if (wsec > 1.0) wsec = 1.0; if (wsec > 1.0) wsec = 1.0;
Thread::sleep(wsec); Thread::sleep(wsec);
} }
tran_ = true; tran_ = true;
trsize_ = size_; trsize_ = size_;
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* Commit transaction. * Try to begin transaction.
* @param hard true for physical synchronization with the device, or fals
e for logical
* synchronization with the file system.
* @return true on success, or false on failure.
*/
virtual bool begin_transaction_try(bool hard = false) {
_assert_(true);
mlock_.lock_writer();
if (omode_ == 0) {
set_error(Error::INVALID, "not opened");
mlock_.unlock();
return false;
}
if (!(omode_ & OWRITER)) {
set_error(Error::NOPERM, "permission denied");
mlock_.unlock();
return false;
}
if (tran_) {
set_error(Error::LOGIC, "competition avoided");
mlock_.unlock();
return false;
}
tran_ = true;
trsize_ = size_;
mlock_.unlock();
return true;
}
/**
* 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.
*/ */
virtual bool end_transaction(bool commit) { virtual bool end_transaction(bool commit = true) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(Error::INVALID, "not in transaction"); set_error(Error::INVALID, "not in transaction");
return false; return false;
} }
if (!commit) { if (!commit) {
skipping to change at line 525 skipping to change at line 617
} }
trlogs_.clear(); trlogs_.clear();
tran_ = false; tran_ = false;
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.
*/ */
virtual bool clear() { virtual bool clear() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return -1; return false;
} }
recs_.clear(); recs_.clear();
if (curs_.size() > 0) { if (curs_.size() > 0) {
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;
cur->it_ = recs_.end(); cur->it_ = recs_.end();
cit++; cit++;
} }
} }
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.
*/ */
virtual int64_t count() { virtual int64_t count() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(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.
*/ */
virtual int64_t size() { virtual int64_t size() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(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 in bytes, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
virtual std::string path() { virtual std::string path() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(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.
*/ */
virtual bool status(std::map<std::string, std::string>* strmap) { virtual bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(Error::INVALID, "not opened"); set_error(Error::INVALID, "not opened");
return false; return false;
} }
(*strmap)["type"] = "ProtoDB"; (*strmap)["type"] = "ProtoDB";
(*strmap)["realtype"] = strprintf("%u", (unsigned)map_type(&recs_));
(*strmap)["path"] = path_; (*strmap)["path"] = path_;
(*strmap)["count"] = strprintf("%lld", (long long)recs_.size()); (*strmap)["count"] = strprintf("%lld", (long long)recs_.size());
(*strmap)["size"] = strprintf("%lld", (long long)size_); (*strmap)["size"] = strprintf("%lld", (long long)size_);
return true; return true;
} }
/** /**
* Create a cursor object. * Create a cursor object.
* @return the return value is the cursor object. * @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the const ructor, it should be * @note Because the object of the return value is allocated by the const ructor, it should be
* released with the delete operator when it is no longer in use. * released with the delete operator when it is no longer in use.
*/ */
virtual Cursor* cursor() { virtual Cursor* cursor() {
_assert_(true);
return new Cursor(this); return new Cursor(this);
} }
private: private:
/** /**
* Transaction log. * Transaction log.
*/ */
struct TranLog { struct TranLog {
bool full; ///< flag whether full bool full; ///< flag whether full
std::string key; ///< old key std::string key; ///< old key
std::string value; ///< old value std::string value; ///< old value
/** constructor for a full record */ /** constructor for a full record */
TranLog(const std::string& pkey, const std::string& pvalue) : explicit TranLog(const std::string& pkey, const std::string& pvalue) :
full(true), key(pkey), value(pvalue) {} full(true), key(pkey), value(pvalue) {}
/** constructor for an empty record */ /** constructor for an empty record */
TranLog(const std::string& pkey) : full(false), key(pkey) {} explicit TranLog(const std::string& pkey) : full(false), key(pkey) {}
}; };
/** 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_; SpinRWLock mlock_;
/** The last happened error. */ /** The last happened error. */
TSD<Error> error_; TSD<Error> error_;
/** The open mode. */ /** The open mode. */
 End of changes. 52 change blocks. 
47 lines changed or deleted 152 lines changed or added


 kcthread.h   kcthread.h 
skipping to change at line 145 skipping to change at line 145
/** /**
* Scoped mutex device. * Scoped mutex device.
*/ */
class ScopedMutex { class ScopedMutex {
public: public:
/** /**
* Constructor. * Constructor.
* @param mutex a mutex to lock the block. * @param mutex a mutex to lock the block.
*/ */
explicit ScopedMutex(Mutex* mutex) : mutex_(mutex) { explicit ScopedMutex(Mutex* mutex) : mutex_(mutex) {
_assert_(mutex);
mutex_->lock(); mutex_->lock();
} }
/** /**
* Destructor. * Destructor.
*/ */
~ScopedMutex() { ~ScopedMutex() {
_assert_(true);
mutex_->unlock(); mutex_->unlock();
} }
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
ScopedMutex(const ScopedMutex&); ScopedMutex(const ScopedMutex&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
ScopedMutex& operator =(const ScopedMutex&); ScopedMutex& operator =(const ScopedMutex&);
/** The inner device. */ /** The inner device. */
Mutex* mutex_; Mutex* mutex_;
}; };
skipping to change at line 172 skipping to change at line 174
/** /**
* Slotted mutex device. * Slotted mutex device.
* @param SLOTNUM the number of slots. * @param SLOTNUM the number of slots.
*/ */
template <int32_t SLOTNUM> template <int32_t SLOTNUM>
class SlottedMutex { class SlottedMutex {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
explicit SlottedMutex() : locks_() {} explicit SlottedMutex() : locks_() {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
*/ */
~SlottedMutex() {} ~SlottedMutex() {
_assert_(true);
}
/** /**
* Get the lock of a slot. * Get the lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void lock(int32_t idx) { void lock(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].lock(); locks_[idx].lock();
} }
/** /**
* Release the lock of a slot. * Release the lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void unlock(int32_t idx) { void unlock(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].unlock(); locks_[idx].unlock();
} }
/** /**
* Get the locks of all slots. * Get the locks of all slots.
*/ */
void lock_all() { void lock_all() {
_assert_(true);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
locks_[i].lock(); locks_[i].lock();
} }
} }
/** /**
* Release the locks of all slots. * Release the locks of all slots.
*/ */
void unlock_all() { void unlock_all() {
_assert_(true);
for (int32_t i = SLOTNUM - 1; i >= 0; i--) { for (int32_t i = SLOTNUM - 1; i >= 0; i--) {
locks_[i].unlock(); locks_[i].unlock();
} }
} }
private: private:
/** The inner devices. */ /** The inner devices. */
Mutex locks_[SLOTNUM]; Mutex locks_[SLOTNUM];
}; };
/** /**
skipping to change at line 257 skipping to change at line 267
/** /**
* Scoped spin lock device. * Scoped spin lock device.
*/ */
class ScopedSpinLock { class ScopedSpinLock {
public: public:
/** /**
* Constructor. * Constructor.
* @param spinlock a spin lock to lock the block. * @param spinlock a spin lock to lock the block.
*/ */
explicit ScopedSpinLock(SpinLock* spinlock) : spinlock_(spinlock) { explicit ScopedSpinLock(SpinLock* spinlock) : spinlock_(spinlock) {
_assert_(spinlock);
spinlock_->lock(); spinlock_->lock();
} }
/** /**
* Destructor. * Destructor.
*/ */
~ScopedSpinLock() { ~ScopedSpinLock() {
_assert_(true);
spinlock_->unlock(); spinlock_->unlock();
} }
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
ScopedSpinLock(const ScopedSpinLock&); ScopedSpinLock(const ScopedSpinLock&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
ScopedSpinLock& operator =(const ScopedSpinLock&); ScopedSpinLock& operator =(const ScopedSpinLock&);
/** The inner device. */ /** The inner device. */
SpinLock* spinlock_; SpinLock* spinlock_;
}; };
skipping to change at line 284 skipping to change at line 296
/** /**
* Slotted spin lock devices. * Slotted spin lock devices.
* @param SLOTNUM the number of slots. * @param SLOTNUM the number of slots.
*/ */
template <int32_t SLOTNUM> template <int32_t SLOTNUM>
class SlottedSpinLock { class SlottedSpinLock {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
explicit SlottedSpinLock() : locks_() {} explicit SlottedSpinLock() : locks_() {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
*/ */
~SlottedSpinLock() {} ~SlottedSpinLock() {
_assert_(true);
}
/** /**
* Get the lock of a slot. * Get the lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void lock(int32_t idx) { void lock(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].lock(); locks_[idx].lock();
} }
/** /**
* Release the lock of a slot. * Release the lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void unlock(int32_t idx) { void unlock(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].unlock(); locks_[idx].unlock();
} }
/** /**
* Get the locks of all slots. * Get the locks of all slots.
*/ */
void lock_all() { void lock_all() {
_assert_(true);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
locks_[i].lock(); locks_[i].lock();
} }
} }
/** /**
* Release the locks of all slots. * Release the locks of all slots.
*/ */
void unlock_all() { void unlock_all() {
_assert_(true);
for (int32_t i = SLOTNUM - 1; i >= 0; i--) { for (int32_t i = SLOTNUM - 1; i >= 0; i--) {
locks_[i].unlock(); locks_[i].unlock();
} }
} }
private: private:
/** The inner devices. */ /** The inner devices. */
SpinLock locks_[SLOTNUM]; SpinLock locks_[SLOTNUM];
}; };
/** /**
skipping to change at line 379 skipping to change at line 399
* Scoped reader-writer locking device. * Scoped reader-writer locking device.
*/ */
class ScopedRWLock { class ScopedRWLock {
public: public:
/** /**
* Constructor. * Constructor.
* @param rwlock a rwlock to lock the block. * @param rwlock a rwlock to lock the block.
* @param writer true for writer lock, or false for reader lock. * @param writer true for writer lock, or false for reader lock.
*/ */
explicit ScopedRWLock(RWLock* rwlock, bool writer) : rwlock_(rwlock) { explicit ScopedRWLock(RWLock* rwlock, bool writer) : rwlock_(rwlock) {
_assert_(rwlock);
if (writer) { if (writer) {
rwlock_->lock_writer(); rwlock_->lock_writer();
} else { } else {
rwlock_->lock_reader(); rwlock_->lock_reader();
} }
} }
/** /**
* Destructor. * Destructor.
*/ */
~ScopedRWLock() { ~ScopedRWLock() {
_assert_(true);
rwlock_->unlock(); rwlock_->unlock();
} }
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
ScopedRWLock(const ScopedRWLock&); ScopedRWLock(const ScopedRWLock&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
ScopedRWLock& operator =(const ScopedRWLock&); ScopedRWLock& operator =(const ScopedRWLock&);
/** The inner device. */ /** The inner device. */
RWLock* rwlock_; RWLock* rwlock_;
}; };
skipping to change at line 410 skipping to change at line 432
/** /**
* Slotted reader-writer lock devices. * Slotted reader-writer lock devices.
* @param SLOTNUM the number of slots. * @param SLOTNUM the number of slots.
*/ */
template <int32_t SLOTNUM> template <int32_t SLOTNUM>
class SlottedRWLock { class SlottedRWLock {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
explicit SlottedRWLock() : locks_() {} explicit SlottedRWLock() : locks_() {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
*/ */
~SlottedRWLock() {} ~SlottedRWLock() {
_assert_(true);
}
/** /**
* Get the writer lock of a slot. * Get the writer lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void lock_writer(int32_t idx) { void lock_writer(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].lock_writer(); locks_[idx].lock_writer();
} }
/** /**
* Get the reader lock of a slot. * Get the reader lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void lock_reader(int32_t idx) { void lock_reader(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].lock_reader(); locks_[idx].lock_reader();
} }
/** /**
* Release the lock of a slot. * Release the lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void unlock(int32_t idx) { void unlock(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].unlock(); locks_[idx].unlock();
} }
/** /**
* Get the writer locks of all slots. * Get the writer locks of all slots.
*/ */
void lock_writer_all() { void lock_writer_all() {
_assert_(true);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
locks_[i].lock_writer(); locks_[i].lock_writer();
} }
} }
/** /**
* Get the reader locks of all slots. * Get the reader locks of all slots.
*/ */
void lock_reader_all() { void lock_reader_all() {
_assert_(true);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
locks_[i].lock_reader(); locks_[i].lock_reader();
} }
} }
/** /**
* Release the locks of all slots. * Release the locks of all slots.
*/ */
void unlock_all() { void unlock_all() {
_assert_(true);
for (int32_t i = SLOTNUM - 1; i >= 0; i--) { for (int32_t i = SLOTNUM - 1; i >= 0; i--) {
locks_[i].unlock(); locks_[i].unlock();
} }
} }
private: private:
/** The inner devices. */ /** The inner devices. */
RWLock locks_[SLOTNUM]; RWLock locks_[SLOTNUM];
}; };
/** /**
skipping to change at line 529 skipping to change at line 561
* Scoped reader-writer locking device. * Scoped reader-writer locking device.
*/ */
class ScopedSpinRWLock { class ScopedSpinRWLock {
public: public:
/** /**
* Constructor. * Constructor.
* @param srwlock a spin rwlock to lock the block. * @param srwlock a spin rwlock to lock the block.
* @param writer true for writer lock, or false for reader lock. * @param writer true for writer lock, or false for reader lock.
*/ */
explicit ScopedSpinRWLock(SpinRWLock* srwlock, bool writer) : srwlock_(sr wlock) { explicit ScopedSpinRWLock(SpinRWLock* srwlock, bool writer) : srwlock_(sr wlock) {
_assert_(srwlock);
if (writer) { if (writer) {
srwlock_->lock_writer(); srwlock_->lock_writer();
} else { } else {
srwlock_->lock_reader(); srwlock_->lock_reader();
} }
} }
/** /**
* Destructor. * Destructor.
*/ */
~ScopedSpinRWLock() { ~ScopedSpinRWLock() {
_assert_(true);
srwlock_->unlock(); srwlock_->unlock();
} }
private: private:
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
ScopedSpinRWLock(const ScopedSpinRWLock&); ScopedSpinRWLock(const ScopedSpinRWLock&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
ScopedSpinRWLock& operator =(const ScopedSpinRWLock&); ScopedSpinRWLock& operator =(const ScopedSpinRWLock&);
/** The inner device. */ /** The inner device. */
SpinRWLock* srwlock_; SpinRWLock* srwlock_;
}; };
skipping to change at line 560 skipping to change at line 594
/** /**
* Slotted lightweight reader-writer lock devices. * Slotted lightweight reader-writer lock devices.
* @param SLOTNUM the number of slots. * @param SLOTNUM the number of slots.
*/ */
template <int32_t SLOTNUM> template <int32_t SLOTNUM>
class SlottedSpinRWLock { class SlottedSpinRWLock {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
explicit SlottedSpinRWLock() : locks_() {} explicit SlottedSpinRWLock() : locks_() {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
*/ */
~SlottedSpinRWLock() {} ~SlottedSpinRWLock() {
_assert_(true);
}
/** /**
* Get the writer lock of a slot. * Get the writer lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void lock_writer(int32_t idx) { void lock_writer(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].lock_writer(); locks_[idx].lock_writer();
} }
/** /**
* Get the reader lock of a slot. * Get the reader lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void lock_reader(int32_t idx) { void lock_reader(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].lock_reader(); locks_[idx].lock_reader();
} }
/** /**
* Release the lock of a slot. * Release the lock of a slot.
* @param idx the index of a slot. * @param idx the index of a slot.
*/ */
void unlock(int32_t idx) { void unlock(int32_t idx) {
_assert_(idx >= 0);
locks_[idx].unlock(); locks_[idx].unlock();
} }
/** /**
* Get the writer locks of all slots. * Get the writer locks of all slots.
*/ */
void lock_writer_all() { void lock_writer_all() {
_assert_(true);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
locks_[i].lock_writer(); locks_[i].lock_writer();
} }
} }
/** /**
* Get the reader locks of all slots. * Get the reader locks of all slots.
*/ */
void lock_reader_all() { void lock_reader_all() {
_assert_(true);
for (int32_t i = 0; i < SLOTNUM; i++) { for (int32_t i = 0; i < SLOTNUM; i++) {
locks_[i].lock_reader(); locks_[i].lock_reader();
} }
} }
/** /**
* Release the locks of all slots. * Release the locks of all slots.
*/ */
void unlock_all() { void unlock_all() {
_assert_(true);
for (int32_t i = SLOTNUM - 1; i >= 0; i--) { for (int32_t i = SLOTNUM - 1; i >= 0; i--) {
locks_[i].unlock(); locks_[i].unlock();
} }
} }
private: private:
/** The inner devices. */ /** The inner devices. */
SpinRWLock locks_[SLOTNUM]; SpinRWLock locks_[SLOTNUM];
}; };
/** /**
skipping to change at line 699 skipping to change at line 743
/** /**
* Smart pointer to thread specific data. * Smart pointer to thread specific data.
*/ */
template <class TYPE> template <class TYPE>
class TSD { class TSD {
public: public:
/** /**
* Default constructor. * Default constructor.
*/ */
explicit TSD() : key_(delete_value) {} explicit TSD() : key_(delete_value) {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
*/ */
~TSD() { ~TSD() {
_assert_(true);
TYPE* obj = (TYPE*)key_.get(); TYPE* obj = (TYPE*)key_.get();
if (obj) { if (obj) {
delete obj; delete obj;
key_.set(NULL); key_.set(NULL);
} }
} }
/** /**
* Dereference operator. * Dereference operator.
* @return the reference to the inner object. * @return the reference to the inner object.
*/ */
TYPE& operator *() { TYPE& operator *() {
_assert_(true);
TYPE* obj = (TYPE*)key_.get(); TYPE* obj = (TYPE*)key_.get();
if (!obj) { if (!obj) {
obj = new TYPE; obj = new TYPE;
key_.set(obj); key_.set(obj);
} }
return *obj; return *obj;
} }
/** /**
* Member reference operator. * Member reference operator.
* @return the pointer to the inner object. * @return the pointer to the inner object.
*/ */
TYPE* operator ->() { TYPE* operator ->() {
_assert_(true);
TYPE* obj = (TYPE*)key_.get(); TYPE* obj = (TYPE*)key_.get();
if (!obj) { if (!obj) {
obj = new TYPE; obj = new TYPE;
key_.set(obj); key_.set(obj);
} }
return obj; return obj;
} }
/** /**
* Cast operator to the original type. * Cast operator to the original type.
* @return the copy of the inner object. * @return the copy of the inner object.
*/ */
operator TYPE() const { operator TYPE() const {
_assert_(true);
TYPE* obj = (TYPE*)key_.get(); TYPE* obj = (TYPE*)key_.get();
if (!obj) return TYPE(); if (!obj) return TYPE();
return *obj; return *obj;
} }
private: private:
/** /**
* Delete the inner object. * Delete the inner object.
* @param obj the inner object. * @param obj the inner object.
*/ */
static void delete_value(void* obj) { static void delete_value(void* obj) {
_assert_(true);
delete (TYPE*)obj; delete (TYPE*)obj;
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
TSD(const TSD&); TSD(const TSD&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
TSD& operator =(const TSD&); TSD& operator =(const TSD&);
/** Key of thread specific data. */ /** Key of thread specific data. */
TSDKey key_; TSDKey key_;
}; };
/** /**
* Integer with atomic operations. * Integer with atomic operations.
*/ */
class AtomicInt64 { class AtomicInt64 {
public: public:
/** /**
* Default constructor. * Default constructor.
*/ */
AtomicInt64() : value_(0), lock_() {} AtomicInt64() : value_(0), lock_() {
_assert_(true);
}
/** /**
* Copy constructor. * Copy constructor.
* @param src the source object. * @param src the source object.
*/ */
AtomicInt64(const AtomicInt64& src) : value_(src.get()), lock_() {}; AtomicInt64(const AtomicInt64& src) : value_(src.get()), lock_() {
_assert_(true);
};
/** /**
* Constructor. * Constructor.
* @param num the initial value. * @param num the initial value.
*/ */
AtomicInt64(int64_t num) : value_(num), lock_() {} AtomicInt64(int64_t num) : value_(num), lock_() {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
*/ */
~AtomicInt64() {} ~AtomicInt64() {
_assert_(true);
}
/** /**
* Set the new value. * Set the new value.
* @param val the new value. * @param val the new value.
* @return the old value. * @return the old value.
*/ */
int64_t set(int64_t val); int64_t set(int64_t val);
/** /**
* Add a value. * Add a value.
* @param val the additional value. * @param val the additional value.
* @return the old value. * @return the old value.
skipping to change at line 812 skipping to change at line 871
* Get the current value. * Get the current value.
* @return the current value. * @return the current value.
*/ */
int64_t get() const; int64_t get() const;
/** /**
* Assignment operator from the self type. * Assignment operator from the self type.
* @param right the right operand. * @param right the right operand.
* @return the reference to itself. * @return the reference to itself.
*/ */
AtomicInt64& operator =(const AtomicInt64& right) { AtomicInt64& operator =(const AtomicInt64& right) {
_assert_(true);
set(right.get()); set(right.get());
return *this; return *this;
} }
/** /**
* Assignment operator from integer. * Assignment operator from integer.
* @param right the right operand. * @param right the right operand.
* @return the reference to itself. * @return the reference to itself.
*/ */
AtomicInt64& operator =(const int64_t& right) { AtomicInt64& operator =(const int64_t& right) {
_assert_(true);
set(right); set(right);
return *this; return *this;
} }
/** /**
* Cast operator to integer. * Cast operator to integer.
* @return the current value. * @return the current value.
*/ */
operator int64_t() const { operator int64_t() const {
_assert_(true);
return get(); return get();
} }
/** /**
* Summation assignment operator by integer. * Summation assignment operator by integer.
* @param right the right operand. * @param right the right operand.
* @return the reference to itself. * @return the reference to itself.
*/ */
AtomicInt64& operator +=(int64_t right) { AtomicInt64& operator +=(int64_t right) {
_assert_(true);
add(right); add(right);
return *this; return *this;
} }
/** /**
* Subtraction assignment operator by integer. * Subtraction assignment operator by integer.
* @param right the right operand. * @param right the right operand.
* @return the reference to itself. * @return the reference to itself.
*/ */
AtomicInt64& operator -=(int64_t right) { AtomicInt64& operator -=(int64_t right) {
_assert_(true);
add(-right); add(-right);
return *this; return *this;
} }
/** /**
* Secure the least value * Secure the least value
* @param val the least value * @param val the least value
* @return the current value. * @return the current value.
*/ */
int64_t secure_least(int64_t val) { int64_t secure_least(int64_t val) {
_assert_(true);
while (true) { while (true) {
int64_t cur = get(); int64_t cur = get();
if (cur >= val) return cur; if (cur >= val) return cur;
if (cas(cur, val)) break; if (cas(cur, val)) break;
} }
return val; return val;
} }
private: private:
/** The value. */ /** The value. */
volatile int64_t value_; volatile int64_t value_;
 End of changes. 52 change blocks. 
13 lines changed or deleted 78 lines changed or added


 kctreedb.h   kctreedb.h 
skipping to change at line 39 skipping to change at line 39
/** /**
* Constants for implementation. * Constants for implementation.
*/ */
namespace { namespace {
const int32_t TDBSLOTNUM = 16; ///< number of cache slots const int32_t TDBSLOTNUM = 16; ///< number of cache slots
const uint8_t TDBDEFAPOW = 8; ///< default alignment power const uint8_t TDBDEFAPOW = 8; ///< default alignment power
const uint8_t TDBDEFFPOW = 10; ///< default free block pool power const uint8_t TDBDEFFPOW = 10; ///< default free block pool power
const int64_t TDBDEFBNUM = 64LL << 10; ///< default bucket number const int64_t TDBDEFBNUM = 64LL << 10; ///< default bucket number
const int32_t TDBDEFPSIZ = 8192; ///< default page size const int32_t TDBDEFPSIZ = 8192; ///< default page size
const int64_t TDBDEFCCAP = 64LL << 20; ///< default capacity size of the cache memory const int64_t TDBDEFPCCAP = 64LL << 20; ///< default capacity size of the page cache
const char TDBMETAKEY[] = "@"; ///< key of the record for meta da ta const char TDBMETAKEY[] = "@"; ///< key of the record for meta da ta
const int64_t TDBHEADSIZ = 64; ///< size of the header const int64_t TDBHEADSIZ = 64; ///< size of the header
const int64_t TDBMOFFNUMS = 8; ///< offset of the numbers const int64_t TDBMOFFNUMS = 8; ///< offset of the numbers
const char TDBLNPREFIX = 'L'; ///< prefix of leaf nodes const char TDBLNPREFIX = 'L'; ///< prefix of leaf nodes
const char TDBINPREFIX = 'I'; ///< prefix of inner nodes const char TDBINPREFIX = 'I'; ///< prefix of inner nodes
const size_t TDBAVGWAY = 16; ///< average number of ways of eac h node const size_t TDBAVGWAY = 16; ///< average number of ways of eac h node
const size_t TDBWARMRATIO = 4; ///< ratio of the warm cache const size_t TDBWARMRATIO = 4; ///< ratio of the warm cache
const size_t TDBINFLRATIO = 32; ///< ratio of flushing inner nodes const size_t TDBINFLRATIO = 32; ///< ratio of flushing inner nodes
const size_t TDBDEFLINUM = 64; ///< default number of items in ea ch leaf node const size_t TDBDEFLINUM = 64; ///< default number of items in ea ch leaf node
const size_t TDBDEFIINUM = 128; ///< default number of items in ea ch inner node const size_t TDBDEFIINUM = 128; ///< default number of items in ea ch inner node
const size_t TDBRECBUFSIZ = 64; ///< size of the record buffer const size_t TDBRECBUFSIZ = 64; ///< size of the record buffer
const int64_t TDBINIDBASE = 1LL << 48; ///< base ID number for inner node s const int64_t TDBINIDBASE = 1LL << 48; ///< base ID number for inner node s
const size_t TDBINLINKMIN = 8; ///< minimum number of links in ea ch inner node const size_t TDBINLINKMIN = 8; ///< minimum number of links in ea ch inner node
const int32_t TDBLEVELMAX = 16; ///< maximum level of B+ tree const int32_t TDBLEVELMAX = 16; ///< maximum level of B+ tree
const int32_t TDBATRANCNUM = 256; ///< number of cached nodes for au
to transaction
const char* BDBTMPPATHEXT = "tmpkct"; ///< extension of the temporary fi
le
} }
/** /**
* File tree database. * File tree database.
*/ */
class TreeDB : public FileDB { class TreeDB : public FileDB {
public: public:
class Cursor; class Cursor;
private: private:
struct Record; struct Record;
skipping to change at line 90 skipping to change at line 92
* Cursor to indicate a record. * Cursor to indicate a record.
*/ */
class Cursor : public FileDB::Cursor { class Cursor : public FileDB::Cursor {
friend class TreeDB; friend class TreeDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(TreeDB* db) : db_(db), stack_(), kbuf_(NULL), ksiz_(0), lid_(0) { explicit Cursor(TreeDB* db) : db_(db), stack_(), kbuf_(NULL), ksiz_(0), lid_(0) {
_assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true);
if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedSpinRWLock lock(&db_->mlock_, true);
if (kbuf_ && kbuf_ != stack_) delete[] kbuf_; if (kbuf_) clear_position();
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
* the same record are blocked.
*/ */
virtual bool accept(Visitor* visitor, bool writable, bool step) { virtual bool accept(Visitor* visitor, bool writable = true, bool step =
false) {
_assert_(visitor);
db_->mlock_.lock_reader(); db_->mlock_.lock_reader();
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
db_->mlock_.unlock(); db_->mlock_.unlock();
return false; return false;
} }
if (writable && !(db_->writer_)) { if (writable && !(db_->writer_)) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "permission deni ed"); db_->set_error(__FILE__, __LINE__, Error::NOPERM, "permission denie d");
db_->mlock_.unlock(); db_->mlock_.unlock();
return false; return false;
} }
if (!kbuf_) { if (!kbuf_) {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record"); db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
db_->mlock_.unlock(); db_->mlock_.unlock();
return false; return false;
} }
bool err = false;
bool hit = false;
if (lid_ > 0 && !accept_spec(visitor, writable, step, &hit)) err = tr
ue;
if (!err && !hit) {
if (!db_->mlock_.promote()) {
db_->mlock_.unlock();
db_->mlock_.lock_writer();
}
if (kbuf_) {
bool retry = true;
while (!err && retry) {
if (!accept_atom(visitor, step, &retry)) err = true;
}
} else {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
err = true;
}
}
db_->mlock_.unlock();
return !err;
}
/**
* Jump the cursor to the first record.
* @return true on success, or false on failure.
*/
virtual bool jump() {
_assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false;
}
if (kbuf_) clear_position();
bool err = false;
if (!set_position(db_->first_)) err = true;
return !err;
}
/**
* Jump the cursor onto a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @return true on success, or false on failure.
*/
virtual bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false;
}
if (kbuf_) clear_position();
set_position(kbuf, ksiz, 0);
bool err = false;
if (!adjust_position()) err = true;
return !err;
}
/**
* Jump the cursor to a record.
* @note Equal to the original Cursor::jump method except that the para
meter is std::string.
*/
virtual bool jump(const std::string& key) {
_assert_(true);
return jump(key.c_str(), key.size());
}
/**
* Step the cursor to the next record.
* @return true on success, or false on failure.
*/
virtual bool step() {
_assert_(true);
DB::Visitor visitor;
if (!accept(&visitor, false, true)) return false;
if (!kbuf_) {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false;
}
return true;
}
/**
* Get the database object.
* @return the database object.
*/
virtual TreeDB* db() {
_assert_(true);
return db_;
}
private:
/**
* Clear the position.
*/
void clear_position() {
_assert_(true);
if (kbuf_ != stack_) delete[] kbuf_;
kbuf_ = NULL;
lid_ = 0;
}
/**
* Set the current position.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param id the ID of the current node.
*/
void set_position(const char* kbuf, size_t ksiz, int64_t id) {
_assert_(kbuf);
kbuf_ = ksiz > sizeof(stack_) ? new char[ksiz] : stack_;
ksiz_ = ksiz;
std::memcpy(kbuf_, kbuf, ksiz);
lid_ = id;
}
/**
* Set the current position with a record.
* @param rec the current record.
* @param id the ID of the current node.
*/
void set_position(Record* rec, int64_t id) {
_assert_(rec);
char* dbuf = (char*)rec + sizeof(*rec);
set_position(dbuf, rec->ksiz, id);
}
/**
* Set the current position at the next node.
* @param id the ID of the next node.
* @return true on success, or false on failure.
*/
bool set_position(int64_t id) {
_assert_(true);
while (id > 0) {
LeafNode* node = db_->load_leaf_node(id, false);
if (!node) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "missing leaf n
ode");
db_->hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)id);
return false;
}
ScopedSpinRWLock lock(&node->lock, false);
RecordArray& recs = node->recs;
if (recs.size() > 0) {
set_position(recs.front(), id);
return true;
} else {
id = node->next;
}
}
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false;
}
/**
* Accept a visitor to the current record speculatively.
* @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only
operation.
* @param step true to move the cursor to the next record, or false for
no move.
* @param hitp the pointer to the variable for the hit flag.
* @return true on success, or false on failure.
*/
bool accept_spec(Visitor* visitor, bool writable, bool step, bool* hitp
) {
_assert_(visitor && hitp);
bool err = false;
bool hit = false;
char rstack[TDBRECBUFSIZ]; char rstack[TDBRECBUFSIZ];
size_t rsiz = sizeof(Record) + ksiz_; size_t rsiz = sizeof(Record) + ksiz_;
char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack;
Record* rec = (Record*)rbuf; Record* rec = (Record*)rbuf;
rec->ksiz = ksiz_; rec->ksiz = ksiz_;
rec->vsiz = 0; rec->vsiz = 0;
std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_); std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_);
LeafNode* node = db_->load_leaf_node(lid_, false); LeafNode* node = db_->load_leaf_node(lid_, false);
bool retry = true;
bool reorg = false;
if (node) { if (node) {
char lstack[TDBRECBUFSIZ];
char* lbuf = NULL;
size_t lsiz = 0;
Link* link = NULL;
int64_t hist[TDBLEVELMAX];
int32_t hnum = 0;
if (writable) { if (writable) {
node->lock.lock_writer(); node->lock.lock_writer();
} else { } else {
node->lock.lock_reader(); node->lock.lock_reader();
} }
if (db_->check_leaf_node_range(node, rec)) { RecordArray& recs = node->recs;
if (recs.size() > 0) {
//std::cout << "ADHOC:" << std::string((char*)rec + sizeof(*rec), Record* frec = recs.front();
rec->ksiz) << std::endl; Record* lrec = recs.back();
if (!db_->reccomp_(rec, frec) && !db_->reccomp_(lrec, rec)) {
retry = false; RecordArray::iterator ritend = recs.end();
reorg = db_->accept_impl(node, rec, visitor); RecordArray::iterator rit = std::lower_bound(recs.begin(), rite
nd,
rec, db_->reccomp_
);
if (rit != ritend) {
hit = true;
if (db_->reccomp_(rec, *rit)) {
clear_position();
set_position(*rit, node->id);
if (rbuf != rstack) delete[] rbuf;
rsiz = sizeof(Record) + ksiz_;
rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack;
rec = (Record*)rbuf;
rec->ksiz = ksiz_;
rec->vsiz = 0;
std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_);
}
rec = *rit;
char* kbuf = (char*)rec + sizeof(*rec);
size_t ksiz = rec->ksiz;
size_t vsiz;
const char* vbuf = visitor->visit_full(kbuf, ksiz, kbuf + ksi
z,
rec->vsiz, &vsiz);
if (vbuf == Visitor::REMOVE) {
rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz;
db_->count_ -= 1;
db_->cusage_ -= rsiz;
node->size -= rsiz;
node->dirty = true;
if (recs.size() <= 1) {
lsiz = sizeof(Link) + ksiz;
lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack;
link = (Link*)lbuf;
link->child = 0;
link->ksiz = ksiz;
std::memcpy(lbuf + sizeof(*link), kbuf, ksiz);
}
xfree(rec);
RecordArray::iterator ritnext = rit + 1;
if (ritnext != ritend) {
clear_position();
set_position(*ritnext, node->id);
step = false;
}
recs.erase(rit);
} else if (vbuf != Visitor::NOP) {
int64_t diff = (int64_t)vsiz - (int64_t)rec->vsiz;
db_->cusage_ += diff;
node->size += diff;
node->dirty = true;
if (vsiz > rec->vsiz) {
*rit = (Record*)xrealloc(rec, sizeof(*rec) + rec->ksiz +
vsiz);
rec = *rit;
kbuf = (char*)rec + sizeof(*rec);
}
std::memcpy(kbuf + rec->ksiz, vbuf, vsiz);
rec->vsiz = vsiz;
if (node->size > db_->psiz_ && recs.size() > 1) {
lsiz = sizeof(Link) + ksiz;
lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack;
link = (Link*)lbuf;
link->child = 0;
link->ksiz = ksiz;
std::memcpy(lbuf + sizeof(*link), kbuf, ksiz);
}
}
if (step) {
rit++;
if (rit != ritend) {
clear_position();
set_position(*rit, node->id);
step = false;
}
}
}
}
} }
bool atran = db_->autotran_ && node->dirty;
bool async = db_->autosync_ && !db_->autotran_ && node->dirty;
node->lock.unlock(); node->lock.unlock();
if (hit && step) {
clear_position();
set_position(node->next);
}
if (hit) {
bool flush = db_->cusage_ > db_->pccap_;
if (link || flush || async) {
int64_t id = node->id;
if (atran && !link && !db_->tran_ && !db_->fix_auto_transaction
_leaf(node))
err = true;
if (!db_->mlock_.promote()) {
db_->mlock_.unlock();
db_->mlock_.lock_writer();
}
if (link) {
node = db_->search_tree(link, true, hist, &hnum);
if (node) {
if (!db_->reorganize_tree(node, hist, hnum)) err = true;
if (atran && !db_->tran_ && !db_->fix_auto_transaction_tree
()) err = true;
} else {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "search f
ailed");
err = true;
}
} else if (flush) {
int32_t idx = id % TDBSLOTNUM;
LeafSlot* lslot = db_->lslots_ + idx;
if (!db_->flush_leaf_cache_part(lslot)) err = true;
InnerSlot* islot = db_->islots_ + idx;
if (islot->warm->count() > lslot->warm->count() + lslot->hot-
>count() + 1 &&
!db_->flush_inner_cache_part(islot)) err = true;
}
if (async && !db_->fix_auto_synchronization()) err = true;
}
}
if (lbuf != lstack) delete[] lbuf;
} }
if (rbuf != rstack) delete[] rbuf;
*hitp = hit;
return !err;
}
/**
* Accept a visitor to the current record atomically.
* @param visitor a visitor object.
* @param step true to move the cursor to the next record, or false for
no move.
* @param retryp the pointer to the variable for the retry flag.
* @return true on success, or false on failure.
*/
bool accept_atom(Visitor* visitor, bool step, bool *retryp) {
_assert_(visitor && retryp);
bool err = false; bool err = false;
bool reorg = false;
*retryp = false;
char lstack[TDBRECBUFSIZ]; char lstack[TDBRECBUFSIZ];
char* lbuf = NULL; size_t lsiz = sizeof(Link) + ksiz_;
size_t lsiz = 0; char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack;
Link* link = NULL; Link* link = (Link*)lbuf;
link->child = 0;
link->ksiz = ksiz_;
std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_);
int64_t hist[TDBLEVELMAX]; int64_t hist[TDBLEVELMAX];
int32_t hnum = 0; int32_t hnum = 0;
if (retry) { LeafNode* node = db_->search_tree(link, true, hist, &hnum);
if (!node) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "search failed");
if (lbuf != lstack) delete[] lbuf;
return false;
}
if (node->recs.size() < 1) {
if (lbuf != lstack) delete[] lbuf;
clear_position();
if (!set_position(node->next)) return false;
node = db_->load_leaf_node(lid_, false);
if (!node) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "search failed"
);
return false;
}
lsiz = sizeof(Link) + ksiz_; lsiz = sizeof(Link) + ksiz_;
lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack;
link = (Link*)lbuf; Link* link = (Link*)lbuf;
link->child = 0; link->child = 0;
link->ksiz = ksiz_; link->ksiz = ksiz_;
std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_); std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_);
node = db_->search_tree(link, false, hist, &hnum); node = db_->search_tree(link, true, hist, &hnum);
if (node) { if (node->id != lid_) {
if (writable) { db_->set_error(__FILE__, __LINE__, Error::BROKEN, "invalid tree")
node->lock.lock_writer(); ;
} else { if (lbuf != lstack) delete[] lbuf;
node->lock.lock_reader(); return false;
}
//std::cout << "RETRY:" << std::string((char*)rec + sizeof(*rec),
rec->ksiz) << std::endl;
reorg = db_->accept_impl(node, rec, visitor);
node->lock.unlock();
} else {
err = true;
} }
} }
if (node) { char rstack[TDBRECBUFSIZ];
if (reorg && !link) { size_t rsiz = sizeof(Record) + ksiz_;
lsiz = sizeof(Link) + ksiz_; char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack;
lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; Record* rec = (Record*)rbuf;
link = (Link*)lbuf; rec->ksiz = ksiz_;
link->child = 0; rec->vsiz = 0;
link->ksiz = ksiz_; std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_);
std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_); RecordArray& recs = node->recs;
node = db_->search_tree(link, false, hist, &hnum); RecordArray::iterator ritend = recs.end();
RecordArray::iterator rit = std::lower_bound(recs.begin(), ritend,
rec, db_->reccomp_);
if (rit != ritend) {
if (db_->reccomp_(rec, *rit)) {
clear_position();
set_position(*rit, node->id);
if (rbuf != rstack) delete[] rbuf;
rsiz = sizeof(Record) + ksiz_;
rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack;
rec = (Record*)rbuf;
rec->ksiz = ksiz_;
rec->vsiz = 0;
std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_);
} }
if (step) { rec = *rit;
if (kbuf_ != stack_) delete[] kbuf_; char* kbuf = (char*)rec + sizeof(*rec);
kbuf_ = NULL; size_t ksiz = rec->ksiz;
lid_ = 0; size_t vsiz;
if (!step_impl(node, rec)) err = true; const char* vbuf = visitor->visit_full(kbuf, ksiz, kbuf + ksiz,
rec->vsiz, &vsiz);
if (vbuf == Visitor::REMOVE) {
rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz;
db_->count_ -= 1;
db_->cusage_ -= rsiz;
node->size -= rsiz;
node->dirty = true;
xfree(rec);
RecordArray::iterator ritnext = rit + 1;
if (ritnext != ritend) {
clear_position();
set_position(*ritnext, node->id);
step = false;
}
recs.erase(rit);
if (recs.size() < 1) reorg = true;
} else if (vbuf != Visitor::NOP) {
int64_t diff = (int64_t)vsiz - (int64_t)rec->vsiz;
db_->cusage_ += diff;
node->size += diff;
node->dirty = true;
if (vsiz > rec->vsiz) {
*rit = (Record*)xrealloc(rec, sizeof(*rec) + rec->ksiz + vsiz);
rec = *rit;
kbuf = (char*)rec + sizeof(*rec);
}
std::memcpy(kbuf + rec->ksiz, vbuf, vsiz);
rec->vsiz = vsiz;
if (node->size > db_->psiz_ && recs.size() > 1) reorg = true;
} }
bool flush = false; if (step) {
if (reorg && db_->mlock_.promote()) { rit++;
if (!db_->reorganize_tree(node, hist, hnum)) err = true; if (rit != ritend) {
reorg = false; clear_position();
} else if (db_->cusage_ > db_->ccap_) { set_position(*rit, node->id);
int32_t idx = node->id % TDBSLOTNUM;
LeafSlot* lslot = db_->lslots_ + idx;
if (!db_->clean_leaf_cache_part(lslot)) err = true;
if (db_->mlock_.promote()) {
if (!db_->flush_leaf_cache_part(lslot)) err = true;
InnerSlot* islot = db_->islots_ + idx;
if (islot->warm->count() > lslot->warm->count() + lslot->hot->c
ount() + 1 &&
!db_->flush_inner_cache_part(islot)) err = true;
} else { } else {
flush = true; clear_position();
set_position(node->next);
} }
} }
db_->mlock_.unlock(); bool atran = db_->autotran_ && node->dirty;
bool async = db_->autosync_ && !db_->autotran_ && node->dirty;
if (atran && !reorg && !db_->tran_ && !db_->fix_auto_transaction_le
af(node)) err = true;
if (reorg) { if (reorg) {
db_->mlock_.lock_writer(); if (!db_->reorganize_tree(node, hist, hnum)) err = true;
node = db_->search_tree(link, false, hist, &hnum); if (atran && !db_->tran_ && !db_->fix_auto_transaction_tree()) er
if (node) { r = true;
if (!db_->reorganize_tree(node, hist, hnum)) err = true; } else if (db_->cusage_ > db_->pccap_) {
} else {
err = true;
}
db_->mlock_.unlock();
} else if (flush) {
int32_t idx = node->id % TDBSLOTNUM; int32_t idx = node->id % TDBSLOTNUM;
LeafSlot* lslot = db_->lslots_ + idx; LeafSlot* lslot = db_->lslots_ + idx;
db_->mlock_.lock_writer();
if (!db_->flush_leaf_cache_part(lslot)) err = true; if (!db_->flush_leaf_cache_part(lslot)) err = true;
InnerSlot* islot = db_->islots_ + idx; InnerSlot* islot = db_->islots_ + idx;
if (islot->warm->count() > lslot->warm->count() + lslot->hot->cou nt() + 1 && if (islot->warm->count() > lslot->warm->count() + lslot->hot->cou nt() + 1 &&
!db_->flush_inner_cache_part(islot)) err = true; !db_->flush_inner_cache_part(islot)) err = true;
db_->mlock_.unlock();
} }
if (async && !db_->fix_auto_synchronization()) err = true;
} else { } else {
db_->mlock_.unlock(); int64_t lid = lid_;
clear_position();
if (set_position(node->next)) {
if (lid_ == lid) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "invalid leaf
node");
err = true;
} else {
*retryp = true;
}
} else {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
err = true;
}
} }
if (rbuf != rstack) delete[] rbuf; if (rbuf != rstack) delete[] rbuf;
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
return !err; return !err;
} }
/** /**
* Jump the cursor to the first record. * Adjust the position to an existing record.
* @return true on success, or false on failure.
*/
virtual bool jump() {
ScopedSpinRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false;
}
if (kbuf_) {
if (kbuf_ != stack_) delete[] kbuf_;
kbuf_ = NULL;
lid_ = 0;
}
int64_t id = db_->first_;
while (id > 0) {
LeafNode* node = db_->load_leaf_node(id, false);
if (!node) return false;
ScopedSpinRWLock lock(&node->lock, false);
RecordArray& recs = node->recs;
if (recs.size() > 0) {
Record* rec = recs.front();
char* dbuf = (char*)rec + sizeof(*rec);
kbuf_ = rec->ksiz > sizeof(stack_) ? new char[rec->ksiz] : stack_
;
ksiz_ = rec->ksiz;
std::memcpy(kbuf_, dbuf, ksiz_);
lid_ = id;
return true;
} else {
id = node->next;
}
}
return true;
}
/**
* Jump the cursor onto a record.
* @param kbuf the pointer to 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.
*/ */
virtual bool jump(const char* kbuf, size_t ksiz) { bool adjust_position() {
ScopedSpinRWLock lock(&db_->mlock_, false); _assert_(true);
if (db_->omode_ == 0) {
db_->set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false;
}
if (kbuf_) {
if (kbuf_ != stack_) delete[] kbuf_;
kbuf_ = NULL;
lid_ = 0;
}
char lstack[TDBRECBUFSIZ]; char lstack[TDBRECBUFSIZ];
size_t lsiz = sizeof(Link) + ksiz; size_t lsiz = sizeof(Link) + ksiz_;
char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack;
Link* link = (Link*)lbuf; Link* link = (Link*)lbuf;
link->child = 0; link->child = 0;
link->ksiz = ksiz; link->ksiz = ksiz_;
std::memcpy(lbuf + sizeof(*link), kbuf, ksiz); std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_);
int64_t hist[TDBLEVELMAX]; int64_t hist[TDBLEVELMAX];
int32_t hnum = 0; int32_t hnum = 0;
LeafNode* node = db_->search_tree(link, true, hist, &hnum); LeafNode* node = db_->search_tree(link, true, hist, &hnum);
if (!node) { if (!node) {
db_->set_error(__FILE__, __LINE__, Error::BROKEN, "search failed");
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
return false; return false;
} }
char rstack[TDBRECBUFSIZ]; char rstack[TDBRECBUFSIZ];
size_t rsiz = sizeof(Record) + ksiz; size_t rsiz = sizeof(Record) + ksiz_;
char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack;
Record* rec = (Record*)rbuf; Record* rec = (Record*)rbuf;
rec->ksiz = ksiz; rec->ksiz = ksiz_;
rec->vsiz = 0; rec->vsiz = 0;
std::memcpy(rbuf + sizeof(*rec), kbuf, ksiz); std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_);
bool err = false; bool err = false;
node->lock.lock_reader(); node->lock.lock_reader();
const RecordArray& recs = node->recs; const RecordArray& recs = node->recs;
RecordArray::const_iterator ritend = node->recs.end(); RecordArray::const_iterator ritend = node->recs.end();
RecordArray::const_iterator rit = std::lower_bound(recs.begin(), rite nd, RecordArray::const_iterator rit = std::lower_bound(recs.begin(), rite nd,
rec, db_->reccomp_ ); rec, db_->reccomp_ );
if (rit == ritend) { if (rit == ritend) {
node->lock.unlock(); node->lock.unlock();
int64_t id = node->next; if (!set_position(node->next)) err = true;
while (id > 0) {
node = db_->load_leaf_node(id, false);
if (!node) {
err = true;
break;
}
ScopedSpinRWLock lock(&node->lock, false);
RecordArray& recs = node->recs;
if (recs.size() > 0) {
rec = recs.front();
char* dbuf = (char*)rec + sizeof(*rec);
kbuf_ = rec->ksiz > sizeof(stack_) ? new char[rec->ksiz] : stac
k_;
ksiz_ = rec->ksiz;
std::memcpy(kbuf_, dbuf, ksiz_);
lid_ = id;
break;
} else {
id = node->next;
}
}
} else { } else {
rec = *rit; set_position(*rit, node->id);
char* dbuf = (char*)rec + sizeof(*rec);
kbuf_ = rec->ksiz > sizeof(stack_) ? new char[rec->ksiz] : stack_;
ksiz_ = rec->ksiz;
std::memcpy(kbuf_, dbuf, ksiz_);
lid_ = node->id;
node->lock.unlock(); node->lock.unlock();
} }
if (rbuf != rstack) delete[] rbuf; if (rbuf != rstack) delete[] rbuf;
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
return !err;
}
/**
* Jump the cursor to a record.
* @note Equal to the original Cursor::jump method except that the para
meter is std::string.
*/
virtual bool jump(const std::string& key) {
return jump(key.c_str(), key.size());
}
/**
* Step the cursor to the next record.
* @return true on success, or false on failure.
*/
virtual bool step() {
DB::Visitor visitor;
if (!accept(&visitor, false, true)) return false;
if (!kbuf_) {
db_->set_error(__FILE__, __LINE__, Error::NOREC, "no record");
return false;
}
return true;
}
private:
/**
* Step the cursor to the next record.
* @param node the leaf node.
* @param rec the record containing the key only.
* @return true on success, or false on failure.
*/
bool step_impl(LeafNode* node, Record* rec) {
node->lock.lock_reader();
const RecordArray& recs = node->recs;
RecordArray::const_iterator ritend = node->recs.end();
RecordArray::const_iterator rit = std::upper_bound(recs.begin(), rite
nd,
rec, db_->reccomp_
);
if (rit == ritend) {
node->lock.unlock();
int64_t id = node->next;
while (id > 0) {
node = db_->load_leaf_node(id, false);
if (!node) return false;
ScopedSpinRWLock lock(&node->lock, false);
RecordArray& recs = node->recs;
if (recs.size() > 0) {
rec = recs.front();
char* dbuf = (char*)rec + sizeof(*rec);
kbuf_ = rec->ksiz > sizeof(stack_) ? new char[rec->ksiz] : stac
k_;
ksiz_ = rec->ksiz;
std::memcpy(kbuf_, dbuf, ksiz_);
lid_ = id;
return true;
} else {
id = node->next;
}
}
} else {
rec = *rit;
char* dbuf = (char*)rec + sizeof(*rec);
kbuf_ = rec->ksiz > sizeof(stack_) ? new char[rec->ksiz] : stack_;
ksiz_ = rec->ksiz;
std::memcpy(kbuf_, dbuf, ksiz_);
lid_ = node->id;
node->lock.unlock();
}
return true; return true;
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
Cursor(const Cursor&); Cursor(const Cursor&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
Cursor& operator =(const Cursor&); Cursor& operator =(const Cursor&);
/** The inner database. */ /** The inner database. */
TreeDB* db_; TreeDB* db_;
/** The stack buffer for the key. */ /** The stack buffer for the key. */
char stack_[TDBRECBUFSIZ]; char stack_[TDBRECBUFSIZ];
skipping to change at line 455 skipping to change at line 666
enum Flag { enum Flag {
FOPEN = HashDB::FOPEN, ///< whether opened FOPEN = HashDB::FOPEN, ///< whether opened
FFATAL = HashDB::FFATAL ///< whether with fatal error FFATAL = HashDB::FFATAL ///< whether with fatal error
}; };
/** /**
* Default constructor. * Default constructor.
*/ */
explicit TreeDB() : explicit TreeDB() :
mlock_(), omode_(0), writer_(false), autotran_(false), autosync_(false) , mlock_(), omode_(0), writer_(false), autotran_(false), autosync_(false) ,
hdb_(), curs_(), apow_(TDBDEFAPOW), fpow_(TDBDEFFPOW), opts_(0), bnum_( TDBDEFBNUM), hdb_(), curs_(), apow_(TDBDEFAPOW), fpow_(TDBDEFFPOW), opts_(0), bnum_( TDBDEFBNUM),
psiz_(TDBDEFPSIZ), ccap_(TDBDEFCCAP), psiz_(TDBDEFPSIZ), pccap_(TDBDEFPCCAP),
root_(0), first_(0), last_(0), lcnt_(0), icnt_(0), count_(0), cusage_(0 ), root_(0), first_(0), last_(0), lcnt_(0), icnt_(0), count_(0), cusage_(0 ),
lslots_(), islots_(), reccomp_(), linkcomp_(), tran_(false), trcnt_(0) lslots_(), islots_(), reccomp_(), linkcomp_(), tran_(false), trcnt_(0)
{} {
_assert_(true);
}
/** /**
* Destructor. * Destructor.
* @note If the database is not closed, it is closed implicitly. * @note If the database is not closed, it is closed implicitly.
*/ */
virtual ~TreeDB() { virtual ~TreeDB() {
_assert_(true);
if (omode_ != 0) close(); if (omode_ != 0) close();
if (curs_.size() > 0) {
CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end();
while (cit != citend) {
Cursor* cur = *cit;
cur->db_ = NULL;
cit++;
}
}
} }
/** /**
* Accept a visitor to a record. * Accept a visitor to a record.
* @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.
* @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
* same record are blocked.
*/ */
virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool
writable) { writable = true) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && visitor);
mlock_.lock_reader(); mlock_.lock_reader();
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
char lstack[TDBRECBUFSIZ]; char lstack[TDBRECBUFSIZ];
size_t lsiz = sizeof(Link) + ksiz; size_t lsiz = sizeof(Link) + ksiz;
char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack;
Link* link = (Link*)lbuf; Link* link = (Link*)lbuf;
link->child = 0; link->child = 0;
link->ksiz = ksiz; link->ksiz = ksiz;
std::memcpy(lbuf + sizeof(*link), kbuf, ksiz); std::memcpy(lbuf + sizeof(*link), kbuf, ksiz);
int64_t hist[TDBLEVELMAX]; int64_t hist[TDBLEVELMAX];
int32_t hnum = 0; int32_t hnum = 0;
LeafNode* node = search_tree(link, true, hist, &hnum); LeafNode* node = search_tree(link, true, hist, &hnum);
if (!node) { if (!node) {
set_error(__FILE__, __LINE__, Error::BROKEN, "search failed");
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
char rstack[TDBRECBUFSIZ]; char rstack[TDBRECBUFSIZ];
size_t rsiz = sizeof(Record) + ksiz; size_t rsiz = sizeof(Record) + ksiz;
char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack;
Record* rec = (Record*)rbuf; Record* rec = (Record*)rbuf;
rec->ksiz = ksiz; rec->ksiz = ksiz;
rec->vsiz = 0; rec->vsiz = 0;
std::memcpy(rbuf + sizeof(*rec), kbuf, ksiz); std::memcpy(rbuf + sizeof(*rec), kbuf, ksiz);
if (writable) { if (writable) {
node->lock.lock_writer(); node->lock.lock_writer();
} else { } else {
node->lock.lock_reader(); node->lock.lock_reader();
} }
bool reorg = accept_impl(node, rec, visitor); bool reorg = accept_impl(node, rec, visitor);
bool atran = autotran_ && node->dirty;
bool async = autosync_ && !autotran_ && node->dirty;
node->lock.unlock(); node->lock.unlock();
bool flush = false; bool flush = false;
bool err = false; bool err = false;
if (atran && !reorg && !tran_ && !fix_auto_transaction_leaf(node)) err = true;
if (reorg && mlock_.promote()) { if (reorg && mlock_.promote()) {
if (!reorganize_tree(node, hist, hnum)) err = true; if (!reorganize_tree(node, hist, hnum)) err = true;
if (atran && !tran_ && !fix_auto_transaction_tree()) err = true;
reorg = false; reorg = false;
} else if (cusage_ > ccap_) { } else if (cusage_ > pccap_) {
int32_t idx = node->id % TDBSLOTNUM; int32_t idx = node->id % TDBSLOTNUM;
LeafSlot* lslot = lslots_ + idx; LeafSlot* lslot = lslots_ + idx;
if (!clean_leaf_cache_part(lslot)) err = true; if (!clean_leaf_cache_part(lslot)) err = true;
if (mlock_.promote()) { if (mlock_.promote()) {
if (!flush_leaf_cache_part(lslot)) err = true; if (!flush_leaf_cache_part(lslot)) err = true;
InnerSlot* islot = islots_ + idx; InnerSlot* islot = islots_ + idx;
if (islot->warm->count() > lslot->warm->count() + lslot->hot->count () + 1 && if (islot->warm->count() > lslot->warm->count() + lslot->hot->count () + 1 &&
!flush_inner_cache_part(islot)) err = true; !flush_inner_cache_part(islot)) err = true;
} else { } else {
flush = true; flush = true;
} }
} }
mlock_.unlock(); mlock_.unlock();
if (reorg) { if (reorg) {
mlock_.lock_writer(); mlock_.lock_writer();
node = search_tree(link, false, hist, &hnum); node = search_tree(link, false, hist, &hnum);
if (node) { if (node) {
if (!reorganize_tree(node, hist, hnum)) err = true; if (!reorganize_tree(node, hist, hnum)) err = true;
if (atran && !tran_ && !fix_auto_transaction_tree()) err = true;
} else { } else {
set_error(__FILE__, __LINE__, Error::BROKEN, "search failed");
err = true; err = true;
} }
mlock_.unlock(); mlock_.unlock();
} else if (flush) { } else if (flush) {
int32_t idx = node->id % TDBSLOTNUM; int32_t idx = node->id % TDBSLOTNUM;
LeafSlot* lslot = lslots_ + idx; LeafSlot* lslot = lslots_ + idx;
mlock_.lock_writer(); mlock_.lock_writer();
if (!flush_leaf_cache_part(lslot)) err = true; if (!flush_leaf_cache_part(lslot)) err = true;
InnerSlot* islot = islots_ + idx; InnerSlot* islot = islots_ + idx;
if (islot->warm->count() > lslot->warm->count() + lslot->hot->count() + 1 && if (islot->warm->count() > lslot->warm->count() + lslot->hot->count() + 1 &&
!flush_inner_cache_part(islot)) err = true; !flush_inner_cache_part(islot)) err = true;
mlock_.unlock(); mlock_.unlock();
} }
if (rbuf != rstack) delete[] rbuf; if (rbuf != rstack) delete[] rbuf;
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
if (async) {
mlock_.lock_writer();
if (!fix_auto_synchronization()) err = true;
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.
* @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.
*/ */
virtual bool iterate(Visitor *visitor, bool writable) { virtual bool iterate(Visitor *visitor, bool writable = true) {
_assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
return false; return false;
} }
bool err = false; bool err = false;
bool atran = false;
if (autotran_ && writable && !tran_) {
if (begin_transaction_impl(autosync_)) {
atran = true;
} else {
err = true;
}
}
int64_t id = first_; int64_t id = first_;
int64_t flcnt = 0; int64_t flcnt = 0;
while (id > 0) { while (id > 0) {
LeafNode* node = load_leaf_node(id, false); LeafNode* node = load_leaf_node(id, false);
if (!node) return false; if (!node) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing leaf node");
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)id);
return false;
}
id = node->next; id = node->next;
const RecordArray& recs = node->recs; const RecordArray& recs = node->recs;
RecordArray keys; RecordArray keys;
keys.reserve(recs.size()); keys.reserve(recs.size());
RecordArray::const_iterator rit = recs.begin(); RecordArray::const_iterator rit = recs.begin();
RecordArray::const_iterator ritend = recs.end(); RecordArray::const_iterator ritend = recs.end();
while (rit != ritend) { while (rit != ritend) {
Record* rec = *rit; Record* rec = *rit;
size_t rsiz = sizeof(*rec) + rec->ksiz; size_t rsiz = sizeof(*rec) + rec->ksiz;
char* dbuf = (char*)rec + sizeof(*rec); char* dbuf = (char*)rec + sizeof(*rec);
skipping to change at line 620 skipping to change at line 872
Link* link = (Link*)lbuf; Link* link = (Link*)lbuf;
link->child = 0; link->child = 0;
link->ksiz = rec->ksiz; link->ksiz = rec->ksiz;
std::memcpy(lbuf + sizeof(*link), dbuf, rec->ksiz); std::memcpy(lbuf + sizeof(*link), dbuf, rec->ksiz);
int64_t hist[TDBLEVELMAX]; int64_t hist[TDBLEVELMAX];
int32_t hnum = 0; int32_t hnum = 0;
node = search_tree(link, false, hist, &hnum); node = search_tree(link, false, hist, &hnum);
if (node) { if (node) {
if (!reorganize_tree(node, hist, hnum)) err = true; if (!reorganize_tree(node, hist, hnum)) err = true;
} else { } else {
set_error(__FILE__, __LINE__, Error::BROKEN, "search failed");
err = true; err = true;
} }
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
} }
if (cusage_ > ccap_) { if (cusage_ > pccap_) {
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
LeafSlot* lslot = lslots_ + i; LeafSlot* lslot = lslots_ + i;
if (!flush_leaf_cache_part(lslot)) err = true; if (!flush_leaf_cache_part(lslot)) err = true;
} }
InnerSlot* islot = islots_ + (flcnt++) % TDBSLOTNUM; InnerSlot* islot = islots_ + (flcnt++) % TDBSLOTNUM;
if (islot->warm->count() > 2 && !flush_inner_cache_part(islot)) err = true; if (islot->warm->count() > 2 && !flush_inner_cache_part(islot)) err = true;
} }
kit = keys.begin(); kit = keys.begin();
while (kit != kitend) { while (kit != kitend) {
xfree(*kit); xfree(*kit);
kit++; kit++;
} }
} }
if (atran && !commit_transaction()) err = true;
if (autosync_ && !autotran_ && writable && !fix_auto_synchronization())
err = true;
return !err; return !err;
} }
/** /**
* Get the last happened error. * Get the last happened error.
* @return the last happened error. * @return the last happened error.
*/ */
virtual Error error() const { virtual Error error() const {
_assert_(true);
return hdb_.error(); return hdb_.error();
} }
/** /**
* Set the error information. * Set the error information.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
virtual void set_error(Error::Code code, const char* message) { virtual void set_error(Error::Code code, const char* message) {
_assert_(message);
hdb_.set_error(code, message); hdb_.set_error(code, message);
} }
/** /**
* Open a database file. * Open a database file.
* @param path the path of a database file. * @param path the path of a database file.
* @param mode the connection mode. HashDB::OWRITER as a writer, HashDB: * @param mode the connection mode. TreeDB::OWRITER as a writer, TreeDB:
:OREADER as a :OREADER as a
* reader. The following may be added to the writer mode by bitwise-or: * reader. The following may be added to the writer mode by bitwise-or:
HashDB::OCREATE, TreeDB::OCREATE,
* which means it creates a new database if the file does not exist, Hash * which means it creates a new database if the file does not exist, Tree
DB::OTRUNCATE, which DB::OTRUNCATE, which
* means it creates a new database regardless if the file exists, HashDB: * means it creates a new database regardless if the file exists, TreeDB:
:OAUTOTRAN, which :OAUTOTRAN, which
* means each updating operation is performed in implicit transaction, Ha * means each updating operation is performed in implicit transaction, Tr
shDB::OAUTOSYNC, eeDB::OAUTOSYNC,
* which means each updating operation is followed by implicit synchroniz ation with the file * which means each updating operation is followed by implicit synchroniz ation with the file
* system. The following may be added to both of the reader mode and the writer mode by * system. The following may be added to both of the reader mode and the writer mode by
* bitwise-or: HashDB::ONOLOCK, which means it opens the database file wi * bitwise-or: TreeDB::ONOLOCK, which means it opens the database file wi
thout file locking, thout file locking,
* HashDB::TRYLOCK, which means locking is performed without blocking. * TreeDB::OTRYLOCK, which means locking is performed without blocking, T
reeDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file
destruction is
* 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 TreeDB::close method
when it is no
* longer in use.
*/ */
virtual bool open(const std::string& path, uint32_t mode) { virtual bool open(const std::string& path, uint32_t mode = OWRITER | OCRE
ATE) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
writer_ = false; writer_ = false;
autotran_ = false; autotran_ = false;
autosync_ = false; autosync_ = false;
if (mode & OWRITER) { if (mode & OWRITER) {
writer_ = true; writer_ = true;
if (mode & OAUTOTRAN) autotran_ = true; if (mode & OAUTOTRAN) autotran_ = true;
if (mode & OAUTOSYNC) autosync_ = true; if (mode & OAUTOSYNC) autosync_ = true;
} }
if (!hdb_.tune_type(TYPETREE)) return false; if (!hdb_.tune_type(TYPETREE)) return false;
if (!hdb_.tune_alignment(apow_)) return false; if (!hdb_.tune_alignment(apow_)) return false;
if (!hdb_.tune_fbp(fpow_)) return false; if (!hdb_.tune_fbp(fpow_)) return false;
if (!hdb_.tune_options(opts_)) return false; if (!hdb_.tune_options(opts_)) return false;
if (!hdb_.tune_buckets(bnum_)) return false; if (!hdb_.tune_buckets(bnum_)) return false;
if (!hdb_.open(path, mode)) return false; if (!hdb_.open(path, mode)) return false;
if (hdb_.type() != TYPETREE) { if (hdb_.type() != TYPETREE) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid database type") ; set_error(__FILE__, __LINE__, Error::INVALID, "invalid database type" );
hdb_.close(); hdb_.close();
return false; return false;
} }
if (hdb_.recovered()) {
if (!writer_) {
if (!hdb_.close()) return false;
mode &= ~OREADER;
mode |= OWRITER;
if (!hdb_.open(path, mode)) return false;
}
if (!recalc_count()) return false;
} else if (hdb_.reorganized()) {
if (!writer_) {
if (!hdb_.close()) return false;
mode &= ~OREADER;
mode |= OWRITER;
if (!hdb_.open(path, mode)) return false;
}
if (!reorganize_file()) return false;
}
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();
if (writer_ && hdb_.count() < 1) { if (writer_ && hdb_.count() < 1) {
lcnt_ = 0; lcnt_ = 0;
create_leaf_node(0, 0); create_leaf_node(0, 0);
root_ = 1; root_ = 1;
skipping to change at line 732 skipping to change at line 1011
} }
if (!load_meta()) { if (!load_meta()) {
delete_inner_cache(); delete_inner_cache();
delete_leaf_cache(); delete_leaf_cache();
hdb_.close(); hdb_.close();
return false; return false;
} }
if (psiz_ < 1 || root_ < 1 || first_ < 1 || last_ < 1 || if (psiz_ < 1 || root_ < 1 || first_ < 1 || last_ < 1 ||
lcnt_ < 1 || icnt_ < 0 || count_ < 0) { lcnt_ < 1 || icnt_ < 0 || count_ < 0) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid meta data"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid meta data");
hdb_.report(__FILE__, __LINE__, "info", "psiz=%ld root=%ld first=%ld
last=%ld"
" lcnt=%ld icnt=%ld count=%ld", (long)psiz_, (long)root_,
(long)first_, (long)last_, (long)lcnt_, (long)icnt_, (lon
g)count_);
delete_inner_cache(); delete_inner_cache();
delete_leaf_cache(); delete_leaf_cache();
hdb_.close(); hdb_.close();
return false; return false;
} }
omode_ = mode; omode_ = mode;
cusage_ = 0; cusage_ = 0;
tran_ = false; tran_ = false;
trcnt_ = 0; trcnt_ = 0;
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.
*/ */
virtual bool close() { virtual bool close() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
bool err = false; bool err = false;
disable_cursors(); disable_cursors();
// hoge
int64_t lsiz = calc_leaf_cache_size(); int64_t lsiz = calc_leaf_cache_size();
int64_t isiz = calc_inner_cache_size(); int64_t isiz = calc_inner_cache_size();
/*
printf("[closing: %lld = %lld + %lld]\n",
(long long)cusage_, (long long)lsiz, (long long)isiz);
*/
if (cusage_ != lsiz + isiz) { if (cusage_ != lsiz + isiz) {
printf("YABASU difference!!\n"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid cache usage");
hdb_.report(__FILE__, __LINE__, "info", "cusage=%ld lsiz=%ld isiz=%ld
",
(long)cusage_, (long)lsiz, (long)isiz);
err = true; err = true;
} }
if (!flush_leaf_cache(true)) err = true; if (!flush_leaf_cache(true)) err = true;
if (!flush_inner_cache(true)) err = true; if (!flush_inner_cache(true)) err = true;
lsiz = calc_leaf_cache_size();
// hoge isiz = calc_inner_cache_size();
if (cusage_ != 0 || calc_leaf_cache_count() != 0 || calc_leaf_cache_siz int64_t lcnt = calc_leaf_cache_count();
e() != 0 || int64_t icnt = calc_inner_cache_count();
calc_inner_cache_count() != 0 || calc_inner_cache_size() != 0) { if (cusage_ != 0 || lsiz != 0 || isiz != 0 || lcnt != 0 || icnt != 0) {
printf("YABASU not ZERO!!\n"); set_error(__FILE__, __LINE__, Error::BROKEN, "remaining cache");
hdb_.report(__FILE__, __LINE__, "info", "cusage=%ld lsiz=%ld isiz=%ld
lcnt=%ld icnt=%ld",
(long)cusage_, (long)lsiz, (long)isiz, (long)lcnt, (long)
icnt);
err = true; err = true;
} }
delete_inner_cache(); delete_inner_cache();
delete_leaf_cache(); delete_leaf_cache();
if (writer_ && !dump_meta()) err = true; if (writer_ && !dump_meta()) err = true;
if (!hdb_.close()) err = true; if (!hdb_.close()) err = true;
omode_ = 0; omode_ = 0;
return !err; return !err;
} }
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
skipping to change at line 792 skipping to change at line 1072
omode_ = 0; omode_ = 0;
return !err; return !err;
} }
/** /**
* Synchronize updated contents with the file and the device. * Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @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.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool synchronize(bool hard, FileProcessor* proc) { virtual bool synchronize(bool hard = false, FileProcessor* proc = NULL) {
_assert_(true);
mlock_.lock_reader(); mlock_.lock_reader();
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
bool err = false; bool err = false;
if (!clean_leaf_cache()) err = true; if (!clean_leaf_cache()) err = true;
if (!clean_inner_cache()) err = true; if (!clean_inner_cache()) err = true;
if (!clean_leaf_cache()) err = true; if (!clean_leaf_cache()) err = true;
if (!mlock_.promote()) { if (!mlock_.promote()) {
mlock_.unlock(); mlock_.unlock();
mlock_.lock_writer(); mlock_.lock_writer();
} }
if (!flush_leaf_cache(true)) err = true; if (!flush_leaf_cache(true)) err = true;
if (!flush_inner_cache(true)) err = true; if (!flush_inner_cache(true)) err = true;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
// yabasu dame
if (!hdb_.synchronize(hard, proc)) err = true; if (!hdb_.synchronize(hard, proc)) err = true;
mlock_.unlock(); mlock_.unlock();
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool begin_transaction(bool hard) { virtual bool begin_transaction(bool hard = false) {
_assert_(true);
for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) { for (double wsec = 1.0 / CLOCKTICK; true; wsec *= 2) {
mlock_.lock_writer(); mlock_.lock_writer();
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!tran_) break; if (!tran_) break;
mlock_.unlock(); mlock_.unlock();
if (wsec > 1.0) wsec = 1.0; if (wsec > 1.0) wsec = 1.0;
Thread::sleep(wsec); Thread::sleep(wsec);
} }
if (!begin_transaction_impl(hard)) { if (!begin_transaction_impl(hard)) {
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
tran_ = true; tran_ = true;
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* Commit transaction. * Try to begin transaction.
* @param hard true for physical synchronization with the device, or fals
e for logical
* synchronization with the file system.
* @return true on success, or false on failure.
*/
virtual bool begin_transaction_try(bool hard = false) {
_assert_(true);
mlock_.lock_writer();
if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
mlock_.unlock();
return false;
}
if (!writer_) {
set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
mlock_.unlock();
return false;
}
if (tran_) {
set_error(__FILE__, __LINE__, Error::LOGIC, "competition avoided");
mlock_.unlock();
return false;
}
if (!begin_transaction_impl(hard)) {
mlock_.unlock();
return false;
}
tran_ = true;
mlock_.unlock();
return true;
}
/**
* 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.
*/ */
virtual bool end_transaction(bool commit) { virtual bool end_transaction(bool commit = true) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(__FILE__, __LINE__, Error::INVALID, "not in transaction"); set_error(__FILE__, __LINE__, Error::INVALID, "not in transaction");
return false; return false;
} }
bool err = false; bool err = false;
skipping to change at line 880 skipping to change at line 1198
if (!abort_transaction()) err = true; if (!abort_transaction()) err = true;
} }
tran_ = false; tran_ = false;
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.
*/ */
virtual bool clear() { virtual bool clear() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(__FILE__, __LINE__, Error::INVALID, "permission denied"); set_error(__FILE__, __LINE__, Error::NOPERM, "permission denied");
return false; return false;
} }
disable_cursors(); disable_cursors();
flush_leaf_cache(false); flush_leaf_cache(false);
flush_inner_cache(false); flush_inner_cache(false);
bool err = false; bool err = false;
if (!hdb_.clear()) err = true; if (!hdb_.clear()) err = true;
lcnt_ = 0; lcnt_ = 0;
create_leaf_node(0, 0); create_leaf_node(0, 0);
root_ = 1; root_ = 1;
skipping to change at line 912 skipping to change at line 1231
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!flush_leaf_cache(true)) err = true; if (!flush_leaf_cache(true)) err = true;
cusage_ = 0; cusage_ = 0;
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.
*/ */
virtual int64_t count() { virtual int64_t count() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual int64_t size() { virtual int64_t size() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return -1; return -1;
} }
return hdb_.size(); return hdb_.size();
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file in bytes, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
virtual std::string path() { virtual std::string path() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return ""; return "";
} }
return hdb_.path(); return hdb_.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.
*/ */
virtual bool status(std::map<std::string, std::string>* strmap) { virtual bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return false; return false;
} }
if (!hdb_.status(strmap)) return false; if (!hdb_.status(strmap)) return false;
(*strmap)["type"] = "TreeDB"; (*strmap)["type"] = "TreeDB";
(*strmap)["psiz"] = strprintf("%d", psiz_); (*strmap)["psiz"] = strprintf("%d", psiz_);
(*strmap)["ccap"] = strprintf("%lld", (long long)ccap_); (*strmap)["pccap"] = strprintf("%lld", (long long)pccap_);
const char* compname = "external";
if (reccomp_.comp == &LEXICALCOMP) {
compname = "lexical";
} else if (reccomp_.comp == &DECIMALCOMP) {
compname = "decimal";
}
(*strmap)["rcomp"] = compname;
(*strmap)["root"] = strprintf("%lld", (long long)root_); (*strmap)["root"] = strprintf("%lld", (long long)root_);
(*strmap)["first"] = strprintf("%lld", (long long)first_); (*strmap)["first"] = strprintf("%lld", (long long)first_);
(*strmap)["last"] = strprintf("%lld", (long long)last_); (*strmap)["last"] = strprintf("%lld", (long long)last_);
(*strmap)["lcnt"] = strprintf("%lld", (long long)lcnt_); (*strmap)["lcnt"] = strprintf("%lld", (long long)lcnt_);
(*strmap)["icnt"] = strprintf("%lld", (long long)icnt_); (*strmap)["icnt"] = strprintf("%lld", (long long)icnt_);
(*strmap)["count"] = strprintf("%lld", (long long)count_); (*strmap)["count"] = strprintf("%lld", (long long)count_);
(*strmap)["cusage"] = strprintf("%lld", (long long)cusage_); (*strmap)["cusage"] = strprintf("%lld", (long long)cusage_);
if (strmap->count("cusage_lcnt") > 0) if (strmap->count("cusage_lcnt") > 0)
(*strmap)["cusage_lcnt"] = strprintf("%lld", (long long)calc_leaf_cac he_count()); (*strmap)["cusage_lcnt"] = strprintf("%lld", (long long)calc_leaf_cac he_count());
if (strmap->count("cusage_lsiz") > 0) if (strmap->count("cusage_lsiz") > 0)
skipping to change at line 985 skipping to change at line 1315
link.ksiz = 0; link.ksiz = 0;
int64_t hist[TDBLEVELMAX]; int64_t hist[TDBLEVELMAX];
int32_t hnum = 0; int32_t hnum = 0;
search_tree(&link, false, hist, &hnum); search_tree(&link, false, hist, &hnum);
(*strmap)["tree_level"] = strprintf("%d", hnum + 1); (*strmap)["tree_level"] = strprintf("%d", hnum + 1);
} }
return true; return true;
} }
/** /**
* Create a cursor object. * Create a cursor object.
* @return the return value is the cursor object. * @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the const ructor, it should be * @note Because the object of the return value is allocated by the const ructor, it should be
* released with the delete operator when it is no longer in use. * released with the delete operator when it is no longer in use.
*/ */
virtual Cursor* cursor() { virtual Cursor* cursor() {
_assert_(true);
return new Cursor(this); return new Cursor(this);
} }
/** /**
* Set the internal error reporter. * Set the internal error reporter.
* @param erstrm a stream object into which internal error messages are s tored. * @param erstrm a stream object into which internal error messages are s tored.
* @param ervbs true to report all errors, or false to report fatal error s only. * @param ervbs true to report all errors, or false to report fatal error s only.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_error_reporter(std::ostream* erstrm, bool ervbs) { virtual bool tune_error_reporter(std::ostream* erstrm, bool ervbs) {
_assert_(erstrm);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
return hdb_.tune_error_reporter(erstrm, ervbs); return hdb_.tune_error_reporter(erstrm, ervbs);
} }
/** /**
* 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.
*/ */
virtual bool tune_alignment(int8_t apow) { virtual bool tune_alignment(int8_t apow) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
apow_ = apow >= 0 ? apow : TDBDEFAPOW; apow_ = apow >= 0 ? apow : TDBDEFAPOW;
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.
*/ */
virtual bool tune_fbp(int8_t fpow) { virtual bool tune_fbp(int8_t fpow) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
fpow_ = fpow >= 0 ? fpow : TDBDEFFPOW; fpow_ = fpow >= 0 ? fpow : TDBDEFFPOW;
return true; return true;
} }
/** /**
* Set the optional features. * Set the optional features.
* @param opts the optional features. * @param opts the optional features by bitwise-or: TreeDB::TSMALL to use
32-bit addressing,
* TreeDB::TLINEAR to use linear collision chaining, TreeDB::TCOMPRESS to
compress each record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_options(int8_t opts) { virtual bool tune_options(int8_t opts) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, 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.
*/ */
virtual bool tune_buckets(int64_t bnum) { virtual bool tune_buckets(int64_t bnum) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum > 0 ? bnum : TDBDEFBNUM; bnum_ = bnum > 0 ? bnum : TDBDEFBNUM;
return true; return true;
} }
/** /**
* Set the size of each page. * Set the size of each page.
* @param psiz the size of each page. * @param psiz the size of each page.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_page(int32_t psiz) { virtual bool tune_page(int32_t psiz) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
psiz_ = psiz > 0 ? psiz : TDBDEFPSIZ; psiz_ = psiz > 0 ? psiz : TDBDEFPSIZ;
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.
*/ */
virtual bool tune_map(int64_t msiz) { virtual bool tune_map(int64_t msiz) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
return hdb_.tune_map(msiz); return hdb_.tune_map(msiz);
} }
/** /**
* 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.
*/ */
virtual bool tune_defrag(int64_t dfunit) { virtual bool tune_defrag(int64_t dfunit) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
return hdb_.tune_defrag(dfunit); return hdb_.tune_defrag(dfunit);
} }
/** /**
* Set the capacity of the total size of the page cache. * Set the capacity size of the page cache.
* @param ccap the capacity of the total size of the page cache. * @param pccap the capacity size of the page cache.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_cache(int64_t ccap) { virtual bool tune_page_cache(int64_t pccap) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
ccap_ = ccap > 0 ? ccap : TDBDEFCCAP; pccap_ = pccap > 0 ? pccap : TDBDEFPCCAP;
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.
*/ */
virtual bool tune_compressor(Compressor* comp) { virtual bool tune_compressor(Compressor* comp) {
_assert_(comp);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
return hdb_.tune_compressor(comp); return hdb_.tune_compressor(comp);
} }
/** /**
* Set the record comparator. * Set the record comparator.
* @param comp the record comparator object. * @param rcomp the record comparator object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
virtual bool tune_comparator(Comparator* rcomp) { virtual bool tune_comparator(Comparator* rcomp) {
_assert_(rcomp);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "already opened"); set_error(__FILE__, __LINE__, Error::INVALID, "already opened");
return false; return false;
} }
reccomp_.comp = rcomp; reccomp_.comp = rcomp;
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.
*/ */
virtual char* opaque() { virtual char* opaque() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return hdb_.opaque(); return hdb_.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.
*/ */
virtual bool synchronize_opaque() { virtual bool synchronize_opaque() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedSpinRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return NULL; return false;
} }
return hdb_.synchronize_opaque(); return hdb_.synchronize_opaque();
} }
/** /**
* 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.
*/ */
virtual bool defrag(int64_t step) { virtual bool defrag(int64_t step) {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return NULL; return false;
} }
return hdb_.defrag(step); return hdb_.defrag(step);
} }
/** /**
* Get the status flags. * Get the status flags.
* @return the status flags, or 0 on failure. * @return the status flags, or 0 on failure.
*/ */
virtual uint8_t flags() { virtual uint8_t flags() {
_assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedSpinRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(__FILE__, __LINE__, Error::INVALID, "not opened"); set_error(__FILE__, __LINE__, Error::INVALID, "not opened");
return 0; return 0;
} }
return hdb_.flags(); return hdb_.flags();
} }
protected: protected:
/** /**
* Set the error information. * Set the error information.
* @param file the file name of the epicenter. * @param file the file name of the epicenter.
* @param line the line number of the epicenter. * @param line the line number of the epicenter.
* @param code an error code. * @param code an error code.
* @param message a supplement message. * @param message a supplement message.
*/ */
virtual void set_error(const char* file, int32_t line, virtual void set_error(const char* file, int32_t line,
Error::Code code, const char* message) { Error::Code code, const char* message) {
_assert_(file && line > 0 && message);
hdb_.set_error(file, line, code, message); hdb_.set_error(file, line, code, message);
} }
private: private:
/** /**
* Record data. * Record data.
*/ */
struct Record { struct Record {
uint32_t ksiz; ///< size of the key uint32_t ksiz; ///< size of the key
uint32_t vsiz; ///< size of the value uint32_t vsiz; ///< size of the value
}; };
/** /**
* Comparator for records. * Comparator for records.
*/ */
struct RecordComparator { struct RecordComparator {
Comparator* comp; ///< comparator Comparator* comp; ///< comparator
/** constructor */ /** constructor */
RecordComparator() : comp(NULL) {} explicit RecordComparator() : comp(NULL) {}
/** comparing operator */ /** comparing operator */
bool operator()(const Record* const& a, const Record* const& b) const { bool operator ()(const Record* const& a, const Record* const& b) const
{
_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 SpinRWLock lock; ///< lock
skipping to change at line 1253 skipping to change at line 1602
struct Link { struct Link {
int64_t child; ///< child node int64_t child; ///< child node
int32_t ksiz; ///< size of the key int32_t ksiz; ///< size of the key
}; };
/** /**
* Comparator for links. * Comparator for links.
*/ */
struct LinkComparator { struct LinkComparator {
Comparator* comp; ///< comparator Comparator* comp; ///< comparator
/** constructor */ /** constructor */
LinkComparator() : comp(NULL) {} explicit LinkComparator() : comp(NULL) {
_assert_(true);
}
/** comparing operator */ /** comparing operator */
bool operator()(const Link* const& a, const Link* const& b) const { bool operator ()(const Link* const& a, const Link* const& b) const {
_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 SpinRWLock 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 inner nodes. * Slot cache of leaf nodes.
*/ */
struct LeafSlot { struct LeafSlot {
SpinLock lock; ///< lock SpinLock 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 SpinLock lock; ///< lock
InnerCache* warm; ///< warm cache InnerCache* warm; ///< warm cache
}; };
/** /**
* Open the leaf cache. * Open the leaf cache.
*/ */
void create_leaf_cache() { void create_leaf_cache() {
_assert_(true);
int64_t bnum = bnum_ / TDBSLOTNUM + 1; int64_t bnum = bnum_ / TDBSLOTNUM + 1;
if (bnum < INT8_MAX) bnum = INT8_MAX; if (bnum < INT8_MAX) bnum = INT8_MAX;
bnum = nearbyprime(bnum); bnum = nearbyprime(bnum);
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
lslots_[i].hot = new LeafCache(bnum); lslots_[i].hot = new LeafCache(bnum);
lslots_[i].warm = new LeafCache(bnum); lslots_[i].warm = new LeafCache(bnum);
} }
} }
/** /**
* Close the leaf cache. * Close the leaf cache.
*/ */
void delete_leaf_cache() { void delete_leaf_cache() {
_assert_(true);
for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) { for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) {
LeafSlot* slot = lslots_ + i; LeafSlot* slot = lslots_ + i;
delete slot->warm; delete slot->warm;
delete slot->hot; delete slot->hot;
} }
} }
/** /**
* Remove all leaf nodes from the leaf cache. * Remove all leaf nodes from the leaf cache.
* @param save whether to save dirty nodes. * @param save whether to save dirty nodes.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool flush_leaf_cache(bool save) { bool flush_leaf_cache(bool save) {
_assert_(true);
bool err = false; bool err = false;
for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) { for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) {
LeafSlot* slot = lslots_ + i; LeafSlot* slot = lslots_ + i;
LeafCache::Iterator it = slot->warm->begin(); LeafCache::Iterator it = slot->warm->begin();
LeafCache::Iterator itend = slot->warm->end(); LeafCache::Iterator itend = slot->warm->end();
while (it != itend) { while (it != itend) {
LeafNode* node = it.value(); LeafNode* node = it.value();
it++; it++;
if (!flush_leaf_node(node, save)) err = true; if (!flush_leaf_node(node, save)) err = true;
} }
skipping to change at line 1342 skipping to change at line 1697
} }
} }
return !err; return !err;
} }
/** /**
* Flush a part of the leaf cache. * Flush a part of the leaf cache.
* @param slot a slot of leaf nodes. * @param slot a slot of leaf nodes.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool flush_leaf_cache_part(LeafSlot* slot) { bool flush_leaf_cache_part(LeafSlot* slot) {
_assert_(slot);
//printf("[%d]", (int)cusage_);
bool err = false; bool err = false;
if (slot->warm->count() > 0) { if (slot->warm->count() > 0) {
LeafNode* node = slot->warm->first_value(); LeafNode* node = slot->warm->first_value();
if (!flush_leaf_node(node, true)) err = true; if (!flush_leaf_node(node, true)) err = true;
} else if (slot->hot->count() > 0) { } else if (slot->hot->count() > 0) {
LeafNode* node = slot->hot->first_value(); LeafNode* node = slot->hot->first_value();
if (!flush_leaf_node(node, true)) err = true; if (!flush_leaf_node(node, true)) err = true;
} }
return !err; return !err;
} }
/** /**
* Clean all of the leaf cache. * Clean all of the leaf cache.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clean_leaf_cache() { bool clean_leaf_cache() {
_assert_(true);
bool err = false; bool err = false;
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
LeafSlot* slot = lslots_ + i; LeafSlot* slot = lslots_ + i;
ScopedSpinLock lock(&slot->lock); ScopedSpinLock lock(&slot->lock);
LeafCache::Iterator it = slot->warm->begin(); LeafCache::Iterator it = slot->warm->begin();
LeafCache::Iterator itend = slot->warm->end(); LeafCache::Iterator itend = slot->warm->end();
while (it != itend) { while (it != itend) {
LeafNode* node = it.value(); LeafNode* node = it.value();
if (!save_leaf_node(node)) err = true; if (!save_leaf_node(node)) err = true;
it++; it++;
skipping to change at line 1387 skipping to change at line 1741
} }
} }
return !err; return !err;
} }
/** /**
* Clean a part of the leaf cache. * Clean a part of the leaf cache.
* @param slot a slot of leaf nodes. * @param slot a slot of leaf nodes.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clean_leaf_cache_part(LeafSlot* slot) { bool clean_leaf_cache_part(LeafSlot* slot) {
_assert_(slot);
bool err = false; bool err = false;
ScopedSpinLock lock(&slot->lock); ScopedSpinLock lock(&slot->lock);
if (slot->warm->count() > 0) { if (slot->warm->count() > 0) {
LeafNode* node = slot->warm->first_value(); LeafNode* node = slot->warm->first_value();
if (!save_leaf_node(node)) err = true; if (!save_leaf_node(node)) err = true;
} else if (slot->hot->count() > 0) { } else if (slot->hot->count() > 0) {
LeafNode* node = slot->hot->first_value(); LeafNode* node = slot->hot->first_value();
if (!save_leaf_node(node)) err = true; if (!save_leaf_node(node)) err = true;
} }
return !err; return !err;
} }
/** /**
* Create a new leaf node. * Create a new leaf node.
* @param prev the ID of the previous node. * @param prev the ID of the previous node.
* @param next the ID of the next node. * @param next the ID of the next node.
* @return the created leaf node. * @return the created leaf node.
*/ */
LeafNode* create_leaf_node(int64_t prev, int64_t next) { LeafNode* create_leaf_node(int64_t prev, int64_t next) {
_assert_(true);
LeafNode* node = new LeafNode; LeafNode* node = new LeafNode;
node->id = ++lcnt_; node->id = ++lcnt_;
node->size = sizeof(int32_t) * 2; node->size = sizeof(int32_t) * 2;
node->recs.reserve(TDBDEFLINUM); node->recs.reserve(TDBDEFLINUM);
node->prev = prev; node->prev = prev;
node->next = next; node->next = next;
node->hot = false; node->hot = false;
node->dirty = true; node->dirty = true;
node->dead = false; node->dead = false;
int32_t sidx = node->id % TDBSLOTNUM; int32_t sidx = node->id % TDBSLOTNUM;
LeafSlot* slot = lslots_ + sidx; LeafSlot* slot = lslots_ + sidx;
slot->warm->set(node->id, node, LeafCache::MLAST); slot->warm->set(node->id, node, LeafCache::MLAST);
cusage_ += node->size; cusage_ += node->size;
return node; return node;
} }
/** /**
* Remove a leaf node from the cache. * Remove a leaf node from the cache.
* @param node the leaf node.
* @param save whether to save dirty node. * @param save whether to save dirty node.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool flush_leaf_node(LeafNode* node, bool save) { bool flush_leaf_node(LeafNode* node, bool save) {
_assert_(node);
bool err = false; bool err = false;
if (save && !save_leaf_node(node)) err = true; if (save && !save_leaf_node(node)) err = true;
RecordArray::const_iterator rit = node->recs.begin(); RecordArray::const_iterator rit = node->recs.begin();
RecordArray::const_iterator ritend = node->recs.end(); RecordArray::const_iterator ritend = node->recs.end();
while (rit != ritend) { while (rit != ritend) {
Record* rec = *rit; Record* rec = *rit;
xfree(rec); xfree(rec);
rit++; rit++;
} }
int32_t sidx = node->id % TDBSLOTNUM; int32_t sidx = node->id % TDBSLOTNUM;
skipping to change at line 1452 skipping to change at line 1810
cusage_ -= node->size; cusage_ -= node->size;
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);
ScopedSpinRWLock lock(&node->lock, true); ScopedSpinRWLock lock(&node->lock, true);
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", TDBLNPREFIX, (long long)node ->id); size_t hsiz = std::sprintf(hbuf, "%c%llX", TDBLNPREFIX, (long long)node ->id);
if (node->dead) { if (node->dead) {
if (!hdb_.remove(hbuf, hsiz)) err = true; if (!hdb_.remove(hbuf, hsiz) && hdb_.error().code() != Error::NOREC) err = 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);
wp += writevarnum(wp, node->next); wp += writevarnum(wp, node->next);
RecordArray::const_iterator rit = node->recs.begin(); RecordArray::const_iterator rit = node->recs.begin();
RecordArray::const_iterator ritend = node->recs.end(); RecordArray::const_iterator ritend = node->recs.end();
while (rit != ritend) { while (rit != ritend) {
Record* rec = *rit; Record* rec = *rit;
wp += writevarnum(wp, rec->ksiz); wp += writevarnum(wp, rec->ksiz);
skipping to change at line 1490 skipping to change at line 1849
node->dirty = false; node->dirty = false;
return !err; return !err;
} }
/** /**
* Load a leaf node. * Load a leaf node.
* @param id the ID number of the leaf node. * @param id the ID number of the leaf node.
* @param prom whether to promote the warm cache. * @param prom whether to promote the warm cache.
* @return the loaded leaf node. * @return the loaded leaf node.
*/ */
LeafNode* load_leaf_node(int64_t id, bool prom) { LeafNode* load_leaf_node(int64_t id, bool prom) {
_assert_(id > 0);
int32_t sidx = id % TDBSLOTNUM; int32_t sidx = id % TDBSLOTNUM;
LeafSlot* slot = lslots_ + sidx; LeafSlot* slot = lslots_ + sidx;
ScopedSpinLock lock(&slot->lock); ScopedSpinLock lock(&slot->lock);
LeafNode** np = slot->hot->get(id, LeafCache::MLAST); LeafNode** np = slot->hot->get(id, LeafCache::MLAST);
if (np) return *np; if (np) return *np;
if (prom) { if (prom) {
if (slot->hot->count() * TDBWARMRATIO > slot->warm->count() + TDBWARM RATIO) { if (slot->hot->count() * TDBWARMRATIO > slot->warm->count() + TDBWARM RATIO) {
slot->hot->first_value()->hot = false; slot->hot->first_value()->hot = false;
slot->hot->migrate(slot->hot->first_key(), slot->warm, LeafCache::M LAST); slot->hot->migrate(slot->hot->first_key(), slot->warm, LeafCache::M LAST);
} }
skipping to change at line 1513 skipping to change at line 1873
return *np; return *np;
} }
} else { } else {
LeafNode** np = slot->warm->get(id, LeafCache::MLAST); LeafNode** np = slot->warm->get(id, LeafCache::MLAST);
if (np) return *np; if (np) return *np;
} }
char hbuf[NUMBUFSIZ]; char hbuf[NUMBUFSIZ];
size_t hsiz = std::sprintf(hbuf, "%c%llX", TDBLNPREFIX, (long long)id); size_t hsiz = std::sprintf(hbuf, "%c%llX", TDBLNPREFIX, (long long)id);
class VisitorImpl : public DB::Visitor { class VisitorImpl : public DB::Visitor {
public: public:
VisitorImpl() : node_(NULL) {} explicit VisitorImpl() : node_(NULL) {}
LeafNode* pop() { LeafNode* pop() {
return node_; return node_;
} }
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) {
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;
skipping to change at line 1578 skipping to change at line 1938
delete node; delete node;
return NOP; return NOP;
} }
node_ = node; node_ = node;
return NOP; return NOP;
} }
LeafNode* node_; LeafNode* node_;
} visitor; } visitor;
if (!hdb_.accept(hbuf, hsiz, &visitor, false)) return NULL; if (!hdb_.accept(hbuf, hsiz, &visitor, false)) return NULL;
LeafNode* node = visitor.pop(); LeafNode* node = visitor.pop();
if (!node) { if (!node) return NULL;
set_error(__FILE__, __LINE__, Error::BROKEN, "leaf node was not found
");
return false;
}
node->id = id; node->id = id;
node->hot = false; node->hot = false;
node->dirty = false; node->dirty = false;
node->dead = false; node->dead = false;
slot->warm->set(id, node, LeafCache::MLAST); slot->warm->set(id, node, LeafCache::MLAST);
cusage_ += node->size; cusage_ += node->size;
return node; return node;
} }
/** /**
* Check whether a record is in the range of a leaf node. * Check whether a record is in the range of a leaf node.
* @param node the leaf node. * @param node the leaf node.
* @param rec the record containing the key only. * @param rec the record containing the key only.
* @return true for in range, or false for out of range. * @return true for in range, or false for out of range.
*/ */
bool check_leaf_node_range(LeafNode* node, Record* rec) { bool check_leaf_node_range(LeafNode* node, Record* rec) {
_assert_(node && rec);
RecordArray& recs = node->recs; RecordArray& recs = node->recs;
if (recs.size() < 1) return false; if (recs.size() < 1) return false;
Record* frec = recs.front(); Record* frec = recs.front();
Record* lrec = recs.back(); Record* lrec = recs.back();
return !reccomp_(rec, frec) && !reccomp_(lrec, rec); return !reccomp_(rec, frec) && !reccomp_(lrec, rec);
} }
/** /**
* Accept a visitor at a leaf node. * Accept a visitor at a leaf node.
* @param node the leaf node. * @param node the leaf node.
* @param rec the record containing the key only. * @param rec the record containing the key only.
* @param visitor a visitor object. * @param visitor a visitor object.
* @return true to reorganize the tree, or false if not. * @return true to reorganize the tree, or false if not.
*/ */
bool accept_impl(LeafNode* node, Record* rec, Visitor* visitor) { bool accept_impl(LeafNode* node, Record* rec, Visitor* visitor) {
_assert_(node && rec && visitor);
bool reorg = false; bool reorg = false;
RecordArray& recs = node->recs; RecordArray& recs = node->recs;
RecordArray::iterator ritend = recs.end(); RecordArray::iterator ritend = recs.end();
RecordArray::iterator rit = std::lower_bound(recs.begin(), ritend, rec, reccomp_); RecordArray::iterator rit = std::lower_bound(recs.begin(), ritend, rec, reccomp_);
if (rit != ritend && !reccomp_(rec, *rit)) { if (rit != ritend && !reccomp_(rec, *rit)) {
Record* rec = *rit; Record* rec = *rit;
char* kbuf = (char*)rec + sizeof(*rec); char* kbuf = (char*)rec + sizeof(*rec);
size_t ksiz = rec->ksiz; size_t ksiz = rec->ksiz;
size_t vsiz; size_t vsiz;
const char* vbuf = visitor->visit_full(kbuf, ksiz, kbuf + ksiz, rec-> vsiz, &vsiz); const char* vbuf = visitor->visit_full(kbuf, ksiz, kbuf + ksiz, rec-> vsiz, &vsiz);
if (vbuf == Visitor::REMOVE) { if (vbuf == Visitor::REMOVE) {
size_t rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz; size_t rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz;
count_ -= 1; count_ -= 1;
cusage_ -= rsiz; cusage_ -= rsiz;
node->size -= rsiz; node->size -= rsiz;
node->dirty = true; node->dirty = true;
xfree(rec); xfree(rec);
recs.erase(rit); recs.erase(rit);
if (recs.size() < 1) reorg = true; if (recs.size() < 1) reorg = true;
} else if (vbuf != Visitor::NOP) { } else if (vbuf != Visitor::NOP) {
int64_t diff = vsiz - rec->vsiz; int64_t diff = (int64_t)vsiz - (int64_t)rec->vsiz;
cusage_ += diff; cusage_ += diff;
node->size += diff; node->size += diff;
node->dirty = true; node->dirty = true;
if (vsiz > rec->vsiz) { if (vsiz > rec->vsiz) {
*rit = (Record*)xrealloc(rec, sizeof(*rec) + rec->ksiz + vsiz); *rit = (Record*)xrealloc(rec, sizeof(*rec) + rec->ksiz + vsiz);
rec = *rit; rec = *rit;
kbuf = (char*)rec + sizeof(*rec); kbuf = (char*)rec + sizeof(*rec);
} }
std::memcpy(kbuf + rec->ksiz, vbuf, vsiz); std::memcpy(kbuf + rec->ksiz, vbuf, vsiz);
rec->vsiz = vsiz; rec->vsiz = vsiz;
skipping to change at line 1673 skipping to change at line 2032
} }
} }
return reorg; return reorg;
} }
/** /**
* Devide a leaf node into two. * Devide a leaf node into two.
* @param node the leaf node. * @param node the leaf node.
* @return the created node, or NULL on failure. * @return the created node, or NULL on failure.
*/ */
LeafNode* divide_leaf_node(LeafNode* node) { LeafNode* divide_leaf_node(LeafNode* node) {
_assert_(node);
LeafNode* newnode = create_leaf_node(node->id, node->next); LeafNode* newnode = create_leaf_node(node->id, node->next);
if (newnode->next > 0) { if (newnode->next > 0) {
LeafNode* nextnode = load_leaf_node(newnode->next, false); LeafNode* nextnode = load_leaf_node(newnode->next, false);
if (!nextnode) return NULL; if (!nextnode) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing leaf node");
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)newnode->ne
xt);
return NULL;
}
nextnode->prev = newnode->id; nextnode->prev = newnode->id;
nextnode->dirty = true; nextnode->dirty = true;
} }
node->next = newnode->id; node->next = newnode->id;
node->dirty = true; node->dirty = true;
RecordArray& recs = node->recs; RecordArray& recs = node->recs;
RecordArray::iterator mid = recs.begin() + recs.size() / 2; RecordArray::iterator mid = recs.begin() + recs.size() / 2;
RecordArray::iterator rit = mid; RecordArray::iterator rit = mid;
RecordArray::iterator ritend = recs.end(); RecordArray::iterator ritend = recs.end();
RecordArray& newrecs = newnode->recs; RecordArray& newrecs = newnode->recs;
while (rit != ritend) { while (rit != ritend) {
Record* rec = *rit; Record* rec = *rit;
newrecs.push_back(rec); newrecs.push_back(rec);
size_t rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz; size_t rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz;
node->size -= rsiz; node->size -= rsiz;
newnode->size += rsiz; newnode->size += rsiz;
rit++; rit++;
} }
escape_cursors(node->id, node->next, *mid);
recs.erase(mid, ritend); recs.erase(mid, ritend);
return newnode; return newnode;
} }
/** /**
* Open the inner cache. * Open the inner cache.
*/ */
void create_inner_cache() { void create_inner_cache() {
_assert_(true);
int64_t bnum = (bnum_ / TDBAVGWAY) / TDBSLOTNUM + 1; int64_t bnum = (bnum_ / TDBAVGWAY) / TDBSLOTNUM + 1;
if (bnum < INT8_MAX) bnum = INT8_MAX; if (bnum < INT8_MAX) bnum = INT8_MAX;
bnum = nearbyprime(bnum); bnum = nearbyprime(bnum);
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
islots_[i].warm = new InnerCache(bnum); islots_[i].warm = new InnerCache(bnum);
} }
} }
/** /**
* Close the inner cache. * Close the inner cache.
*/ */
void delete_inner_cache() { void delete_inner_cache() {
_assert_(true);
for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) { for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) {
InnerSlot* slot = islots_ + i; InnerSlot* slot = islots_ + i;
delete slot->warm; delete slot->warm;
} }
} }
/** /**
* Remove all inner nodes from the inner cache. * Remove all inner nodes from the inner cache.
* @param save whether to save dirty nodes. * @param save whether to save dirty nodes.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool flush_inner_cache(bool save) { bool flush_inner_cache(bool save) {
_assert_(true);
bool err = false; bool err = false;
for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) { for (int32_t i = TDBSLOTNUM - 1; i >= 0; i--) {
InnerSlot* slot = islots_ + i; InnerSlot* slot = islots_ + i;
InnerCache::Iterator it = slot->warm->begin(); InnerCache::Iterator it = slot->warm->begin();
InnerCache::Iterator itend = slot->warm->end(); InnerCache::Iterator itend = slot->warm->end();
while (it != itend) { while (it != itend) {
InnerNode* node = it.value(); InnerNode* node = it.value();
it++; it++;
if (!flush_inner_node(node, save)) err = true; if (!flush_inner_node(node, save)) err = true;
} }
} }
return !err; return !err;
} }
/** /**
* Flush a part of the inner cache. * Flush a part of the inner cache.
* @param slot a slot of inner nodes. * @param slot a slot of inner nodes.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool flush_inner_cache_part(InnerSlot* slot) { bool flush_inner_cache_part(InnerSlot* slot) {
_assert_(slot);
bool err = false; bool err = false;
if (slot->warm->count() > 0) { if (slot->warm->count() > 0) {
InnerNode* node = slot->warm->first_value(); InnerNode* node = slot->warm->first_value();
if (!flush_inner_node(node, true)) err = true; if (!flush_inner_node(node, true)) err = true;
} }
return !err; return !err;
} }
/** /**
* 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);
bool err = false; bool err = false;
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
InnerSlot* slot = islots_ + i; InnerSlot* slot = islots_ + i;
ScopedSpinLock lock(&slot->lock); ScopedSpinLock lock(&slot->lock);
InnerCache::Iterator it = slot->warm->begin(); InnerCache::Iterator it = slot->warm->begin();
InnerCache::Iterator itend = slot->warm->end(); 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;
} }
/** /**
* Create a new inner node. * Create a new inner node.
* @param heir the ID of the child before the first link. * @param heir the ID of the child before the first link.
* @return the created inner node. * @return the created inner node.
*/ */
InnerNode* create_inner_node(int64_t heir) { InnerNode* create_inner_node(int64_t heir) {
_assert_(true);
InnerNode* node = new InnerNode; InnerNode* node = new InnerNode;
node->id = ++icnt_ + TDBINIDBASE; node->id = ++icnt_ + TDBINIDBASE;
node->heir = heir; node->heir = heir;
node->links.reserve(TDBDEFIINUM); node->links.reserve(TDBDEFIINUM);
node->size = sizeof(int64_t); node->size = sizeof(int64_t);
node->dirty = true; node->dirty = true;
node->dead = false; node->dead = false;
int32_t sidx = node->id % TDBSLOTNUM; int32_t sidx = node->id % TDBSLOTNUM;
InnerSlot* slot = islots_ + sidx; InnerSlot* slot = islots_ + sidx;
slot->warm->set(node->id, node, InnerCache::MLAST); slot->warm->set(node->id, node, InnerCache::MLAST);
cusage_ += node->size; cusage_ += node->size;
return node; return node;
} }
/** /**
* Remove an inner node from the cache. * Remove an inner node from the cache.
* @param node the inner node.
* @param save whether to save dirty node. * @param save whether to save dirty node.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool flush_inner_node(InnerNode* node, bool save) { bool flush_inner_node(InnerNode* node, bool save) {
_assert_(node);
bool err = false; bool err = false;
if (save && !save_inner_node(node)) err = true; if (save && !save_inner_node(node)) err = true;
LinkArray::const_iterator lit = node->links.begin(); LinkArray::const_iterator lit = node->links.begin();
LinkArray::const_iterator litend = node->links.end(); LinkArray::const_iterator litend = node->links.end();
while (lit != litend) { while (lit != litend) {
Link* link = *lit; Link* link = *lit;
xfree(link); xfree(link);
lit++; lit++;
} }
int32_t sidx = node->id % TDBSLOTNUM; int32_t sidx = node->id % TDBSLOTNUM;
skipping to change at line 1816 skipping to change at line 2189
cusage_ -= node->size; cusage_ -= node->size;
delete node; delete node;
return !err; return !err;
} }
/** /**
* Save a inner node. * Save a inner node.
* @param node the inner node. * @param node the inner node.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool save_inner_node(InnerNode* node) { bool save_inner_node(InnerNode* node) {
_assert_(true);
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", size_t hsiz = std::sprintf(hbuf, "%c%llX",
TDBINPREFIX, (long long)(node->id - TDBINIDB ASE)); TDBINPREFIX, (long long)(node->id - TDBINIDB ASE));
if (node->dead) { if (node->dead) {
if (!hdb_.remove(hbuf, hsiz)) err = true; if (!hdb_.remove(hbuf, hsiz) && hdb_.error().code() != Error::NOREC) err = 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->heir); wp += writevarnum(wp, node->heir);
LinkArray::const_iterator lit = node->links.begin(); LinkArray::const_iterator lit = node->links.begin();
LinkArray::const_iterator litend = node->links.end(); LinkArray::const_iterator litend = node->links.end();
while (lit != litend) { while (lit != litend) {
Link* link = *lit; Link* link = *lit;
wp += writevarnum(wp, link->child); wp += writevarnum(wp, link->child);
wp += writevarnum(wp, link->ksiz); wp += writevarnum(wp, link->ksiz);
skipping to change at line 1850 skipping to change at line 2224
} }
node->dirty = false; node->dirty = false;
return !err; return !err;
} }
/** /**
* 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);
int32_t sidx = id % TDBSLOTNUM; int32_t sidx = id % TDBSLOTNUM;
InnerSlot* slot = islots_ + sidx; InnerSlot* slot = islots_ + sidx;
ScopedSpinLock lock(&slot->lock); ScopedSpinLock 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", TDBINPREFIX, (long long)(id - TDBINIDBASE)); size_t hsiz = std::sprintf(hbuf, "%c%llX", TDBINPREFIX, (long long)(id - TDBINIDBASE));
class VisitorImpl : public DB::Visitor { class VisitorImpl : public DB::Visitor {
public: public:
VisitorImpl() : node_(NULL) {} explicit VisitorImpl() : node_(NULL) {}
InnerNode* pop() { InnerNode* pop() {
return node_; return node_;
} }
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) {
uint64_t heir; uint64_t heir;
size_t step = readvarnum(vbuf, vsiz, &heir); size_t step = readvarnum(vbuf, vsiz, &heir);
if (step < 1) return NOP; if (step < 1) return NOP;
vbuf += step; vbuf += step;
skipping to change at line 1914 skipping to change at line 2289
delete node; delete node;
return NOP; return NOP;
} }
node_ = node; node_ = node;
return NOP; return NOP;
} }
InnerNode* node_; InnerNode* node_;
} visitor; } visitor;
if (!hdb_.accept(hbuf, hsiz, &visitor, false)) return NULL; if (!hdb_.accept(hbuf, hsiz, &visitor, false)) return NULL;
InnerNode* node = visitor.pop(); InnerNode* node = visitor.pop();
if (!node) { if (!node) return NULL;
set_error(__FILE__, __LINE__, Error::BROKEN, "inner node was not foun
d");
return false;
}
node->id = id; node->id = id;
node->dirty = false; node->dirty = false;
node->dead = false; node->dead = false;
slot->warm->set(id, node, InnerCache::MLAST); slot->warm->set(id, node, InnerCache::MLAST);
cusage_ += node->size; cusage_ += node->size;
return node; return node;
} }
/** /**
* Search the B+ tree. * Search the B+ tree.
* @param link the link containing the key only. * @param link the link containing the key only.
* @param prom whether to promote the warm cache. * @param prom whether to promote the warm cache.
* @param hist the array of visiting history. * @param hist the array of visiting history.
* @param hnp the pointer to the variable into which the number of the hi story is assigned. * @param hnp the pointer to the variable into which the number of the hi story is assigned.
* @return the corresponding leaf node, or NULL on failure. * @return the corresponding leaf node, or NULL on failure.
*/ */
LeafNode* search_tree(Link* link, bool prom, int64_t* hist, int32_t* hnp) { LeafNode* search_tree(Link* link, bool prom, int64_t* hist, int32_t* hnp) {
_assert_(link && hist && hnp);
int64_t id = root_; int64_t id = root_;
int32_t hnum = 0; int32_t hnum = 0;
while (id > TDBINIDBASE) { while (id > TDBINIDBASE) {
InnerNode* node = load_inner_node(id); InnerNode* node = load_inner_node(id);
if (!node) return NULL; if (!node) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing inner node");
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)id);
return NULL;
}
hist[hnum++] = id; hist[hnum++] = id;
const LinkArray& links = node->links; const LinkArray& links = node->links;
LinkArray::const_iterator litbeg = links.begin(); LinkArray::const_iterator litbeg = links.begin();
LinkArray::const_iterator litend = links.end(); LinkArray::const_iterator litend = links.end();
LinkArray::const_iterator lit = std::upper_bound(litbeg, litend, link , linkcomp_); LinkArray::const_iterator lit = std::upper_bound(litbeg, litend, link , linkcomp_);
if (lit == litbeg) { if (lit == litbeg) {
id = node->heir; id = node->heir;
} else { } else {
lit--; lit--;
Link* link = *lit; Link* link = *lit;
skipping to change at line 1963 skipping to change at line 2340
return load_leaf_node(id, prom); return load_leaf_node(id, prom);
} }
/** /**
* Reorganize the B+ tree. * Reorganize the B+ tree.
* @param node a leaf node. * @param node a leaf node.
* @param hist the array of visiting history. * @param hist the array of visiting history.
* @param hnum the number of the history. * @param hnum the number of the history.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool reorganize_tree(LeafNode* node, int64_t* hist, int32_t hnum) { bool reorganize_tree(LeafNode* node, int64_t* hist, int32_t hnum) {
_assert_(node && hist && hnum >= 0);
if (node->size > psiz_ && node->recs.size() > 1) { if (node->size > psiz_ && node->recs.size() > 1) {
LeafNode* newnode = divide_leaf_node(node); LeafNode* newnode = divide_leaf_node(node);
if (!newnode) return false; if (!newnode) return false;
if (node->id == last_) last_ = newnode->id; if (node->id == last_) last_ = newnode->id;
int64_t heir = node->id; int64_t heir = node->id;
int64_t child = newnode->id; int64_t child = newnode->id;
Record* rec = *newnode->recs.begin(); Record* rec = *newnode->recs.begin();
char* dbuf = (char*)rec + sizeof(*rec); char* dbuf = (char*)rec + sizeof(*rec);
int32_t ksiz = rec->ksiz; int32_t ksiz = rec->ksiz;
char* kbuf = new char[ksiz]; char* kbuf = new char[ksiz];
std::memcpy(kbuf, dbuf, ksiz); std::memcpy(kbuf, dbuf, ksiz);
while (true) { while (true) {
if (hnum < 1) { if (hnum < 1) {
InnerNode* inode = create_inner_node(heir); InnerNode* inode = create_inner_node(heir);
add_link_inner_node(inode, child, kbuf, ksiz); add_link_inner_node(inode, child, kbuf, ksiz);
root_ = inode->id; root_ = inode->id;
// hoge
std::string key(kbuf, ksiz);
printf("root?: root=%llX heir=%llX child=%llX %s\n",
(long long)root_, (long long)heir, (long long)child, key.c
_str());
delete[] kbuf; delete[] kbuf;
break; break;
} }
int64_t parent = hist[--hnum]; int64_t parent = hist[--hnum];
InnerNode* inode = load_inner_node(parent); InnerNode* inode = load_inner_node(parent);
if (!inode) { if (!inode) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing inner node"
);
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)parent);
delete[] kbuf; delete[] kbuf;
return false; return false;
} }
add_link_inner_node(inode, child, kbuf, ksiz); add_link_inner_node(inode, child, kbuf, ksiz);
delete[] kbuf; delete[] kbuf;
LinkArray& links = inode->links; LinkArray& links = inode->links;
if (inode->size <= psiz_ || links.size() <= TDBINLINKMIN) break; if (inode->size <= psiz_ || links.size() <= TDBINLINKMIN) break;
LinkArray::iterator litbeg = links.begin(); LinkArray::iterator litbeg = links.begin();
LinkArray::iterator mid = litbeg + links.size() / 2; LinkArray::iterator mid = litbeg + links.size() / 2;
Link* link = *mid; Link* link = *mid;
skipping to change at line 2027 skipping to change at line 2401
for (int32_t i = 0; i <= num; i++) { for (int32_t i = 0; i <= num; i++) {
Link* link = links.back(); Link* link = links.back();
size_t rsiz = sizeof(*link) + link->ksiz; size_t rsiz = sizeof(*link) + link->ksiz;
cusage_ -= rsiz; cusage_ -= rsiz;
inode->size -= rsiz; inode->size -= rsiz;
xfree(link); xfree(link);
links.pop_back(); links.pop_back();
} }
inode->dirty = true; inode->dirty = true;
} }
} else if (node->recs.size() < 1) { } else if (node->recs.size() < 1 && hnum > 0) {
if (!escape_cursors(node->id, node->next)) return false;
// hoge InnerNode* inode = load_inner_node(hist[--hnum]);
//printf("kill\n"); if (!inode) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing inner node");
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)hist[hnum])
;
return false;
}
if (sub_link_tree(inode, node->id, hist, hnum)) {
if (node->prev > 0) {
LeafNode* tnode = load_leaf_node(node->prev, false);
if (!tnode) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing node");
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)node->p
rev);
return false;
}
tnode->next = node->next;
tnode->dirty = true;
if (last_ == node->id) last_ = node->prev;
}
if (node->next > 0) {
LeafNode* tnode = load_leaf_node(node->next, false);
if (!tnode) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing node");
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)node->n
ext);
return false;
}
tnode->prev = node->prev;
tnode->dirty = true;
if (first_ == node->id) first_ = node->next;
}
node->dead = true;
}
} }
return true; return true;
} }
/** /**
* Add a link to a inner node. * Add a link to a inner node.
* @param node the inner node. * @param node the inner node.
* @param child the ID number of the child. * @param child the ID number of the child.
* @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.
*/ */
void add_link_inner_node(InnerNode* node, int64_t child, const char* kbuf , size_t ksiz) { void add_link_inner_node(InnerNode* node, int64_t child, const char* kbuf , size_t ksiz) {
_assert_(node && kbuf);
size_t rsiz = sizeof(Link) + ksiz; size_t rsiz = sizeof(Link) + ksiz;
Link* link = (Link*)xmalloc(rsiz); Link* link = (Link*)xmalloc(rsiz);
link->child = child; link->child = child;
link->ksiz = ksiz; link->ksiz = ksiz;
char* dbuf = (char*)link + sizeof(*link); char* dbuf = (char*)link + sizeof(*link);
std::memcpy(dbuf, kbuf, ksiz); std::memcpy(dbuf, kbuf, ksiz);
LinkArray& links = node->links; LinkArray& links = node->links;
LinkArray::iterator litend = links.end(); LinkArray::iterator litend = links.end();
LinkArray::iterator lit = std::upper_bound(links.begin(), litend, link, linkcomp_); LinkArray::iterator lit = std::upper_bound(links.begin(), litend, link, linkcomp_);
links.insert(lit, link); links.insert(lit, link);
node->size += rsiz; node->size += rsiz;
node->dirty = true; node->dirty = true;
cusage_ += rsiz; cusage_ += rsiz;
} }
/** /**
* Subtract a link from the B+ tree.
* @param node the inner node.
* @param child the ID number of the child.
* @param hist the array of visiting history.
* @param hnum the number of the history.
* @return true on success, or false on failure.
*/
bool sub_link_tree(InnerNode* node, int64_t child, int64_t* hist, int32_t
hnum) {
_assert_(node && hist && hnum >= 0);
node->dirty = true;
LinkArray& links = node->links;
LinkArray::iterator lit = links.begin();
LinkArray::iterator litend = links.end();
if (node->heir == child) {
if (links.size() > 0) {
Link* link = *lit;
node->heir = link->child;
xfree(link);
links.erase(lit);
return true;
} else if (hnum > 0) {
InnerNode* pnode = load_inner_node(hist[--hnum]);
if (!pnode) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing inner node"
);
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)hist[hnum
]);
return false;
}
node->dead = true;
return sub_link_tree(pnode, node->id, hist, hnum);
}
node->dead = true;
root_ = child;
while (child > TDBINIDBASE) {
node = load_inner_node(child);
if (!node) {
set_error(__FILE__, __LINE__, Error::BROKEN, "missing inner node"
);
hdb_.report(__FILE__, __LINE__, "info", "id=%ld", (long)child);
return false;
}
if (node->dead) {
child = node->heir;
root_ = child;
} else {
child = 0;
}
}
return false;
}
while (lit != litend) {
Link* link = *lit;
if (link->child == child) {
xfree(link);
links.erase(lit);
return true;
}
lit++;
}
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid tree");
return false;
}
/**
* Dump the meta data into the file. * Dump the meta data into the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool dump_meta() { bool dump_meta() {
_assert_(true);
char head[TDBHEADSIZ]; char head[TDBHEADSIZ];
std::memset(head, 0, sizeof(head)); std::memset(head, 0, sizeof(head));
char* wp = head; char* wp = head;
if (reccomp_.comp == &LEXICALCOMP) { if (reccomp_.comp == &LEXICALCOMP) {
*(uint8_t*)(wp++) = 0x10; *(uint8_t*)(wp++) = 0x10;
} else if (reccomp_.comp == &DECIMALCOMP) { } else if (reccomp_.comp == &DECIMALCOMP) {
*(uint8_t*)(wp++) = 0x11; *(uint8_t*)(wp++) = 0x11;
} else { } else {
*(uint8_t*)(wp++) = 0xff; *(uint8_t*)(wp++) = 0xff;
} }
skipping to change at line 2102 skipping to change at line 2567
std::memcpy(wp, &num, sizeof(num)); std::memcpy(wp, &num, sizeof(num));
wp += sizeof(num); wp += sizeof(num);
if (!hdb_.set(TDBMETAKEY, sizeof(TDBMETAKEY) - 1, head, sizeof(head))) return false; if (!hdb_.set(TDBMETAKEY, sizeof(TDBMETAKEY) - 1, head, sizeof(head))) return false;
return true; return true;
} }
/** /**
* Load the meta data from the file. * Load the meta data from the file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool load_meta() { bool load_meta() {
_assert_(true);
char head[TDBHEADSIZ]; char head[TDBHEADSIZ];
int32_t hsiz = hdb_.get(TDBMETAKEY, sizeof(TDBMETAKEY) - 1, head, sizeo f(head)); int32_t hsiz = hdb_.get(TDBMETAKEY, sizeof(TDBMETAKEY) - 1, head, sizeo f(head));
if (hsiz < 0) return false; if (hsiz < 0) return false;
if (hsiz != sizeof(head)) { if (hsiz != sizeof(head)) {
set_error(__FILE__, __LINE__, Error::BROKEN, "invalid meta data recor d"); set_error(__FILE__, __LINE__, Error::BROKEN, "invalid meta data recor d");
hdb_.report(__FILE__, __LINE__, "info", "hsiz=%d", hsiz);
return false; return false;
} }
const char* rp = head; const char* rp = head;
if (*(uint8_t*)rp == 0x10) { if (*(uint8_t*)rp == 0x10) {
reccomp_.comp = &LEXICALCOMP; reccomp_.comp = &LEXICALCOMP;
linkcomp_.comp = &LEXICALCOMP; linkcomp_.comp = &LEXICALCOMP;
} else if (*(uint8_t*)rp == 0x11) { } else if (*(uint8_t*)rp == 0x11) {
reccomp_.comp = &DECIMALCOMP; reccomp_.comp = &DECIMALCOMP;
linkcomp_.comp = &DECIMALCOMP; linkcomp_.comp = &DECIMALCOMP;
} else if (*(uint8_t*)rp != 0xff || !reccomp_.comp) { } else if (*(uint8_t*)rp != 0xff || !reccomp_.comp) {
skipping to change at line 2150 skipping to change at line 2617
std::memcpy(&num, rp, sizeof(num)); std::memcpy(&num, rp, sizeof(num));
count_ = ntoh64(num); count_ = ntoh64(num);
rp += sizeof(num); rp += sizeof(num);
return true; return true;
} }
/** /**
* Caluculate the total number of nodes in the leaf cache. * Caluculate the total number of nodes in the leaf cache.
* @return the total number of nodes in the leaf cache. * @return the total number of nodes in the leaf cache.
*/ */
int64_t calc_leaf_cache_count() { int64_t calc_leaf_cache_count() {
_assert_(true);
int64_t sum = 0; int64_t sum = 0;
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
LeafSlot* slot = lslots_ + i; LeafSlot* slot = lslots_ + i;
sum += slot->warm->count(); sum += slot->warm->count();
sum += slot->hot->count(); sum += slot->hot->count();
} }
return sum; return sum;
} }
/** /**
* Caluculate the amount of memory usage of the leaf cache. * Caluculate the amount of memory usage of the leaf cache.
* @return the amount of memory usage of the leaf cache. * @return the amount of memory usage of the leaf cache.
*/ */
int64_t calc_leaf_cache_size() { int64_t calc_leaf_cache_size() {
_assert_(true);
int64_t sum = 0; int64_t sum = 0;
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
LeafSlot* slot = lslots_ + i; LeafSlot* slot = lslots_ + i;
LeafCache::Iterator it = slot->warm->begin(); LeafCache::Iterator it = slot->warm->begin();
LeafCache::Iterator itend = slot->warm->end(); LeafCache::Iterator itend = slot->warm->end();
while (it != itend) { while (it != itend) {
LeafNode* node = it.value(); LeafNode* node = it.value();
sum += node->size; sum += node->size;
it++; it++;
} }
skipping to change at line 2188 skipping to change at line 2657
it++; it++;
} }
} }
return sum; return sum;
} }
/** /**
* Caluculate the total number of nodes in the inner cache. * Caluculate the total number of nodes in the inner cache.
* @return the total number of nodes in the inner cache. * @return the total number of nodes in the inner cache.
*/ */
int64_t calc_inner_cache_count() { int64_t calc_inner_cache_count() {
_assert_(true);
int64_t sum = 0; int64_t sum = 0;
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
InnerSlot* slot = islots_ + i; InnerSlot* slot = islots_ + i;
sum += slot->warm->count(); sum += slot->warm->count();
} }
return sum; return sum;
} }
/** /**
* Caluculate the amount of memory usage of the inner cache. * Caluculate the amount of memory usage of the inner cache.
* @return the amount of memory usage of the inner cache. * @return the amount of memory usage of the inner cache.
*/ */
int64_t calc_inner_cache_size() { int64_t calc_inner_cache_size() {
_assert_(true);
int64_t sum = 0; int64_t sum = 0;
for (int32_t i = 0; i < TDBSLOTNUM; i++) { for (int32_t i = 0; i < TDBSLOTNUM; i++) {
InnerSlot* slot = islots_ + i; InnerSlot* slot = islots_ + i;
InnerCache::Iterator it = slot->warm->begin(); InnerCache::Iterator it = slot->warm->begin();
InnerCache::Iterator itend = slot->warm->end(); InnerCache::Iterator itend = slot->warm->end();
while (it != itend) { while (it != itend) {
InnerNode* node = it.value(); InnerNode* node = it.value();
sum += node->size; sum += node->size;
it++; it++;
} }
} }
return sum; return sum;
} }
/** /**
* Disable all cursors. * Disable all cursors.
*/ */
void disable_cursors() { void disable_cursors() {
_assert_(true);
if (curs_.size() < 1) return; if (curs_.size() < 1) 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->kbuf_) { if (cur->kbuf_) cur->clear_position();
if (cur->kbuf_ != cur->stack_) delete[] cur->kbuf_; cit++;
cur->kbuf_ = NULL; }
cur->lid_ = 0; }
/**
* Escape cursors on a divided leaf node.
* @param src the ID of the source node.
* @param dest the ID of the destination node.
* @param rec the pivot record.
* @return true on success, or false on failure.
*/
void escape_cursors(int64_t src, int64_t dest, Record* rec) {
_assert_(src > 0 && dest >= 0 && rec);
if (curs_.size() < 1) return;
CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end();
while (cit != citend) {
Cursor* cur = *cit;
if (cur->lid_ == src) {
char* dbuf = (char*)rec + sizeof(*rec);
if (reccomp_.comp->compare(cur->kbuf_, cur->ksiz_, dbuf, rec->ksiz)
>= 0)
cur->lid_ = dest;
}
cit++;
}
}
/**
* Escape cursors on a removed leaf node.
* @param src the ID of the source node.
* @param dest the ID of the destination node.
* @return true on success, or false on failure.
*/
bool escape_cursors(int64_t src, int64_t dest) {
_assert_(src > 0 && dest >= 0);
if (curs_.size() < 1) return true;
bool err = false;
CursorList::const_iterator cit = curs_.begin();
CursorList::const_iterator citend = curs_.end();
while (cit != citend) {
Cursor* cur = *cit;
if (cur->lid_ == src) {
cur->clear_position();
if (!cur->set_position(dest) && hdb_.error().code() != Error::NOREC
) err = true;
} }
cit++; cit++;
} }
return !err;
}
/**
* Recalculate the count data.
* @return true on success, or false on failure.
*/
bool recalc_count() {
_assert_(true);
if (!load_meta()) return false;
bool err = false;
create_leaf_cache();
create_inner_cache();
int64_t count = 0;
int64_t id = first_;
while (id > 0) {
LeafNode* node = load_leaf_node(id, false);
if (!node) break;
count += node->recs.size();
id = node->next;
flush_leaf_node(node, false);
}
hdb_.report(__FILE__, __LINE__, "info", "recalculated the record count
from %ld to %ld",
(long)count_, (long)count);
count_ = count;
if (!dump_meta()) err = true;
delete_inner_cache();
delete_leaf_cache();
return !err;
}
/**
* Reorganize the database file.
* @return true on success, or false on failure.
*/
bool reorganize_file() {
_assert_(true);
if (!load_meta()) return false;
const std::string& npath = hdb_.path() + File::EXTCHR + BDBTMPPATHEXT;
TreeDB tdb;
tdb.tune_comparator(reccomp_.comp);
if (!tdb.open(npath, OWRITER | OCREATE | OTRUNCATE)) {
set_error(__FILE__, __LINE__, tdb.error().code(), "opening the destin
ation failed");
return false;
}
hdb_.report(__FILE__, __LINE__, "info", "reorganizing the database");
bool err = false;
create_leaf_cache();
create_inner_cache();
DB::Cursor* cur = hdb_.cursor();
cur->jump();
char* kbuf;
size_t ksiz;
while (!err && (kbuf = cur->get_key(&ksiz)) != NULL) {
if (*kbuf == TDBLNPREFIX) {
int64_t id = std::strtol(kbuf + 1, NULL, 16);
if (id > 0 && id < TDBINIDBASE) {
LeafNode* node = load_leaf_node(id, false);
if (node) {
const RecordArray& recs = node->recs;
RecordArray::const_iterator rit = recs.begin();
RecordArray::const_iterator ritend = recs.end();
while (rit != ritend) {
Record* rec = *rit;
char* dbuf = (char*)rec + sizeof(*rec);
if (!tdb.set(dbuf, rec->ksiz, dbuf + rec->ksiz, rec->vsiz)) {
set_error(__FILE__, __LINE__, tdb.error().code(),
"opening the destination failed");
err = true;
}
rit++;
}
flush_leaf_node(node, false);
}
}
}
delete[] kbuf;
cur->step();
}
delete cur;
delete_inner_cache();
delete_leaf_cache();
if (!tdb.close()) {
set_error(__FILE__, __LINE__, tdb.error().code(), "opening the destin
ation failed");
err = true;
}
HashDB hdb;
if (!err && hdb.open(npath, OREADER)) {
if (!hdb_.clear()) err = true;
cur = hdb.cursor();
cur->jump();
const char* vbuf;
size_t vsiz;
while (!err && (kbuf = cur->get(&ksiz, &vbuf, &vsiz)) != NULL) {
if (!hdb_.set(kbuf, ksiz, vbuf, vsiz)) err = true;
delete[] kbuf;
cur->step();
}
delete cur;
if (!hdb_.synchronize(false, NULL)) err = true;
if (!hdb.close()) {
set_error(__FILE__, __LINE__, hdb.error().code(), "opening the dest
ination failed");
err = true;
}
} else {
set_error(__FILE__, __LINE__, hdb.error().code(), "opening the destin
ation failed");
err = true;
}
File::remove(npath);
return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
* @param hard true for physical synchronization with the device, or fals e for logical * @param hard true for physical synchronization with the device, or fals e for logical
* synchronization with the file system. * synchronization with the file system.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool begin_transaction_impl(bool hard) { bool begin_transaction_impl(bool hard) {
_assert_(true);
if (!clean_leaf_cache()) return false; if (!clean_leaf_cache()) return false;
if (!clean_inner_cache()) return false; if (!clean_inner_cache()) return false;
int32_t idx = trcnt_ % TDBSLOTNUM; int32_t idx = trcnt_++ % TDBSLOTNUM;
LeafSlot* lslot = lslots_ + idx; LeafSlot* lslot = lslots_ + idx;
if (lslot->warm->count() + lslot->hot->count() > 1) flush_leaf_cache_pa rt(lslot); if (lslot->warm->count() + lslot->hot->count() > 1) flush_leaf_cache_pa rt(lslot);
InnerSlot* islot = islots_ + idx; InnerSlot* islot = islots_ + idx;
if (islot->warm->count() > 1) flush_inner_cache_part(islot); if (islot->warm->count() > 1) flush_inner_cache_part(islot);
if (!dump_meta()) return false; if (!dump_meta()) return false;
if (!hdb_.begin_transaction(hard)) return false; if (!hdb_.begin_transaction(hard)) return false;
trcnt_++;
return true; return true;
} }
/** /**
* Commit transaction. * Commit transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool commit_transaction() { bool commit_transaction() {
_assert_(true);
bool err = false; bool err = false;
if (!clean_leaf_cache()) return false; if (!clean_leaf_cache()) return false;
if (!clean_inner_cache()) return false; if (!clean_inner_cache()) return false;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!hdb_.end_transaction(true)) return false; if (!hdb_.end_transaction(true)) return false;
return !err; return !err;
} }
/** /**
* Abort transaction. * Abort transaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool abort_transaction() { bool abort_transaction() {
_assert_(true);
bool err = false; bool err = false;
flush_leaf_cache(false); flush_leaf_cache(false);
flush_inner_cache(false); flush_inner_cache(false);
if (!hdb_.end_transaction(false)) err = true; if (!hdb_.end_transaction(false)) err = true;
if (!load_meta()) err = true; if (!load_meta()) err = true;
disable_cursors(); disable_cursors();
return !err; return !err;
} }
/**
* Fix auto transaction for the B+ tree.
* @return true on success, or false on failure.
*/
bool fix_auto_transaction_tree() {
_assert_(true);
if (!hdb_.begin_transaction(autosync_)) return false;
bool err = false;
if (!clean_leaf_cache()) err = true;
if (!clean_inner_cache()) err = true;
size_t cnum = TDBATRANCNUM / TDBSLOTNUM;
int32_t idx = trcnt_++ % TDBSLOTNUM;
LeafSlot* lslot = lslots_ + idx;
if (lslot->warm->count() + lslot->hot->count() > cnum) flush_leaf_cache
_part(lslot);
InnerSlot* islot = islots_ + idx;
if (islot->warm->count() > cnum) flush_inner_cache_part(islot);
if (!dump_meta()) err = true;
if (!hdb_.end_transaction(true)) err = true;
return !err;
}
/**
* Fix auto transaction for a leaf.
* @return true on success, or false on failure.
*/
bool fix_auto_transaction_leaf(LeafNode* node) {
_assert_(node);
if (!hdb_.begin_transaction(autosync_)) return false;
bool err = false;
if (!save_leaf_node(node)) err = true;
if (!dump_meta()) err = true;
if (!hdb_.end_transaction(true)) err = true;
return !err;
}
/**
* Fix auto synchronization.
* @return true on success, or false on failure.
*/
bool fix_auto_synchronization() {
_assert_(true);
bool err = false;
if (!flush_leaf_cache(true)) err = true;
if (!flush_inner_cache(true)) err = true;
if (!dump_meta()) err = true;
if (!hdb_.synchronize(true, NULL)) err = true;
return !err;
}
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
TreeDB(const TreeDB&); TreeDB(const TreeDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
TreeDB& operator =(const TreeDB&); TreeDB& operator =(const TreeDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; SpinRWLock mlock_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
/** The flag for writer. */ /** The flag for writer. */
bool writer_; bool writer_;
skipping to change at line 2303 skipping to change at line 2969
/** The alignment power. */ /** The alignment power. */
uint8_t apow_; uint8_t apow_;
/** The free block pool power. */ /** The free block pool power. */
uint8_t fpow_; uint8_t fpow_;
/** The options. */ /** The options. */
uint8_t opts_; uint8_t opts_;
/** The bucket number. */ /** The bucket number. */
int64_t bnum_; int64_t bnum_;
/** The page size. */ /** The page size. */
int32_t psiz_; int32_t psiz_;
/** The capacity of cache memory. */ /** The capacity of page cache. */
int64_t ccap_; int64_t pccap_;
/** The root node. */ /** The root node. */
int64_t root_; int64_t root_;
/** The first node. */ /** The first node. */
int64_t first_; int64_t first_;
/** The last node. */ /** The last node. */
int64_t last_; int64_t last_;
/** The count of leaf nodes. */ /** The count of leaf nodes. */
int64_t lcnt_; int64_t lcnt_;
/** The count of inner nodes. */ /** The count of inner nodes. */
int64_t icnt_; int64_t icnt_;
 End of changes. 182 change blocks. 
333 lines changed or deleted 1042 lines changed or added


 kcutil.h   kcutil.h 
skipping to change at line 29 skipping to change at line 29
namespace kyotocabinet { // common namespace namespace kyotocabinet { // common namespace
/** An alias of hash map of strings. */ /** An alias of hash map of strings. */
typedef std::unordered_map<std::string, std::string> StringHashMap; typedef std::unordered_map<std::string, std::string> StringHashMap;
/** An alias of tree map of strings. */ /** An alias of tree map of strings. */
typedef std::map<std::string, std::string> StringTreeMap; typedef std::map<std::string, std::string> StringTreeMap;
/** The package version. */ /** The package version. */
extern const char* VERSION; extern const char* const VERSION;
/** The library version. */ /** The library version. */
extern const int32_t LIBVER; extern const int32_t LIBVER;
/** The library revision. */ /** The library revision. */
extern const int32_t LIBREV; extern const int32_t LIBREV;
/** The database format version. */ /** The database format version. */
extern const int32_t FMTVER; extern const int32_t FMTVER;
skipping to change at line 55 skipping to change at line 55
/** The clock tick of interruption. */ /** The clock tick of interruption. */
extern const int32_t CLOCKTICK; extern const int32_t CLOCKTICK;
/** The size of a page. */ /** The size of a page. */
extern const int32_t PAGESIZE; extern const int32_t PAGESIZE;
/** The buffer size for numeric data. */ /** The buffer size for numeric data. */
const size_t NUMBUFSIZ = 32; const size_t NUMBUFSIZ = 32;
/** The maximum memory size for debugging. */
const size_t MEMMAXSIZ = INT32_MAX / 2;
/** /**
* Convert a string to an integer. * Convert a string to an integer.
* @param str specifies the string. * @param str specifies the string.
* @return the integer. If the string does not contain numeric expression, 0 is returned. * @return the integer. If the string does not contain numeric expression, 0 is returned.
*/ */
int64_t atoi(const std::string& str); int64_t atoi(const char* str);
/** /**
* Convert a string with a metric prefix to an integer. * Convert a string with a metric prefix to an integer.
* @param str the string, which can be trailed by a binary metric prefix. "K", "M", "G", "T", * @param str the string, which can be trailed by a binary metric prefix. "K", "M", "G", "T",
* "P", and "E" are supported. They are case-insensitive. * "P", and "E" are supported. They are case-insensitive.
* @return the integer. If the string does not contain numeric expression, 0 is returned. If * @return the integer. If the string does not contain numeric expression, 0 is returned. If
* the integer overflows the domain, INT64_MAX or INT64_MIN is returned acc ording to the * the integer overflows the domain, INT64_MAX or INT64_MIN is returned acc ording to the
* sign. * sign.
*/ */
int64_t atoix(const std::string& str); int64_t atoix(const char* str);
/** /**
* Convert a string to a real number. * Convert a string to a real number.
* @param str' specifies the string. * @param str' specifies the string.
* @return the real number. If the string does not contain numeric express ion, 0.0 is * @return the real number. If the string does not contain numeric express ion, 0.0 is
* returned. * returned.
*/ */
double atof(const std::string& str); double atof(const char* str);
/** /**
* Normalize a 16-bit number in the native order into the network byte orde r. * Normalize a 16-bit number in the native order into the network byte orde r.
* @param num the 16-bit number in the native order. * @param num the 16-bit number in the native order.
* @return the number in the network byte order. * @return the number in the network byte order.
*/ */
uint16_t hton16(uint16_t num); uint16_t hton16(uint16_t num);
/** /**
* Normalize a 32-bit number in the native order into the network byte orde r. * Normalize a 32-bit number in the native order into the network byte orde r.
skipping to change at line 179 skipping to change at line 182
uint64_t hashfnv(const void* buf, size_t size); uint64_t hashfnv(const void* buf, size_t size);
/** /**
* Get a prime number nearby a number. * Get a prime number nearby a number.
* @param num a natural number. * @param num a natural number.
* @return the result number. * @return the result number.
*/ */
uint64_t nearbyprime(uint64_t num); uint64_t nearbyprime(uint64_t num);
/** /**
* Get the quiet Not-a-Number value.
* @return the quiet Not-a-Number value.
*/
double nan();
/**
* Get the positive infinity value.
* @return the positive infinity value.
*/
double inf();
/**
* Check a number is a Not-a-Number value.
* @return true for the number is a Not-a-Number value, or false if not.
*/
bool chknan(double num);
/**
* Check a number is an infinity value.
* @return true for the number is an infinity value, or false if not.
*/
bool chkinf(double num);
/**
* Append a formatted string at the end of a string. * Append a formatted string at the end of a string.
* @param dest the destination string. * @param dest the destination string.
* @param format the printf-like format string. The conversion character ` %' can be used with * @param format the printf-like format string. The conversion character ` %' can be used with
* such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f' , `g', `G', and `%'. * such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f' , `g', `G', and `%'.
* `S' treats the pointer to a std::string object. * `S' treats the pointer to a std::string object.
* @param ap used according to the format string. * @param ap used according to the format string.
*/ */
void strprintf(std::string* dest, const char* format, va_list ap); void strprintf(std::string* dest, const char* format, va_list ap);
/** /**
* Append a formatted string at the end of a string. * Append a formatted string at the end of a string.
* @param dest the destination string. * @param dest the destination string.
* @param format the printf-like format string. The conversion character ` %' can be used with * @param format the printf-like format string. The conversion character ` %' can be used with
* such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f' , `g', `G', and `%'. * such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f' , `g', `G', and `%'.
* `S' treats the pointer to a std::string object. * `S' treats the pointer to a std::string object.
* @param ... used according to the format string. * @param ... used according to the format string.
*/ */
void strprintf(std::string* dest, const std::string& format, ...); void strprintf(std::string* dest, const char* format, ...);
/** /**
* Generate a formatted string. * Generate a formatted string.
* @param format the printf-like format string. The conversion character ` %' can be used with * @param format the printf-like format string. The conversion character ` %' can be used with
* such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f' , `g', `G', and `%'. * such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f' , `g', `G', and `%'.
* `S' treats the pointer to a std::string object. * `S' treats the pointer to a std::string object.
* @param ... used according to the format string. * @param ... used according to the format string.
* @return the result string. * @return the result string.
*/ */
std::string strprintf(const std::string& format, ...); std::string strprintf(const char* format, ...);
/**
* Split a string with a delimiter
* @param str the string.
* @param delim the delimiter.
* @param elems a vector object into which the result elements are pushed.
* @return the number of result elements.
*/
size_t strsplit(const std::string& str, char delim, std::vector<std::string
>* elems);
/** /**
* Encode a serial object with hexadecimal encoding. * Encode a serial object with hexadecimal encoding.
* @param buf the pointer to the region. * @param buf the pointer to the region.
* @param size the size of the region. * @param size the size of the region.
* @return the result string. * @return the result string.
* @note Because the region of the return value is allocated with the the n ew[] operator, it * @note Because the region of the return value is allocated with the the n ew[] operator, it
* should be released with the delete[] operator when it is no longer in us e. * should be released with the delete[] operator when it is no longer in us e.
*/ */
char* hexencode(const char* buf, size_t size); char* hexencode(const char* buf, size_t size);
skipping to change at line 280 skipping to change at line 316
*/ */
void mapfree(void* ptr); void mapfree(void* ptr);
/** /**
* Get the time of day in seconds. * Get the time of day in seconds.
* @return the time of day in seconds. The accuracy is in microseconds. * @return the time of day in seconds. The accuracy is in microseconds.
*/ */
double time(); double time();
/** /**
* Get the process ID.
* @return the process ID.
*/
int64_t getpid();
/**
* Get the value of an environment variable.
* @return the value of the environment variable, or NULL on failure.
*/
const char* getenv(const char* name);
/**
* Convert a string to an integer. * Convert a string to an integer.
*/ */
inline int64_t atoi(const std::string& str) { inline int64_t atoi(const char* str) {
const char* rp = str.c_str(); _assert_(str);
while (*rp > '\0' && *rp <= ' ') { while (*str > '\0' && *str <= ' ') {
rp++; str++;
} }
int32_t sign = 1; int32_t sign = 1;
int64_t num = 0; int64_t num = 0;
if (*rp == '-') { if (*str == '-') {
rp++; str++;
sign = -1; sign = -1;
} else if (*rp == '+') { } else if (*str == '+') {
rp++; str++;
} }
while (*rp != '\0') { while (*str != '\0') {
if (*rp < '0' || *rp > '9') break; if (*str < '0' || *str > '9') break;
num = num * 10 + *rp - '0'; num = num * 10 + *str - '0';
rp++; str++;
} }
return num * sign; return num * sign;
} }
/** /**
* Convert a string with a metric prefix to an integer. * Convert a string with a metric prefix to an integer.
*/ */
inline int64_t atoix(const std::string& str) { inline int64_t atoix(const char* str) {
const char* rp = str.c_str(); _assert_(str);
while (*rp > '\0' && *rp <= ' ') { while (*str > '\0' && *str <= ' ') {
rp++; str++;
} }
int32_t sign = 1; int32_t sign = 1;
if (*rp == '-') { if (*str == '-') {
rp++; str++;
sign = -1; sign = -1;
} else if (*rp == '+') { } else if (*str == '+') {
rp++; str++;
} }
long double num = 0; long double num = 0;
while (*rp != '\0') { while (*str != '\0') {
if (*rp < '0' || *rp > '9') break; if (*str < '0' || *str > '9') break;
num = num * 10 + *rp - '0'; num = num * 10 + *str - '0';
rp++; str++;
} }
if (*rp == '.') { if (*str == '.') {
rp++; str++;
long double base = 10; long double base = 10;
while (*rp != '\0') { while (*str != '\0') {
if (*rp < '0' || *rp > '9') break; if (*str < '0' || *str > '9') break;
num += (*rp - '0') / base; num += (*str - '0') / base;
rp++; str++;
base *= 10; base *= 10;
} }
} }
num *= sign; num *= sign;
while (*rp > '\0' && *rp <= ' ') { while (*str > '\0' && *str <= ' ') {
rp++; str++;
} }
if (*rp == 'k' || *rp == 'K') { if (*str == 'k' || *str == 'K') {
num *= 1LL << 10; num *= 1LL << 10;
} else if (*rp == 'm' || *rp == 'M') { } else if (*str == 'm' || *str == 'M') {
num *= 1LL << 20; num *= 1LL << 20;
} else if (*rp == 'g' || *rp == 'G') { } else if (*str == 'g' || *str == 'G') {
num *= 1LL << 30; num *= 1LL << 30;
} else if (*rp == 't' || *rp == 'T') { } else if (*str == 't' || *str == 'T') {
num *= 1LL << 40; num *= 1LL << 40;
} else if (*rp == 'p' || *rp == 'P') { } else if (*str == 'p' || *str == 'P') {
num *= 1LL << 50; num *= 1LL << 50;
} else if (*rp == 'e' || *rp == 'E') { } else if (*str == 'e' || *str == 'E') {
num *= 1LL << 60; num *= 1LL << 60;
} }
if (num > INT64_MAX) return INT64_MAX; if (num > INT64_MAX) return INT64_MAX;
if (num < INT64_MIN) return INT64_MIN; if (num < INT64_MIN) return INT64_MIN;
return num; return (int64_t)num;
} }
/** /**
* Convert a string to a real number. * Convert a string to a real number.
*/ */
inline double atof(const std::string& str) { inline double atof(const char* str) {
const char* rp = str.c_str(); _assert_(str);
while (*rp > '\0' && *rp <= ' ') { while (*str > '\0' && *str <= ' ') {
rp++; str++;
} }
int32_t sign = 1; int32_t sign = 1;
if (*rp == '-') { if (*str == '-') {
rp++; str++;
sign = -1; sign = -1;
} else if (*rp == '+') { } else if (*str == '+') {
rp++; str++;
} }
if ((rp[0] == 'i' || rp[0] == 'I') && (rp[1] == 'n' || rp[1] == 'N') && if ((str[0] == 'i' || str[0] == 'I') && (str[1] == 'n' || str[1] == 'N')
(rp[2] == 'f' || rp[2] == 'F')) return HUGE_VAL * sign; &&
if ((rp[0] == 'n' || rp[0] == 'N') && (rp[1] == 'a' || rp[1] == 'A') && (str[2] == 'f' || str[2] == 'F')) return HUGE_VAL * sign;
(rp[2] == 'n' || rp[2] == 'N')) return std::nan(""); if ((str[0] == 'n' || str[0] == 'N') && (str[1] == 'a' || str[1] == 'A')
&&
(str[2] == 'n' || str[2] == 'N')) return nan();
long double num = 0; long double num = 0;
int32_t col = 0; int32_t col = 0;
while (*rp != '\0') { while (*str != '\0') {
if (*rp < '0' || *rp > '9') break; if (*str < '0' || *str > '9') break;
num = num * 10 + *rp - '0'; num = num * 10 + *str - '0';
rp++; str++;
if (num > 0) col++; if (num > 0) col++;
} }
if (*rp == '.') { if (*str == '.') {
rp++; str++;
long double fract = 0.0; long double fract = 0.0;
long double base = 10; long double base = 10;
while (col < 16 && *rp != '\0') { while (col < 16 && *str != '\0') {
if (*rp < '0' || *rp > '9') break; if (*str < '0' || *str > '9') break;
fract += (*rp - '0') / base; fract += (*str - '0') / base;
rp++; str++;
col++; col++;
base *= 10; base *= 10;
} }
num += fract; num += fract;
} }
if (*rp == 'e' || *rp == 'E') { if (*str == 'e' || *str == 'E') {
rp++; str++;
num *= std::pow((long double)10, (long double)atoi(rp)); num *= std::pow((long double)10, (long double)atoi(str));
} }
return num * sign; return num * sign;
} }
/** /**
* Normalize a 16-bit number in the native order into the network byte orde r. * Normalize a 16-bit number in the native order into the network byte orde r.
*/ */
inline uint16_t hton16(uint16_t num) { inline uint16_t hton16(uint16_t num) {
_assert_(true);
if (BIGEND) return num; if (BIGEND) return num;
return ((num & 0x00ffU) << 8) | ((num & 0xff00U) >> 8); return ((num & 0x00ffU) << 8) | ((num & 0xff00U) >> 8);
} }
/** /**
* Normalize a 32-bit number in the native order into the network byte orde r. * Normalize a 32-bit number in the native order into the network byte orde r.
*/ */
inline uint32_t hton32(uint32_t num) { inline uint32_t hton32(uint32_t num) {
_assert_(true);
if (BIGEND) return num; if (BIGEND) return num;
return ((num & 0x000000ffUL) << 24) | ((num & 0x0000ff00UL) << 8) | \ return ((num & 0x000000ffUL) << 24) | ((num & 0x0000ff00UL) << 8) | \
((num & 0x00ff0000UL) >> 8) | ((num & 0xff000000UL) >> 24); ((num & 0x00ff0000UL) >> 8) | ((num & 0xff000000UL) >> 24);
} }
/** /**
* Normalize a 64-bit number in the native order into the network byte orde r. * Normalize a 64-bit number in the native order into the network byte orde r.
*/ */
inline uint64_t hton64(uint64_t num) { inline uint64_t hton64(uint64_t num) {
_assert_(true);
if (BIGEND) return num; if (BIGEND) return num;
return ((num & 0x00000000000000ffULL) << 56) | ((num & 0x000000000000ff00 ULL) << 40) | return ((num & 0x00000000000000ffULL) << 56) | ((num & 0x000000000000ff00 ULL) << 40) |
((num & 0x0000000000ff0000ULL) << 24) | ((num & 0x00000000ff000000ULL) << 8) | ((num & 0x0000000000ff0000ULL) << 24) | ((num & 0x00000000ff000000ULL) << 8) |
((num & 0x000000ff00000000ULL) >> 8) | ((num & 0x0000ff0000000000ULL) > > 24) | ((num & 0x000000ff00000000ULL) >> 8) | ((num & 0x0000ff0000000000ULL) > > 24) |
((num & 0x00ff000000000000ULL) >> 40) | ((num & 0xff00000000000000ULL) >> 56); ((num & 0x00ff000000000000ULL) >> 40) | ((num & 0xff00000000000000ULL) >> 56);
} }
/** /**
* Denormalize a 16-bit number in the network byte order into the native or der. * Denormalize a 16-bit number in the network byte order into the native or der.
*/ */
inline uint16_t ntoh16(uint16_t num) { inline uint16_t ntoh16(uint16_t num) {
_assert_(true);
return hton16(num); return hton16(num);
} }
/** /**
* Denormalize a 32-bit number in the network byte order into the native or der. * Denormalize a 32-bit number in the network byte order into the native or der.
*/ */
inline uint32_t ntoh32(uint32_t num) { inline uint32_t ntoh32(uint32_t num) {
_assert_(true);
return hton32(num); return hton32(num);
} }
/** /**
* Denormalize a 64-bit number in the network byte order into the native or der. * Denormalize a 64-bit number in the network byte order into the native or der.
*/ */
inline uint64_t ntoh64(uint64_t num) { inline uint64_t ntoh64(uint64_t num) {
_assert_(true);
return hton64(num); return hton64(num);
} }
/** /**
* Write a number in fixed length format into a buffer. * Write a number in fixed length format into a buffer.
*/ */
inline void writefixnum(void* buf, uint64_t num, size_t width) { inline void writefixnum(void* buf, uint64_t num, size_t width) {
_assert_(buf && width <= sizeof(int64_t));
num = hton64(num); num = hton64(num);
std::memcpy(buf, (const char*)&num + sizeof(num) - width, width); std::memcpy(buf, (const char*)&num + sizeof(num) - width, width);
} }
/** /**
* Read a number in fixed length format from a buffer. * Read a number in fixed length format from a buffer.
*/ */
inline uint64_t readfixnum(const void* buf, size_t width) { inline uint64_t readfixnum(const void* buf, size_t width) {
_assert_(buf && width <= sizeof(int64_t));
uint64_t num = 0; uint64_t num = 0;
std::memcpy(&num, buf, width); std::memcpy(&num, buf, width);
return ntoh64(num) >> ((sizeof(num) - width) * 8); return ntoh64(num) >> ((sizeof(num) - width) * 8);
} }
/** /**
* Write a number in variable length format into a buffer. * Write a number in variable length format into a buffer.
*/ */
inline size_t writevarnum(void* buf, uint64_t num) { inline size_t writevarnum(void* buf, uint64_t num) {
_assert_(buf);
unsigned char* wp = (unsigned char*)buf; unsigned char* wp = (unsigned char*)buf;
if (num < (1ULL << 7)) { if (num < (1ULL << 7)) {
*(wp++) = num; *(wp++) = num;
} else if (num < (1ULL << 14)) { } else if (num < (1ULL << 14)) {
*(wp++) = (num >> 7) | 0x80; *(wp++) = (num >> 7) | 0x80;
*(wp++) = num & 0x7f; *(wp++) = num & 0x7f;
} else if (num < (1ULL << 21)) { } else if (num < (1ULL << 21)) {
*(wp++) = (num >> 14) | 0x80; *(wp++) = (num >> 14) | 0x80;
*(wp++) = ((num >> 7) & 0x7f) | 0x80; *(wp++) = ((num >> 7) & 0x7f) | 0x80;
*(wp++) = num & 0x7f; *(wp++) = num & 0x7f;
skipping to change at line 547 skipping to change at line 604
*(wp++) = ((num >> 7) & 0x7f) | 0x80; *(wp++) = ((num >> 7) & 0x7f) | 0x80;
*(wp++) = num & 0x7f; *(wp++) = num & 0x7f;
} }
return wp - (unsigned char*)buf; return wp - (unsigned char*)buf;
} }
/** /**
* Read a number in variable length format from a buffer. * Read a number in variable length format from a buffer.
*/ */
inline size_t readvarnum(const void* buf, size_t size, uint64_t* np) { inline size_t readvarnum(const void* buf, size_t size, uint64_t* np) {
_assert_(buf && size <= MEMMAXSIZ && np);
const unsigned char* rp = (const unsigned char*)buf; const unsigned char* rp = (const unsigned char*)buf;
const unsigned char* ep = rp + size; const unsigned char* ep = rp + size;
uint64_t num = 0; uint64_t num = 0;
uint32_t c; uint32_t c;
do { do {
if (rp >= ep) { if (rp >= ep) {
*np = 0; *np = 0;
return 0; return 0;
} }
c = *rp; c = *rp;
skipping to change at line 568 skipping to change at line 626
rp++; rp++;
} while (c >= 0x80); } while (c >= 0x80);
*np = num; *np = num;
return rp - (const unsigned char*)buf; return rp - (const unsigned char*)buf;
} }
/** /**
* Get the hash value by MurMur hashing. * Get the hash value by MurMur hashing.
*/ */
inline uint64_t hashmurmur(const void* buf, size_t size) { inline uint64_t hashmurmur(const void* buf, size_t size) {
_assert_(buf && size <= MEMMAXSIZ);
const uint64_t mul = 0xc6a4a7935bd1e995ULL; const uint64_t mul = 0xc6a4a7935bd1e995ULL;
const int32_t rtt = 47; const int32_t rtt = 47;
uint64_t hash = 19780211ULL ^ (size * mul); uint64_t hash = 19780211ULL ^ (size * mul);
const unsigned char* rp = (const unsigned char*)buf; const unsigned char* rp = (const unsigned char*)buf;
while (size >= sizeof(uint64_t)) { while (size >= sizeof(uint64_t)) {
uint64_t num = ((uint64_t)rp[0] << 0) | ((uint64_t)rp[1] << 8) | uint64_t num = ((uint64_t)rp[0] << 0) | ((uint64_t)rp[1] << 8) |
((uint64_t)rp[2] << 16) | ((uint64_t)rp[3] << 24) | ((uint64_t)rp[2] << 16) | ((uint64_t)rp[3] << 24) |
((uint64_t)rp[4] << 32) | ((uint64_t)rp[5] << 40) | ((uint64_t)rp[4] << 32) | ((uint64_t)rp[5] << 40) |
((uint64_t)rp[6] << 48) | ((uint64_t)rp[7] << 56); ((uint64_t)rp[6] << 48) | ((uint64_t)rp[7] << 56);
num *= mul; num *= mul;
skipping to change at line 605 skipping to change at line 664
hash ^= hash >> rtt; hash ^= hash >> rtt;
hash *= mul; hash *= mul;
hash ^= hash >> rtt; hash ^= hash >> rtt;
return hash; return hash;
} }
/** /**
* Get the hash value by FNV hashing. * Get the hash value by FNV hashing.
*/ */
inline uint64_t hashfnv(const void* buf, size_t size) { inline uint64_t hashfnv(const void* buf, size_t size) {
_assert_(buf && size <= MEMMAXSIZ);
uint64_t hash = 14695981039346656037ULL; uint64_t hash = 14695981039346656037ULL;
const unsigned char* rp = (unsigned char*)buf; const unsigned char* rp = (unsigned char*)buf;
const unsigned char* ep = rp + size; const unsigned char* ep = rp + size;
while (rp < ep) { while (rp < ep) {
hash = (hash ^ *(rp++)) * 109951162811ULL; hash = (hash ^ *(rp++)) * 109951162811ULL;
} }
return hash; return hash;
} }
/** /**
* Get a prime number nearby a number. * Get a prime number nearby a number.
*/ */
inline uint64_t nearbyprime(uint64_t num) { inline uint64_t nearbyprime(uint64_t num) {
_assert_(true);
static uint64_t table[] = { static uint64_t table[] = {
2ULL, 3ULL, 5ULL, 7ULL, 11ULL, 13ULL, 17ULL, 19ULL, 23ULL, 29ULL, 31ULL , 37ULL, 41ULL, 2ULL, 3ULL, 5ULL, 7ULL, 11ULL, 13ULL, 17ULL, 19ULL, 23ULL, 29ULL, 31ULL , 37ULL, 41ULL,
43ULL, 47ULL, 53ULL, 59ULL, 61ULL, 67ULL, 71ULL, 79ULL, 97ULL, 107ULL, 131ULL, 157ULL, 43ULL, 47ULL, 53ULL, 59ULL, 61ULL, 67ULL, 71ULL, 79ULL, 97ULL, 107ULL, 131ULL, 157ULL,
181ULL, 223ULL, 257ULL, 307ULL, 367ULL, 431ULL, 521ULL, 613ULL, 727ULL, 863ULL, 1031ULL, 181ULL, 223ULL, 257ULL, 307ULL, 367ULL, 431ULL, 521ULL, 613ULL, 727ULL, 863ULL, 1031ULL,
1217ULL, 1451ULL, 1723ULL, 2053ULL, 2437ULL, 2897ULL, 3449ULL, 4099ULL, 4871ULL, 5801ULL, 1217ULL, 1451ULL, 1723ULL, 2053ULL, 2437ULL, 2897ULL, 3449ULL, 4099ULL, 4871ULL, 5801ULL,
6899ULL, 8209ULL, 9743ULL, 11587ULL, 13781ULL, 16411ULL, 19483ULL, 2317 3ULL, 27581ULL, 6899ULL, 8209ULL, 9743ULL, 11587ULL, 13781ULL, 16411ULL, 19483ULL, 2317 3ULL, 27581ULL,
32771ULL, 38971ULL, 46349ULL, 55109ULL, 65537ULL, 77951ULL, 92681ULL, 1 10221ULL, 131101ULL, 32771ULL, 38971ULL, 46349ULL, 55109ULL, 65537ULL, 77951ULL, 92681ULL, 1 10221ULL, 131101ULL,
155887ULL, 185363ULL, 220447ULL, 262147ULL, 311743ULL, 370759ULL, 44089 3ULL, 524309ULL, 155887ULL, 185363ULL, 220447ULL, 262147ULL, 311743ULL, 370759ULL, 44089 3ULL, 524309ULL,
623521ULL, 741457ULL, 881743ULL, 1048583ULL, 1246997ULL, 1482919ULL, 17 63491ULL, 623521ULL, 741457ULL, 881743ULL, 1048583ULL, 1246997ULL, 1482919ULL, 17 63491ULL,
2097169ULL, 2493949ULL, 2965847ULL, 3526987ULL, 4194319ULL, 4987901ULL, 5931641ULL, 2097169ULL, 2493949ULL, 2965847ULL, 3526987ULL, 4194319ULL, 4987901ULL, 5931641ULL,
skipping to change at line 660 skipping to change at line 721
1125899906842679ULL, 1338928179998197ULL, 1592262918131449ULL, 18935303 91196921ULL, 1125899906842679ULL, 1338928179998197ULL, 1592262918131449ULL, 18935303 91196921ULL,
2251799813685269ULL, 2677856359996339ULL, 3184525836262943ULL, 37870607 82393821ULL, 2251799813685269ULL, 2677856359996339ULL, 3184525836262943ULL, 37870607 82393821ULL,
4503599627370517ULL, 5355712719992603ULL, 6369051672525833ULL, 75741215 64787633ULL 4503599627370517ULL, 5355712719992603ULL, 6369051672525833ULL, 75741215 64787633ULL
}; };
static const size_t tnum = sizeof(table) / sizeof(table[0]); static const size_t tnum = sizeof(table) / sizeof(table[0]);
uint64_t* ub = std::lower_bound(table, table + tnum, num); uint64_t* ub = std::lower_bound(table, table + tnum, num);
return ub == (uint64_t*)table + tnum ? num : *ub; return ub == (uint64_t*)table + tnum ? num : *ub;
} }
/** /**
* Get the quiet Not-a-Number value.
*/
inline double nan() {
_assert_(true);
return std::numeric_limits<double>::quiet_NaN();
}
/**
* Get the positive infinity value.
*/
inline double inf() {
_assert_(true);
return std::numeric_limits<double>::infinity();
}
/**
* Check a number is a Not-a-Number value.
*/
inline bool chknan(double num) {
_assert_(true);
return num != num;
}
/**
* Check a number is an infinity value.
*/
inline bool chkinf(double num) {
_assert_(true);
return num == inf() || num == -inf();
}
/**
* Append a formatted string at the end of a string. * Append a formatted string at the end of a string.
*/ */
inline void strprintf(std::string* dest, const char* format, va_list ap) { inline void strprintf(std::string* dest, const char* format, va_list ap) {
_assert_(dest && format);
while (*format != '\0') { while (*format != '\0') {
if (*format == '%') { if (*format == '%') {
char cbuf[NUMBUFSIZ]; char cbuf[NUMBUFSIZ];
cbuf[0] = '%'; cbuf[0] = '%';
size_t cbsiz = 1; size_t cbsiz = 1;
int32_t lnum = 0; int32_t lnum = 0;
format++; format++;
while (std::strchr("0123456789 .+-hlLz", *format) && *format != '\0' && while (std::strchr("0123456789 .+-hlLz", *format) && *format != '\0' &&
cbsiz < NUMBUFSIZ - 1) { cbsiz < NUMBUFSIZ - 1) {
if (*format == 'l' || *format == 'L') lnum++; if (*format == 'l' || *format == 'L') lnum++;
skipping to change at line 758 skipping to change at line 852
} else { } else {
dest->append(format, 1); dest->append(format, 1);
} }
format++; format++;
} }
} }
/** /**
* Append a formatted string at the end of a string. * Append a formatted string at the end of a string.
*/ */
inline void strprintf(std::string* dest, const std::string& format, ...) { inline void strprintf(std::string* dest, const char* format, ...) {
_assert_(dest && format);
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
strprintf(dest, format.c_str(), ap); strprintf(dest, format, ap);
va_end(ap); va_end(ap);
} }
/** /**
* Generate a formatted string on memory. * Generate a formatted string on memory.
*/ */
inline std::string strprintf(const std::string& format, ...) { inline std::string strprintf(const char* format, ...) {
_assert_(format);
std::string str; std::string str;
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
strprintf(&str, format.c_str(), ap); strprintf(&str, format, ap);
va_end(ap); va_end(ap);
return str; return str;
} }
/** /**
* Split a string with a delimiter
*/
inline size_t strsplit(const std::string& str, char delim, std::vector<std:
:string>* elems) {
_assert_(elems);
elems->clear();
std::string::const_iterator it = str.begin();
std::string field;
while (it != str.end()) {
if (*it == delim) {
elems->push_back(field);
field.clear();
} else {
field.append(1, *it);
}
it++;
}
elems->push_back(field);
return elems->size();
}
/**
* Encode a serial object with hexadecimal encoding. * Encode a serial object with hexadecimal encoding.
*/ */
inline char* hexencode(const char* buf, size_t size) { inline char* hexencode(const char* buf, size_t size) {
_assert_(buf && size <= MEMMAXSIZ);
const unsigned char* rp = (const unsigned char* )buf; const unsigned char* rp = (const unsigned char* )buf;
char* zbuf = new char[size*2+1]; char* zbuf = new char[size*2+1];
char* wp = zbuf; char* wp = zbuf;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
wp += std::sprintf(wp, "%02x", rp[i]); wp += std::sprintf(wp, "%02x", rp[i]);
} }
*wp = '\0'; *wp = '\0';
return zbuf; return zbuf;
} }
/** /**
* Decode a string encoded with hexadecimal encoding. * Decode a string encoded with hexadecimal encoding.
*/ */
inline char* hexdecode(const char* str, size_t* sp) { inline char* hexdecode(const char* str, size_t* sp) {
_assert_(str && sp);
size_t zsiz = std::strlen(str); size_t zsiz = std::strlen(str);
char* zbuf = new char[zsiz+1]; char* zbuf = new char[zsiz+1];
char* wp = zbuf; char* wp = zbuf;
for (size_t i = 0; i < zsiz; i += 2) { for (size_t i = 0; i < zsiz; i += 2) {
while (str[i] >= '\0' && str[i] <= ' ') { while (str[i] >= '\0' && str[i] <= ' ') {
i++; i++;
} }
int32_t num = 0; int32_t num = 0;
int32_t c = str[i]; int32_t c = str[i];
if (c == '\0') break; if (c == '\0') break;
skipping to change at line 835 skipping to change at line 954
} }
*wp = '\0'; *wp = '\0';
*sp = wp - zbuf; *sp = wp - zbuf;
return zbuf; return zbuf;
} }
/** /**
* Allocate a region on memory. * Allocate a region on memory.
*/ */
inline void* xmalloc(size_t size) { inline void* xmalloc(size_t size) {
_assert_(size <= MEMMAXSIZ);
void* ptr = std::malloc(size); void* ptr = std::malloc(size);
if (!ptr) throw std::bad_alloc(); if (!ptr) throw std::bad_alloc();
return ptr; return ptr;
} }
/** /**
* Allocate a nullified region on memory. * Allocate a nullified region on memory.
*/ */
inline void* xcalloc(size_t nmemb, size_t size) { inline void* xcalloc(size_t nmemb, size_t size) {
_assert_(nmemb <= MEMMAXSIZ && size <= MEMMAXSIZ);
void* ptr = std::calloc(nmemb, size); void* ptr = std::calloc(nmemb, size);
if (!ptr) throw std::bad_alloc(); if (!ptr) throw std::bad_alloc();
return ptr; return ptr;
} }
/** /**
* Re-allocate a region on memory. * Re-allocate a region on memory.
*/ */
inline void* xrealloc(void* ptr, size_t size) { inline void* xrealloc(void* ptr, size_t size) {
_assert_(size <= MEMMAXSIZ);
ptr = std::realloc(ptr, size); ptr = std::realloc(ptr, size);
if (!ptr) throw std::bad_alloc(); if (!ptr) throw std::bad_alloc();
return ptr; return ptr;
} }
/** /**
* Free a region on memory. * Free a region on memory.
*/ */
inline void xfree(void* ptr) { inline void xfree(void* ptr) {
_assert_(true);
std::free(ptr); std::free(ptr);
} }
} // common namespace } // common namespace
#endif // duplication check #endif // duplication check
// END OF FILE // END OF FILE
 End of changes. 61 change blocks. 
74 lines changed or deleted 201 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/