r189: Effective permissions are shown in brackets.
[rox-filer.git] / ROX-Filer / src / fscache.c
blob6d920ebd63fd15e35a86ef5096ce14f0d3bc2d06
1 /*
2 * $Id$
4 * FSCache - a glib-style object for caching files
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * A cache object holds stat details about files in a hash table, along
8 * with user-specified data. When you want to read in a file try to
9 * get the data via the cache - if the file is cached AND has not been
10 * modified since it was last loaded the cached copy is returned, else the
11 * file is reloaded.
13 * The actual data need not be the raw file contents - a user specified
14 * function loads the file and associates data with the file in the cache.
16 * This program is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the Free
18 * Software Foundation; either version 2 of the License, or (at your option)
19 * any later version.
21 * This program is distributed in the hope that it will be useful, but WITHOUT
22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
24 * more details.
26 * You should have received a copy of the GNU General Public License along with
27 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
28 * Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "fscache.h"
33 #define UPTODATE(data, info) \
34 (data->m_time == info.st_mtime \
35 && data->length == info.st_size \
36 && data->mode == info.st_mode) \
39 /* Static prototypes */
41 static guint hash_key(gconstpointer key);
42 static gint cmp_stats(gconstpointer a, gconstpointer b);
43 static void destroy_hash_entry(gpointer key, gpointer data, gpointer user_data);
44 static gboolean purge_hash_entry(gpointer key, gpointer data,
45 gpointer user_data);
48 struct PurgeInfo
50 GFSCache *cache;
51 gint age;
52 time_t now;
55 /****************************************************************
56 * EXTERNAL INTERFACE *
57 ****************************************************************/
60 /* Create a new GFSCache object and return a pointer to it.
62 * When someone tries to lookup a file which is not in the cache,
63 * load() is called.
64 * It should load the file and return a pointer to an object for the file.
65 * The object should have a ref count of 1.
67 * ref() and unref() should modify the reference counter of the object.
68 * When the counter reaches zero, destroy the object.
70 * getref() returns the current value. This can be used to stop objects
71 * being purged from the cache while they are being used elsewhere, which
72 * is rather wasteful. If NULL then this check isn't done.
74 * update() will be called to update an object which is cached, but
75 * out of date. If NULL, the object will be unref'd and load() used
76 * to make a new one.
78 * 'user_data' will be passed to all of the above functions.
80 GFSCache *g_fscache_new(GFSLoadFunc load,
81 GFSRefFunc ref,
82 GFSRefFunc unref,
83 GFSGetRefFunc getref,
84 GFSUpdateFunc update,
85 gpointer user_data)
87 GFSCache *cache;
89 cache = g_new(GFSCache, 1);
90 cache->inode_to_stats = g_hash_table_new(hash_key, cmp_stats);
91 cache->load = load;
92 cache->ref = ref;
93 cache->unref = unref;
94 cache->getref = getref;
95 cache->update = update;
96 cache->user_data = user_data;
98 return cache;
101 void g_fscache_destroy(GFSCache *cache)
103 g_return_if_fail(cache != NULL);
105 g_hash_table_foreach(cache->inode_to_stats, destroy_hash_entry, cache);
106 g_hash_table_destroy(cache->inode_to_stats);
108 g_free(cache);
111 /* Call the ref() user function for this object */
112 void g_fscache_data_ref(GFSCache *cache, gpointer data)
114 g_return_if_fail(cache != NULL);
116 if (cache->ref)
117 cache->ref(data, cache->user_data);
120 /* Call the unref() user function for this object */
121 void g_fscache_data_unref(GFSCache *cache, gpointer data)
123 g_return_if_fail(cache != NULL);
125 if (cache->unref)
126 cache->unref(data, cache->user_data);
129 /* Find the data for this file in the cache, loading it into
130 * the cache if it isn't there already.
132 * Remember to g_fscache_data_unref() the returned value when
133 * you're done with it.
135 * Returns NULL on failure.
137 gpointer g_fscache_lookup(GFSCache *cache, char *pathname)
139 struct stat info;
140 GFSCacheKey key;
141 GFSCacheData *data;
143 g_return_val_if_fail(cache != NULL, NULL);
144 g_return_val_if_fail(pathname != NULL, NULL);
146 if (stat(pathname, &info))
147 return NULL;
149 key.device = info.st_dev;
150 key.inode = info.st_ino;
152 data = g_hash_table_lookup(cache->inode_to_stats, &key);
154 if (data)
156 /* We've cached this file already - is it up-to-date? */
158 if (UPTODATE(data, info))
159 goto out;
161 /* Out-of-date */
162 if (cache->update)
163 cache->update(data->data, pathname, cache->user_data);
164 else
166 if (cache->unref)
167 cache->unref(data->data, cache->user_data);
168 data->data = NULL;
171 else
173 GFSCacheKey *new_key;
175 new_key = g_memdup(&key, sizeof(key));
177 data = g_new(GFSCacheData, 1);
178 data->data = NULL;
180 g_hash_table_insert(cache->inode_to_stats, new_key, data);
183 data->m_time = info.st_mtime;
184 data->length = info.st_size;
185 data->mode = info.st_mode;
187 if (data->data == NULL)
189 /* Create the object for the file (ie, not an update) */
190 if (cache->load)
191 data->data = cache->load(pathname, cache->user_data);
193 out:
194 if (cache->ref)
195 cache->ref(data->data, cache->user_data);
196 data->last_lookup = time(NULL);
197 return data->data;
200 /* Call the update() function on this item if it's in the cache
201 * AND it's out-of-date.
203 void g_fscache_may_update(GFSCache *cache, char *pathname)
205 GFSCacheKey key;
206 GFSCacheData *data;
207 struct stat info;
209 g_return_if_fail(cache != NULL);
210 g_return_if_fail(pathname != NULL);
211 g_return_if_fail(cache->update != NULL);
213 if (stat(pathname, &info))
214 return;
216 key.device = info.st_dev;
217 key.inode = info.st_ino;
219 data = g_hash_table_lookup(cache->inode_to_stats, &key);
221 if (data && !UPTODATE(data, info))
223 cache->update(data->data, pathname, cache->user_data);
224 data->m_time = info.st_mtime;
225 data->length = info.st_size;
226 data->mode = info.st_mode;
230 /* Call the update() function on this item iff it's in the cache. */
231 void g_fscache_update(GFSCache *cache, char *pathname)
233 GFSCacheKey key;
234 GFSCacheData *data;
235 struct stat info;
237 g_return_if_fail(cache != NULL);
238 g_return_if_fail(pathname != NULL);
239 g_return_if_fail(cache->update != NULL);
241 if (stat(pathname, &info))
242 return;
244 key.device = info.st_dev;
245 key.inode = info.st_ino;
247 data = g_hash_table_lookup(cache->inode_to_stats, &key);
249 if (data)
251 cache->update(data->data, pathname, cache->user_data);
252 data->m_time = info.st_mtime;
253 data->length = info.st_size;
254 data->mode = info.st_mode;
258 /* Remove all cache entries last accessed more than 'age' seconds
259 * ago.
261 void g_fscache_purge(GFSCache *cache, gint age)
263 struct PurgeInfo info;
265 g_return_if_fail(cache != NULL);
267 info.age = age;
268 info.cache = cache;
269 info.now = time(NULL);
271 g_hash_table_foreach_remove(cache->inode_to_stats, purge_hash_entry,
272 (gpointer) &info);
276 /****************************************************************
277 * INTERNAL FUNCTIONS *
278 ****************************************************************/
281 /* Generate a hash number for some stats */
282 static guint hash_key(gconstpointer key)
284 GFSCacheKey *stats = (GFSCacheKey *) key;
286 return stats->inode;
289 /* See if two stats blocks represent the same file */
290 static gint cmp_stats(gconstpointer a, gconstpointer b)
292 GFSCacheKey *c = (GFSCacheKey *) a;
293 GFSCacheKey *d = (GFSCacheKey *) b;
295 return c->device == d->device && c->inode == d->inode;
298 static void destroy_hash_entry(gpointer key, gpointer data, gpointer user_data)
300 GFSCache *cache = (GFSCache *) user_data;
301 GFSCacheData *cache_data = (GFSCacheData *) data;
303 if (cache->unref)
304 cache->unref(cache_data->data, cache->user_data);
306 g_free(key);
307 g_free(data);
310 static gboolean purge_hash_entry(gpointer key, gpointer data,
311 gpointer user_data)
313 struct PurgeInfo *info = (struct PurgeInfo *) user_data;
314 GFSCacheData *cache_data = (GFSCacheData *) data;
315 GFSCache *cache = info->cache;
317 /* It's wasteful to remove an entry if someone else is using it */
318 if (cache->getref &&
319 cache->getref(cache_data->data, cache->user_data) > 1)
320 return FALSE;
322 if (cache_data->last_lookup <= info->now
323 && cache_data->last_lookup >= info->now - info->age)
324 return FALSE;
326 if (cache->unref)
327 cache->unref(cache_data->data, cache->user_data);
329 g_free(key);
330 g_free(data);
332 return TRUE;