summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/CMakeLists.txt12
-rw-r--r--common/storage.h28
-rw-r--r--common/storage_kyoto.cpp164
-rw-r--r--common/storage_lmdb.cpp266
4 files changed, 469 insertions, 1 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index d409828..9b3f777 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -2,10 +2,20 @@ project(akonadinextcommon)
2generate_flatbuffers(commands/handshake 2generate_flatbuffers(commands/handshake
3 commands/revisionupdate) 3 commands/revisionupdate)
4 4
5if (STORAGE_KYOTO)
6 set(storage_SRCS storage_kyoto.cpp)
7 set(storage_LIBS kyotocabinet)
8else (STORAGE_KYOTO)
9 set(storage_SRCS storage_lmdb.cpp)
10 set(storage_LIBS lmdb)
11endif (STORAGE_KYOTO)
12
5set(command_SRCS 13set(command_SRCS
6 commands.cpp 14 commands.cpp
7 console.cpp 15 console.cpp
8 ${CMAKE_CURRENT_BINARY_DIR}/commands/handshake_generated.h) 16 ${storage_SRCS})
9 17
10add_library(${PROJECT_NAME} ${command_SRCS}) 18add_library(${PROJECT_NAME} ${command_SRCS})
19SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
11qt5_use_modules(${PROJECT_NAME} Widgets) 20qt5_use_modules(${PROJECT_NAME} Widgets)
21target_link_libraries(${PROJECT_NAME} ${storage_LIBS})
diff --git a/common/storage.h b/common/storage.h
new file mode 100644
index 0000000..f7dbd89
--- /dev/null
+++ b/common/storage.h
@@ -0,0 +1,28 @@
1#pragma once
2
3#include <string>
4#include <QString>
5
6class Storage {
7public:
8 enum TransactionType { ReadOnly, ReadWrite };
9
10 Storage(const QString &storageRoot, const QString &name);
11 ~Storage();
12 bool isInTransaction() const;
13 bool startTransaction(TransactionType type = ReadWrite);
14 bool commitTransaction();
15 void abortTransaction();
16 bool write(const char *key, size_t keySize, const char *value, size_t valueSize);
17 bool write(const std::string &sKey, const std::string &sValue);
18 //Perhaps prefer iterators (assuming we need to be able to match multiple values
19 void read(const std::string &sKey, const std::function<void(const std::string &value)> &);
20 void read(const std::string &sKey, const std::function<void(void *ptr, int size)> &);
21
22 qint64 diskUsage() const;
23 void removeFromDisk() const;
24private:
25 class Private;
26 Private * const d;
27};
28
diff --git a/common/storage_kyoto.cpp b/common/storage_kyoto.cpp
new file mode 100644
index 0000000..05942c2
--- /dev/null
+++ b/common/storage_kyoto.cpp
@@ -0,0 +1,164 @@
1#include "storage.h"
2
3#include <iostream>
4
5#include <QAtomicInt>
6#include <QDebug>
7#include <QDir>
8#include <QFileInfo>
9#include <QReadWriteLock>
10#include <QString>
11#include <QTime>
12
13#include <kchashdb.h>
14
15class Storage::Private
16{
17public:
18 Private(const QString &storageRoot, const QString &name);
19 ~Private();
20
21 kyotocabinet::TreeDB db;
22 bool dbOpen;
23 bool inTransaction;
24};
25
26Storage::Private::Private(const QString &storageRoot, const QString &name)
27 : inTransaction(false)
28{
29 QDir dir;
30 dir.mkdir(storageRoot);
31
32 //create file
33 dbOpen = db.open((storageRoot + "/" + name + ".kch").toStdString(), kyotocabinet::BasicDB::OWRITER | kyotocabinet::BasicDB::OCREATE);
34 if (!dbOpen) {
35 // TODO: handle error
36 }
37}
38
39Storage::Private::~Private()
40{
41 if (dbOpen && inTransaction) {
42 db.end_transaction(false);
43 }
44}
45
46Storage::Storage(const QString &storageRoot, const QString &name)
47 : d(new Private(storageRoot, name))
48{
49}
50
51Storage::~Storage()
52{
53 delete d;
54}
55
56bool Storage::isInTransaction() const
57{
58 return d->inTransaction;
59}
60
61bool Storage::startTransaction(TransactionType type)
62{
63 if (!d->dbOpen) {
64 return false;
65 }
66
67 if (d->inTransaction) {
68 return true;
69 }
70
71 //TODO handle errors
72 d->inTransaction = d->db.begin_transaction();
73 return d->inTransaction;
74}
75
76bool Storage::commitTransaction()
77{
78 if (!d->dbOpen) {
79 return false;
80 }
81
82 if (!d->inTransaction) {
83 return false;
84 }
85
86 bool success = d->db.end_transaction(true);
87 d->inTransaction = false;
88 return success;
89}
90
91void Storage::abortTransaction()
92{
93 if (!d->dbOpen || !d->inTransaction) {
94 return;
95 }
96
97 d->db.end_transaction(false);
98 d->inTransaction = false;
99}
100
101bool Storage::write(const char *key, size_t keySize, const char *value, size_t valueSize)
102{
103 if (!d->dbOpen) {
104 return false;
105 }
106
107 bool success = d->db.set(key, keySize, value, valueSize);
108 return success;
109}
110
111bool Storage::write(const std::string &sKey, const std::string &sValue)
112{
113 if (!d->dbOpen) {
114 return false;
115 }
116
117 bool success = d->db.set(sKey, sValue);
118 return success;
119}
120
121void Storage::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler)
122{
123 if (!d->dbOpen) {
124 return;
125 }
126
127 std::string value;
128 if (d->db.get(sKey, &value)) {
129 resultHandler(value);
130 }
131}
132
133void Storage::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
134{
135 if (!d->dbOpen) {
136 return;
137 }
138
139 size_t valueSize;
140 char *valueBuffer = d->db.get(sKey.data(), sKey.size(), &valueSize);
141 resultHandler(valueBuffer, valueSize);
142 delete[] valueBuffer;
143}
144
145qint64 Storage::diskUsage() const
146{
147 if (!d->dbOpen) {
148 return 0;
149 }
150
151 QFileInfo info(QString::fromStdString(d->db.path()));
152 return info.size();
153}
154
155void Storage::removeFromDisk() const
156{
157 if (!d->dbOpen) {
158 return;
159 }
160
161 QFileInfo info(QString::fromStdString(d->db.path()));
162 QDir dir = info.dir();
163 dir.remove(info.fileName());
164}
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
new file mode 100644
index 0000000..6c25448
--- /dev/null
+++ b/common/storage_lmdb.cpp
@@ -0,0 +1,266 @@
1#include "storage.h"
2
3#include <iostream>
4
5#include <QAtomicInt>
6#include <QDebug>
7#include <QDir>
8#include <QReadWriteLock>
9#include <QString>
10#include <QTime>
11
12#include <lmdb.h>
13
14class Storage::Private
15{
16public:
17 Private(const QString &p);
18 ~Private();
19
20 QString path;
21 MDB_dbi dbi;
22 MDB_env *env;
23 MDB_txn *transaction;
24 bool readTransaction;
25 bool firstOpen;
26};
27
28Storage::Private::Private(const QString &p)
29 : path(p),
30 transaction(0),
31 readTransaction(false),
32 firstOpen(true)
33{
34 QDir dir;
35 dir.mkdir(path);
36
37 //create file
38 if (mdb_env_create(&env)) {
39 // TODO: handle error
40 } else {
41 int rc = mdb_env_open(env, path.toStdString().data(), 0, 0664);
42
43 if (rc) {
44 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl;
45 mdb_env_close(env);
46 env = 0;
47 } else {
48 const size_t dbSize = 10485760 * 100; //10MB * 100
49 mdb_env_set_mapsize(env, dbSize);
50 }
51 }
52}
53
54Storage::Private::~Private()
55{
56 if (transaction) {
57 mdb_txn_abort(transaction);
58 }
59
60 // it is still there and still unused, so we can shut it down
61 mdb_dbi_close(env, dbi);
62 mdb_env_close(env);
63}
64
65Storage::Storage(const QString &storageRoot, const QString &name)
66 : d(new Private(storageRoot + '/' + name))
67{
68}
69
70Storage::~Storage()
71{
72 delete d;
73}
74
75bool Storage::isInTransaction() const
76{
77 return d->transaction;
78}
79
80bool Storage::startTransaction(TransactionType type)
81{
82 if (!d->env) {
83 return false;
84 }
85
86 bool requestedRead = type == ReadOnly;
87 if (d->transaction && (!d->readTransaction || requestedRead)) {
88 return true;
89 }
90
91 if (d->transaction) {
92 // we are about to turn a read transaction into a writable one
93 abortTransaction();
94 }
95
96 if (d->firstOpen && requestedRead) {
97 //A write transaction is at least required the first time
98 mdb_txn_begin(d->env, nullptr, 0, &d->transaction);
99 //Open the database
100 //With this we could open multiple named databases if we wanted to
101 mdb_dbi_open(d->transaction, nullptr, 0, &d->dbi);
102 mdb_txn_abort(d->transaction);
103 }
104
105 int rc;
106 rc = mdb_txn_begin(d->env, NULL, requestedRead ? MDB_RDONLY : 0, &d->transaction);
107 if (!rc) {
108 rc = mdb_dbi_open(d->transaction, NULL, 0, &d->dbi);
109 }
110
111 d->firstOpen = false;
112 return !rc;
113}
114
115bool Storage::commitTransaction()
116{
117 if (!d->env) {
118 return false;
119 }
120
121 if (!d->transaction) {
122 return false;
123 }
124
125 int rc;
126 rc = mdb_txn_commit(d->transaction);
127 d->transaction = 0;
128
129 if (rc) {
130 std::cerr << "mdb_txn_commit: " << rc << " " << mdb_strerror(rc) << std::endl;
131 }
132
133 return !rc;
134}
135
136void Storage::abortTransaction()
137{
138 if (!d->env || !d->transaction) {
139 return;
140 }
141
142 mdb_txn_abort(d->transaction);
143 d->transaction = 0;
144}
145
146bool Storage::write(const std::string &sKey, const std::string &sValue)
147{
148 if (!d->env) {
149 return false;
150 }
151
152 const bool implicitTransaction = !d->transaction || d->readTransaction;
153 if (implicitTransaction) {
154 // TODO: if this fails, still try the write below?
155 if (!startTransaction()) {
156 return false;
157 }
158 }
159
160 int rc;
161 MDB_val key, data;
162 key.mv_size = sKey.size();
163 key.mv_data = (void*)sKey.data();
164 data.mv_size = sValue.size();
165 data.mv_data = (void*)sValue.data();
166 rc = mdb_put(d->transaction, d->dbi, &key, &data, 0);
167
168 if (rc) {
169 std::cerr << "mdb_put: " << rc << " " << mdb_strerror(rc) << std::endl;
170 }
171
172 if (implicitTransaction) {
173 if (rc) {
174 abortTransaction();
175 } else {
176 rc = commitTransaction();
177 }
178 }
179
180 return !rc;
181}
182
183void Storage::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler)
184{
185 read(sKey,
186 [&](void *ptr, int size) {
187 const std::string resultValue(static_cast<char*>(ptr), size);
188 resultHandler(resultValue);
189 });
190// std::cout << "key: " << resultKey << " data: " << resultValue << std::endl;
191}
192
193void Storage::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
194{
195 if (!d->env) {
196 return;
197 }
198
199 int rc;
200 MDB_val key;
201 MDB_val data;
202 MDB_cursor *cursor;
203
204 key.mv_size = sKey.size();
205 key.mv_data = (void*)sKey.data();
206
207 const bool implicitTransaction = !d->transaction;
208 if (implicitTransaction) {
209 // TODO: if this fails, still try the write below?
210 if (!startTransaction(ReadOnly)) {
211 return;
212 }
213 }
214
215 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor);
216 if (rc) {
217 std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl;
218 return;
219 }
220
221 if (sKey.empty()) {
222 std::cout << "Iterating over all values of store!" << std::endl;
223 rc = mdb_cursor_get(cursor, &key, &data, MDB_FIRST);
224 while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
225 resultHandler(key.mv_data, data.mv_size);
226 }
227
228 //We never find the last value
229 if (rc == MDB_NOTFOUND) {
230 rc = 0;
231 }
232 } else {
233 if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) {
234 resultHandler(data.mv_data, data.mv_size);
235 } else {
236 std::cout << "couldn't find value " << sKey << " " << std::endl;
237 }
238 }
239
240 if (rc) {
241 std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl;
242 }
243
244 mdb_cursor_close(cursor);
245
246 /**
247 we don't abort the transaction since we need it for reading the values
248 if (implicitTransaction) {
249 abortTransaction();
250 }
251 */
252}
253
254qint64 Storage::diskUsage() const
255{
256 QFileInfo info(d->path, "data.mdb");
257 return info.size();
258}
259
260void Storage::removeFromDisk() const
261{
262 QDir dir(d->path);
263 dir.remove("data.mdb");
264 dir.remove("lock.mdb");
265}
266