File indexing completed on 2024-04-28 17:07:02
0001 /* 0002 This file is part of the Okteta Core library, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2008 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 "piecetable.hpp" 0010 0011 // Qt 0012 #include <QMutableLinkedListIterator> 0013 0014 namespace KPieceTable { 0015 0016 PieceTable::PieceTable(Size size) 0017 { 0018 init(size); 0019 } 0020 0021 void PieceTable::init(Size size) 0022 { 0023 mList.clear(); 0024 if (size > 0) { 0025 mList.append(Piece(0, size, Piece::OriginalStorage)); 0026 } 0027 0028 mSize = size; 0029 } 0030 0031 bool PieceTable::getStorageData(int* storageId, Address* storageOffset, Address dataOffset) const 0032 { 0033 bool result = false; 0034 0035 // TODO: use width or offset from current and next? 0036 AddressRange dataRange(0, -1); 0037 for (const Piece& piece : mList) { 0038 dataRange.setEndByWidth(piece.width()); 0039 0040 if (dataRange.includes(dataOffset)) { 0041 *storageId = piece.storageId(); 0042 // qCDebug(LOG_OKTETA_CORE) <<piece.start()<<"+"<<dataRange.localIndex( dataOffset ); 0043 *storageOffset = piece.start() + dataRange.localIndex(dataOffset); 0044 result = true; 0045 break; 0046 } 0047 dataRange.setStart(dataRange.nextBehindEnd()); 0048 } 0049 0050 return result; 0051 } 0052 0053 // TODO: optimize search from behind if dataOffset is large (perhaps by counting total size 0054 // TODO: combine sucsequenting inserts, also on other changes if possible link neighbors 0055 void PieceTable::insert(Address insertDataOffset, Size insertLength, Address storageOffset) 0056 { 0057 const int storageId = Piece::ChangeStorage; 0058 QMutableLinkedListIterator<Piece> it(mList); 0059 0060 const Piece insertPiece(storageOffset, insertLength, storageId); 0061 0062 // TODO: use width or offset from current and next? 0063 AddressRange dataRange(0, -1); 0064 while (it.hasNext()) { 0065 Piece& piece = it.peekNext(); 0066 dataRange.setEndByWidth(piece.width()); 0067 0068 // piece starts at offset? 0069 if (dataRange.start() == insertDataOffset) { 0070 it.insert(insertPiece); 0071 break; 0072 } 0073 if (dataRange.nextBehindEnd() == insertDataOffset) { 0074 if (piece.append(insertPiece)) { 0075 break; 0076 } 0077 } 0078 it.next(); 0079 if (dataRange.includes(insertDataOffset)) { 0080 const Piece secondPiece = piece.splitAtLocal(dataRange.localIndex(insertDataOffset)); 0081 it.insert(insertPiece); 0082 it.insert(secondPiece); 0083 break; 0084 } 0085 0086 dataRange.setStart(dataRange.nextBehindEnd()); 0087 } 0088 if (!it.hasNext() && (dataRange.start() == insertDataOffset)) { 0089 it.insert(insertPiece); 0090 } 0091 0092 mSize += insertLength; 0093 } 0094 0095 void PieceTable::insert(Address insertDataOffset, const PieceList& insertPieceList) 0096 { 0097 if (insertPieceList.isEmpty()) { 0098 return; 0099 } 0100 0101 bool isInserted = false; 0102 QMutableLinkedListIterator<Piece> it(mList); 0103 0104 // TODO: use width or offset from current and next? 0105 AddressRange dataRange(0, -1); 0106 while (it.hasNext()) { 0107 Piece& piece = it.peekNext(); 0108 dataRange.setEndByWidth(piece.width()); 0109 0110 // piece starts at offset? 0111 if (dataRange.start() == insertDataOffset) { 0112 int i = 0; 0113 if (it.hasPrevious()) { 0114 Piece& previousPiece = it.peekPrevious(); 0115 if (previousPiece.append(insertPieceList.at(0))) { 0116 if (insertPieceList.size() == 1) { 0117 if (previousPiece.append(piece)) { 0118 it.next(); 0119 it.remove(); 0120 } 0121 isInserted = true; 0122 break; 0123 } 0124 ++i; 0125 } 0126 } 0127 0128 const int lastIndex = insertPieceList.size() - 1; 0129 for (; i < lastIndex; ++i) { 0130 it.insert(insertPieceList.at(i)); 0131 } 0132 0133 const Piece& lastInsertPiece = insertPieceList.at(lastIndex); 0134 if (!piece.prepend(lastInsertPiece)) { 0135 it.insert(lastInsertPiece); 0136 } 0137 isInserted = true; 0138 break; 0139 } 0140 it.next(); 0141 if (dataRange.includes(insertDataOffset)) { 0142 const Piece secondPiece = piece.splitAtLocal(dataRange.localIndex(insertDataOffset)); 0143 for (int i = 0; i < insertPieceList.size(); ++i) { 0144 it.insert(insertPieceList.at(i)); 0145 } 0146 0147 it.insert(secondPiece); 0148 isInserted = true; 0149 break; 0150 } 0151 0152 dataRange.setStart(dataRange.nextBehindEnd()); 0153 } 0154 if (!isInserted && (dataRange.start() == insertDataOffset)) { 0155 int i = 0; 0156 if (it.hasPrevious()) { 0157 Piece& previousPiece = it.peekPrevious(); 0158 if (previousPiece.append(insertPieceList.at(0))) { 0159 ++i; 0160 } 0161 } 0162 for (; i < insertPieceList.size(); ++i) { 0163 it.insert(insertPieceList.at(i)); 0164 } 0165 } 0166 0167 mSize += insertPieceList.totalLength(); 0168 } 0169 0170 // TODO: make algorithm simpler 0171 PieceList PieceTable::remove(const AddressRange& removeRange) 0172 { 0173 PieceList removedPieceList; 0174 0175 AddressRange dataRange(0, -1); 0176 0177 QLinkedList<Piece>::Iterator it = mList.begin(); 0178 while (it != mList.end()) { 0179 Piece* piece = &*it; 0180 dataRange.setEndByWidth(piece->width()); 0181 0182 if (dataRange.includes(removeRange.start())) { 0183 QLinkedList<Piece>::Iterator firstRemoved = it; 0184 const Address firstDataRangeStart = dataRange.start(); 0185 // int sections = 1; 0186 0187 if (dataRange.includesInside(removeRange)) { 0188 const AddressRange localRange = dataRange.localRange(removeRange); 0189 const Piece removedPiece = piece->subPiece(localRange); 0190 const Piece secondPiece = piece->removeLocal(localRange); 0191 0192 mList.insert(++it, secondPiece); 0193 removedPieceList.append(removedPiece); 0194 // qCDebug(LOG_OKTETA_CORE) << "removed intern"; 0195 break; 0196 } 0197 do { 0198 if (dataRange.includes(removeRange.end())) { 0199 QLinkedList<Piece>::Iterator lastRemoved = it; 0200 // qCDebug(LOG_OKTETA_CORE) << removeRange.start() << removeRange.end() << firstDataRangeStart << dataRange.end(); 0201 // cut from first section if not all 0202 bool onlyCompletePiecesRemoved = true; 0203 if (firstDataRangeStart < removeRange.start()) { 0204 const Address newLocalEnd = removeRange.start() - firstDataRangeStart - 1; 0205 const Piece removedPiece = (*firstRemoved).removeEndBehindLocal(newLocalEnd); 0206 removedPieceList.append(removedPiece); 0207 0208 ++firstRemoved; 0209 onlyCompletePiecesRemoved = false; 0210 // qCDebug(LOG_OKTETA_CORE) << "end of first removed"<<piece->start()<<piece->end()<<"->"<<removedPiece.start()<<removedPiece.end(); 0211 // --sections; 0212 } 0213 0214 Piece removedPartialPieceFromLast; 0215 // cut from last section if not all 0216 if (removeRange.end() < dataRange.end()) { 0217 const Address newLocalStart = dataRange.localIndex(removeRange.end()) + 1; 0218 removedPartialPieceFromLast = piece->removeStartBeforeLocal(newLocalStart); 0219 0220 onlyCompletePiecesRemoved = false; 0221 // qCDebug(LOG_OKTETA_CORE) << "start of last removed"<<piece->start()<<piece->end()<<"->"<<removedPartialPieceFromLast.start()<<removedPartialPieceFromLast.end(); 0222 // --sections; 0223 } else { 0224 ++lastRemoved; 0225 } 0226 0227 std::for_each(firstRemoved, lastRemoved, [&removedPieceList](const Piece& piece) mutable { 0228 removedPieceList.append(piece); 0229 }); 0230 0231 if (removedPartialPieceFromLast.isValid()) { 0232 removedPieceList.append(removedPartialPieceFromLast); 0233 } 0234 0235 if (onlyCompletePiecesRemoved) { 0236 if (firstRemoved != mList.begin() && lastRemoved != mList.end()) { 0237 QLinkedList<Piece>::Iterator beforeFirstRemoved = firstRemoved - 1; 0238 if ((*beforeFirstRemoved).append(*lastRemoved)) { 0239 ++lastRemoved; 0240 } 0241 } 0242 } 0243 0244 mList.erase(firstRemoved, lastRemoved); 0245 // qCDebug(LOG_OKTETA_CORE) << "removed "<<sections; 0246 break; 0247 } 0248 dataRange.setStart(dataRange.nextBehindEnd()); 0249 ++it; 0250 // ++sections; 0251 // removeRange is longer than content TODO: just quit or at least remove till the end? 0252 if (it == mList.end()) { 0253 break; 0254 } 0255 piece = &*it; 0256 dataRange.setEndByWidth(piece->width()); 0257 } while (it != mList.end()); 0258 break; 0259 } 0260 0261 dataRange.setStart(dataRange.nextBehindEnd()); 0262 ++it; 0263 } 0264 0265 mSize -= removeRange.width(); 0266 0267 // qCDebug(LOG_OKTETA_CORE)<<"end:"<<asStringList(mList); 0268 return removedPieceList; 0269 } 0270 0271 PieceList PieceTable::replace(const AddressRange& removeRange, Size insertLength, Address storageOffset) 0272 { 0273 PieceList removedPieceList = remove(removeRange); 0274 insert(removeRange.start(), insertLength, storageOffset); 0275 return removedPieceList; 0276 } 0277 void PieceTable::replace(const AddressRange& removeRange, const PieceList& insertPieceList) 0278 { 0279 remove(removeRange); 0280 insert(removeRange.start(), insertPieceList); 0281 } 0282 0283 void PieceTable::swap(Address firstStart, const AddressRange& secondRange) 0284 { 0285 AddressRange dataRange(0, -1); 0286 0287 QLinkedList<Piece>::Iterator it = mList.begin(); 0288 while (it != mList.end()) { 0289 Piece* piece = &*it; 0290 dataRange.setEndByWidth(piece->width()); 0291 0292 if (dataRange.includes(firstStart)) { 0293 // piece does not start at offset? 0294 if (dataRange.start() < firstStart) { 0295 // well, split and insert second piece, even if we move it later, just make it work for now 0296 const Piece secondPiece = piece->splitAtLocal(dataRange.localIndex(firstStart)); 0297 it = mList.insert(++it, secondPiece); 0298 piece = &*it; 0299 dataRange.setStart(firstStart); 0300 } 0301 QLinkedList<Piece>::Iterator firstIt = it; 0302 0303 do { 0304 if (dataRange.includes(secondRange.start())) { 0305 // piece does not start at source? 0306 if (dataRange.start() < secondRange.start()) { 0307 // well, split and insert second piece, even if we move it later, just make it work for now 0308 const Piece secondPiece = piece->splitAtLocal(dataRange.localIndex(secondRange.start())); 0309 it = mList.insert(++it, secondPiece); 0310 piece = &*it; 0311 dataRange.setStart(secondRange.start()); 0312 } 0313 QLinkedList<Piece>::Iterator firstSecondIt = it; 0314 0315 do { 0316 if (dataRange.includes(secondRange.end())) { 0317 // piece does not start at source? 0318 if (secondRange.end() < dataRange.end()) { 0319 // well, split and insert second piece, even if we move it later, just make it work for now 0320 const Piece secondPiece = piece->splitAtLocal(dataRange.localIndex(secondRange.nextBehindEnd())); 0321 it = mList.insert(++it, secondPiece); 0322 } else { 0323 ++it; 0324 } 0325 QLinkedList<Piece>::Iterator behindLastSecondIt = it; 0326 0327 for (it = firstSecondIt; it != behindLastSecondIt; ++it) { 0328 firstIt = mList.insert(firstIt, *it) + 1; 0329 } 0330 0331 mList.erase(firstSecondIt, behindLastSecondIt); 0332 0333 // done, move out of here 0334 it = mList.end(); 0335 break; 0336 } 0337 dataRange.setStart(dataRange.nextBehindEnd()); 0338 ++it; 0339 0340 // removeRange is longer than content TODO: just quit or at least remove till the end? 0341 if (it == mList.end()) { 0342 break; 0343 } 0344 piece = &*it; 0345 dataRange.setEndByWidth(piece->width()); 0346 } while (it != mList.end()); 0347 } else { 0348 dataRange.setStart(dataRange.nextBehindEnd()); 0349 ++it; 0350 0351 // removeRange is longer than content TODO: just quit or at least remove till the end? 0352 if (it == mList.end()) { 0353 break; 0354 } 0355 piece = &*it; 0356 dataRange.setEndByWidth(piece->width()); 0357 } 0358 } while (it != mList.end()); 0359 break; 0360 } 0361 0362 dataRange.setStart(dataRange.nextBehindEnd()); 0363 ++it; 0364 } 0365 } 0366 0367 Piece PieceTable::replaceOne(Address dataOffset, Address storageOffset, int storageId) 0368 { 0369 int replacedStorageId = Piece::OriginalStorage; 0370 Address replacedStorageOffset = -1; 0371 0372 QMutableLinkedListIterator<Piece> it(mList); 0373 0374 AddressRange dataRange(0, -1); 0375 while (it.hasNext()) { 0376 Piece* piece = &it.peekNext(); 0377 dataRange.setEndByWidth(piece->width()); 0378 if (dataRange.includes(dataOffset)) { 0379 replacedStorageId = piece->storageId(); 0380 0381 const Piece replacePiece(storageOffset, 1, storageId); 0382 // piece starts at offset? 0383 if (dataRange.start() == dataOffset) { 0384 replacedStorageOffset = piece->start(); 0385 if (dataRange.width() == 1) { 0386 piece->set(storageOffset, storageOffset); 0387 piece->setStorageId(storageId); 0388 } else { 0389 it.insert(replacePiece); 0390 piece->moveStartBy(1); 0391 } 0392 } else if (dataRange.end() == dataOffset) { 0393 replacedStorageOffset = piece->end(); 0394 piece->moveEndBy(-1); 0395 it.next(); 0396 it.insert(replacePiece); 0397 } else { 0398 const Address localIndex = dataRange.localIndex(dataOffset); 0399 replacedStorageOffset = piece->start() + localIndex; 0400 0401 const Piece secondPiece = piece->removeLocal(AddressRange::fromWidth(localIndex, 1)); 0402 it.next(); 0403 it.insert(replacePiece); 0404 it.insert(secondPiece); 0405 } 0406 break; 0407 } 0408 it.next(); 0409 dataRange.setStart(dataRange.nextBehindEnd()); 0410 } 0411 return Piece(replacedStorageOffset, 1, replacedStorageId); 0412 } 0413 0414 }