| 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 | |
|
| 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 | |
|
| 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 | |
|
| 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 | |
|
| 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 | |
|