File indexing completed on 2024-04-14 05:45:46

0001 /*
0002     This file is part of the Okteta Core library, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2008-2009 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "piecetablebytearraymodel_p.hpp"
0010 #include "piecetablebytearraymodel.hpp"
0011 
0012 // lib
0013 #include <addressrangelist.hpp>
0014 #include <logging.hpp>
0015 
0016 namespace Okteta {
0017 
0018 static constexpr int InvalidVersionIndex = -1;
0019 
0020 PieceTableByteArrayModelPrivate::PieceTableByteArrayModelPrivate(PieceTableByteArrayModel* parent, const QByteArray& data)
0021     : AbstractByteArrayModelPrivate(parent)
0022     , mReadOnly(false)
0023     , mInitialData(data)
0024 {
0025     mPieceTable.init(data.size());
0026 }
0027 
0028 PieceTableByteArrayModelPrivate::PieceTableByteArrayModelPrivate(PieceTableByteArrayModel* parent, int size, Byte fillByte)
0029     : AbstractByteArrayModelPrivate(parent)
0030     , mReadOnly(false)
0031     , mInitialData(size, fillByte)
0032 {
0033     mPieceTable.init(size);
0034 }
0035 
0036 PieceTableByteArrayModelPrivate::~PieceTableByteArrayModelPrivate() = default;
0037 
0038 // TODO: getStorageData needs some caching, optimize for successive access
0039 Byte PieceTableByteArrayModelPrivate::byte(Address offset) const
0040 {
0041     int storageId;
0042     Address storageOffset;
0043     mPieceTable.getStorageData(&storageId, &storageOffset, offset);
0044 
0045     const Byte result = (storageId == KPieceTable::Piece::OriginalStorage) ?
0046                         mInitialData[storageOffset] :
0047                         mChangesDataStorage[storageOffset];
0048     return result;
0049 }
0050 
0051 void PieceTableByteArrayModelPrivate::setData(const QByteArray& data)
0052 {
0053     Q_Q(PieceTableByteArrayModel);
0054 
0055     const int oldSize = mPieceTable.size();
0056     const bool wasModifiedBefore = isModified();
0057     const QVector<Bookmark> bookmarks = mBookmarks.list();
0058 
0059     mInitialData = data;
0060     mPieceTable.init(data.size());
0061     mChangesDataStorage.clear();
0062     mBookmarks.clear();
0063 
0064     // TODO: how to tell this to the synchronizer?
0065     Q_EMIT q->contentsChanged(ArrayChangeMetricsList::oneReplacement(0, oldSize, data.size()));
0066     if (wasModifiedBefore) {
0067         Q_EMIT q->modifiedChanged(false);
0068     }
0069     if (!bookmarks.empty()) {
0070         Q_EMIT q->bookmarksRemoved(bookmarks);
0071     }
0072     Q_EMIT q->headVersionChanged(0);
0073     Q_EMIT q->revertedToVersionIndex(0);
0074 }
0075 
0076 void PieceTableByteArrayModelPrivate::setByte(Address offset, Byte byte)
0077 {
0078     Q_Q(PieceTableByteArrayModel);
0079 
0080     if (mReadOnly) {
0081         return;
0082     }
0083 
0084     const bool wasModifiedBefore = isModified();
0085     const int oldVersionIndex = versionIndex();
0086 
0087     Address storageOffset;
0088     const bool newChange = mPieceTable.replaceOne(offset, &storageOffset);
0089     mChangesDataStorage.append(storageOffset, byte);
0090 
0091     const ArrayChangeMetrics metrics =
0092         ArrayChangeMetrics::asReplacement(offset, 1, 1);
0093     const ByteArrayChange modification(metrics, mChangesDataStorage.data(storageOffset, 1));
0094     const QVector<Okteta::ByteArrayChange> modificationsList {
0095         modification
0096     };
0097 
0098     Q_EMIT q->contentsChanged(ArrayChangeMetricsList(metrics));
0099     Q_EMIT q->changesDone(modificationsList, oldVersionIndex, versionIndex());
0100     if (!wasModifiedBefore) {
0101         Q_EMIT q->modifiedChanged(true);
0102     }
0103     if (newChange) {
0104         Q_EMIT q->headVersionChanged(mPieceTable.changesCount());
0105     } else {
0106         Q_EMIT q->headVersionDescriptionChanged(mPieceTable.headChangeDescription());
0107     }
0108 }
0109 
0110 Size PieceTableByteArrayModelPrivate::insert(Address offset, const Byte* insertData, int insertLength)
0111 {
0112     // correct parameters
0113     const int oldSize = mPieceTable.size();
0114     if (offset > oldSize) {
0115         offset = oldSize;
0116     }
0117     // check all parameters
0118     if (mReadOnly || insertLength == 0) {
0119         return 0;
0120     }
0121 
0122     beginChanges();
0123 
0124     doInsertChange(offset, insertData, insertLength);
0125 
0126     endChanges();
0127 
0128     return insertLength;
0129 }
0130 
0131 // TODO: is anyone interested in the removed data? so we need a signal beforeRemoving(section)?
0132 Size PieceTableByteArrayModelPrivate::remove(const AddressRange& _removeRange)
0133 {
0134     AddressRange removeRange(_removeRange);
0135     // correct parameters
0136     const int oldSize = mPieceTable.size();
0137     removeRange.restrictEndTo(oldSize - 1);
0138     // check parameters
0139     if (removeRange.start() >= oldSize || removeRange.width() == 0) {
0140         return 0;
0141     }
0142 
0143     beginChanges();
0144 
0145     doRemoveChange(removeRange);
0146 
0147     endChanges();
0148 
0149     return removeRange.width();
0150 }
0151 
0152 Size PieceTableByteArrayModelPrivate::replace(const AddressRange& _removeRange, const Byte* insertData, int insertLength)
0153 {
0154     AddressRange removeRange(_removeRange);
0155     // correct parameters
0156     const int oldSize = mPieceTable.size();
0157     removeRange.restrictEndTo(oldSize - 1);
0158     // check parameters
0159     if ((removeRange.startsBehind(oldSize - 1) && removeRange.width() > 0)
0160         || (removeRange.width() == 0 && insertLength == 0)) {
0161         return 0;
0162     }
0163 
0164     beginChanges();
0165 
0166     doReplaceChange(removeRange, insertData, insertLength);
0167 
0168     endChanges();
0169 
0170     return insertLength;
0171 }
0172 
0173 bool PieceTableByteArrayModelPrivate::swap(Address firstStart, const AddressRange& _secondRange)
0174 {
0175     AddressRange secondRange(_secondRange);
0176     // correct parameters
0177     secondRange.restrictEndTo(mPieceTable.size() - 1);
0178     // check parameters
0179     if (secondRange.start() >= mPieceTable.size() || secondRange.width() <= 0
0180         || firstStart > mPieceTable.size() || secondRange.start() == firstStart) {
0181         return false;
0182     }
0183 
0184     beginChanges();
0185 
0186     doSwapChange(firstStart, secondRange);
0187 
0188     endChanges();
0189 
0190     return true;
0191 }
0192 
0193 Size PieceTableByteArrayModelPrivate::fill(Byte fillByte, Address offset, Size fillLength)
0194 {
0195     // correct parameters
0196     const int lengthToEnd = mPieceTable.size() - offset;
0197     if (fillLength < 0) {
0198         fillLength = lengthToEnd;
0199     }
0200     const int filledLength = (fillLength < lengthToEnd) ? fillLength : lengthToEnd;
0201     // check parameters
0202     const bool nothingToFill = (((int)offset >= mPieceTable.size()) || (fillLength == 0));
0203     if (nothingToFill) {
0204         return 0;
0205     }
0206 
0207     beginChanges();
0208 
0209     doFillChange(offset, filledLength, fillByte, fillLength);
0210 
0211     endChanges();
0212 
0213     return fillLength;
0214 }
0215 
0216 void PieceTableByteArrayModelPrivate::revertToVersionByIndex(int versionIndex)
0217 {
0218     Q_Q(PieceTableByteArrayModel);
0219 
0220     ArrayChangeMetricsList changeList;
0221     AddressRangeList changedRanges;
0222 
0223     const bool oldModified = isModified();
0224 
0225     const bool anyChanges =
0226         mPieceTable.revertBeforeChange(versionIndex, &changedRanges, &changeList);   // TODO: changedRanges no longer used
0227 
0228     if (!anyChanges) {
0229         return;
0230     }
0231 
0232     const bool newModified = isModified();
0233     const bool isModificationChanged = (oldModified != newModified);
0234 
0235 // TODO: what about the bookmarks? They need version support, too.
0236 // Modell of the bookmarks. But shouldn't they be independent?
0237 
0238     Q_EMIT q->contentsChanged(changeList);
0239     if (isModificationChanged) {
0240         Q_EMIT q->modifiedChanged(newModified);
0241     }
0242     Q_EMIT q->revertedToVersionIndex(versionIndex);
0243 }
0244 
0245 void PieceTableByteArrayModelPrivate::openGroupedChange(const QString& description)
0246 {
0247     Q_Q(PieceTableByteArrayModel);
0248 
0249     const bool isModifiedBefore = isModified();
0250     mBeforeGroupedChangeVersionIndex = mPieceTable.appliedChangesCount();
0251     mPieceTable.openGroupedChange(description);
0252 
0253     if (!isModifiedBefore) {
0254         Q_EMIT q->modifiedChanged(true);
0255     }
0256     Q_EMIT q->headVersionChanged(mPieceTable.changesCount());
0257 }
0258 
0259 void PieceTableByteArrayModelPrivate::cancelGroupedChange()
0260 {
0261     if (mBeforeGroupedChangeVersionIndex != InvalidVersionIndex) {
0262         revertToVersionByIndex(mBeforeGroupedChangeVersionIndex);
0263     }
0264 }
0265 
0266 void PieceTableByteArrayModelPrivate::closeGroupedChange(const QString& description)
0267 {
0268     Q_Q(PieceTableByteArrayModel);
0269 
0270     mPieceTable.closeGroupedChange(description);
0271     mBeforeGroupedChangeVersionIndex = InvalidVersionIndex;
0272 
0273     Q_EMIT q->headVersionDescriptionChanged(mPieceTable.headChangeDescription());
0274 }
0275 
0276 QVector<ByteArrayChange> PieceTableByteArrayModelPrivate::changes(int firstVersionIndex, int lastVersionIndex) const
0277 {
0278     QVector<ByteArrayChange> result;
0279 
0280     result.reserve(lastVersionIndex - firstVersionIndex);
0281     for (int i = firstVersionIndex; i < lastVersionIndex; ++i) {
0282         ArrayChangeMetrics metrics;
0283         Address storageOffset;
0284         mPieceTable.getChangeData(&metrics, &storageOffset, i);
0285 
0286         QByteArray data;
0287         if (metrics.type() == ArrayChangeMetrics::Replacement) {
0288             data = mChangesDataStorage.data(storageOffset, metrics.insertLength());
0289         }
0290         result.append(ByteArrayChange(metrics, data));
0291     }
0292 
0293     return result;
0294 }
0295 
0296 void PieceTableByteArrayModelPrivate::doChanges(const QVector<ByteArrayChange>& changes,
0297                                                 int oldVersionIndex, int newVersionIndex)
0298 {
0299 // qCDebug(LOG_OKTETA_CORE) << this<<" is at "<<versionIndex()<<", should from "<<oldVersionIndex<<" to "<<newVersionIndex;
0300     // changes already done? TODO: should this check be here?
0301     if (newVersionIndex == versionIndex()) {
0302         return;
0303     }
0304 
0305     // collision! TODO: what to do?
0306     if (oldVersionIndex != versionIndex()) {
0307         return;
0308     }
0309 
0310     beginChanges();
0311 
0312     for (const ByteArrayChange& change : changes) {
0313         const ArrayChangeMetrics& metrics = change.metrics();
0314         switch (metrics.type())
0315         {
0316         case ArrayChangeMetrics::Replacement:
0317         {
0318             const int oldSize = mPieceTable.size();
0319             const AddressRange removeRange = AddressRange::fromWidth(metrics.offset(), metrics.removeLength());
0320             // check parameters
0321             if (removeRange.startsBehind(oldSize - 1) && (removeRange.width() > 0)) {
0322                 // something is not matching
0323                 ; // TODO: set flag to undo all changes
0324             }
0325 
0326             const QByteArray& insertData = change.data();
0327             doReplaceChange(removeRange, reinterpret_cast<const Byte*>(insertData.constData()), insertData.size());
0328             break;
0329         }
0330         case ArrayChangeMetrics::Swapping:
0331         {
0332             const AddressRange secondRange(metrics.secondStart(), metrics.secondEnd());
0333             doSwapChange(metrics.offset(), secondRange);
0334             break;
0335         }
0336         default:
0337             ;
0338         }
0339     }
0340 
0341     // not the same versioning? TODO: where and how to define the version id?
0342 //     if( newVersionIndex != versionIndex() )
0343 //         return;
0344 // qCDebug(LOG_OKTETA_CORE)<<this<<" is now at "<<versionIndex();
0345 
0346     endChanges();
0347 }
0348 
0349 void PieceTableByteArrayModelPrivate::beginChanges()
0350 {
0351     mBeforeChangesVersionIndex = versionIndex();
0352     mBeforeChangesModified = isModified();
0353 }
0354 
0355 void PieceTableByteArrayModelPrivate::endChanges()
0356 {
0357     Q_Q(PieceTableByteArrayModel);
0358 
0359     const int currentVersionIndex = versionIndex();
0360     const bool newChange = (mBeforeChangesVersionIndex != currentVersionIndex);
0361     const bool currentIsModified = isModified();
0362     const bool modifiedChanged = (mBeforeChangesModified != currentIsModified);
0363 
0364     Q_EMIT q->contentsChanged(mChangeMetrics);
0365     Q_EMIT q->changesDone(mChanges, mBeforeChangesVersionIndex, currentVersionIndex);
0366     if (mBookmarksModified) {
0367         Q_EMIT q->bookmarksModified(true);
0368     }
0369     if (modifiedChanged) {
0370         Q_EMIT q->modifiedChanged(currentIsModified);
0371     }
0372     if (newChange) {
0373         Q_EMIT q->headVersionChanged(mPieceTable.changesCount());
0374     } else {
0375         Q_EMIT q->headVersionDescriptionChanged(mPieceTable.headChangeDescription());
0376     }
0377 
0378     // clean
0379     mChangeMetrics.clear();
0380     mChanges.clear();
0381     mBookmarksModified = false;
0382 }
0383 
0384 void PieceTableByteArrayModelPrivate::doInsertChange(Address offset,
0385                                                      const Byte* insertData, int insertLength)
0386 {
0387     Address storageOffset;
0388     mPieceTable.insert(offset, insertLength, &storageOffset);
0389     mChangesDataStorage.append(storageOffset, reinterpret_cast<const char*>(insertData), insertLength);
0390 
0391     mBookmarksModified |= mBookmarks.adjustToReplaced(offset, 0, insertLength);
0392 
0393     const ArrayChangeMetrics metrics = ArrayChangeMetrics::asReplacement(offset, 0, insertLength);
0394     const ByteArrayChange change(metrics, mChangesDataStorage.data(storageOffset, insertLength));
0395 
0396     mChangeMetrics.append(metrics);
0397     mChanges.append(change);
0398 }
0399 
0400 void PieceTableByteArrayModelPrivate::doRemoveChange(const AddressRange& removeRange)
0401 {
0402     mPieceTable.remove(removeRange);
0403 
0404     mBookmarksModified |= mBookmarks.adjustToReplaced(removeRange.start(), removeRange.width(), 0);
0405 
0406     const ArrayChangeMetrics metrics =
0407         ArrayChangeMetrics::asReplacement(removeRange.start(), removeRange.width(), 0);
0408     const ByteArrayChange change(metrics);
0409 
0410     mChangeMetrics.append(metrics);
0411     mChanges.append(change);
0412 }
0413 
0414 void PieceTableByteArrayModelPrivate::doReplaceChange(const AddressRange& removeRange,
0415                                                       const Byte* insertData, int insertLength)
0416 {
0417     Address storageOffset;
0418     mPieceTable.replace(removeRange, insertLength, &storageOffset);
0419     mChangesDataStorage.append(storageOffset, reinterpret_cast<const char*>(insertData), insertLength);
0420 
0421     mBookmarksModified |= mBookmarks.adjustToReplaced(removeRange.start(), removeRange.width(), insertLength);
0422 
0423     const ArrayChangeMetrics metrics =
0424         ArrayChangeMetrics::asReplacement(removeRange.start(), removeRange.width(), insertLength);
0425     const ByteArrayChange change(metrics, mChangesDataStorage.data(storageOffset, insertLength));
0426 
0427     mChangeMetrics.append(metrics);
0428     mChanges.append(change);
0429 }
0430 
0431 void PieceTableByteArrayModelPrivate::doSwapChange(Address firstStart, const AddressRange& secondRange)
0432 {
0433     mPieceTable.swap(firstStart, secondRange);
0434 
0435     mBookmarksModified |= mBookmarks.adjustToSwapped(firstStart, secondRange.start(), secondRange.width());
0436 
0437     const ArrayChangeMetrics metrics =
0438         ArrayChangeMetrics::asSwapping(firstStart, secondRange.start(), secondRange.width());
0439     const ByteArrayChange change(metrics);
0440 
0441     mChangeMetrics.append(metrics);
0442     mChanges.append(change);
0443 }
0444 
0445 void PieceTableByteArrayModelPrivate::doFillChange(Address offset, Size filledLength,
0446                                                    Byte fillByte, int fillLength)
0447 {
0448     Address storageOffset;
0449     mPieceTable.replace(offset, filledLength, fillLength, &storageOffset);
0450 
0451     mChangesDataStorage.appendFill(storageOffset, fillByte, fillLength);
0452 
0453     const ArrayChangeMetrics metrics =
0454         ArrayChangeMetrics::asReplacement(offset, filledLength, fillLength);
0455     const ByteArrayChange change(metrics);
0456 
0457     mChangeMetrics.append(metrics);
0458     mChanges.append(change);
0459 }
0460 
0461 }