File indexing completed on 2024-04-14 15:52:45

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