/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QBS_PERSISTENCE #define QBS_PERSISTENCE #include "error.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace qbs { namespace Internal { class NoBuildGraphError : public ErrorInfo { public: NoBuildGraphError(const QString &filePath); }; template struct PPHelper; class PersistentPool { public: PersistentPool(Logger &logger); ~PersistentPool(); class HeadData { public: QVariantMap projectConfig; }; template void store(const T &value, const Types &...args) { PPHelper::store(value, this); store(args...); } template void load(T &value, Types &...args) { PPHelper::load(value, this); load(args...); } template T load() { T tmp; PPHelper::load(tmp, this); return tmp; } enum OpType { Store, Load }; template struct OpTypeHelper { }; template struct OpTypeHelper { static void serializationOp(PersistentPool *pool, const T &value, const Types &...args) { pool->store(value, args...); } }; template struct OpTypeHelper { static void serializationOp(PersistentPool *pool, T &value, Types &...args) { pool->load(value, args...); } }; template void serializationOp(const T &value, const Types &...args) { OpTypeHelper::serializationOp(this, value, args...); } template void serializationOp(T &value, Types &...args) { OpTypeHelper::serializationOp(this, value, args...); } void load(const QString &filePath); void setupWriteStream(const QString &filePath); void finalizeWriteStream(); void clear(); const HeadData &headData() const { return m_headData; } void setHeadData(const HeadData &hd) { m_headData = hd; } private: using PersistentObjectId = int; template T *idLoad(); template std::shared_ptr idLoadS(); template T idLoadValue(); void doLoadValue(QString &s); void doLoadValue(QStringList &l); void doLoadValue(QProcessEnvironment &env); template void storeSharedObject(const T *object); void storeVariant(const QVariant &variant); QVariant loadVariant(); template void idStoreValue(const T &value); void doStoreValue(const QString &s); void doStoreValue(const QStringList &l); void doStoreValue(const QProcessEnvironment &env); template std::vector &idStorage(); template QHash &idMap(); template PersistentObjectId &lastStoredId(); // Recursion termination void store() {} void load() {} static const PersistentObjectId ValueNotFoundId = -1; static const PersistentObjectId EmptyValueId = -2; std::unique_ptr m_file; QDataStream m_stream; HeadData m_headData; std::vector m_loadedRaw; std::vector> m_loaded; std::unordered_map m_storageIndices; PersistentObjectId m_lastStoredObjectId = 0; std::vector m_stringStorage; QHash m_inverseStringStorage; PersistentObjectId m_lastStoredStringId = 0; std::vector m_envStorage; QHash m_inverseEnvStorage; PersistentObjectId m_lastStoredEnvId = 0; std::vector m_stringListStorage; QHash m_inverseStringListStorage; PersistentObjectId m_lastStoredStringListId = 0; Logger &m_logger; template friend struct PPHelper; }; template inline const void *uniqueAddress(const T *t) { return t; } template inline void PersistentPool::storeSharedObject(const T *object) { if (!object) { m_stream << -1; return; } const void * const addr = uniqueAddress(object); const auto found = m_storageIndices.find(addr); if (found == m_storageIndices.end()) { PersistentObjectId id = m_lastStoredObjectId++; m_storageIndices[addr] = id; m_stream << id; store(*object); } else { m_stream << found->second; } } template inline T *PersistentPool::idLoad() { PersistentObjectId id; m_stream >> id; if (id < 0) return nullptr; if (id < static_cast(m_loadedRaw.size())) return static_cast(m_loadedRaw.at(id)); auto i = m_loadedRaw.size(); m_loadedRaw.resize(id + 1); for (; i < m_loadedRaw.size(); ++i) m_loadedRaw[i] = nullptr; const auto t = new T; m_loadedRaw[id] = t; load(*t); return t; } template<> inline std::vector &PersistentPool::idStorage() { return m_stringStorage; } template<> inline QHash &PersistentPool::idMap() { return m_inverseStringStorage; } template<> inline PersistentPool::PersistentObjectId &PersistentPool::lastStoredId() { return m_lastStoredStringId; } template<> inline std::vector &PersistentPool::idStorage() { return m_stringListStorage; } template<> inline QHash &PersistentPool::idMap() { return m_inverseStringListStorage; } template<> inline PersistentPool::PersistentObjectId &PersistentPool::lastStoredId() { return m_lastStoredStringListId; } template<> inline std::vector &PersistentPool::idStorage() { return m_envStorage; } template<> inline QHash &PersistentPool::idMap() { return m_inverseEnvStorage; } template<> inline PersistentPool::PersistentObjectId &PersistentPool::lastStoredId() { return m_lastStoredEnvId; } template inline std::shared_ptr PersistentPool::idLoadS() { PersistentObjectId id; m_stream >> id; if (id < 0) return std::shared_ptr(); if (id < static_cast(m_loaded.size())) return std::static_pointer_cast(m_loaded.at(id)); m_loaded.resize(id + 1); const std::shared_ptr t = T::create(); m_loaded[id] = t; load(*t); return t; } template inline T PersistentPool::idLoadValue() { int id; m_stream >> id; if (id == EmptyValueId) return T(); QBS_CHECK(id >= 0); if (id >= static_cast(idStorage().size())) { T value; doLoadValue(value); idStorage().resize(id + 1); idStorage()[id] = value; return value; } return idStorage().at(id); } template void PersistentPool::idStoreValue(const T &value) { if (value.isEmpty()) { m_stream << EmptyValueId; return; } int id = idMap().value(value, ValueNotFoundId); if (id < 0) { id = lastStoredId()++; idMap().insert(value, id); m_stream << id; doStoreValue(value); } else { m_stream << id; } } // We need a helper class template, because we require partial specialization for some of // the aggregate types, which is not possible with function templates. // The generic implementation assumes that T is of class type and has load() and store() // member functions. template struct PPHelper { static void store(const T &object, PersistentPool *pool) { const_cast(object).store(*pool); } static void load(T &object, PersistentPool *pool) { object.load(*pool); } }; /***** Specializations of Helper class *****/ template struct PPHelper)>>> { static void store(const T &value, PersistentPool *pool) { const_cast(value).template completeSerializationOp(*pool); } static void load(T &value, PersistentPool *pool) { value.template completeSerializationOp(*pool); } }; template struct PPHelper>> { static void store(const T &value, PersistentPool *pool) { pool->m_stream << value; } static void load(T &value, PersistentPool *pool) { pool->m_stream >> value; } }; template<> struct PPHelper { static void store(long value, PersistentPool *pool) { pool->m_stream << qint64(value); } static void load(long &value, PersistentPool *pool) { qint64 v; pool->m_stream >> v; value = long(v); } }; template struct PPHelper>> { using U = std::underlying_type_t; static void store(const T &value, PersistentPool *pool) { pool->m_stream << static_cast(value); } static void load(T &value, PersistentPool *pool) { pool->m_stream >> reinterpret_cast(value); } }; template struct PPHelper> { static void store(const std::shared_ptr &value, PersistentPool *pool) { pool->store(value.get()); } static void load(std::shared_ptr &value, PersistentPool *pool) { value = pool->idLoadS>(); } }; template struct PPHelper> { static void store(const std::unique_ptr &value, PersistentPool *pool) { pool->store(value.get()); } static void load(std::unique_ptr &ptr, PersistentPool *pool) { ptr.reset(pool->idLoad>()); } }; template struct PPHelper { static void store(const T *value, PersistentPool *pool) { pool->storeSharedObject(value); } static void load(T* &value, PersistentPool *pool) { value = pool->idLoad(); } }; template struct PPHelper || std::is_same_v || std::is_same_v>> { static void store(const T &v, PersistentPool *pool) { pool->idStoreValue(v); } static void load(T &v, PersistentPool *pool) { v = pool->idLoadValue(); } }; template<> struct PPHelper { static void store(const QVariant &v, PersistentPool *pool) { pool->storeVariant(v); } static void load(QVariant &v, PersistentPool *pool) { v = pool->loadVariant(); } }; template<> struct PPHelper { static void store(const QRegularExpression &re, PersistentPool *pool) { pool->store(re.pattern()); } static void load(QRegularExpression &re, PersistentPool *pool) { re.setPattern(pool->load()); } }; template struct PPHelper> { static void store(const std::pair &pair, PersistentPool *pool) { pool->store(pair.first); pool->store(pair.second); } static void load(std::pair &pair, PersistentPool *pool) { pool->load(pair.first); pool->load(pair.second); } }; template struct PPHelper> { using Int = typename QFlags::Int; static void store(const QFlags &flags, PersistentPool *pool) { pool->store(flags); } static void load(QFlags &flags, PersistentPool *pool) { flags = QFlags(pool->load()); } }; template struct IsSimpleContainer : std::false_type { }; template struct IsSimpleContainer> : std::true_type { }; template struct IsSimpleContainer> : std::true_type { }; template struct PPHelper::value>> { static void store(const T &container, PersistentPool *pool) { pool->store(int(container.size())); for (auto it = container.cbegin(); it != container.cend(); ++it) pool->store(*it); } static void load(T &container, PersistentPool *pool) { const int count = pool->load(); container.clear(); container.reserve(count); for (int i = count; --i >= 0;) container.push_back(pool->load()); } }; template struct IsKeyValueContainer : std::false_type { }; template struct IsKeyValueContainer> : std::true_type { }; template struct IsKeyValueContainer> : std::true_type { }; template struct PPHelper::value>> { static void store(const T &container, PersistentPool *pool) { pool->store(container.size()); for (auto it = container.cbegin(); it != container.cend(); ++it) { pool->store(it.key()); pool->store(it.value()); } } static void load(T &container, PersistentPool *pool) { container.clear(); const int count = pool->load(); for (int i = 0; i < count; ++i) { const auto &key = pool->load(); const auto &value = pool->load(); container.insert(key, value); } } }; template struct PPHelper> { static void store(const std::unordered_map &map, PersistentPool *pool) { pool->store(quint32(map.size())); for (auto it = map.cbegin(); it != map.cend(); ++it) pool->store(*it); } static void load(std::unordered_map &map, PersistentPool *pool) { map.clear(); const auto count = pool->load(); for (std::size_t i = 0; i < count; ++i) map.insert(pool->load>()); } }; } // namespace Internal } // namespace qbs #endif