browsercore/core/network/featherweightcache.cpp
author hgs
Fri, 15 Oct 2010 17:30:59 -0400
changeset 16 3c88a81ff781
permissions -rw-r--r--
201041
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
16
hgs
parents:
diff changeset
     1
/****************************************************************************
hgs
parents:
diff changeset
     2
**
hgs
parents:
diff changeset
     3
 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
hgs
parents:
diff changeset
     4
 *
hgs
parents:
diff changeset
     5
 * This file is part of Qt Web Runtime.
hgs
parents:
diff changeset
     6
 *
hgs
parents:
diff changeset
     7
 * This library is free software; you can redistribute it and/or
hgs
parents:
diff changeset
     8
 * modify it under the terms of the GNU Lesser General Public License
hgs
parents:
diff changeset
     9
 * version 2.1 as published by the Free Software Foundation.
hgs
parents:
diff changeset
    10
 *
hgs
parents:
diff changeset
    11
 * This library is distributed in the hope that it will be useful,
hgs
parents:
diff changeset
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
hgs
parents:
diff changeset
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
hgs
parents:
diff changeset
    14
 * Lesser General Public License for more details.
hgs
parents:
diff changeset
    15
 *
hgs
parents:
diff changeset
    16
 * You should have received a copy of the GNU Lesser General Public
hgs
parents:
diff changeset
    17
 * License along with this library; if not, write to the Free Software
hgs
parents:
diff changeset
    18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
hgs
parents:
diff changeset
    19
 *
hgs
parents:
diff changeset
    20
 */
hgs
parents:
diff changeset
    21
hgs
parents:
diff changeset
    22
# include <QAbstractNetworkCache>
hgs
parents:
diff changeset
    23
# include <QNetworkCacheMetaData>
hgs
parents:
diff changeset
    24
# include <QDateTime>
hgs
parents:
diff changeset
    25
# include <QDir>
hgs
parents:
diff changeset
    26
# include <QDirIterator>
hgs
parents:
diff changeset
    27
# include <QFile>
hgs
parents:
diff changeset
    28
# include <QtGlobal>
hgs
parents:
diff changeset
    29
# include <QDebug>
hgs
parents:
diff changeset
    30
# include <QQueue>
hgs
parents:
diff changeset
    31
# include <featherweightcache.h>
hgs
parents:
diff changeset
    32
# include <featherweightcache_p.h>
hgs
parents:
diff changeset
    33
#if defined(Q_OS_SYMBIAN)
hgs
parents:
diff changeset
    34
#include <e32std.h>
hgs
parents:
diff changeset
    35
#endif
hgs
parents:
diff changeset
    36
hgs
parents:
diff changeset
    37
//#define FEATHERWEIGHTCACHE_DEBUG
hgs
parents:
diff changeset
    38
hgs
parents:
diff changeset
    39
#define CACHE_POSTFIX QLatin1String(".d")
hgs
parents:
diff changeset
    40
#define PREPARED_SLASH QLatin1String("prepared/")
hgs
parents:
diff changeset
    41
#define DATA_SLASH QLatin1String("data/")
hgs
parents:
diff changeset
    42
#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3)
hgs
parents:
diff changeset
    43
hgs
parents:
diff changeset
    44
namespace WRT {
hgs
parents:
diff changeset
    45
hgs
parents:
diff changeset
    46
/*!
hgs
parents:
diff changeset
    47
    \class FeatherWeightCache
hgs
parents:
diff changeset
    48
hgs
parents:
diff changeset
    49
    \brief The FeatherWeightCache class provides a very basic disk cache.
hgs
parents:
diff changeset
    50
hgs
parents:
diff changeset
    51
    FeatherWeightCache stores each url in its own file inside of the
hgs
parents:
diff changeset
    52
    cacheDirectory using QDataStream.  Files with a text MimeType
hgs
parents:
diff changeset
    53
    are compressed using qCompress.  Each cache file starts with "cache_"
hgs
parents:
diff changeset
    54
    and ends in ".cache".  Data is written to disk only in insert()
hgs
parents:
diff changeset
    55
    and updateMetaData().
hgs
parents:
diff changeset
    56
hgs
parents:
diff changeset
    57
    Currently you can not share the same cache files with more then
hgs
parents:
diff changeset
    58
    one disk cache.
hgs
parents:
diff changeset
    59
hgs
parents:
diff changeset
    60
    FeatherWeightCache by default limits the amount of space that the cache will
hgs
parents:
diff changeset
    61
    use on the system to 50MB.
hgs
parents:
diff changeset
    62
hgs
parents:
diff changeset
    63
    Note you have to set the cache directory before it will work.
hgs
parents:
diff changeset
    64
hgs
parents:
diff changeset
    65
    A network disk cache can be enabled by:
hgs
parents:
diff changeset
    66
hgs
parents:
diff changeset
    67
    \snippet doc/src/snippets/code/src_network_access_FeatherWeightCache.cpp 0
hgs
parents:
diff changeset
    68
hgs
parents:
diff changeset
    69
    When sending requests, to control the preference of when to use the cache
hgs
parents:
diff changeset
    70
    and when to use the network, consider the following:
hgs
parents:
diff changeset
    71
hgs
parents:
diff changeset
    72
    \snippet doc/src/snippets/code/src_network_access_FeatherWeightCache.cpp 1
hgs
parents:
diff changeset
    73
hgs
parents:
diff changeset
    74
    To check whether the response came from the cache or from the network, the
hgs
parents:
diff changeset
    75
    following can be applied:
hgs
parents:
diff changeset
    76
hgs
parents:
diff changeset
    77
    \snippet doc/src/snippets/code/src_network_access_FeatherWeightCache.cpp 2
hgs
parents:
diff changeset
    78
*/
hgs
parents:
diff changeset
    79
hgs
parents:
diff changeset
    80
/*!
hgs
parents:
diff changeset
    81
    Creates a new disk cache. The \a parent argument is passed to
hgs
parents:
diff changeset
    82
    QAbstractNetworkCache's constructor.
hgs
parents:
diff changeset
    83
 */
hgs
parents:
diff changeset
    84
FeatherWeightCache::FeatherWeightCache(QObject *parent)
hgs
parents:
diff changeset
    85
    : QAbstractNetworkCache(parent)
hgs
parents:
diff changeset
    86
{
hgs
parents:
diff changeset
    87
hgs
parents:
diff changeset
    88
    d = new FeatherWeightCachePrivate(this);
hgs
parents:
diff changeset
    89
}
hgs
parents:
diff changeset
    90
hgs
parents:
diff changeset
    91
/*!
hgs
parents:
diff changeset
    92
    Destroys the cache object.  This does not clear the disk cache.
hgs
parents:
diff changeset
    93
 */
hgs
parents:
diff changeset
    94
FeatherWeightCache::~FeatherWeightCache()
hgs
parents:
diff changeset
    95
{
hgs
parents:
diff changeset
    96
    QHashIterator<QIODevice*, CacheItem*> it(d->inserting);
hgs
parents:
diff changeset
    97
    while (it.hasNext()) {
hgs
parents:
diff changeset
    98
        it.next();
hgs
parents:
diff changeset
    99
        delete it.value();
hgs
parents:
diff changeset
   100
    }
hgs
parents:
diff changeset
   101
hgs
parents:
diff changeset
   102
}
hgs
parents:
diff changeset
   103
hgs
parents:
diff changeset
   104
/*!
hgs
parents:
diff changeset
   105
    Returns the location where cached files will be stored.
hgs
parents:
diff changeset
   106
*/
hgs
parents:
diff changeset
   107
QString FeatherWeightCache::cacheDirectory() const
hgs
parents:
diff changeset
   108
{
hgs
parents:
diff changeset
   109
    return d->cacheDirectory;
hgs
parents:
diff changeset
   110
}
hgs
parents:
diff changeset
   111
hgs
parents:
diff changeset
   112
/*!
hgs
parents:
diff changeset
   113
    Sets the directory where cached files will be stored to \a cacheDir
hgs
parents:
diff changeset
   114
hgs
parents:
diff changeset
   115
    FeatherWeightCache will create this directory if it does not exists.
hgs
parents:
diff changeset
   116
hgs
parents:
diff changeset
   117
    Prepared cache items will be stored in the new cache directory when
hgs
parents:
diff changeset
   118
    they are inserted.
hgs
parents:
diff changeset
   119
hgs
parents:
diff changeset
   120
    \sa QDesktopServices::CacheLocation
hgs
parents:
diff changeset
   121
*/
hgs
parents:
diff changeset
   122
void FeatherWeightCache::setCacheDirectory(const QString &cacheDir)
hgs
parents:
diff changeset
   123
{
hgs
parents:
diff changeset
   124
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   125
    qDebug() << "FeatherWeightCache::setCacheDirectory()" << cacheDir;
hgs
parents:
diff changeset
   126
#endif
hgs
parents:
diff changeset
   127
    if (cacheDir.isEmpty())
hgs
parents:
diff changeset
   128
        return;
hgs
parents:
diff changeset
   129
    d->cacheDirectory = cacheDir;
hgs
parents:
diff changeset
   130
    QDir dir(d->cacheDirectory);
hgs
parents:
diff changeset
   131
    d->cacheDirectory = dir.absolutePath();
hgs
parents:
diff changeset
   132
    if (!d->cacheDirectory.endsWith(QLatin1Char('/')))
hgs
parents:
diff changeset
   133
        d->cacheDirectory += QLatin1Char('/');
hgs
parents:
diff changeset
   134
hgs
parents:
diff changeset
   135
    d->prepareLayout();
hgs
parents:
diff changeset
   136
}
hgs
parents:
diff changeset
   137
hgs
parents:
diff changeset
   138
/*!
hgs
parents:
diff changeset
   139
    \reimp
hgs
parents:
diff changeset
   140
*/
hgs
parents:
diff changeset
   141
qint64 FeatherWeightCache::cacheSize() const
hgs
parents:
diff changeset
   142
{
hgs
parents:
diff changeset
   143
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   144
    qDebug() << "FeatherWeightCache::cacheSize()";
hgs
parents:
diff changeset
   145
#endif
hgs
parents:
diff changeset
   146
    if (d->cacheDirectory.isEmpty())
hgs
parents:
diff changeset
   147
        return 0;
hgs
parents:
diff changeset
   148
    if (d->currentCacheSize < 0) {
hgs
parents:
diff changeset
   149
        FeatherWeightCache *that = const_cast<FeatherWeightCache*>(this);
hgs
parents:
diff changeset
   150
        that->d->currentCacheSize = that->expire();
hgs
parents:
diff changeset
   151
    }
hgs
parents:
diff changeset
   152
    return d->currentCacheSize;
hgs
parents:
diff changeset
   153
}
hgs
parents:
diff changeset
   154
hgs
parents:
diff changeset
   155
/*!
hgs
parents:
diff changeset
   156
    \reimp
hgs
parents:
diff changeset
   157
*/
hgs
parents:
diff changeset
   158
QIODevice *FeatherWeightCache::prepare(const QNetworkCacheMetaData &metaData)
hgs
parents:
diff changeset
   159
{
hgs
parents:
diff changeset
   160
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   161
    //qDebug() << "FeatherWeightCache::prepare()" << metaData.url();
hgs
parents:
diff changeset
   162
#endif
hgs
parents:
diff changeset
   163
    if (!metaData.isValid() || !metaData.url().isValid() || !metaData.saveToDisk())
hgs
parents:
diff changeset
   164
        return 0;
hgs
parents:
diff changeset
   165
hgs
parents:
diff changeset
   166
    if (d->cacheDirectory.isEmpty()) {
hgs
parents:
diff changeset
   167
        qWarning() << "FeatherWeightCache::prepare() The cache directory is not set";
hgs
parents:
diff changeset
   168
        return 0;
hgs
parents:
diff changeset
   169
    }
hgs
parents:
diff changeset
   170
hgs
parents:
diff changeset
   171
    foreach (QNetworkCacheMetaData::RawHeader header, metaData.rawHeaders()) {
hgs
parents:
diff changeset
   172
        if (header.first.toLower() == "content-length") {
hgs
parents:
diff changeset
   173
            qint64 size = header.second.toInt();
hgs
parents:
diff changeset
   174
            if (size > (maximumCacheSize() * 3)/4)
hgs
parents:
diff changeset
   175
                return 0;
hgs
parents:
diff changeset
   176
            break;
hgs
parents:
diff changeset
   177
        }
hgs
parents:
diff changeset
   178
    }
hgs
parents:
diff changeset
   179
    QScopedPointer<CacheItem> cacheItem(new CacheItem);
hgs
parents:
diff changeset
   180
    cacheItem->metaData = metaData;
hgs
parents:
diff changeset
   181
hgs
parents:
diff changeset
   182
    QIODevice *device = 0;
hgs
parents:
diff changeset
   183
    if (cacheItem->canCompress()) {
hgs
parents:
diff changeset
   184
        cacheItem->data.open(QBuffer::ReadWrite);
hgs
parents:
diff changeset
   185
        device = &(cacheItem->data);
hgs
parents:
diff changeset
   186
    } else {
hgs
parents:
diff changeset
   187
        QString templateName = d->tmpCacheFileName();
hgs
parents:
diff changeset
   188
        QT_TRY {
hgs
parents:
diff changeset
   189
            cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
hgs
parents:
diff changeset
   190
        } QT_CATCH(...) {
hgs
parents:
diff changeset
   191
            cacheItem->file = 0;
hgs
parents:
diff changeset
   192
        }
hgs
parents:
diff changeset
   193
        if (!cacheItem->file || !cacheItem->file->open()) {
hgs
parents:
diff changeset
   194
            qWarning() << "FeatherWeightCache::prepare() unable to open temporary file";
hgs
parents:
diff changeset
   195
            cacheItem.reset();
hgs
parents:
diff changeset
   196
            return 0;
hgs
parents:
diff changeset
   197
        }
hgs
parents:
diff changeset
   198
        cacheItem->writeHeader(cacheItem->file);
hgs
parents:
diff changeset
   199
        device = cacheItem->file;
hgs
parents:
diff changeset
   200
    }
hgs
parents:
diff changeset
   201
    d->inserting[device] = cacheItem.take();
hgs
parents:
diff changeset
   202
    return device;
hgs
parents:
diff changeset
   203
}
hgs
parents:
diff changeset
   204
hgs
parents:
diff changeset
   205
/*!
hgs
parents:
diff changeset
   206
    \reimp
hgs
parents:
diff changeset
   207
*/
hgs
parents:
diff changeset
   208
void FeatherWeightCache::insert(QIODevice *device)
hgs
parents:
diff changeset
   209
{
hgs
parents:
diff changeset
   210
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   211
    //qDebug() << "FeatherWeightCache::insert()" << device;
hgs
parents:
diff changeset
   212
#endif
hgs
parents:
diff changeset
   213
    QHash<QIODevice*, CacheItem*>::iterator it = d->inserting.find(device);
hgs
parents:
diff changeset
   214
    if (it == d->inserting.end()) {
hgs
parents:
diff changeset
   215
        qWarning() << "FeatherWeightCache::insert() called on a device we don't know about" << device;
hgs
parents:
diff changeset
   216
        return;
hgs
parents:
diff changeset
   217
    }
hgs
parents:
diff changeset
   218
hgs
parents:
diff changeset
   219
    d->storeItem(it.value());
hgs
parents:
diff changeset
   220
    delete it.value();
hgs
parents:
diff changeset
   221
    d->inserting.erase(it);
hgs
parents:
diff changeset
   222
}
hgs
parents:
diff changeset
   223
hgs
parents:
diff changeset
   224
hgs
parents:
diff changeset
   225
/*!
hgs
parents:
diff changeset
   226
    Create subdirectories and other housekeeping on the filesystem.
hgs
parents:
diff changeset
   227
    Prevents too many files from being present in any single directory.
hgs
parents:
diff changeset
   228
*/
hgs
parents:
diff changeset
   229
void FeatherWeightCachePrivate::prepareLayout()
hgs
parents:
diff changeset
   230
{
hgs
parents:
diff changeset
   231
    QDir prepared;
hgs
parents:
diff changeset
   232
    prepared.mkpath(cacheDirectory + PREPARED_SLASH);
hgs
parents:
diff changeset
   233
hgs
parents:
diff changeset
   234
    QString path = cacheDirectory + DATA_SLASH;
hgs
parents:
diff changeset
   235
    QDir dataDirectory(path);
hgs
parents:
diff changeset
   236
hgs
parents:
diff changeset
   237
    //Create directory and subdirectories 0-F
hgs
parents:
diff changeset
   238
    dataDirectory.mkpath(path);
hgs
parents:
diff changeset
   239
    for ( uint i = 0; i < 16 ; i++ ) {
hgs
parents:
diff changeset
   240
        QString str = QString::number(i, 16);
hgs
parents:
diff changeset
   241
        QString subdir = dataDirectory.path() + QDir::separator() + str;
hgs
parents:
diff changeset
   242
        dataDirectory.mkdir(subdir);
hgs
parents:
diff changeset
   243
    }
hgs
parents:
diff changeset
   244
hgs
parents:
diff changeset
   245
    // TODO: populate volumeInfo members here base on which disk/fileystem
hgs
parents:
diff changeset
   246
    // you plan to write (a) temp ("prepared") files to (b) write final cache files too
hgs
parents:
diff changeset
   247
    // volumeInfo->clusterSize = 1024;
hgs
parents:
diff changeset
   248
    // volumeInfo->readBufSize = 16384;
hgs
parents:
diff changeset
   249
    // volumeInfo->writeBufSize = 16384;
hgs
parents:
diff changeset
   250
#ifdef Q_OS_SYMBIAN
hgs
parents:
diff changeset
   251
    //VolumeIOParam(TInt aDriveNo, TVolumeIOParamInfo &aParamInfo) const;
hgs
parents:
diff changeset
   252
#endif
hgs
parents:
diff changeset
   253
}
hgs
parents:
diff changeset
   254
hgs
parents:
diff changeset
   255
// CRC32 implementation.
hgs
parents:
diff changeset
   256
// Could be made into new API QByteArray:qChecksum32()
hgs
parents:
diff changeset
   257
static const quint32 crc_tbl32[256] = {
hgs
parents:
diff changeset
   258
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
hgs
parents:
diff changeset
   259
    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
hgs
parents:
diff changeset
   260
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
hgs
parents:
diff changeset
   261
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
hgs
parents:
diff changeset
   262
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
hgs
parents:
diff changeset
   263
    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
hgs
parents:
diff changeset
   264
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
hgs
parents:
diff changeset
   265
    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
hgs
parents:
diff changeset
   266
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
hgs
parents:
diff changeset
   267
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
hgs
parents:
diff changeset
   268
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
hgs
parents:
diff changeset
   269
    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
hgs
parents:
diff changeset
   270
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
hgs
parents:
diff changeset
   271
    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
hgs
parents:
diff changeset
   272
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
hgs
parents:
diff changeset
   273
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
hgs
parents:
diff changeset
   274
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
hgs
parents:
diff changeset
   275
    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
hgs
parents:
diff changeset
   276
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
hgs
parents:
diff changeset
   277
    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
hgs
parents:
diff changeset
   278
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
hgs
parents:
diff changeset
   279
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
hgs
parents:
diff changeset
   280
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
hgs
parents:
diff changeset
   281
    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
hgs
parents:
diff changeset
   282
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
hgs
parents:
diff changeset
   283
    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
hgs
parents:
diff changeset
   284
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
hgs
parents:
diff changeset
   285
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
hgs
parents:
diff changeset
   286
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
hgs
parents:
diff changeset
   287
    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
hgs
parents:
diff changeset
   288
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
hgs
parents:
diff changeset
   289
    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
hgs
parents:
diff changeset
   290
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
hgs
parents:
diff changeset
   291
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
hgs
parents:
diff changeset
   292
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
hgs
parents:
diff changeset
   293
    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
hgs
parents:
diff changeset
   294
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
hgs
parents:
diff changeset
   295
    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
hgs
parents:
diff changeset
   296
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
hgs
parents:
diff changeset
   297
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
hgs
parents:
diff changeset
   298
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
hgs
parents:
diff changeset
   299
    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
hgs
parents:
diff changeset
   300
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
hgs
parents:
diff changeset
   301
    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
hgs
parents:
diff changeset
   302
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
hgs
parents:
diff changeset
   303
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
hgs
parents:
diff changeset
   304
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
hgs
parents:
diff changeset
   305
    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
hgs
parents:
diff changeset
   306
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
hgs
parents:
diff changeset
   307
    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
hgs
parents:
diff changeset
   308
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
hgs
parents:
diff changeset
   309
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
hgs
parents:
diff changeset
   310
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
hgs
parents:
diff changeset
   311
    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
hgs
parents:
diff changeset
   312
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
hgs
parents:
diff changeset
   313
    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
hgs
parents:
diff changeset
   314
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
hgs
parents:
diff changeset
   315
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
hgs
parents:
diff changeset
   316
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
hgs
parents:
diff changeset
   317
    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
hgs
parents:
diff changeset
   318
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
hgs
parents:
diff changeset
   319
    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
hgs
parents:
diff changeset
   320
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
hgs
parents:
diff changeset
   321
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
hgs
parents:
diff changeset
   322
};
hgs
parents:
diff changeset
   323
hgs
parents:
diff changeset
   324
quint32 FeatherWeightCachePrivate::crc32(const char *data, uint len)
hgs
parents:
diff changeset
   325
{
hgs
parents:
diff changeset
   326
    const uchar *p = reinterpret_cast<const uchar *>(data);
hgs
parents:
diff changeset
   327
    const uchar *q = p + len;
hgs
parents:
diff changeset
   328
    const quint32 init = 0xFFFFFFFFL;
hgs
parents:
diff changeset
   329
hgs
parents:
diff changeset
   330
    quint32 crc32 = init;
hgs
parents:
diff changeset
   331
    while (p < q) {
hgs
parents:
diff changeset
   332
        crc32 = (crc32 >> 8) ^ crc_tbl32[(crc32 ^ *p++) & 0xffL];
hgs
parents:
diff changeset
   333
    }
hgs
parents:
diff changeset
   334
    return crc32 ^ init ;
hgs
parents:
diff changeset
   335
}
hgs
parents:
diff changeset
   336
hgs
parents:
diff changeset
   337
void FeatherWeightCachePrivate::storeItem(CacheItem *cacheItem)
hgs
parents:
diff changeset
   338
{
hgs
parents:
diff changeset
   339
    Q_ASSERT(cacheItem->metaData.saveToDisk());
hgs
parents:
diff changeset
   340
hgs
parents:
diff changeset
   341
    QString fileName = cacheFileName(cacheItem->metaData.url());
hgs
parents:
diff changeset
   342
    Q_ASSERT(!fileName.isEmpty());
hgs
parents:
diff changeset
   343
hgs
parents:
diff changeset
   344
hgs
parents:
diff changeset
   345
    if (currentCacheSize > 0) {
hgs
parents:
diff changeset
   346
        currentCacheSize += FILESYSTEMOVERHEAD + cacheItem->size();
hgs
parents:
diff changeset
   347
    }
hgs
parents:
diff changeset
   348
hgs
parents:
diff changeset
   349
hgs
parents:
diff changeset
   350
    //lut.insert( URL2HASH(cacheItem->metaData.url()), FILESYSTEMOVERHEAD + cacheItem->size() ) ;
hgs
parents:
diff changeset
   351
hgs
parents:
diff changeset
   352
    currentCacheSize = (reinterpret_cast<FeatherWeightCache *>(parent()))->expire();
hgs
parents:
diff changeset
   353
hgs
parents:
diff changeset
   354
hgs
parents:
diff changeset
   355
hgs
parents:
diff changeset
   356
    if (!cacheItem->file) {
hgs
parents:
diff changeset
   357
        QString templateName = tmpCacheFileName();
hgs
parents:
diff changeset
   358
        cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
hgs
parents:
diff changeset
   359
        if (cacheItem->file->open()) {
hgs
parents:
diff changeset
   360
            cacheItem->writeHeader(cacheItem->file);
hgs
parents:
diff changeset
   361
            cacheItem->writeCompressedData(cacheItem->file);
hgs
parents:
diff changeset
   362
        }
hgs
parents:
diff changeset
   363
    }
hgs
parents:
diff changeset
   364
hgs
parents:
diff changeset
   365
    if (cacheItem->file
hgs
parents:
diff changeset
   366
        && cacheItem->file->isOpen()
hgs
parents:
diff changeset
   367
        && cacheItem->file->error() == QFile::NoError) {
hgs
parents:
diff changeset
   368
        cacheItem->file->setAutoRemove(false);
hgs
parents:
diff changeset
   369
        // ### use atomic rename rather then remove & rename
hgs
parents:
diff changeset
   370
        if (cacheItem->file->rename(fileName))
hgs
parents:
diff changeset
   371
            currentCacheSize += cacheItem->file->size();
hgs
parents:
diff changeset
   372
        else {
hgs
parents:
diff changeset
   373
            // Presume that the destination file exists and/or is open. So try nuking.
hgs
parents:
diff changeset
   374
            bool err1 = QFile::remove(fileName);
hgs
parents:
diff changeset
   375
            Q_UNUSED(err1);
hgs
parents:
diff changeset
   376
            bool err2 = cacheItem->file->rename(fileName);
hgs
parents:
diff changeset
   377
            // You are hopeless. Don't persist
hgs
parents:
diff changeset
   378
            if (!err2)  {
hgs
parents:
diff changeset
   379
                cacheItem->file->setAutoRemove(true);
hgs
parents:
diff changeset
   380
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   381
                qWarning() << "FeatherWeightCache: couldn't replace the cache file " << fileName;
hgs
parents:
diff changeset
   382
#endif
hgs
parents:
diff changeset
   383
            }
hgs
parents:
diff changeset
   384
        }
hgs
parents:
diff changeset
   385
    }
hgs
parents:
diff changeset
   386
    if (cacheItem->metaData.url() == lastItem.metaData.url())
hgs
parents:
diff changeset
   387
        lastItem.reset();
hgs
parents:
diff changeset
   388
}
hgs
parents:
diff changeset
   389
hgs
parents:
diff changeset
   390
/*!
hgs
parents:
diff changeset
   391
    \reimp
hgs
parents:
diff changeset
   392
*/
hgs
parents:
diff changeset
   393
bool FeatherWeightCache::remove(const QUrl &url)
hgs
parents:
diff changeset
   394
{
hgs
parents:
diff changeset
   395
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   396
    //qDebug() << "FeatherWeightCache::remove()" << url;
hgs
parents:
diff changeset
   397
#endif
hgs
parents:
diff changeset
   398
hgs
parents:
diff changeset
   399
    // remove is also used to cancel insertions, not a common operation
hgs
parents:
diff changeset
   400
    QHashIterator<QIODevice*, CacheItem*> it(d->inserting);
hgs
parents:
diff changeset
   401
    while (it.hasNext()) {
hgs
parents:
diff changeset
   402
        it.next();
hgs
parents:
diff changeset
   403
        CacheItem *item = it.value();
hgs
parents:
diff changeset
   404
        if (item && item->metaData.url() == url) {
hgs
parents:
diff changeset
   405
            delete item;
hgs
parents:
diff changeset
   406
            d->inserting.remove(it.key());
hgs
parents:
diff changeset
   407
            return true;
hgs
parents:
diff changeset
   408
        }
hgs
parents:
diff changeset
   409
    }
hgs
parents:
diff changeset
   410
hgs
parents:
diff changeset
   411
    if (d->lastItem.metaData.url() == url)
hgs
parents:
diff changeset
   412
        d->lastItem.reset();
hgs
parents:
diff changeset
   413
    return d->removeFile(d->cacheFileName(url));
hgs
parents:
diff changeset
   414
}
hgs
parents:
diff changeset
   415
hgs
parents:
diff changeset
   416
/*!
hgs
parents:
diff changeset
   417
    Put all of the misc file removing into one function to be extra safe
hgs
parents:
diff changeset
   418
 */
hgs
parents:
diff changeset
   419
bool FeatherWeightCachePrivate::removeFile(const QString &file)
hgs
parents:
diff changeset
   420
{
hgs
parents:
diff changeset
   421
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   422
    //qDebug() << "FeatherWeightCache::removFile()" << file;
hgs
parents:
diff changeset
   423
#endif
hgs
parents:
diff changeset
   424
    if (file.isEmpty())
hgs
parents:
diff changeset
   425
        return false;
hgs
parents:
diff changeset
   426
    QFileInfo info(file);
hgs
parents:
diff changeset
   427
    QString fileName = info.fileName();
hgs
parents:
diff changeset
   428
    if (!fileName.endsWith(CACHE_POSTFIX))
hgs
parents:
diff changeset
   429
        return false;
hgs
parents:
diff changeset
   430
    qint64 size = info.size();
hgs
parents:
diff changeset
   431
    if (QFile::remove(file)) {
hgs
parents:
diff changeset
   432
        currentCacheSize -= size;
hgs
parents:
diff changeset
   433
        return true;
hgs
parents:
diff changeset
   434
    }
hgs
parents:
diff changeset
   435
    return false;
hgs
parents:
diff changeset
   436
}
hgs
parents:
diff changeset
   437
hgs
parents:
diff changeset
   438
/*!
hgs
parents:
diff changeset
   439
    Use signal from worker thread to update disk usage awareness
hgs
parents:
diff changeset
   440
 */
hgs
parents:
diff changeset
   441
void FeatherWeightCachePrivate::updateCacheSize(qint64 newSize)
hgs
parents:
diff changeset
   442
{
hgs
parents:
diff changeset
   443
    currentCacheSize = newSize;
hgs
parents:
diff changeset
   444
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   445
    qDebug() << "FeatherWeightCachePrivate::updateCacheSize " << " new size " << currentCacheSize;
hgs
parents:
diff changeset
   446
#endif
hgs
parents:
diff changeset
   447
}
hgs
parents:
diff changeset
   448
hgs
parents:
diff changeset
   449
/*!
hgs
parents:
diff changeset
   450
    \reimp
hgs
parents:
diff changeset
   451
*/
hgs
parents:
diff changeset
   452
QNetworkCacheMetaData FeatherWeightCache::metaData(const QUrl &url)
hgs
parents:
diff changeset
   453
{
hgs
parents:
diff changeset
   454
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   455
    //qDebug() << "FeatherWeightCache::metaData()" << url;
hgs
parents:
diff changeset
   456
#endif
hgs
parents:
diff changeset
   457
    if (d->lastItem.metaData.url() == url)
hgs
parents:
diff changeset
   458
        return d->lastItem.metaData;
hgs
parents:
diff changeset
   459
    return fileMetaData(d->cacheFileName(url));
hgs
parents:
diff changeset
   460
}
hgs
parents:
diff changeset
   461
hgs
parents:
diff changeset
   462
/*!
hgs
parents:
diff changeset
   463
    Returns the QNetworkCacheMetaData for the cache file \a fileName.
hgs
parents:
diff changeset
   464
hgs
parents:
diff changeset
   465
    If \a fileName is not a cache file QNetworkCacheMetaData will be invalid.
hgs
parents:
diff changeset
   466
 */
hgs
parents:
diff changeset
   467
QNetworkCacheMetaData FeatherWeightCache::fileMetaData(const QString &fileName) const
hgs
parents:
diff changeset
   468
{
hgs
parents:
diff changeset
   469
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   470
    //qDebug() << "FeatherWeightCache::fileMetaData()" << fileName;
hgs
parents:
diff changeset
   471
#endif
hgs
parents:
diff changeset
   472
    QFile file(fileName);
hgs
parents:
diff changeset
   473
    if (!file.open(QFile::ReadOnly))
hgs
parents:
diff changeset
   474
        return QNetworkCacheMetaData();
hgs
parents:
diff changeset
   475
    if (!d->lastItem.read(&file, false)) {
hgs
parents:
diff changeset
   476
        file.close();
hgs
parents:
diff changeset
   477
        FeatherWeightCachePrivate *that = const_cast<FeatherWeightCachePrivate*>(d);
hgs
parents:
diff changeset
   478
        that->removeFile(fileName);
hgs
parents:
diff changeset
   479
    }
hgs
parents:
diff changeset
   480
    return d->lastItem.metaData;
hgs
parents:
diff changeset
   481
}
hgs
parents:
diff changeset
   482
hgs
parents:
diff changeset
   483
/*!
hgs
parents:
diff changeset
   484
    \reimp
hgs
parents:
diff changeset
   485
*/
hgs
parents:
diff changeset
   486
QIODevice *FeatherWeightCache::data(const QUrl &url)
hgs
parents:
diff changeset
   487
{
hgs
parents:
diff changeset
   488
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   489
    //qDebug() << "FeatherWeightCache::data()" << url;
hgs
parents:
diff changeset
   490
#endif
hgs
parents:
diff changeset
   491
hgs
parents:
diff changeset
   492
    QScopedPointer<QBuffer> buffer;
hgs
parents:
diff changeset
   493
    if (!url.isValid())
hgs
parents:
diff changeset
   494
        return 0;
hgs
parents:
diff changeset
   495
    if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
hgs
parents:
diff changeset
   496
        buffer.reset(new QBuffer);
hgs
parents:
diff changeset
   497
        buffer->setData(d->lastItem.data.data());
hgs
parents:
diff changeset
   498
    } else {
hgs
parents:
diff changeset
   499
        QScopedPointer<QFile> file(new QFile(d->cacheFileName(url)));
hgs
parents:
diff changeset
   500
        if (!file->open(QFile::ReadOnly | QIODevice::Unbuffered))
hgs
parents:
diff changeset
   501
            return 0;
hgs
parents:
diff changeset
   502
hgs
parents:
diff changeset
   503
        if (!d->lastItem.read(file.data(), true)) {
hgs
parents:
diff changeset
   504
            file->close();
hgs
parents:
diff changeset
   505
            remove(url);
hgs
parents:
diff changeset
   506
            return 0;
hgs
parents:
diff changeset
   507
        }
hgs
parents:
diff changeset
   508
        if (d->lastItem.data.isOpen()) {
hgs
parents:
diff changeset
   509
            // compressed
hgs
parents:
diff changeset
   510
            buffer.reset(new QBuffer);
hgs
parents:
diff changeset
   511
            buffer->setData(d->lastItem.data.data());
hgs
parents:
diff changeset
   512
        } else {
hgs
parents:
diff changeset
   513
            buffer.reset(new QBuffer);
hgs
parents:
diff changeset
   514
            // ### verify that QFile uses the fd size and not the file name
hgs
parents:
diff changeset
   515
            qint64 size = file->size() - file->pos();
hgs
parents:
diff changeset
   516
            const uchar *p = 0;
hgs
parents:
diff changeset
   517
#ifndef Q_OS_WINCE
hgs
parents:
diff changeset
   518
            p = file->map(file->pos(), size);
hgs
parents:
diff changeset
   519
#endif
hgs
parents:
diff changeset
   520
            if (p) {
hgs
parents:
diff changeset
   521
                buffer->setData((const char *)p, size);
hgs
parents:
diff changeset
   522
                file.take()->setParent(buffer.data());
hgs
parents:
diff changeset
   523
            } else {
hgs
parents:
diff changeset
   524
                buffer->setData(file->readAll());
hgs
parents:
diff changeset
   525
            }
hgs
parents:
diff changeset
   526
        }
hgs
parents:
diff changeset
   527
    }
hgs
parents:
diff changeset
   528
    buffer->open(QBuffer::ReadOnly);
hgs
parents:
diff changeset
   529
    return buffer.take();
hgs
parents:
diff changeset
   530
}
hgs
parents:
diff changeset
   531
hgs
parents:
diff changeset
   532
/*!
hgs
parents:
diff changeset
   533
    \reimp
hgs
parents:
diff changeset
   534
*/
hgs
parents:
diff changeset
   535
void FeatherWeightCache::updateMetaData(const QNetworkCacheMetaData &metaData)
hgs
parents:
diff changeset
   536
{
hgs
parents:
diff changeset
   537
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   538
    qDebug() << "FeatherWeightCache::updateMetaData()" << metaData.url();
hgs
parents:
diff changeset
   539
#endif
hgs
parents:
diff changeset
   540
    QUrl url = metaData.url();
hgs
parents:
diff changeset
   541
    QIODevice *oldDevice = data(url);
hgs
parents:
diff changeset
   542
    if (!oldDevice) {
hgs
parents:
diff changeset
   543
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   544
        qDebug() << "FeatherWeightCache::updateMetaData(), no device!";
hgs
parents:
diff changeset
   545
#endif
hgs
parents:
diff changeset
   546
        return;
hgs
parents:
diff changeset
   547
    }
hgs
parents:
diff changeset
   548
hgs
parents:
diff changeset
   549
    QIODevice *newDevice = prepare(metaData);
hgs
parents:
diff changeset
   550
    if (!newDevice) {
hgs
parents:
diff changeset
   551
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   552
        qDebug() << "FeatherWeightCache::updateMetaData(), no new device!" << url;
hgs
parents:
diff changeset
   553
#endif
hgs
parents:
diff changeset
   554
        return;
hgs
parents:
diff changeset
   555
    }
hgs
parents:
diff changeset
   556
    //TODO: Optimize this somehow?
hgs
parents:
diff changeset
   557
    char data[1024];
hgs
parents:
diff changeset
   558
    while (!oldDevice->atEnd()) {
hgs
parents:
diff changeset
   559
        qint64 s = oldDevice->read(data, 1024);
hgs
parents:
diff changeset
   560
        newDevice->write(data, s);
hgs
parents:
diff changeset
   561
    }
hgs
parents:
diff changeset
   562
    delete oldDevice;
hgs
parents:
diff changeset
   563
    insert(newDevice);
hgs
parents:
diff changeset
   564
}
hgs
parents:
diff changeset
   565
hgs
parents:
diff changeset
   566
/*!
hgs
parents:
diff changeset
   567
    Returns the current maximum size for the disk cache.
hgs
parents:
diff changeset
   568
hgs
parents:
diff changeset
   569
    \sa setMaximumCacheSize()
hgs
parents:
diff changeset
   570
 */
hgs
parents:
diff changeset
   571
qint64 FeatherWeightCache::maximumCacheSize() const
hgs
parents:
diff changeset
   572
{
hgs
parents:
diff changeset
   573
    return d->maximumCacheSize;
hgs
parents:
diff changeset
   574
}
hgs
parents:
diff changeset
   575
hgs
parents:
diff changeset
   576
/*!
hgs
parents:
diff changeset
   577
    Sets the maximum size of the disk cache to be \a size.
hgs
parents:
diff changeset
   578
hgs
parents:
diff changeset
   579
    If the new size is smaller then the current cache size then the cache will call expire().
hgs
parents:
diff changeset
   580
hgs
parents:
diff changeset
   581
    \sa maximumCacheSize()
hgs
parents:
diff changeset
   582
 */
hgs
parents:
diff changeset
   583
void FeatherWeightCache::setMaximumCacheSize(qint64 size)
hgs
parents:
diff changeset
   584
{
hgs
parents:
diff changeset
   585
hgs
parents:
diff changeset
   586
    bool expireCache = (size < d->maximumCacheSize);
hgs
parents:
diff changeset
   587
    d->maximumCacheSize = size;
hgs
parents:
diff changeset
   588
    if (expireCache)
hgs
parents:
diff changeset
   589
        d->currentCacheSize = expire();
hgs
parents:
diff changeset
   590
}
hgs
parents:
diff changeset
   591
hgs
parents:
diff changeset
   592
/*!
hgs
parents:
diff changeset
   593
    Cleans the cache so that its size is under the maximum cache size.
hgs
parents:
diff changeset
   594
    Returns the current size of the cache.
hgs
parents:
diff changeset
   595
hgs
parents:
diff changeset
   596
    When the current size of the cache is greater than the maximumCacheSize()
hgs
parents:
diff changeset
   597
    older cache files are removed until the total size is less then 90% of
hgs
parents:
diff changeset
   598
    maximumCacheSize() starting with the oldest ones first using the file
hgs
parents:
diff changeset
   599
    creation date to determine how old a cache file is.
hgs
parents:
diff changeset
   600
hgs
parents:
diff changeset
   601
    Subclasses can reimplement this function to change the order that cache
hgs
parents:
diff changeset
   602
    files are removed taking into account information in the application
hgs
parents:
diff changeset
   603
    knows about that FeatherWeightCache does not, for example the number of times
hgs
parents:
diff changeset
   604
    a cache is accessed.
hgs
parents:
diff changeset
   605
hgs
parents:
diff changeset
   606
    Note: cacheSize() calls expire if the current cache size is unknown.
hgs
parents:
diff changeset
   607
hgs
parents:
diff changeset
   608
    \sa maximumCacheSize(), fileMetaData()
hgs
parents:
diff changeset
   609
 */
hgs
parents:
diff changeset
   610
qint64 FeatherWeightCache::expire()
hgs
parents:
diff changeset
   611
{
hgs
parents:
diff changeset
   612
hgs
parents:
diff changeset
   613
    if (d->currentCacheSize >= 0 && d->currentCacheSize < maximumCacheSize())
hgs
parents:
diff changeset
   614
        return d->currentCacheSize;
hgs
parents:
diff changeset
   615
hgs
parents:
diff changeset
   616
    if (cacheDirectory().isEmpty()) {
hgs
parents:
diff changeset
   617
        qWarning() << "FeatherWeightCache::expire() The cache directory is not set";
hgs
parents:
diff changeset
   618
        return 0;
hgs
parents:
diff changeset
   619
    }
hgs
parents:
diff changeset
   620
hgs
parents:
diff changeset
   621
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   622
    qDebug() << "Calling expire, size = " << d->currentCacheSize << " , max = " << maximumCacheSize() ;
hgs
parents:
diff changeset
   623
#endif
hgs
parents:
diff changeset
   624
    return d->expire();
hgs
parents:
diff changeset
   625
}
hgs
parents:
diff changeset
   626
hgs
parents:
diff changeset
   627
/*!
hgs
parents:
diff changeset
   628
    \reimp
hgs
parents:
diff changeset
   629
*/
hgs
parents:
diff changeset
   630
void FeatherWeightCache::clear()
hgs
parents:
diff changeset
   631
{
hgs
parents:
diff changeset
   632
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   633
    qDebug() << "FeatherWeightCache::clear()";
hgs
parents:
diff changeset
   634
#endif
hgs
parents:
diff changeset
   635
hgs
parents:
diff changeset
   636
    qint64 size = d->maximumCacheSize;
hgs
parents:
diff changeset
   637
    d->maximumCacheSize = 0;
hgs
parents:
diff changeset
   638
    d->currentCacheSize = expire();
hgs
parents:
diff changeset
   639
    d->maximumCacheSize = size;
hgs
parents:
diff changeset
   640
}
hgs
parents:
diff changeset
   641
hgs
parents:
diff changeset
   642
qint64 FeatherWeightCachePrivate::expire()
hgs
parents:
diff changeset
   643
{
hgs
parents:
diff changeset
   644
hgs
parents:
diff changeset
   645
    // ASYNC expiration via background thread
hgs
parents:
diff changeset
   646
    beastOfBurden.expireLazily(cacheDirectory, maximumCacheSize);
hgs
parents:
diff changeset
   647
hgs
parents:
diff changeset
   648
    // Note: this cache size will not/cannot reflect the reduced
hgs
parents:
diff changeset
   649
    // cache size due to the async nature of the expire() above.
hgs
parents:
diff changeset
   650
    return currentCacheSize;
hgs
parents:
diff changeset
   651
}
hgs
parents:
diff changeset
   652
hgs
parents:
diff changeset
   653
QByteArray FeatherWeightCachePrivate::generateId(const QUrl &url)
hgs
parents:
diff changeset
   654
{
hgs
parents:
diff changeset
   655
    QUrl cleanUrl = url;
hgs
parents:
diff changeset
   656
    cleanUrl.setPassword(QString());
hgs
parents:
diff changeset
   657
    cleanUrl.setFragment(QString());
hgs
parents:
diff changeset
   658
    QByteArray blob = cleanUrl.toEncoded();
hgs
parents:
diff changeset
   659
hgs
parents:
diff changeset
   660
    QByteArray hash;
hgs
parents:
diff changeset
   661
    hash.setNum(crc32(blob.data(), blob.length()), 16);
hgs
parents:
diff changeset
   662
    return hash;
hgs
parents:
diff changeset
   663
}
hgs
parents:
diff changeset
   664
hgs
parents:
diff changeset
   665
QString FeatherWeightCachePrivate::tmpCacheFileName() const
hgs
parents:
diff changeset
   666
{
hgs
parents:
diff changeset
   667
    //The subdirectory is presumed to be already read for use.
hgs
parents:
diff changeset
   668
    return cacheDirectory + PREPARED_SLASH + QLatin1String("XXXXXX") + CACHE_POSTFIX;
hgs
parents:
diff changeset
   669
}
hgs
parents:
diff changeset
   670
hgs
parents:
diff changeset
   671
/*!
hgs
parents:
diff changeset
   672
    Genrates fully qualified path of cached resource from a URL.
hgs
parents:
diff changeset
   673
 */
hgs
parents:
diff changeset
   674
QString FeatherWeightCachePrivate::cacheFileName(const QUrl &url) const
hgs
parents:
diff changeset
   675
{
hgs
parents:
diff changeset
   676
    if (!url.isValid())
hgs
parents:
diff changeset
   677
        return QString();
hgs
parents:
diff changeset
   678
hgs
parents:
diff changeset
   679
    // map URL to a unique enough signature
hgs
parents:
diff changeset
   680
    const QByteArray unique(generateId(url));
hgs
parents:
diff changeset
   681
hgs
parents:
diff changeset
   682
    // generates <cache dir>/data/e/cache_beefcafe.cache
hgs
parents:
diff changeset
   683
    // where 'e' is the last character of a hex string
hgs
parents:
diff changeset
   684
    QString fullpath = cacheDirectory + DATA_SLASH
hgs
parents:
diff changeset
   685
                       + QLatin1Char(unique.at(unique.length()-1)) + QLatin1String("/")
hgs
parents:
diff changeset
   686
                       + QLatin1String(unique) + CACHE_POSTFIX;
hgs
parents:
diff changeset
   687
hgs
parents:
diff changeset
   688
    return  fullpath;
hgs
parents:
diff changeset
   689
}
hgs
parents:
diff changeset
   690
hgs
parents:
diff changeset
   691
hgs
parents:
diff changeset
   692
/* Important: This c'tor runs in the same thread as main cache */
hgs
parents:
diff changeset
   693
WorkerThread::WorkerThread()
hgs
parents:
diff changeset
   694
{
hgs
parents:
diff changeset
   695
    abort = false;
hgs
parents:
diff changeset
   696
}
hgs
parents:
diff changeset
   697
hgs
parents:
diff changeset
   698
/* Important: This d'tor runs in the same thread as main cache */
hgs
parents:
diff changeset
   699
WorkerThread::~WorkerThread()
hgs
parents:
diff changeset
   700
{
hgs
parents:
diff changeset
   701
    // The destructor can be called at any point while the thread is active.
hgs
parents:
diff changeset
   702
    // So we set abort to true to tell run() to stop running as soon as possible.
hgs
parents:
diff changeset
   703
    mutex.lock();
hgs
parents:
diff changeset
   704
    abort = true;
hgs
parents:
diff changeset
   705
    condition.wakeOne(); // wake up thread if it has nothing to do
hgs
parents:
diff changeset
   706
    mutex.unlock();
hgs
parents:
diff changeset
   707
hgs
parents:
diff changeset
   708
    wait(); // waits for run() to return
hgs
parents:
diff changeset
   709
}
hgs
parents:
diff changeset
   710
hgs
parents:
diff changeset
   711
/* Important: This method runs in its own thread, unlike the c'tor and d'tor */
hgs
parents:
diff changeset
   712
void WorkerThread::run()
hgs
parents:
diff changeset
   713
{
hgs
parents:
diff changeset
   714
hgs
parents:
diff changeset
   715
#if defined(Q_OS_SYMBIAN)
hgs
parents:
diff changeset
   716
    // Remove this once QTBUG-10271 is fixed
hgs
parents:
diff changeset
   717
    RThread myThread;
hgs
parents:
diff changeset
   718
    myThread.SetPriority(EPriorityLess);
hgs
parents:
diff changeset
   719
#endif
hgs
parents:
diff changeset
   720
hgs
parents:
diff changeset
   721
    qint64 size = expireImpl();
hgs
parents:
diff changeset
   722
    emit onDiskSizeChanged(size);
hgs
parents:
diff changeset
   723
hgs
parents:
diff changeset
   724
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   725
    qDebug() << "New on-disk cache size: " << size <<  QThread::currentThreadId();
hgs
parents:
diff changeset
   726
#endif
hgs
parents:
diff changeset
   727
hgs
parents:
diff changeset
   728
hgs
parents:
diff changeset
   729
}
hgs
parents:
diff changeset
   730
hgs
parents:
diff changeset
   731
qint64 WorkerThread::expireImpl()
hgs
parents:
diff changeset
   732
{
hgs
parents:
diff changeset
   733
    QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
hgs
parents:
diff changeset
   734
    QDirIterator it(this->cacheDir, filters, QDirIterator::Subdirectories);
hgs
parents:
diff changeset
   735
hgs
parents:
diff changeset
   736
    QMultiMap<QDateTime, QString> cacheItems;
hgs
parents:
diff changeset
   737
    qint64 totalSize = 0;
hgs
parents:
diff changeset
   738
    while (it.hasNext()) {
hgs
parents:
diff changeset
   739
        QString path = it.next();
hgs
parents:
diff changeset
   740
        QFileInfo info = it.fileInfo();
hgs
parents:
diff changeset
   741
        QString fileName = info.fileName();
hgs
parents:
diff changeset
   742
        if (fileName.endsWith(CACHE_POSTFIX)) {
hgs
parents:
diff changeset
   743
            cacheItems.insert(info.created(), path);
hgs
parents:
diff changeset
   744
            totalSize += info.size();
hgs
parents:
diff changeset
   745
        }
hgs
parents:
diff changeset
   746
hgs
parents:
diff changeset
   747
        // Interrupts this slow loop when d'tor is called
hgs
parents:
diff changeset
   748
        if (abort) {
hgs
parents:
diff changeset
   749
            // potentially incorrect, but can't do any better
hgs
parents:
diff changeset
   750
            return totalSize;
hgs
parents:
diff changeset
   751
        }
hgs
parents:
diff changeset
   752
    }
hgs
parents:
diff changeset
   753
hgs
parents:
diff changeset
   754
    int removedFiles = 0;
hgs
parents:
diff changeset
   755
    // this goal setting could be made smarter based on max cache size
hgs
parents:
diff changeset
   756
    // e.g on desktop with large 50MB caches, freeing 10% is probably enough
hgs
parents:
diff changeset
   757
    // but on mobile where caches are smaller (e.g 5MB) and disks are slow, you want
hgs
parents:
diff changeset
   758
    // to free atleast 0.5-1MB if going through all this trouble.
hgs
parents:
diff changeset
   759
    // Also TODO: Move to LRU algorithm
hgs
parents:
diff changeset
   760
    qint64 goal = (this->maxCacheSize * 8) / 10;
hgs
parents:
diff changeset
   761
    QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
hgs
parents:
diff changeset
   762
    while (i != cacheItems.constEnd()) {
hgs
parents:
diff changeset
   763
        if (totalSize < goal)
hgs
parents:
diff changeset
   764
            break;
hgs
parents:
diff changeset
   765
        QString name = i.value();
hgs
parents:
diff changeset
   766
        QFile file(name);
hgs
parents:
diff changeset
   767
        qint64 size = file.size();
hgs
parents:
diff changeset
   768
        file.remove();
hgs
parents:
diff changeset
   769
        totalSize -= size;
hgs
parents:
diff changeset
   770
        ++removedFiles;
hgs
parents:
diff changeset
   771
        ++i;
hgs
parents:
diff changeset
   772
hgs
parents:
diff changeset
   773
        // Interrupts this slow loop when d'tor is called
hgs
parents:
diff changeset
   774
        if (abort) {
hgs
parents:
diff changeset
   775
            // potentially incorrect, but can't do any better
hgs
parents:
diff changeset
   776
            return totalSize;
hgs
parents:
diff changeset
   777
        }
hgs
parents:
diff changeset
   778
hgs
parents:
diff changeset
   779
    }
hgs
parents:
diff changeset
   780
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   781
    if (removedFiles > 0) {
hgs
parents:
diff changeset
   782
        qDebug() << "FeatherWeightCache::expire()"
hgs
parents:
diff changeset
   783
                << "Removed:" << removedFiles
hgs
parents:
diff changeset
   784
                << "Kept:" << cacheItems.count() - removedFiles;
hgs
parents:
diff changeset
   785
    }
hgs
parents:
diff changeset
   786
#endif
hgs
parents:
diff changeset
   787
hgs
parents:
diff changeset
   788
    //TODO: Why do we do this in the original
hgs
parents:
diff changeset
   789
    //implementation? It isn't necessary that
hgs
parents:
diff changeset
   790
    //running expiration logics caused last
hgs
parents:
diff changeset
   791
    //insertion to become invalid?
hgs
parents:
diff changeset
   792
    //if (removedFiles > 0)
hgs
parents:
diff changeset
   793
    //    lastItem.reset();
hgs
parents:
diff changeset
   794
hgs
parents:
diff changeset
   795
    return totalSize;
hgs
parents:
diff changeset
   796
hgs
parents:
diff changeset
   797
}
hgs
parents:
diff changeset
   798
hgs
parents:
diff changeset
   799
/* Important: this function runs in the same thread as main cache */
hgs
parents:
diff changeset
   800
void WorkerThread::expireLazily(QString cacheDir, qint64 maxCacheSize)
hgs
parents:
diff changeset
   801
{
hgs
parents:
diff changeset
   802
hgs
parents:
diff changeset
   803
    //lock mutex. unlock automatically when locker goes out of scope
hgs
parents:
diff changeset
   804
    QMutexLocker locker(&mutex);
hgs
parents:
diff changeset
   805
hgs
parents:
diff changeset
   806
    //make private copy so that other member functions can use this
hgs
parents:
diff changeset
   807
    this->cacheDir = cacheDir;
hgs
parents:
diff changeset
   808
    this->maxCacheSize = maxCacheSize;
hgs
parents:
diff changeset
   809
hgs
parents:
diff changeset
   810
    if (!isRunning()) {
hgs
parents:
diff changeset
   811
hgs
parents:
diff changeset
   812
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   813
        qDebug() << "Starting worker thread a low priority";
hgs
parents:
diff changeset
   814
#endif
hgs
parents:
diff changeset
   815
hgs
parents:
diff changeset
   816
        start(LowPriority);
hgs
parents:
diff changeset
   817
hgs
parents:
diff changeset
   818
    } else {
hgs
parents:
diff changeset
   819
hgs
parents:
diff changeset
   820
#if defined(FEATHERWEIGHTCACHE_DEBUG)
hgs
parents:
diff changeset
   821
        qDebug() << "Waking up sleeping worker thread";
hgs
parents:
diff changeset
   822
#endif
hgs
parents:
diff changeset
   823
hgs
parents:
diff changeset
   824
        condition.wakeOne();
hgs
parents:
diff changeset
   825
hgs
parents:
diff changeset
   826
    }
hgs
parents:
diff changeset
   827
hgs
parents:
diff changeset
   828
}
hgs
parents:
diff changeset
   829
hgs
parents:
diff changeset
   830
hgs
parents:
diff changeset
   831
/*!
hgs
parents:
diff changeset
   832
    We compress small text and JavaScript files.
hgs
parents:
diff changeset
   833
 */
hgs
parents:
diff changeset
   834
bool CacheItem::canCompress() const
hgs
parents:
diff changeset
   835
{
hgs
parents:
diff changeset
   836
#if 1
hgs
parents:
diff changeset
   837
    bool sizeOk = false;
hgs
parents:
diff changeset
   838
    bool typeOk = false;
hgs
parents:
diff changeset
   839
    foreach (QNetworkCacheMetaData::RawHeader header, metaData.rawHeaders()) {
hgs
parents:
diff changeset
   840
        if (header.first.toLower() == "content-length") {
hgs
parents:
diff changeset
   841
            qint64 size = header.second.toLongLong();
hgs
parents:
diff changeset
   842
            if (size > MAX_COMPRESSION_SIZE)
hgs
parents:
diff changeset
   843
                return false;
hgs
parents:
diff changeset
   844
            else
hgs
parents:
diff changeset
   845
                sizeOk = true;
hgs
parents:
diff changeset
   846
        }
hgs
parents:
diff changeset
   847
hgs
parents:
diff changeset
   848
        if (header.first.toLower() == "content-type") {
hgs
parents:
diff changeset
   849
            QByteArray type = header.second;
hgs
parents:
diff changeset
   850
            if (type.startsWith("text/")
hgs
parents:
diff changeset
   851
                    || (type.startsWith("application/")
hgs
parents:
diff changeset
   852
                        && (type.endsWith("javascript") || type.endsWith("ecmascript"))))
hgs
parents:
diff changeset
   853
                typeOk = true;
hgs
parents:
diff changeset
   854
            else
hgs
parents:
diff changeset
   855
                return false;
hgs
parents:
diff changeset
   856
        }
hgs
parents:
diff changeset
   857
        if (sizeOk && typeOk)
hgs
parents:
diff changeset
   858
            return true;
hgs
parents:
diff changeset
   859
    }
hgs
parents:
diff changeset
   860
    return false;
hgs
parents:
diff changeset
   861
#else
hgs
parents:
diff changeset
   862
    return false;
hgs
parents:
diff changeset
   863
#endif
hgs
parents:
diff changeset
   864
hgs
parents:
diff changeset
   865
}
hgs
parents:
diff changeset
   866
hgs
parents:
diff changeset
   867
enum
hgs
parents:
diff changeset
   868
{
hgs
parents:
diff changeset
   869
    CacheMagic = 0xe8,
hgs
parents:
diff changeset
   870
    CurrentCacheVersion = 7
hgs
parents:
diff changeset
   871
};
hgs
parents:
diff changeset
   872
hgs
parents:
diff changeset
   873
void CacheItem::writeHeader(QFile *device) const
hgs
parents:
diff changeset
   874
{
hgs
parents:
diff changeset
   875
    QDataStream out(device);
hgs
parents:
diff changeset
   876
hgs
parents:
diff changeset
   877
    out << qint32(CacheMagic);
hgs
parents:
diff changeset
   878
    out << qint32(CurrentCacheVersion);
hgs
parents:
diff changeset
   879
    out << metaData;
hgs
parents:
diff changeset
   880
    bool compressed = canCompress();
hgs
parents:
diff changeset
   881
    out << compressed;
hgs
parents:
diff changeset
   882
}
hgs
parents:
diff changeset
   883
hgs
parents:
diff changeset
   884
void CacheItem::writeCompressedData(QFile *device) const
hgs
parents:
diff changeset
   885
{
hgs
parents:
diff changeset
   886
    QDataStream out(device);
hgs
parents:
diff changeset
   887
hgs
parents:
diff changeset
   888
    out << qCompress(data.data());
hgs
parents:
diff changeset
   889
}
hgs
parents:
diff changeset
   890
hgs
parents:
diff changeset
   891
/*!
hgs
parents:
diff changeset
   892
    Returns false if the file is a cache file,
hgs
parents:
diff changeset
   893
    but is an older version and should be removed otherwise true.
hgs
parents:
diff changeset
   894
 */
hgs
parents:
diff changeset
   895
bool CacheItem::read(QFile *device, bool readData)
hgs
parents:
diff changeset
   896
{
hgs
parents:
diff changeset
   897
    reset();
hgs
parents:
diff changeset
   898
hgs
parents:
diff changeset
   899
    QDataStream in(device);
hgs
parents:
diff changeset
   900
hgs
parents:
diff changeset
   901
    qint32 marker;
hgs
parents:
diff changeset
   902
    qint32 v;
hgs
parents:
diff changeset
   903
    in >> marker;
hgs
parents:
diff changeset
   904
    in >> v;
hgs
parents:
diff changeset
   905
    if (marker != CacheMagic)
hgs
parents:
diff changeset
   906
        return true;
hgs
parents:
diff changeset
   907
hgs
parents:
diff changeset
   908
    // If the cache magic is correct, but the version is not we should remove it
hgs
parents:
diff changeset
   909
    if (v != CurrentCacheVersion)
hgs
parents:
diff changeset
   910
        return false;
hgs
parents:
diff changeset
   911
hgs
parents:
diff changeset
   912
    bool compressed;
hgs
parents:
diff changeset
   913
    QByteArray dataBA;
hgs
parents:
diff changeset
   914
    in >> metaData;
hgs
parents:
diff changeset
   915
    in >> compressed;
hgs
parents:
diff changeset
   916
    if (readData && compressed) {
hgs
parents:
diff changeset
   917
        in >> dataBA;
hgs
parents:
diff changeset
   918
        data.setData(qUncompress(dataBA));
hgs
parents:
diff changeset
   919
        data.open(QBuffer::ReadOnly);
hgs
parents:
diff changeset
   920
    }
hgs
parents:
diff changeset
   921
    return metaData.isValid();
hgs
parents:
diff changeset
   922
}
hgs
parents:
diff changeset
   923
hgs
parents:
diff changeset
   924
} // namespace WRT