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 }