/*************************************************************************************************
* Database interface
* 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 .
*************************************************************************************************/
#ifndef _KCDB_H // duplication check
#define _KCDB_H
#include
#include
#include
#include
#include
#include
#include
#define KCDBSSMAGICDATA "KCSS\n" ///< The magic data of the snapshot file
namespace kyotocabinet { // common namespace
/**
* Interface of database abstraction.
* @note This class is an abstract class to prescribe the interface of record access.
*/
class DB {
public:
/**
* Interface to access a record.
*/
class Visitor {
public:
/** Special pointer for no operation. */
static const char* const NOP;
/** Special pointer to remove the record. */
static const char* const REMOVE;
/**
* Destructor.
*/
virtual ~Visitor() {
_assert_(true);
}
/**
* Visit a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @return If it is the pointer to a region, the value is replaced by the content. If it
* is Visitor::NOP, nothing is modified. If it is Visitor::REMOVE, the record is removed.
*/
virtual const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ && sp);
return NOP;
}
/**
* Visit a empty record space.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @return If it is the pointer to a region, the value is replaced by the content. If it
* is Visitor::NOP or Visitor::REMOVE, nothing is modified.
*/
virtual const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && sp);
return NOP;
}
/**
* Preprocess the main operations.
*/
virtual void visit_before() {
_assert_(true);
}
/**
* Postprocess the main operations.
*/
virtual void visit_after() {
_assert_(true);
}
};
/**
* Interface of cursor to indicate a record.
*/
class Cursor {
public:
/**
* Destructor.
*/
virtual ~Cursor() {
_assert_(true);
}
/**
* 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.
*/
virtual bool accept(Visitor* visitor, bool writable = true, bool step = false) = 0;
/**
* Set the value of the current record.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure.
*/
virtual bool set_value(const char* vbuf, size_t vsiz, bool step = false) = 0;
/**
* Set the value of the current record.
* @note Equal to the original Cursor::set_value method except that the parameter is
* std::string.
*/
virtual bool set_value_str(const std::string& value, bool step = false) = 0;
/**
* Remove the current record.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned. The cursor is moved to the
* next record implicitly.
*/
virtual bool remove() = 0;
/**
* Get the key of the current record.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the key region of the current record, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
virtual char* get_key(size_t* sp, bool step = false) = 0;
/**
* Get the key of the current record.
* @note Equal to the original Cursor::get_key method except that a parameter is a string to
* contain the result and the return value is bool for success.
*/
virtual bool get_key(std::string* key, bool step = false) = 0;
/**
* Get the value of the current record.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the value region of the current record, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
virtual char* get_value(size_t* sp, bool step = false) = 0;
/**
* Get the value of the current record.
* @note Equal to the original Cursor::get_value method except that a parameter is a string
* to contain the result and the return value is bool for success.
*/
virtual bool get_value(std::string* value, bool step = false) = 0;
/**
* Get a pair of the key and the value of the current record.
* @param ksp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param vbp the pointer to the variable into which the pointer to the value region is
* assigned.
* @param vsp the pointer to the variable into which the size of the value region is
* assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the key region, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero code is
* appended at the end of each region of the key and the value, each region can be treated
* as a C-style string. The return value should be deleted explicitly by the caller with
* the detele[] operator.
*/
virtual char* get(size_t* ksp, const char** vbp, size_t* vsp, bool step = false) = 0;
/**
* Get a pair of the key and the value of the current record.
* @note Equal to the original Cursor::get method except that parameters are strings
* to contain the result and the return value is bool for success.
*/
virtual bool get(std::string* key, std::string* value, bool step = false) = 0;
/**
* Jump the cursor to the first record for forward scan.
* @return true on success, or false on failure.
*/
virtual bool jump() = 0;
/**
* 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.
*/
virtual bool jump(const char* kbuf, size_t ksiz) = 0;
/**
* Jump the cursor to a record for forward scan.
* @note Equal to the original Cursor::jump method except that the parameter is std::string.
*/
virtual bool jump(const std::string& key) = 0;
/**
* 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, will provide a dummy implementation.
*/
virtual bool jump_back() = 0;
/**
* 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.
* @note This method is dedicated to tree databases. Some database types, especially hash
* databases, will provide a dummy implementation.
*/
virtual bool jump_back(const char* kbuf, size_t ksiz) = 0;
/**
* 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.
*/
virtual bool jump_back(const std::string& key) = 0;
/**
* Step the cursor to the next record.
* @return true on success, or false on failure.
*/
virtual bool step() = 0;
/**
* Step the cursor to the previous record.
* @return true on success, or false on failure.
* @note This method is dedicated to tree databases. Some database types, especially hash
* databases, will provide a dummy implementation.
*/
virtual bool step_back() = 0;
/**
* Get the database object.
* @return the database object.
*/
virtual DB* db() = 0;
};
/**
* Destructor.
*/
virtual ~DB() {
_assert_(true);
}
/**
* 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.
*/
virtual bool accept(const char* kbuf, size_t ksiz, Visitor* visitor, bool writable = true) = 0;
/**
* Set the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the value is overwritten.
*/
virtual bool set(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) = 0;
/**
* Set the value of a record.
* @note Equal to the original DB::set method except that the parameters are std::string.
*/
virtual bool set(const std::string& key, const std::string& value) = 0;
/**
* Add a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the record is not modified and false is returned.
*/
virtual bool add(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) = 0;
/**
* Set the value of a record.
* @note Equal to the original DB::add method except that the parameters are std::string.
*/
virtual bool add(const std::string& key, const std::string& value) = 0;
/**
* Replace the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, no new record is created and false is returned.
* If the corresponding record exists, the value is modified.
*/
virtual bool replace(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) = 0;
/**
* Replace the value of a record.
* @note Equal to the original DB::replace method except that the parameters are std::string.
*/
virtual bool replace(const std::string& key, const std::string& value) = 0;
/**
* Append the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the given value is appended at the end of the existing value.
*/
virtual bool append(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) = 0;
/**
* Set the value of a record.
* @note Equal to the original DB::append method except that the parameters are std::string.
*/
virtual bool append(const std::string& key, const std::string& value) = 0;
/**
* Add a number to the numeric integer value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param num the additional number.
* @param orig the origin number if no record corresponds to the key. If it is INT64MIN and
* no record corresponds, this function fails. If it is INT64MAX, the value is set as the
* additional number regardless of the current value.
* @return the result value, or kyotocabinet::INT64MIN on failure.
* @note The value is serialized as an 8-byte binary integer in big-endian order, not a decimal
* string. If existing value is not 8-byte, this function fails.
*/
virtual int64_t increment(const char* kbuf, size_t ksiz, int64_t num, int64_t orig = 0) = 0;
/**
* Add a number to the numeric integer value of a record.
* @note Equal to the original DB::increment method except that the parameter is std::string.
*/
virtual int64_t increment(const std::string& key, int64_t num, int64_t orig = 0) = 0;
/**
* Add a number to the numeric double value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param num the additional number.
* @param orig the origin number if no record corresponds to the key. If it is negative
* infinity and no record corresponds, this function fails. If it is positive infinity, the
* value is set as the additional number regardless of the current value.
* @return the result value, or Not-a-number on failure.
* @note The value is serialized as an 16-byte binary fixed-point number in big-endian order,
* not a decimal string. If existing value is not 16-byte, this function fails.
*/
virtual double increment_double(const char* kbuf, size_t ksiz, double num,
double orig = 0) = 0;
/**
* Add a number to the numeric double value of a record.
* @note Equal to the original DB::increment_double method except that the parameter is
* std::string.
*/
virtual double increment_double(const std::string& key, double num, double orig = 0) = 0;
/**
* Perform compare-and-swap.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param ovbuf the pointer to the old value region. NULL means that no record corresponds.
* @param ovsiz the size of the old value region.
* @param nvbuf the pointer to the new value region. NULL means that the record is removed.
* @param nvsiz the size of new old value region.
* @return true on success, or false on failure.
*/
virtual bool cas(const char* kbuf, size_t ksiz,
const char* ovbuf, size_t ovsiz, const char* nvbuf, size_t nvsiz) = 0;
/**
* Perform compare-and-swap.
* @note Equal to the original DB::cas method except that the parameters are std::string.
*/
virtual bool cas(const std::string& key,
const std::string& ovalue, const std::string& nvalue) = 0;
/**
* Remove a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned.
*/
virtual bool remove(const char* kbuf, size_t ksiz) = 0;
/**
* Remove a record.
* @note Equal to the original DB::remove method except that the parameter is std::string.
*/
virtual bool remove(const std::string& key) = 0;
/**
* Retrieve the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @return the pointer to the value region of the corresponding record, or NULL on failure.
* @note If no record corresponds to the key, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
virtual char* get(const char* kbuf, size_t ksiz, size_t* sp) = 0;
/**
* Retrieve the value of a record.
* @note Equal to the original DB::get method except that the first parameters is the key
* string and the second parameter is a string to contain the result and the return value is
* bool for success.
*/
virtual bool get(const std::string& key, std::string* value) = 0;
/**
* Retrieve the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the buffer into which the value of the corresponding record is
* written.
* @param max the size of the buffer.
* @return the size of the value, or -1 on failure.
*/
virtual int32_t get(const char* kbuf, size_t ksiz, char* vbuf, size_t max) = 0;
/**
* Check the existence of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @return the size of the value, or -1 on failure.
*/
virtual int32_t check(const char* kbuf, size_t ksiz) = 0;
/**
* Check the existence of a record.
* @note Equal to the original DB::check method except that the parameter is std::string.
*/
virtual int32_t check(const std::string& key) = 0;
/**
* Remove all records.
* @return true on success, or false on failure.
*/
virtual bool clear() = 0;
/**
* Get the number of records.
* @return the number of records, or -1 on failure.
*/
virtual int64_t count() = 0;
/**
* 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.
*/
virtual Cursor* cursor() = 0;
};
/**
* Basic implementation of database.
* @note This class is an abstract class to prescribe the interface of file operations and
* provide mix-in methods. This class can be inherited but overwriting methods is forbidden.
* 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.
*/
class BasicDB : public DB {
public:
class Cursor;
class Error;
class ProgressChecker;
class FileProcessor;
class Logger;
class MetaTrigger;
private:
/** The size of the IO buffer. */
static const size_t IOBUFSIZ = 8192;
public:
/**
* Database types.
*/
enum Type {
TYPEVOID = 0x00, ///< void database
TYPEPHASH = 0x10, ///< prototype hash database
TYPEPTREE = 0x11, ///< prototype tree database
TYPESTASH = 0x18, ///< stash database
TYPECACHE = 0x20, ///< cache hash database
TYPEGRASS = 0x21, ///< cache tree database
TYPEHASH = 0x30, ///< file hash database
TYPETREE = 0x31, ///< file tree database
TYPEDIR = 0x40, ///< directory hash database
TYPEFOREST = 0x41, ///< directory tree database
TYPETEXT = 0x50, ///< plain text database
TYPEMISC = 0x80 ///< miscellaneous database
};
/**
* Interface of cursor to indicate a record.
*/
class Cursor : public DB::Cursor {
public:
/**
* Destructor.
*/
virtual ~Cursor() {
_assert_(true);
}
/**
* Set the value of the current record.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @param step true to move the cursor to the next record, or false for no move.
* @return true on success, or false on failure.
*/
bool set_value(const char* vbuf, size_t vsiz, bool step = false) {
_assert_(vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
*sp = vsiz_;
return vbuf_;
}
const char* vbuf_;
size_t vsiz_;
bool ok_;
};
VisitorImpl visitor(vbuf, vsiz);
if (!accept(&visitor, true, step)) return false;
if (!visitor.ok()) return false;
return true;
}
/**
* Set the value of the current record.
* @note Equal to the original Cursor::set_value method except that the parameter is
* std::string.
*/
bool set_value_str(const std::string& value, bool step = false) {
_assert_(true);
return set_value(value.c_str(), value.size(), step);
}
/**
* Remove the current record.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned. The cursor is moved to the
* next record implicitly.
*/
bool remove() {
_assert_(true);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
return REMOVE;
}
bool ok_;
};
VisitorImpl visitor;
if (!accept(&visitor, true, false)) return false;
if (!visitor.ok()) return false;
return true;
}
/**
* Get the key of the current record.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the key region of the current record, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
char* get_key(size_t* sp, bool step = false) {
_assert_(sp);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : kbuf_(NULL), ksiz_(0) {}
char* pop(size_t* sp) {
*sp = ksiz_;
return kbuf_;
}
void clear() {
delete[] kbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
kbuf_ = new char[ksiz+1];
std::memcpy(kbuf_, kbuf, ksiz);
kbuf_[ksiz] = '\0';
ksiz_ = ksiz;
return NOP;
}
char* kbuf_;
size_t ksiz_;
};
VisitorImpl visitor;
if (!accept(&visitor, false, step)) {
visitor.clear();
*sp = 0;
return NULL;
}
size_t ksiz;
char* kbuf = visitor.pop(&ksiz);
if (!kbuf) {
*sp = 0;
return NULL;
}
*sp = ksiz;
return kbuf;
}
/**
* Get the key of the current record.
* @note Equal to the original Cursor::get_key method except that a parameter is a string to
* contain the result and the return value is bool for success.
*/
bool get_key(std::string* key, bool step = false) {
_assert_(key);
size_t ksiz;
char* kbuf = get_key(&ksiz, step);
if (!kbuf) return false;
key->clear();
key->append(kbuf, ksiz);
delete[] kbuf;
return true;
}
/**
* Get the value of the current record.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the value region of the current record, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
char* get_value(size_t* sp, bool step = false) {
_assert_(sp);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* sp) {
*sp = vsiz_;
return vbuf_;
}
void clear() {
delete[] vbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
vbuf_ = new char[vsiz+1];
std::memcpy(vbuf_, vbuf, vsiz);
vbuf_[vsiz] = '\0';
vsiz_ = vsiz;
return NOP;
}
char* vbuf_;
size_t vsiz_;
};
VisitorImpl visitor;
if (!accept(&visitor, false, step)) {
visitor.clear();
*sp = 0;
return NULL;
}
size_t vsiz;
char* vbuf = visitor.pop(&vsiz);
if (!vbuf) {
*sp = 0;
return NULL;
}
*sp = vsiz;
return vbuf;
}
/**
* Get the value of the current record.
* @note Equal to the original Cursor::get_value method except that a parameter is a string
* to contain the result and the return value is bool for success.
*/
bool get_value(std::string* value, bool step = false) {
_assert_(value);
size_t vsiz;
char* vbuf = get_value(&vsiz, step);
if (!vbuf) return false;
value->clear();
value->append(vbuf, vsiz);
delete[] vbuf;
return true;
}
/**
* Get a pair of the key and the value of the current record.
* @param ksp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param vbp the pointer to the variable into which the pointer to the value region is
* assigned.
* @param vsp the pointer to the variable into which the size of the value region is
* assigned.
* @param step true to move the cursor to the next record, or false for no move.
* @return the pointer to the key region, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero code is
* appended at the end of each region of the key and the value, each region can be treated
* as a C-style string. The return value should be deleted explicitly by the caller with
* the detele[] operator.
*/
char* get(size_t* ksp, const char** vbp, size_t* vsp, bool step = false) {
_assert_(ksp && vbp && vsp);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : kbuf_(NULL), ksiz_(0), vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* ksp, const char** vbp, size_t* vsp) {
*ksp = ksiz_;
*vbp = vbuf_;
*vsp = vsiz_;
return kbuf_;
}
void clear() {
delete[] kbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
size_t rsiz = ksiz + 1 + vsiz + 1;
kbuf_ = new char[rsiz];
std::memcpy(kbuf_, kbuf, ksiz);
kbuf_[ksiz] = '\0';
ksiz_ = ksiz;
vbuf_ = kbuf_ + ksiz + 1;
std::memcpy(vbuf_, vbuf, vsiz);
vbuf_[vsiz] = '\0';
vsiz_ = vsiz;
return NOP;
}
char* kbuf_;
size_t ksiz_;
char* vbuf_;
size_t vsiz_;
};
VisitorImpl visitor;
if (!accept(&visitor, false, step)) {
visitor.clear();
*ksp = 0;
*vbp = NULL;
*vsp = 0;
return NULL;
}
return visitor.pop(ksp, vbp, vsp);
}
/**
* Get a pair of the key and the value of the current record.
* @note Equal to the original Cursor::get method except that parameters are strings
* to contain the result and the return value is bool for success.
*/
bool get(std::string* key, std::string* value, bool step = false) {
_assert_(key && value);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::string* key, std::string* value) :
key_(key), value_(value), ok_(false) {}
bool ok() {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
key_->clear();
key_->append(kbuf, ksiz);
value_->clear();
value_->append(vbuf, vsiz);
ok_ = true;
return NOP;
}
std::string* key_;
std::string* value_;
bool ok_;
};
VisitorImpl visitor(key, value);
if (!accept(&visitor, false, step)) return false;
return visitor.ok();
}
/**
* Get a pair of the key and the value of the current record and remove it atomically.
* @param ksp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @param vbp the pointer to the variable into which the pointer to the value region is
* assigned.
* @param vsp the pointer to the variable into which the size of the value region is
* assigned.
* @return the pointer to the key region, or NULL on failure.
* @note If the cursor is invalidated, NULL is returned. Because an additional zero code is
* appended at the end of each region of the key and the value, each region can be treated
* as a C-style string. The return value should be deleted explicitly by the caller with
* the detele[] operator. The cursor is moved to the next record implicitly.
*/
char* seize(size_t* ksp, const char** vbp, size_t* vsp) {
_assert_(ksp && vbp && vsp);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : kbuf_(NULL), ksiz_(0), vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* ksp, const char** vbp, size_t* vsp) {
*ksp = ksiz_;
*vbp = vbuf_;
*vsp = vsiz_;
return kbuf_;
}
void clear() {
delete[] kbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
size_t rsiz = ksiz + 1 + vsiz + 1;
kbuf_ = new char[rsiz];
std::memcpy(kbuf_, kbuf, ksiz);
kbuf_[ksiz] = '\0';
ksiz_ = ksiz;
vbuf_ = kbuf_ + ksiz + 1;
std::memcpy(vbuf_, vbuf, vsiz);
vbuf_[vsiz] = '\0';
vsiz_ = vsiz;
return REMOVE;
}
char* kbuf_;
size_t ksiz_;
char* vbuf_;
size_t vsiz_;
};
VisitorImpl visitor;
if (!accept(&visitor, true, false)) {
visitor.clear();
*ksp = 0;
*vbp = NULL;
*vsp = 0;
return NULL;
}
return visitor.pop(ksp, vbp, vsp);
}
/**
* Get a pair of the key and the value of the current record and remove it atomically.
* @note Equal to the original Cursor::seize method except that parameters are strings
* to contain the result and the return value is bool for success.
*/
bool seize(std::string* key, std::string* value) {
_assert_(key && value);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::string* key, std::string* value) :
key_(key), value_(value), ok_(false) {}
bool ok() {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
key_->clear();
key_->append(kbuf, ksiz);
value_->clear();
value_->append(vbuf, vsiz);
ok_ = true;
return REMOVE;
}
std::string* key_;
std::string* value_;
bool ok_;
};
VisitorImpl visitor(key, value);
if (!accept(&visitor, true, false)) return false;
return visitor.ok();
}
/**
* Get the database object.
* @return the database object.
*/
virtual BasicDB* db() = 0;
/**
* Get the last happened error.
* @return the last happened error.
*/
Error error() {
_assert_(true);
return db()->error();
}
};
/**
* Error data.
*/
class Error {
public:
/**
* Error codes.
*/
enum Code {
SUCCESS, ///< success
NOIMPL, ///< not implemented
INVALID, ///< invalid operation
NOREPOS, ///< no repository
NOPERM, ///< no permission
BROKEN, ///< broken file
DUPREC, ///< record duplication
NOREC, ///< no record
LOGIC, ///< logical inconsistency
SYSTEM, ///< system error
MISC = 15 ///< miscellaneous error
};
/**
* Default constructor.
*/
explicit Error() : code_(SUCCESS), message_("no error") {
_assert_(true);
}
/**
* Copy constructor.
* @param src the source object.
*/
Error(const Error& src) : code_(src.code_), message_(src.message_) {
_assert_(true);
}
/**
* Constructor.
* @param code an error code.
* @param message a supplement message.
*/
explicit Error(Code code, const char* message) : code_(code), message_(message) {
_assert_(message);
}
/**
* Destructor.
*/
~Error() {
_assert_(true);
}
/**
* Set the error information.
* @param code an error code.
* @param message a supplement message.
*/
void set(Code code, const char* message) {
_assert_(message);
code_ = code;
message_ = message;
}
/**
* Get the error code.
* @return the error code.
*/
Code code() const {
_assert_(true);
return code_;
}
/**
* Get the readable string of the code.
* @return the readable string of the code.
*/
const char* name() const {
_assert_(true);
return codename(code_);
}
/**
* Get the supplement message.
* @return the supplement message.
*/
const char* message() const {
_assert_(true);
return message_;
}
/**
* Get the readable string of an error code.
* @param code the error code.
* @return the readable string of the error code.
*/
static const char* codename(Code code) {
_assert_(true);
switch (code) {
case SUCCESS: return "success";
case NOIMPL: return "not implemented";
case INVALID: return "invalid operation";
case NOREPOS: return "no repository";
case NOPERM: return "no permission";
case BROKEN: return "broken file";
case DUPREC: return "record duplication";
case NOREC: return "no record";
case LOGIC: return "logical inconsistency";
case SYSTEM: return "system error";
default: break;
}
return "miscellaneous error";
}
/**
* Assignment operator from the self type.
* @param right the right operand.
* @return the reference to itself.
*/
Error& operator =(const Error& right) {
_assert_(true);
if (&right == this) return *this;
code_ = right.code_;
message_ = right.message_;
return *this;
}
/**
* Cast operator to integer.
* @return the error code.
*/
operator int32_t() const {
return code_;
}
private:
/** The error code. */
Code code_;
/** The supplement message. */
const char* message_;
};
/**
* Interface to check progress status of long process.
*/
class ProgressChecker {
public:
/**
* Destructor.
*/
virtual ~ProgressChecker() {
_assert_(true);
}
/**
* Check the progress status.
* @param name the name of the process.
* @param message a supplement message.
* @param curcnt the count of the current step of the progress, or -1 if not applicable.
* @param allcnt the estimation count of all steps of the progress, or -1 if not applicable.
* @return true to continue the process, or false to stop the process.
*/
virtual bool check(const char* name, const char* message,
int64_t curcnt, int64_t allcnt) = 0;
};
/**
* Interface to process the database file.
*/
class FileProcessor {
public:
/**
* Destructor.
*/
virtual ~FileProcessor() {
_assert_(true);
}
/**
* Process the database file.
* @param path the path of the database file.
* @param count the number of records. A negative value means omission.
* @param size the size of the available region. A negative value means omission.
* @return true on success, or false on failure.
*/
virtual bool process(const std::string& path, int64_t count, int64_t size) = 0;
};
/**
* Interface to log internal information and errors.
*/
class Logger {
public:
/**
* Event kinds.
*/
enum Kind {
DEBUG = 1 << 0, ///< debugging
INFO = 1 << 1, ///< normal information
WARN = 1 << 2, ///< warning
ERROR = 1 << 3 ///< error
};
/**
* Destructor.
*/
virtual ~Logger() {
_assert_(true);
}
/**
* Process 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.
*/
virtual void log(const char* file, int32_t line, const char* func, Kind kind,
const char* message) = 0;
};
/**
* Interface to trigger meta database operations.
*/
class MetaTrigger {
public:
/**
* Event kinds.
*/
enum Kind {
OPEN, ///< opening
CLOSE, ///< closing
CLEAR, ///< clearing
ITERATE, ///< iteration
SYNCHRONIZE, ///< synchronization
OCCUPY, ///< occupation
BEGINTRAN, ///< beginning transaction
COMMITTRAN, ///< committing transaction
ABORTTRAN, ///< aborting transaction
MISC = 15 ///< miscellaneous operation
};
/**
* Destructor.
*/
virtual ~MetaTrigger() {
_assert_(true);
}
/**
* 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::OCCUPY for occupation,
* 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.
*/
virtual void trigger(Kind kind, const char* message) = 0;
};
/**
* Open modes.
*/
enum OpenMode {
OREADER = 1 << 0, ///< open as a reader
OWRITER = 1 << 1, ///< open as a writer
OCREATE = 1 << 2, ///< writer creating
OTRUNCATE = 1 << 3, ///< writer truncating
OAUTOTRAN = 1 << 4, ///< auto transaction
OAUTOSYNC = 1 << 5, ///< auto synchronization
ONOLOCK = 1 << 6, ///< open without locking
OTRYLOCK = 1 << 7, ///< lock without blocking
ONOREPAIR = 1 << 8 ///< open without auto repair
};
/**
* Destructor.
* @note If the database is not closed, it is closed implicitly.
*/
virtual ~BasicDB() {
_assert_(true);
}
/**
* Get the last happened error.
* @return the last happened error.
*/
virtual Error error() const = 0;
/**
* 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.
*/
virtual void set_error(const char* file, int32_t line, const char* func,
Error::Code code, const char* message) = 0;
/**
* 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, File::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.
*/
virtual bool open(const std::string& path, uint32_t mode = OWRITER | OCREATE) = 0;
/**
* Close the database file.
* @return true on success, or false on failure.
*/
virtual bool close() = 0;
/**
* 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.
*/
virtual bool accept_bulk(const std::vector& keys, Visitor* visitor,
bool writable = true) = 0;
/**
* 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.
*/
virtual bool iterate(Visitor *visitor, bool writable = true,
ProgressChecker* checker = NULL) = 0;
/**
* 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.
*/
virtual bool scan_parallel(Visitor *visitor, size_t thnum,
ProgressChecker* checker = NULL) = 0;
/**
* 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.
*/
virtual bool synchronize(bool hard = false, FileProcessor* proc = NULL,
ProgressChecker* checker = NULL) = 0;
/**
* 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.
*/
virtual bool occupy(bool writable = true, FileProcessor* proc = NULL) = 0;
/**
* Create a copy of the database file.
* @param dest the path of the destination file.
* @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure.
*/
bool copy(const std::string& dest, ProgressChecker* checker = NULL) {
_assert_(true);
class FileProcessorImpl : public FileProcessor {
public:
explicit FileProcessorImpl(const std::string& dest, ProgressChecker* checker,
BasicDB* db) :
dest_(dest), checker_(checker), db_(db) {}
private:
bool process(const std::string& path, int64_t count, int64_t size) {
File::Status sbuf;
if (!File::status(path, &sbuf)) return false;
if (sbuf.isdir) {
if (!File::make_directory(dest_)) return false;
bool err = false;
DirStream dir;
if (dir.open(path)) {
if (checker_ && !checker_->check("copy", "beginning", 0, -1)) {
db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
}
std::string name;
int64_t curcnt = 0;
while (!err && dir.read(&name)) {
const std::string& spath = path + File::PATHCHR + name;
const std::string& dpath = dest_ + File::PATHCHR + name;
int64_t dsiz;
char* dbuf = File::read_file(spath, &dsiz);
if (dbuf) {
if (!File::write_file(dpath, dbuf, dsiz)) err = true;
delete[] dbuf;
} else {
err = true;
}
curcnt++;
if (checker_ && !checker_->check("copy", "processing", curcnt, -1)) {
db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
break;
}
}
if (checker_ && !checker_->check("copy", "ending", -1, -1)) {
db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
}
if (!dir.close()) err = true;
} else {
err = true;
}
return !err;
}
std::ofstream ofs;
ofs.open(dest_.c_str(),
std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
if (!ofs) return false;
bool err = false;
std::ifstream ifs;
ifs.open(path.c_str(), std::ios_base::in | std::ios_base::binary);
if (checker_ && !checker_->check("copy", "beginning", 0, size)) {
db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
}
if (ifs) {
char buf[IOBUFSIZ];
int64_t curcnt = 0;
while (!err && !ifs.eof()) {
size_t n = ifs.read(buf, sizeof(buf)).gcount();
if (n > 0) {
ofs.write(buf, n);
if (!ofs) {
err = true;
break;
}
}
curcnt += n;
if (checker_ && !checker_->check("copy", "processing", curcnt, size)) {
db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
break;
}
}
ifs.close();
if (ifs.bad()) err = true;
} else {
err = true;
}
if (checker_ && !checker_->check("copy", "ending", -1, size)) {
db_->set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
}
ofs.close();
if (!ofs) err = true;
return !err;
}
const std::string& dest_;
ProgressChecker* checker_;
BasicDB* db_;
};
FileProcessorImpl proc(dest, checker, this);
return synchronize(false, &proc, checker);
}
/**
* 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.
*/
virtual bool begin_transaction(bool hard = false) = 0;
/**
* 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.
*/
virtual bool begin_transaction_try(bool hard = false) = 0;
/**
* End transaction.
* @param commit true to commit the transaction, or false to abort the transaction.
* @return true on success, or false on failure.
*/
virtual bool end_transaction(bool commit = true) = 0;
/**
* Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure.
*/
virtual int64_t size() = 0;
/**
* Get the path of the database file.
* @return the path of the database file, or an empty string on failure.
*/
virtual std::string path() = 0;
/**
* Get the miscellaneous status information.
* @param strmap a string map to contain the result.
* @return true on success, or false on failure.
*/
virtual bool status(std::map* strmap) = 0;
/**
* Set the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the value is overwritten.
*/
bool set(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) : vbuf_(vbuf), vsiz_(vsiz) {}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
*sp = vsiz_;
return vbuf_;
}
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
*sp = vsiz_;
return vbuf_;
}
const char* vbuf_;
size_t vsiz_;
};
VisitorImpl visitor(vbuf, vsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false;
return true;
}
/**
* Set the value of a record.
* @note Equal to the original DB::set method except that the parameters are std::string.
*/
bool set(const std::string& key, const std::string& value) {
_assert_(true);
return set(key.c_str(), key.size(), value.c_str(), value.size());
}
/**
* Add a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the record is not modified and false is returned.
*/
bool add(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
ok_ = true;
*sp = vsiz_;
return vbuf_;
}
const char* vbuf_;
size_t vsiz_;
bool ok_;
};
VisitorImpl visitor(vbuf, vsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false;
if (!visitor.ok()) {
set_error(_KCCODELINE_, Error::DUPREC, "record duplication");
return false;
}
return true;
}
/**
* Set the value of a record.
* @note Equal to the original DB::add method except that the parameters are std::string.
*/
bool add(const std::string& key, const std::string& value) {
_assert_(true);
return add(key.c_str(), key.size(), value.c_str(), value.size());
}
/**
* Replace the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, no new record is created and false is returned.
* If the corresponding record exists, the value is modified.
*/
bool replace(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
*sp = vsiz_;
return vbuf_;
}
const char* vbuf_;
size_t vsiz_;
bool ok_;
};
VisitorImpl visitor(vbuf, vsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false;
if (!visitor.ok()) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
return false;
}
return true;
}
/**
* Replace the value of a record.
* @note Equal to the original DB::replace method except that the parameters are std::string.
*/
bool replace(const std::string& key, const std::string& value) {
_assert_(true);
return replace(key.c_str(), key.size(), value.c_str(), value.size());
}
/**
* Append the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the value region.
* @param vsiz the size of the value region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the given value is appended at the end of the existing value.
*/
bool append(const char* kbuf, size_t ksiz, const char* vbuf, size_t vsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf && vsiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* vbuf, size_t vsiz) :
vbuf_(vbuf), vsiz_(vsiz), nbuf_(NULL) {}
~VisitorImpl() {
if (nbuf_) delete[] nbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
size_t nsiz = vsiz + vsiz_;
nbuf_ = new char[nsiz];
std::memcpy(nbuf_, vbuf, vsiz);
std::memcpy(nbuf_ + vsiz, vbuf_, vsiz_);
*sp = nsiz;
return nbuf_;
}
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
*sp = vsiz_;
return vbuf_;
}
const char* vbuf_;
size_t vsiz_;
char* nbuf_;
};
VisitorImpl visitor(vbuf, vsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false;
return true;
}
/**
* Set the value of a record.
* @note Equal to the original DB::append method except that the parameters are std::string.
*/
bool append(const std::string& key, const std::string& value) {
_assert_(true);
return append(key.c_str(), key.size(), value.c_str(), value.size());
}
/**
* Add a number to the numeric value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param num the additional number.
* @param orig the origin number if no record corresponds to the key. If it is INT64MIN and
* no record corresponds, this function fails. If it is INT64MAX, the value is set as the
* additional number regardless of the current value.
* @return the result value, or kyotocabinet::INT64MIN on failure.
* @note The value is serialized as an 8-byte binary integer in big-endian order, not a decimal
* string. If existing value is not 8-byte, this function fails.
*/
int64_t increment(const char* kbuf, size_t ksiz, int64_t num, int64_t orig = 0) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(int64_t num, int64_t orig) : num_(num), orig_(orig), big_(0) {}
int64_t num() {
return num_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
if (vsiz != sizeof(num_)) {
num_ = INT64MIN;
return NOP;
}
int64_t onum;
if (orig_ == INT64MAX) {
onum = 0;
} else {
std::memcpy(&onum, vbuf, vsiz);
onum = ntoh64(onum);
if (num_ == 0) {
num_ = onum;
return NOP;
}
}
num_ += onum;
big_ = hton64(num_);
*sp = sizeof(big_);
return (const char*)&big_;
}
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
if (orig_ == INT64MIN) {
num_ = INT64MIN;
return NOP;
}
if (orig_ != INT64MAX) num_ += orig_;
big_ = hton64(num_);
*sp = sizeof(big_);
return (const char*)&big_;
}
int64_t num_;
int64_t orig_;
uint64_t big_;
};
VisitorImpl visitor(num, orig);
if (!accept(kbuf, ksiz, &visitor, num != 0 || orig != INT64MIN)) return INT64MIN;
num = visitor.num();
if (num == INT64MIN) {
set_error(_KCCODELINE_, Error::LOGIC, "logical inconsistency");
return num;
}
return num;
}
/**
* Add a number to the numeric value of a record.
* @note Equal to the original DB::increment method except that the parameter is std::string.
*/
int64_t increment(const std::string& key, int64_t num, int64_t orig = 0) {
_assert_(true);
return increment(key.c_str(), key.size(), num, orig);
}
/**
* Add a number to the numeric double value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param num the additional number.
* @param orig the origin number if no record corresponds to the key. If it is negative
* infinity and no record corresponds, this function fails. If it is positive infinity, the
* value is set as the additional number regardless of the current value.
* @return the result value, or Not-a-number on failure.
* @note The value is serialized as an 16-byte binary fixed-point number in big-endian order,
* not a decimal string. If existing value is not 16-byte, this function fails.
*/
double increment_double(const char* kbuf, size_t ksiz, double num, double orig = 0) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(double num, double orig) :
DECUNIT(1000000000000000LL), num_(num), orig_(orig), buf_() {}
double num() {
return num_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
if (vsiz != sizeof(buf_)) {
num_ = nan();
return NOP;
}
int64_t linteg, lfract;
if (chkinf(orig_) && orig_ >= 0) {
linteg = 0;
lfract = 0;
} else {
std::memcpy(&linteg, vbuf, sizeof(linteg));
linteg = ntoh64(linteg);
std::memcpy(&lfract, vbuf + sizeof(linteg), sizeof(lfract));
lfract = ntoh64(lfract);
}
if (lfract == INT64MIN && linteg == INT64MIN) {
num_ = nan();
return NOP;
} else if (linteg == INT64MAX) {
num_ = HUGE_VAL;
return NOP;
} else if (linteg == INT64MIN) {
num_ = -HUGE_VAL;
return NOP;
}
if (num_ == 0.0 && !(chkinf(orig_) && orig_ >= 0)) {
num_ = linteg + (double)lfract / DECUNIT;
return NOP;
}
long double dinteg;
long double dfract = std::modfl(num_, &dinteg);
if (chknan(dinteg)) {
linteg = INT64MIN;
lfract = INT64MIN;
num_ = nan();
} else if (chkinf(dinteg)) {
linteg = dinteg > 0 ? INT64MAX : INT64MIN;
lfract = 0;
num_ = dinteg;
} else {
linteg += (int64_t)dinteg;
lfract += (int64_t)(dfract * DECUNIT);
if (lfract >= DECUNIT) {
linteg += 1;
lfract -= DECUNIT;
}
num_ = linteg + (double)lfract / DECUNIT;
}
linteg = hton64(linteg);
std::memcpy(buf_, &linteg, sizeof(linteg));
lfract = hton64(lfract);
std::memcpy(buf_ + sizeof(linteg), &lfract, sizeof(lfract));
*sp = sizeof(buf_);
return buf_;
}
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
if (chknan(orig_) || (chkinf(orig_) && orig_ < 0)) {
num_ = nan();
return NOP;
}
if (!chkinf(orig_)) num_ += orig_;
long double dinteg;
long double dfract = std::modfl(num_, &dinteg);
int64_t linteg, lfract;
if (chknan(dinteg)) {
linteg = INT64MIN;
lfract = INT64MIN;
} else if (chkinf(dinteg)) {
linteg = dinteg > 0 ? INT64MAX : INT64MIN;
lfract = 0;
} else {
linteg = (int64_t)dinteg;
lfract = (int64_t)(dfract * DECUNIT);
}
linteg = hton64(linteg);
std::memcpy(buf_, &linteg, sizeof(linteg));
lfract = hton64(lfract);
std::memcpy(buf_ + sizeof(linteg), &lfract, sizeof(lfract));
*sp = sizeof(buf_);
return buf_;
}
const int64_t DECUNIT;
double num_;
double orig_;
char buf_[sizeof(int64_t)*2];
};
VisitorImpl visitor(num, orig);
if (!accept(kbuf, ksiz, &visitor, true)) return nan();
num = visitor.num();
if (chknan(num)) {
set_error(_KCCODELINE_, Error::LOGIC, "logical inconsistency");
return nan();
}
return num;
}
/**
* Add a number to the numeric double value of a record.
* @note Equal to the original DB::increment_double method except that the parameter is
* std::string.
*/
double increment_double(const std::string& key, double num, double orig) {
_assert_(true);
return increment_double(key.c_str(), key.size(), num, orig);
}
/**
* Perform compare-and-swap.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param ovbuf the pointer to the old value region. NULL means that no record corresponds.
* @param ovsiz the size of the old value region.
* @param nvbuf the pointer to the new value region. NULL means that the record is removed.
* @param nvsiz the size of new old value region.
* @return true on success, or false on failure.
*/
bool cas(const char* kbuf, size_t ksiz,
const char* ovbuf, size_t ovsiz, const char* nvbuf, size_t nvsiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const char* ovbuf, size_t ovsiz, const char* nvbuf, size_t nvsiz) :
ovbuf_(ovbuf), ovsiz_(ovsiz), nvbuf_(nvbuf), nvsiz_(nvsiz), ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
if (!ovbuf_ || vsiz != ovsiz_ || std::memcmp(vbuf, ovbuf_, vsiz)) return NOP;
ok_ = true;
if (!nvbuf_) return REMOVE;
*sp = nvsiz_;
return nvbuf_;
}
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
if (ovbuf_) return NOP;
ok_ = true;
if (!nvbuf_) return NOP;
*sp = nvsiz_;
return nvbuf_;
}
const char* ovbuf_;
size_t ovsiz_;
const char* nvbuf_;
size_t nvsiz_;
bool ok_;
};
VisitorImpl visitor(ovbuf, ovsiz, nvbuf, nvsiz);
if (!accept(kbuf, ksiz, &visitor, true)) return false;
if (!visitor.ok()) {
set_error(_KCCODELINE_, Error::LOGIC, "status conflict");
return false;
}
return true;
}
/**
* Perform compare-and-swap.
* @note Equal to the original DB::cas method except that the parameters are std::string.
*/
bool cas(const std::string& key,
const std::string& ovalue, const std::string& nvalue) {
_assert_(true);
return cas(key.c_str(), key.size(),
ovalue.c_str(), ovalue.size(), nvalue.c_str(), nvalue.size());
}
/**
* Remove a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned.
*/
bool remove(const char* kbuf, size_t ksiz) {
_assert_(kbuf && ksiz <= MEMMAXSIZ);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : ok_(false) {}
bool ok() const {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
ok_ = true;
return REMOVE;
}
bool ok_;
};
VisitorImpl visitor;
if (!accept(kbuf, ksiz, &visitor, true)) return false;
if (!visitor.ok()) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
return false;
}
return true;
}
/**
* Remove a record.
* @note Equal to the original DB::remove method except that the parameter is std::string.
*/
bool remove(const std::string& key) {
_assert_(true);
return remove(key.c_str(), key.size());
}
/**
* Retrieve the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @return the pointer to the value region of the corresponding record, or NULL on failure.
* @note If no record corresponds to the key, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
char* get(const char* kbuf, size_t ksiz, size_t* sp) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && sp);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* sp) {
*sp = vsiz_;
return vbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
vbuf_ = new char[vsiz+1];
std::memcpy(vbuf_, vbuf, vsiz);
vbuf_[vsiz] = '\0';
vsiz_ = vsiz;
return NOP;
}
char* vbuf_;
size_t vsiz_;
};
VisitorImpl visitor;
if (!accept(kbuf, ksiz, &visitor, false)) {
*sp = 0;
return NULL;
}
size_t vsiz;
char* vbuf = visitor.pop(&vsiz);
if (!vbuf) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
*sp = 0;
return NULL;
}
*sp = vsiz;
return vbuf;
}
/**
* Retrieve the value of a record.
* @note Equal to the original DB::get method except that the first parameters is the key
* string and the second parameter is a string to contain the result and the return value is
* bool for success.
*/
bool get(const std::string& key, std::string* value) {
_assert_(value);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::string* value) : value_(value), ok_(false) {}
bool ok() {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
value_->clear();
value_->append(vbuf, vsiz);
ok_ = true;
return NOP;
}
std::string* value_;
bool ok_;
};
VisitorImpl visitor(value);
if (!accept(key.data(), key.size(), &visitor, false)) return false;
if (!visitor.ok()) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
return false;
}
return true;
}
/**
* Retrieve the value of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param vbuf the pointer to the buffer into which the value of the corresponding record is
* written.
* @param max the size of the buffer.
* @return the size of the value, or -1 on failure.
*/
int32_t get(const char* kbuf, size_t ksiz, char* vbuf, size_t max) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && vbuf);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(char* vbuf, size_t max) : vbuf_(vbuf), max_(max), vsiz_(-1) {}
int32_t vsiz() {
return vsiz_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
vsiz_ = vsiz;
size_t max = vsiz < max_ ? vsiz : max_;
std::memcpy(vbuf_, vbuf, max);
return NOP;
}
char* vbuf_;
size_t max_;
int32_t vsiz_;
};
VisitorImpl visitor(vbuf, max);
if (!accept(kbuf, ksiz, &visitor, false)) return -1;
int32_t vsiz = visitor.vsiz();
if (vsiz < 0) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
return -1;
}
return vsiz;
}
/**
* Check the existence of a record.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @return the size of the value, or -1 on failure.
*/
int32_t check(const char* kbuf, size_t ksiz) {
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : vsiz_(-1) {}
int32_t vsiz() {
return vsiz_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
vsiz_ = vsiz;
return NOP;
}
size_t vsiz_;
};
VisitorImpl visitor;
if (!accept(kbuf, ksiz, &visitor, false)) return -1;
int32_t vsiz = visitor.vsiz();
if (vsiz < 0) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
return -1;
}
return vsiz;
}
/**
* Check the existence of a record.
* @note Equal to the original DB::check method except that the parameter is std::string.
*/
int32_t check(const std::string& key) {
return check(key.data(), key.size());
}
/**
* Retrieve the value of a record and remove it atomically.
* @param kbuf the pointer to the key region.
* @param ksiz the size of the key region.
* @param sp the pointer to the variable into which the size of the region of the return
* value is assigned.
* @return the pointer to the value region of the corresponding record, or NULL on failure.
* @note If no record corresponds to the key, NULL is returned. Because an additional zero
* code is appended at the end of the region of the return value, the return value can be
* treated as a C-style string. Because the region of the return value is allocated with the
* the new[] operator, it should be released with the delete[] operator when it is no longer
* in use.
*/
char* seize(const char* kbuf, size_t ksiz, size_t* sp) {
_assert_(kbuf && ksiz <= MEMMAXSIZ && sp);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : vbuf_(NULL), vsiz_(0) {}
char* pop(size_t* sp) {
*sp = vsiz_;
return vbuf_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
vbuf_ = new char[vsiz+1];
std::memcpy(vbuf_, vbuf, vsiz);
vbuf_[vsiz] = '\0';
vsiz_ = vsiz;
return REMOVE;
}
char* vbuf_;
size_t vsiz_;
};
VisitorImpl visitor;
if (!accept(kbuf, ksiz, &visitor, true)) {
*sp = 0;
return NULL;
}
size_t vsiz;
char* vbuf = visitor.pop(&vsiz);
if (!vbuf) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
*sp = 0;
return NULL;
}
*sp = vsiz;
return vbuf;
}
/**
* Retrieve the value of a record and remove it atomically.
* @note Equal to the original DB::seize method except that the first parameters is the key
* string and the second parameter is a string to contain the result and the return value is
* bool for success.
*/
bool seize(const std::string& key, std::string* value) {
_assert_(value);
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::string* value) : value_(value), ok_(false) {}
bool ok() {
return ok_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
value_->clear();
value_->append(vbuf, vsiz);
ok_ = true;
return REMOVE;
}
std::string* value_;
bool ok_;
};
VisitorImpl visitor(value);
if (!accept(key.data(), key.size(), &visitor, true)) return false;
if (!visitor.ok()) {
set_error(_KCCODELINE_, Error::NOREC, "no record");
return false;
}
return true;
}
/**
* Store records at once.
* @param recs the records to store.
* @param atomic true to perform all operations atomically, or false for non-atomic operations.
* @return the number of stored records, or -1 on failure.
*/
int64_t set_bulk(const std::map& recs, bool atomic = true) {
_assert_(true);
if (atomic) {
std::vector keys;
keys.reserve(recs.size());
std::map::const_iterator rit = recs.begin();
std::map::const_iterator ritend = recs.end();
while (rit != ritend) {
keys.push_back(rit->first);
++rit;
}
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(const std::map& recs) : recs_(recs) {}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
std::map::const_iterator rit =
recs_.find(std::string(kbuf, ksiz));
if (rit == recs_.end()) return NOP;
*sp = rit->second.size();
return rit->second.data();
}
const char* visit_empty(const char* kbuf, size_t ksiz, size_t* sp) {
std::map::const_iterator rit =
recs_.find(std::string(kbuf, ksiz));
if (rit == recs_.end()) return NOP;
*sp = rit->second.size();
return rit->second.data();
}
const std::map& recs_;
};
VisitorImpl visitor(recs);
if (!accept_bulk(keys, &visitor, true)) return -1;
return keys.size();
}
std::map::const_iterator rit = recs.begin();
std::map::const_iterator ritend = recs.end();
while (rit != ritend) {
if (!set(rit->first.data(), rit->first.size(), rit->second.data(), rit->second.size()))
return -1;
++rit;
}
return recs.size();
}
/**
* Remove records at once.
* @param keys the keys of the records to remove.
* @param atomic true to perform all operations atomically, or false for non-atomic operations.
* @return the number of removed records, or -1 on failure.
*/
int64_t remove_bulk(const std::vector& keys, bool atomic = true) {
_assert_(true);
if (atomic) {
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl() : cnt_(0) {}
int64_t cnt() const {
return cnt_;
}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
cnt_++;
return REMOVE;
}
int64_t cnt_;
};
VisitorImpl visitor;
if (!accept_bulk(keys, &visitor, true)) return -1;
return visitor.cnt();
}
int64_t cnt = 0;
std::vector::const_iterator kit = keys.begin();
std::vector::const_iterator kitend = keys.end();
while (kit != kitend) {
if (remove(kit->data(), kit->size())) {
cnt++;
} else if (error() != Error::NOREC) {
return -1;
}
++kit;
}
return cnt;
}
/**
* Retrieve records at once.
* @param keys the keys of the records to retrieve.
* @param recs a string map to contain the retrieved records.
* @param atomic true to perform all operations atomically, or false for non-atomic operations.
* @return the number of retrieved records, or -1 on failure.
*/
int64_t get_bulk(const std::vector& keys,
std::map* recs, bool atomic = true) {
_assert_(recs);
if (atomic) {
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::map* recs) : recs_(recs) {}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
(*recs_)[std::string(kbuf, ksiz)] = std::string(vbuf, vsiz);
return NOP;
}
std::map* recs_;
};
VisitorImpl visitor(recs);
if (!accept_bulk(keys, &visitor, false)) return -1;
return recs->size();
}
std::vector::const_iterator kit = keys.begin();
std::vector::const_iterator kitend = keys.end();
while (kit != kitend) {
size_t vsiz;
const char* vbuf = get(kit->data(), kit->size(), &vsiz);
if (vbuf) {
(*recs)[*kit] = std::string(vbuf, vsiz);
delete[] vbuf;
} else if (error() != Error::NOREC) {
return -1;
}
++kit;
}
return recs->size();
}
/**
* Dump records into a data stream.
* @param dest the destination stream.
* @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure.
*/
bool dump_snapshot(std::ostream* dest, ProgressChecker* checker = NULL) {
_assert_(dest);
if (dest->fail()) {
set_error(_KCCODELINE_, Error::INVALID, "invalid stream");
return false;
}
class VisitorImpl : public Visitor {
public:
explicit VisitorImpl(std::ostream* dest) : dest_(dest), stack_() {}
private:
const char* visit_full(const char* kbuf, size_t ksiz,
const char* vbuf, size_t vsiz, size_t* sp) {
char* wp = stack_;
*(wp++) = 0x00;
wp += writevarnum(wp, ksiz);
wp += writevarnum(wp, vsiz);
dest_->write(stack_, wp - stack_);
dest_->write(kbuf, ksiz);
dest_->write(vbuf, vsiz);
return NOP;
}
std::ostream* dest_;
char stack_[NUMBUFSIZ*2];
};
VisitorImpl visitor(dest);
bool err = false;
dest->write(KCDBSSMAGICDATA, sizeof(KCDBSSMAGICDATA));
if (iterate(&visitor, false, checker)) {
unsigned char c = 0xff;
dest->write((char*)&c, 1);
if (dest->fail()) {
set_error(_KCCODELINE_, Error::SYSTEM, "stream output error");
err = true;
}
} else {
err = true;
}
return !err;
}
/**
* Dump records into a file.
* @param dest the path of the destination file.
* @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure.
*/
bool dump_snapshot(const std::string& dest, ProgressChecker* checker = NULL) {
_assert_(true);
std::ofstream ofs;
ofs.open(dest.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
if (!ofs) {
set_error(_KCCODELINE_, Error::NOREPOS, "open failed");
return false;
}
bool err = false;
if (!dump_snapshot(&ofs, checker)) err = true;
ofs.close();
if (!ofs) {
set_error(_KCCODELINE_, Error::SYSTEM, "close failed");
err = true;
}
return !err;
}
/**
* Load records from a data stream.
* @param src the source stream.
* @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure.
*/
bool load_snapshot(std::istream* src, ProgressChecker* checker = NULL) {
_assert_(src);
if (src->fail()) {
set_error(_KCCODELINE_, Error::INVALID, "invalid stream");
return false;
}
char buf[IOBUFSIZ];
src->read(buf, sizeof(KCDBSSMAGICDATA));
if (src->fail()) {
set_error(_KCCODELINE_, Error::SYSTEM, "stream input error");
return false;
}
if (std::memcmp(buf, KCDBSSMAGICDATA, sizeof(KCDBSSMAGICDATA))) {
set_error(_KCCODELINE_, Error::INVALID, "invalid magic data of input stream");
return false;
}
bool err = false;
if (checker && !checker->check("load_snapshot", "beginning", 0, -1)) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
}
int64_t curcnt = 0;
while (!err) {
int32_t c = src->get();
if (src->fail()) {
set_error(_KCCODELINE_, Error::SYSTEM, "stream input error");
err = true;
break;
}
if (c == 0xff) break;
if (c == 0x00) {
size_t ksiz = 0;
do {
c = src->get();
ksiz = (ksiz << 7) + (c & 0x7f);
} while (c >= 0x80);
size_t vsiz = 0;
do {
c = src->get();
vsiz = (vsiz << 7) + (c & 0x7f);
} while (c >= 0x80);
size_t rsiz = ksiz + vsiz;
char* rbuf = rsiz > sizeof(buf) ? new char[rsiz] : buf;
src->read(rbuf, ksiz + vsiz);
if (src->fail()) {
set_error(_KCCODELINE_, Error::SYSTEM, "stream input error");
err = true;
if (rbuf != buf) delete[] rbuf;
break;
}
if (!set(rbuf, ksiz, rbuf + ksiz, vsiz)) {
err = true;
if (rbuf != buf) delete[] rbuf;
break;
}
if (rbuf != buf) delete[] rbuf;
} else {
set_error(_KCCODELINE_, Error::INVALID, "invalid magic data of input stream");
err = true;
break;
}
curcnt++;
if (checker && !checker->check("load_snapshot", "processing", curcnt, -1)) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
break;
}
}
if (checker && !checker->check("load_snapshot", "ending", -1, -1)) {
set_error(_KCCODELINE_, Error::LOGIC, "checker failed");
err = true;
}
return !err;
}
/**
* Load records from a file.
* @param src the path of the source file.
* @param checker a progress checker object. If it is NULL, no checking is performed.
* @return true on success, or false on failure.
*/
bool load_snapshot(const std::string& src, ProgressChecker* checker = NULL) {
_assert_(true);
std::ifstream ifs;
ifs.open(src.c_str(), std::ios_base::in | std::ios_base::binary);
if (!ifs) {
set_error(_KCCODELINE_, Error::NOREPOS, "open failed");
return false;
}
bool err = false;
if (!load_snapshot(&ifs, checker)) err = true;
ifs.close();
if (ifs.bad()) {
set_error(_KCCODELINE_, Error::SYSTEM, "close failed");
return false;
}
return !err;
}
/**
* Create a cursor object.
* @return the return value is the created cursor object.
* @note Because the object of the return value is allocated by the constructor, it should be
* released with the delete operator when it is no longer in use.
*/
virtual Cursor* cursor() = 0;
/**
* 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.
*/
virtual void log(const char* file, int32_t line, const char* func, Logger::Kind kind,
const char* message) = 0;
/**
* 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.
*/
virtual bool tune_logger(Logger* logger, uint32_t kinds = Logger::WARN | Logger::ERROR) = 0;
/**
* Set the internal meta operation trigger.
* @param trigger the trigger object.
* @return true on success, or false on failure.
*/
virtual bool tune_meta_trigger(MetaTrigger* trigger) = 0;
/**
* Get the class name of a database type.
* @param type the database type.
* @return the string of the type name.
*/
static const char* typecname(uint32_t type) {
_assert_(true);
switch (type) {
case TYPEVOID: return "void";
case TYPEPHASH: return "ProtoHashDB";
case TYPEPTREE: return "ProtoTreeDB";
case TYPESTASH: return "StashDB";
case TYPECACHE: return "CacheDB";
case TYPEGRASS: return "GrassDB";
case TYPEHASH: return "HashDB";
case TYPETREE: return "TreeDB";
case TYPEDIR: return "DirDB";
case TYPEFOREST: return "ForestDB";
case TYPEMISC: return "misc";
}
return "unknown";
}
/**
* Get the description string of a database type.
* @param type the database type.
* @return the string of the type name.
*/
static const char* typestring(uint32_t type) {
_assert_(true);
switch (type) {
case TYPEVOID: return "void";
case TYPEPHASH: return "prototype hash database";
case TYPEPTREE: return "prototype tree database";
case TYPESTASH: return "stash database";
case TYPECACHE: return "cache hash database";
case TYPEGRASS: return "cache tree database";
case TYPEHASH: return "file hash database";
case TYPETREE: return "file tree database";
case TYPEDIR: return "directory hash database";
case TYPEFOREST: return "directory tree database";
case TYPEMISC: return "miscellaneous database";
}
return "unknown";
}
};
} // common namespace
#endif // duplication check
// END OF FILE