/* * filecache.h - File storage with age and size control * Copyright (C) 2010 Rion * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef FILECACHE_H #define FILECACHE_H #include #include #include #include #include class QTimer; class OptionsTree; class FileCache; class FileCacheItem : public QObject { Q_OBJECT public: enum Flags { OnDisk = 0x1, Registered = 0x2, SessionUndeletable = 0x4 // The item is undeletable by expiration or cache size limits during this session //Unloadable = 0x8 // another good idea }; FileCacheItem(FileCache *parent, const QString &itemId, const QVariantMap &metadata, const QDateTime &dt, unsigned int maxAge, unsigned int size, const QByteArray &data = QByteArray()); void flushToDisk(); // put data to disk, but not to registry. don't call this directly. FileCache will care about it. bool remove() const; // remove file from disk but not from registry. don't call this directly. void unload(); // drop file to disk, deallocate memory inline bool inMemory() const; inline bool isOnDisk() const { return _flags & OnDisk; } // data is on disk and not in rgistry inline bool isRegistered() const { return _flags & Registered; } // data is on disk and not in rgistry bool isExpired(bool finishSession = false) const; inline FileCache *parentCache() const { return (FileCache *)parent(); } inline QString id() const { return _id; } inline QVariantMap metadata() const { return _metadata; } inline void setMetadata(const QVariantMap &md) { _metadata = md; _flags &= ~Registered; } // we have to update registry eventually inline QDateTime created() const { return _ctime; } inline void reborn() { _ctime = QDateTime::currentDateTime(); } inline unsigned int maxAge() const { return _maxAge; } inline unsigned int size() const { return _size; } QByteArray data(); inline QString fileName() const { return _fileName; } inline QString hash() const { return _hash; } // it's a hash from 'id'. so you can have string for id. the hash will be used for filename inline void setSessionUndeletable(bool state = true) { if (state) _flags |= SessionUndeletable; else _flags &= ~SessionUndeletable; } void setUndeletable(bool state = true); // survives restarts inline bool isDeletable() const; private: friend class FileCache; QString _id; QVariantMap _metadata; QDateTime _ctime; unsigned int _maxAge; unsigned int _size; QByteArray _data; quint16 _flags; QString _fileName; QString _hash; }; class FileCache : public QObject { Q_OBJECT public: static const unsigned int Session = 0; //remove data when application exits static const unsigned int Forever = -1; //never remove static const unsigned int DefaultMemoryCacheSize = 1*1024*1024; //1 Mb static const unsigned int DefaultFileCacheSize = 50*1024*1024; //50 Mb enum SyncPolicy { InstantFLush, // always flush all data to disk (keeps copy in memory if fit) FlushOverflow // flush to disk only when memory cache limit is exceeded }; FileCache(const QString &cacheDir, QObject *parent = 0); ~FileCache(); void gc(); inline QString cacheDir() const { return _cacheDir; } inline void setMemoryCacheSize(unsigned int size) { _memoryCacheSize = size; } inline unsigned int memoryCacheSize() const { return _memoryCacheSize; } inline void setFileCacheSize(unsigned int size) { _fileCacheSize = size; } inline unsigned int fileCacheSize() const { return _fileCacheSize; } inline void setDefaultMaxAge(unsigned int maxAge) { _defaultMaxAge = maxAge; } inline unsigned int defaultMaxAge() const { return _defaultMaxAge; } inline void setSyncPolicy(SyncPolicy sp) { _syncPolicy = sp; } inline SyncPolicy syncPolicy() const { return _syncPolicy; } /** * @brief Add data to cache * @param id unique id (e.g. sha1 of data) * @param type e.g. content-type * @param data / if no data(size=0) memory size restiction won't affect this item, also no files will be created * @param maxAge Session/Forever or just seconds to live * @return a new cache item. Not yet synchronized to disk */ FileCacheItem *append(const QString &id, const QByteArray &data, const QVariantMap &metadata = QVariantMap(), unsigned int maxAge = Forever); void remove(const QString &id, bool needSync = true); /** * @brief get cache item metadata from cache (does not involve actual data loading) * @param id uniqie id * @param reborn - if more than half of the item age passed then set create-date to current * @return */ FileCacheItem *get(const QString &id, bool reborn = false); QByteArray getData(const QString &id, bool reborn = false); void sync(bool finishSession); protected: /** * @brief removeItem item from disk, shedules registry update as well if required. * When called explicitly, removes even "Undeletable" items. * @param item - item to delete * @param needSync - shedule regitry update. */ virtual void removeItem(FileCacheItem *item, bool needSync); public slots: void sync(); private: void toRegistry(FileCacheItem *); protected: QHash _items; private: QString _cacheDir; unsigned int _memoryCacheSize; unsigned int _fileCacheSize; unsigned int _defaultMaxAge; SyncPolicy _syncPolicy; QTimer *_syncTimer; OptionsTree *_registry; QHash _pendingRegisterItems; bool _registryChanged; }; #endif //FILECACHE_H