File indexing completed on 2024-04-28 13:45: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 #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 }