kcplantdb.h   kcplantdb.h 
skipping to change at line 125 skipping to change at line 125
*/ */
class Cursor : public BasicDB::Cursor { class Cursor : public BasicDB::Cursor {
friend class PlantDB; friend class PlantDB;
public: public:
/** /**
* Constructor. * Constructor.
* @param db the container database object. * @param db the container database object.
*/ */
explicit Cursor(PlantDB* db) : db_(db), stack_(), kbuf_(NULL), ksiz_(0) , lid_(0) { explicit Cursor(PlantDB* db) : db_(db), stack_(), kbuf_(NULL), ksiz_(0) , lid_(0) {
_assert_(db); _assert_(db);
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
db_->curs_.push_back(this); db_->curs_.push_back(this);
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Cursor() { virtual ~Cursor() {
_assert_(true); _assert_(true);
if (!db_) return; if (!db_) return;
ScopedSpinRWLock lock(&db_->mlock_, true); ScopedRWLock lock(&db_->mlock_, true);
if (kbuf_) clear_position(); if (kbuf_) clear_position();
db_->curs_.remove(this); db_->curs_.remove(this);
} }
/** /**
* Accept a visitor to the current record. * Accept a visitor to the current record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation. * @param writable true for writable operation, or false for read-only operation.
* @param step true to move the cursor to the next record, or false for no move. * @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation for each record is performed atomically and othe r threads accessing * @note The operation for each record is performed atomically and othe r threads accessing
skipping to change at line 170 skipping to change at line 170
} }
if (!kbuf_) { if (!kbuf_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
db_->mlock_.unlock(); db_->mlock_.unlock();
return false; return false;
} }
bool err = false; bool err = false;
bool hit = false; bool hit = false;
if (lid_ > 0 && !accept_spec(visitor, writable, step, &hit)) err = tr ue; if (lid_ > 0 && !accept_spec(visitor, writable, step, &hit)) err = tr ue;
if (!err && !hit) { if (!err && !hit) {
if (!db_->mlock_.promote()) { db_->mlock_.unlock();
db_->mlock_.unlock(); db_->mlock_.lock_writer();
db_->mlock_.lock_writer();
}
if (kbuf_) { if (kbuf_) {
bool retry = true; bool retry = true;
while (!err && retry) { while (!err && retry) {
if (!accept_atom(visitor, step, &retry)) err = true; if (!accept_atom(visitor, step, &retry)) err = true;
} }
} else { } else {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
err = true; err = true;
} }
} }
db_->mlock_.unlock(); db_->mlock_.unlock();
return !err; return !err;
} }
/** /**
* Jump the cursor to the first record for forward scan. * Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump() { bool jump() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, false); ScopedRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (kbuf_) clear_position(); if (kbuf_) clear_position();
bool err = false; bool err = false;
if (!set_position(db_->first_)) err = true; if (!set_position(db_->first_)) err = true;
return !err; return !err;
} }
/** /**
* Jump the cursor to a record for forward scan. * Jump the cursor to a record for forward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump(const char* kbuf, size_t ksiz) { bool jump(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, false); ScopedRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (kbuf_) clear_position(); if (kbuf_) clear_position();
set_position(kbuf, ksiz, 0); set_position(kbuf, ksiz, 0);
bool err = false; bool err = false;
if (!adjust_position()) { if (!adjust_position()) {
if (kbuf_) clear_position(); if (kbuf_) clear_position();
err = true; err = true;
skipping to change at line 241 skipping to change at line 239
return jump(key.c_str(), key.size()); return jump(key.c_str(), key.size());
} }
/** /**
* Jump the cursor to the last record for backward scan. * Jump the cursor to the last record for backward scan.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note This method is dedicated to tree databases. Some database typ es, especially hash * @note This method is dedicated to tree databases. Some database typ es, especially hash
* databases, may provide a dummy implementation. * databases, may provide a dummy implementation.
*/ */
bool jump_back() { bool jump_back() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&db_->mlock_, false); ScopedRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (kbuf_) clear_position(); if (kbuf_) clear_position();
bool err = false; bool err = false;
if (!set_position_back(db_->last_)) err = true; if (!set_position_back(db_->last_)) err = true;
return !err; return !err;
} }
/** /**
* Jump the cursor to a record for backward scan. * Jump the cursor to a record for backward scan.
* @param kbuf the pointer to the key region. * @param kbuf the pointer to the key region.
* @param ksiz the size of the key region. * @param ksiz the size of the key region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool jump_back(const char* kbuf, size_t ksiz) { bool jump_back(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ); _assert_(kbuf && ksiz <= MEMMAXSIZ);
ScopedSpinRWLock lock(&db_->mlock_, false); ScopedRWLock lock(&db_->mlock_, false);
if (db_->omode_ == 0) { if (db_->omode_ == 0) {
db_->set_error(_KCCODELINE_, Error::INVALID, "not opened"); db_->set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (kbuf_) clear_position(); if (kbuf_) clear_position();
set_position(kbuf, ksiz, 0); set_position(kbuf, ksiz, 0);
bool err = false; bool err = false;
if (adjust_position()) { if (adjust_position()) {
if (db_->reccomp_.comp->compare(kbuf, ksiz, kbuf_, ksiz_) < 0) { if (db_->reccomp_.comp->compare(kbuf, ksiz, kbuf_, ksiz_) < 0) {
bool hit = false; bool hit = false;
if (lid_ > 0 && !back_position_spec(&hit)) err = true; if (lid_ > 0 && !back_position_spec(&hit)) err = true;
if (!err && !hit) { if (!err && !hit) {
if (!db_->mlock_.promote()) { db_->mlock_.unlock();
db_->mlock_.unlock(); db_->mlock_.lock_writer();
db_->mlock_.lock_writer();
}
if (kbuf_) { if (kbuf_) {
if (!back_position_atom()) err = true; if (!back_position_atom()) err = true;
} else { } else {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
err = true; err = true;
} }
} }
} }
} else { } else {
if (kbuf_) clear_position(); if (kbuf_) clear_position();
skipping to change at line 334 skipping to change at line 330
} }
if (!kbuf_) { if (!kbuf_) {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
db_->mlock_.unlock(); db_->mlock_.unlock();
return false; return false;
} }
bool err = false; bool err = false;
bool hit = false; bool hit = false;
if (lid_ > 0 && !back_position_spec(&hit)) err = true; if (lid_ > 0 && !back_position_spec(&hit)) err = true;
if (!err && !hit) { if (!err && !hit) {
if (!db_->mlock_.promote()) { db_->mlock_.unlock();
db_->mlock_.unlock(); db_->mlock_.lock_writer();
db_->mlock_.lock_writer();
}
if (kbuf_) { if (kbuf_) {
if (!back_position_atom()) err = true; if (!back_position_atom()) err = true;
} else { } else {
db_->set_error(_KCCODELINE_, Error::NOREC, "no record"); db_->set_error(_KCCODELINE_, Error::NOREC, "no record");
err = true; err = true;
} }
} }
db_->mlock_.unlock(); db_->mlock_.unlock();
return !err; return !err;
} }
skipping to change at line 566 skipping to change at line 560
node->lock.unlock(); node->lock.unlock();
if (hit && step) { if (hit && step) {
clear_position(); clear_position();
set_position(node->next); set_position(node->next);
} }
if (hit) { if (hit) {
bool flush = db_->cusage_ > db_->pccap_; bool flush = db_->cusage_ > db_->pccap_;
if (link || flush || async) { if (link || flush || async) {
int64_t id = node->id; int64_t id = node->id;
if (atran && !link && !db_->fix_auto_transaction_leaf(node)) er r = true; if (atran && !link && !db_->fix_auto_transaction_leaf(node)) er r = true;
if (!db_->mlock_.promote()) { db_->mlock_.unlock();
db_->mlock_.unlock(); db_->mlock_.lock_writer();
db_->mlock_.lock_writer();
}
if (link) { if (link) {
node = db_->search_tree(link, true, hist, &hnum); node = db_->search_tree(link, true, hist, &hnum);
if (node) { if (node) {
if (!db_->reorganize_tree(node, hist, hnum)) err = true; if (!db_->reorganize_tree(node, hist, hnum)) err = true;
if (atran && !db_->tran_ && !db_->fix_auto_transaction_tree ()) err = true; if (atran && !db_->tran_ && !db_->fix_auto_transaction_tree ()) err = true;
} else { } else {
db_->set_error(_KCCODELINE_, Error::BROKEN, "search failed" ); db_->set_error(_KCCODELINE_, Error::BROKEN, "search failed" );
err = true; err = true;
} }
} else if (flush) { } else if (flush) {
skipping to change at line 1020 skipping to change at line 1012
node->lock.lock_reader(); node->lock.lock_reader();
} }
bool reorg = accept_impl(node, rec, visitor); bool reorg = accept_impl(node, rec, visitor);
bool atran = autotran_ && !tran_ && node->dirty; bool atran = autotran_ && !tran_ && node->dirty;
bool async = autosync_ && !autotran_ && !tran_ && node->dirty; bool async = autosync_ && !autotran_ && !tran_ && node->dirty;
node->lock.unlock(); node->lock.unlock();
bool flush = false; bool flush = false;
bool err = false; bool err = false;
int64_t id = node->id; int64_t id = node->id;
if (atran && !reorg && !fix_auto_transaction_leaf(node)) err = true; if (atran && !reorg && !fix_auto_transaction_leaf(node)) err = true;
if (reorg && mlock_.promote()) { if (cusage_ > pccap_) {
if (!reorganize_tree(node, hist, hnum)) err = true;
if (atran && !fix_auto_transaction_tree()) err = true;
reorg = false;
} else if (cusage_ > pccap_) {
int32_t idx = id % SLOTNUM; int32_t idx = id % SLOTNUM;
LeafSlot* lslot = lslots_ + idx; LeafSlot* lslot = lslots_ + idx;
if (!clean_leaf_cache_part(lslot)) err = true; if (!clean_leaf_cache_part(lslot)) err = true;
if (mlock_.promote()) { flush = true;
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;
} else {
flush = true;
}
} }
mlock_.unlock(); mlock_.unlock();
if (reorg) { if (reorg) {
mlock_.lock_writer(); mlock_.lock_writer();
node = search_tree(link, false, hist, &hnum); node = search_tree(link, false, hist, &hnum);
if (node) { if (node) {
if (!reorganize_tree(node, hist, hnum)) err = true; if (!reorganize_tree(node, hist, hnum)) err = true;
if (atran && !tran_ && !fix_auto_transaction_tree()) err = true; if (atran && !tran_ && !fix_auto_transaction_tree()) err = true;
} }
mlock_.unlock(); mlock_.unlock();
skipping to change at line 1078 skipping to change at line 1059
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operations for specified records are performed atomically an d other threads * @note The operations for specified records are performed atomically an d other threads
* accessing the same records are blocked. To avoid deadlock, any explic it database operation * accessing the same records are blocked. To avoid deadlock, any explic it database operation
* must not be performed in this function. * must not be performed in this function.
*/ */
bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor, bool accept_bulk(const std::vector<std::string>& keys, Visitor* visitor,
bool writable = true) { bool writable = true) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
if (keys.empty()) return true; if (keys.empty()) return true;
skipping to change at line 1128 skipping to change at line 1109
bool reorg = accept_impl(node, rec, visitor); bool reorg = accept_impl(node, rec, visitor);
bool atran = autotran_ && !tran_ && node->dirty; bool atran = autotran_ && !tran_ && node->dirty;
bool async = autosync_ && !autotran_ && !tran_ && node->dirty; bool async = autosync_ && !autotran_ && !tran_ && node->dirty;
if (atran && !reorg && !fix_auto_transaction_leaf(node)) err = true; if (atran && !reorg && !fix_auto_transaction_leaf(node)) err = true;
if (reorg) { if (reorg) {
if (!reorganize_tree(node, hist, hnum)) err = true; if (!reorganize_tree(node, hist, hnum)) err = true;
if (atran && !fix_auto_transaction_tree()) err = true; if (atran && !fix_auto_transaction_tree()) err = true;
} else if (cusage_ > pccap_) { } else if (cusage_ > pccap_) {
int32_t idx = node->id % SLOTNUM; int32_t idx = node->id % SLOTNUM;
LeafSlot* lslot = lslots_ + idx; LeafSlot* lslot = lslots_ + idx;
if (!clean_leaf_cache_part(lslot)) err = true;
if (!flush_leaf_cache_part(lslot)) err = true; if (!flush_leaf_cache_part(lslot)) err = true;
InnerSlot* islot = islots_ + idx; InnerSlot* islot = islots_ + idx;
if (islot->warm->count() > lslot->warm->count() + lslot->hot->count () + 1 && if (islot->warm->count() > lslot->warm->count() + lslot->hot->count () + 1 &&
!flush_inner_cache_part(islot)) err = true; !flush_inner_cache_part(islot)) err = true;
} }
if (rbuf != rstack) delete[] rbuf; if (rbuf != rstack) delete[] rbuf;
if (lbuf != lstack) delete[] lbuf; if (lbuf != lstack) delete[] lbuf;
if (async && !fix_auto_synchronization()) err = true; if (async && !fix_auto_synchronization()) err = true;
++kit; ++kit;
} }
skipping to change at line 1152 skipping to change at line 1132
* Iterate to accept a visitor for each record. * Iterate to accept a visitor for each record.
* @param visitor a visitor object. * @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only op eration. * @param writable true for writable operation, or false for read-only op eration.
* @param checker a progress checker object. If it is NULL, no checking is performed. * @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads ar e blocked. To avoid * @note The whole iteration is performed atomically and other threads ar e blocked. To avoid
* deadlock, any explicit database operation must not be performed in thi s function. * deadlock, any explicit database operation must not be performed in thi s function.
*/ */
bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) { bool iterate(Visitor *visitor, bool writable = true, ProgressChecker* che cker = NULL) {
_assert_(visitor); _assert_(visitor);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (writable && !writer_) { if (writable && !writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
ScopedVisitor svis(visitor); ScopedVisitor svis(visitor);
int64_t allcnt = count_; int64_t allcnt = count_;
skipping to change at line 1304 skipping to change at line 1284
* BasicDB::OTRYLOCK, which means locking is performed without blocking, BasicDB::ONOREPAIR, * BasicDB::OTRYLOCK, which means locking is performed without blocking, BasicDB::ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is * which means the database file is not repaired implicitly even if file destruction is
* detected. * detected.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Every opened database must be closed by the BasicDB::close metho d when it is no * @note Every opened database must be closed by the BasicDB::close metho d when it is no
* longer in use. It is not allowed for two or more database objects in the same process to * longer in use. It is not allowed for two or more database objects in the same process to
* keep their connections to the same database file at the same time. * keep their connections to the same database file at the same time.
*/ */
bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) { bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "opening the database (path=%s)", p ath.c_str());
if (DBTYPE == TYPEGRASS) { if (DBTYPE == TYPEGRASS) {
mode &= ~OREADER; mode &= ~OREADER;
mode |= OWRITER | OCREATE; mode |= OWRITER | OCREATE;
} }
writer_ = false; writer_ = false;
skipping to change at line 1403 skipping to change at line 1383
trclock_ = 0; trclock_ = 0;
trigger_meta(MetaTrigger::OPEN, "open"); trigger_meta(MetaTrigger::OPEN, "open");
return true; return true;
} }
/** /**
* Close the database file. * Close the database file.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool close() { bool close() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
const std::string& path = db_.path(); const std::string& path = db_.path();
report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath.c_str()); report(_KCCODELINE_, Logger::DEBUG, "closing the database (path=%s)", p ath.c_str());
bool err = false; bool err = false;
disable_cursors(); disable_cursors();
int64_t lsiz = calc_leaf_cache_size(); int64_t lsiz = calc_leaf_cache_size();
int64_t isiz = calc_inner_cache_size(); int64_t isiz = calc_inner_cache_size();
skipping to change at line 1475 skipping to change at line 1455
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!clean_leaf_cache()) err = true; if (!clean_leaf_cache()) err = true;
if (checker && !checker->check("synchronize", "cleaning the inner nod e cache", -1, -1)) { if (checker && !checker->check("synchronize", "cleaning the inner nod e cache", -1, -1)) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!clean_inner_cache()) err = true; if (!clean_inner_cache()) err = true;
if (!mlock_.promote()) { mlock_.unlock();
mlock_.unlock(); mlock_.lock_writer();
mlock_.lock_writer();
}
if (checker && !checker->check("synchronize", "flushing the leaf node cache", -1, -1)) { if (checker && !checker->check("synchronize", "flushing the leaf node cache", -1, -1)) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
mlock_.unlock(); mlock_.unlock();
return false; return false;
} }
if (!flush_leaf_cache(true)) err = true; if (!flush_leaf_cache(true)) err = true;
if (checker && !checker->check("synchronize", "flushing the inner nod e cache", -1, -1)) { if (checker && !checker->check("synchronize", "flushing the inner nod e cache", -1, -1)) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed"); set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
mlock_.unlock(); mlock_.unlock();
return false; return false;
skipping to change at line 1525 skipping to change at line 1503
* Occupy database by locking and do something meanwhile. * Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock. * @param writable true to use writer lock, or false to use reader lock.
* @param proc a processor object. If it is NULL, no processing is perfo rmed. * @param proc a processor object. If it is NULL, no processing is perfo rmed.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing * @note The operation of the processor is performed atomically and other threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not * the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this function. * be performed in this function.
*/ */
bool occupy(bool writable = true, FileProcessor* proc = NULL) { bool occupy(bool writable = true, FileProcessor* proc = NULL) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, writable); ScopedRWLock lock(&mlock_, writable);
bool err = false; bool err = false;
if (proc && !proc->process(db_.path(), count_, db_.size())) { if (proc && !proc->process(db_.path(), count_, db_.size())) {
set_error(_KCCODELINE_, Error::LOGIC, "processing failed"); set_error(_KCCODELINE_, Error::LOGIC, "processing failed");
err = true; err = true;
} }
trigger_meta(MetaTrigger::OCCUPY, "occupy"); trigger_meta(MetaTrigger::OCCUPY, "occupy");
return !err; return !err;
} }
/** /**
* Begin transaction. * Begin transaction.
skipping to change at line 1613 skipping to change at line 1591
mlock_.unlock(); mlock_.unlock();
return true; return true;
} }
/** /**
* End transaction. * End transaction.
* @param commit true to commit the transaction, or false to abort the tr ansaction. * @param commit true to commit the transaction, or false to abort the tr ansaction.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool end_transaction(bool commit = true) { bool end_transaction(bool commit = true) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!tran_) { if (!tran_) {
set_error(_KCCODELINE_, Error::INVALID, "not in transaction"); set_error(_KCCODELINE_, Error::INVALID, "not in transaction");
return false; return false;
} }
bool err = false; bool err = false;
if (commit) { if (commit) {
skipping to change at line 1638 skipping to change at line 1616
tran_ = false; tran_ = false;
trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction"); trigger_meta(commit ? MetaTrigger::COMMITTRAN : MetaTrigger::ABORTTRAN, "end_transaction");
return !err; return !err;
} }
/** /**
* Remove all records. * Remove all records.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool clear() { bool clear() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!writer_) { if (!writer_) {
set_error(_KCCODELINE_, Error::NOPERM, "permission denied"); set_error(_KCCODELINE_, Error::NOPERM, "permission denied");
return false; return false;
} }
disable_cursors(); disable_cursors();
flush_leaf_cache(false); flush_leaf_cache(false);
skipping to change at line 1672 skipping to change at line 1650
cusage_ = 0; cusage_ = 0;
trigger_meta(MetaTrigger::CLEAR, "clear"); trigger_meta(MetaTrigger::CLEAR, "clear");
return !err; return !err;
} }
/** /**
* Get the number of records. * Get the number of records.
* @return the number of records, or -1 on failure. * @return the number of records, or -1 on failure.
*/ */
int64_t count() { int64_t count() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return count_; return count_;
} }
/** /**
* Get the size of the database file. * Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure. * @return the size of the database file in bytes, or -1 on failure.
*/ */
int64_t size() { int64_t size() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return -1; return -1;
} }
return db_.size(); return db_.size();
} }
/** /**
* Get the path of the database file. * Get the path of the database file.
* @return the path of the database file, or an empty string on failure. * @return the path of the database file, or an empty string on failure.
*/ */
std::string path() { std::string path() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return ""; return "";
} }
return db_.path(); return db_.path();
} }
/** /**
* Get the miscellaneous status information. * Get the miscellaneous status information.
* @param strmap a string map to contain the result. * @param strmap a string map to contain the result.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool status(std::map<std::string, std::string>* strmap) { bool status(std::map<std::string, std::string>* strmap) {
_assert_(strmap); _assert_(strmap);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
if (!db_.status(strmap)) return false; if (!db_.status(strmap)) return false;
(*strmap)["type"] = strprintf("%u", (unsigned)DBTYPE); (*strmap)["type"] = strprintf("%u", (unsigned)DBTYPE);
(*strmap)["psiz"] = strprintf("%d", psiz_); (*strmap)["psiz"] = strprintf("%d", psiz_);
(*strmap)["pccap"] = strprintf("%lld", (long long)pccap_); (*strmap)["pccap"] = strprintf("%lld", (long long)pccap_);
const char* compname = "external"; const char* compname = "external";
if (reccomp_.comp == LEXICALCOMP) { if (reccomp_.comp == LEXICALCOMP) {
skipping to change at line 1781 skipping to change at line 1759
* @param file the file name of the program source code. * @param file the file name of the program source code.
* @param line the line number of the program source code. * @param line the line number of the program source code.
* @param func the function name of the program source code. * @param func the function name of the program source code.
* @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal * @param kind the kind of the event. Logger::DEBUG for debugging, Logge r::INFO for normal
* information, Logger::WARN for warning, and Logger::ERROR for fatal err or. * information, Logger::WARN for warning, and Logger::ERROR for fatal err or.
* @param message the supplement message. * @param message the supplement message.
*/ */
void log(const char* file, int32_t line, const char* func, Logger::Kind k ind, void log(const char* file, int32_t line, const char* func, Logger::Kind k ind,
const char* message) { const char* message) {
_assert_(file && line > 0 && func && message); _assert_(file && line > 0 && func && message);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
db_.log(file, line, func, kind, message); db_.log(file, line, func, kind, message);
} }
/** /**
* Set the internal logger. * Set the internal logger.
* @param logger the logger object. * @param logger the logger object.
* @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging, * @param kinds kinds of logged messages by bitwise-or: Logger::DEBUG for debugging,
* Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal * Logger::INFO for normal information, Logger::WARN for warning, and Log ger::ERROR for fatal
* error. * error.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) { bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger:: ERROR) {
_assert_(logger); _assert_(logger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
return db_.tune_logger(logger, kinds); return db_.tune_logger(logger, kinds);
} }
/** /**
* Set the internal meta operation trigger. * Set the internal meta operation trigger.
* @param trigger the trigger object. * @param trigger the trigger object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_meta_trigger(MetaTrigger* trigger) { bool tune_meta_trigger(MetaTrigger* trigger) {
_assert_(trigger); _assert_(trigger);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
mtrigger_ = trigger; mtrigger_ = trigger;
return true; return true;
} }
/** /**
* Set the power of the alignment of record size. * Set the power of the alignment of record size.
* @param apow the power of the alignment of record size. * @param apow the power of the alignment of record size.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_alignment(int8_t apow) { bool tune_alignment(int8_t apow) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
apow_ = apow >= 0 ? apow : DEFAPOW; apow_ = apow >= 0 ? apow : DEFAPOW;
return true; return true;
} }
/** /**
* Set the power of the capacity of the free block pool. * Set the power of the capacity of the free block pool.
* @param fpow the power of the capacity of the free block pool. * @param fpow the power of the capacity of the free block pool.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_fbp(int8_t fpow) { bool tune_fbp(int8_t fpow) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
fpow_ = fpow >= 0 ? fpow : DEFFPOW; fpow_ = fpow >= 0 ? fpow : DEFFPOW;
return true; return true;
} }
/** /**
* Set the optional features. * Set the optional features.
* @param opts the optional features by bitwise-or: BasicDB::TSMALL to us e 32-bit addressing, * @param opts the optional features by bitwise-or: BasicDB::TSMALL to us e 32-bit addressing,
* BasicDB::TLINEAR to use linear collision chaining, BasicDB::TCOMPRESS to compress each * BasicDB::TLINEAR to use linear collision chaining, BasicDB::TCOMPRESS to compress each
* record. * record.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_options(int8_t opts) { bool tune_options(int8_t opts) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
opts_ = opts; opts_ = opts;
return true; return true;
} }
/** /**
* Set the number of buckets of the hash table. * Set the number of buckets of the hash table.
* @param bnum the number of buckets of the hash table. * @param bnum the number of buckets of the hash table.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_buckets(int64_t bnum) { bool tune_buckets(int64_t bnum) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
bnum_ = bnum > 0 ? bnum : DEFBNUM; bnum_ = bnum > 0 ? bnum : DEFBNUM;
return true; return true;
} }
/** /**
* Set the size of each page. * Set the size of each page.
* @param psiz the size of each page. * @param psiz the size of each page.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_page(int32_t psiz) { bool tune_page(int32_t psiz) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
psiz_ = psiz > 0 ? psiz : DEFPSIZ; psiz_ = psiz > 0 ? psiz : DEFPSIZ;
return true; return true;
} }
/** /**
* Set the size of the internal memory-mapped region. * Set the size of the internal memory-mapped region.
* @param msiz the size of the internal memory-mapped region. * @param msiz the size of the internal memory-mapped region.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_map(int64_t msiz) { bool tune_map(int64_t msiz) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
return db_.tune_map(msiz); return db_.tune_map(msiz);
} }
/** /**
* Set the unit step number of auto defragmentation. * Set the unit step number of auto defragmentation.
* @param dfunit the unit step number of auto defragmentation. * @param dfunit the unit step number of auto defragmentation.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_defrag(int64_t dfunit) { bool tune_defrag(int64_t dfunit) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
return db_.tune_defrag(dfunit); return db_.tune_defrag(dfunit);
} }
/** /**
* Set the capacity size of the page cache. * Set the capacity size of the page cache.
* @param pccap the capacity size of the page cache. * @param pccap the capacity size of the page cache.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_page_cache(int64_t pccap) { bool tune_page_cache(int64_t pccap) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
pccap_ = pccap > 0 ? pccap : DEFPCCAP; pccap_ = pccap > 0 ? pccap : DEFPCCAP;
return true; return true;
} }
/** /**
* Set the data compressor. * Set the data compressor.
* @param comp the data compressor object. * @param comp the data compressor object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool tune_compressor(Compressor* comp) { bool tune_compressor(Compressor* comp) {
_assert_(comp); _assert_(comp);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
return db_.tune_compressor(comp); return db_.tune_compressor(comp);
} }
/** /**
* Set the record comparator. * Set the record comparator.
* @param rcomp the record comparator object. * @param rcomp the record comparator object.
* @return true on success, or false on failure. * @return true on success, or false on failure.
* @note Several built-in comparators are provided. LEXICALCOMP for the default lexical * @note Several built-in comparators are provided. LEXICALCOMP for the default lexical
* comparator. DECIMALCOMP for the decimal comparator. LEXICALDESCCOMP for the lexical * comparator. DECIMALCOMP for the decimal comparator. LEXICALDESCCOMP for the lexical
* descending comparator. DECIMALDESCCOMP for the lexical descending com parator. * descending comparator. DECIMALDESCCOMP for the lexical descending com parator.
*/ */
bool tune_comparator(Comparator* rcomp) { bool tune_comparator(Comparator* rcomp) {
_assert_(rcomp); _assert_(rcomp);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ != 0) { if (omode_ != 0) {
set_error(_KCCODELINE_, Error::INVALID, "already opened"); set_error(_KCCODELINE_, Error::INVALID, "already opened");
return false; return false;
} }
reccomp_.comp = rcomp; reccomp_.comp = rcomp;
return true; return true;
} }
/** /**
* Get the opaque data. * Get the opaque data.
* @return the pointer to the opaque data region, whose size is 16 bytes. * @return the pointer to the opaque data region, whose size is 16 bytes.
*/ */
char* opaque() { char* opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return NULL; return NULL;
} }
return db_.opaque(); return db_.opaque();
} }
/** /**
* Synchronize the opaque data. * Synchronize the opaque data.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool synchronize_opaque() { bool synchronize_opaque() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
return db_.synchronize_opaque(); return db_.synchronize_opaque();
} }
/** /**
* Perform defragmentation of the file. * Perform defragmentation of the file.
* @param step the number of steps. If it is not more than 0, the whole region is defraged. * @param step the number of steps. If it is not more than 0, the whole region is defraged.
* @return true on success, or false on failure. * @return true on success, or false on failure.
*/ */
bool defrag(int64_t step = 0) { bool defrag(int64_t step = 0) {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return false; return false;
} }
bool err = false; bool err = false;
if (step < 1 && writer_) { if (step < 1 && writer_) {
if (!clean_leaf_cache()) err = true; if (!clean_leaf_cache()) err = true;
if (!clean_inner_cache()) err = true; if (!clean_inner_cache()) err = true;
} }
if (!db_.defrag(step)) err = true; if (!db_.defrag(step)) err = true;
return !err; return !err;
} }
/** /**
* Get the status flags. * Get the status flags.
* @return the status flags, or 0 on failure. * @return the status flags, or 0 on failure.
*/ */
uint8_t flags() { uint8_t flags() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, false); ScopedRWLock lock(&mlock_, false);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return db_.flags(); return db_.flags();
} }
/** /**
* Get the record comparator. * Get the record comparator.
* @return the record comparator object. * @return the record comparator object.
*/ */
Comparator* rcomp() { Comparator* rcomp() {
_assert_(true); _assert_(true);
ScopedSpinRWLock lock(&mlock_, true); ScopedRWLock lock(&mlock_, true);
if (omode_ == 0) { if (omode_ == 0) {
set_error(_KCCODELINE_, Error::INVALID, "not opened"); set_error(_KCCODELINE_, Error::INVALID, "not opened");
return 0; return 0;
} }
return reccomp_.comp; return reccomp_.comp;
} }
protected: protected:
/** /**
* Report a message for debugging. * Report a message for debugging.
* @param file the file name of the program source code. * @param file the file name of the program source code.
skipping to change at line 3365 skipping to change at line 3343
int64_t count() { int64_t count() {
return count_; return count_;
} }
private: private:
const char* visit_full(const char* kbuf, size_t ksiz, const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) { const char* vbuf, size_t vsiz, size_t* sp) {
if (ksiz < 2 || ksiz >= NUMBUFSIZ || kbuf[0] != LNPREFIX) return NO P; if (ksiz < 2 || ksiz >= NUMBUFSIZ || kbuf[0] != LNPREFIX) return NO P;
kbuf++; kbuf++;
ksiz--; ksiz--;
char tkbuf[NUMBUFSIZ]; char tkbuf[NUMBUFSIZ];
memcpy(tkbuf, kbuf, ksiz); std::memcpy(tkbuf, kbuf, ksiz);
tkbuf[ksiz] = '\0'; tkbuf[ksiz] = '\0';
int64_t id = atoih(tkbuf); int64_t id = atoih(tkbuf);
uint64_t prev; uint64_t prev;
size_t step = readvarnum(vbuf, vsiz, &prev); size_t step = readvarnum(vbuf, vsiz, &prev);
if (step < 1) return NOP; if (step < 1) return NOP;
vbuf += step; vbuf += step;
vsiz -= step; vsiz -= step;
uint64_t next; uint64_t next;
step = readvarnum(vbuf, vsiz, &next); step = readvarnum(vbuf, vsiz, &next);
if (step < 1) return NOP; if (step < 1) return NOP;
skipping to change at line 3656 skipping to change at line 3634
if (!flush_inner_cache(true)) err = true; if (!flush_inner_cache(true)) err = true;
if (!dump_meta()) err = true; if (!dump_meta()) err = true;
if (!db_.synchronize(true, NULL)) err = true; if (!db_.synchronize(true, NULL)) err = true;
return !err; return !err;
} }
/** Dummy constructor to forbid the use. */ /** Dummy constructor to forbid the use. */
PlantDB(const PlantDB&); PlantDB(const PlantDB&);
/** Dummy Operator to forbid the use. */ /** Dummy Operator to forbid the use. */
PlantDB& operator =(const PlantDB&); PlantDB& operator =(const PlantDB&);
/** The method lock. */ /** The method lock. */
SpinRWLock mlock_; RWLock mlock_;
/** The internal meta operation trigger. */ /** The internal meta operation trigger. */
MetaTrigger* mtrigger_; MetaTrigger* mtrigger_;
/** The open mode. */ /** The open mode. */
uint32_t omode_; uint32_t omode_;
/** The flag for writer. */ /** The flag for writer. */
bool writer_; bool writer_;
/** The flag for auto transaction. */ /** The flag for auto transaction. */
bool autotran_; bool autotran_;
/** The flag for auto synchronization. */ /** The flag for auto synchronization. */
bool autosync_; bool autosync_;
 End of changes. 45 change blocks. 
72 lines changed or deleted 49 lines changed or added

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