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