diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/CMakeLists.txt | 12 | ||||
-rw-r--r-- | common/storage.h | 28 | ||||
-rw-r--r-- | common/storage_kyoto.cpp | 170 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 282 |
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) | |||
2 | generate_flatbuffers(commands/handshake | 2 | generate_flatbuffers(commands/handshake |
3 | commands/revisionupdate) | 3 | commands/revisionupdate) |
4 | 4 | ||
5 | if (STORAGE_KYOTO) | ||
6 | set(storage_SRCS storage_kyoto.cpp) | ||
7 | set(storage_LIBS kyotocabinet) | ||
8 | else (STORAGE_KYOTO) | ||
9 | set(storage_SRCS storage_lmdb.cpp) | ||
10 | set(storage_LIBS lmdb) | ||
11 | endif (STORAGE_KYOTO) | ||
12 | |||
5 | set(command_SRCS | 13 | set(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 | ||
10 | add_library(${PROJECT_NAME} ${command_SRCS}) | 18 | add_library(${PROJECT_NAME} ${command_SRCS}) |
19 | SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX) | ||
11 | qt5_use_modules(${PROJECT_NAME} Widgets) | 20 | qt5_use_modules(${PROJECT_NAME} Widgets) |
21 | target_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 | |||
6 | class Storage { | ||
7 | public: | ||
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; | ||
24 | private: | ||
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 | |||
15 | class Storage::Private | ||
16 | { | ||
17 | public: | ||
18 | Private(const QString &storageRoot, const QString &name); | ||
19 | ~Private(); | ||
20 | |||
21 | kyotocabinet::TreeDB db; | ||
22 | bool dbOpen; | ||
23 | bool inTransaction; | ||
24 | }; | ||
25 | |||
26 | Storage::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 | |||
39 | Storage::Private::~Private() | ||
40 | { | ||
41 | if (dbOpen && inTransaction) { | ||
42 | db.end_transaction(false); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | Storage::Storage(const QString &storageRoot, const QString &name) | ||
47 | : d(new Private(storageRoot, name)) | ||
48 | { | ||
49 | } | ||
50 | |||
51 | Storage::~Storage() | ||
52 | { | ||
53 | delete d; | ||
54 | } | ||
55 | |||
56 | bool Storage::isInTransaction() const | ||
57 | { | ||
58 | return d->inTransaction; | ||
59 | } | ||
60 | |||
61 | bool 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 | |||
76 | bool 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 | |||
91 | void 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 | |||
101 | bool 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 | |||
111 | bool 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 | |||
121 | bool 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 | |||
136 | bool 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 | |||
151 | qint64 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 | |||
161 | void 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 | |||
14 | class Storage::Private | ||
15 | { | ||
16 | public: | ||
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 | |||
30 | Storage::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 | |||
57 | Storage::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 | |||
68 | Storage::Storage(const QString &storageRoot, const QString &name) | ||
69 | : d(new Private(storageRoot, name)) | ||
70 | { | ||
71 | } | ||
72 | |||
73 | Storage::~Storage() | ||
74 | { | ||
75 | delete d; | ||
76 | } | ||
77 | |||
78 | bool Storage::isInTransaction() const | ||
79 | { | ||
80 | return d->transaction; | ||
81 | } | ||
82 | |||
83 | bool 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 | |||
118 | bool 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 | |||
139 | void 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 | |||
149 | bool 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 | |||
154 | bool 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 | |||
191 | bool 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 | |||
201 | bool 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 | |||
264 | qint64 Storage::diskUsage() const | ||
265 | { | ||
266 | QFileInfo info(d->storageRoot + "/data.mdb"); | ||
267 | return info.size(); | ||
268 | } | ||
269 | |||
270 | void Storage::removeFromDisk() const | ||
271 | { | ||
272 | QDir dir(d->path); | ||
273 | dir.remove("data.mdb"); | ||
274 | dir.remove("lock.mdb"); | ||
275 | } | ||
276 | |||
277 | void Storage::removeFromDisk() const | ||
278 | { | ||
279 | QDir dir(d->storageRoot); | ||
280 | dir.remove("data.mdb"); | ||
281 | dir.remove("lock.mdb"); | ||
282 | } | ||