File indexing completed on 2024-06-23 05:49:00

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2010, 2011 Alex Richardson <alex.richardson@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #ifndef KASTEN_TOPLEVELDATAINFORMATION_HPP
0010 #define KASTEN_TOPLEVELDATAINFORMATION_HPP
0011 
0012 #include "datainformationbase.hpp"
0013 #include "../script/scripthandler.hpp"
0014 #include <Okteta/ArrayChangeMetricsList>
0015 // Qt
0016 #include <QHash>
0017 #include <QFileInfo>
0018 #include <QSharedPointer>
0019 #include <QQueue>
0020 // Std
0021 #include <memory>
0022 
0023 namespace Okteta {
0024 class AbstractByteArrayModel;
0025 } // namespace Okteta
0026 
0027 class ScriptLogger;
0028 class ScriptHandlerInfo;
0029 class QScriptEngine;
0030 class ScriptHandler;
0031 class DataInformation;
0032 class PointerDataInformation;
0033 
0034 class TopLevelDataInformation : public QObject
0035                               , public DataInformationBase
0036 {
0037     Q_OBJECT
0038 
0039 public:
0040     /** create a new TopLevelDataInformation wrapping @p data
0041      *  @param data the object to wrap (takes ownership). If data is dummy then this object is invalid
0042      *  @param logger the logger to use (takes ownership). If null a new one is created
0043      *  @param engine the script engine to use. If null this object contains no dynamic elements
0044      *  @param structureFile the file which this was loaded from
0045      */
0046     explicit TopLevelDataInformation(DataInformation* data, ScriptLogger* logger = nullptr,
0047                                      QScriptEngine* engine = nullptr, const QFileInfo& structureFile = QFileInfo());
0048     ~TopLevelDataInformation() override;
0049 
0050     using Ptr = QSharedPointer<TopLevelDataInformation>;
0051     using List = QVector<Ptr>;
0052     static const Okteta::Address INVALID_OFFSET;
0053 
0054 public:
0055     void validate();
0056     /** Reads the necessary data from @p input
0057      *
0058      * @param input the byte array to read from
0059      * @param address the starting offset to read from (will be ignored if the offset is locked)
0060      * @param changesList the list with changes to @p input, so that it is possible to check whether reading is necessary
0061      *      This parameter is only useful if the structure was locked to a specific position.
0062      * @param forceRead whether to always read data, ignoring the change list
0063      */
0064     void read(const Okteta::AbstractByteArrayModel* input, Okteta::Address address,
0065               const Okteta::ArrayChangeMetricsList& changesList, bool forceRead);
0066     QScriptEngine* scriptEngine() const;
0067 
0068     DataInformation* actualDataInformation() const;
0069     void setActualDataInformation(DataInformation* newData);
0070     bool isValid() const;
0071     void lockPositionToOffset(Okteta::Address offset, const Okteta::AbstractByteArrayModel* model);
0072     void unlockPosition(const Okteta::AbstractByteArrayModel* model);
0073     bool isLockedFor(const Okteta::AbstractByteArrayModel* model) const;
0074     Okteta::Address lockPositionFor(const Okteta::AbstractByteArrayModel* model) const;
0075     bool isLockedByDefault() const;
0076     void setDefaultLockOffset(Okteta::Address offset);
0077     int indexOf(const DataInformation* const data) const;
0078     int index() const;
0079     void setIndex(int newIndex);
0080     ScriptHandler* scriptHandler() const;
0081     ScriptLogger* logger() const;
0082     void setChildDataChanged();
0083     void enqueueReadData(PointerDataInformation* toRead);
0084 
0085     bool isTopLevel() const override;
0086 
0087     // only public so that DataInformation and subclasses can call them (TODO move)
0088     void _childCountAboutToChange(DataInformation* sender, uint oldCount, uint newCount);
0089     void _childCountChanged(DataInformation* sender, uint oldCount, uint newCount);
0090 
0091 private:
0092     bool isReadingNecessary(const Okteta::AbstractByteArrayModel* model, Okteta::Address address,
0093                             const Okteta::ArrayChangeMetricsList& changesList);
0094 
0095 public Q_SLOTS:
0096     void resetValidationState();
0097     void newModelActivated(Okteta::AbstractByteArrayModel* model);
0098 
0099 private Q_SLOTS:
0100     void removeByteArrayModelFromList(QObject* obj);
0101 Q_SIGNALS:
0102     void dataChanged();
0103     /** items are inserted before @p startIndex */
0104     void childrenAboutToBeInserted(DataInformation* sender, uint startIndex, uint endIndex);
0105     /** items are inserted before @p startIndex */
0106     void childrenInserted(const DataInformation* sender, uint startIndex, uint endIndex);
0107     /** items are removed before @p startIndex */
0108     void childrenAboutToBeRemoved(DataInformation* sender, uint startIndex, uint endIndex);
0109     /** items are inserted before @p startIndex */
0110     void childrenRemoved(const DataInformation* sender, uint startIndex, uint endIndex);
0111 
0112 private:
0113     std::unique_ptr<DataInformation> mData;
0114     std::unique_ptr<ScriptHandler> mScriptHandler;
0115     std::unique_ptr<ScriptLogger> mLogger;
0116     QFileInfo mStructureFile;
0117     /** Save the position this structure is locked to for each ByteArrayModel
0118      * QObject::destroyed() has to be connected to slot removeByteArrayModel()
0119      * so that no dangling pointers remain
0120      */
0121     QHash<const Okteta::AbstractByteArrayModel*, Okteta::Address> mLockedPositions;
0122     int mIndex = -1;
0123     bool mValid : 1;
0124     bool mChildDataChanged : 1;
0125     Okteta::Address mDefaultLockOffset = INVALID_OFFSET;
0126     Okteta::Address mLastReadOffset = INVALID_OFFSET;
0127     const Okteta::AbstractByteArrayModel* mLastModel = nullptr;
0128     QQueue<PointerDataInformation*> mDelayedRead;
0129 
0130     friend class LockToOffsetTest; // needs to call isReadingNecessary()
0131 };
0132 
0133 inline DataInformation* TopLevelDataInformation::actualDataInformation() const
0134 {
0135     return mData.get();
0136 }
0137 
0138 inline bool TopLevelDataInformation::isValid() const
0139 {
0140     return mValid;
0141 }
0142 
0143 inline int TopLevelDataInformation::index() const
0144 {
0145     return mIndex;
0146 }
0147 
0148 inline void TopLevelDataInformation::setIndex(int newIndex)
0149 {
0150     mIndex = newIndex;
0151 }
0152 
0153 inline void TopLevelDataInformation::setChildDataChanged()
0154 {
0155     mChildDataChanged = true;
0156 }
0157 
0158 inline ScriptLogger* TopLevelDataInformation::logger() const
0159 {
0160     return mLogger.get();
0161 }
0162 
0163 inline ScriptHandler* TopLevelDataInformation::scriptHandler() const
0164 {
0165     return mScriptHandler.get();
0166 }
0167 
0168 inline void TopLevelDataInformation::_childCountAboutToChange(DataInformation* sender, uint oldCount, uint newCount)
0169 {
0170     if (newCount < oldCount) { // newCount is smaller so oldCount is at least 1 -> no underflow
0171         Q_EMIT childrenAboutToBeRemoved(sender, newCount, oldCount - 1);
0172     } else if (newCount > oldCount) { // newCount is larger so it is at least 1 -> no underflow
0173         Q_EMIT childrenAboutToBeInserted(sender, oldCount, newCount - 1);
0174     }
0175 }
0176 
0177 inline void TopLevelDataInformation::_childCountChanged(DataInformation* sender, uint oldCount, uint newCount)
0178 {
0179     if (newCount < oldCount) { // newCount is smaller so oldCount is at least 1 -> no underflow
0180         Q_EMIT childrenRemoved(sender, newCount, oldCount - 1);
0181     } else if (newCount > oldCount) { // newCount is larger so it is at least 1 -> no underflow
0182         Q_EMIT childrenInserted(sender, oldCount, newCount - 1);
0183     }
0184 }
0185 
0186 #endif /* KASTEN_TOPLEVELDATAINFORMATION_HPP */