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