summaryrefslogtreecommitdiff
path: root/plugins/Dbx_kyoto/src/kyotocabinet/kcdb.h
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Dbx_kyoto/src/kyotocabinet/kcdb.h')
-rw-r--r--plugins/Dbx_kyoto/src/kyotocabinet/kcdb.h2520
1 files changed, 2520 insertions, 0 deletions
diff --git a/plugins/Dbx_kyoto/src/kyotocabinet/kcdb.h b/plugins/Dbx_kyoto/src/kyotocabinet/kcdb.h
new file mode 100644
index 0000000000..8abffb4c6b
--- /dev/null
+++ b/plugins/Dbx_kyoto/src/kyotocabinet/kcdb.h
@@ -0,0 +1,2520 @@
+/*************************************************************************************************
+ * 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 <http://www.gnu.org/licenses/>.
+ *************************************************************************************************/
+
+
+#ifndef _KCDB_H // duplication check
+#define _KCDB_H
+
+#include <kccommon.h>
+#include <kcutil.h>
+#include <kcthread.h>
+#include <kcfile.h>
+#include <kccompress.h>
+#include <kccompare.h>
+#include <kcmap.h>
+
+#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<std::string>& 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<std::string, std::string>* 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<std::string, std::string>& recs, bool atomic = true) {
+ _assert_(true);
+ if (atomic) {
+ std::vector<std::string> keys;
+ keys.reserve(recs.size());
+ std::map<std::string, std::string>::const_iterator rit = recs.begin();
+ std::map<std::string, std::string>::const_iterator ritend = recs.end();
+ while (rit != ritend) {
+ keys.push_back(rit->first);
+ ++rit;
+ }
+ class VisitorImpl : public Visitor {
+ public:
+ explicit VisitorImpl(const std::map<std::string, std::string>& 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<std::string, std::string>::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<std::string, std::string>::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<std::string, std::string>& recs_;
+ };
+ VisitorImpl visitor(recs);
+ if (!accept_bulk(keys, &visitor, true)) return -1;
+ return keys.size();
+ }
+ std::map<std::string, std::string>::const_iterator rit = recs.begin();
+ std::map<std::string, std::string>::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<std::string>& 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<std::string>::const_iterator kit = keys.begin();
+ std::vector<std::string>::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<std::string>& keys,
+ std::map<std::string, std::string>* recs, bool atomic = true) {
+ _assert_(recs);
+ if (atomic) {
+ class VisitorImpl : public Visitor {
+ public:
+ explicit VisitorImpl(std::map<std::string, std::string>* 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<std::string, std::string>* recs_;
+ };
+ VisitorImpl visitor(recs);
+ if (!accept_bulk(keys, &visitor, false)) return -1;
+ return recs->size();
+ }
+ std::vector<std::string>::const_iterator kit = keys.begin();
+ std::vector<std::string>::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