diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/CMakeLists.txt | 19 | ||||
-rw-r--r-- | common/clientapi.h | 38 | ||||
-rw-r--r-- | common/domain/event.fbs | 10 | ||||
-rw-r--r-- | common/entitybuffer.fbs | 9 | ||||
-rw-r--r-- | common/metadata.fbs | 7 | ||||
-rw-r--r-- | common/pipeline.cpp | 40 | ||||
-rw-r--r-- | common/pipeline.h | 8 | ||||
-rw-r--r-- | common/storage.h | 2 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 25 | ||||
-rw-r--r-- | common/test/clientapitest.cpp | 2 |
10 files changed, 121 insertions, 39 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 001dab5..ec13e07 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt | |||
@@ -1,13 +1,16 @@ | |||
1 | project(akonadi2common) | 1 | project(akonadi2common) |
2 | generate_flatbuffers( | 2 | generate_flatbuffers( |
3 | commands/commandcompletion | 3 | commands/commandcompletion |
4 | commands/createentity | 4 | commands/createentity |
5 | commands/deleteentity | 5 | commands/deleteentity |
6 | commands/fetchentity | 6 | commands/fetchentity |
7 | commands/handshake | 7 | commands/handshake |
8 | commands/modifyentity | 8 | commands/modifyentity |
9 | commands/revisionupdate | 9 | commands/revisionupdate |
10 | ) | 10 | domain/event |
11 | entitybuffer | ||
12 | metadata | ||
13 | ) | ||
11 | 14 | ||
12 | if (STORAGE_unqlite) | 15 | if (STORAGE_unqlite) |
13 | add_definitions(-DUNQLITE_ENABLE_THREADS -fpermissive) | 16 | add_definitions(-DUNQLITE_ENABLE_THREADS -fpermissive) |
diff --git a/common/clientapi.h b/common/clientapi.h index d2757e7..ba0cb19 100644 --- a/common/clientapi.h +++ b/common/clientapi.h | |||
@@ -173,18 +173,36 @@ namespace Akonadi2 { | |||
173 | */ | 173 | */ |
174 | namespace Domain { | 174 | namespace Domain { |
175 | 175 | ||
176 | /** | ||
177 | * This class has to be implemented by resources and can be used as generic interface to access the buffer properties | ||
178 | */ | ||
179 | class BufferAdaptor { | ||
180 | public: | ||
181 | virtual QVariant getProperty(const QString &key) { return QVariant(); } | ||
182 | virtual void setProperty(const QString &key, const QVariant &value) {} | ||
183 | }; | ||
184 | |||
185 | /** | ||
186 | * The domain type interface has two purposes: | ||
187 | * * provide a unified interface to read buffers (for zero-copy reading) | ||
188 | * * record changes to generate changesets for modifications | ||
189 | */ | ||
176 | class AkonadiDomainType { | 190 | class AkonadiDomainType { |
177 | public: | 191 | public: |
178 | AkonadiDomainType(const QString &resourceName, const QString &identifier, qint64 revision) | 192 | AkonadiDomainType(const QString &resourceName, const QString &identifier, qint64 revision, const QSharedPointer<BufferAdaptor> &adaptor) |
179 | : mResourceName(resourceName), | 193 | : mAdaptor(adaptor), |
194 | mResourceName(resourceName), | ||
180 | mIdentifier(identifier), | 195 | mIdentifier(identifier), |
181 | mRevision(revision) | 196 | mRevision(revision) |
182 | { | 197 | { |
183 | } | 198 | } |
184 | 199 | ||
185 | virtual QVariant getProperty(const QString &key){ return QVariant(); } | 200 | virtual QVariant getProperty(const QString &key){ return mAdaptor->getProperty(key); } |
201 | virtual void setProperty(const QString &key, const QVariant &value){ mChangeSet.insert(key, value); } | ||
186 | 202 | ||
187 | private: | 203 | private: |
204 | QSharedPointer<BufferAdaptor> mAdaptor; | ||
205 | QHash<QString, QVariant> mChangeSet; | ||
188 | /* | 206 | /* |
189 | * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. | 207 | * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. |
190 | */ | 208 | */ |
@@ -193,21 +211,19 @@ private: | |||
193 | qint64 mRevision; | 211 | qint64 mRevision; |
194 | }; | 212 | }; |
195 | 213 | ||
196 | class Event : public AkonadiDomainType { | 214 | struct Event : public AkonadiDomainType { |
197 | public: | ||
198 | typedef QSharedPointer<Event> Ptr; | 215 | typedef QSharedPointer<Event> Ptr; |
199 | Event(const QString &resource, const QString &identifier, qint64 revision):AkonadiDomainType(resource, identifier, revision){}; | 216 | using AkonadiDomainType::AkonadiDomainType; |
200 | |||
201 | }; | 217 | }; |
202 | 218 | ||
203 | class Todo : public AkonadiDomainType { | 219 | struct Todo : public AkonadiDomainType { |
204 | public: | ||
205 | typedef QSharedPointer<Todo> Ptr; | 220 | typedef QSharedPointer<Todo> Ptr; |
221 | using AkonadiDomainType::AkonadiDomainType; | ||
206 | }; | 222 | }; |
207 | 223 | ||
208 | class Calendar : public AkonadiDomainType { | 224 | struct Calendar : public AkonadiDomainType { |
209 | public: | ||
210 | typedef QSharedPointer<Calendar> Ptr; | 225 | typedef QSharedPointer<Calendar> Ptr; |
226 | using AkonadiDomainType::AkonadiDomainType; | ||
211 | }; | 227 | }; |
212 | 228 | ||
213 | class Mail : public AkonadiDomainType { | 229 | class Mail : public AkonadiDomainType { |
diff --git a/common/domain/event.fbs b/common/domain/event.fbs new file mode 100644 index 0000000..6865cc5 --- /dev/null +++ b/common/domain/event.fbs | |||
@@ -0,0 +1,10 @@ | |||
1 | namespace Akonadi2.Domain.Buffer; | ||
2 | |||
3 | table Event { | ||
4 | summary:string; | ||
5 | description:string; | ||
6 | attachment:[ubyte]; | ||
7 | } | ||
8 | |||
9 | root_type Event; | ||
10 | file_identifier "AKFB"; | ||
diff --git a/common/entitybuffer.fbs b/common/entitybuffer.fbs new file mode 100644 index 0000000..28c9b2a --- /dev/null +++ b/common/entitybuffer.fbs | |||
@@ -0,0 +1,9 @@ | |||
1 | namespace Akonadi2; | ||
2 | |||
3 | table EntityBuffer { | ||
4 | metadata: [ubyte]; | ||
5 | resource: [ubyte]; | ||
6 | local: [ubyte]; | ||
7 | } | ||
8 | |||
9 | root_type EntityBuffer; | ||
diff --git a/common/metadata.fbs b/common/metadata.fbs new file mode 100644 index 0000000..71684b6 --- /dev/null +++ b/common/metadata.fbs | |||
@@ -0,0 +1,7 @@ | |||
1 | namespace Akonadi2; | ||
2 | |||
3 | table Metadata { | ||
4 | revision: ulong; | ||
5 | } | ||
6 | |||
7 | root_type Metadata; | ||
diff --git a/common/pipeline.cpp b/common/pipeline.cpp index 739909d..5ca8b95 100644 --- a/common/pipeline.cpp +++ b/common/pipeline.cpp | |||
@@ -23,6 +23,9 @@ | |||
23 | #include <QByteArray> | 23 | #include <QByteArray> |
24 | #include <QStandardPaths> | 24 | #include <QStandardPaths> |
25 | #include <QVector> | 25 | #include <QVector> |
26 | #include <QDebug> | ||
27 | #include "entitybuffer_generated.h" | ||
28 | #include "metadata_generated.h" | ||
26 | 29 | ||
27 | namespace Akonadi2 | 30 | namespace Akonadi2 |
28 | { | 31 | { |
@@ -69,11 +72,40 @@ void Pipeline::null() | |||
69 | state.step(); | 72 | state.step(); |
70 | } | 73 | } |
71 | 74 | ||
72 | void Pipeline::newEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder &entity) | 75 | void Pipeline::newEntity(const QByteArray &key, void *resourceBufferData, size_t size) |
73 | { | 76 | { |
74 | const qint64 newRevision = storage().maxRevision() + 1; | 77 | const qint64 newRevision = storage().maxRevision() + 1; |
75 | //FIXME this should go into a preprocessor | 78 | |
76 | storage().write(key, key.size(), reinterpret_cast<char*>(entity.GetBufferPointer()), entity.GetSize()); | 79 | flatbuffers::FlatBufferBuilder fbb; |
80 | auto builder = Akonadi2::EntityBufferBuilder(fbb); | ||
81 | |||
82 | //Add metadata buffer | ||
83 | { | ||
84 | flatbuffers::FlatBufferBuilder metadataFbb; | ||
85 | auto metadataBuilder = Akonadi2::MetadataBuilder(metadataFbb); | ||
86 | metadataBuilder.add_revision(newRevision); | ||
87 | auto metadataBuffer = metadataBuilder.Finish(); | ||
88 | Akonadi2::FinishMetadataBuffer(fbb, metadataBuffer); | ||
89 | //TODO use memcpy | ||
90 | auto metadata = fbb.CreateVector<uint8_t>(metadataFbb.GetBufferPointer(), metadataFbb.GetSize()); | ||
91 | builder.add_metadata(metadata); | ||
92 | } | ||
93 | |||
94 | //Add resource buffer | ||
95 | { | ||
96 | //TODO use memcpy | ||
97 | auto resource = fbb.CreateVector<uint8_t>(static_cast<uint8_t*>(resourceBufferData), size); | ||
98 | builder.add_resource(resource); | ||
99 | } | ||
100 | |||
101 | //We don't have a local buffer yet | ||
102 | // builder.add_local(); | ||
103 | |||
104 | auto buffer = builder.Finish(); | ||
105 | Akonadi2::FinishEntityBufferBuffer(fbb, buffer); | ||
106 | |||
107 | qDebug() << "writing new entity" << key; | ||
108 | storage().write(key.data(), key.size(), fbb.GetBufferPointer(), fbb.GetSize()); | ||
77 | storage().setMaxRevision(newRevision); | 109 | storage().setMaxRevision(newRevision); |
78 | 110 | ||
79 | PipelineState state(this, NewPipeline, key, d->newPipeline); | 111 | PipelineState state(this, NewPipeline, key, d->newPipeline); |
@@ -81,7 +113,7 @@ void Pipeline::newEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder & | |||
81 | state.step(); | 113 | state.step(); |
82 | } | 114 | } |
83 | 115 | ||
84 | void Pipeline::modifiedEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder &entityDelta) | 116 | void Pipeline::modifiedEntity(const QByteArray &key, void *data, size_t size) |
85 | { | 117 | { |
86 | PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline); | 118 | PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline); |
87 | d->activePipelines << state; | 119 | d->activePipelines << state; |
diff --git a/common/pipeline.h b/common/pipeline.h index d7048ff..159cc1c 100644 --- a/common/pipeline.h +++ b/common/pipeline.h | |||
@@ -46,10 +46,11 @@ public: | |||
46 | 46 | ||
47 | Storage &storage() const; | 47 | Storage &storage() const; |
48 | 48 | ||
49 | // domain objects needed here | ||
50 | void null(); | 49 | void null(); |
51 | void newEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder &entity); | 50 | //FIXME We should probably directly provide a DomainTypeAdapter here. The data has already been written and we only need to read it for processing. And we need to read all buffers. |
52 | void modifiedEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder &entityDelta); | 51 | void newEntity(const QByteArray &key, void *resourceBufferData, size_t size); |
52 | //TODO Send local buffer data as well? | ||
53 | void modifiedEntity(const QByteArray &key, void *data, size_t size); | ||
53 | void deletedEntity(const QByteArray &key); | 54 | void deletedEntity(const QByteArray &key); |
54 | 55 | ||
55 | Q_SIGNALS: | 56 | Q_SIGNALS: |
@@ -72,7 +73,6 @@ private: | |||
72 | class AKONADI2COMMON_EXPORT PipelineState | 73 | class AKONADI2COMMON_EXPORT PipelineState |
73 | { | 74 | { |
74 | public: | 75 | public: |
75 | // domain objects? | ||
76 | PipelineState(); | 76 | PipelineState(); |
77 | PipelineState(Pipeline *pipeline, Pipeline::Type type, const QByteArray &key, const QVector<Preprocessor *> &filters); | 77 | PipelineState(Pipeline *pipeline, Pipeline::Type type, const QByteArray &key, const QVector<Preprocessor *> &filters); |
78 | PipelineState(const PipelineState &other); | 78 | PipelineState(const PipelineState &other); |
diff --git a/common/storage.h b/common/storage.h index 6cfa3d6..6ae7838 100644 --- a/common/storage.h +++ b/common/storage.h | |||
@@ -52,7 +52,7 @@ public: | |||
52 | //TODO: row removal | 52 | //TODO: row removal |
53 | //TODO: cursor based read | 53 | //TODO: cursor based read |
54 | //TODO: query? | 54 | //TODO: query? |
55 | bool write(const char *key, size_t keySize, const char *value, size_t valueSize); | 55 | bool write(void const *key, size_t keySize, void const *value, size_t valueSize); |
56 | bool write(const std::string &sKey, const std::string &sValue); | 56 | bool write(const std::string &sKey, const std::string &sValue); |
57 | void read(const std::string &sKey, | 57 | void read(const std::string &sKey, |
58 | const std::function<bool(const std::string &value)> &resultHandler); | 58 | const std::function<bool(const std::string &value)> &resultHandler); |
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index eeb0045..42e3d33 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -194,17 +194,17 @@ void Storage::abortTransaction() | |||
194 | d->transaction = 0; | 194 | d->transaction = 0; |
195 | } | 195 | } |
196 | 196 | ||
197 | bool Storage::write(const char *key, size_t keySize, const char *value, size_t valueSize) | 197 | bool Storage::write(void const *keyPtr, size_t keySize, void const *valuePtr, size_t valueSize) |
198 | { | ||
199 | return write(std::string(key, keySize), std::string(value, valueSize)); | ||
200 | } | ||
201 | |||
202 | bool Storage::write(const std::string &sKey, const std::string &sValue) | ||
203 | { | 198 | { |
204 | if (!d->env) { | 199 | if (!d->env) { |
205 | return false; | 200 | return false; |
206 | } | 201 | } |
207 | 202 | ||
203 | if (d->mode == ReadOnly) { | ||
204 | std::cerr << "tried to write in read-only mode." << std::endl; | ||
205 | return false; | ||
206 | } | ||
207 | |||
208 | const bool implicitTransaction = !d->transaction || d->readTransaction; | 208 | const bool implicitTransaction = !d->transaction || d->readTransaction; |
209 | if (implicitTransaction) { | 209 | if (implicitTransaction) { |
210 | // TODO: if this fails, still try the write below? | 210 | // TODO: if this fails, still try the write below? |
@@ -215,10 +215,10 @@ bool Storage::write(const std::string &sKey, const std::string &sValue) | |||
215 | 215 | ||
216 | int rc; | 216 | int rc; |
217 | MDB_val key, data; | 217 | MDB_val key, data; |
218 | key.mv_size = sKey.size(); | 218 | key.mv_size = keySize; |
219 | key.mv_data = (void*)sKey.data(); | 219 | key.mv_data = const_cast<void*>(keyPtr); |
220 | data.mv_size = sValue.size(); | 220 | data.mv_size = valueSize; |
221 | data.mv_data = (void*)sValue.data(); | 221 | data.mv_data = const_cast<void*>(valuePtr); |
222 | rc = mdb_put(d->transaction, d->dbi, &key, &data, 0); | 222 | rc = mdb_put(d->transaction, d->dbi, &key, &data, 0); |
223 | 223 | ||
224 | if (rc) { | 224 | if (rc) { |
@@ -236,6 +236,11 @@ bool Storage::write(const std::string &sKey, const std::string &sValue) | |||
236 | return !rc; | 236 | return !rc; |
237 | } | 237 | } |
238 | 238 | ||
239 | bool Storage::write(const std::string &sKey, const std::string &sValue) | ||
240 | { | ||
241 | return write(const_cast<char*>(sKey.data()), sKey.size(), const_cast<char*>(sValue.data()), sValue.size()); | ||
242 | } | ||
243 | |||
239 | void Storage::read(const std::string &sKey, | 244 | void Storage::read(const std::string &sKey, |
240 | const std::function<bool(const std::string &value)> &resultHandler, | 245 | const std::function<bool(const std::string &value)> &resultHandler, |
241 | const std::function<void(const Storage::Error &error)> &errorHandler) | 246 | const std::function<void(const Storage::Error &error)> &errorHandler) |
diff --git a/common/test/clientapitest.cpp b/common/test/clientapitest.cpp index dd634f1..2ba0dcf 100644 --- a/common/test/clientapitest.cpp +++ b/common/test/clientapitest.cpp | |||
@@ -31,7 +31,7 @@ private Q_SLOTS: | |||
31 | void testLoad() | 31 | void testLoad() |
32 | { | 32 | { |
33 | DummyResourceFacade facade; | 33 | DummyResourceFacade facade; |
34 | facade.results << QSharedPointer<Akonadi2::Domain::Event>::create("resource", "id", 0); | 34 | facade.results << QSharedPointer<Akonadi2::Domain::Event>::create("resource", "id", 0, QSharedPointer<Akonadi2::Domain::BufferAdaptor>()); |
35 | 35 | ||
36 | Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); }); | 36 | Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); }); |
37 | 37 | ||