forked from blue/squawk
full transition to lmdbal, DOESNT WORK, DONT TAKE!
This commit is contained in:
parent
23ec80ccba
commit
9d688e8596
18 changed files with 497 additions and 1752 deletions
File diff suppressed because it is too large
Load diff
|
@ -29,18 +29,21 @@
|
|||
#include <lmdb.h>
|
||||
#include <list>
|
||||
|
||||
#include <lmdbal/base.h>
|
||||
#include <lmdbal/storage.h>
|
||||
#include <lmdbal/cursor.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Archive : public QObject
|
||||
{
|
||||
class Archive : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class AvatarInfo;
|
||||
|
||||
Archive(const QString& jid, QObject* parent = 0);
|
||||
Archive(const QString& account, const QString& jid, QObject* parent = 0);
|
||||
~Archive();
|
||||
|
||||
void open(const QString& account);
|
||||
void open();
|
||||
void close();
|
||||
|
||||
bool addElement(const Shared::Message& message);
|
||||
|
@ -48,13 +51,13 @@ public:
|
|||
Shared::Message getElement(const QString& id) const;
|
||||
bool hasElement(const QString& id) const;
|
||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||
Shared::Message oldest();
|
||||
QString oldestId();
|
||||
Shared::Message newest();
|
||||
QString newestId();
|
||||
Shared::Message oldest() const;
|
||||
QString oldestId() const;
|
||||
Shared::Message newest() const;
|
||||
QString newestId() const;
|
||||
void clear();
|
||||
long unsigned int size() const;
|
||||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
||||
std::list<Shared::Message> getBefore(unsigned int count, const QString& id);
|
||||
bool isFromTheBeginning() const;
|
||||
void setFromTheBeginning(bool is);
|
||||
bool isEncryptionEnabled() const;
|
||||
|
@ -68,103 +71,14 @@ public:
|
|||
|
||||
public:
|
||||
const QString jid;
|
||||
const QString account;
|
||||
|
||||
public:
|
||||
class Directory:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Directory(const std::string& p_path):Exception(), path(p_path){}
|
||||
|
||||
std::string getMessage() const{return "Can't create directory for database at " + path;}
|
||||
private:
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class Closed:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){}
|
||||
|
||||
std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;}
|
||||
private:
|
||||
std::string operation;
|
||||
std::string account;
|
||||
};
|
||||
|
||||
class NotFound:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){}
|
||||
|
||||
std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;}
|
||||
private:
|
||||
std::string key;
|
||||
std::string account;
|
||||
};
|
||||
|
||||
class Empty:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Empty(const std::string& acc):Exception(), account(acc){}
|
||||
|
||||
std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";}
|
||||
private:
|
||||
std::string account;
|
||||
};
|
||||
|
||||
class Exist:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){}
|
||||
|
||||
std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";}
|
||||
private:
|
||||
std::string account;
|
||||
std::string key;
|
||||
};
|
||||
|
||||
class NoAvatar:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){
|
||||
if (resource.size() == 0) {
|
||||
resource = "for himself";
|
||||
}
|
||||
}
|
||||
|
||||
std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;}
|
||||
private:
|
||||
std::string element;
|
||||
std::string resource;
|
||||
};
|
||||
|
||||
class Unknown:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){}
|
||||
|
||||
std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;}
|
||||
private:
|
||||
std::string account;
|
||||
std::string msg;
|
||||
};
|
||||
|
||||
|
||||
class AvatarInfo {
|
||||
public:
|
||||
AvatarInfo();
|
||||
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
|
||||
|
||||
void deserialize(char* pointer, uint32_t size);
|
||||
void serialize(QByteArray* ba) const;
|
||||
|
||||
QString type;
|
||||
QByteArray hash;
|
||||
bool autogenerated;
|
||||
|
@ -172,29 +86,18 @@ public:
|
|||
|
||||
private:
|
||||
bool opened;
|
||||
bool fromTheBeginning;
|
||||
bool encryptionEnabled;
|
||||
MDB_env* environment;
|
||||
MDB_dbi main; //id to message
|
||||
MDB_dbi order; //time to id
|
||||
MDB_dbi stats;
|
||||
MDB_dbi avatars;
|
||||
MDB_dbi sid; //stanzaId to id
|
||||
|
||||
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
||||
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
||||
|
||||
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
|
||||
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
|
||||
bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const;
|
||||
void printOrder();
|
||||
void printKeys();
|
||||
bool dropAvatar(const std::string& resource);
|
||||
Shared::Message getMessage(const std::string& id, MDB_txn* txn) const;
|
||||
Shared::Message getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc);
|
||||
Shared::Message edge(bool end);
|
||||
LMDBAL::Base db;
|
||||
LMDBAL::Storage<QString, Shared::Message>* messages;
|
||||
LMDBAL::Storage<uint64_t, QString>* order;
|
||||
LMDBAL::Storage<QString, QVariant>* stats;
|
||||
LMDBAL::Storage<QString, AvatarInfo>* avatars;
|
||||
LMDBAL::Storage<QString, QString>* stanzaIdToId;
|
||||
mutable LMDBAL::Cursor<uint64_t, QString> cursor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info);
|
||||
QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info);
|
||||
|
||||
#endif // CORE_ARCHIVE_H
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
// Squawk messenger.
|
||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef CORE_CACHE_H
|
||||
#define CORE_CACHE_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <core/storage/storage.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
template <class K, class V>
|
||||
class Cache
|
||||
{
|
||||
public:
|
||||
Cache(const QString& name);
|
||||
~Cache();
|
||||
|
||||
void open();
|
||||
void close();
|
||||
|
||||
void addRecord(const K& key, const V& value);
|
||||
void changeRecord(const K& key, const V& value);
|
||||
void removeRecord(const K& key);
|
||||
V getRecord(const K& key) const;
|
||||
bool checkRecord(const K& key) const;
|
||||
|
||||
private:
|
||||
Core::Storage<K, V> storage;
|
||||
std::map<K, V>* cache;
|
||||
std::set<K>* abscent;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "cache.hpp"
|
||||
|
||||
#endif // CORE_CACHE_H
|
|
@ -1,102 +0,0 @@
|
|||
// Squawk messenger.
|
||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef CORE_CACHE_HPP
|
||||
#define CORE_CACHE_HPP
|
||||
#include "cache.h"
|
||||
|
||||
template <class K, class V>
|
||||
Core::Cache<K, V>::Cache(const QString& name):
|
||||
storage(name),
|
||||
cache(new std::map<K, V> ()),
|
||||
abscent(new std::set<K> ()) {}
|
||||
|
||||
template <class K, class V>
|
||||
Core::Cache<K, V>::~Cache() {
|
||||
close();
|
||||
delete cache;
|
||||
delete abscent;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Cache<K, V>::open() {
|
||||
storage.open();}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Cache<K, V>::close() {
|
||||
storage.close();}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Cache<K, V>::addRecord(const K& key, const V& value) {
|
||||
storage.addRecord(key, value);
|
||||
cache->insert(std::make_pair(key, value));
|
||||
abscent->erase(key);
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
V Core::Cache<K, V>::getRecord(const K& key) const {
|
||||
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
||||
if (itr == cache->end()) {
|
||||
if (abscent->count(key) > 0) {
|
||||
throw Archive::NotFound(std::to_string(key), storage.getName().toStdString());
|
||||
}
|
||||
|
||||
try {
|
||||
V value = storage.getRecord(key);
|
||||
itr = cache->insert(std::make_pair(key, value)).first;
|
||||
} catch (const Archive::NotFound& error) {
|
||||
abscent->insert(key);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
template<class K, class V>
|
||||
bool Core::Cache<K, V>::checkRecord(const K& key) const {
|
||||
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
||||
if (itr != cache->end())
|
||||
return true;
|
||||
|
||||
if (abscent->count(key) > 0)
|
||||
return false;
|
||||
|
||||
try {
|
||||
V value = storage.getRecord(key);
|
||||
itr = cache->insert(std::make_pair(key, value)).first;
|
||||
} catch (const Archive::NotFound& error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class K, class V>
|
||||
void Core::Cache<K, V>::changeRecord(const K& key, const V& value) {
|
||||
storage.changeRecord(key, value); //there is a non straightforward behaviour: if there was no element at the sorage it will be added
|
||||
cache->at(key) = value;
|
||||
abscent->erase(key); //so... this line here is to make it coherent with the storage
|
||||
}
|
||||
|
||||
template<class K, class V>
|
||||
void Core::Cache<K, V>::removeRecord(const K& key) {
|
||||
storage.removeRecord(key);
|
||||
cache->erase(key);
|
||||
abscent->insert(key);
|
||||
}
|
||||
|
||||
#endif //CORE_CACHE_HPP
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CORE_STORAGE_H
|
||||
#define CORE_STORAGE_H
|
||||
|
||||
#include <QString>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
template <class K, class V>
|
||||
class Storage
|
||||
{
|
||||
public:
|
||||
Storage(const QString& name);
|
||||
~Storage();
|
||||
|
||||
void open();
|
||||
void close();
|
||||
|
||||
void addRecord(const K& key, const V& value);
|
||||
void changeRecord(const K& key, const V& value);
|
||||
void removeRecord(const K& key);
|
||||
V getRecord(const K& key) const;
|
||||
QString getName() const;
|
||||
|
||||
|
||||
private:
|
||||
QString name;
|
||||
bool opened;
|
||||
MDB_env* environment;
|
||||
MDB_dbi base;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
MDB_val& operator << (MDB_val& data, QString& value);
|
||||
MDB_val& operator >> (MDB_val& data, QString& value);
|
||||
|
||||
MDB_val& operator << (MDB_val& data, uint32_t& value);
|
||||
MDB_val& operator >> (MDB_val& data, uint32_t& value);
|
||||
|
||||
namespace std {
|
||||
std::string to_string(const QString& str);
|
||||
}
|
||||
|
||||
#include "storage.hpp"
|
||||
|
||||
#endif // CORE_STORAGE_H
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
* Squawk messenger.
|
||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CORE_STORAGE_HPP
|
||||
#define CORE_STORAGE_HPP
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include "storage.h"
|
||||
#include <cstring>
|
||||
|
||||
template <class K, class V>
|
||||
Core::Storage<K, V>::Storage(const QString& p_name):
|
||||
name(p_name),
|
||||
opened(false),
|
||||
environment(),
|
||||
base()
|
||||
{
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
Core::Storage<K, V>::~Storage()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::open()
|
||||
{
|
||||
if (!opened) {
|
||||
mdb_env_create(&environment);
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + name;
|
||||
QDir cache(path);
|
||||
|
||||
if (!cache.exists()) {
|
||||
bool res = cache.mkpath(path);
|
||||
if (!res) {
|
||||
throw Archive::Directory(path.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
mdb_env_set_maxdbs(environment, 1);
|
||||
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
|
||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
|
||||
mdb_txn_commit(txn);
|
||||
opened = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::close()
|
||||
{
|
||||
if (opened) {
|
||||
mdb_dbi_close(environment, base);
|
||||
mdb_env_close(environment);
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::addRecord(const K& key, const V& value)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Archive::Closed("addRecord", name.toStdString());
|
||||
}
|
||||
QByteArray ba;
|
||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||
ds << value;
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey << key;
|
||||
|
||||
lmdbData.mv_size = ba.size();
|
||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
||||
if (rc != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_KEYEXIST) {
|
||||
throw Archive::Exist(name.toStdString(), std::to_string(key));
|
||||
} else {
|
||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::changeRecord(const K& key, const V& value)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Archive::Closed("changeRecord", name.toStdString());
|
||||
}
|
||||
|
||||
QByteArray ba;
|
||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||
ds << value;
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey << key;
|
||||
lmdbData.mv_size = ba.size();
|
||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc) {
|
||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
V Core::Storage<K, V>::getRecord(const K& key) const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Archive::Closed("addElement", name.toStdString());
|
||||
}
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey << key;
|
||||
|
||||
MDB_txn *txn;
|
||||
int rc;
|
||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||
rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
|
||||
if (rc) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw Archive::NotFound(std::to_string(key), name.toStdString());
|
||||
} else {
|
||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||
QDataStream ds(&ba, QIODevice::ReadOnly);
|
||||
V value;
|
||||
ds >> value;
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::removeRecord(const K& key)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Archive::Closed("addElement", name.toStdString());
|
||||
}
|
||||
|
||||
MDB_val lmdbKey;
|
||||
lmdbKey << key;
|
||||
|
||||
MDB_txn *txn;
|
||||
int rc;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
rc = mdb_del(txn, base, &lmdbKey, NULL);
|
||||
if (rc) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw Archive::NotFound(std::to_string(key), name.toStdString());
|
||||
} else {
|
||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
QString Core::Storage<K, V>::getName() const {
|
||||
return name;}
|
||||
|
||||
MDB_val& operator << (MDB_val& data, const QString& value) {
|
||||
QByteArray ba = value.toUtf8();
|
||||
data.mv_size = ba.size();
|
||||
data.mv_data = ba.data();
|
||||
return data;
|
||||
}
|
||||
MDB_val& operator >> (MDB_val& data, QString& value) {
|
||||
value = QString::fromUtf8((const char*)data.mv_data, data.mv_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
MDB_val& operator << (MDB_val& data, uint32_t& value) {
|
||||
data.mv_size = 4;
|
||||
data.mv_data = &value;
|
||||
return data;
|
||||
}
|
||||
MDB_val& operator >> (MDB_val& data, uint32_t& value) {
|
||||
std::memcpy(&value, data.mv_data, data.mv_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string std::to_string(const QString& str) {
|
||||
return str.toStdString();
|
||||
}
|
||||
#endif //CORE_STORAGE_HPP
|
Loading…
Add table
Add a link
Reference in a new issue