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 }