Improve comments

This commit is contained in:
Martchus 2022-02-21 23:46:32 +01:00
parent 6c21fe7cb1
commit bf6dbf0e42
2 changed files with 115 additions and 47 deletions

View File

@ -40,6 +40,9 @@ using string_view = boost::string_ref;
#endif #endif
#endif #endif
/*!
* \brief The LMDBError class is thrown when an error happens.
*/
class LMDB_SAFE_EXPORT LMDBError : public std::runtime_error { class LMDB_SAFE_EXPORT LMDBError : public std::runtime_error {
public: public:
explicit LMDBError(const std::string &error) noexcept explicit LMDBError(const std::string &error) noexcept
@ -79,19 +82,24 @@ public:
class MDBRWTransactionImpl; class MDBRWTransactionImpl;
class MDBROTransactionImpl; class MDBROTransactionImpl;
using MDBROTransaction = std::unique_ptr<MDBROTransactionImpl>; using MDBROTransaction = std::unique_ptr<MDBROTransactionImpl>;
using MDBRWTransaction = std::unique_ptr<MDBRWTransactionImpl>; using MDBRWTransaction = std::unique_ptr<MDBRWTransactionImpl>;
/*!
* \brief The MDBEnv class is a handle to an MDB environment.
*/
class LMDB_SAFE_EXPORT MDBEnv { class LMDB_SAFE_EXPORT MDBEnv {
public: 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);
/*!
* \brief Closes the MDB environment.
* \remarks Only a single thread may call this function. All transactions, databases, and cursors must already be closed
* before calling this function.
*/
~MDBEnv() ~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); 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); MDBDbi openDB(const string_view dbname, unsigned int flags);
@ -119,8 +127,14 @@ private:
std::map<std::thread::id, int> d_ROtransactionsOut; std::map<std::thread::id, int> d_ROtransactionsOut;
}; };
/*!
* \brief Opens an MDB environment for the specified database file.
*/
LMDB_SAFE_EXPORT std::shared_ptr<MDBEnv> getMDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 128); LMDB_SAFE_EXPORT std::shared_ptr<MDBEnv> getMDBEnv(const char *fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 128);
/*!
* \brief The MDBOutVal struct is the handle to an MDB value used as output.
*/
struct LMDB_SAFE_EXPORT MDBOutVal { struct LMDB_SAFE_EXPORT MDBOutVal {
operator MDB_val &() operator MDB_val &()
{ {
@ -170,6 +184,9 @@ template <> inline string_view MDBOutVal::get<string_view>() const
return string_view(static_cast<char *>(d_mdbval.mv_data), d_mdbval.mv_size); return string_view(static_cast<char *>(d_mdbval.mv_data), d_mdbval.mv_size);
} }
/*!
* \brief The MDBInVal struct is the handle to an MDB value used as input.
*/
class LMDB_SAFE_EXPORT MDBInVal { class LMDB_SAFE_EXPORT MDBInVal {
public: public:
MDBInVal(const MDBOutVal &rhs) MDBInVal(const MDBOutVal &rhs)
@ -226,6 +243,9 @@ private:
class MDBROCursor; class MDBROCursor;
/*!
* \brief The MDBROTransactionImpl class wraps read operations.
*/
class LMDB_SAFE_EXPORT MDBROTransactionImpl { class LMDB_SAFE_EXPORT MDBROTransactionImpl {
protected: protected:
MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn); MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn);
@ -465,6 +485,9 @@ public:
} }
}; };
/*!
* \brief The MDBROCursor class represents a read-only cursor.
*/
class LMDB_SAFE_EXPORT MDBROCursor : public MDBGenCursor<MDBROTransactionImpl, MDBROCursor> { class LMDB_SAFE_EXPORT MDBROCursor : public MDBGenCursor<MDBROTransactionImpl, MDBROCursor> {
public: public:
MDBROCursor() = default; MDBROCursor() = default;
@ -478,6 +501,9 @@ public:
class MDBRWCursor; class MDBRWCursor;
/*!
* \brief The MDBRWTransactionImpl class wraps write operations.
*/
class LMDB_SAFE_EXPORT MDBRWTransactionImpl : public MDBROTransactionImpl { class LMDB_SAFE_EXPORT MDBRWTransactionImpl : public MDBROTransactionImpl {
protected: protected:
MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn); MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn);

View File

@ -8,32 +8,37 @@
namespace LMDBSafe { namespace LMDBSafe {
/*
Open issues:
Perhaps use the separate index concept from multi_index
perhaps get eiter to be of same type so for(auto& a : x) works
make it more value "like" with unique_ptr
*/
/*! /*!
* \brief The type used to store IDs. "0" indicates "no such ID". * \brief The type used to store IDs. "0" indicates "no such ID".
*/ */
using IDType = std::uint32_t; using IDType = std::uint32_t;
/** Return the highest ID used in a database. Returns 0 for an empty DB. /*!
This makes us start everything at ID=1, which might make it possible to * \brief Returns the highest ID used in a database. Returns 0 for an empty DB.
treat id 0 as special * \remarks
*/ * This makes us start everything at ID "1", which might make it possible to
* treat id 0 as special.
*/
LMDB_SAFE_EXPORT IDType MDBGetMaxID(MDBRWTransaction &txn, MDBDbi &dbi); LMDB_SAFE_EXPORT IDType 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. * \brief Converts \a t to an std::string.
*/ *
* This is the serialization interface. You need to define this function for the
* types you'd like to store.
*/
template <typename T> std::string serToString(const T &t); template <typename T> std::string serToString(const T &t);
/*!
* \brief Initializes \a ret from \a str.
*
* This is the deserialization interface. You need to define this function for the
* types you'd like to store.
*/
template <typename T> void serFromString(string_view str, T &ret); template <typename T> void serFromString(string_view str, T &ret);
// define some "shortcuts" (to avoid full-blown serialization stuff for trivial cases) /// \cond
/// Define some "shortcuts" (to avoid full-blown serialization stuff for trivial cases):
template <> inline std::string serToString(const std::string_view &t) template <> inline std::string serToString(const std::string_view &t)
{ {
return std::string(t); return std::string(t);
@ -107,9 +112,14 @@ template <> inline void serFromString<>(string_view str, std::uint64_t &ret)
ret = CppUtilities::LE::toUInt64(str.data()); ret = CppUtilities::LE::toUInt64(str.data());
} }
/** This is the serialization interface for keys. /// \endcond
You need to define your these functions for the types you'd like to use as keys.
*/ /*!
* \brief Converts \a t to an std::string.
*
* This is the serialization interface for keys. You need to define your this function
* for the types you'd like to use as keys.
*/
template <class T, class Enable> inline std::string keyConv(const T &t); template <class T, class Enable> inline std::string keyConv(const T &t);
template <class T, typename std::enable_if<std::is_arithmetic<T>::value, T>::type * = nullptr> inline string_view keyConv(const T &t) template <class T, typename std::enable_if<std::is_arithmetic<T>::value, T>::type * = nullptr> inline string_view keyConv(const T &t)
@ -117,6 +127,8 @@ template <class T, typename std::enable_if<std::is_arithmetic<T>::value, T>::typ
return string_view(reinterpret_cast<const char *>(&t), sizeof(t)); return string_view(reinterpret_cast<const char *>(&t), sizeof(t));
} }
/// \cond
/// Define keyConv for trivial cases:
template <class T, typename std::enable_if<std::is_same<T, std::string>::value, T>::type * = nullptr> inline string_view keyConv(const T &t) template <class T, typename std::enable_if<std::is_same<T, std::string>::value, T>::type * = nullptr> inline string_view keyConv(const T &t)
{ {
return t; return t;
@ -126,6 +138,7 @@ template <class T, typename std::enable_if<std::is_same<T, string_view>::value,
{ {
return t; return t;
} }
/// \endcond
/*! /*!
* \brief The LMDBIndexOps struct implements index operations, but only the operations that * \brief The LMDBIndexOps struct implements index operations, but only the operations that
@ -135,7 +148,7 @@ template <class T, typename std::enable_if<std::is_same<T, string_view>::value,
* only includes calls that should be ignored for empty indexes. * only includes calls that should be ignored for empty indexes.
* *
* This class only needs methods that must happen for all indexes at once. So specifically, *not* * This class only needs methods that must happen for all indexes at once. So specifically, *not*
* size<t> or get<t>. People ask for those themselves, and should no do that on indexes that * size<t> or get<t>. People ask for those themselves, and should not do that on indexes that
* don't exist. * don't exist.
*/ */
template <class Class, typename Type, typename Parent> struct LMDB_SAFE_EXPORT LMDBIndexOps { template <class Class, typename Type, typename Parent> struct LMDB_SAFE_EXPORT LMDBIndexOps {
@ -170,8 +183,9 @@ template <class Class, typename Type, typename Parent> struct LMDB_SAFE_EXPORT L
Parent *d_parent; Parent *d_parent;
}; };
/** This is an index on a field in a struct, it derives from the LMDBIndexOps */ /*!
* \brief The index_on struct is used to declare an index on a member variable of a particular type.
*/
template <class Class, typename Type, Type Class::*PtrToMember> struct index_on : LMDBIndexOps<Class, Type, index_on<Class, Type, PtrToMember>> { template <class Class, typename Type, Type Class::*PtrToMember> struct index_on : LMDBIndexOps<Class, Type, index_on<Class, Type, PtrToMember>> {
index_on() index_on()
: LMDBIndexOps<Class, Type, index_on<Class, Type, PtrToMember>>(this) : LMDBIndexOps<Class, Type, index_on<Class, Type, PtrToMember>>(this)
@ -185,7 +199,10 @@ template <class Class, typename Type, Type Class::*PtrToMember> struct index_on
typedef Type type; typedef Type type;
}; };
/** This is a calculated index */ /*!
* \brief The index_on_function struct is used to declare an index which is dynamically computed via
* a function.
*/
template <class Class, typename Type, class Func> struct index_on_function : LMDBIndexOps<Class, Type, index_on_function<Class, Type, Func>> { template <class Class, typename Type, class Func> struct index_on_function : LMDBIndexOps<Class, Type, index_on_function<Class, Type, Func>> {
index_on_function() index_on_function()
: LMDBIndexOps<Class, Type, index_on_function<Class, Type, Func>>(this) : LMDBIndexOps<Class, Type, index_on_function<Class, Type, Func>>(this)
@ -204,6 +221,11 @@ template <class Class, typename Type, class Func> struct index_on_function : LMD
* \brief The TypedDBI class is the main class. * \brief The TypedDBI class is the main class.
* \tparam T Specifies the type to store within the database. * \tparam T Specifies the type to store within the database.
* \tparam I Specifies an index, should be an instantiation of index_on. * \tparam I Specifies an index, should be an instantiation of index_on.
*
* Open issues:
* - Perhaps use the separate index concept from multi_index.
* - Perhaps get eiter to be of same type so for(auto& a : x) works
* make it more value "like" with unique_ptr.
*/ */
template <typename T, typename... I> class LMDB_SAFE_EXPORT TypedDBI { template <typename T, typename... I> class LMDB_SAFE_EXPORT TypedDBI {
public: public:
@ -214,6 +236,7 @@ public:
private: private:
tuple_t d_tuple; tuple_t d_tuple;
/// \cond
template <class Tuple, std::size_t N> struct IndexIterator { template <class Tuple, std::size_t N> struct IndexIterator {
static inline void apply(Tuple &tuple, auto &&func) static inline void apply(Tuple &tuple, auto &&func)
{ {
@ -238,6 +261,7 @@ private:
{ {
IndexIterator<tuple_t, std::tuple_size_v<tuple_t>>::apply(d_tuple, std::forward<decltype(func)>(func)); IndexIterator<tuple_t, std::tuple_size_v<tuple_t>>::apply(d_tuple, std::forward<decltype(func)>(func));
} }
/// \endcond
public: public:
TypedDBI(std::shared_ptr<MDBEnv> env, string_view name) TypedDBI(std::shared_ptr<MDBEnv> env, string_view name)
@ -249,8 +273,9 @@ public:
forEachIndex([&](auto &&i) { i.openDB(d_env, CppUtilities::argsToString(name, '_', index++), MDB_CREATE | MDB_DUPFIXED | MDB_DUPSORT); }); forEachIndex([&](auto &&i) { i.openDB(d_env, CppUtilities::argsToString(name, '_', index++), MDB_CREATE | MDB_DUPFIXED | MDB_DUPSORT); });
} }
// We support readonly and rw transactions. Here we put the Readonly operations /*!
// which get sourced by both kinds of transactions * \brief The ReadonlyOperations struct defines read-only operations.
*/
template <class Parent> struct ReadonlyOperations { template <class Parent> struct ReadonlyOperations {
ReadonlyOperations(Parent &parent) ReadonlyOperations(Parent &parent)
: d_parent(parent) : d_parent(parent)
@ -309,7 +334,9 @@ public:
return count; return count;
} }
//! End iderator type /*!
* \brief The eiter_t struct is the end iterator.
*/
struct eiter_t { struct eiter_t {
}; };
@ -317,11 +344,18 @@ public:
template <typename> struct DirectStorage { template <typename> struct DirectStorage {
}; };
// can be on main, or on an index /*!
// when on main, return data directly * \brief The iter_t struct is the iterator type for walking through the database rows.
// when on index, indirect * \remarks
// we can be limited to one key, or iterate over entire database * - The iterator can be on the main database or on an index. It returns the data directly on
// iter requires you to put the cursor in the right place first! * the main database and indirectly when on an index.
* - An iterator can be limited to one key or iterate over the entire database.
* - The iter_t struct requires you to put the cursor in the right place first.
* - The object can be stored as direct member of iter_t or as std::unique_ptr or std::shared_ptr
* by specifying the corresponding template as \tp ElementType. The pointer can then be accessed
* via getPointer(). Note that the returned pointer object is re-used when the iterator is incremented
* or decremented unless the owned object is moved into another pointer object.
*/
template <template <typename> class StorageType, typename ElementType = T> struct iter_t { template <template <typename> class StorageType, typename ElementType = T> struct iter_t {
using UsingDirectStorage = CppUtilities::Traits::IsSpecializationOf<StorageType<ElementType>, DirectStorage>; using UsingDirectStorage = CppUtilities::Traits::IsSpecializationOf<StorageType<ElementType>, DirectStorage>;
@ -345,7 +379,7 @@ public:
: d_parent(parent) : d_parent(parent)
, d_cursor(std::move(cursor)) , d_cursor(std::move(cursor))
, d_prefix(prefix) , d_prefix(prefix)
, d_on_index(true) // is this an iterator on main database or on index? , d_on_index(true)
, d_one_key(false) , d_one_key(false)
, d_end(false) , d_end(false)
, d_deserialized(false) , d_deserialized(false)
@ -356,7 +390,6 @@ public:
throw LMDBError("Missing id in constructor"); throw LMDBError("Missing id in constructor");
} }
std::function<bool(const MDBOutVal &)> filter;
void del() void del()
{ {
d_cursor.del(); d_cursor.del();
@ -433,7 +466,7 @@ public:
return &value(); return &value();
} }
// implements generic ++ or -- //! Implements generic ++ or --.
iter_t &genoperator(MDB_cursor_op dupop, MDB_cursor_op op) iter_t &genoperator(MDB_cursor_op dupop, MDB_cursor_op op)
{ {
d_deserialized = false; d_deserialized = false;
@ -463,7 +496,7 @@ public:
return genoperator(MDB_PREV_DUP, MDB_PREV); return genoperator(MDB_PREV_DUP, MDB_PREV);
} }
// get ID this iterator points to //! Returns the ID this iterator points to.
IDType getID() IDType getID()
{ {
return d_on_index ? d_id.get<IDType>() : d_key.get<IDType>(); return d_on_index ? d_id.get<IDType>() : d_key.get<IDType>();
@ -474,14 +507,17 @@ public:
return d_key; return d_key;
} }
//! A filter to allow skipping rows by their raw value.
std::function<bool(const MDBOutVal &)> filter;
private: private:
// transaction we are part of //! The transaction the iterator is part of.
Parent *d_parent; Parent *d_parent;
typename Parent::cursor_t d_cursor; typename Parent::cursor_t d_cursor;
// gcc complains if I don't zero-init these, which is worrying XXX
std::string d_prefix; std::string d_prefix;
MDBOutVal d_key{ { 0, 0 } }, d_data{ { 0, 0 } }, d_id{ { 0, 0 } }; MDBOutVal d_key{ { 0, 0 } }, d_data{ { 0, 0 } }, d_id{ { 0, 0 } };
//! Whether it is an iterator on the main database or an index.
bool d_on_index; bool d_on_index;
bool d_one_key; bool d_one_key;
bool d_end; bool d_end;
@ -575,7 +611,7 @@ public:
return genfind<N, StorageType>(key, MDB_SET_RANGE); return genfind<N, StorageType>(key, MDB_SET_RANGE);
} }
//! equal range - could possibly be expressed through genfind //! Returns the range matching the specified \a key.
template <std::size_t N, template <typename> class StorageType = DirectStorage> template <std::size_t N, template <typename> class StorageType = DirectStorage>
std::pair<iter_t<StorageType>, eiter_t> equal_range(const index_t<N> &key) std::pair<iter_t<StorageType>, eiter_t> equal_range(const index_t<N> &key)
{ {
@ -594,7 +630,7 @@ public:
return { iter_t<StorageType>{ &d_parent, std::move(cursor), true, true }, eiter_t() }; return { iter_t<StorageType>{ &d_parent, std::move(cursor), true, true }, eiter_t() };
}; };
//! equal range - could possibly be expressed through genfind //! Returns the range where the key starts with the specified \a key.
template <std::size_t N, template <typename> class StorageType = DirectStorage> template <std::size_t N, template <typename> class StorageType = DirectStorage>
std::pair<iter_t<StorageType>, eiter_t> prefix_range(const index_t<N> &key) std::pair<iter_t<StorageType>, eiter_t> prefix_range(const index_t<N> &key)
{ {
@ -616,6 +652,9 @@ public:
Parent &d_parent; Parent &d_parent;
}; };
/*!
* \brief The ROTransaction class represents a read-only transaction.
*/
class LMDB_SAFE_EXPORT ROTransaction : public ReadonlyOperations<ROTransaction> { class LMDB_SAFE_EXPORT ROTransaction : public ReadonlyOperations<ROTransaction> {
public: public:
explicit ROTransaction() explicit ROTransaction()
@ -677,6 +716,9 @@ public:
std::shared_ptr<MDBROTransaction> d_txn; std::shared_ptr<MDBROTransaction> d_txn;
}; };
/*!
* \brief The RWTransaction class represents a read-write transaction.
*/
class LMDB_SAFE_EXPORT RWTransaction : public ReadonlyOperations<RWTransaction> { class LMDB_SAFE_EXPORT RWTransaction : public ReadonlyOperations<RWTransaction> {
public: public:
explicit RWTransaction() explicit RWTransaction()
@ -721,7 +763,7 @@ public:
return d_txn != nullptr; return d_txn != nullptr;
} }
// insert something, with possibly a specific id //! Inserts something, with possibly a specific id.
IDType put(const T &t, IDType id = 0) IDType put(const T &t, IDType id = 0)
{ {
unsigned int flags = 0; unsigned int flags = 0;
@ -734,7 +776,7 @@ public:
return id; return id;
} }
// modify an item 'in place', plus update indexes //! Modifies an item "in place" updating indexes.
void modify(IDType id, std::function<void(T &)> func) void modify(IDType id, std::function<void(T &)> func)
{ {
T t; T t;
@ -746,7 +788,7 @@ public:
put(t, id); put(t, id);
} }
//! delete an item, and from indexes //! Deletes an item from the main database and from indexes.
void del(IDType id) void del(IDType id)
{ {
T t; T t;
@ -757,7 +799,7 @@ public:
clearIndex(id, t); clearIndex(id, t);
} }
//! clear database & indexes //! Clears the database and indexes.
void clear() void clear()
{ {
if (const auto rc = mdb_drop(**d_txn, d_parent->d_main, 0)) { if (const auto rc = mdb_drop(**d_txn, d_parent->d_main, 0)) {
@ -766,13 +808,13 @@ public:
d_parent->forEachIndex([&](auto &&i) { i.clear(*d_txn); }); d_parent->forEachIndex([&](auto &&i) { i.clear(*d_txn); });
} }
//! commit this transaction //! Commits this transaction.
void commit() void commit()
{ {
(*d_txn)->commit(); (*d_txn)->commit();
} }
//! abort this transaction //! Aborts this transaction.
void abort() void abort()
{ {
(*d_txn)->abort(); (*d_txn)->abort();