1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qbs.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #ifndef QBS_PERSISTENCE
41 #define QBS_PERSISTENCE
42
43 #include "error.h"
44 #include <logging/logger.h>
45 #include <tools/qbsassert.h>
46 #include <tools/qttools.h>
47
48 #include <QtCore/qdatastream.h>
49 #include <QtCore/qflags.h>
50 #include <QtCore/qprocess.h>
51 #include <QtCore/qregularexpression.h>
52 #include <QtCore/qstring.h>
53 #include <QtCore/qstringlist.h>
54 #include <QtCore/qvariant.h>
55
56 #include <memory>
57 #include <type_traits>
58 #include <unordered_map>
59 #include <vector>
60
61 namespace qbs {
62 namespace Internal {
63
64 class NoBuildGraphError : public ErrorInfo
65 {
66 public:
67 NoBuildGraphError(const QString &filePath);
68 };
69
70 template<typename T, typename Enable = void>
71 struct PPHelper;
72
73 class PersistentPool
74 {
75 public:
76 PersistentPool(Logger &logger);
77 ~PersistentPool();
78
79 class HeadData
80 {
81 public:
82 QVariantMap projectConfig;
83 };
84
store(const Types &...args)85 template<typename ...Types> void store(const Types &...args)
86 {
87 (... , PPHelper<Types>::store(args, this));
88 }
89
load(Types &...args)90 template<typename ...Types> void load(Types &...args)
91 {
92 (... , PPHelper<Types>::load(args, this));
93 }
load()94 template<typename T> T load() {
95 T tmp;
96 PPHelper<T>::load(tmp, this);
97 return tmp;
98 }
99
100 enum OpType { Store, Load };
serializationOp(const Types &...args)101 template<OpType type, typename ...Types> void serializationOp(const Types &...args)
102 {
103 static_assert(type == Store);
104 store(args...);
105 }
serializationOp(Types &...args)106 template<OpType type, typename ...Types> void serializationOp(Types &...args)
107 {
108 if constexpr(type == Store)
109 store(args...);
110 else
111 load(args...);
112 }
113
114 void load(const QString &filePath);
115 void setupWriteStream(const QString &filePath);
116 void finalizeWriteStream();
117 void clear();
118
headData()119 const HeadData &headData() const { return m_headData; }
setHeadData(const HeadData & hd)120 void setHeadData(const HeadData &hd) { m_headData = hd; }
121
122 private:
123 using PersistentObjectId = int;
124
125 template <typename T> T *idLoad();
126 template <class T> std::shared_ptr<T> idLoadS();
127 template <typename T> T idLoadValue();
128
129 void doLoadValue(QString &s);
130 void doLoadValue(QStringList &l);
131 void doLoadValue(QProcessEnvironment &env);
132
133 template<typename T> void storeSharedObject(const T *object);
134
135 void storeVariant(const QVariant &variant);
136 QVariant loadVariant();
137
138 template <typename T> void idStoreValue(const T &value);
139
140 void doStoreValue(const QString &s);
141 void doStoreValue(const QStringList &l);
142 void doStoreValue(const QProcessEnvironment &env);
143
144 template<typename T> std::vector<T> &idStorage();
145 template<typename T> QHash<T, PersistentObjectId> &idMap();
146 template<typename T> PersistentObjectId &lastStoredId();
147
148 static const PersistentObjectId ValueNotFoundId = -1;
149 static const PersistentObjectId EmptyValueId = -2;
150
151 std::unique_ptr<QIODevice> m_file;
152 QDataStream m_stream;
153 HeadData m_headData;
154 std::vector<void *> m_loadedRaw;
155 std::vector<std::shared_ptr<void>> m_loaded;
156 std::unordered_map<const void*, int> m_storageIndices;
157 PersistentObjectId m_lastStoredObjectId = 0;
158
159 std::vector<QString> m_stringStorage;
160 QHash<QString, int> m_inverseStringStorage;
161 PersistentObjectId m_lastStoredStringId = 0;
162 std::vector<QProcessEnvironment> m_envStorage;
163 QHash<QProcessEnvironment, int> m_inverseEnvStorage;
164 PersistentObjectId m_lastStoredEnvId = 0;
165 std::vector<QStringList> m_stringListStorage;
166 QHash<QStringList, int> m_inverseStringListStorage;
167 PersistentObjectId m_lastStoredStringListId = 0;
168 Logger &m_logger;
169
170 template<typename T, typename Enable>
171 friend struct PPHelper;
172 };
173
uniqueAddress(const T * t)174 template<typename T> inline const void *uniqueAddress(const T *t) { return t; }
175
storeSharedObject(const T * object)176 template<typename T> inline void PersistentPool::storeSharedObject(const T *object)
177 {
178 if (!object) {
179 m_stream << -1;
180 return;
181 }
182 const void * const addr = uniqueAddress(object);
183 const auto found = m_storageIndices.find(addr);
184 if (found == m_storageIndices.end()) {
185 PersistentObjectId id = m_lastStoredObjectId++;
186 m_storageIndices[addr] = id;
187 m_stream << id;
188 store(*object);
189 } else {
190 m_stream << found->second;
191 }
192 }
193
idLoad()194 template <typename T> inline T *PersistentPool::idLoad()
195 {
196 PersistentObjectId id;
197 m_stream >> id;
198
199 if (id < 0)
200 return nullptr;
201
202 if (id < static_cast<PersistentObjectId>(m_loadedRaw.size()))
203 return static_cast<T *>(m_loadedRaw.at(id));
204
205 auto i = m_loadedRaw.size();
206 m_loadedRaw.resize(id + 1);
207 for (; i < m_loadedRaw.size(); ++i)
208 m_loadedRaw[i] = nullptr;
209
210 const auto t = new T;
211 m_loadedRaw[id] = t;
212 load(*t);
213 return t;
214 }
215
idStorage()216 template<> inline std::vector<QString> &PersistentPool::idStorage() { return m_stringStorage; }
idMap()217 template<> inline QHash<QString, PersistentPool::PersistentObjectId> &PersistentPool::idMap()
218 {
219 return m_inverseStringStorage;
220 }
221 template<> inline PersistentPool::PersistentObjectId &PersistentPool::lastStoredId<QString>()
222 {
223 return m_lastStoredStringId;
224 }
idStorage()225 template<> inline std::vector<QStringList> &PersistentPool::idStorage()
226 {
227 return m_stringListStorage;
228 }
idMap()229 template<> inline QHash<QStringList, PersistentPool::PersistentObjectId> &PersistentPool::idMap()
230 {
231 return m_inverseStringListStorage;
232 }
233 template<> inline PersistentPool::PersistentObjectId &PersistentPool::lastStoredId<QStringList>()
234 {
235 return m_lastStoredStringListId;
236 }
idStorage()237 template<> inline std::vector<QProcessEnvironment> &PersistentPool::idStorage()
238 {
239 return m_envStorage;
240 }
241 template<> inline QHash<QProcessEnvironment, PersistentPool::PersistentObjectId>
idMap()242 &PersistentPool::idMap()
243 {
244 return m_inverseEnvStorage;
245 }
246 template<> inline PersistentPool::PersistentObjectId
247 &PersistentPool::lastStoredId<QProcessEnvironment>()
248 {
249 return m_lastStoredEnvId;
250 }
251
idLoadS()252 template <class T> inline std::shared_ptr<T> PersistentPool::idLoadS()
253 {
254 PersistentObjectId id;
255 m_stream >> id;
256
257 if (id < 0)
258 return std::shared_ptr<T>();
259
260 if (id < static_cast<PersistentObjectId>(m_loaded.size()))
261 return std::static_pointer_cast<T>(m_loaded.at(id));
262
263 m_loaded.resize(id + 1);
264 const std::shared_ptr<T> t = T::create();
265 m_loaded[id] = t;
266 load(*t);
267 return t;
268 }
269
idLoadValue()270 template<typename T> inline T PersistentPool::idLoadValue()
271 {
272 int id;
273 m_stream >> id;
274 if (id == EmptyValueId)
275 return T();
276 QBS_CHECK(id >= 0);
277 if (id >= static_cast<int>(idStorage<T>().size())) {
278 T value;
279 doLoadValue(value);
280 idStorage<T>().resize(id + 1);
281 idStorage<T>()[id] = value;
282 return value;
283 }
284 return idStorage<T>().at(id);
285 }
286
287 template<typename T>
idStoreValue(const T & value)288 void PersistentPool::idStoreValue(const T &value)
289 {
290 if (value.isEmpty()) {
291 m_stream << EmptyValueId;
292 return;
293 }
294 int id = idMap<T>().value(value, ValueNotFoundId);
295 if (id < 0) {
296 id = lastStoredId<T>()++;
297 idMap<T>().insert(value, id);
298 m_stream << id;
299 doStoreValue(value);
300 } else {
301 m_stream << id;
302 }
303 }
304
305 // We need a helper class template, because we require partial specialization for some of
306 // the aggregate types, which is not possible with function templates.
307 // The generic implementation assumes that T is of class type and has load() and store()
308 // member functions.
309 template<typename T, typename Enable>
310 struct PPHelper
311 {
storePPHelper312 static void store(const T &object, PersistentPool *pool)
313 {
314 const_cast<T &>(object).store(*pool);
315 }
loadPPHelper316 static void load(T &object, PersistentPool *pool)
317 {
318 object.load(*pool);
319 }
320 };
321
322 /***** Specializations of Helper class *****/
323
324 template<typename T>
325 struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer_v<
326 decltype(&T::template completeSerializationOp<PersistentPool::Load>)>>>
327 {
328 static void store(const T &value, PersistentPool *pool)
329 {
330 const_cast<T &>(value).template completeSerializationOp<PersistentPool::Store>(*pool);
331 }
332 static void load(T &value, PersistentPool *pool)
333 {
334 value.template completeSerializationOp<PersistentPool::Load>(*pool);
335 }
336 };
337
338 template<typename T> struct PPHelper<T, std::enable_if_t<std::is_integral_v<T>>>
339 {
340 static void store(const T &value, PersistentPool *pool) { pool->m_stream << value; }
341 static void load(T &value, PersistentPool *pool) { pool->m_stream >> value; }
342 };
343
344 template<> struct PPHelper<long>
345 {
346 static void store(long value, PersistentPool *pool) { pool->m_stream << qint64(value); }
347 static void load(long &value, PersistentPool *pool)
348 {
349 qint64 v;
350 pool->m_stream >> v;
351 value = long(v);
352 }
353 };
354
355 template<typename T> struct PPHelper<T, std::enable_if_t<std::is_enum_v<T>>>
356 {
357 using U = std::underlying_type_t<T>;
358 static void store(const T &value, PersistentPool *pool)
359 {
360 pool->m_stream << static_cast<U>(value);
361 }
362 static void load(T &value, PersistentPool *pool)
363 {
364 pool->m_stream >> reinterpret_cast<U &>(value);
365 }
366 };
367
368 template<typename T> struct PPHelper<std::shared_ptr<T>>
369 {
370 static void store(const std::shared_ptr<T> &value, PersistentPool *pool)
371 {
372 pool->store(value.get());
373 }
374 static void load(std::shared_ptr<T> &value, PersistentPool *pool)
375 {
376 value = pool->idLoadS<std::remove_const_t<T>>();
377 }
378 };
379
380 template<typename T> struct PPHelper<std::unique_ptr<T>>
381 {
382 static void store(const std::unique_ptr<T> &value, PersistentPool *pool)
383 {
384 pool->store(value.get());
385 }
386 static void load(std::unique_ptr<T> &ptr, PersistentPool *pool)
387 {
388 ptr.reset(pool->idLoad<std::remove_const_t<T>>());
389 }
390 };
391
392 template<typename T> struct PPHelper<T *>
393 {
394 static void store(const T *value, PersistentPool *pool) { pool->storeSharedObject(value); }
395 static void load(T* &value, PersistentPool *pool) { value = pool->idLoad<T>(); }
396 };
397
398 template<typename T> struct PPHelper<T, std::enable_if_t<std::is_same_v<T, QString>
399 || std::is_same_v<T, QStringList> || std::is_same_v<T, QProcessEnvironment>>>
400 {
401 static void store(const T &v, PersistentPool *pool) { pool->idStoreValue(v); }
402 static void load(T &v, PersistentPool *pool) { v = pool->idLoadValue<T>(); }
403 };
404
405 template<> struct PPHelper<QVariant>
406 {
407 static void store(const QVariant &v, PersistentPool *pool) { pool->storeVariant(v); }
408 static void load(QVariant &v, PersistentPool *pool) { v = pool->loadVariant(); }
409 };
410
411 template<> struct PPHelper<QRegularExpression>
412 {
413 static void store(const QRegularExpression &re, PersistentPool *pool)
414 {
415 pool->store(re.pattern());
416 }
417 static void load(QRegularExpression &re, PersistentPool *pool)
418 {
419 re.setPattern(pool->load<QString>());
420 }
421 };
422
423 template<typename T, typename U> struct PPHelper<std::pair<T, U>>
424 {
425 static void store(const std::pair<T, U> &pair, PersistentPool *pool)
426 {
427 pool->store(pair.first);
428 pool->store(pair.second);
429 }
430 static void load(std::pair<T, U> &pair, PersistentPool *pool)
431 {
432 pool->load(pair.first);
433 pool->load(pair.second);
434 }
435 };
436
437 template<typename... Args> struct PPHelper<std::tuple<Args...>>
438 {
439 template<std::size_t... Ns>
440 static void storeHelper(
441 std::index_sequence<Ns...>, const std::tuple<Args...> &tuple, PersistentPool *pool)
442 {
443 (pool->store(std::get<Ns>(tuple)), ...);
444 }
445
446 static void store(const std::tuple<Args...> &tuple, PersistentPool *pool)
447 {
448 storeHelper(std::make_index_sequence<sizeof...(Args)>(), tuple, pool);
449 }
450
451 template<std::size_t... Ns>
452 static void loadHelper(
453 std::index_sequence<Ns...>, std::tuple<Args...> &tuple, PersistentPool *pool)
454 {
455 (pool->load(std::get<Ns>(tuple)), ...);
456 }
457
458 static void load(std::tuple<Args...> &tuple, PersistentPool * pool)
459 {
460 loadHelper(std::make_index_sequence<sizeof...(Args)>(), tuple, pool);
461 }
462 };
463
464 template<typename T> struct PPHelper<QFlags<T>>
465 {
466 using Int = typename QFlags<T>::Int;
467 static void store(const QFlags<T> &flags, PersistentPool *pool)
468 {
469 pool->store<Int>(flags);
470 }
471 static void load(QFlags<T> &flags, PersistentPool *pool)
472 {
473 flags = QFlags<T>(pool->load<Int>());
474 }
475 };
476
477 template<typename T> struct IsSimpleContainer : std::false_type { };
478 template<typename T> struct IsSimpleContainer<QList<T>> : std::true_type { };
479 template<> struct IsSimpleContainer<QStringList> : std::false_type { };
480 template<typename T> struct IsSimpleContainer<std::vector<T>> : std::true_type { };
481
482 template<typename T> struct PPHelper<T, std::enable_if_t<IsSimpleContainer<T>::value>>
483 {
484 static void store(const T &container, PersistentPool *pool)
485 {
486 pool->store(int(container.size()));
487 for (auto it = container.cbegin(); it != container.cend(); ++it)
488 pool->store(*it);
489 }
490 static void load(T &container, PersistentPool *pool)
491 {
492 const int count = pool->load<int>();
493 container.clear();
494 container.reserve(count);
495 for (int i = count; --i >= 0;)
496 container.push_back(pool->load<typename T::value_type>());
497 }
498 };
499
500 template<typename T> struct IsKeyValueContainer : std::false_type { };
501 template<typename K, typename V> struct IsKeyValueContainer<QMap<K, V>> : std::true_type { };
502 template<typename K, typename V> struct IsKeyValueContainer<QHash<K, V>> : std::true_type { };
503
504 template<typename T>
505 struct PPHelper<T, std::enable_if_t<IsKeyValueContainer<T>::value>>
506 {
507 static void store(const T &container, PersistentPool *pool)
508 {
509 pool->store(int(container.size()));
510 for (auto it = container.cbegin(); it != container.cend(); ++it) {
511 pool->store(it.key());
512 pool->store(it.value());
513 }
514 }
515 static void load(T &container, PersistentPool *pool)
516 {
517 container.clear();
518 const int count = pool->load<int>();
519 for (int i = 0; i < count; ++i) {
520 const auto &key = pool->load<typename T::key_type>();
521 const auto &value = pool->load<typename T::mapped_type>();
522 container.insert(key, value);
523 }
524 }
525 };
526
527 template<typename K, typename V, typename H>
528 struct PPHelper<std::unordered_map<K, V, H>>
529 {
530 static void store(const std::unordered_map<K, V, H> &map, PersistentPool *pool)
531 {
532 pool->store(quint32(map.size()));
533 for (auto it = map.cbegin(); it != map.cend(); ++it)
534 pool->store(*it);
535 }
536 static void load(std::unordered_map<K, V, H> &map, PersistentPool *pool)
537 {
538 map.clear();
539 const auto count = pool->load<quint32>();
540 for (std::size_t i = 0; i < count; ++i)
541 map.insert(pool->load<std::pair<K, V>>());
542 }
543 };
544
545 } // namespace Internal
546 } // namespace qbs
547
548 #endif
549