qint64 KisTileDataSwapper::pass(qint64 needToFreeMetric)
{
    qint64 freedMetric = 0;
    QList<KisTileData*> additionalCandidates;

    typename strategy::iterator *iter =
        strategy::beginIteration(m_d->store);

    KisTileData *item;

    while(iter->hasNext()) {
        item = iter->next();

        if(freedMetric >= needToFreeMetric) break;


        if(!strategy::isInteresting(item)) continue;

        if(strategy::swapOutFirst(item)) {
            if(iter->trySwapOut(item)) {
                freedMetric += item->pixelSize();
            }
        }
        else {
            item->markOld();
            additionalCandidates.append(item);
        }

    }

    foreach(item, additionalCandidates) {
        if(freedMetric >= needToFreeMetric) break;

        if(iter->trySwapOut(item)) {
            freedMetric += item->pixelSize();
        }
    }

    strategy::endIteration(m_d->store, iter);

    return freedMetric;
}
void KisTileCompressorsTest::doLowLevelRoundTrip(KisAbstractTileCompressor *compressor)
{
    const qint32 pixelSize = 1;
    quint8 oddPixel1 = 128;
    quint8 oddPixel2 = 129;

    /**
     * A small hack to acquire a standalone tile data.
     * globalTileDataStore is not exported out of kritaimage.so,
     * so we get it from the data manager
     */
    KisTiledDataManager dm(pixelSize, &oddPixel1);
    KisTileSP tile = dm.getTile(0, 0, true);
    tile->lockForWrite();


    KisTileData *td = tile->tileData();
    QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));

    qint32 bufferSize = compressor->tileDataBufferSize(td);
    quint8 *buffer = new quint8[bufferSize];
    qint32 bytesWritten;
    compressor->compressTileData(td, buffer, bufferSize, bytesWritten);
    dbgKrita << ppVar(bytesWritten);


    memset(td->data(), oddPixel2, TILESIZE);
    QVERIFY(memoryIsFilled(oddPixel2, td->data(), TILESIZE));

    compressor->decompressTileData(buffer, bytesWritten, td);

    QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));

    delete[] buffer;
    tile->unlock();
}
void KisSwappedDataStoreTest::testRoundTrip()
{
    const qint32 pixelSize = 1;
    const quint8 defaultPixel = 128;
    const qint32 NUM_TILES = 10000;

    KisImageConfig config;
    config.setMaxSwapSize(4);
    config.setSwapSlabSize(1);
    config.setSwapWindowSize(1);


    KisSwappedDataStore store;

    QList<KisTileData*> tileDataList;
    for(qint32 i = 0; i < NUM_TILES; i++)
        tileDataList.append(new KisTileData(pixelSize, &defaultPixel, KisTileDataStore::instance()));

    for(qint32 i = 0; i < NUM_TILES; i++) {
        KisTileData *td = tileDataList[i];
        QVERIFY(memoryIsFilled(defaultPixel, td->data(), TILESIZE));

        memset(td->data(), COLUMN2COLOR(i), TILESIZE);
        QVERIFY(memoryIsFilled(COLUMN2COLOR(i), td->data(), TILESIZE));

        // FIXME: take a lock of the tile data
        store.swapOutTileData(td);
    }

    store.debugStatistics();

    for(qint32 i = 0; i < NUM_TILES; i++) {
        KisTileData *td = tileDataList[i];
        QVERIFY(!td->data());
        // TODO: check num clones

        // FIXME: take a lock of the tile data
        store.swapInTileData(td);
        QVERIFY(memoryIsFilled(COLUMN2COLOR(i), td->data(), TILESIZE));
    }

    store.debugStatistics();

    for(qint32 i = 0; i < NUM_TILES; i++)
        delete tileDataList[i];
}
void KisTileCompressorsTest::doLowLevelRoundTripIncompressible(KisAbstractTileCompressor *compressor)
{
    const qint32 pixelSize = 1;
    quint8 oddPixel1 = 128;
    quint8 oddPixel2 = 129;

    QFile file(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QByteArray incompressibleArray = file.readAll();

    /**
     * A small hack to acquire a standalone tile data.
     * globalTileDataStore is not exported out of kritaimage.so,
     * so we get it from the data manager
     */
    KisTiledDataManager dm(pixelSize, &oddPixel1);
    KisTileSP tile = dm.getTile(0, 0, true);
    tile->lockForWrite();


    KisTileData *td = tile->tileData();
    QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));

    memcpy(td->data(), incompressibleArray.data(), TILESIZE);
    QVERIFY(!memcmp(td->data(), incompressibleArray.data(), TILESIZE));

    qint32 bufferSize = compressor->tileDataBufferSize(td);
    quint8 *buffer = new quint8[bufferSize];
    qint32 bytesWritten;
    compressor->compressTileData(td, buffer, bufferSize, bytesWritten);
    dbgKrita << ppVar(bytesWritten);


    memset(td->data(), oddPixel2, TILESIZE);
    QVERIFY(memoryIsFilled(oddPixel2, td->data(), TILESIZE));

    compressor->decompressTileData(buffer, bytesWritten, td);

    QVERIFY(!memcmp(td->data(), incompressibleArray.data(), TILESIZE));

    delete[] buffer;
    tile->unlock();
}
void KisLowMemoryTests::hangingTilesTest()
{
    quint8 defaultPixel = 0;
    KisTiledDataManager srcDM(1, &defaultPixel);

    KisTileSP srcTile = srcDM.getTile(0, 0, true);

    srcTile->lockForWrite();
    srcTile->lockForRead();


    KisTiledDataManager dstDM(1, &defaultPixel);
    dstDM.bitBlt(&srcDM, QRect(0,0,64,64));

    KisTileSP dstTile = dstDM.getTile(0, 0, true);

    dstTile->lockForRead();
    KisTileData *weirdTileData = dstTile->tileData();
    quint8 *weirdData = dstTile->data();

    QCOMPARE(weirdTileData, srcTile->tileData());
    QCOMPARE(weirdData, srcTile->data());

    KisTileDataStore::instance()->debugSwapAll();
    QCOMPARE(srcTile->tileData(), weirdTileData);
    QCOMPARE(dstTile->tileData(), weirdTileData);
    QCOMPARE(srcTile->data(), weirdData);
    QCOMPARE(dstTile->data(), weirdData);

    dstTile->lockForWrite();
    KisTileData *cowedTileData = dstTile->tileData();
    quint8 *cowedData = dstTile->data();

    QVERIFY(cowedTileData != weirdTileData);

    KisTileDataStore::instance()->debugSwapAll();
    QCOMPARE(srcTile->tileData(), weirdTileData);
    QCOMPARE(dstTile->tileData(), cowedTileData);
    QCOMPARE(srcTile->data(), weirdData);
    QCOMPARE(dstTile->data(), cowedData);

    QCOMPARE((int)weirdTileData->m_usersCount, 2);

    srcTile->unlock();
    srcTile->unlock();
    srcTile = 0;

    srcDM.clear();

    KisTileDataStore::instance()->debugSwapAll();
    QCOMPARE(dstTile->tileData(), cowedTileData);
    QCOMPARE(dstTile->data(), cowedData);

    // two crash tests
    QCOMPARE(weirdTileData->data(), weirdData);
    quint8 testPixel = *weirdData;
    QCOMPARE(testPixel, defaultPixel);

    QCOMPARE((int)weirdTileData->m_usersCount, 1);

    dstTile->unlock();
    dstTile->unlock();
    dstTile = 0;
}