4 * FSCache - a glib-style object for caching files
5 * Copyright (C) 2001, the ROX-Filer team.
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
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)
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
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
35 #define UPTODATE(data, info) \
36 (data->m_time == info.st_mtime \
37 && data->length == info.st_size \
38 && data->mode == info.st_mode) \
41 /* Static prototypes */
43 static guint
hash_key(gconstpointer key
);
44 static gint
cmp_stats(gconstpointer a
, gconstpointer b
);
45 static void destroy_hash_entry(gpointer key
, gpointer data
, gpointer user_data
);
46 static gboolean
purge_hash_entry(gpointer key
, gpointer data
,
57 /****************************************************************
58 * EXTERNAL INTERFACE *
59 ****************************************************************/
62 /* Create a new GFSCache object and return a pointer to it.
64 * When someone tries to lookup a file which is not in the cache,
66 * It should load the file and return a pointer to an object for the file.
67 * The object should have a ref count of 1.
69 * ref() and unref() should modify the reference counter of the object.
70 * When the counter reaches zero, destroy the object.
72 * getref() returns the current value. This can be used to stop objects
73 * being purged from the cache while they are being used elsewhere, which
74 * is rather wasteful. If NULL then this check isn't done.
76 * update() will be called to update an object which is cached, but
77 * out of date. If NULL, the object will be unref'd and load() used
80 * 'user_data' will be passed to all of the above functions.
82 GFSCache
*g_fscache_new(GFSLoadFunc load
,
91 cache
= g_new(GFSCache
, 1);
92 cache
->inode_to_stats
= g_hash_table_new(hash_key
, cmp_stats
);
96 cache
->getref
= getref
;
97 cache
->update
= update
;
98 cache
->user_data
= user_data
;
103 void g_fscache_destroy(GFSCache
*cache
)
105 g_return_if_fail(cache
!= NULL
);
107 g_hash_table_foreach(cache
->inode_to_stats
, destroy_hash_entry
, cache
);
108 g_hash_table_destroy(cache
->inode_to_stats
);
113 /* Call the ref() user function for this object */
114 void g_fscache_data_ref(GFSCache
*cache
, gpointer data
)
116 g_return_if_fail(cache
!= NULL
);
119 cache
->ref(data
, cache
->user_data
);
122 /* Call the unref() user function for this object */
123 void g_fscache_data_unref(GFSCache
*cache
, gpointer data
)
125 g_return_if_fail(cache
!= NULL
);
128 cache
->unref(data
, cache
->user_data
);
131 /* Find the data for this file in the cache, loading it into
132 * the cache if it isn't there already.
134 * Remember to g_fscache_data_unref() the returned value when
135 * you're done with it.
137 * Returns NULL on failure.
139 gpointer
g_fscache_lookup(GFSCache
*cache
, char *pathname
)
141 return g_fscache_lookup_full(cache
, pathname
, FSCACHE_LOOKUP_CREATE
);
144 /* As g_fscache_lookup, but 'load' flags control what happens if the data
146 * TRUE => Data is updated / reloaded
147 * FALSE => Returns NULL
149 gpointer
g_fscache_lookup_full(GFSCache
*cache
, char *pathname
,
150 FSCacheLookup lookup_type
)
156 g_return_val_if_fail(cache
!= NULL
, NULL
);
157 g_return_val_if_fail(pathname
!= NULL
, NULL
);
159 if (mc_stat(pathname
, &info
))
162 key
.device
= info
.st_dev
;
163 key
.inode
= info
.st_ino
;
165 data
= g_hash_table_lookup(cache
->inode_to_stats
, &key
);
169 /* We've cached this file already */
171 if (lookup_type
== FSCACHE_LOOKUP_PEEK
)
172 goto out
; /* Never update on peeks */
174 /* Is it up-to-date? */
176 if (UPTODATE(data
, info
))
179 if (lookup_type
== FSCACHE_LOOKUP_ONLY_NEW
)
184 cache
->update(data
->data
, pathname
, cache
->user_data
);
188 cache
->unref(data
->data
, cache
->user_data
);
194 GFSCacheKey
*new_key
;
196 if (lookup_type
!= FSCACHE_LOOKUP_CREATE
)
199 new_key
= g_memdup(&key
, sizeof(key
));
201 data
= g_new(GFSCacheData
, 1);
204 g_hash_table_insert(cache
->inode_to_stats
, new_key
, data
);
207 data
->m_time
= info
.st_mtime
;
208 data
->length
= info
.st_size
;
209 data
->mode
= info
.st_mode
;
211 if (data
->data
== NULL
)
213 /* Create the object for the file (ie, not an update) */
215 data
->data
= cache
->load(pathname
, cache
->user_data
);
219 cache
->ref(data
->data
, cache
->user_data
);
220 data
->last_lookup
= time(NULL
);
224 /* Call the update() function on this item if it's in the cache
225 * AND it's out-of-date.
227 void g_fscache_may_update(GFSCache
*cache
, char *pathname
)
233 g_return_if_fail(cache
!= NULL
);
234 g_return_if_fail(pathname
!= NULL
);
235 g_return_if_fail(cache
->update
!= NULL
);
237 if (mc_stat(pathname
, &info
))
240 key
.device
= info
.st_dev
;
241 key
.inode
= info
.st_ino
;
243 data
= g_hash_table_lookup(cache
->inode_to_stats
, &key
);
245 if (data
&& !UPTODATE(data
, info
))
247 cache
->update(data
->data
, pathname
, cache
->user_data
);
248 data
->m_time
= info
.st_mtime
;
249 data
->length
= info
.st_size
;
250 data
->mode
= info
.st_mode
;
254 /* Call the update() function on this item iff it's in the cache. */
255 void g_fscache_update(GFSCache
*cache
, char *pathname
)
261 g_return_if_fail(cache
!= NULL
);
262 g_return_if_fail(pathname
!= NULL
);
263 g_return_if_fail(cache
->update
!= NULL
);
265 if (mc_stat(pathname
, &info
))
268 key
.device
= info
.st_dev
;
269 key
.inode
= info
.st_ino
;
271 data
= g_hash_table_lookup(cache
->inode_to_stats
, &key
);
275 cache
->update(data
->data
, pathname
, cache
->user_data
);
276 data
->m_time
= info
.st_mtime
;
277 data
->length
= info
.st_size
;
278 data
->mode
= info
.st_mode
;
282 /* Remove all cache entries last accessed more than 'age' seconds
285 void g_fscache_purge(GFSCache
*cache
, gint age
)
287 struct PurgeInfo info
;
289 g_return_if_fail(cache
!= NULL
);
293 info
.now
= time(NULL
);
295 g_hash_table_foreach_remove(cache
->inode_to_stats
, purge_hash_entry
,
300 /****************************************************************
301 * INTERNAL FUNCTIONS *
302 ****************************************************************/
305 /* Generate a hash number for some stats */
306 static guint
hash_key(gconstpointer key
)
308 GFSCacheKey
*stats
= (GFSCacheKey
*) key
;
313 /* See if two stats blocks represent the same file */
314 static gint
cmp_stats(gconstpointer a
, gconstpointer b
)
316 GFSCacheKey
*c
= (GFSCacheKey
*) a
;
317 GFSCacheKey
*d
= (GFSCacheKey
*) b
;
319 return c
->device
== d
->device
&& c
->inode
== d
->inode
;
322 static void destroy_hash_entry(gpointer key
, gpointer data
, gpointer user_data
)
324 GFSCache
*cache
= (GFSCache
*) user_data
;
325 GFSCacheData
*cache_data
= (GFSCacheData
*) data
;
328 cache
->unref(cache_data
->data
, cache
->user_data
);
334 static gboolean
purge_hash_entry(gpointer key
, gpointer data
,
337 struct PurgeInfo
*info
= (struct PurgeInfo
*) user_data
;
338 GFSCacheData
*cache_data
= (GFSCacheData
*) data
;
339 GFSCache
*cache
= info
->cache
;
341 /* It's wasteful to remove an entry if someone else is using it */
343 cache
->getref(cache_data
->data
, cache
->user_data
) > 1)
346 if (cache_data
->last_lookup
<= info
->now
347 && cache_data
->last_lookup
>= info
->now
- info
->age
)
351 cache
->unref(cache_data
->data
, cache
->user_data
);