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.cpp170
-rw-r--r--common/storage_lmdb.cpp282
4 files changed, 491 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..0b548fb
--- /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 bool read(const std::string &sKey, const std::function<void(const std::string &value)> &);
20 bool 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..40bd3e6
--- /dev/null
+++ b/common/storage_kyoto.cpp
@@ -0,0 +1,170 @@
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
121bool Storage::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler)
122{
123 if (!d->dbOpen) {
124 return false;
125 }
126
127 std::string value;
128 if (d->db.get(sKey, &value)) {
129 resultHandler(value);
130 return true;
131 }
132
133 return false;
134}
135
136bool Storage::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
137{
138 if (!d->dbOpen) {
139 return false;
140 }
141
142 size_t valueSize;
143 char *valueBuffer = d->db.get(sKey.data(), sKey.size(), &valueSize);
144 if (valueBuffer) {
145 resultHandler(valueBuffer, valueSize);
146 }
147 delete[] valueBuffer;
148 return valueBuffer != nullptr;
149}
150
151qint64 Storage::diskUsage() const
152{
153 if (!d->dbOpen) {
154 return 0;
155 }
156
157 QFileInfo info(QString::fromStdString(d->db.path()));
158 return info.size();
159}
160
161void Storage::removeFromDisk() const
162{
163 if (!d->dbOpen) {
164 return;
165 }
166
167 QFileInfo info(QString::fromStdString(d->db.path()));
168 QDir dir = info.dir();
169 dir.remove(info.fileName());
170}
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
new file mode 100644
index 0000000..283205f
--- /dev/null
+++ b/common/storage_lmdb.cpp
@@ -0,0 +1,282 @@
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 &s, const QString &name);
18 ~Private();
19
20 QString storageRoot;
21 QString name;
22
23 MDB_dbi dbi;
24 MDB_env *env;
25 MDB_txn *transaction;
26 bool readTransaction;
27 bool firstOpen;
28};
29
30Storage::Private::Private(const QString &s, const QString &n)
31 : transaction(0),
32 readTransaction(false),
33 firstOpen(true),
34 storageRoot(s),
35 name(n)
36{
37 QDir dir;
38 dir.mkdir(storageRoot);
39
40 //create file
41 if (mdb_env_create(&env)) {
42 // TODO: handle error
43 } else {
44 int rc = mdb_env_open(env, storageRoot.toStdString().data(), 0, 0664);
45
46 if (rc) {
47 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl;
48 mdb_env_close(env);
49 env = 0;
50 } else {
51 const size_t dbSize = 10485760 * 100; //10MB * 100
52 mdb_env_set_mapsize(env, dbSize);
53 }
54 }
55}
56
57Storage::Private::~Private()
58{
59 if (transaction) {
60 mdb_txn_abort(transaction);
61 }
62
63 // it is still there and still unused, so we can shut it down
64 mdb_dbi_close(env, dbi);
65 mdb_env_close(env);
66}
67
68Storage::Storage(const QString &storageRoot, const QString &name)
69 : d(new Private(storageRoot, name))
70{
71}
72
73Storage::~Storage()
74{
75 delete d;
76}
77
78bool Storage::isInTransaction() const
79{
80 return d->transaction;
81}
82
83bool Storage::startTransaction(TransactionType type)
84{
85 if (!d->env) {
86 return false;
87 }
88
89 bool requestedRead = type == ReadOnly;
90 if (d->transaction && (!d->readTransaction || requestedRead)) {
91 return true;
92 }
93
94 if (d->transaction) {
95 // we are about to turn a read transaction into a writable one
96 abortTransaction();
97 }
98
99 if (d->firstOpen && requestedRead) {
100 //A write transaction is at least required the first time
101 mdb_txn_begin(d->env, nullptr, 0, &d->transaction);
102 //Open the database
103 //With this we could open multiple named databases if we wanted to
104 mdb_dbi_open(d->transaction, nullptr, 0, &d->dbi);
105 mdb_txn_abort(d->transaction);
106 }
107
108 int rc;
109 rc = mdb_txn_begin(d->env, NULL, requestedRead ? MDB_RDONLY : 0, &d->transaction);
110 if (!rc) {
111 rc = mdb_dbi_open(d->transaction, NULL, 0, &d->dbi);
112 }
113
114 d->firstOpen = false;
115 return !rc;
116}
117
118bool Storage::commitTransaction()
119{
120 if (!d->env) {
121 return false;
122 }
123
124 if (!d->transaction) {
125 return false;
126 }
127
128 int rc;
129 rc = mdb_txn_commit(d->transaction);
130 d->transaction = 0;
131
132 if (rc) {
133 std::cerr << "mdb_txn_commit: " << rc << " " << mdb_strerror(rc) << std::endl;
134 }
135
136 return !rc;
137}
138
139void Storage::abortTransaction()
140{
141 if (!d->env || !d->transaction) {
142 return;
143 }
144
145 mdb_txn_abort(d->transaction);
146 d->transaction = 0;
147}
148
149bool Storage::write(const char *key, size_t keySize, const char *value, size_t valueSize)
150{
151 write(std::string(key, keySize), std::string(value, valueSize));
152}
153
154bool Storage::write(const std::string &sKey, const std::string &sValue)
155{
156 if (!d->env) {
157 return false;
158 }
159
160 const bool implicitTransaction = !d->transaction || d->readTransaction;
161 if (implicitTransaction) {
162 // TODO: if this fails, still try the write below?
163 if (!startTransaction()) {
164 return false;
165 }
166 }
167
168 int rc;
169 MDB_val key, data;
170 key.mv_size = sKey.size();
171 key.mv_data = (void*)sKey.data();
172 data.mv_size = sValue.size();
173 data.mv_data = (void*)sValue.data();
174 rc = mdb_put(d->transaction, d->dbi, &key, &data, 0);
175
176 if (rc) {
177 std::cerr << "mdb_put: " << rc << " " << mdb_strerror(rc) << std::endl;
178 }
179
180 if (implicitTransaction) {
181 if (rc) {
182 abortTransaction();
183 } else {
184 rc = commitTransaction();
185 }
186 }
187
188 return !rc;
189}
190
191bool Storage::read(const std::string &sKey, const std::function<void(const std::string &value)> &resultHandler)
192{
193 return read(sKey,
194 [&](void *ptr, int size) {
195 const std::string resultValue(static_cast<char*>(ptr), size);
196 resultHandler(resultValue);
197 });
198// std::cout << "key: " << resultKey << " data: " << resultValue << std::endl;
199}
200
201bool Storage::read(const std::string &sKey, const std::function<void(void *ptr, int size)> &resultHandler)
202{
203 if (!d->env) {
204 return false;
205 }
206
207 int rc;
208 MDB_val key;
209 MDB_val data;
210 MDB_cursor *cursor;
211
212 key.mv_size = sKey.size();
213 key.mv_data = (void*)sKey.data();
214
215 const bool implicitTransaction = !d->transaction;
216 if (implicitTransaction) {
217 // TODO: if this fails, still try the write below?
218 if (!startTransaction(ReadOnly)) {
219 return false;
220 }
221 }
222
223 rc = mdb_cursor_open(d->transaction, d->dbi, &cursor);
224 if (rc) {
225 std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl;
226 return false;
227 }
228
229 if (sKey.empty()) {
230 std::cout << "Iterating over all values of store!" << std::endl;
231 rc = mdb_cursor_get(cursor, &key, &data, MDB_FIRST);
232 while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
233 resultHandler(key.mv_data, data.mv_size);
234 }
235
236 //We never find the last value
237 if (rc == MDB_NOTFOUND) {
238 rc = 0;
239 }
240 } else {
241 if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) {
242 resultHandler(data.mv_data, data.mv_size);
243 } else {
244 std::cout << "couldn't find value " << sKey << " " << std::endl;
245 }
246 }
247
248 mdb_cursor_close(cursor);
249
250 if (rc) {
251 std::cerr << "mdb_cursor_get: " << rc << " " << mdb_strerror(rc) << std::endl;
252 return false;
253 }
254
255 /**
256 we don't abort the transaction since we need it for reading the values
257 if (implicitTransaction) {
258 abortTransaction();
259 }
260 */
261 return true;
262}
263
264qint64 Storage::diskUsage() const
265{
266 QFileInfo info(d->storageRoot + "/data.mdb");
267 return info.size();
268}
269
270void Storage::removeFromDisk() const
271{
272 QDir dir(d->path);
273 dir.remove("data.mdb");
274 dir.remove("lock.mdb");
275}
276
277void Storage::removeFromDisk() const
278{
279 QDir dir(d->storageRoot);
280 dir.remove("data.mdb");
281 dir.remove("lock.mdb");
282}