diff --git a/.gitignore b/.gitignore index 259148f..8089ac9 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ *.exe *.out *.app + +# Misc +.clang-format diff --git a/lmdb-boost-serialization.hh b/lmdb-boost-serialization.hh index c4ffd1a..5bbc6a6 100644 --- a/lmdb-boost-serialization.hh +++ b/lmdb-boost-serialization.hh @@ -2,37 +2,35 @@ #include "./lmdb-safe.hh" -#include #include +#include -#include #include #include +#include +#include #include #include -#include namespace LMDBSafe { -template -std::string serToString(const T& t) +template std::string serToString(const T &t) { - auto ret = std::string(); - auto inserter = boost::iostreams::back_insert_device(ret); - auto stream = boost::iostreams::stream>(inserter); - auto oa = boost::archive::binary_oarchive(stream, boost::archive::no_header | boost::archive::no_codecvt); - oa << t; - return ret; + auto ret = std::string(); + auto inserter = boost::iostreams::back_insert_device(ret); + auto stream = boost::iostreams::stream>(inserter); + auto oa = boost::archive::binary_oarchive(stream, boost::archive::no_header | boost::archive::no_codecvt); + oa << t; + return ret; } -template -void serFromString(string_view str, T& ret) +template void serFromString(string_view str, T &ret) { - auto source = boost::iostreams::array_source(str.data(), str.size()); - auto stream = boost::iostreams::stream(source); - auto ia = boost::archive::binary_iarchive(stream, boost::archive::no_header|boost::archive::no_codecvt); - ia >> ret; + auto source = boost::iostreams::array_source(str.data(), str.size()); + auto stream = boost::iostreams::stream(source); + auto ia = boost::archive::binary_iarchive(stream, boost::archive::no_header | boost::archive::no_codecvt); + ia >> ret; } -} +} // namespace LMDBSafe diff --git a/lmdb-reflective.hh b/lmdb-reflective.hh index cb5ff3c..4d673fb 100644 --- a/lmdb-reflective.hh +++ b/lmdb-reflective.hh @@ -6,31 +6,29 @@ #include +#include #include #include -#include namespace LMDBSafe { -template -std::string serToString(const T& t) +template std::string serToString(const T &t) { - auto ret = std::string(); - auto inserter = boost::iostreams::back_insert_device(ret); - auto stream = boost::iostreams::stream>(inserter); - auto deserializer = ReflectiveRapidJSON::BinaryReflector::BinarySerializer(&stream); - deserializer.write(t); - return ret; + auto ret = std::string(); + auto inserter = boost::iostreams::back_insert_device(ret); + auto stream = boost::iostreams::stream>(inserter); + auto deserializer = ReflectiveRapidJSON::BinaryReflector::BinarySerializer(&stream); + deserializer.write(t); + return ret; } -template -void serFromString(string_view str, T& ret) +template void serFromString(string_view str, T &ret) { - auto source = boost::iostreams::array_source(str.data(), str.size()); - auto stream = boost::iostreams::stream(source); - auto serializer = ReflectiveRapidJSON::BinaryReflector::BinaryDeserializer(&stream); - ret = T(); - serializer.read(ret); + auto source = boost::iostreams::array_source(str.data(), str.size()); + auto stream = boost::iostreams::stream(source); + auto serializer = ReflectiveRapidJSON::BinaryReflector::BinaryDeserializer(&stream); + ret = T(); + serializer.read(ret); } -} +} // namespace LMDBSafe diff --git a/lmdb-safe.cc b/lmdb-safe.cc index fb8b0bd..0c181e5 100644 --- a/lmdb-safe.cc +++ b/lmdb-safe.cc @@ -3,369 +3,360 @@ #include #include -#include -#include #include #include +#include +#include using namespace std; namespace LMDBSafe { -MDBDbi::MDBDbi(MDB_env* env, MDB_txn* txn, const string_view dbname, unsigned int flags) +MDBDbi::MDBDbi(MDB_env *env, MDB_txn *txn, const string_view dbname, unsigned int flags) { - (void)env; - // A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function. - - if(const auto rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi)) - throw LMDBError("Unable to open named database: ", rc); - - // Database names are keys in the unnamed database, and may be read but not written. + (void)env; + // A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function. + + if (const auto rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi)) + throw LMDBError("Unable to open named database: ", rc); + + // Database names are keys in the unnamed database, and may be read but not written. } -MDBEnv::MDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs) +MDBEnv::MDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs) { - mdb_env_create(&d_env); - if(const auto rc = mdb_env_set_mapsize(d_env, 16ULL * 4096 * 244140ULL)) { // 4GB - throw LMDBError("Setting map size: ", rc); - } - // Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(), - if (const auto rc = mdb_env_set_maxdbs(d_env, maxDBs)) { - throw LMDBError("Setting maxdbs: ", rc); - } + mdb_env_create(&d_env); + if (const auto rc = mdb_env_set_mapsize(d_env, 16ULL * 4096 * 244140ULL)) { // 4GB + throw LMDBError("Setting map size: ", rc); + } + // Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(), + if (const auto rc = mdb_env_set_maxdbs(d_env, maxDBs)) { + throw LMDBError("Setting maxdbs: ", rc); + } - // we need MDB_NOTLS since we rely on its semantics - if(const auto rc = mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) { - // If this function fails, mdb_env_close() must be called to discard the MDB_env handle. - mdb_env_close(d_env); - throw LMDBError("Unable to open database file " + std::string(fname) + ": ", rc); - } + // we need MDB_NOTLS since we rely on its semantics + if (const auto rc = mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) { + // If this function fails, mdb_env_close() must be called to discard the MDB_env handle. + mdb_env_close(d_env); + throw LMDBError("Unable to open database file " + std::string(fname) + ": ", rc); + } } void MDBEnv::incROTX() { - std::lock_guard l(d_countmutex); - ++d_ROtransactionsOut[std::this_thread::get_id()]; + std::lock_guard l(d_countmutex); + ++d_ROtransactionsOut[std::this_thread::get_id()]; } void MDBEnv::decROTX() { - std::lock_guard l(d_countmutex); - --d_ROtransactionsOut[std::this_thread::get_id()]; + std::lock_guard l(d_countmutex); + --d_ROtransactionsOut[std::this_thread::get_id()]; } void MDBEnv::incRWTX() { - std::lock_guard l(d_countmutex); - ++d_RWtransactionsOut[std::this_thread::get_id()]; + std::lock_guard l(d_countmutex); + ++d_RWtransactionsOut[std::this_thread::get_id()]; } void MDBEnv::decRWTX() { - std::lock_guard l(d_countmutex); - --d_RWtransactionsOut[std::this_thread::get_id()]; + std::lock_guard l(d_countmutex); + --d_RWtransactionsOut[std::this_thread::get_id()]; } int MDBEnv::getRWTX() { - std::lock_guard l(d_countmutex); - return d_RWtransactionsOut[std::this_thread::get_id()]; + std::lock_guard l(d_countmutex); + return d_RWtransactionsOut[std::this_thread::get_id()]; } int MDBEnv::getROTX() { - std::lock_guard l(d_countmutex); - return d_ROtransactionsOut[std::this_thread::get_id()]; + std::lock_guard l(d_countmutex); + return d_ROtransactionsOut[std::this_thread::get_id()]; } - -std::shared_ptr getMDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs) +std::shared_ptr getMDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs) { - struct Value - { - weak_ptr wp; - unsigned int flags; - }; - - static std::map, Value> s_envs; - static std::mutex mut; - - struct stat statbuf; - if(stat(fname, &statbuf)) { - if(errno != ENOENT) - throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno))); - else { - std::lock_guard l(mut); - auto fresh = std::make_shared(fname, flags, mode, maxDBs); - if(stat(fname, &statbuf)) - throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno))); - auto key = std::tie(statbuf.st_dev, statbuf.st_ino); - s_envs[key] = {fresh, flags}; - return fresh; - } - } + struct Value { + weak_ptr wp; + unsigned int flags; + }; - std::lock_guard l(mut); - auto key = std::tie(statbuf.st_dev, statbuf.st_ino); - auto iter = s_envs.find(key); - if(iter != s_envs.end()) { - auto sp = iter->second.wp.lock(); - if(sp) { - if(iter->second.flags != flags) - throw LMDBError("Can't open mdb with differing flags"); + static std::map, Value> s_envs; + static std::mutex mut; - return sp; + struct stat statbuf; + if (stat(fname, &statbuf)) { + if (errno != ENOENT) + throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno))); + else { + std::lock_guard l(mut); + auto fresh = std::make_shared(fname, flags, mode, maxDBs); + if (stat(fname, &statbuf)) + throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno))); + auto key = std::tie(statbuf.st_dev, statbuf.st_ino); + s_envs[key] = { fresh, flags }; + return fresh; + } } - else { - s_envs.erase(iter); // useful if make_shared fails - } - } - auto fresh = std::make_shared(fname, flags, mode, maxDBs); - s_envs[key] = {fresh, flags}; - - return fresh; + std::lock_guard l(mut); + auto key = std::tie(statbuf.st_dev, statbuf.st_ino); + auto iter = s_envs.find(key); + if (iter != s_envs.end()) { + auto sp = iter->second.wp.lock(); + if (sp) { + if (iter->second.flags != flags) + throw LMDBError("Can't open mdb with differing flags"); + + return sp; + } else { + s_envs.erase(iter); // useful if make_shared fails + } + } + + auto fresh = std::make_shared(fname, flags, mode, maxDBs); + s_envs[key] = { fresh, flags }; + + return fresh; } - MDBDbi MDBEnv::openDB(const string_view dbname, unsigned int flags) { - unsigned int envflags; - mdb_env_get_flags(d_env, &envflags); - /* + unsigned int envflags; + mdb_env_get_flags(d_env, &envflags); + /* This function must not be called from multiple concurrent transactions in the same process. A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function. */ - std::lock_guard l(d_openmut); - - if(!(envflags & MDB_RDONLY)) { - auto rwt = getRWTransaction(); - MDBDbi ret = rwt->openDB(dbname, flags); - rwt->commit(); - return ret; - } + std::lock_guard l(d_openmut); - MDBDbi ret; - { - auto rwt = getROTransaction(); - ret = rwt->openDB(dbname, flags); - } - return ret; + if (!(envflags & MDB_RDONLY)) { + auto rwt = getRWTransaction(); + MDBDbi ret = rwt->openDB(dbname, flags); + rwt->commit(); + return ret; + } + + MDBDbi ret; + { + auto rwt = getROTransaction(); + ret = rwt->openDB(dbname, flags); + } + return ret; } -MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn): - MDBROTransactionImpl(parent, txn) +MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn) + : MDBROTransactionImpl(parent, txn) { - } MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags) { - MDB_txn *result; - if(env->getRWTX()) - throw LMDBError("Duplicate RW transaction"); + MDB_txn *result; + if (env->getRWTX()) + throw LMDBError("Duplicate RW transaction"); - for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows - if(int rc=mdb_txn_begin(env->d_env, parent, flags, &result)) { - if(rc == MDB_MAP_RESIZED && tries < 2) { - // "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED. - // call mdb_env_set_mapsize with a size of zero to adopt the new size." - mdb_env_set_mapsize(env->d_env, 0); - continue; - } - throw LMDBError("Unable to start RW transaction: ", rc); + for (int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows + if (int rc = mdb_txn_begin(env->d_env, parent, flags, &result)) { + if (rc == MDB_MAP_RESIZED && tries < 2) { + // "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED. + // call mdb_env_set_mapsize with a size of zero to adopt the new size." + mdb_env_set_mapsize(env->d_env, 0); + continue; + } + throw LMDBError("Unable to start RW transaction: ", rc); + } + break; } - break; - } - env->incRWTX(); - return result; + env->incRWTX(); + return result; } -MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv* parent, unsigned int flags): - MDBRWTransactionImpl(parent, openRWTransaction(parent, nullptr, flags)) +MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, unsigned int flags) + : MDBRWTransactionImpl(parent, openRWTransaction(parent, nullptr, flags)) { } MDBRWTransactionImpl::~MDBRWTransactionImpl() { - MDBRWTransactionImpl::abort(); + MDBRWTransactionImpl::abort(); } void MDBRWTransactionImpl::commit() { - closeRORWCursors(); - if (!d_txn) { - return; - } + closeRORWCursors(); + if (!d_txn) { + return; + } - if(const auto rc = mdb_txn_commit(d_txn)) { - throw LMDBError("Committing transaction: ", rc); - } - environment().decRWTX(); - d_txn = nullptr; + if (const auto rc = mdb_txn_commit(d_txn)) { + throw LMDBError("Committing transaction: ", rc); + } + environment().decRWTX(); + d_txn = nullptr; } void MDBRWTransactionImpl::abort() { - closeRORWCursors(); - if (!d_txn) { - return; - } + closeRORWCursors(); + if (!d_txn) { + return; + } - mdb_txn_abort(d_txn); - // prevent the RO destructor from cleaning up the transaction itself - environment().decRWTX(); - d_txn = nullptr; + mdb_txn_abort(d_txn); + // prevent the RO destructor from cleaning up the transaction itself + environment().decRWTX(); + d_txn = nullptr; } -MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn): - d_parent(parent), - d_cursors(), - d_txn(txn) +MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn) + : d_parent(parent) + , d_cursors() + , d_txn(txn) { - } MDB_txn *MDBROTransactionImpl::openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags) { - if(env->getRWTX()) - throw LMDBError("Duplicate RO transaction"); - - /* - A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */ - MDB_txn *result = nullptr; - for(int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows - if(const auto rc = mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) { - if(rc == MDB_MAP_RESIZED && tries < 2) { - // "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED. - // call mdb_env_set_mapsize with a size of zero to adopt the new size." - mdb_env_set_mapsize(env->d_env, 0); - continue; - } - throw LMDBError("Unable to start RO transaction: ", rc); - } - break; - } - env->incROTX(); + if (env->getRWTX()) + throw LMDBError("Duplicate RO transaction"); - return result; + /* + A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */ + MDB_txn *result = nullptr; + for (int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows + if (const auto rc = mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) { + if (rc == MDB_MAP_RESIZED && tries < 2) { + // "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED. + // call mdb_env_set_mapsize with a size of zero to adopt the new size." + mdb_env_set_mapsize(env->d_env, 0); + continue; + } + throw LMDBError("Unable to start RO transaction: ", rc); + } + break; + } + env->incROTX(); + + return result; } void MDBROTransactionImpl::closeROCursors() { - // we need to move the vector away to ensure that the cursors don’t mess with our iteration. - std::vector buf; - std::swap(d_cursors, buf); - for (auto &cursor: buf) { - cursor->close(); - } + // we need to move the vector away to ensure that the cursors don’t mess with our iteration. + std::vector buf; + std::swap(d_cursors, buf); + for (auto &cursor : buf) { + cursor->close(); + } } -MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, unsigned int flags): - MDBROTransactionImpl(parent, openROTransaction(parent, nullptr, flags)) +MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, unsigned int flags) + : MDBROTransactionImpl(parent, openROTransaction(parent, nullptr, flags)) { - } MDBROTransactionImpl::~MDBROTransactionImpl() { - // this is safe because C++ will not call overrides of virtual methods in destructors. - MDBROTransactionImpl::commit(); + // this is safe because C++ will not call overrides of virtual methods in destructors. + MDBROTransactionImpl::commit(); } void MDBROTransactionImpl::abort() { - closeROCursors(); - // if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort). - if (d_txn) { - d_parent->decROTX(); - mdb_txn_abort(d_txn); // this appears to work better than abort for r/o database opening - d_txn = nullptr; - } + closeROCursors(); + // if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort). + if (d_txn) { + d_parent->decROTX(); + mdb_txn_abort(d_txn); // this appears to work better than abort for r/o database opening + d_txn = nullptr; + } } void MDBROTransactionImpl::commit() { - closeROCursors(); - // if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort). - if (d_txn) { - d_parent->decROTX(); - if (const auto rc = mdb_txn_commit(d_txn)) { // this appears to work better than abort for r/o database opening - throw LMDBError("Error comitting transaction: ", rc); + closeROCursors(); + // if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort). + if (d_txn) { + d_parent->decROTX(); + if (const auto rc = mdb_txn_commit(d_txn)) { // this appears to work better than abort for r/o database opening + throw LMDBError("Error comitting transaction: ", rc); + } + d_txn = nullptr; } - d_txn = nullptr; - } } - - void MDBRWTransactionImpl::clear(MDB_dbi dbi) { - if(const auto rc = mdb_drop(d_txn, dbi, 0)) { - throw LMDBError("Error clearing database: ", rc); - } + if (const auto rc = mdb_drop(d_txn, dbi, 0)) { + throw LMDBError("Error clearing database: ", rc); + } } -MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi& dbi) +MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi &dbi) { - MDB_cursor *cursor;; - if(const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) { - throw LMDBError("Error creating RO cursor: ", rc); - } - return MDBRWCursor(d_rw_cursors, cursor); + MDB_cursor *cursor; + ; + if (const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) { + throw LMDBError("Error creating RO cursor: ", rc); + } + return MDBRWCursor(d_rw_cursors, cursor); } MDBRWCursor MDBRWTransactionImpl::getCursor(const MDBDbi &dbi) { - return getRWCursor(dbi); + return getRWCursor(dbi); } MDBRWTransaction MDBRWTransactionImpl::getRWTransaction() { - MDB_txn *txn; - if (const auto rc = mdb_txn_begin(environment(), *this, 0, &txn)) { - throw LMDBError("Failed to start child transaction: ", rc); - } - // we need to increase the counter here because commit/abort on the child transaction will decrease it - environment().incRWTX(); - return MDBRWTransaction(new MDBRWTransactionImpl(&environment(), txn)); + MDB_txn *txn; + if (const auto rc = mdb_txn_begin(environment(), *this, 0, &txn)) { + throw LMDBError("Failed to start child transaction: ", rc); + } + // we need to increase the counter here because commit/abort on the child transaction will decrease it + environment().incRWTX(); + return MDBRWTransaction(new MDBRWTransactionImpl(&environment(), txn)); } MDBROTransaction MDBRWTransactionImpl::getROTransaction() { - return getRWTransaction(); + return getRWTransaction(); } MDBROTransaction MDBEnv::getROTransaction() { - return MDBROTransaction(new MDBROTransactionImpl(this)); + return MDBROTransaction(new MDBROTransactionImpl(this)); } MDBRWTransaction MDBEnv::getRWTransaction() { - return MDBRWTransaction(new MDBRWTransactionImpl(this)); + return MDBRWTransaction(new MDBRWTransactionImpl(this)); } - void MDBRWTransactionImpl::closeRWCursors() { - decltype(d_rw_cursors) buf; - std::swap(d_rw_cursors, buf); - for (auto &cursor: buf) { - cursor->close(); - } + decltype(d_rw_cursors) buf; + std::swap(d_rw_cursors, buf); + for (auto &cursor : buf) { + cursor->close(); + } } -MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi& dbi) +MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi &dbi) { - return getROCursor(dbi); + return getROCursor(dbi); } MDBROCursor MDBROTransactionImpl::getROCursor(const MDBDbi &dbi) { - MDB_cursor *cursor; - if(const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) { - throw LMDBError("Error creating RO cursor: ", rc); - } - return MDBROCursor(d_cursors, cursor); + MDB_cursor *cursor; + if (const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) { + throw LMDBError("Error creating RO cursor: ", rc); + } + return MDBROCursor(d_cursors, cursor); } -} +} // namespace LMDBSafe diff --git a/lmdb-safe.hh b/lmdb-safe.hh index 90d8e45..73c30f4 100644 --- a/lmdb-safe.hh +++ b/lmdb-safe.hh @@ -4,19 +4,19 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include #include +#include +#include +#include +#include #include +#include +#include +#include /*! * \brief The LMDBSafe namespace contains all classes/types contained by the lmdb-safe and @@ -43,43 +43,41 @@ using string_view = boost::string_ref; #endif #endif -class LMDB_SAFE_EXPORT LMDBError : public std::runtime_error -{ +class LMDB_SAFE_EXPORT LMDBError : public std::runtime_error { public: - explicit LMDBError(const std::string &error) noexcept - : std::runtime_error(error) - , ec(0) - { - } + explicit LMDBError(const std::string &error) noexcept + : std::runtime_error(error) + , ec(0) + { + } - explicit LMDBError(const std::string &context, int error) noexcept - : std::runtime_error(context + mdb_strerror(error)) - , ec(error) - { - } + explicit LMDBError(const std::string &context, int error) noexcept + : std::runtime_error(context + mdb_strerror(error)) + , ec(error) + { + } - const int ec; + const int ec; }; /*! * \brief The MDBDbi class is our only 'value type' object as 1) a dbi is actually an integer * and 2) per LMDB documentation, we never close it. */ -class LMDB_SAFE_EXPORT MDBDbi -{ +class LMDB_SAFE_EXPORT MDBDbi { public: - MDBDbi() - { - d_dbi = std::numeric_limits::max(); - } - explicit MDBDbi(MDB_env* env, MDB_txn* txn, string_view dbname, unsigned int flags); + MDBDbi() + { + d_dbi = std::numeric_limits::max(); + } + explicit MDBDbi(MDB_env *env, MDB_txn *txn, string_view dbname, unsigned int flags); - operator const MDB_dbi&() const - { - return d_dbi; - } - - MDB_dbi d_dbi; + operator const MDB_dbi &() const + { + return d_dbi; + } + + MDB_dbi d_dbi; }; class MDBRWTransactionImpl; @@ -88,237 +86,223 @@ class MDBROTransactionImpl; using MDBROTransaction = std::unique_ptr; using MDBRWTransaction = std::unique_ptr; -class LMDB_SAFE_EXPORT MDBEnv -{ +class LMDB_SAFE_EXPORT MDBEnv { public: - MDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 10); + MDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 10); - ~MDBEnv() - { - // Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function - mdb_env_close(d_env); - // but, elsewhere, docs say database handles do not need to be closed? - } + ~MDBEnv() + { + // Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function + mdb_env_close(d_env); + // but, elsewhere, docs say database handles do not need to be closed? + } - MDBDbi openDB(const string_view dbname, unsigned int flags); - - MDBRWTransaction getRWTransaction(); - MDBROTransaction getROTransaction(); + MDBDbi openDB(const string_view dbname, unsigned int flags); - operator MDB_env*& () - { - return d_env; - } - MDB_env* d_env; + MDBRWTransaction getRWTransaction(); + MDBROTransaction getROTransaction(); + + operator MDB_env *&() + { + return d_env; + } + MDB_env *d_env; + + int getRWTX(); + void incRWTX(); + void decRWTX(); + int getROTX(); + void incROTX(); + void decROTX(); - int getRWTX(); - void incRWTX(); - void decRWTX(); - int getROTX(); - void incROTX(); - void decROTX(); private: - std::mutex d_openmut; - std::mutex d_countmutex; - std::map d_RWtransactionsOut; - std::map d_ROtransactionsOut; + std::mutex d_openmut; + std::mutex d_countmutex; + std::map d_RWtransactionsOut; + std::map d_ROtransactionsOut; }; -LMDB_SAFE_EXPORT std::shared_ptr getMDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 128); +LMDB_SAFE_EXPORT std::shared_ptr getMDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 128); -struct LMDB_SAFE_EXPORT MDBOutVal -{ - operator MDB_val&() - { - return d_mdbval; - } +struct LMDB_SAFE_EXPORT MDBOutVal { + operator MDB_val &() + { + return d_mdbval; + } - template ::value, - T>::type* = nullptr> const - T get() - { - T ret; - if(d_mdbval.mv_size != sizeof(T)) - throw LMDBError("MDB data has wrong length for type"); - - memcpy(&ret, d_mdbval.mv_data, sizeof(T)); - return ret; - } + template ::value, T>::type * = nullptr> const T get() + { + T ret; + if (d_mdbval.mv_size != sizeof(T)) + throw LMDBError("MDB data has wrong length for type"); - template ::value,T>::type* = nullptr> - T get() const; + memcpy(&ret, d_mdbval.mv_data, sizeof(T)); + return ret; + } - template - T get_struct() const - { - T ret; - if(d_mdbval.mv_size != sizeof(T)) - throw LMDBError("MDB data has wrong length for type"); - - memcpy(&ret, d_mdbval.mv_data, sizeof(T)); - return ret; - } + template ::value, T>::type * = nullptr> T get() const; - template - const T* get_struct_ptr() const - { - if(d_mdbval.mv_size != sizeof(T)) - throw LMDBError("MDB data has wrong length for type"); - - return reinterpret_cast(d_mdbval.mv_data); - } - - - MDB_val d_mdbval; + template T get_struct() const + { + T ret; + if (d_mdbval.mv_size != sizeof(T)) + throw LMDBError("MDB data has wrong length for type"); + + memcpy(&ret, d_mdbval.mv_data, sizeof(T)); + return ret; + } + + template const T *get_struct_ptr() const + { + if (d_mdbval.mv_size != sizeof(T)) + throw LMDBError("MDB data has wrong length for type"); + + return reinterpret_cast(d_mdbval.mv_data); + } + + MDB_val d_mdbval; }; -template<> inline std::string MDBOutVal::get() const +template <> inline std::string MDBOutVal::get() const { - return std::string(static_cast(d_mdbval.mv_data), d_mdbval.mv_size); + return std::string(static_cast(d_mdbval.mv_data), d_mdbval.mv_size); } -template<> inline string_view MDBOutVal::get() const +template <> inline string_view MDBOutVal::get() const { - return string_view(static_cast(d_mdbval.mv_data), d_mdbval.mv_size); + return string_view(static_cast(d_mdbval.mv_data), d_mdbval.mv_size); } -class LMDB_SAFE_EXPORT MDBInVal -{ +class LMDB_SAFE_EXPORT MDBInVal { public: - MDBInVal(const MDBOutVal& rhs) - { - d_mdbval = rhs.d_mdbval; - } + MDBInVal(const MDBOutVal &rhs) + { + d_mdbval = rhs.d_mdbval; + } - template ::value, - T>::type* = nullptr> - MDBInVal(T i) - { - memcpy(&d_memory[0], &i, sizeof(i)); - d_mdbval.mv_size = sizeof(T); - d_mdbval.mv_data = d_memory;; - } + template ::value, T>::type * = nullptr> MDBInVal(T i) + { + memcpy(&d_memory[0], &i, sizeof(i)); + d_mdbval.mv_size = sizeof(T); + d_mdbval.mv_data = d_memory; + ; + } - MDBInVal(const char* s) - { - d_mdbval.mv_size = strlen(s); - d_mdbval.mv_data = static_cast(const_cast(s)); - } - - MDBInVal(string_view v) - { - d_mdbval.mv_size = v.size(); - d_mdbval.mv_data = static_cast(const_cast(v.data())); - } + MDBInVal(const char *s) + { + d_mdbval.mv_size = strlen(s); + d_mdbval.mv_data = static_cast(const_cast(s)); + } - MDBInVal(const std::string& v) - { - d_mdbval.mv_size = v.size(); - d_mdbval.mv_data = static_cast(const_cast(v.data())); - } + MDBInVal(string_view v) + { + d_mdbval.mv_size = v.size(); + d_mdbval.mv_data = static_cast(const_cast(v.data())); + } + + MDBInVal(const std::string &v) + { + d_mdbval.mv_size = v.size(); + d_mdbval.mv_data = static_cast(const_cast(v.data())); + } + + template static MDBInVal fromStruct(const T &t) + { + MDBInVal ret; + ret.d_mdbval.mv_size = sizeof(T); + ret.d_mdbval.mv_data = static_cast(&const_cast(t)); + return ret; + } + + operator MDB_val &() + { + return d_mdbval; + } + MDB_val d_mdbval; - - template - static MDBInVal fromStruct(const T& t) - { - MDBInVal ret; - ret.d_mdbval.mv_size = sizeof(T); - ret.d_mdbval.mv_data = static_cast(&const_cast(t)); - return ret; - } - - operator MDB_val&() - { - return d_mdbval; - } - MDB_val d_mdbval; private: - MDBInVal(){} - char d_memory[sizeof(double)]; - + MDBInVal() + { + } + char d_memory[sizeof(double)]; }; class MDBROCursor; -class LMDB_SAFE_EXPORT MDBROTransactionImpl -{ +class LMDB_SAFE_EXPORT MDBROTransactionImpl { protected: - MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn); + MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn); private: - static MDB_txn *openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags=0); + static MDB_txn *openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags = 0); - MDBEnv* d_parent; - std::vector d_cursors; + MDBEnv *d_parent; + std::vector d_cursors; protected: - MDB_txn* d_txn; + MDB_txn *d_txn; - void closeROCursors(); + void closeROCursors(); public: - explicit MDBROTransactionImpl(MDBEnv* parent, unsigned int flags=0); + explicit MDBROTransactionImpl(MDBEnv *parent, unsigned int flags = 0); - MDBROTransactionImpl(const MDBROTransactionImpl& src) = delete; - MDBROTransactionImpl &operator=(const MDBROTransactionImpl& src) = delete; + MDBROTransactionImpl(const MDBROTransactionImpl &src) = delete; + MDBROTransactionImpl &operator=(const MDBROTransactionImpl &src) = delete; - // The move constructor/operator cannot be made safe due to Object Slicing with MDBRWTransaction. - MDBROTransactionImpl(MDBROTransactionImpl&& rhs) = delete; - MDBROTransactionImpl &operator=(MDBROTransactionImpl &&rhs) = delete; + // The move constructor/operator cannot be made safe due to Object Slicing with MDBRWTransaction. + MDBROTransactionImpl(MDBROTransactionImpl &&rhs) = delete; + MDBROTransactionImpl &operator=(MDBROTransactionImpl &&rhs) = delete; - virtual ~MDBROTransactionImpl(); + virtual ~MDBROTransactionImpl(); - virtual void abort(); - virtual void commit(); + virtual void abort(); + virtual void commit(); - int get(MDB_dbi dbi, const MDBInVal& key, MDBOutVal& val) - { - if(!d_txn) - throw LMDBError("Attempt to use a closed RO transaction for get"); + int get(MDB_dbi dbi, const MDBInVal &key, MDBOutVal &val) + { + if (!d_txn) + throw LMDBError("Attempt to use a closed RO transaction for get"); - const auto rc = mdb_get(d_txn, dbi, const_cast(&key.d_mdbval), - &val.d_mdbval); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Getting data: ", rc); - - return rc; - } + const auto rc = mdb_get(d_txn, dbi, const_cast(&key.d_mdbval), &val.d_mdbval); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Getting data: ", rc); - int get(MDB_dbi dbi, const MDBInVal& key, string_view& val) - { - MDBOutVal out; - int rc = get(dbi, key, out); - if(!rc) - val = out.get(); - return rc; - } + return rc; + } - - // this is something you can do, readonly - MDBDbi openDB(string_view dbname, unsigned int flags) - { - return MDBDbi( d_parent->d_env, d_txn, dbname, flags); - } + int get(MDB_dbi dbi, const MDBInVal &key, string_view &val) + { + MDBOutVal out; + int rc = get(dbi, key, out); + if (!rc) + val = out.get(); + return rc; + } - MDBROCursor getCursor(const MDBDbi&); - MDBROCursor getROCursor(const MDBDbi&); - - operator MDB_txn*() - { - return d_txn; - } + // this is something you can do, readonly + MDBDbi openDB(string_view dbname, unsigned int flags) + { + return MDBDbi(d_parent->d_env, d_txn, dbname, flags); + } - inline operator bool() const { - return d_txn; - } + MDBROCursor getCursor(const MDBDbi &); + MDBROCursor getROCursor(const MDBDbi &); - inline MDBEnv &environment() - { - return *d_parent; - } + operator MDB_txn *() + { + return d_txn; + } + + inline operator bool() const + { + return d_txn; + } + + inline MDBEnv &environment() + { + return *d_parent; + } }; /*! @@ -328,276 +312,261 @@ public: * It can be reused with mdb_cursor_renew() before finally closing it. * - "If the parent transaction commits, the cursor must not be used again." */ -template -class MDBGenCursor -{ +template class MDBGenCursor { private: - std::vector *d_registry; - MDB_cursor* d_cursor; + std::vector *d_registry; + MDB_cursor *d_cursor; public: - MDBGenCursor(): - d_registry(nullptr), - d_cursor(nullptr) - { + MDBGenCursor() + : d_registry(nullptr) + , d_cursor(nullptr) + { + } - } - - MDBGenCursor(std::vector ®istry, MDB_cursor *cursor): - d_registry(®istry), - d_cursor(cursor) - { - registry.emplace_back(static_cast(this)); - } + MDBGenCursor(std::vector ®istry, MDB_cursor *cursor) + : d_registry(®istry) + , d_cursor(cursor) + { + registry.emplace_back(static_cast(this)); + } private: - void move_from(MDBGenCursor *src) - { - if (!d_registry) { - return; - } + void move_from(MDBGenCursor *src) + { + if (!d_registry) { + return; + } - auto iter = std::find(d_registry->begin(), - d_registry->end(), - src); - if (iter != d_registry->end()) { - *iter = static_cast(this); - } else { - d_registry->emplace_back(static_cast(this)); + auto iter = std::find(d_registry->begin(), d_registry->end(), src); + if (iter != d_registry->end()) { + *iter = static_cast(this); + } else { + d_registry->emplace_back(static_cast(this)); + } } - } public: - MDBGenCursor(const MDBGenCursor &src) = delete; + MDBGenCursor(const MDBGenCursor &src) = delete; - MDBGenCursor(MDBGenCursor &&src) noexcept: - d_registry(src.d_registry), - d_cursor(src.d_cursor) - { - move_from(&src); - src.d_registry = nullptr; - src.d_cursor = nullptr; - } + MDBGenCursor(MDBGenCursor &&src) noexcept + : d_registry(src.d_registry) + , d_cursor(src.d_cursor) + { + move_from(&src); + src.d_registry = nullptr; + src.d_cursor = nullptr; + } - MDBGenCursor &operator=(const MDBGenCursor &src) = delete; + MDBGenCursor &operator=(const MDBGenCursor &src) = delete; - MDBGenCursor &operator=(MDBGenCursor &&src) noexcept - { - d_registry = src.d_registry; - d_cursor = src.d_cursor; - move_from(&src); - src.d_registry = nullptr; - src.d_cursor = nullptr; - return *this; - } + MDBGenCursor &operator=(MDBGenCursor &&src) noexcept + { + d_registry = src.d_registry; + d_cursor = src.d_cursor; + move_from(&src); + src.d_registry = nullptr; + src.d_cursor = nullptr; + return *this; + } - ~MDBGenCursor() - { - close(); - } + ~MDBGenCursor() + { + close(); + } public: - int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op) - { - const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Unable to get from cursor: ", rc); - return rc; - } - - int find(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data) - { - key.d_mdbval = in.d_mdbval; - const auto rc = mdb_cursor_get(d_cursor, const_cast(&key.d_mdbval), &data.d_mdbval, MDB_SET); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Unable to find from cursor: ", rc); - return rc; - } - - int lower_bound(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data) - { - key.d_mdbval = in.d_mdbval; - - const auto rc = mdb_cursor_get(d_cursor, const_cast(&key.d_mdbval), &data.d_mdbval, MDB_SET_RANGE); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Unable to lower_bound from cursor: ", rc); - return rc; - } - - - int nextprev(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op) - { - const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Unable to prevnext from cursor: ", rc); - return rc; - } - - int next(MDBOutVal& key, MDBOutVal& data) - { - return nextprev(key, data, MDB_NEXT); - } - - int prev(MDBOutVal& key, MDBOutVal& data) - { - return nextprev(key, data, MDB_PREV); - } - - int currentlast(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op) - { - const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Unable to next from cursor: ", rc); - return rc; - } - - int current(MDBOutVal& key, MDBOutVal& data) - { - return currentlast(key, data, MDB_GET_CURRENT); - } - int last(MDBOutVal& key, MDBOutVal& data) - { - return currentlast(key, data, MDB_LAST); - } - int first(MDBOutVal& key, MDBOutVal& data) - { - return currentlast(key, data, MDB_FIRST); - } - - operator MDB_cursor*() - { - return d_cursor; - } - - operator bool() const - { - return d_cursor; - } - - void close() - { - if (d_registry) { - auto iter = std::find(d_registry->begin(), - d_registry->end(), - static_cast(this)); - if (iter != d_registry->end()) { - d_registry->erase(iter); - } - d_registry = nullptr; + int get(MDBOutVal &key, MDBOutVal &data, MDB_cursor_op op) + { + const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Unable to get from cursor: ", rc); + return rc; } - if (d_cursor) { - mdb_cursor_close(d_cursor); - d_cursor = nullptr; + + int find(const MDBInVal &in, MDBOutVal &key, MDBOutVal &data) + { + key.d_mdbval = in.d_mdbval; + const auto rc = mdb_cursor_get(d_cursor, const_cast(&key.d_mdbval), &data.d_mdbval, MDB_SET); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Unable to find from cursor: ", rc); + return rc; + } + + int lower_bound(const MDBInVal &in, MDBOutVal &key, MDBOutVal &data) + { + key.d_mdbval = in.d_mdbval; + + const auto rc = mdb_cursor_get(d_cursor, const_cast(&key.d_mdbval), &data.d_mdbval, MDB_SET_RANGE); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Unable to lower_bound from cursor: ", rc); + return rc; + } + + int nextprev(MDBOutVal &key, MDBOutVal &data, MDB_cursor_op op) + { + const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Unable to prevnext from cursor: ", rc); + return rc; + } + + int next(MDBOutVal &key, MDBOutVal &data) + { + return nextprev(key, data, MDB_NEXT); + } + + int prev(MDBOutVal &key, MDBOutVal &data) + { + return nextprev(key, data, MDB_PREV); + } + + int currentlast(MDBOutVal &key, MDBOutVal &data, MDB_cursor_op op) + { + const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Unable to next from cursor: ", rc); + return rc; + } + + int current(MDBOutVal &key, MDBOutVal &data) + { + return currentlast(key, data, MDB_GET_CURRENT); + } + int last(MDBOutVal &key, MDBOutVal &data) + { + return currentlast(key, data, MDB_LAST); + } + int first(MDBOutVal &key, MDBOutVal &data) + { + return currentlast(key, data, MDB_FIRST); + } + + operator MDB_cursor *() + { + return d_cursor; + } + + operator bool() const + { + return d_cursor; + } + + void close() + { + if (d_registry) { + auto iter = std::find(d_registry->begin(), d_registry->end(), static_cast(this)); + if (iter != d_registry->end()) { + d_registry->erase(iter); + } + d_registry = nullptr; + } + if (d_cursor) { + mdb_cursor_close(d_cursor); + d_cursor = nullptr; + } } - } }; -class LMDB_SAFE_EXPORT MDBROCursor : public MDBGenCursor -{ +class LMDB_SAFE_EXPORT MDBROCursor : public MDBGenCursor { public: - MDBROCursor() = default; - using MDBGenCursor::MDBGenCursor; - MDBROCursor(const MDBROCursor &src) = delete; - MDBROCursor(MDBROCursor &&src) = default; - MDBROCursor &operator=(const MDBROCursor &src) = delete; - MDBROCursor &operator=(MDBROCursor &&src) = default; - ~MDBROCursor() = default; - + MDBROCursor() = default; + using MDBGenCursor::MDBGenCursor; + MDBROCursor(const MDBROCursor &src) = delete; + MDBROCursor(MDBROCursor &&src) = default; + MDBROCursor &operator=(const MDBROCursor &src) = delete; + MDBROCursor &operator=(MDBROCursor &&src) = default; + ~MDBROCursor() = default; }; class MDBRWCursor; -class LMDB_SAFE_EXPORT MDBRWTransactionImpl: public MDBROTransactionImpl -{ +class LMDB_SAFE_EXPORT MDBRWTransactionImpl : public MDBROTransactionImpl { protected: - MDBRWTransactionImpl(MDBEnv* parent, MDB_txn* txn); + MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn); private: - static MDB_txn *openRWTransaction(MDBEnv* env, MDB_txn *parent, unsigned int flags); + static MDB_txn *openRWTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags); private: - std::vector d_rw_cursors; + std::vector d_rw_cursors; - void closeRWCursors(); - inline void closeRORWCursors() { - closeROCursors(); - closeRWCursors(); - } + void closeRWCursors(); + inline void closeRORWCursors() + { + closeROCursors(); + closeRWCursors(); + } public: - explicit MDBRWTransactionImpl(MDBEnv* parent, unsigned int flags=0); + explicit MDBRWTransactionImpl(MDBEnv *parent, unsigned int flags = 0); - MDBRWTransactionImpl(const MDBRWTransactionImpl& rhs) = delete; - MDBRWTransactionImpl(MDBRWTransactionImpl&& rhs) = delete; - MDBRWTransactionImpl &operator=(const MDBRWTransactionImpl& rhs) = delete; - MDBRWTransactionImpl &operator=(MDBRWTransactionImpl&& rhs) = delete; + MDBRWTransactionImpl(const MDBRWTransactionImpl &rhs) = delete; + MDBRWTransactionImpl(MDBRWTransactionImpl &&rhs) = delete; + MDBRWTransactionImpl &operator=(const MDBRWTransactionImpl &rhs) = delete; + MDBRWTransactionImpl &operator=(MDBRWTransactionImpl &&rhs) = delete; - ~MDBRWTransactionImpl() override; - - void commit() override; - void abort() override; + ~MDBRWTransactionImpl() override; - void clear(MDB_dbi dbi); - - void put(MDB_dbi dbi, const MDBInVal& key, const MDBInVal& val, unsigned int flags=0) - { - if(!d_txn) - throw LMDBError("Attempt to use a closed RW transaction for put"); - if(const auto rc = mdb_put(d_txn, dbi, - const_cast(&key.d_mdbval), - const_cast(&val.d_mdbval), flags)) - throw LMDBError("Putting data: ", rc); - } + void commit() override; + void abort() override; + void clear(MDB_dbi dbi); - int del(MDBDbi& dbi, const MDBInVal& key, const MDBInVal& val) - { - const auto rc = mdb_del(d_txn, dbi, const_cast(&key.d_mdbval), const_cast(&val.d_mdbval)); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Deleting data: ", rc); - return rc; - } + void put(MDB_dbi dbi, const MDBInVal &key, const MDBInVal &val, unsigned int flags = 0) + { + if (!d_txn) + throw LMDBError("Attempt to use a closed RW transaction for put"); + if (const auto rc = mdb_put(d_txn, dbi, const_cast(&key.d_mdbval), const_cast(&val.d_mdbval), flags)) + throw LMDBError("Putting data: ", rc); + } - int del(MDBDbi& dbi, const MDBInVal& key) - { - const auto rc = mdb_del(d_txn, dbi, const_cast(&key.d_mdbval), 0); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Deleting data: ", rc); - return rc; - } + int del(MDBDbi &dbi, const MDBInVal &key, const MDBInVal &val) + { + const auto rc = mdb_del(d_txn, dbi, const_cast(&key.d_mdbval), const_cast(&val.d_mdbval)); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Deleting data: ", rc); + return rc; + } - - int get(MDBDbi& dbi, const MDBInVal& key, MDBOutVal& val) - { - if(!d_txn) - throw LMDBError("Attempt to use a closed RW transaction for get"); + int del(MDBDbi &dbi, const MDBInVal &key) + { + const auto rc = mdb_del(d_txn, dbi, const_cast(&key.d_mdbval), 0); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Deleting data: ", rc); + return rc; + } - const auto rc = mdb_get(d_txn, dbi, const_cast(&key.d_mdbval), - &val.d_mdbval); - if(rc && rc != MDB_NOTFOUND) - throw LMDBError("Getting data: ", rc); - return rc; - } + int get(MDBDbi &dbi, const MDBInVal &key, MDBOutVal &val) + { + if (!d_txn) + throw LMDBError("Attempt to use a closed RW transaction for get"); - int get(MDBDbi& dbi, const MDBInVal& key, string_view& val) - { - MDBOutVal out; - const auto rc = get(dbi, key, out); - if(!rc) - val = out.get(); - return rc; - } - - MDBDbi openDB(string_view dbname, unsigned int flags) - { - return MDBDbi(environment().d_env, d_txn, dbname, flags); - } + const auto rc = mdb_get(d_txn, dbi, const_cast(&key.d_mdbval), &val.d_mdbval); + if (rc && rc != MDB_NOTFOUND) + throw LMDBError("Getting data: ", rc); + return rc; + } - MDBRWCursor getRWCursor(const MDBDbi&); - MDBRWCursor getCursor(const MDBDbi&); + int get(MDBDbi &dbi, const MDBInVal &key, string_view &val) + { + MDBOutVal out; + const auto rc = get(dbi, key, out); + if (!rc) + val = out.get(); + return rc; + } - MDBRWTransaction getRWTransaction(); - MDBROTransaction getROTransaction(); + MDBDbi openDB(string_view dbname, unsigned int flags) + { + return MDBDbi(environment().d_env, d_txn, dbname, flags); + } + + MDBRWCursor getRWCursor(const MDBDbi &); + MDBRWCursor getCursor(const MDBDbi &); + + MDBRWTransaction getRWTransaction(); + MDBROTransaction getROTransaction(); }; /*! @@ -607,40 +576,33 @@ public: * be closed when its transaction ends." This is a problem for us since it may means we are closing * the cursor twice, which is bad. */ -class LMDB_SAFE_EXPORT MDBRWCursor : public MDBGenCursor -{ +class LMDB_SAFE_EXPORT MDBRWCursor : public MDBGenCursor { public: - MDBRWCursor() = default; - using MDBGenCursor::MDBGenCursor; - MDBRWCursor(const MDBRWCursor &src) = delete; - MDBRWCursor(MDBRWCursor &&src) = default; - MDBRWCursor &operator=(const MDBRWCursor &src) = delete; - MDBRWCursor &operator=(MDBRWCursor &&src) = default; - ~MDBRWCursor() = default; + MDBRWCursor() = default; + using MDBGenCursor::MDBGenCursor; + MDBRWCursor(const MDBRWCursor &src) = delete; + MDBRWCursor(MDBRWCursor &&src) = default; + MDBRWCursor &operator=(const MDBRWCursor &src) = delete; + MDBRWCursor &operator=(MDBRWCursor &&src) = default; + ~MDBRWCursor() = default; - void put(const MDBOutVal& key, const MDBInVal& data) - { - if(const auto rc = mdb_cursor_put(*this, - const_cast(&key.d_mdbval), - const_cast(&data.d_mdbval), MDB_CURRENT)) - throw LMDBError("Putting data via mdb_cursor_put: ", rc); - } + void put(const MDBOutVal &key, const MDBInVal &data) + { + if (const auto rc = mdb_cursor_put(*this, const_cast(&key.d_mdbval), const_cast(&data.d_mdbval), MDB_CURRENT)) + throw LMDBError("Putting data via mdb_cursor_put: ", rc); + } - - void put(const MDBOutVal& key, const MDBOutVal& data, unsigned int flags=0) - { - if (const auto rc = mdb_cursor_put(*this, - const_cast(&key.d_mdbval), - const_cast(&data.d_mdbval), flags)) - throw LMDBError("Putting data via mdb_cursor_put: ", rc); - } - - void del(unsigned int flags=0) - { - if (const auto rc = mdb_cursor_del(*this, flags)) - throw LMDBError("Deleting data via mdb_cursor_del: ", rc); - } + void put(const MDBOutVal &key, const MDBOutVal &data, unsigned int flags = 0) + { + if (const auto rc = mdb_cursor_put(*this, const_cast(&key.d_mdbval), const_cast(&data.d_mdbval), flags)) + throw LMDBError("Putting data via mdb_cursor_put: ", rc); + } + void del(unsigned int flags = 0) + { + if (const auto rc = mdb_cursor_del(*this, flags)) + throw LMDBError("Deleting data via mdb_cursor_del: ", rc); + } }; -} +} // namespace LMDBSafe diff --git a/lmdb-typed.cc b/lmdb-typed.cc index 4a904ac..a625316 100644 --- a/lmdb-typed.cc +++ b/lmdb-typed.cc @@ -2,15 +2,15 @@ namespace LMDBSafe { -unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi) +unsigned int MDBGetMaxID(MDBRWTransaction &txn, MDBDbi &dbi) { - auto cursor = txn->getRWCursor(dbi); - MDBOutVal maxidval, maxcontent; - unsigned int maxid{0}; - if(!cursor.get(maxidval, maxcontent, MDB_LAST)) { - maxid = maxidval.get(); - } - return maxid; + auto cursor = txn->getRWCursor(dbi); + MDBOutVal maxidval, maxcontent; + unsigned int maxid{ 0 }; + if (!cursor.get(maxidval, maxcontent, MDB_LAST)) { + maxid = maxidval.get(); + } + return maxid; } -} +} // namespace LMDBSafe diff --git a/lmdb-typed.hh b/lmdb-typed.hh index d4c4051..d5ae0c0 100644 --- a/lmdb-typed.hh +++ b/lmdb-typed.hh @@ -19,39 +19,33 @@ namespace LMDBSafe { This makes us start everything at ID=1, which might make it possible to treat id 0 as special */ -LMDB_SAFE_EXPORT unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi); +LMDB_SAFE_EXPORT unsigned int MDBGetMaxID(MDBRWTransaction &txn, MDBDbi &dbi); /** This is the serialization interface. You need to define your these functions for the types you'd like to store. */ -template -std::string serToString(const T& t); +template std::string serToString(const T &t); -template -void serFromString(string_view str, T& ret); +template void serFromString(string_view str, T &ret); /** This is the serialization interface for keys. You need to define your these functions for the types you'd like to use as keys. */ -template -inline std::string keyConv(const T& t); +template inline std::string keyConv(const T &t); -template ::value,T>::type* = nullptr> -inline string_view keyConv(const T& t) +template ::value, T>::type * = nullptr> inline string_view keyConv(const T &t) { - return string_view(reinterpret_cast(&t), sizeof(t)); + return string_view(reinterpret_cast(&t), sizeof(t)); } -template::value,T>::type* = nullptr> -inline string_view keyConv(const T& t) +template ::value, T>::type * = nullptr> inline string_view keyConv(const T &t) { - return t; + return t; } -template::value,T>::type* = nullptr> -inline string_view keyConv(string_view t) +template ::value, T>::type * = nullptr> inline string_view keyConv(string_view t) { - return t; + return t; } /*! @@ -65,652 +59,636 @@ inline string_view keyConv(string_view t) * size or get. People ask for those themselves, and should no do that on indexes that * don't exist. */ -template -struct LMDB_SAFE_EXPORT LMDBIndexOps -{ - explicit LMDBIndexOps(Parent* parent) : d_parent(parent){} - void put(MDBRWTransaction& txn, const Class& t, uint32_t id, unsigned int flags=0) - { - txn->put(d_idx, keyConv(d_parent->getMember(t)), id, flags); - } - - void del(MDBRWTransaction& txn, const Class& t, uint32_t id) - { - if(const auto rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) { - throw LMDBError("Error deleting from index: ", rc); +template struct LMDB_SAFE_EXPORT LMDBIndexOps { + explicit LMDBIndexOps(Parent *parent) + : d_parent(parent) + { } - } - - void clear(MDBRWTransaction& txn) - { - if (const auto rc = mdb_drop(*txn, d_idx, 0)) { - throw LMDBError("Error clearing index: ", rc); + void put(MDBRWTransaction &txn, const Class &t, uint32_t id, unsigned int flags = 0) + { + txn->put(d_idx, keyConv(d_parent->getMember(t)), id, flags); } - } - void openDB(std::shared_ptr& env, string_view str, unsigned int flags) - { - d_idx = env->openDB(str, flags); - } - MDBDbi d_idx; - Parent* d_parent; + void del(MDBRWTransaction &txn, const Class &t, uint32_t id) + { + if (const auto rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) { + throw LMDBError("Error deleting from index: ", rc); + } + } + + void clear(MDBRWTransaction &txn) + { + if (const auto rc = mdb_drop(*txn, d_idx, 0)) { + throw LMDBError("Error clearing index: ", rc); + } + } + + void openDB(std::shared_ptr &env, string_view str, unsigned int flags) + { + d_idx = env->openDB(str, flags); + } + MDBDbi d_idx; + Parent *d_parent; }; /** This is an index on a field in a struct, it derives from the LMDBIndexOps */ -template -struct index_on : LMDBIndexOps> -{ - index_on() : LMDBIndexOps>(this) - {} - static Type getMember(const Class& c) - { - return c.*PtrToMember; - } - - typedef Type type; +template struct index_on : LMDBIndexOps> { + index_on() + : LMDBIndexOps>(this) + { + } + static Type getMember(const Class &c) + { + return c.*PtrToMember; + } + + typedef Type type; }; /** This is a calculated index */ -template -struct index_on_function : LMDBIndexOps > -{ - index_on_function() : LMDBIndexOps >(this) - {} - static Type getMember(const Class& c) - { - Func f; - return f(c); - } +template struct index_on_function : LMDBIndexOps> { + index_on_function() + : LMDBIndexOps>(this) + { + } + static Type getMember(const Class &c) + { + Func f; + return f(c); + } - typedef Type type; + typedef Type type; }; /** nop index, so we can fill our N indexes, even if you don't use them all */ -struct nullindex_t -{ - template - void put(MDBRWTransaction& txn, const Class& t, uint32_t id, unsigned int flags=0) - { - (void)txn; - (void)t; - (void)id; - (void)flags; - } - template - void del(MDBRWTransaction& txn, const Class& t, uint32_t id) - { - (void)txn; - (void)t; - (void)id; - } - template - void clear(Class& txn) - { - (void)txn; - } - - void openDB(std::shared_ptr& env, string_view str, unsigned int flags) - { - (void)env; - (void)str; - (void)flags; - } - typedef uint32_t type; // dummy -}; +struct nullindex_t { + template void put(MDBRWTransaction &txn, const Class &t, uint32_t id, unsigned int flags = 0) + { + (void)txn; + (void)t; + (void)id; + (void)flags; + } + template void del(MDBRWTransaction &txn, const Class &t, uint32_t id) + { + (void)txn; + (void)t; + (void)id; + } + template void clear(Class &txn) + { + (void)txn; + } + void openDB(std::shared_ptr &env, string_view str, unsigned int flags) + { + (void)env; + (void)str; + (void)flags; + } + typedef uint32_t type; // dummy +}; /** The main class. Templatized only on the indexes and typename right now */ -template -class LMDB_SAFE_EXPORT TypedDBI -{ +template +class LMDB_SAFE_EXPORT TypedDBI { public: - TypedDBI(std::shared_ptr env, string_view name) - : d_env(env), d_name(name) - { - d_main = d_env->openDB(name, MDB_CREATE | MDB_INTEGERKEY); + TypedDBI(std::shared_ptr env, string_view name) + : d_env(env) + , d_name(name) + { + d_main = d_env->openDB(name, MDB_CREATE | MDB_INTEGERKEY); - // now you might be tempted to go all MPL on this so we can get rid of the - // ugly macro. I'm not very receptive to that idea since it will make things - // EVEN uglier. -#define openMacro(N) std::get(d_tuple).openDB(d_env, std::string(name)+"_"#N, MDB_CREATE | MDB_DUPFIXED | MDB_DUPSORT); - openMacro(0); - openMacro(1); - openMacro(2); - openMacro(3); + // now you might be tempted to go all MPL on this so we can get rid of the + // ugly macro. I'm not very receptive to that idea since it will make things + // EVEN uglier. +#define openMacro(N) std::get(d_tuple).openDB(d_env, std::string(name) + "_" #N, MDB_CREATE | MDB_DUPFIXED | MDB_DUPSORT); + openMacro(0); + openMacro(1); + openMacro(2); + openMacro(3); #undef openMacro - } - - - // we get a lot of our smarts from this tuple, it enables get<0> etc - typedef std::tuple tuple_t; - tuple_t d_tuple; - - // We support readonly and rw transactions. Here we put the Readonly operations - // which get sourced by both kinds of transactions - template - struct ReadonlyOperations - { - ReadonlyOperations(Parent& parent) : d_parent(parent) - {} - - //! Number of entries in main database - size_t size() - { - MDB_stat stat; - mdb_stat(**d_parent.d_txn, d_parent.d_parent->d_main, &stat); - return stat.ms_entries; } - //! Number of entries in the various indexes - should be the same - template - size_t size() - { - MDB_stat stat; - mdb_stat(**d_parent.d_txn, std::get(d_parent.d_parent->d_tuple).d_idx, &stat); - return stat.ms_entries; - } + // we get a lot of our smarts from this tuple, it enables get<0> etc + typedef std::tuple tuple_t; + tuple_t d_tuple; - //! Get item with id, from main table directly - bool get(uint32_t id, T& t) - { - MDBOutVal data; - if((*d_parent.d_txn)->get(d_parent.d_parent->d_main, id, data)) - return false; - - serFromString(data.get(), t); - return true; - } - - //! Get item through index N, then via the main database - template - uint32_t get(const typename std::tuple_element::type::type& key, T& out) - { - MDBOutVal id; - if(!(*d_parent.d_txn)->get(std::get(d_parent.d_parent->d_tuple).d_idx, keyConv(key), id)) { - if(get(id.get(), out)) - return id.get(); - } - return 0; - } - - //! Cardinality of index N - template - uint32_t cardinality() - { - auto cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); - bool first = true; - MDBOutVal key, data; - uint32_t count = 0; - while(!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT_NODUP)) { - ++count; - first=false; - } - return count; - } - - //! End iderator type - struct eiter_t - {}; - - // can be on main, or on an index - // when on main, return data directly - // when on index, indirect - // we can be limited to one key, or iterate over entire database - // iter requires you to put the cursor in the right place first! - struct iter_t - { - explicit iter_t(Parent* parent, typename Parent::cursor_t&& cursor, bool on_index, bool one_key, bool end=false) : - d_parent(parent), - d_cursor(std::move(cursor)), - d_on_index(on_index), // is this an iterator on main database or on index? - d_one_key(one_key), // should we stop at end of key? (equal range) - d_end(end) - { - if(d_end) - return; - d_prefix.clear(); - - if(d_cursor.get(d_key, d_id, MDB_GET_CURRENT)) { - d_end = true; - return; + // We support readonly and rw transactions. Here we put the Readonly operations + // which get sourced by both kinds of transactions + template struct ReadonlyOperations { + ReadonlyOperations(Parent &parent) + : d_parent(parent) + { } - if(d_on_index) { - if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data)) - throw LMDBError("Missing id in constructor"); - serFromString(d_data.get(), d_t); - } - else - serFromString(d_id.get(), d_t); - } - - explicit iter_t(Parent* parent, typename Parent::cursor_t&& cursor, string_view prefix) : - d_parent(parent), - d_cursor(std::move(cursor)), - d_on_index(true), // is this an iterator on main database or on index? - d_one_key(false), - d_prefix(prefix), - d_end(false) - { - if(d_end) - return; - - if(d_cursor.get(d_key, d_id, MDB_GET_CURRENT)) { - d_end = true; - return; + //! Number of entries in main database + size_t size() + { + MDB_stat stat; + mdb_stat(**d_parent.d_txn, d_parent.d_parent->d_main, &stat); + return stat.ms_entries; } - if(d_on_index) { - if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data)) - throw LMDBError("Missing id in constructor"); - serFromString(d_data.get(), d_t); + //! Number of entries in the various indexes - should be the same + template size_t size() + { + MDB_stat stat; + mdb_stat(**d_parent.d_txn, std::get(d_parent.d_parent->d_tuple).d_idx, &stat); + return stat.ms_entries; } - else - serFromString(d_id.get(), d_t); - } - - std::function filter; - void del() - { - d_cursor.del(); - } - - bool operator!=(const eiter_t&) const - { - return !d_end; - } - - bool operator==(const eiter_t&) const - { - return d_end; - } - - const T& operator*() - { - return d_t; - } - - const T* operator->() - { - return &d_t; - } + //! Get item with id, from main table directly + bool get(uint32_t id, T &t) + { + MDBOutVal data; + if ((*d_parent.d_txn)->get(d_parent.d_parent->d_main, id, data)) + return false; - T &value() - { - return d_t; - } - - // implements generic ++ or -- - iter_t& genoperator(MDB_cursor_op dupop, MDB_cursor_op op) - { - MDBOutVal data; - next:; - const auto rc = d_cursor.get(d_key, d_id, d_one_key ? dupop : op); - if(rc == MDB_NOTFOUND) { - d_end = true; + serFromString(data.get(), t); + return true; } - else if(rc) { - throw LMDBError("Unable to get in genoperator: ", rc); + + //! Get item through index N, then via the main database + template uint32_t get(const typename std::tuple_element::type::type &key, T &out) + { + MDBOutVal id; + if (!(*d_parent.d_txn)->get(std::get(d_parent.d_parent->d_tuple).d_idx, keyConv(key), id)) { + if (get(id.get(), out)) + return id.get(); + } + return 0; } - else if(!d_prefix.empty() && d_key.get().rfind(d_prefix, 0)!=0) { - d_end = true; + + //! Cardinality of index N + template uint32_t cardinality() + { + auto cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); + bool first = true; + MDBOutVal key, data; + uint32_t count = 0; + while (!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT_NODUP)) { + ++count; + first = false; + } + return count; } - else { - if(d_on_index) { - if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, data)) - throw LMDBError("Missing id field in genoperator"); - if(filter && !filter(data)) - goto next; - - serFromString(data.get(), d_t); - } - else { - if(filter && !filter(data)) - goto next; - - serFromString(d_id.get(), d_t); - } + + //! End iderator type + struct eiter_t { + }; + + // can be on main, or on an index + // when on main, return data directly + // when on index, indirect + // we can be limited to one key, or iterate over entire database + // iter requires you to put the cursor in the right place first! + struct iter_t { + explicit iter_t(Parent *parent, typename Parent::cursor_t &&cursor, bool on_index, bool one_key, bool end = false) + : d_parent(parent) + , d_cursor(std::move(cursor)) + , d_on_index(on_index) + , // is this an iterator on main database or on index? + d_one_key(one_key) + , // should we stop at end of key? (equal range) + d_end(end) + { + if (d_end) + return; + d_prefix.clear(); + + if (d_cursor.get(d_key, d_id, MDB_GET_CURRENT)) { + d_end = true; + return; + } + + if (d_on_index) { + if ((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data)) + throw LMDBError("Missing id in constructor"); + serFromString(d_data.get(), d_t); + } else + serFromString(d_id.get(), d_t); + } + + explicit iter_t(Parent *parent, typename Parent::cursor_t &&cursor, string_view prefix) + : d_parent(parent) + , d_cursor(std::move(cursor)) + , d_on_index(true) + , // is this an iterator on main database or on index? + d_one_key(false) + , d_prefix(prefix) + , d_end(false) + { + if (d_end) + return; + + if (d_cursor.get(d_key, d_id, MDB_GET_CURRENT)) { + d_end = true; + return; + } + + if (d_on_index) { + if ((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data)) + throw LMDBError("Missing id in constructor"); + serFromString(d_data.get(), d_t); + } else + serFromString(d_id.get(), d_t); + } + + std::function filter; + void del() + { + d_cursor.del(); + } + + bool operator!=(const eiter_t &) const + { + return !d_end; + } + + bool operator==(const eiter_t &) const + { + return d_end; + } + + const T &operator*() + { + return d_t; + } + + const T *operator->() + { + return &d_t; + } + + T &value() + { + return d_t; + } + + // implements generic ++ or -- + iter_t &genoperator(MDB_cursor_op dupop, MDB_cursor_op op) + { + MDBOutVal data; + next:; + const auto rc = d_cursor.get(d_key, d_id, d_one_key ? dupop : op); + if (rc == MDB_NOTFOUND) { + d_end = true; + } else if (rc) { + throw LMDBError("Unable to get in genoperator: ", rc); + } else if (!d_prefix.empty() && d_key.get().rfind(d_prefix, 0) != 0) { + d_end = true; + } else { + if (d_on_index) { + if ((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, data)) + throw LMDBError("Missing id field in genoperator"); + if (filter && !filter(data)) + goto next; + + serFromString(data.get(), d_t); + } else { + if (filter && !filter(data)) + goto next; + + serFromString(d_id.get(), d_t); + } + } + return *this; + } + + iter_t &operator++() + { + return genoperator(MDB_NEXT_DUP, MDB_NEXT); + } + iter_t &operator--() + { + return genoperator(MDB_PREV_DUP, MDB_PREV); + } + + // get ID this iterator points to + uint32_t getID() + { + if (d_on_index) + return d_id.get(); + else + return d_key.get(); + } + + const MDBOutVal &getKey() + { + return d_key; + } + + // transaction we are part of + Parent *d_parent; + typename Parent::cursor_t d_cursor; + + // gcc complains if I don't zero-init these, which is worrying XXX + MDBOutVal d_key{ { 0, 0 } }, d_data{ { 0, 0 } }, d_id{ { 0, 0 } }; + bool d_on_index; + bool d_one_key; + std::string d_prefix; + bool d_end{ false }; + T d_t; + }; + + template iter_t genbegin(MDB_cursor_op op) + { + typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); + + MDBOutVal out, id; + + if (cursor.get(out, id, op)) { + // on_index, one_key, end + return iter_t{ &d_parent, std::move(cursor), true, false, true }; + } + + return iter_t{ &d_parent, std::move(cursor), true, false }; + }; + + template iter_t begin() + { + return genbegin(MDB_FIRST); } - return *this; - } - iter_t& operator++() - { - return genoperator(MDB_NEXT_DUP, MDB_NEXT); - } - iter_t& operator--() - { - return genoperator(MDB_PREV_DUP, MDB_PREV); - } + template iter_t rbegin() + { + return genbegin(MDB_LAST); + } - // get ID this iterator points to - uint32_t getID() - { - if(d_on_index) - return d_id.get(); - else - return d_key.get(); - } + iter_t begin() + { + typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main); - const MDBOutVal& getKey() - { - return d_key; - } - - - // transaction we are part of - Parent* d_parent; - typename Parent::cursor_t d_cursor; + MDBOutVal out, id; - // gcc complains if I don't zero-init these, which is worrying XXX - MDBOutVal d_key{{0,0}}, d_data{{0,0}}, d_id{{0,0}}; - bool d_on_index; - bool d_one_key; - std::string d_prefix; - bool d_end{false}; - T d_t; + if (cursor.get(out, id, MDB_FIRST)) { + // on_index, one_key, end + return iter_t{ &d_parent, std::move(cursor), false, false, true }; + } + + return iter_t{ &d_parent, std::move(cursor), false, false }; + }; + + eiter_t end() + { + return eiter_t(); + } + + // basis for find, lower_bound + template iter_t genfind(const typename std::tuple_element::type::type &key, MDB_cursor_op op) + { + typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); + + const auto keystr = keyConv(key); + MDBInVal in(keystr); + MDBOutVal out, id; + out.d_mdbval = in.d_mdbval; + + if (cursor.get(out, id, op)) { + // on_index, one_key, end + return iter_t{ &d_parent, std::move(cursor), true, false, true }; + } + + return iter_t{ &d_parent, std::move(cursor), true, false }; + }; + + template iter_t find(const typename std::tuple_element::type::type &key) + { + return genfind(key, MDB_SET); + } + + template iter_t lower_bound(const typename std::tuple_element::type::type &key) + { + return genfind(key, MDB_SET_RANGE); + } + + //! equal range - could possibly be expressed through genfind + template std::pair equal_range(const typename std::tuple_element::type::type &key) + { + typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); + + const auto keyString = keyConv(key); + MDBInVal in(keyString); + MDBOutVal out, id; + out.d_mdbval = in.d_mdbval; + + if (cursor.get(out, id, MDB_SET)) { + // on_index, one_key, end + return { iter_t{ &d_parent, std::move(cursor), true, true, true }, eiter_t() }; + } + + return { iter_t{ &d_parent, std::move(cursor), true, true }, eiter_t() }; + }; + + //! equal range - could possibly be expressed through genfind + template std::pair prefix_range(const typename std::tuple_element::type::type &key) + { + typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); + + const auto keyString = keyConv(key); + MDBInVal in(keyString); + MDBOutVal out, id; + out.d_mdbval = in.d_mdbval; + + if (cursor.get(out, id, MDB_SET_RANGE)) { + // on_index, one_key, end + return { iter_t{ &d_parent, std::move(cursor), true, true, true }, eiter_t() }; + } + + return { iter_t(&d_parent, std::move(cursor), keyString), eiter_t() }; + }; + + Parent &d_parent; }; - template - iter_t genbegin(MDB_cursor_op op) - { - typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); - - MDBOutVal out, id; - - if(cursor.get(out, id, op)) { - // on_index, one_key, end - return iter_t{&d_parent, std::move(cursor), true, false, true}; - } + class LMDB_SAFE_EXPORT ROTransaction : public ReadonlyOperations { + public: + explicit ROTransaction(TypedDBI *parent) + : ReadonlyOperations(*this) + , d_parent(parent) + , d_txn(std::make_shared(d_parent->d_env->getROTransaction())) + { + } - return iter_t{&d_parent, std::move(cursor), true, false}; + explicit ROTransaction(TypedDBI *parent, std::shared_ptr txn) + : ReadonlyOperations(*this) + , d_parent(parent) + , d_txn(txn) + { + } + + ROTransaction(ROTransaction &&rhs) + : ReadonlyOperations(*this) + , d_parent(rhs.d_parent) + , d_txn(std::move(rhs.d_txn)) + + { + rhs.d_parent = 0; + } + + std::shared_ptr getTransactionHandle() + { + return d_txn; + } + + typedef MDBROCursor cursor_t; + + TypedDBI *d_parent; + std::shared_ptr d_txn; }; - template - iter_t begin() - { - return genbegin(MDB_FIRST); - } + class LMDB_SAFE_EXPORT RWTransaction : public ReadonlyOperations { + public: + explicit RWTransaction(TypedDBI *parent) + : ReadonlyOperations(*this) + , d_parent(parent) + { + d_txn = std::make_shared(d_parent->d_env->getRWTransaction()); + } - template - iter_t rbegin() - { - return genbegin(MDB_LAST); - } + explicit RWTransaction(TypedDBI *parent, std::shared_ptr txn) + : ReadonlyOperations(*this) + , d_parent(parent) + , d_txn(txn) + { + } - iter_t begin() - { - typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main); - - MDBOutVal out, id; - - if(cursor.get(out, id, MDB_FIRST)) { - // on_index, one_key, end - return iter_t{&d_parent, std::move(cursor), false, false, true}; - } + RWTransaction(RWTransaction &&rhs) + : ReadonlyOperations(*this) + , d_parent(rhs.d_parent) + , d_txn(std::move(rhs.d_txn)) + { + rhs.d_parent = 0; + } - return iter_t{&d_parent, std::move(cursor), false, false}; - }; - - eiter_t end() - { - return eiter_t(); - } - - // basis for find, lower_bound - template - iter_t genfind(const typename std::tuple_element::type::type& key, MDB_cursor_op op) - { - typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); - - const auto keystr = keyConv(key); - MDBInVal in(keystr); - MDBOutVal out, id; - out.d_mdbval = in.d_mdbval; - - if(cursor.get(out, id, op)) { - // on_index, one_key, end - return iter_t{&d_parent, std::move(cursor), true, false, true}; - } - - return iter_t{&d_parent, std::move(cursor), true, false}; - }; - - template - iter_t find(const typename std::tuple_element::type::type& key) - { - return genfind(key, MDB_SET); - } - - template - iter_t lower_bound(const typename std::tuple_element::type::type& key) - { - return genfind(key, MDB_SET_RANGE); - } - - - //! equal range - could possibly be expressed through genfind - template - std::pair equal_range(const typename std::tuple_element::type::type& key) - { - typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); - - const auto keyString=keyConv(key); - MDBInVal in(keyString); - MDBOutVal out, id; - out.d_mdbval = in.d_mdbval; - - if(cursor.get(out, id, MDB_SET)) { - // on_index, one_key, end - return {iter_t{&d_parent, std::move(cursor), true, true, true}, eiter_t()}; - } - - return {iter_t{&d_parent, std::move(cursor), true, true}, eiter_t()}; - }; - - //! equal range - could possibly be expressed through genfind - template - std::pair prefix_range(const typename std::tuple_element::type::type& key) - { - typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get(d_parent.d_parent->d_tuple).d_idx); - - const auto keyString=keyConv(key); - MDBInVal in(keyString); - MDBOutVal out, id; - out.d_mdbval = in.d_mdbval; - - if(cursor.get(out, id, MDB_SET_RANGE)) { - // on_index, one_key, end - return {iter_t{&d_parent, std::move(cursor), true, true, true}, eiter_t()}; - } - - return {iter_t(&d_parent, std::move(cursor), keyString), eiter_t()}; - }; - - - Parent& d_parent; - }; - - class LMDB_SAFE_EXPORT ROTransaction : public ReadonlyOperations - { - public: - explicit ROTransaction(TypedDBI* parent) : ReadonlyOperations(*this), d_parent(parent), d_txn(std::make_shared(d_parent->d_env->getROTransaction())) - { - } - - explicit ROTransaction(TypedDBI* parent, std::shared_ptr txn) : ReadonlyOperations(*this), d_parent(parent), d_txn(txn) - { - } - - - ROTransaction(ROTransaction&& rhs) : - ReadonlyOperations(*this), d_parent(rhs.d_parent),d_txn(std::move(rhs.d_txn)) - - { - rhs.d_parent = 0; - } - - std::shared_ptr getTransactionHandle() - { - return d_txn; - } - - typedef MDBROCursor cursor_t; - - TypedDBI* d_parent; - std::shared_ptr d_txn; - }; - - - class LMDB_SAFE_EXPORT RWTransaction : public ReadonlyOperations - { - public: - explicit RWTransaction(TypedDBI* parent) : ReadonlyOperations(*this), d_parent(parent) - { - d_txn = std::make_shared(d_parent->d_env->getRWTransaction()); - } - - explicit RWTransaction(TypedDBI* parent, std::shared_ptr txn) : ReadonlyOperations(*this), d_parent(parent), d_txn(txn) - { - } - - - RWTransaction(RWTransaction&& rhs) : - ReadonlyOperations(*this), - d_parent(rhs.d_parent), d_txn(std::move(rhs.d_txn)) - { - rhs.d_parent = 0; - } - - // insert something, with possibly a specific id - uint32_t put(const T& t, uint32_t id=0) - { - unsigned int flags = 0; - if(!id) { - id = MDBGetMaxID(*d_txn, d_parent->d_main) + 1; - flags = MDB_APPEND; - } - (*d_txn)->put(d_parent->d_main, id, serToString(t), flags); + // insert something, with possibly a specific id + uint32_t put(const T &t, uint32_t id = 0) + { + unsigned int flags = 0; + if (!id) { + id = MDBGetMaxID(*d_txn, d_parent->d_main) + 1; + flags = MDB_APPEND; + } + (*d_txn)->put(d_parent->d_main, id, serToString(t), flags); #define insertMacro(N) std::get(d_parent->d_tuple).put(*d_txn, t, id); - insertMacro(0); - insertMacro(1); - insertMacro(2); - insertMacro(3); + insertMacro(0); + insertMacro(1); + insertMacro(2); + insertMacro(3); #undef insertMacro - return id; - } + return id; + } - // modify an item 'in place', plus update indexes - void modify(uint32_t id, std::function func) - { - T t; - if(!this->get(id, t)) - throw LMDBError("Could not modify id " + std::to_string(id)); - func(t); - - del(id); // this is the lazy way. We could test for changed index fields - put(t, id); - } + // modify an item 'in place', plus update indexes + void modify(uint32_t id, std::function func) + { + T t; + if (!this->get(id, t)) + throw LMDBError("Could not modify id " + std::to_string(id)); + func(t); - //! delete an item, and from indexes - void del(uint32_t id) - { - T t; - if(!this->get(id, t)) - return; - - (*d_txn)->del(d_parent->d_main, id); - clearIndex(id, t); - } + del(id); // this is the lazy way. We could test for changed index fields + put(t, id); + } - //! clear database & indexes - void clear() - { - if (const auto rc = mdb_drop(**d_txn, d_parent->d_main, 0)) { - throw LMDBError("Error database: ", rc); - } + //! delete an item, and from indexes + void del(uint32_t id) + { + T t; + if (!this->get(id, t)) + return; + + (*d_txn)->del(d_parent->d_main, id); + clearIndex(id, t); + } + + //! clear database & indexes + void clear() + { + if (const auto rc = mdb_drop(**d_txn, d_parent->d_main, 0)) { + throw LMDBError("Error database: ", rc); + } #define clearMacro(N) std::get(d_parent->d_tuple).clear(*d_txn); - clearMacro(0); - clearMacro(1); - clearMacro(2); - clearMacro(3); + clearMacro(0); + clearMacro(1); + clearMacro(2); + clearMacro(3); #undef clearMacro - } + } - //! commit this transaction - void commit() - { - (*d_txn)->commit(); - } + //! commit this transaction + void commit() + { + (*d_txn)->commit(); + } - //! abort this transaction - void abort() - { - (*d_txn)->abort(); - } + //! abort this transaction + void abort() + { + (*d_txn)->abort(); + } - typedef MDBRWCursor cursor_t; + typedef MDBRWCursor cursor_t; - std::shared_ptr getTransactionHandle() - { - return d_txn; - } + std::shared_ptr getTransactionHandle() + { + return d_txn; + } - - private: - // clear this ID from all indexes - void clearIndex(uint32_t id, const T& t) - { + private: + // clear this ID from all indexes + void clearIndex(uint32_t id, const T &t) + { #define clearMacro(N) std::get(d_parent->d_tuple).del(*d_txn, t, id); - clearMacro(0); - clearMacro(1); - clearMacro(2); - clearMacro(3); -#undef clearMacro + clearMacro(0); + clearMacro(1); + clearMacro(2); + clearMacro(3); +#undef clearMacro + } + + public: + TypedDBI *d_parent; + std::shared_ptr d_txn; + }; + + //! Get an RW transaction + RWTransaction getRWTransaction() + { + return RWTransaction(this); } - public: - TypedDBI* d_parent; - std::shared_ptr d_txn; - }; + //! Get an RO transaction + ROTransaction getROTransaction() + { + return ROTransaction(this); + } - //! Get an RW transaction - RWTransaction getRWTransaction() - { - return RWTransaction(this); - } + //! Get an RW transaction + RWTransaction getRWTransaction(std::shared_ptr txn) + { + return RWTransaction(this, txn); + } - //! Get an RO transaction - ROTransaction getROTransaction() - { - return ROTransaction(this); - } + //! Get an RO transaction + ROTransaction getROTransaction(std::shared_ptr txn) + { + return ROTransaction(this, txn); + } - //! Get an RW transaction - RWTransaction getRWTransaction(std::shared_ptr txn) - { - return RWTransaction(this, txn); - } + std::shared_ptr getEnv() + { + return d_env; + } - //! Get an RO transaction - ROTransaction getROTransaction(std::shared_ptr txn) - { - return ROTransaction(this, txn); - } - - std::shared_ptr getEnv() - { - return d_env; - } - private: - std::shared_ptr d_env; - MDBDbi d_main; - std::string d_name; + std::shared_ptr d_env; + MDBDbi d_main; + std::string d_name; }; -} +} // namespace LMDBSafe