1 /*
2 SPDX-FileCopyrightText: 2008 Tobias Koenig <tokoe@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #pragma once
8
9 #include <QDateTime>
10
11 #include "itemchangelog_p.h"
12 #include "itempayloadinternals_p.h"
13 #include "tag.h"
14
15 #include <algorithm>
16 #include <cassert>
17 #include <memory>
18 #include <vector>
19
20 namespace Akonadi
21 {
22 namespace _detail
23 {
24 template<typename T> class clone_ptr
25 {
26 std::unique_ptr<T> t;
27
28 public:
29 explicit clone_ptr() = default;
clone_ptr(T * t)30 explicit clone_ptr(T *t)
31 : t(t)
32 {
33 }
34
clone_ptr(const clone_ptr & other)35 clone_ptr(const clone_ptr &other)
36 : t(other.t ? other.t->clone() : nullptr)
37 {
38 }
39
40 clone_ptr(clone_ptr &&) noexcept = default;
41
42 ~clone_ptr() = default;
43
44 clone_ptr &operator=(const clone_ptr &other)
45 {
46 if (this != &other) {
47 clone_ptr copy(other);
48 swap(copy);
49 }
50 return *this;
51 }
52
53 clone_ptr &operator=(clone_ptr &&) noexcept = default;
54
swap(clone_ptr & other)55 void swap(clone_ptr &other)
56 {
57 using std::swap;
58 swap(t, other.t);
59 }
60
61 T *operator->() const
62 {
63 return get();
64 }
65
66 T &operator*() const
67 {
68 assert(get() != nullptr);
69 return *get();
70 }
71
get()72 T *get() const
73 {
74 return t.get();
75 }
76
release()77 T *release()
78 {
79 return t.release();
80 }
81
82 void reset(T *other = nullptr)
83 {
84 t.reset(other);
85 }
86
87 explicit operator bool() const noexcept
88 {
89 return get() != nullptr;
90 }
91 };
92
swap(clone_ptr<T> & lhs,clone_ptr<T> & rhs)93 template<typename T> inline void swap(clone_ptr<T> &lhs, clone_ptr<T> &rhs) noexcept
94 {
95 lhs.swap(rhs);
96 }
97
98 struct TypedPayload {
99 clone_ptr<Internal::PayloadBase> payload;
100 int sharedPointerId;
101 int metaTypeId;
102 };
103
104 struct BySharedPointerAndMetaTypeID {
105 const int spid;
106 const int mtid;
BySharedPointerAndMetaTypeIDBySharedPointerAndMetaTypeID107 BySharedPointerAndMetaTypeID(int spid, int mtid)
108 : spid(spid)
109 , mtid(mtid)
110 {
111 }
operatorBySharedPointerAndMetaTypeID112 bool operator()(const TypedPayload &tp) const
113 {
114 return (mtid == -1 || mtid == tp.metaTypeId) && (spid == -1 || spid == tp.sharedPointerId);
115 }
116 };
117
118 }
119
120 } // namespace Akonadi
121
122 namespace std
123 {
124 template<> inline void swap<Akonadi::_detail::TypedPayload>(Akonadi::_detail::TypedPayload &lhs, Akonadi::_detail::TypedPayload &rhs) noexcept
125 {
126 lhs.payload.swap(rhs.payload);
127 swap(lhs.sharedPointerId, rhs.sharedPointerId);
128 swap(lhs.metaTypeId, rhs.metaTypeId);
129 }
130 }
131
132 namespace Akonadi
133 {
134 using PayloadContainer = std::vector<_detail::TypedPayload>;
135 }
136
137 namespace Akonadi
138 {
139 /**
140 * @internal
141 */
142 class ItemPrivate : public QSharedData
143 {
144 public:
145 explicit ItemPrivate(Item::Id id = -1)
QSharedData()146 : QSharedData()
147 , mRevision(-1)
148 , mId(id)
149 , mPayloads()
150 , mCollectionId(-1)
151 , mSize(0)
152 , mModificationTime()
153 , mFlagsOverwritten(false)
154 , mTagsOverwritten(false)
155 , mSizeChanged(false)
156 , mClearPayload(false)
157 , mConversionInProgress(false)
158 {
159 }
160
ItemPrivate(const ItemPrivate & other)161 ItemPrivate(const ItemPrivate &other)
162 : QSharedData(other)
163 {
164 mId = other.mId;
165 mRemoteId = other.mRemoteId;
166 mRemoteRevision = other.mRemoteRevision;
167 mPayloadPath = other.mPayloadPath;
168 if (other.mParent) {
169 mParent.reset(new Collection(*(other.mParent)));
170 }
171 mFlags = other.mFlags;
172 mRevision = other.mRevision;
173 mTags = other.mTags;
174 mRelations = other.mRelations;
175 mSize = other.mSize;
176 mModificationTime = other.mModificationTime;
177 mMimeType = other.mMimeType;
178 mPayloads = other.mPayloads;
179 mFlagsOverwritten = other.mFlagsOverwritten;
180 mSizeChanged = other.mSizeChanged;
181 mCollectionId = other.mCollectionId;
182 mClearPayload = other.mClearPayload;
183 mVirtualReferences = other.mVirtualReferences;
184 mGid = other.mGid;
185 mCachedPayloadParts = other.mCachedPayloadParts;
186 mTagsOverwritten = other.mTagsOverwritten;
187 mConversionInProgress = false;
188
189 ItemChangeLog *changelog = ItemChangeLog::instance();
190 changelog->addedFlags(this) = changelog->addedFlags(&other);
191 changelog->deletedFlags(this) = changelog->deletedFlags(&other);
192 changelog->addedTags(this) = changelog->addedTags(&other);
193 changelog->deletedTags(this) = changelog->deletedTags(&other);
194 changelog->attributeStorage(this) = changelog->attributeStorage(&other);
195 }
196
~ItemPrivate()197 ~ItemPrivate()
198 {
199 ItemChangeLog::instance()->removeItem(this);
200 }
201
resetChangeLog()202 void resetChangeLog()
203 {
204 mFlagsOverwritten = false;
205 mSizeChanged = false;
206 mTagsOverwritten = false;
207 ItemChangeLog::instance()->clearItemChangelog(this);
208 }
209
hasMetaTypeId(int mtid)210 bool hasMetaTypeId(int mtid) const
211 {
212 return std::any_of(mPayloads.cbegin(), mPayloads.cend(), _detail::BySharedPointerAndMetaTypeID(-1, mtid));
213 }
214
payloadBaseImpl(int spid,int mtid)215 Internal::PayloadBase *payloadBaseImpl(int spid, int mtid) const
216 {
217 auto it = std::find_if(mPayloads.cbegin(), mPayloads.cend(), _detail::BySharedPointerAndMetaTypeID(spid, mtid));
218 return it == mPayloads.cend() ? nullptr : it->payload.get();
219 }
220
movePayloadFrom(ItemPrivate * other,int mtid)221 bool movePayloadFrom(ItemPrivate *other, int mtid) const /*sic!*/
222 {
223 assert(other);
224 const size_t oldSize = mPayloads.size();
225 PayloadContainer &oPayloads = other->mPayloads;
226 const _detail::BySharedPointerAndMetaTypeID matcher(-1, mtid);
227 const size_t numMatching = std::count_if(oPayloads.begin(), oPayloads.end(), matcher);
228 mPayloads.resize(oldSize + numMatching);
229 using namespace std; // for swap()
230 for (auto dst = mPayloads.begin() + oldSize, src = oPayloads.begin(), end = oPayloads.end(); src != end; ++src) {
231 if (matcher(*src)) {
232 swap(*dst, *src);
233 ++dst;
234 }
235 }
236 return numMatching > 0;
237 }
238
setPayloadBaseImpl(int spid,int mtid,std::unique_ptr<Internal::PayloadBase> & p,bool add)239 void setPayloadBaseImpl(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p, bool add) const /*sic!*/
240 {
241 if (!p.get()) {
242 if (!add) {
243 mPayloads.clear();
244 }
245 return;
246 }
247
248 // if !add, delete all payload variants
249 // (they're conversions of each other)
250 mPayloadPath.clear();
251 mPayloads.resize(add ? mPayloads.size() + 1 : 1);
252 _detail::TypedPayload &tp = mPayloads.back();
253 tp.payload.reset(p.release());
254 tp.sharedPointerId = spid;
255 tp.metaTypeId = mtid;
256 }
257
258 // Utilise the 4-bytes padding from QSharedData
259 int mRevision;
260 Item::Id mId;
261 QString mRemoteId;
262 QString mRemoteRevision;
263 mutable QString mPayloadPath;
264 mutable QScopedPointer<Collection> mParent;
265 mutable PayloadContainer mPayloads;
266 Item::Flags mFlags;
267 Tag::List mTags;
268 Relation::List mRelations;
269 Item::Id mCollectionId;
270 Collection::List mVirtualReferences;
271 // TODO: Maybe just use uint? Would save us another 8 bytes after reordering
272 qint64 mSize;
273 QDateTime mModificationTime;
274 QString mMimeType;
275 QString mGid;
276 QSet<QByteArray> mCachedPayloadParts;
277 bool mFlagsOverwritten : 1;
278 bool mTagsOverwritten : 1;
279 bool mSizeChanged : 1;
280 bool mClearPayload : 1;
281 mutable bool mConversionInProgress;
282 // 6 bytes padding here
283 };
284
285 }
286
287