1 /*
2  * filecache.h - File storage with age and size control
3  * Copyright (C) 2010 Rion
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  */
20 
21 #ifndef FILECACHE_H
22 #define FILECACHE_H
23 
24 #include <QObject>
25 #include <QDateTime>
26 #include <QFile>
27 #include <QHash>
28 #include <QVariantMap>
29 
30 class QTimer;
31 class OptionsTree;
32 class FileCache;
33 
34 class FileCacheItem : public QObject
35 {
36 	Q_OBJECT
37 public:
38 	enum Flags {
39 		OnDisk       = 0x1,
40 		Registered   = 0x2,
41 		SessionUndeletable  = 0x4 // The item is undeletable by expiration or cache size limits during this session
42 		//Unloadable  = 0x8 // another good idea
43 	};
44 
45 	FileCacheItem(FileCache *parent, const QString &itemId, const QVariantMap &metadata,
46 				  const QDateTime &dt, unsigned int maxAge, unsigned int size,
47 				  const QByteArray &data = QByteArray());
48 
49 	void flushToDisk(); // put data to disk, but not to registry. don't call this directly. FileCache will care about it.
50 	bool remove() const; // remove file from disk but not from registry. don't call this directly.
51 	void unload(); // drop file to disk, deallocate memory
52 	inline bool inMemory() const;
isOnDisk()53 	inline bool isOnDisk() const { return _flags & OnDisk; } // data is on disk and not in rgistry
isRegistered()54 	inline bool isRegistered() const { return _flags & Registered; } // data is on disk and not in rgistry
55 	bool isExpired(bool finishSession = false) const;
parentCache()56 	inline FileCache *parentCache() const { return (FileCache *)parent(); }
id()57 	inline QString id() const { return _id; }
metadata()58 	inline QVariantMap metadata() const { return _metadata; }
setMetadata(const QVariantMap & md)59 	inline void setMetadata(const QVariantMap &md) { _metadata = md; _flags &= ~Registered; } // we have to update registry eventually
created()60 	inline QDateTime created() const { return _ctime; }
reborn()61 	inline void reborn() { _ctime = QDateTime::currentDateTime(); }
maxAge()62 	inline unsigned int maxAge() const { return _maxAge; }
size()63 	inline unsigned int size() const { return _size; }
64 	QByteArray data();
fileName()65 	inline QString fileName() const { return _fileName; }
hash()66 	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
67 
68 	inline void setSessionUndeletable(bool state = true) { if (state) _flags |= SessionUndeletable; else _flags &= ~SessionUndeletable; }
69 	void setUndeletable(bool state = true); // survives restarts
70 	inline bool isDeletable() const;
71 
72 private:
73 	friend class FileCache;
74 
75 	QString _id;
76 	QVariantMap _metadata;
77 	QDateTime _ctime;
78 	unsigned int _maxAge;
79 	unsigned int _size;
80 	QByteArray _data;
81 
82 	quint16 _flags;
83 	QString _fileName;
84 	QString _hash;
85 };
86 
87 class FileCache : public QObject
88 {
89 	Q_OBJECT
90 public:
91 	static const unsigned int Session = 0; //remove data when application exits
92 	static const unsigned int Forever = -1; //never remove
93 
94 	static const unsigned int DefaultMemoryCacheSize = 1*1024*1024; //1 Mb
95 	static const unsigned int DefaultFileCacheSize = 50*1024*1024; //50 Mb
96 
97 	enum SyncPolicy {
98 		InstantFLush, // always flush all data to disk (keeps copy in memory if fit)
99 		FlushOverflow // flush to disk only when memory cache limit is exceeded
100 	};
101 
102 	FileCache(const QString &cacheDir, QObject *parent = 0);
103 	~FileCache();
104 
105 	void gc();
106 
cacheDir()107 	inline QString cacheDir() const { return _cacheDir; }
108 
setMemoryCacheSize(unsigned int size)109 	inline void setMemoryCacheSize(unsigned int size)
110 				{ _memoryCacheSize = size; }
memoryCacheSize()111 	inline unsigned int memoryCacheSize() const { return _memoryCacheSize; }
112 
setFileCacheSize(unsigned int size)113 	inline void setFileCacheSize(unsigned int size) { _fileCacheSize = size; }
fileCacheSize()114 	inline unsigned int fileCacheSize() const { return _fileCacheSize; }
115 
setDefaultMaxAge(unsigned int maxAge)116 	inline void setDefaultMaxAge(unsigned int maxAge)
117 				{ _defaultMaxAge = maxAge; }
defaultMaxAge()118 	inline unsigned int defaultMaxAge() const { return _defaultMaxAge; }
119 
setSyncPolicy(SyncPolicy sp)120 	inline void setSyncPolicy(SyncPolicy sp) { _syncPolicy = sp; }
syncPolicy()121 	inline SyncPolicy syncPolicy() const { return _syncPolicy; }
122 
123 	/**
124 	 * @brief Add data to cache
125 	 * @param id unique id (e.g. sha1 of data)
126 	 * @param type e.g. content-type
127 	 * @param data / if no data(size=0) memory size restiction won't affect this item, also no files will be created
128 	 * @param maxAge Session/Forever or just seconds to live
129 	 * @return a new cache item. Not yet synchronized to disk
130 	 */
131 	FileCacheItem *append(const QString &id, const QByteArray &data,
132 						  const QVariantMap &metadata = QVariantMap(), unsigned int maxAge = Forever);
133 	void remove(const QString &id, bool needSync = true);
134 
135 	/**
136 	 * @brief get cache item metadata from cache (does not involve actual data loading)
137 	 * @param id uniqie id
138 	 * @param reborn - if more than half of the item age passed then set create-date to current
139 	 * @return
140 	 */
141 	FileCacheItem *get(const QString &id, bool reborn = false);
142 	QByteArray getData(const QString &id, bool reborn = false);
143 	void sync(bool finishSession);
144 
145 protected:
146 	/**
147 	 * @brief removeItem item from disk, shedules registry update as well if required.
148 	 *   When called explicitly, removes even "Undeletable" items.
149 	 * @param item - item to delete
150 	 * @param needSync - shedule regitry update.
151 	 */
152 	virtual void removeItem(FileCacheItem *item, bool needSync);
153 
154 public slots:
155 	void sync();
156 
157 private:
158 	void toRegistry(FileCacheItem *);
159 
160 protected:
161 	QHash<QString, FileCacheItem*> _items;
162 
163 private:
164 	QString _cacheDir;
165 	unsigned int _memoryCacheSize;
166 	unsigned int _fileCacheSize;
167 	unsigned int _defaultMaxAge;
168 	SyncPolicy _syncPolicy;
169 	QTimer *_syncTimer;
170 	OptionsTree *_registry;
171 	QHash<QString, FileCacheItem*> _pendingRegisterItems;
172 
173 	bool _registryChanged;
174 };
175 
176 #endif //FILECACHE_H
177