diff options
author | George Hazan <ghazan@miranda.im> | 2017-10-24 14:21:30 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2017-10-24 14:21:30 +0300 |
commit | 753a9206768e249e8efedbab65c06641bb8956ac (patch) | |
tree | a91251bfd66b5279a867c9c708bd913a980f02c5 /plugins/Dbx_kyoto/src/kyotocabinet/kcplantdb.h | |
parent | 3aeb9c4fc4877b04b2ec05a191a6a7b8d231f104 (diff) |
we don't need these test plugins anymore
Diffstat (limited to 'plugins/Dbx_kyoto/src/kyotocabinet/kcplantdb.h')
-rw-r--r-- | plugins/Dbx_kyoto/src/kyotocabinet/kcplantdb.h | 3807 |
1 files changed, 0 insertions, 3807 deletions
diff --git a/plugins/Dbx_kyoto/src/kyotocabinet/kcplantdb.h b/plugins/Dbx_kyoto/src/kyotocabinet/kcplantdb.h deleted file mode 100644 index 92a979e317..0000000000 --- a/plugins/Dbx_kyoto/src/kyotocabinet/kcplantdb.h +++ /dev/null @@ -1,3807 +0,0 @@ -/************************************************************************************************* - * Plant database - * Copyright (C) 2009-2012 FAL Labs - * This file is part of Kyoto Cabinet. - * This program is free software: you can redistribute it and/or modify it under the terms of - * the GNU General Public License as published by the Free Software Foundation, either version - * 3 of the License, or any later version. - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * You should have received a copy of the GNU General Public License along with this program. - * If not, see <http://www.gnu.org/licenses/>. - *************************************************************************************************/ - - -#ifndef _KCPLANTDB_H // duplication check -#define _KCPLANTDB_H - -#include <kccommon.h> -#include <kcutil.h> -#include <kcthread.h> -#include <kcfile.h> -#include <kccompress.h> -#include <kccompare.h> -#include <kcmap.h> -#include <kcregex.h> -#include <kcdb.h> - -#define KCPDBMETAKEY "@" ///< key of the record for meta data -#define KCPDBTMPPATHEXT "tmpkct" ///< extension of the temporary file -#define KCPDRECBUFSIZ 128 ///< size of the record buffer - -namespace kyotocabinet { // common namespace - - -/** - * Plant database. - * @param BASEDB a class compatible with the file hash database class. - * @param DBTYPE the database type number of the class. - * @note This class template is a template for concrete classes to operate tree databases. - * Template instance classes can be inherited but overwriting methods is forbidden. The class - * TreeDB is the instance of the file tree database. The class ForestDB is the instance of the - * directory tree database. Before every database operation, it is necessary to call the - * BasicDB::open method in order to open a database file and connect the database object to it. - * To avoid data missing or corruption, it is important to close every database file by the - * BasicDB::close method when the database is no longer in use. It is forbidden for multible - * database objects in a process to open the same database at the same time. It is forbidden to - * share a database object with child processes. - */ -template <class BASEDB, uint8_t DBTYPE> -class PlantDB : public BasicDB { - public: - class Cursor; - private: - struct Record; - struct RecordComparator; - struct LeafNode; - struct Link; - struct LinkComparator; - struct InnerNode; - struct LeafSlot; - struct InnerSlot; - class ScopedVisitor; - /** An alias of array of records. */ - typedef std::vector<Record*> RecordArray; - /** An alias of array of records. */ - typedef std::vector<Link*> LinkArray; - /** An alias of leaf node cache. */ - typedef LinkedHashMap<int64_t, LeafNode*> LeafCache; - /** An alias of inner node cache. */ - typedef LinkedHashMap<int64_t, InnerNode*> InnerCache; - /** An alias of list of cursors. */ - typedef std::list<Cursor*> CursorList; - /** The number of cache slots. */ - static const int32_t SLOTNUM = 16; - /** The default alignment power. */ - static const uint8_t DEFAPOW = 8; - /** The default free block pool power. */ - static const uint8_t DEFFPOW = 10; - /** The default bucket number. */ - static const int64_t DEFBNUM = 64LL << 10; - /** The default page size. */ - static const int32_t DEFPSIZ = 8192; - /** The default capacity size of the page cache. */ - static const int64_t DEFPCCAP = 64LL << 20; - /** The size of the header. */ - static const int64_t HEADSIZ = 80; - /** The offset of the numbers. */ - static const int64_t MOFFNUMS = 8; - /** The prefix of leaf nodes. */ - static const char LNPREFIX = 'L'; - /** The prefix of inner nodes. */ - static const char INPREFIX = 'I'; - /** The average number of ways of each node. */ - static const size_t AVGWAY = 16; - /** The ratio of the warm cache. */ - static const size_t WARMRATIO = 4; - /** The ratio of flushing inner nodes. */ - static const size_t INFLRATIO = 32; - /** The default number of items in each leaf node. */ - static const size_t DEFLINUM = 64; - /** The default number of items in each inner node. */ - static const size_t DEFIINUM = 128; - /** The base ID number for inner nodes. */ - static const int64_t INIDBASE = 1LL << 48; - /** The minimum number of links in each inner node. */ - static const size_t INLINKMIN = 8; - /** The maximum level of B+ tree. */ - static const int32_t LEVELMAX = 16; - /** The number of cached nodes for auto transaction. */ - static const int32_t ATRANCNUM = 256; - /** The threshold of busy loop and sleep for locking. */ - static const uint32_t LOCKBUSYLOOP = 8192; - public: - /** - * Cursor to indicate a record. - */ - class Cursor : public BasicDB::Cursor { - friend class PlantDB; - public: - /** - * Constructor. - * @param db the container database object. - */ - explicit Cursor(PlantDB* db) : - db_(db), stack_(), kbuf_(NULL), ksiz_(0), lid_(0), back_(false) { - _assert_(db); - db_->curs_.push_back(this); - } - /** - * Destructor. - */ - virtual ~Cursor() { - _assert_(true); - if (!db_) return; - if (kbuf_) clear_position(); - db_->curs_.remove(this); - } - /** - * Accept a visitor to the current record. - * @param visitor a visitor object. - * @param writable true for writable operation, or false for read-only operation. - * @param step true to move the cursor to the next record, or false for no move. - * @return true on success, or false on failure. - * @note The operation for each record is performed atomically and other threads accessing - * the same record are blocked. To avoid deadlock, any explicit database operation must not - * be performed in this function. - */ - bool accept(Visitor* visitor, bool writable = true, bool step = false) { - _assert_(visitor); - if (db_->omode_ == 0) { - db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (writable && !(db_->writer_)) { - db_->set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - if (!kbuf_) { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - return false; - } - bool err = false; - bool hit = false; - - - if (lid_ > 0 && !accept_spec(visitor, writable, step, &hit)) err = true; - - - if (!err && !hit) { - if (kbuf_) { - bool retry = true; - while (!err && retry) { - if (!accept_atom(visitor, step, &retry)) err = true; - } - } else { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - err = true; - } - } - return !err; - } - /** - * Jump the cursor to the first record for forward scan. - * @return true on success, or false on failure. - */ - bool jump() { - _assert_(true); - if (db_->omode_ == 0) { - db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - back_ = false; - if (kbuf_) clear_position(); - bool err = false; - if (!set_position(db_->first_)) err = true; - return !err; - } - /** - * Jump the cursor to a record for forward scan. - * @param kbuf the pointer to the key region. - * @param ksiz the size of the key region. - * @return true on success, or false on failure. - */ - bool jump(const char* kbuf, size_t ksiz) { - _assert_(kbuf && ksiz <= MEMMAXSIZ); - if (db_->omode_ == 0) { - db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - back_ = false; - if (kbuf_) clear_position(); - set_position(kbuf, ksiz, 0); - bool err = false; - if (!adjust_position()) { - if (kbuf_) clear_position(); - err = true; - } - return !err; - } - /** - * Jump the cursor to a record for forward scan. - * @note Equal to the original Cursor::jump method except that the parameter is std::string. - */ - bool jump(const std::string& key) { - _assert_(true); - return jump(key.c_str(), key.size()); - } - /** - * Jump the cursor to the last record for backward scan. - * @return true on success, or false on failure. - * @note This method is dedicated to tree databases. Some database types, especially hash - * databases, may provide a dummy implementation. - */ - bool jump_back() { - _assert_(true); - if (db_->omode_ == 0) { - db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - back_ = true; - if (kbuf_) clear_position(); - bool err = false; - if (!set_position_back(db_->last_)) err = true; - return !err; - } - /** - * Jump the cursor to a record for backward scan. - * @param kbuf the pointer to the key region. - * @param ksiz the size of the key region. - * @return true on success, or false on failure. - */ - bool jump_back(const char* kbuf, size_t ksiz) { - _assert_(kbuf && ksiz <= MEMMAXSIZ); - if (db_->omode_ == 0) { - db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - back_ = true; - if (kbuf_) clear_position(); - set_position(kbuf, ksiz, 0); - bool err = false; - if (adjust_position()) { - if (db_->reccomp_.comp->compare(kbuf, ksiz, kbuf_, ksiz_) < 0) { - bool hit = false; - if (lid_ > 0 && !back_position_spec(&hit)) err = true; - if (!err && !hit) { - if (kbuf_) { - if (!back_position_atom()) err = true; - } else { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - err = true; - } - } - } - } else { - if (kbuf_) clear_position(); - if (!set_position_back(db_->last_)) err = true; - } - return !err; - } - /** - * Jump the cursor to a record for backward scan. - * @note Equal to the original Cursor::jump_back method except that the parameter is - * std::string. - */ - bool jump_back(const std::string& key) { - _assert_(true); - return jump_back(key.c_str(), key.size()); - } - /** - * Step the cursor to the next record. - * @return true on success, or false on failure. - */ - bool step() { - _assert_(true); - back_ = false; - DB::Visitor visitor; - if (!accept(&visitor, false, true)) return false; - if (!kbuf_) { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - return false; - } - return true; - } - /** - * Step the cursor to the previous record. - * @return true on success, or false on failure. - */ - bool step_back() { - _assert_(true); - if (db_->omode_ == 0) { - db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (!kbuf_) { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - return false; - } - back_ = true; - bool err = false; - bool hit = false; - if (lid_ > 0 && !back_position_spec(&hit)) err = true; - if (!err && !hit) { - if (kbuf_) { - if (!back_position_atom()) err = true; - } else { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - err = true; - } - } - return !err; - } - /** - * Get the database object. - * @return the database object. - */ - PlantDB* db() { - _assert_(true); - return db_; - } - - /** - * 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(_KCCODELINE_, Error::BROKEN, "missing leaf node"); - db_->db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)id); - return false; - } - RecordArray& recs = node->recs; - if (!recs.empty()) { - set_position(recs.front(), id); - return true; - } else { - id = node->next; - } - } - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - return false; - } - /** - * Set the current position at the previous node. - * @param id the ID of the previous node. - * @return true on success, or false on failure. - */ - bool set_position_back(int64_t id) { - _assert_(true); - while (id > 0) { - LeafNode* node = db_->load_leaf_node(id, false); - if (!node) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node"); - db_->db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)id); - return false; - } - RecordArray& recs = node->recs; - if (!recs.empty()) { - set_position(recs.back(), id); - return true; - } else { - id = node->prev; - } - } - db_->set_error(_KCCODELINE_, 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[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz_; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz_; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_); - LeafNode* node = db_->load_leaf_node(lid_, false); - if (node) { - char lstack[KCPDRECBUFSIZ]; - char* lbuf = NULL; - size_t lsiz = 0; - Link* link = NULL; - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - RecordArray& recs = node->recs; - if (!recs.empty()) { - Record* frec = recs.front(); - Record* lrec = recs.back(); - if (!db_->reccomp_(rec, frec) && !db_->reccomp_(lrec, rec)) { - typename RecordArray::iterator ritend = recs.end(); - typename RecordArray::iterator rit = std::lower_bound(recs.begin(), ritend, - 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 + 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; - 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); - if (back_) { - if (rit == recs.begin()) { - step = true; - } else { - typename RecordArray::iterator ritprev = rit - 1; - set_position(*ritprev, node->id); - step = false; - } - } else { - typename RecordArray::iterator ritnext = rit + 1; - if (ritnext == ritend) { - step = true; - } else { - 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) { - if (back_) { - if (rit != recs.begin()) { - --rit; - set_position(*rit, node->id); - step = false; - } - } else { - ++rit; - if (rit != ritend) { - clear_position(); - set_position(*rit, node->id); - step = false; - } - } - } - } - } - } - bool atran = db_->autotran_ && !db_->tran_ && node->dirty; - bool async = db_->autosync_ && !db_->autotran_ && !db_->tran_ && node->dirty; - if (hit && step) { - clear_position(); - if (back_) { - set_position_back(node->prev); - } else { - 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_->fix_auto_transaction_leaf(node)) err = true; - 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(_KCCODELINE_, Error::BROKEN, "search failed"); - err = true; - } - } else if (flush) { - int32_t idx = id % SLOTNUM; - 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; - } else { - if (!db_->fix_auto_transaction_leaf(node)) 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 reorg = false; - *retryp = false; - char lstack[KCPDRECBUFSIZ]; - size_t lsiz = sizeof(Link) + ksiz_; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = ksiz_; - std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_); - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - LeafNode* node = db_->search_tree(link, true, hist, &hnum); - if (!node) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "search failed"); - if (lbuf != lstack) delete[] lbuf; - return false; - } - if (node->recs.empty()) { - 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(_KCCODELINE_, Error::BROKEN, "search failed"); - return false; - } - lsiz = sizeof(Link) + ksiz_; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = ksiz_; - std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_); - node = db_->search_tree(link, true, hist, &hnum); - if (node->id != lid_) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "invalid tree"); - if (lbuf != lstack) delete[] lbuf; - return false; - } - } - char rstack[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz_; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz_; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_); - RecordArray& recs = node->recs; - typename RecordArray::iterator ritend = recs.end(); - typename 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_); - } - 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 + 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); - step = false; - clear_position(); - if (back_) { - if (rit == recs.begin()) { - set_position_back(node->prev); - } else { - typename RecordArray::iterator ritprev = rit - 1; - set_position(*ritprev, node->id); - } - } else { - typename RecordArray::iterator ritnext = rit + 1; - if (ritnext == ritend) { - set_position(node->next); - } else { - set_position(*ritnext, node->id); - } - } - recs.erase(rit); - if (recs.empty()) 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; - } - if (step) { - clear_position(); - if (back_) { - if (rit == recs.begin()) { - set_position_back(node->prev); - } else { - --rit; - set_position(*rit, node->id); - } - } else { - ++rit; - if (rit == ritend) { - set_position(node->next); - } else { - set_position(*rit, node->id); - } - } - } - bool atran = db_->autotran_ && !db_->tran_ && node->dirty; - bool async = db_->autosync_ && !db_->autotran_ && !db_->tran_ && node->dirty; - if (atran && !reorg && !db_->fix_auto_transaction_leaf(node)) err = true; - if (reorg) { - if (!db_->reorganize_tree(node, hist, hnum)) err = true; - if (atran && !db_->fix_auto_transaction_tree()) err = true; - } else if (db_->cusage_ > db_->pccap_) { - int32_t idx = node->id % SLOTNUM; - 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; - } else { - int64_t lid = lid_; - clear_position(); - if (back_) { - if (set_position_back(node->prev)) { - if (lid_ == lid) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "invalid leaf node"); - err = true; - } else { - *retryp = true; - } - } else { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - err = true; - } - } else { - if (set_position(node->next)) { - if (lid_ == lid) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "invalid leaf node"); - err = true; - } else { - *retryp = true; - } - } else { - db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); - err = true; - } - } - } - if (rbuf != rstack) delete[] rbuf; - if (lbuf != lstack) delete[] lbuf; - return !err; - } - /** - * Adjust the position to an existing record. - * @return true on success, or false on failure. - */ - bool adjust_position() { - _assert_(true); - char lstack[KCPDRECBUFSIZ]; - size_t lsiz = sizeof(Link) + ksiz_; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = ksiz_; - std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_); - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - LeafNode* node = db_->search_tree(link, true, hist, &hnum); - if (!node) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "search failed"); - if (lbuf != lstack) delete[] lbuf; - return false; - } - char rstack[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz_; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz_; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_); - bool err = false; - const RecordArray& recs = node->recs; - typename RecordArray::const_iterator ritend = node->recs.end(); - typename RecordArray::const_iterator rit = std::lower_bound(recs.begin(), ritend, - rec, db_->reccomp_); - clear_position(); - if (rit == ritend) { - if (!set_position(node->next)) err = true; - } else { - set_position(*rit, node->id); - } - if (rbuf != rstack) delete[] rbuf; - if (lbuf != lstack) delete[] lbuf; - return !err; - } - /** - * Back the position to the previous record speculatively. - * @param hitp the pointer to the variable for the hit flag. - * @return true on success, or false on failure. - */ - bool back_position_spec(bool* hitp) { - _assert_(hitp); - bool err = false; - bool hit = false; - char rstack[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz_; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz_; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_); - LeafNode* node = db_->load_leaf_node(lid_, false); - if (node) { - RecordArray& recs = node->recs; - if (!recs.empty()) { - Record* frec = recs.front(); - Record* lrec = recs.back(); - if (db_->reccomp_(rec, frec)) { - hit = true; - clear_position(); - if (!set_position_back(node->prev)) err = true; - } else if (db_->reccomp_(lrec, rec)) { - } else { - hit = true; - typename RecordArray::iterator ritbeg = recs.begin(); - typename RecordArray::iterator ritend = recs.end(); - typename RecordArray::iterator rit = std::lower_bound(recs.begin(), ritend, - rec, db_->reccomp_); - clear_position(); - if (rit == ritbeg) { - if (!set_position_back(node->prev)) err = true; - } else { - --rit; - set_position(*rit, node->id); - } - } - } - } - if (rbuf != rstack) delete[] rbuf; - *hitp = hit; - return !err; - } - /** - * Back the position to the previous record atomically. - * @return true on success, or false on failure. - */ - bool back_position_atom() { - _assert_(true); - char lstack[KCPDRECBUFSIZ]; - size_t lsiz = sizeof(Link) + ksiz_; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = ksiz_; - std::memcpy(lbuf + sizeof(*link), kbuf_, ksiz_); - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - LeafNode* node = db_->search_tree(link, true, hist, &hnum); - if (!node) { - db_->set_error(_KCCODELINE_, Error::BROKEN, "search failed"); - if (lbuf != lstack) delete[] lbuf; - return false; - } - char rstack[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz_; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz_; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf_, ksiz_); - bool err = false; - const RecordArray& recs = node->recs; - typename RecordArray::const_iterator ritbeg = node->recs.begin(); - typename RecordArray::const_iterator ritend = node->recs.end(); - typename RecordArray::const_iterator rit = std::lower_bound(recs.begin(), ritend, - rec, db_->reccomp_); - clear_position(); - if (rit == ritbeg) { - if (!set_position_back(node->prev)) err = true; - } else if (rit == ritend) { - ritend--; - set_position(*ritend, node->id); - } else { - --rit; - set_position(*rit, node->id); - } - if (rbuf != rstack) delete[] rbuf; - if (lbuf != lstack) delete[] lbuf; - return !err; - } - /** Dummy constructor to forbid the use. */ - Cursor(const Cursor&); - /** Dummy Operator to forbid the use. */ - Cursor& operator =(const Cursor&); - /** The inner database. */ - PlantDB* db_; - /** The stack buffer for the key. */ - char stack_[KCPDRECBUFSIZ]; - /** The pointer to the key region. */ - char* kbuf_; - /** The size of the key region. */ - size_t ksiz_; - /** The last visited leaf. */ - int64_t lid_; - /** The backward flag. */ - bool back_; - }; - /** - * Tuning options. - */ - enum Option { - TSMALL = BASEDB::TSMALL, ///< use 32-bit addressing - TLINEAR = BASEDB::TLINEAR, ///< use linear collision chaining - TCOMPRESS = BASEDB::TCOMPRESS ///< compress each record - }; - /** - * Status flags. - */ - enum Flag { - FOPEN = BASEDB::FOPEN, ///< whether opened - FFATAL = BASEDB::FFATAL ///< whether with fatal error - }; - /** - * Default constructor. - */ - explicit PlantDB() : - mtrigger_(NULL), omode_(0), writer_(false), autotran_(false), autosync_(false), - db_(), curs_(), apow_(DEFAPOW), fpow_(DEFFPOW), opts_(0), bnum_(DEFBNUM), - psiz_(DEFPSIZ), pccap_(DEFPCCAP), - root_(0), first_(0), last_(0), lcnt_(0), icnt_(0), count_(0), cusage_(0), - lslots_(), islots_(), reccomp_(), linkcomp_(), - tran_(false), trclock_(0), trlcnt_(0), trcount_(0) { - _assert_(true); - } - /** - * Destructor. - * @note If the database is not closed, it is closed implicitly. - */ - virtual ~PlantDB() { - _assert_(true); - if (omode_ != 0) close(); - if (!curs_.empty()) { - 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. - * @param kbuf the pointer to the key region. - * @param ksiz the size of the key region. - * @param visitor a visitor object. - * @param writable true for writable operation, or false for read-only operation. - * @return true on success, or false on failure. - * @note The operation for each record is performed atomically and other threads accessing the - * same record are blocked. To avoid deadlock, any explicit database operation must not be - * performed in this function. - */ - bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writable = true) { - _assert_(kbuf && ksiz <= MEMMAXSIZ && visitor); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (writable && !writer_) { - set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - char lstack[KCPDRECBUFSIZ]; - size_t lsiz = sizeof(Link) + ksiz; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = ksiz; - std::memcpy(lbuf + sizeof(*link), kbuf, ksiz); - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - LeafNode* node = search_tree(link, true, hist, &hnum); - if (!node) { - set_error(_KCCODELINE_, Error::BROKEN, "search failed"); - if (lbuf != lstack) delete[] lbuf; - return false; - } - char rstack[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf, ksiz); - - bool reorg = accept_impl(node, rec, visitor); - bool atran = autotran_ && !tran_ && node->dirty; - bool async = autosync_ && !autotran_ && !tran_ && node->dirty; - - bool flush = false; - bool err = false; - int64_t id = node->id; - if (atran && !reorg && !fix_auto_transaction_leaf(node)) err = true; - if (cusage_ > pccap_) { - int32_t idx = id % SLOTNUM; - LeafSlot* lslot = lslots_ + idx; - if (!clean_leaf_cache_part(lslot)) err = true; - flush = true; - } - if (reorg) { - node = search_tree(link, false, hist, &hnum); - if (node) { - if (!reorganize_tree(node, hist, hnum)) err = true; - if (atran && !tran_ && !fix_auto_transaction_tree()) err = true; - } - } else if (flush) { - int32_t idx = id % SLOTNUM; - LeafSlot* lslot = lslots_ + idx; - if (!flush_leaf_cache_part(lslot)) err = true; - InnerSlot* islot = islots_ + idx; - if (islot->warm->count() > lslot->warm->count() + lslot->hot->count() + 1 && - !flush_inner_cache_part(islot)) err = true; - } - if (rbuf != rstack) delete[] rbuf; - if (lbuf != lstack) delete[] lbuf; - if (async) { - if (!fix_auto_synchronization()) err = true; - } - return !err; - } - /** - * Accept a visitor to multiple records at once. - * @param keys specifies a string vector of the keys. - * @param visitor a visitor object. - * @param writable true for writable operation, or false for read-only operation. - * @return true on success, or false on failure. - * @note The operations for specified records are performed atomically and other threads - * accessing the same records are blocked. To avoid deadlock, any explicit database operation - * must not be performed in this function. - */ - bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, - bool writable = true) { - _assert_(visitor); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (writable && !writer_) { - set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - ScopedVisitor svis(visitor); - if (keys.empty()) return true; - bool err = false; - std::vector<std::string>::const_iterator kit = keys.begin(); - std::vector<std::string>::const_iterator kitend = keys.end(); - while (!err && kit != kitend) { - const char* kbuf = kit->data(); - size_t ksiz = kit->size(); - char lstack[KCPDRECBUFSIZ]; - size_t lsiz = sizeof(Link) + ksiz; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = ksiz; - std::memcpy(lbuf + sizeof(*link), kbuf, ksiz); - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - LeafNode* node = search_tree(link, true, hist, &hnum); - if (!node) { - set_error(_KCCODELINE_, Error::BROKEN, "search failed"); - if (lbuf != lstack) delete[] lbuf; - err = true; - break; - } - char rstack[KCPDRECBUFSIZ]; - size_t rsiz = sizeof(Record) + ksiz; - char* rbuf = rsiz > sizeof(rstack) ? new char[rsiz] : rstack; - Record* rec = (Record*)rbuf; - rec->ksiz = ksiz; - rec->vsiz = 0; - std::memcpy(rbuf + sizeof(*rec), kbuf, ksiz); - bool reorg = accept_impl(node, rec, visitor); - bool atran = autotran_ && !tran_ && node->dirty; - bool async = autosync_ && !autotran_ && !tran_ && node->dirty; - if (atran && !reorg && !fix_auto_transaction_leaf(node)) err = true; - if (reorg) { - if (!reorganize_tree(node, hist, hnum)) err = true; - if (atran && !fix_auto_transaction_tree()) err = true; - } else if (cusage_ > pccap_) { - int32_t idx = node->id % SLOTNUM; - LeafSlot* lslot = lslots_ + idx; - if (!flush_leaf_cache_part(lslot)) err = true; - InnerSlot* islot = islots_ + idx; - if (islot->warm->count() > lslot->warm->count() + lslot->hot->count() + 1 && - !flush_inner_cache_part(islot)) err = true; - } - if (rbuf != rstack) delete[] rbuf; - if (lbuf != lstack) delete[] lbuf; - if (async && !fix_auto_synchronization()) err = true; - ++kit; - } - return !err; - } - /** - * Iterate to accept a visitor for each record. - * @param visitor a visitor object. - * @param writable true for writable operation, or false for read-only operation. - * @param checker a progress checker object. If it is NULL, no checking is performed. - * @return true on success, or false on failure. - * @note The whole iteration is performed atomically and other threads are blocked. To avoid - * deadlock, any explicit database operation must not be performed in this function. - */ - bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* checker = NULL) { - _assert_(visitor); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (writable && !writer_) { - set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - ScopedVisitor svis(visitor); - int64_t allcnt = count_; - if (checker && !checker->check("iterate", "beginning", 0, allcnt)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return 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 flcnt = 0; - int64_t curcnt = 0; - while (!err && id > 0) { - LeafNode* node = load_leaf_node(id, false); - if (!node) { - set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)id); - return false; - } - id = node->next; - const RecordArray& recs = node->recs; - RecordArray keys; - keys.reserve(recs.size()); - typename RecordArray::const_iterator rit = recs.begin(); - typename RecordArray::const_iterator ritend = recs.end(); - while (rit != ritend) { - Record* rec = *rit; - size_t rsiz = sizeof(*rec) + rec->ksiz; - char* dbuf = (char*)rec + sizeof(*rec); - Record* key = (Record*)xmalloc(rsiz); - key->ksiz = rec->ksiz; - key->vsiz = 0; - char* kbuf = (char*)key + sizeof(*key); - std::memcpy(kbuf, dbuf, rec->ksiz); - keys.push_back(key); - ++rit; - } - typename RecordArray::const_iterator kit = keys.begin(); - typename RecordArray::const_iterator kitend = keys.end(); - bool reorg = false; - while (kit != kitend) { - Record* rec = *kit; - if (accept_impl(node, rec, visitor)) reorg = true; - curcnt++; - if (checker && !checker->check("iterate", "processing", curcnt, allcnt)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - err = true; - break; - } - ++kit; - } - if (reorg) { - Record* rec = keys.front(); - char* dbuf = (char*)rec + sizeof(*rec); - char lstack[KCPDRECBUFSIZ]; - size_t lsiz = sizeof(Link) + rec->ksiz; - char* lbuf = lsiz > sizeof(lstack) ? new char[lsiz] : lstack; - Link* link = (Link*)lbuf; - link->child = 0; - link->ksiz = rec->ksiz; - std::memcpy(lbuf + sizeof(*link), dbuf, rec->ksiz); - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - node = search_tree(link, false, hist, &hnum); - if (node) { - if (!reorganize_tree(node, hist, hnum)) err = true; - } else { - set_error(_KCCODELINE_, Error::BROKEN, "search failed"); - err = true; - } - if (lbuf != lstack) delete[] lbuf; - } - if (cusage_ > pccap_) { - for (int32_t i = 0; i < SLOTNUM; i++) { - LeafSlot* lslot = lslots_ + i; - if (!flush_leaf_cache_part(lslot)) err = true; - } - InnerSlot* islot = islots_ + (flcnt++) % SLOTNUM; - if (islot->warm->count() > 2 && !flush_inner_cache_part(islot)) err = true; - } - kit = keys.begin(); - while (kit != kitend) { - xfree(*kit); - ++kit; - } - } - if (checker && !checker->check("iterate", "ending", -1, allcnt)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - err = true; - } - if (atran && !commit_transaction()) err = true; - if (autosync_ && !autotran_ && writable && !fix_auto_synchronization()) err = true; - trigger_meta(MetaTrigger::ITERATE, "iterate"); - return !err; - } - /** - * Scan each record in parallel. - * @param visitor a visitor object. - * @param thnum the number of worker threads. - * @param checker a progress checker object. If it is NULL, no checking is performed. - * @return true on success, or false on failure. - * @note This function is for reading records and not for updating ones. The return value of - * the visitor is just ignored. To avoid deadlock, any explicit database operation must not - * be performed in this function. - */ - bool scan_parallel(Visitor *visitor, size_t thnum, ProgressChecker* checker = NULL) { - _assert_(visitor && thnum <= MEMMAXSIZ); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (thnum < 1) thnum = 0; - if (thnum > (size_t)INT8MAX) thnum = INT8MAX; - bool err = false; - if (writer_) { - if (checker && !checker->check("scan_parallel", "cleaning the leaf node cache", -1, -1)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - if (!clean_leaf_cache()) err = true; - } - ScopedVisitor svis(visitor); - int64_t allcnt = count_; - if (checker && !checker->check("scan_parallel", "beginning", 0, allcnt)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - class ProgressCheckerImpl : public ProgressChecker { - public: - explicit ProgressCheckerImpl() : ok_(1) {} - void stop() { - ok_.set(0); - } - private: - bool check(const char* name, const char* message, int64_t curcnt, int64_t allcnt) { - return ok_ > 0; - } - AtomicInt64 ok_; - }; - ProgressCheckerImpl ichecker; - class VisitorImpl : public Visitor { - public: - explicit VisitorImpl(PlantDB* db, Visitor* visitor, - ProgressChecker* checker, int64_t allcnt, - ProgressCheckerImpl* ichecker) : - db_(db), visitor_(visitor), checker_(checker), allcnt_(allcnt), - ichecker_(ichecker), error_() {} - const Error& error() { - return error_; - } - private: - const char* visit_full(const char* kbuf, size_t ksiz, - const char* vbuf, size_t vsiz, size_t* sp) { - if (ksiz < 2 || ksiz >= NUMBUFSIZ || kbuf[0] != LNPREFIX) return NOP; - uint64_t prev; - size_t step = readvarnum(vbuf, vsiz, &prev); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - uint64_t next; - step = readvarnum(vbuf, vsiz, &next); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - while (vsiz > 1) { - uint64_t rksiz; - step = readvarnum(vbuf, vsiz, &rksiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - uint64_t rvsiz; - step = readvarnum(vbuf, vsiz, &rvsiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - if (vsiz < rksiz + rvsiz) break; - size_t xvsiz; - visitor_->visit_full(vbuf, rksiz, vbuf + rksiz, rvsiz, &xvsiz); - vbuf += rksiz; - vsiz -= rksiz; - vbuf += rvsiz; - vsiz -= rvsiz; - if (checker_ && !checker_->check("scan_parallel", "processing", -1, allcnt_)) { - db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - error_ = db_->error(); - ichecker_->stop(); - break; - } - } - return NOP; - } - PlantDB* db_; - Visitor* visitor_; - ProgressChecker* checker_; - int64_t allcnt_; - ProgressCheckerImpl* ichecker_; - Error error_; - }; - VisitorImpl ivisitor(this, visitor, checker, allcnt, &ichecker); - if (!db_.scan_parallel(&ivisitor, thnum, &ichecker)) err = true; - if (ivisitor.error() != Error::SUCCESS) { - const Error& e = ivisitor.error(); - db_.set_error(_KCCODELINE_, e.code(), e.message()); - err = true; - } - if (checker && !checker->check("scan_parallel", "ending", -1, allcnt)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - err = true; - } - trigger_meta(MetaTrigger::ITERATE, "scan_parallel"); - return !err; - } - /** - * Get the last happened error. - * @return the last happened error. - */ - Error error() const { - _assert_(true); - return db_.error(); - } - /** - * Set the error information. - * @param file the file name of the program source code. - * @param line the line number of the program source code. - * @param func the function name of the program source code. - * @param code an error code. - * @param message a supplement message. - */ - void set_error(const char* file, int32_t line, const char* func, - Error::Code code, const char* message) { - _assert_(file && line > 0 && func && message); - db_.set_error(file, line, func, code, message); - } - /** - * Open a database file. - * @param path the path of a database file. - * @param mode the connection mode. BasicDB::OWRITER as a writer, BasicDB::OREADER as a - * reader. The following may be added to the writer mode by bitwise-or: BasicDB::OCREATE, - * which means it creates a new database if the file does not exist, BasicDB::OTRUNCATE, which - * means it creates a new database regardless if the file exists, BasicDB::OAUTOTRAN, which - * means each updating operation is performed in implicit transaction, BasicDB::OAUTOSYNC, - * which means each updating operation is followed by implicit synchronization with the file - * system. The following may be added to both of the reader mode and the writer mode by - * bitwise-or: BasicDB::ONOLOCK, which means it opens the database file without file locking, - * BasicDB::OTRYLOCK, which means locking is performed without blocking, BasicDB::ONOREPAIR, - * which means the database file is not repaired implicitly even if file destruction is - * detected. - * @return true on success, or false on failure. - * @note Every opened database must be closed by the BasicDB::close method when it is no - * longer in use. It is not allowed for two or more database objects in the same process to - * keep their connections to the same database file at the same time. - */ - bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", path.c_str()); - if (DBTYPE == TYPEGRASS) { - mode &= ~OREADER; - mode |= OWRITER | OCREATE; - } - writer_ = false; - autotran_ = false; - autosync_ = false; - if (mode & OWRITER) { - writer_ = true; - if (mode & OAUTOTRAN) autotran_ = true; - if (mode & OAUTOSYNC) autosync_ = true; - } - if (!db_.tune_type(DBTYPE)) return false; - if (!db_.tune_alignment(apow_)) return false; - if (!db_.tune_fbp(fpow_)) return false; - if (!db_.tune_options(opts_)) return false; - if (!db_.tune_buckets(bnum_)) return false; - if (!db_.open(path, mode)) return false; - if (db_.type() != DBTYPE) { - set_error(_KCCODELINE_, Error::INVALID, "invalid database type"); - db_.close(); - return false; - } - if (db_.reorganized()) { - if (!reorganize_file(mode)) return false; - } else if (db_.recovered()) { - if (!writer_) { - if (!db_.close()) return false; - uint32_t tmode = (mode & ~OREADER) | OWRITER; - if (!db_.open(path, tmode)) return false; - } - if (!recalc_count()) return false; - if (!writer_) { - if (!db_.close()) return false; - if (!db_.open(path, mode)) return false; - } - if (count_ == INT64MAX && !reorganize_file(mode)) return false; - } - if (writer_ && db_.count() < 1) { - root_ = 0; - first_ = 0; - last_ = 0; - count_ = 0; - create_leaf_cache(); - create_inner_cache(); - lcnt_ = 0; - create_leaf_node(0, 0); - root_ = 1; - first_ = 1; - last_ = 1; - lcnt_ = 1; - icnt_ = 0; - count_ = 0; - if (!reccomp_.comp) reccomp_.comp = LEXICALCOMP; - if (!dump_meta() || !flush_leaf_cache(true) || !load_meta()) { - delete_inner_cache(); - delete_leaf_cache(); - db_.close(); - return false; - } - } else { - if (!load_meta()) { - db_.close(); - return false; - } - create_leaf_cache(); - create_inner_cache(); - } - if (psiz_ < 1 || root_ < 1 || first_ < 1 || last_ < 1 || - lcnt_ < 1 || icnt_ < 0 || count_ < 0 || bnum_ < 1) { - set_error(_KCCODELINE_, Error::BROKEN, "invalid meta data"); - db_.report(_KCCODELINE_, Logger::WARN, "psiz=%lld root=%lld first=%lld last=%lld" - " lcnt=%lld icnt=%lld count=%lld bnum=%lld", - (long long)psiz_, (long long)root_, (long long)first_, (long long)last_, - (long long)lcnt_, (long long)icnt_, (long long)count_, (long long)bnum_); - delete_inner_cache(); - delete_leaf_cache(); - db_.close(); - return false; - } - omode_ = mode; - cusage_ = 0; - tran_ = false; - trclock_ = 0; - trigger_meta(MetaTrigger::OPEN, "open"); - return true; - } - /** - * Close the database file. - * @return true on success, or false on failure. - */ - bool close() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - const std::string& path = db_.path(); - report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", path.c_str()); - bool err = false; - disable_cursors(); - int64_t lsiz = calc_leaf_cache_size(); - int64_t isiz = calc_inner_cache_size(); - if (cusage_ != lsiz + isiz) { - set_error(_KCCODELINE_, Error::BROKEN, "invalid cache usage"); - db_.report(_KCCODELINE_, Logger::WARN, "cusage=%lld lsiz=%lld isiz=%lld", - (long long)cusage_, (long long)lsiz, (long long)isiz); - err = true; - } - if (!flush_leaf_cache(true)) err = true; - if (!flush_inner_cache(true)) err = true; - lsiz = calc_leaf_cache_size(); - isiz = calc_inner_cache_size(); - int64_t lcnt = calc_leaf_cache_count(); - int64_t icnt = calc_inner_cache_count(); - if (cusage_ != 0 || lsiz != 0 || isiz != 0 || lcnt != 0 || icnt != 0) { - set_error(_KCCODELINE_, Error::BROKEN, "remaining cache"); - db_.report(_KCCODELINE_, Logger::WARN, "cusage=%lld lsiz=%lld isiz=%lld" - " lcnt=%lld icnt=%lld", (long long)cusage_, (long long)lsiz, (long long)isiz, - (long long)lcnt, (long long)icnt); - err = true; - } - delete_inner_cache(); - delete_leaf_cache(); - if (writer_ && !dump_meta()) err = true; - if (!db_.close()) err = true; - omode_ = 0; - trigger_meta(MetaTrigger::CLOSE, "close"); - return !err; - } - /** - * Synchronize updated contents with the file and the device. - * @param hard true for physical synchronization with the device, or false for logical - * synchronization with the file system. - * @param proc a postprocessor object. If it is NULL, no postprocessing is performed. - * @param checker a progress checker object. If it is NULL, no checking is performed. - * @return true on success, or false on failure. - * @note The operation of the postprocessor is performed atomically and other threads accessing - * the same record are blocked. To avoid deadlock, any explicit database operation must not - * be performed in this function. - */ - bool synchronize(bool hard = false, FileProcessor* proc = NULL, - ProgressChecker* checker = NULL) { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - bool err = false; - if (writer_) { - if (checker && !checker->check("synchronize", "cleaning the leaf node cache", -1, -1)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - if (!clean_leaf_cache()) err = true; - if (checker && !checker->check("synchronize", "cleaning the inner node cache", -1, -1)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - if (!clean_inner_cache()) err = true; - if (checker && !checker->check("synchronize", "flushing the leaf node cache", -1, -1)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - if (!flush_leaf_cache(true)) err = true; - if (checker && !checker->check("synchronize", "flushing the inner node cache", -1, -1)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - if (!flush_inner_cache(true)) err = true; - if (checker && !checker->check("synchronize", "dumping the meta data", -1, -1)) { - set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); - return false; - } - if (!dump_meta()) err = true; - } - class Wrapper : public FileProcessor { - public: - Wrapper(FileProcessor* proc, int64_t count) : proc_(proc), count_(count) {} - private: - bool process(const std::string& path, int64_t count, int64_t size) { - if (proc_) return proc_->process(path, count_, size); - return true; - } - FileProcessor* proc_; - int64_t count_; - } wrapper(proc, count_); - if (!db_.synchronize(hard, &wrapper, checker)) err = true; - trigger_meta(MetaTrigger::SYNCHRONIZE, "synchronize"); - return !err; - } - /** - * Occupy database by locking and do something meanwhile. - * @param writable true to use writer lock, or false to use reader lock. - * @param proc a processor object. If it is NULL, no processing is performed. - * @return true on success, or false on failure. - * @note The operation of the processor is performed atomically and other threads accessing - * the same record are blocked. To avoid deadlock, any explicit database operation must not - * be performed in this function. - */ - bool occupy(bool writable = true, FileProcessor* proc = NULL) { - _assert_(true); - bool err = false; - if (proc && !proc->process(db_.path(), count_, db_.size())) { - set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); - err = true; - } - trigger_meta(MetaTrigger::OCCUPY, "occupy"); - return !err; - } - /** - * Begin transaction. - * @param hard true for physical synchronization with the device, or false for logical - * synchronization with the file system. - * @return true on success, or false on failure. - */ - bool begin_transaction(bool hard = false) { - _assert_(true); - uint32_t wcnt = 0; - while (true) { - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (!writer_) { - set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - if (!tran_) break; - if (wcnt >= LOCKBUSYLOOP) { - Thread::chill(); - } else { - Thread::yield(); - wcnt++; - } - } - if (!begin_transaction_impl(hard)) { - return false; - } - tran_ = true; - trigger_meta(MetaTrigger::BEGINTRAN, "begin_transaction"); - return true; - } - /** - * Try to begin transaction. - * @param hard true for physical synchronization with the device, or false for logical - * synchronization with the file system. - * @return true on success, or false on failure. - */ - bool begin_transaction_try(bool hard = false) { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (!writer_) { - set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - if (tran_) { - set_error(_KCCODELINE_, Error::LOGIC, "competition avoided"); - return false; - } - if (!begin_transaction_impl(hard)) { - return false; - } - tran_ = true; - trigger_meta(MetaTrigger::BEGINTRAN, "begin_transaction_try"); - return true; - } - /** - * End transaction. - * @param commit true to commit the transaction, or false to abort the transaction. - * @return true on success, or false on failure. - */ - bool end_transaction(bool commit = true) { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (!tran_) { - set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); - return false; - } - bool err = false; - if (commit) { - if (!commit_transaction()) err = true; - } else { - if (!abort_transaction()) err = true; - } - tran_ = false; - trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); - return !err; - } - /** - * Remove all records. - * @return true on success, or false on failure. - */ - bool clear() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (!writer_) { - set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); - return false; - } - disable_cursors(); - flush_leaf_cache(false); - flush_inner_cache(false); - bool err = false; - if (!db_.clear()) err = true; - lcnt_ = 0; - create_leaf_node(0, 0); - root_ = 1; - first_ = 1; - last_ = 1; - lcnt_ = 1; - icnt_ = 0; - count_ = 0; - if (!dump_meta()) err = true; - if (!flush_leaf_cache(true)) err = true; - cusage_ = 0; - trigger_meta(MetaTrigger::CLEAR, "clear"); - return !err; - } - /** - * Get the number of records. - * @return the number of records, or -1 on failure. - */ - int64_t count() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return -1; - } - return count_; - } - /** - * Get the size of the database file. - * @return the size of the database file in bytes, or -1 on failure. - */ - int64_t size() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return -1; - } - return db_.size(); - } - /** - * Get the path of the database file. - * @return the path of the database file, or an empty string on failure. - */ - std::string path() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return ""; - } - return db_.path(); - } - /** - * Get the miscellaneous status information. - * @param strmap a string map to contain the result. - * @return true on success, or false on failure. - */ - bool status(std::map<std::string, std::string>* strmap) { - _assert_(strmap); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - if (!db_.status(strmap)) return false; - (*strmap)["type"] = strprintf("%u", (unsigned)DBTYPE); - (*strmap)["psiz"] = strprintf("%d", psiz_); - (*strmap)["pccap"] = strprintf("%lld", (long long)pccap_); - const char* compname = "external"; - if (reccomp_.comp == LEXICALCOMP) { - compname = "lexical"; - } else if (reccomp_.comp == DECIMALCOMP) { - compname = "decimal"; - } else if (reccomp_.comp == LEXICALDESCCOMP) { - compname = "lexicaldesc"; - } else if (reccomp_.comp == DECIMALDESCCOMP) { - compname = "decimaldesc"; - } - (*strmap)["rcomp"] = compname; - (*strmap)["root"] = strprintf("%lld", (long long)root_); - (*strmap)["first"] = strprintf("%lld", (long long)first_); - (*strmap)["last"] = strprintf("%lld", (long long)last_); - (*strmap)["lcnt"] = strprintf("%lld", (long long)lcnt_); - (*strmap)["icnt"] = strprintf("%lld", (long long)icnt_); - (*strmap)["count"] = strprintf("%lld", (long long)count_); - (*strmap)["bnum"] = strprintf("%lld", (long long)bnum_); - (*strmap)["pnum"] = strprintf("%lld", (long long)db_.count()); - (*strmap)["cusage"] = strprintf("%lld", (long long)cusage_); - if (strmap->count("cusage_lcnt") > 0) - (*strmap)["cusage_lcnt"] = strprintf("%lld", (long long)calc_leaf_cache_count()); - if (strmap->count("cusage_lsiz") > 0) - (*strmap)["cusage_lsiz"] = strprintf("%lld", (long long)calc_leaf_cache_size()); - if (strmap->count("cusage_icnt") > 0) - (*strmap)["cusage_icnt"] = strprintf("%lld", (long long)calc_inner_cache_count()); - if (strmap->count("cusage_isiz") > 0) - (*strmap)["cusage_isiz"] = strprintf("%lld", (long long)calc_inner_cache_size()); - if (strmap->count("tree_level") > 0) { - Link link; - link.ksiz = 0; - int64_t hist[LEVELMAX]; - int32_t hnum = 0; - search_tree(&link, false, hist, &hnum); - (*strmap)["tree_level"] = strprintf("%d", hnum + 1); - } - return true; - } - /** - * Create a cursor object. - * @return the return value is the created cursor object. - * @note Because the object of the return value is allocated by the constructor, it should be - * released with the delete operator when it is no longer in use. - */ - Cursor* cursor() { - _assert_(true); - return new Cursor(this); - } - /** - * Write a log message. - * @param file the file name of the program source code. - * @param line the line number of the program source code. - * @param func the function name of the program source code. - * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal - * information, Logger::WARN for warning, and Logger::ERROR for fatal error. - * @param message the supplement message. - */ - void log(const char* file, int32_t line, const char* func, Logger::Kind kind, - const char* message) { - _assert_(file && line > 0 && func && message); - db_.log(file, line, func, kind, message); - } - /** - * Set the internal logger. - * @param logger the logger object. - * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, - * Logger::INFO for normal information, Logger::WARN for warning, and Logger::ERROR for fatal - * error. - * @return true on success, or false on failure. - */ - bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger::ERROR) { - _assert_(logger); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - return db_.tune_logger(logger, kinds); - } - /** - * Set the internal meta operation trigger. - * @param trigger the trigger object. - * @return true on success, or false on failure. - */ - bool tune_meta_trigger(MetaTrigger* trigger) { - _assert_(trigger); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - mtrigger_ = trigger; - return true; - } - /** - * Set 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. - */ - bool tune_alignment(int8_t apow) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - apow_ = apow >= 0 ? apow : DEFAPOW; - return true; - } - /** - * Set 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. - */ - bool tune_fbp(int8_t fpow) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - fpow_ = fpow >= 0 ? fpow : DEFFPOW; - return true; - } - /** - * Set the optional features. - * @param opts the optional features by bitwise-or: BasicDB::TSMALL to use 32-bit addressing, - * BasicDB::TLINEAR to use linear collision chaining, BasicDB::TCOMPRESS to compress each - * record. - * @return true on success, or false on failure. - */ - bool tune_options(int8_t opts) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - opts_ = opts; - return true; - } - /** - * Set 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. - */ - bool tune_buckets(int64_t bnum) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - bnum_ = bnum > 0 ? bnum : DEFBNUM; - return true; - } - /** - * Set the size of each page. - * @param psiz the size of each page. - * @return true on success, or false on failure. - */ - bool tune_page(int32_t psiz) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - psiz_ = psiz > 0 ? psiz : DEFPSIZ; - return true; - } - /** - * Set 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. - */ - bool tune_map(int64_t msiz) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - return db_.tune_map(msiz); - } - /** - * Set the unit step number of auto defragmentation. - * @param dfunit the unit step number of auto defragmentation. - * @return true on success, or false on failure. - */ - bool tune_defrag(int64_t dfunit) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - return db_.tune_defrag(dfunit); - } - /** - * Set the capacity size of the page cache. - * @param pccap the capacity size of the page cache. - * @return true on success, or false on failure. - */ - bool tune_page_cache(int64_t pccap) { - _assert_(true); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - pccap_ = pccap > 0 ? pccap : DEFPCCAP; - return true; - } - /** - * Set the data compressor. - * @param comp the data compressor object. - * @return true on success, or false on failure. - */ - bool tune_compressor(Compressor* comp) { - _assert_(comp); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - return db_.tune_compressor(comp); - } - /** - * Set the record comparator. - * @param rcomp the record comparator object. - * @return true on success, or false on failure. - * @note Several built-in comparators are provided. LEXICALCOMP for the default lexical - * comparator. DECIMALCOMP for the decimal comparator. LEXICALDESCCOMP for the lexical - * descending comparator. DECIMALDESCCOMP for the lexical descending comparator. - */ - bool tune_comparator(Comparator* rcomp) { - _assert_(rcomp); - if (omode_ != 0) { - set_error(_KCCODELINE_, Error::INVALID, "already opened"); - return false; - } - reccomp_.comp = rcomp; - return true; - } - /** - * Get the opaque data. - * @return the pointer to the opaque data region, whose size is 16 bytes. - */ - char* opaque() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return NULL; - } - return db_.opaque(); - } - /** - * Synchronize the opaque data. - * @return true on success, or false on failure. - */ - bool synchronize_opaque() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - return db_.synchronize_opaque(); - } - /** - * Perform defragmentation of the file. - * @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. - */ - bool defrag(int64_t step = 0) { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return false; - } - bool err = false; - if (step < 1 && writer_) { - if (!clean_leaf_cache()) err = true; - if (!clean_inner_cache()) err = true; - } - if (!db_.defrag(step)) err = true; - return !err; - } - /** - * Get the status flags. - * @return the status flags, or 0 on failure. - */ - uint8_t flags() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return 0; - } - return db_.flags(); - } - /** - * Get the record comparator. - * @return the record comparator object. - */ - Comparator* rcomp() { - _assert_(true); - if (omode_ == 0) { - set_error(_KCCODELINE_, Error::INVALID, "not opened"); - return 0; - } - return reccomp_.comp; - } - protected: - /** - * Report a message for debugging. - * @param file the file name of the program source code. - * @param line the line number of the program source code. - * @param func the function name of the program source code. - * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal - * information, Logger::WARN for warning, and Logger::ERROR for fatal error. - * @param format the printf-like format string. - * @param ... used according to the format string. - */ - void report(const char* file, int32_t line, const char* func, Logger::Kind kind, - const char* format, ...) { - _assert_(file && line > 0 && func && format); - va_list ap; - va_start(ap, format); - db_.report_valist(file, line, func, kind, format, ap); - va_end(ap); - } - /** - * Report a message for debugging with variable number of arguments. - * @param file the file name of the program source code. - * @param line the line number of the program source code. - * @param func the function name of the program source code. - * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal - * information, Logger::WARN for warning, and Logger::ERROR for fatal error. - * @param format the printf-like format string. - * @param ap used according to the format string. - */ - void report_valist(const char* file, int32_t line, const char* func, Logger::Kind kind, - const char* format, va_list ap) { - _assert_(file && line > 0 && func && format); - db_.report_valist(file, line, func, kind, format, ap); - } - /** - * Report the content of a binary buffer for debugging. - * @param file the file name of the epicenter. - * @param line the line number of the epicenter. - * @param func the function name of the program source code. - * @param kind the kind of the event. Logger::DEBUG for debugging, Logger::INFO for normal - * information, Logger::WARN for warning, and Logger::ERROR for fatal error. - * @param name the name of the information. - * @param buf the binary buffer. - * @param size the size of the binary buffer - */ - void report_binary(const char* file, int32_t line, const char* func, Logger::Kind kind, - const char* name, const char* buf, size_t size) { - _assert_(file && line > 0 && func && name && buf && size <= MEMMAXSIZ); - db_.report_binary(file, line, func, kind, name, buf, size); - } - /** - * Trigger a meta database operation. - * @param kind the kind of the event. MetaTrigger::OPEN for opening, MetaTrigger::CLOSE for - * closing, MetaTrigger::CLEAR for clearing, MetaTrigger::ITERATE for iteration, - * MetaTrigger::SYNCHRONIZE for synchronization, MetaTrigger::BEGINTRAN for beginning - * transaction, MetaTrigger::COMMITTRAN for committing transaction, MetaTrigger::ABORTTRAN - * for aborting transaction, and MetaTrigger::MISC for miscellaneous operations. - * @param message the supplement message. - */ - void trigger_meta(MetaTrigger::Kind kind, const char* message) { - _assert_(message); - if (mtrigger_) mtrigger_->trigger(kind, message); - } - private: - /** - * Record data. - */ - struct Record { - uint32_t ksiz; ///< size of the key - uint32_t vsiz; ///< size of the value - }; - /** - * Comparator for records. - */ - struct RecordComparator { - Comparator* comp; ///< comparator - /** constructor */ - explicit RecordComparator() : comp(NULL) {} - /** comparing operator */ - bool operator ()(const Record* const& a, const Record* const& b) const { - _assert_(true); - char* akbuf = (char*)a + sizeof(*a); - char* bkbuf = (char*)b + sizeof(*b); - return comp->compare(akbuf, a->ksiz, bkbuf, b->ksiz) < 0; - } - }; - /** - * Leaf node of B+ tree. - */ - struct LeafNode { - int64_t id; ///< page ID number - RecordArray recs; ///< sorted array of records - int64_t size; ///< total size of records - int64_t prev; ///< previous leaf node - int64_t next; ///< next leaf node - bool hot; ///< whether in the hot cache - bool dirty; ///< whether to be written back - bool dead; ///< whether to be removed - }; - /** - * Link to a node. - */ - struct Link { - int64_t child; ///< child node - int32_t ksiz; ///< size of the key - }; - /** - * Comparator for links. - */ - struct LinkComparator { - Comparator* comp; ///< comparator - /** constructor */ - explicit LinkComparator() : comp(NULL) { - _assert_(true); - } - /** comparing operator */ - bool operator ()(const Link* const& a, const Link* const& b) const { - _assert_(true); - char* akbuf = (char*)a + sizeof(*a); - char* bkbuf = (char*)b + sizeof(*b); - return comp->compare(akbuf, a->ksiz, bkbuf, b->ksiz) < 0; - } - }; - /** - * Inner node of B+ tree. - */ - struct InnerNode { - int64_t id; ///< page ID numger - int64_t heir; ///< child before the first link - LinkArray links; ///< sorted array of links - int64_t size; ///< total size of links - bool dirty; ///< whether to be written back - bool dead; ///< whether to be removed - }; - /** - * Slot cache of leaf nodes. - */ - struct LeafSlot { - Mutex lock; ///< lock - LeafCache* hot; ///< hot cache - LeafCache* warm; ///< warm cache - }; - /** - * Slot cache of inner nodes. - */ - struct InnerSlot { - Mutex lock; ///< lock - InnerCache* warm; ///< warm cache - }; - /** - * Scoped visitor. - */ - class ScopedVisitor { - public: - /** constructor */ - explicit ScopedVisitor(Visitor* visitor) : visitor_(visitor) { - _assert_(visitor); - visitor_->visit_before(); - } - /** destructor */ - ~ScopedVisitor() { - _assert_(true); - visitor_->visit_after(); - } - private: - Visitor* visitor_; ///< visitor - }; - /** - * Open the leaf cache. - */ - void create_leaf_cache() { - _assert_(true); - int64_t bnum = bnum_ / SLOTNUM + 1; - if (bnum < INT8MAX) bnum = INT8MAX; - bnum = nearbyprime(bnum); - for (int32_t i = 0; i < SLOTNUM; i++) { - lslots_[i].hot = new LeafCache(bnum); - lslots_[i].warm = new LeafCache(bnum); - } - } - /** - * Close the leaf cache. - */ - void delete_leaf_cache() { - _assert_(true); - for (int32_t i = SLOTNUM - 1; i >= 0; i--) { - LeafSlot* slot = lslots_ + i; - delete slot->warm; - delete slot->hot; - } - } - /** - * Remove all leaf nodes from the leaf cache. - * @param save whether to save dirty nodes. - * @return true on success, or false on failure. - */ - bool flush_leaf_cache(bool save) { - _assert_(true); - bool err = false; - for (int32_t i = SLOTNUM - 1; i >= 0; i--) { - LeafSlot* slot = lslots_ + i; - typename LeafCache::Iterator it = slot->warm->begin(); - typename LeafCache::Iterator itend = slot->warm->end(); - while (it != itend) { - LeafNode* node = it.value(); - ++it; - if (!flush_leaf_node(node, save)) err = true; - } - it = slot->hot->begin(); - itend = slot->hot->end(); - while (it != itend) { - LeafNode* node = it.value(); - ++it; - if (!flush_leaf_node(node, save)) err = true; - } - } - return !err; - } - /** - * Flush a part of the leaf cache. - * @param slot a slot of leaf nodes. - * @return true on success, or false on failure. - */ - bool flush_leaf_cache_part(LeafSlot* slot) { - _assert_(slot); - bool err = false; - if (slot->warm->count() > 0) { - LeafNode* node = slot->warm->first_value(); - if (!flush_leaf_node(node, true)) err = true; - } else if (slot->hot->count() > 0) { - LeafNode* node = slot->hot->first_value(); - if (!flush_leaf_node(node, true)) err = true; - } - return !err; - } - /** - * Clean all of the leaf cache. - * @return true on success, or false on failure. - */ - bool clean_leaf_cache() { - _assert_(true); - bool err = false; - for (int32_t i = 0; i < SLOTNUM; i++) { - LeafSlot* slot = lslots_ + i; - ScopedMutex lock(&slot->lock); - typename LeafCache::Iterator it = slot->warm->begin(); - typename LeafCache::Iterator itend = slot->warm->end(); - while (it != itend) { - LeafNode* node = it.value(); - if (!save_leaf_node(node)) err = true; - ++it; - } - it = slot->hot->begin(); - itend = slot->hot->end(); - while (it != itend) { - LeafNode* node = it.value(); - if (!save_leaf_node(node)) err = true; - ++it; - } - } - return !err; - } - /** - * Clean a part of the leaf cache. - * @param slot a slot of leaf nodes. - * @return true on success, or false on failure. - */ - bool clean_leaf_cache_part(LeafSlot* slot) { - _assert_(slot); - bool err = false; - ScopedMutex lock(&slot->lock); - if (slot->warm->count() > 0) { - LeafNode* node = slot->warm->first_value(); - if (!save_leaf_node(node)) err = true; - } else if (slot->hot->count() > 0) { - LeafNode* node = slot->hot->first_value(); - if (!save_leaf_node(node)) err = true; - } - return !err; - } - /** - * Create a new leaf node. - * @param prev the ID of the previous node. - * @param next the ID of the next node. - * @return the created leaf node. - */ - LeafNode* create_leaf_node(int64_t prev, int64_t next) { - _assert_(true); - LeafNode* node = new LeafNode; - node->id = ++lcnt_; - node->size = sizeof(int32_t) * 2; - node->recs.reserve(DEFLINUM); - node->prev = prev; - node->next = next; - node->hot = false; - node->dirty = true; - node->dead = false; - int32_t sidx = node->id % SLOTNUM; - LeafSlot* slot = lslots_ + sidx; - slot->warm->set(node->id, node, LeafCache::MLAST); - cusage_ += node->size; - return node; - } - /** - * Remove a leaf node from the cache. - * @param node the leaf node. - * @param save whether to save dirty node. - * @return true on success, or false on failure. - */ - bool flush_leaf_node(LeafNode* node, bool save) { - _assert_(node); - bool err = false; - if (save && !save_leaf_node(node)) err = true; - typename RecordArray::const_iterator rit = node->recs.begin(); - typename RecordArray::const_iterator ritend = node->recs.end(); - while (rit != ritend) { - Record* rec = *rit; - xfree(rec); - ++rit; - } - int32_t sidx = node->id % SLOTNUM; - LeafSlot* slot = lslots_ + sidx; - if (node->hot) { - slot->hot->remove(node->id); - } else { - slot->warm->remove(node->id); - } - cusage_ -= node->size; - delete node; - return !err; - } - /** - * Save a leaf node. - * @param node the leaf node. - * @return true on success, or false on failure. - */ - bool save_leaf_node(LeafNode* node) { - _assert_(node); - if (!node->dirty) return true; - bool err = false; - char hbuf[NUMBUFSIZ]; - size_t hsiz = write_key(hbuf, LNPREFIX, node->id); - if (node->dead) { - if (!db_.remove(hbuf, hsiz) && db_.error().code() != Error::NOREC) err = true; - } else { - char* rbuf = new char[node->size]; - char* wp = rbuf; - wp += writevarnum(wp, node->prev); - wp += writevarnum(wp, node->next); - typename RecordArray::const_iterator rit = node->recs.begin(); - typename RecordArray::const_iterator ritend = node->recs.end(); - while (rit != ritend) { - Record* rec = *rit; - wp += writevarnum(wp, rec->ksiz); - wp += writevarnum(wp, rec->vsiz); - char* dbuf = (char*)rec + sizeof(*rec); - std::memcpy(wp, dbuf, rec->ksiz); - wp += rec->ksiz; - std::memcpy(wp, dbuf + rec->ksiz, rec->vsiz); - wp += rec->vsiz; - ++rit; - } - if (!db_.set(hbuf, hsiz, rbuf, wp - rbuf)) err = true; - delete[] rbuf; - } - node->dirty = false; - return !err; - } - /** - * Load a leaf node. - * @param id the ID number of the leaf node. - * @param prom whether to promote the warm cache. - * @return the loaded leaf node. - */ - LeafNode* load_leaf_node(int64_t id, bool prom) { - _assert_(id > 0); - int32_t sidx = id % SLOTNUM; - LeafSlot* slot = lslots_ + sidx; - ScopedMutex lock(&slot->lock); - LeafNode** np = slot->hot->get(id, LeafCache::MLAST); - if (np) return *np; - if (prom) { - if (slot->hot->count() * WARMRATIO > slot->warm->count() + WARMRATIO) { - slot->hot->first_value()->hot = false; - slot->hot->migrate(slot->hot->first_key(), slot->warm, LeafCache::MLAST); - } - np = slot->warm->migrate(id, slot->hot, LeafCache::MLAST); - if (np) { - (*np)->hot = true; - return *np; - } - } else { - LeafNode** np = slot->warm->get(id, LeafCache::MLAST); - if (np) return *np; - } - char hbuf[NUMBUFSIZ]; - size_t hsiz = write_key(hbuf, LNPREFIX, id); - class VisitorImpl : public DB::Visitor { - public: - explicit VisitorImpl() : node_(NULL) {} - LeafNode* pop() { - return node_; - } - private: - const char* visit_full(const char* kbuf, size_t ksiz, - const char* vbuf, size_t vsiz, size_t* sp) { - uint64_t prev; - size_t step = readvarnum(vbuf, vsiz, &prev); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - uint64_t next; - step = readvarnum(vbuf, vsiz, &next); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - LeafNode* node = new LeafNode; - node->size = sizeof(int32_t) * 2; - node->prev = prev; - node->next = next; - while (vsiz > 1) { - uint64_t rksiz; - step = readvarnum(vbuf, vsiz, &rksiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - uint64_t rvsiz; - step = readvarnum(vbuf, vsiz, &rvsiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - if (vsiz < rksiz + rvsiz) break; - size_t rsiz = sizeof(Record) + rksiz + rvsiz; - Record* rec = (Record*)xmalloc(rsiz); - rec->ksiz = rksiz; - rec->vsiz = rvsiz; - char* dbuf = (char*)rec + sizeof(*rec); - std::memcpy(dbuf, vbuf, rksiz); - vbuf += rksiz; - vsiz -= rksiz; - std::memcpy(dbuf + rksiz, vbuf, rvsiz); - vbuf += rvsiz; - vsiz -= rvsiz; - node->recs.push_back(rec); - node->size += rsiz; - } - if (vsiz != 0) { - typename RecordArray::const_iterator rit = node->recs.begin(); - typename RecordArray::const_iterator ritend = node->recs.end(); - while (rit != ritend) { - Record* rec = *rit; - xfree(rec); - ++rit; - } - delete node; - return NOP; - } - node_ = node; - return NOP; - } - LeafNode* node_; - } visitor; - if (!db_.accept(hbuf, hsiz, &visitor, false)) return NULL; - LeafNode* node = visitor.pop(); - if (!node) return NULL; - node->id = id; - node->hot = false; - node->dirty = false; - node->dead = false; - slot->warm->set(id, node, LeafCache::MLAST); - cusage_ += node->size; - return node; - } - /** - * Check whether a record is in the range of a leaf node. - * @param node the leaf node. - * @param rec the record containing the key only. - * @return true for in range, or false for out of range. - */ - bool check_leaf_node_range(LeafNode* node, Record* rec) { - _assert_(node && rec); - RecordArray& recs = node->recs; - if (recs.empty()) return false; - Record* frec = recs.front(); - Record* lrec = recs.back(); - return !reccomp_(rec, frec) && !reccomp_(lrec, rec); - } - /** - * Accept a visitor at a leaf node. - * @param node the leaf node. - * @param rec the record containing the key only. - * @param visitor a visitor object. - * @return true to reorganize the tree, or false if not. - */ - bool accept_impl(LeafNode* node, Record* rec, Visitor* visitor) { - _assert_(node && rec && visitor); - bool reorg = false; - RecordArray& recs = node->recs; - typename RecordArray::iterator ritend = recs.end(); - typename RecordArray::iterator rit = std::lower_bound(recs.begin(), ritend, rec, reccomp_); - if (rit != ritend && !reccomp_(rec, *rit)) { - Record* 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 + ksiz, rec->vsiz, &vsiz); - if (vbuf == Visitor::REMOVE) { - size_t rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz; - count_ -= 1; - cusage_ -= rsiz; - node->size -= rsiz; - node->dirty = true; - xfree(rec); - recs.erase(rit); - if (recs.empty()) reorg = true; - } else if (vbuf != Visitor::NOP) { - int64_t diff = (int64_t)vsiz - (int64_t)rec->vsiz; - 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 > psiz_ && recs.size() > 1) reorg = true; - } - } else { - const char* kbuf = (char*)rec + sizeof(*rec); - size_t ksiz = rec->ksiz; - size_t vsiz; - const char* vbuf = visitor->visit_empty(kbuf, ksiz, &vsiz); - if (vbuf != Visitor::NOP && vbuf != Visitor::REMOVE) { - size_t rsiz = sizeof(*rec) + ksiz + vsiz; - count_ += 1; - cusage_ += rsiz; - node->size += rsiz; - node->dirty = true; - rec = (Record*)xmalloc(rsiz); - rec->ksiz = ksiz; - rec->vsiz = vsiz; - char* dbuf = (char*)rec + sizeof(*rec); - std::memcpy(dbuf, kbuf, ksiz); - std::memcpy(dbuf + ksiz, vbuf, vsiz); - recs.insert(rit, rec); - if (node->size > psiz_ && recs.size() > 1) reorg = true; - } - } - return reorg; - } - /** - * Devide a leaf node into two. - * @param node the leaf node. - * @return the created node, or NULL on failure. - */ - LeafNode* divide_leaf_node(LeafNode* node) { - _assert_(node); - LeafNode* newnode = create_leaf_node(node->id, node->next); - if (newnode->next > 0) { - LeafNode* nextnode = load_leaf_node(newnode->next, false); - if (!nextnode) { - set_error(_KCCODELINE_, Error::BROKEN, "missing leaf node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)newnode->next); - return NULL; - } - nextnode->prev = newnode->id; - nextnode->dirty = true; - } - node->next = newnode->id; - node->dirty = true; - RecordArray& recs = node->recs; - typename RecordArray::iterator mid = recs.begin() + recs.size() / 2; - typename RecordArray::iterator rit = mid; - typename RecordArray::iterator ritend = recs.end(); - RecordArray& newrecs = newnode->recs; - while (rit != ritend) { - Record* rec = *rit; - newrecs.push_back(rec); - size_t rsiz = sizeof(*rec) + rec->ksiz + rec->vsiz; - node->size -= rsiz; - newnode->size += rsiz; - ++rit; - } - escape_cursors(node->id, node->next, *mid); - recs.erase(mid, ritend); - return newnode; - } - /** - * Open the inner cache. - */ - void create_inner_cache() { - _assert_(true); - int64_t bnum = (bnum_ / AVGWAY) / SLOTNUM + 1; - if (bnum < INT8MAX) bnum = INT8MAX; - bnum = nearbyprime(bnum); - for (int32_t i = 0; i < SLOTNUM; i++) { - islots_[i].warm = new InnerCache(bnum); - } - } - /** - * Close the inner cache. - */ - void delete_inner_cache() { - _assert_(true); - for (int32_t i = SLOTNUM - 1; i >= 0; i--) { - InnerSlot* slot = islots_ + i; - delete slot->warm; - } - } - /** - * Remove all inner nodes from the inner cache. - * @param save whether to save dirty nodes. - * @return true on success, or false on failure. - */ - bool flush_inner_cache(bool save) { - _assert_(true); - bool err = false; - for (int32_t i = SLOTNUM - 1; i >= 0; i--) { - InnerSlot* slot = islots_ + i; - typename InnerCache::Iterator it = slot->warm->begin(); - typename InnerCache::Iterator itend = slot->warm->end(); - while (it != itend) { - InnerNode* node = it.value(); - ++it; - if (!flush_inner_node(node, save)) err = true; - } - } - return !err; - } - /** - * Flush a part of the inner cache. - * @param slot a slot of inner nodes. - * @return true on success, or false on failure. - */ - bool flush_inner_cache_part(InnerSlot* slot) { - _assert_(slot); - bool err = false; - if (slot->warm->count() > 0) { - InnerNode* node = slot->warm->first_value(); - if (!flush_inner_node(node, true)) err = true; - } - return !err; - } - /** - * Clean all of the inner cache. - * @return true on success, or false on failure. - */ - bool clean_inner_cache() { - _assert_(true); - bool err = false; - for (int32_t i = 0; i < SLOTNUM; i++) { - InnerSlot* slot = islots_ + i; - ScopedMutex lock(&slot->lock); - typename InnerCache::Iterator it = slot->warm->begin(); - typename InnerCache::Iterator itend = slot->warm->end(); - while (it != itend) { - InnerNode* node = it.value(); - if (!save_inner_node(node)) err = true; - ++it; - } - } - return !err; - } - /** - * Create a new inner node. - * @param heir the ID of the child before the first link. - * @return the created inner node. - */ - InnerNode* create_inner_node(int64_t heir) { - _assert_(true); - InnerNode* node = new InnerNode; - node->id = ++icnt_ + INIDBASE; - node->heir = heir; - node->links.reserve(DEFIINUM); - node->size = sizeof(int64_t); - node->dirty = true; - node->dead = false; - int32_t sidx = node->id % SLOTNUM; - InnerSlot* slot = islots_ + sidx; - slot->warm->set(node->id, node, InnerCache::MLAST); - cusage_ += node->size; - return node; - } - /** - * Remove an inner node from the cache. - * @param node the inner node. - * @param save whether to save dirty node. - * @return true on success, or false on failure. - */ - bool flush_inner_node(InnerNode* node, bool save) { - _assert_(node); - bool err = false; - if (save && !save_inner_node(node)) err = true; - typename LinkArray::const_iterator lit = node->links.begin(); - typename LinkArray::const_iterator litend = node->links.end(); - while (lit != litend) { - Link* link = *lit; - xfree(link); - ++lit; - } - int32_t sidx = node->id % SLOTNUM; - InnerSlot* slot = islots_ + sidx; - slot->warm->remove(node->id); - cusage_ -= node->size; - delete node; - return !err; - } - /** - * Save a inner node. - * @param node the inner node. - * @return true on success, or false on failure. - */ - bool save_inner_node(InnerNode* node) { - _assert_(true); - if (!node->dirty) return true; - bool err = false; - char hbuf[NUMBUFSIZ]; - size_t hsiz = write_key(hbuf, INPREFIX, node->id - INIDBASE); - if (node->dead) { - if (!db_.remove(hbuf, hsiz) && db_.error().code() != Error::NOREC) err = true; - } else { - char* rbuf = new char[node->size]; - char* wp = rbuf; - wp += writevarnum(wp, node->heir); - typename LinkArray::const_iterator lit = node->links.begin(); - typename LinkArray::const_iterator litend = node->links.end(); - while (lit != litend) { - Link* link = *lit; - wp += writevarnum(wp, link->child); - wp += writevarnum(wp, link->ksiz); - char* dbuf = (char*)link + sizeof(*link); - std::memcpy(wp, dbuf, link->ksiz); - wp += link->ksiz; - ++lit; - } - if (!db_.set(hbuf, hsiz, rbuf, wp - rbuf)) err = true; - delete[] rbuf; - } - node->dirty = false; - return !err; - } - /** - * Load an inner node. - * @param id the ID number of the inner node. - * @return the loaded inner node. - */ - InnerNode* load_inner_node(int64_t id) { - _assert_(id > 0); - int32_t sidx = id % SLOTNUM; - InnerSlot* slot = islots_ + sidx; - ScopedMutex lock(&slot->lock); - InnerNode** np = slot->warm->get(id, InnerCache::MLAST); - if (np) return *np; - char hbuf[NUMBUFSIZ]; - size_t hsiz = write_key(hbuf, INPREFIX, id - INIDBASE); - class VisitorImpl : public DB::Visitor { - public: - explicit VisitorImpl() : node_(NULL) {} - InnerNode* pop() { - return node_; - } - private: - const char* visit_full(const char* kbuf, size_t ksiz, - const char* vbuf, size_t vsiz, size_t* sp) { - uint64_t heir; - size_t step = readvarnum(vbuf, vsiz, &heir); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - InnerNode* node = new InnerNode; - node->size = sizeof(int64_t); - node->heir = heir; - while (vsiz > 1) { - uint64_t child; - step = readvarnum(vbuf, vsiz, &child); - if (step < 1) break; - vbuf += step; - vsiz -= step; - uint64_t rksiz; - step = readvarnum(vbuf, vsiz, &rksiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - if (vsiz < rksiz) break; - Link* link = (Link*)xmalloc(sizeof(*link) + rksiz); - link->child = child; - link->ksiz = rksiz; - char* dbuf = (char*)link + sizeof(*link); - std::memcpy(dbuf, vbuf, rksiz); - vbuf += rksiz; - vsiz -= rksiz; - node->links.push_back(link); - node->size += sizeof(*link) + rksiz; - } - if (vsiz != 0) { - typename LinkArray::const_iterator lit = node->links.begin(); - typename LinkArray::const_iterator litend = node->links.end(); - while (lit != litend) { - Link* link = *lit; - xfree(link); - ++lit; - } - delete node; - return NOP; - } - node_ = node; - return NOP; - } - InnerNode* node_; - } visitor; - if (!db_.accept(hbuf, hsiz, &visitor, false)) return NULL; - InnerNode* node = visitor.pop(); - if (!node) return NULL; - node->id = id; - node->dirty = false; - node->dead = false; - slot->warm->set(id, node, InnerCache::MLAST); - cusage_ += node->size; - return node; - } - /** - * Search the B+ tree. - * @param link the link containing the key only. - * @param prom whether to promote the warm cache. - * @param hist the array of visiting history. - * @param hnp the pointer to the variable into which the number of the history is assigned. - * @return the corresponding leaf node, or NULL on failure. - */ - LeafNode* search_tree(Link* link, bool prom, int64_t* hist, int32_t* hnp) { - _assert_(link && hist && hnp); - int64_t id = root_; - int32_t hnum = 0; - while (id > INIDBASE) { - InnerNode* node = load_inner_node(id); - if (!node) { - set_error(_KCCODELINE_, Error::BROKEN, "missing inner node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)id); - return NULL; - } - hist[hnum++] = id; - const LinkArray& links = node->links; - typename LinkArray::const_iterator litbeg = links.begin(); - typename LinkArray::const_iterator litend = links.end(); - typename LinkArray::const_iterator lit = std::upper_bound(litbeg, litend, link, linkcomp_); - if (lit == litbeg) { - id = node->heir; - } else { - --lit; - Link* link = *lit; - id = link->child; - } - } - *hnp = hnum; - return load_leaf_node(id, prom); - } - /** - * Reorganize the B+ tree. - * @param node a leaf node. - * @param hist the array of visiting history. - * @param hnum the number of the history. - * @return true on success, or false on failure. - */ - bool reorganize_tree(LeafNode* node, int64_t* hist, int32_t hnum) { - _assert_(node && hist && hnum >= 0); - if (node->size > psiz_ && node->recs.size() > 1) { - LeafNode* newnode = divide_leaf_node(node); - if (!newnode) return false; - if (node->id == last_) last_ = newnode->id; - int64_t heir = node->id; - int64_t child = newnode->id; - Record* rec = *newnode->recs.begin(); - char* dbuf = (char*)rec + sizeof(*rec); - int32_t ksiz = rec->ksiz; - char* kbuf = new char[ksiz]; - std::memcpy(kbuf, dbuf, ksiz); - while (true) { - if (hnum < 1) { - InnerNode* inode = create_inner_node(heir); - add_link_inner_node(inode, child, kbuf, ksiz); - root_ = inode->id; - delete[] kbuf; - break; - } - int64_t parent = hist[--hnum]; - InnerNode* inode = load_inner_node(parent); - if (!inode) { - set_error(_KCCODELINE_, Error::BROKEN, "missing inner node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)parent); - delete[] kbuf; - return false; - } - add_link_inner_node(inode, child, kbuf, ksiz); - delete[] kbuf; - LinkArray& links = inode->links; - if (inode->size <= psiz_ || links.size() <= INLINKMIN) break; - typename LinkArray::iterator litbeg = links.begin(); - typename LinkArray::iterator mid = litbeg + links.size() / 2; - Link* link = *mid; - InnerNode* newinode = create_inner_node(link->child); - heir = inode->id; - child = newinode->id; - char* dbuf = (char*)link + sizeof(*link); - ksiz = link->ksiz; - kbuf = new char[ksiz]; - std::memcpy(kbuf, dbuf, ksiz); - typename LinkArray::iterator lit = mid + 1; - typename LinkArray::iterator litend = links.end(); - while (lit != litend) { - link = *lit; - char* dbuf = (char*)link + sizeof(*link); - add_link_inner_node(newinode, link->child, dbuf, link->ksiz); - ++lit; - } - int32_t num = newinode->links.size(); - for (int32_t i = 0; i <= num; i++) { - Link* link = links.back(); - size_t rsiz = sizeof(*link) + link->ksiz; - cusage_ -= rsiz; - inode->size -= rsiz; - xfree(link); - links.pop_back(); - } - inode->dirty = true; - } - } else if (node->recs.empty() && hnum > 0) { - if (!escape_cursors(node->id, node->next)) return false; - InnerNode* inode = load_inner_node(hist[--hnum]); - if (!inode) { - set_error(_KCCODELINE_, Error::BROKEN, "missing inner node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long 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(_KCCODELINE_, Error::BROKEN, "missing node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)node->prev); - 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(_KCCODELINE_, Error::BROKEN, "missing node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)node->next); - return false; - } - tnode->prev = node->prev; - tnode->dirty = true; - if (first_ == node->id) first_ = node->next; - } - node->dead = true; - } - } - return true; - } - /** - * Add a link to a inner node. - * @param node the inner node. - * @param child the ID number of the child. - * @param kbuf the pointer to 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) { - _assert_(node && kbuf); - size_t rsiz = sizeof(Link) + ksiz; - Link* link = (Link*)xmalloc(rsiz); - link->child = child; - link->ksiz = ksiz; - char* dbuf = (char*)link + sizeof(*link); - std::memcpy(dbuf, kbuf, ksiz); - LinkArray& links = node->links; - typename LinkArray::iterator litend = links.end(); - typename LinkArray::iterator lit = std::upper_bound(links.begin(), litend, link, linkcomp_); - links.insert(lit, link); - node->size += rsiz; - node->dirty = true; - 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; - typename LinkArray::iterator lit = links.begin(); - typename LinkArray::iterator litend = links.end(); - if (node->heir == child) { - if (!links.empty()) { - 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(_KCCODELINE_, Error::BROKEN, "missing inner node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long long)hist[hnum]); - return false; - } - node->dead = true; - return sub_link_tree(pnode, node->id, hist, hnum); - } - node->dead = true; - root_ = child; - while (child > INIDBASE) { - node = load_inner_node(child); - if (!node) { - set_error(_KCCODELINE_, Error::BROKEN, "missing inner node"); - db_.report(_KCCODELINE_, Logger::WARN, "id=%lld", (long 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(_KCCODELINE_, Error::BROKEN, "invalid tree"); - return false; - } - /** - * Dump the meta data into the file. - * @return true on success, or false on failure. - */ - bool dump_meta() { - _assert_(true); - char head[HEADSIZ]; - std::memset(head, 0, sizeof(head)); - char* wp = head; - if (reccomp_.comp == LEXICALCOMP) { - *(uint8_t*)(wp++) = 0x10; - } else if (reccomp_.comp == DECIMALCOMP) { - *(uint8_t*)(wp++) = 0x11; - } else if (reccomp_.comp == LEXICALDESCCOMP) { - *(uint8_t*)(wp++) = 0x18; - } else if (reccomp_.comp == DECIMALDESCCOMP) { - *(uint8_t*)(wp++) = 0x19; - } else { - *(uint8_t*)(wp++) = 0xff; - } - wp = head + MOFFNUMS; - uint64_t num = hton64(psiz_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(root_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(first_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(last_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(lcnt_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(icnt_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(count_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - num = hton64(bnum_); - std::memcpy(wp, &num, sizeof(num)); - wp += sizeof(num); - std::memcpy(wp, "\x0a\x42\x6f\x6f\x66\x79\x21\x0a", sizeof(num)); - wp += sizeof(num); - if (!db_.set(KCPDBMETAKEY, sizeof(KCPDBMETAKEY) - 1, head, sizeof(head))) return false; - trlcnt_ = lcnt_; - trcount_ = count_; - return true; - } - /** - * Load the meta data from the file. - * @return true on success, or false on failure. - */ - bool load_meta() { - _assert_(true); - char head[HEADSIZ]; - int32_t hsiz = db_.get(KCPDBMETAKEY, sizeof(KCPDBMETAKEY) - 1, head, sizeof(head)); - if (hsiz < 0) return false; - if (hsiz != sizeof(head)) { - set_error(_KCCODELINE_, Error::BROKEN, "invalid meta data record"); - db_.report(_KCCODELINE_, Logger::WARN, "hsiz=%d", hsiz); - return false; - } - const char* rp = head; - if (*(uint8_t*)rp == 0x10) { - reccomp_.comp = LEXICALCOMP; - linkcomp_.comp = LEXICALCOMP; - } else if (*(uint8_t*)rp == 0x11) { - reccomp_.comp = DECIMALCOMP; - linkcomp_.comp = DECIMALCOMP; - } else if (*(uint8_t*)rp == 0x18) { - reccomp_.comp = LEXICALDESCCOMP; - linkcomp_.comp = LEXICALDESCCOMP; - } else if (*(uint8_t*)rp == 0x19) { - reccomp_.comp = DECIMALDESCCOMP; - linkcomp_.comp = DECIMALDESCCOMP; - } else if (*(uint8_t*)rp == 0xff) { - if (!reccomp_.comp) { - set_error(_KCCODELINE_, Error::INVALID, "the custom comparator is not given"); - return false; - } - linkcomp_.comp = reccomp_.comp; - } else { - set_error(_KCCODELINE_, Error::BROKEN, "comparator is invalid"); - return false; - } - rp = head + MOFFNUMS; - uint64_t num; - std::memcpy(&num, rp, sizeof(num)); - psiz_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - root_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - first_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - last_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - lcnt_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - icnt_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - count_ = ntoh64(num); - rp += sizeof(num); - std::memcpy(&num, rp, sizeof(num)); - bnum_ = ntoh64(num); - rp += sizeof(num); - trlcnt_ = lcnt_; - trcount_ = count_; - return true; - } - /** - * Caluculate 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() { - _assert_(true); - int64_t sum = 0; - for (int32_t i = 0; i < SLOTNUM; i++) { - LeafSlot* slot = lslots_ + i; - sum += slot->warm->count(); - sum += slot->hot->count(); - } - return sum; - } - /** - * Caluculate 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() { - _assert_(true); - int64_t sum = 0; - for (int32_t i = 0; i < SLOTNUM; i++) { - LeafSlot* slot = lslots_ + i; - typename LeafCache::Iterator it = slot->warm->begin(); - typename LeafCache::Iterator itend = slot->warm->end(); - while (it != itend) { - LeafNode* node = it.value(); - sum += node->size; - ++it; - } - it = slot->hot->begin(); - itend = slot->hot->end(); - while (it != itend) { - LeafNode* node = it.value(); - sum += node->size; - ++it; - } - } - return sum; - } - /** - * Caluculate 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() { - _assert_(true); - int64_t sum = 0; - for (int32_t i = 0; i < SLOTNUM; i++) { - InnerSlot* slot = islots_ + i; - sum += slot->warm->count(); - } - return sum; - } - /** - * Caluculate 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() { - _assert_(true); - int64_t sum = 0; - for (int32_t i = 0; i < SLOTNUM; i++) { - InnerSlot* slot = islots_ + i; - typename InnerCache::Iterator it = slot->warm->begin(); - typename InnerCache::Iterator itend = slot->warm->end(); - while (it != itend) { - InnerNode* node = it.value(); - sum += node->size; - ++it; - } - } - return sum; - } - /** - * Disable all cursors. - */ - void disable_cursors() { - _assert_(true); - if (curs_.empty()) return; - typename CursorList::const_iterator cit = curs_.begin(); - typename CursorList::const_iterator citend = curs_.end(); - while (cit != citend) { - Cursor* cur = *cit; - if (cur->kbuf_) cur->clear_position(); - ++cit; - } - } - /** - * 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_.empty()) return; - typename CursorList::const_iterator cit = curs_.begin(); - typename 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_.empty()) return true; - bool err = false; - typename CursorList::const_iterator cit = curs_.begin(); - typename CursorList::const_iterator citend = curs_.end(); - while (cit != citend) { - Cursor* cur = *cit; - if (cur->lid_ == src) { - cur->clear_position(); - if (!cur->set_position(dest) && db_.error().code() != Error::NOREC) err = true; - } - ++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; - std::set<int64_t> ids; - std::set<int64_t> prevs; - std::set<int64_t> nexts; - class VisitorImpl : public DB::Visitor { - public: - explicit VisitorImpl(std::set<int64_t>* ids, - std::set<int64_t>* prevs, std::set<int64_t>* nexts) : - ids_(ids), prevs_(prevs), nexts_(nexts), count_(0) {} - int64_t count() { - return count_; - } - private: - const char* visit_full(const char* kbuf, size_t ksiz, - const char* vbuf, size_t vsiz, size_t* sp) { - if (ksiz < 2 || ksiz >= NUMBUFSIZ || kbuf[0] != LNPREFIX) return NOP; - kbuf++; - ksiz--; - char tkbuf[NUMBUFSIZ]; - std::memcpy(tkbuf, kbuf, ksiz); - tkbuf[ksiz] = '\0'; - int64_t id = atoih(tkbuf); - uint64_t prev; - size_t step = readvarnum(vbuf, vsiz, &prev); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - uint64_t next; - step = readvarnum(vbuf, vsiz, &next); - if (step < 1) return NOP; - vbuf += step; - vsiz -= step; - ids_->insert(id); - if (prev > 0) prevs_->insert(prev); - if (next > 0) nexts_->insert(next); - while (vsiz > 1) { - uint64_t rksiz; - step = readvarnum(vbuf, vsiz, &rksiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - uint64_t rvsiz; - step = readvarnum(vbuf, vsiz, &rvsiz); - if (step < 1) break; - vbuf += step; - vsiz -= step; - if (vsiz < rksiz + rvsiz) break; - vbuf += rksiz; - vsiz -= rksiz; - vbuf += rvsiz; - vsiz -= rvsiz; - count_++; - } - return NOP; - } - std::set<int64_t>* ids_; - std::set<int64_t>* prevs_; - std::set<int64_t>* nexts_; - int64_t count_; - } visitor(&ids, &prevs, &nexts); - if (!db_.iterate(&visitor, false)) err = true; - int64_t count = visitor.count(); - db_.report(_KCCODELINE_, Logger::WARN, "recalculated the record count from %lld to %lld", - (long long)count_, (long long)count); - std::set<int64_t>::iterator iitend = ids.end(); - std::set<int64_t>::iterator nit = nexts.begin(); - std::set<int64_t>::iterator nitend = nexts.end(); - while (nit != nitend) { - if (ids.find(*nit) == ids.end()) { - db_.report(_KCCODELINE_, Logger::WARN, "detected missing leaf: %lld", (long long)*nit); - count = INT64MAX; - } - ++nit; - } - std::set<int64_t>::iterator pit = prevs.begin(); - std::set<int64_t>::iterator pitend = prevs.end(); - while (pit != pitend) { - if (ids.find(*pit) == iitend) { - db_.report(_KCCODELINE_, Logger::WARN, "detected missing leaf: %lld", (long long)*pit); - count = INT64MAX; - } - ++pit; - } - count_ = count; - if (!dump_meta()) err = true; - return !err; - } - /** - * Reorganize the database file. - * @param mode the connection mode of the internal database. - * @return true on success, or false on failure. - */ - bool reorganize_file(uint32_t mode) { - _assert_(true); - if (!load_meta()) { - if (reccomp_.comp) { - linkcomp_.comp = reccomp_.comp; - } else { - reccomp_.comp = LEXICALCOMP; - linkcomp_.comp = LEXICALCOMP; - } - } - const std::string& path = db_.path(); - const std::string& npath = path + File::EXTCHR + KCPDBTMPPATHEXT; - PlantDB tdb; - tdb.tune_comparator(reccomp_.comp); - if (!tdb.open(npath, OWRITER | OCREATE | OTRUNCATE)) { - set_error(_KCCODELINE_, tdb.error().code(), "opening the destination failed"); - return false; - } - db_.report(_KCCODELINE_, Logger::WARN, "reorganizing the database"); - bool err = false; - create_leaf_cache(); - create_inner_cache(); - DB::Cursor* cur = db_.cursor(); - cur->jump(); - char* kbuf; - size_t ksiz; - while (!err && (kbuf = cur->get_key(&ksiz)) != NULL) { - if (*kbuf == LNPREFIX) { - int64_t id = std::strtol(kbuf + 1, NULL, 16); - if (id > 0 && id < INIDBASE) { - LeafNode* node = load_leaf_node(id, false); - if (node) { - const RecordArray& recs = node->recs; - typename RecordArray::const_iterator rit = recs.begin(); - typename 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(_KCCODELINE_, 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(_KCCODELINE_, tdb.error().code(), "opening the destination failed"); - err = true; - } - if (DBTYPE == TYPETREE) { - if (File::rename(npath, path)) { - if (!db_.close()) err = true; - if (!db_.open(path, mode)) err = true; - } else { - set_error(_KCCODELINE_, Error::SYSTEM, "renaming the destination failed"); - err = true; - } - File::remove(npath); - } else if (DBTYPE == TYPEFOREST) { - const std::string& tpath = npath + File::EXTCHR + KCPDBTMPPATHEXT; - File::remove_recursively(tpath); - if (File::rename(path, tpath)) { - if (File::rename(npath, path)) { - if (!db_.close()) err = true; - if (!db_.open(path, mode)) err = true; - } else { - set_error(_KCCODELINE_, Error::SYSTEM, "renaming the destination failed"); - File::rename(tpath, path); - err = true; - } - } else { - set_error(_KCCODELINE_, Error::SYSTEM, "renaming the source failed"); - err = true; - } - File::remove_recursively(tpath); - File::remove_recursively(npath); - } else { - BASEDB udb; - if (!err && udb.open(npath, OREADER)) { - if (writer_) { - if (!db_.clear()) err = true; - } else { - if (!db_.close()) err = true; - uint32_t tmode = (mode & ~OREADER) | OWRITER | OCREATE | OTRUNCATE; - if (!db_.open(path, tmode)) err = true; - } - cur = udb.cursor(); - cur->jump(); - const char* vbuf; - size_t vsiz; - while (!err && (kbuf = cur->get(&ksiz, &vbuf, &vsiz)) != NULL) { - if (!db_.set(kbuf, ksiz, vbuf, vsiz)) err = true; - delete[] kbuf; - cur->step(); - } - delete cur; - if (writer_) { - if (!db_.synchronize(false, NULL)) err = true; - } else { - if (!db_.close()) err = true; - if (!db_.open(path, mode)) err = true; - } - if (!udb.close()) { - set_error(_KCCODELINE_, udb.error().code(), "closing the destination failed"); - err = true; - } - } else { - set_error(_KCCODELINE_, udb.error().code(), "opening the destination failed"); - err = true; - } - File::remove_recursively(npath); - } - return !err; - } - /** - * Begin transaction. - * @param hard true for physical synchronization with the device, or false for logical - * synchronization with the file system. - * @return true on success, or false on failure. - */ - bool begin_transaction_impl(bool hard) { - _assert_(true); - if (!clean_leaf_cache()) return false; - if (!clean_inner_cache()) return false; - int32_t idx = trclock_++ % SLOTNUM; - LeafSlot* lslot = lslots_ + idx; - if (lslot->warm->count() + lslot->hot->count() > 1) flush_leaf_cache_part(lslot); - InnerSlot* islot = islots_ + idx; - if (islot->warm->count() > 1) flush_inner_cache_part(islot); - if ((trlcnt_ != lcnt_ || count_ != trcount_) && !dump_meta()) return false; - if (!db_.begin_transaction(hard)) return false; - return true; - } - /** - * Commit transaction. - * @return true on success, or false on failure. - */ - bool commit_transaction() { - _assert_(true); - bool err = false; - if (!clean_leaf_cache()) return false; - if (!clean_inner_cache()) return false; - if ((trlcnt_ != lcnt_ || count_ != trcount_) && !dump_meta()) err = true; - if (!db_.end_transaction(true)) return false; - return !err; - } - /** - * Abort transaction. - * @return true on success, or false on failure. - */ - bool abort_transaction() { - _assert_(true); - bool err = false; - flush_leaf_cache(false); - flush_inner_cache(false); - if (!db_.end_transaction(false)) err = true; - if (!load_meta()) err = true; - disable_cursors(); - 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 (!db_.begin_transaction(autosync_)) return false; - bool err = false; - if (!clean_leaf_cache()) err = true; - if (!clean_inner_cache()) err = true; - size_t cnum = ATRANCNUM / SLOTNUM; - int32_t idx = trclock_++ % SLOTNUM; - 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 (!db_.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); - bool err = false; - if (!save_leaf_node(node)) 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 (!db_.synchronize(true, NULL)) err = true; - return !err; - } - /** - * Write the key pattern into a buffer. - * @param kbuf the destination buffer. - * @param pc the prefix character. - * @param id the ID number of the page. - * @return the size of the key pattern. - */ - size_t write_key(char* kbuf, int32_t pc, int64_t num) { - _assert_(kbuf && num >= 0); - char* wp = kbuf; - *(wp++) = pc; - bool hit = false; - for (size_t i = 0; i < sizeof(num); i++) { - uint8_t c = num >> ((sizeof(num) - 1 - i) * 8); - uint8_t h = c >> 4; - if (h < 10) { - if (hit || h != 0) { - *(wp++) = '0' + h; - hit = true; - } - } else { - *(wp++) = 'A' - 10 + h; - hit = true; - } - uint8_t l = c & 0xf; - if (l < 10) { - if (hit || l != 0) { - *(wp++) = '0' + l; - hit = true; - } - } else { - *(wp++) = 'A' - 10 + l; - hit = true; - } - } - return wp - kbuf; - } - /** Dummy constructor to forbid the use. */ - PlantDB(const PlantDB&); - /** Dummy Operator to forbid the use. */ - PlantDB& operator =(const PlantDB&); - /** The internal meta operation trigger. */ - MetaTrigger* mtrigger_; - /** The open mode. */ - uint32_t omode_; - /** The flag for writer. */ - bool writer_; - /** The flag for auto transaction. */ - bool autotran_; - /** The flag for auto synchronization. */ - bool autosync_; - /** The internal database. */ - BASEDB db_; - /** The cursor objects. */ - CursorList curs_; - /** The alignment power. */ - uint8_t apow_; - /** The free block pool power. */ - uint8_t fpow_; - /** The options. */ - uint8_t opts_; - /** The bucket number. */ - int64_t bnum_; - /** The page size. */ - int32_t psiz_; - /** The capacity of page cache. */ - int64_t pccap_; - /** The root node. */ - int64_t root_; - /** The first node. */ - int64_t first_; - /** The last node. */ - int64_t last_; - /** The count of leaf nodes. */ - int64_t lcnt_; - /** The count of inner nodes. */ - int64_t icnt_; - /** The record number. */ - AtomicInt64 count_; - /** The cache memory usage. */ - AtomicInt64 cusage_; - /** The Slots of leaf nodes. */ - LeafSlot lslots_[SLOTNUM]; - /** The Slots of inner nodes. */ - InnerSlot islots_[SLOTNUM]; - /** The record comparator. */ - RecordComparator reccomp_; - /** The link comparator. */ - LinkComparator linkcomp_; - /** The flag whether in transaction. */ - bool tran_; - /** The logical clock for transaction. */ - int64_t trclock_; - /** The leaf count history for transaction. */ - int64_t trlcnt_; - /** The record count history for transaction. */ - int64_t trcount_; -}; - - -} // common namespace - -#endif // duplication check - -// END OF FILE |