Commit 2841c81b authored by Jiří Techet's avatar Jiří Techet

Introduce new memory cache and remove non-persistent file cache

The new memory cache can store specified number of tiles in
memory without storing them to the disk. It replaces
the temporary file cache - it was a bit scary to recursively
delete some directories and libchamplain shouldn't do things
like that.
Signed-off-by: 's avatarJiří Techet <techet@gmail.com>
parent 537d540c
......@@ -34,6 +34,7 @@ libchamplain_headers = \
champlain-map-source-chain.h \
champlain-tile-source.h \
champlain-tile-cache.h \
champlain-memory-cache.h \
champlain-network-tile-source.h \
champlain-file-cache.h \
champlain-map-source-factory.h \
......@@ -70,6 +71,7 @@ libchamplain_@CHAMPLAIN_API_VERSION@_la_SOURCES = \
champlain-map-source-chain.c \
champlain-tile-source.c \
champlain-tile-cache.c \
champlain-memory-cache.c \
champlain-network-tile-source.c \
champlain-file-cache.c \
champlain-map-source-factory.c \
......@@ -101,6 +103,7 @@ libchamplain_include_HEADERS = \
champlain-map-source-chain.h \
champlain-tile-source.h \
champlain-tile-cache.h \
champlain-memory-cache.h \
champlain-network-tile-source.h \
champlain-file-cache.h \
champlain-map-source-factory.h \
......
......@@ -22,8 +22,7 @@
* @short_description: Stores and loads cached tiles from the file system
*
* #ChamplainFileCache is a map source that stores and retrieves tiles from the
* file system. It can be temporary (deleted when the object is destroyed) or
* permanent. Tiles most frequently loaded gain in "popularity". This popularity
* file system. Tiles most frequently loaded gain in "popularity". This popularity
* is taken into account when purging the cache.
*/
......@@ -55,14 +54,12 @@ struct _ChamplainFileCachePrivate
guint size_limit;
gchar *cache_dir;
gchar *real_cache_dir;
sqlite3 *db;
sqlite3_stmt *stmt_select;
sqlite3_stmt *stmt_update;
};
static void finalize_sql (ChamplainFileCache *file_cache);
static void delete_temp_cache (ChamplainFileCache *file_cache);
static void init_cache (ChamplainFileCache *file_cache);
static gchar *get_filename (ChamplainFileCache *file_cache,
ChamplainTile *tile);
......@@ -70,7 +67,6 @@ static gboolean tile_is_expired (ChamplainFileCache *file_cache,
ChamplainTile *tile);
static void delete_tile (ChamplainFileCache *file_cache,
const gchar *filename);
static void delete_dir_recursive (GFile *parent);
static gboolean create_cache_dir (const gchar *dir_name);
static void fill_tile (ChamplainMapSource *map_source,
......@@ -84,7 +80,6 @@ static void refresh_tile_time (ChamplainTileCache *tile_cache,
ChamplainTile *tile);
static void on_tile_filled (ChamplainTileCache *tile_cache,
ChamplainTile *tile);
static void clean (ChamplainTileCache *tile_cache);
static void
champlain_file_cache_get_property (GObject *object,
......@@ -164,41 +159,16 @@ finalize_sql (ChamplainFileCache *file_cache)
}
}
static void
delete_temp_cache (ChamplainFileCache *file_cache)
{
ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
ChamplainFileCachePrivate *priv = file_cache->priv;
if (!champlain_tile_cache_get_persistent (tile_cache) && priv->real_cache_dir)
{
GFile *file = NULL;
/* delete the directory contents */
file = g_file_new_for_path (priv->real_cache_dir);
delete_dir_recursive (file);
/* delete the directory itself */
if (!g_file_delete (file, NULL, NULL))
g_warning ("Failed to remove temporary cache main directory");
g_object_unref (file);
}
}
static void
champlain_file_cache_finalize (GObject *object)
{
ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
ChamplainFileCachePrivate *priv = file_cache->priv;
finalize_sql (file_cache);
if (!champlain_tile_cache_get_persistent (tile_cache))
delete_temp_cache (file_cache);
g_free (priv->real_cache_dir);
g_free (priv->cache_dir);
G_OBJECT_CLASS (champlain_file_cache_parent_class)->finalize (object);
......@@ -220,64 +190,18 @@ create_cache_dir (const gchar *dir_name)
return TRUE;
}
#ifdef G_OS_WIN32
#include <io.h>
#include <glib/gstdio.h>
static char *mkdtemp(char *template)
{
gunichar2 *wtemplate;
gchar *tmpl;
if (!template)
return NULL;
wtemplate = g_utf8_to_utf16 (template, -1, NULL, NULL, NULL);
if (!_wmktemp (wtemplate)) {
g_free (wtemplate);
return NULL;
}
tmpl = g_utf16_to_utf8 (wtemplate, -1, NULL, NULL, NULL);
g_free (wtemplate);
if ((strlen (template) != strlen (tmpl)) || g_mkdir (tmpl, 0700)) {
g_free (tmpl);
return NULL;
}
strcpy (template,tmpl);
g_free (tmpl);
return template;
}
#endif
static void
init_cache (ChamplainFileCache *file_cache)
{
ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
ChamplainFileCachePrivate *priv = file_cache->priv;
gchar *filename = NULL;
gchar *error_msg = NULL;
gint error;
if (champlain_tile_cache_get_persistent (tile_cache))
{
priv->real_cache_dir = g_strdup (priv->cache_dir);
g_return_if_fail (create_cache_dir (priv->real_cache_dir));
}
else
{
/* Create temporary directory for non-persistent caches */
gchar *tmplate = NULL;
tmplate = g_build_filename (priv->cache_dir, "champlain-XXXXXX", NULL);
g_return_if_fail (create_cache_dir (priv->cache_dir));
priv->real_cache_dir = mkdtemp (tmplate);
if (!priv->real_cache_dir)
{
g_warning ("Failed to create filename for temporary cache: template '%s'", tmplate);
g_free (tmplate);
return;
}
}
filename = g_build_filename (priv->real_cache_dir,
filename = g_build_filename (priv->cache_dir,
"cache.db", NULL);
error = sqlite3_open_v2 (filename, &priv->db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
......@@ -343,13 +267,10 @@ static void
champlain_file_cache_constructed (GObject *object)
{
ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(object);
ChamplainTileCache *tile_cache = CHAMPLAIN_TILE_CACHE(file_cache);
ChamplainFileCachePrivate *priv = file_cache->priv;
if (!priv->cache_dir)
{
if (champlain_tile_cache_get_persistent (tile_cache))
{
#ifdef CHAMPLAIN_HAS_MAEMO
priv->cache_dir = g_strdup ("/home/user/MyDocs/.Maps/");
#else
......@@ -358,9 +279,6 @@ champlain_file_cache_constructed (GObject *object)
"champlain", NULL);
#endif
}
else
priv->cache_dir = g_strdup (g_get_tmp_dir ());
}
init_cache (file_cache);
......@@ -419,7 +337,6 @@ champlain_file_cache_class_init (ChamplainFileCacheClass *klass)
tile_cache_class->store_tile = store_tile;
tile_cache_class->refresh_tile_time = refresh_tile_time;
tile_cache_class->on_tile_filled = on_tile_filled;
tile_cache_class->clean = clean;
map_source_class->fill_tile = fill_tile;
}
......@@ -433,7 +350,7 @@ champlain_file_cache_init (ChamplainFileCache *file_cache)
priv->cache_dir = NULL;
priv->size_limit = 100000000;
priv->real_cache_dir = NULL;
priv->cache_dir = NULL;
priv->db = NULL;
priv->stmt_select = NULL;
priv->stmt_update = NULL;
......@@ -444,7 +361,7 @@ champlain_file_cache_init (ChamplainFileCache *file_cache)
*
* Default constructor of #ChamplainFileCache.
*
* Returns: a constructed permanent cache of maximal size 100000000 B inside
* Returns: a constructed cache of maximal size 100000000 B inside
* ~/.cache/champlain.
*
* Since: 0.6
......@@ -459,10 +376,7 @@ ChamplainFileCache* champlain_file_cache_new (void)
* @size_limit: maximal size of the cache in bytes
* @cache_dir: the directory where the cache is created. For temporary caches
* one more directory with random name is created inside this directory.
* When cache_dir == NULL, a cache in ~/.cache/champlain is used for permanent
* caches and /tmp for temporary caches.
* @persistent: if TRUE, the cache is persistent; otherwise the cache is
* temporary and will be deleted when the cache object is destructed.
* When cache_dir == NULL, a cache in ~/.cache/champlain is used.
*
* Constructor of #ChamplainFileCache.
*
......@@ -471,12 +385,11 @@ ChamplainFileCache* champlain_file_cache_new (void)
* Since: 0.6
*/
ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
const gchar *cache_dir,
gboolean persistent)
const gchar *cache_dir)
{
ChamplainFileCache * cache;
cache = g_object_new (CHAMPLAIN_TYPE_FILE_CACHE, "size-limit", size_limit,
"cache-dir", cache_dir, "persistent-cache", persistent, NULL);
"cache-dir", cache_dir, NULL);
return cache;
}
......@@ -545,7 +458,7 @@ get_filename (ChamplainFileCache *file_cache,
g_return_val_if_fail (CHAMPLAIN_IS_FILE_CACHE (file_cache), NULL);
g_return_val_if_fail (CHAMPLAIN_IS_TILE (tile), NULL);
g_return_val_if_fail (priv->real_cache_dir, NULL);
g_return_val_if_fail (priv->cache_dir, NULL);
ChamplainMapSource* map_source = CHAMPLAIN_MAP_SOURCE(file_cache);
......@@ -553,7 +466,7 @@ get_filename (ChamplainFileCache *file_cache,
"%s" G_DIR_SEPARATOR_S
"%d" G_DIR_SEPARATOR_S
"%d" G_DIR_SEPARATOR_S "%d.png",
priv->real_cache_dir,
priv->cache_dir,
champlain_map_source_get_id (map_source),
champlain_tile_get_zoom_level (tile),
champlain_tile_get_x (tile),
......@@ -784,9 +697,7 @@ refresh_tile_time (ChamplainTileCache *tile_cache,
g_object_unref (info);
if (CHAMPLAIN_IS_TILE_CACHE(next_source))
{
refresh_tile_time (CHAMPLAIN_TILE_CACHE(next_source), tile);
}
champlain_tile_cache_refresh_tile_time (CHAMPLAIN_TILE_CACHE(next_source), tile);
}
static void
......@@ -863,7 +774,7 @@ store_tile (ChamplainTileCache *tile_cache,
store_next:
if (CHAMPLAIN_IS_TILE_CACHE(next_source))
store_tile (CHAMPLAIN_TILE_CACHE(next_source), tile, contents, size);
champlain_tile_cache_store_tile (CHAMPLAIN_TILE_CACHE(next_source), tile, contents, size);
g_free (filename);
g_free (path);
......@@ -906,27 +817,9 @@ on_tile_filled (ChamplainTileCache *tile_cache,
call_next:
if (CHAMPLAIN_IS_TILE_CACHE(next_source))
on_tile_filled (CHAMPLAIN_TILE_CACHE(next_source), tile);
champlain_tile_cache_on_tile_filled (CHAMPLAIN_TILE_CACHE(next_source), tile);
}
static void
clean (ChamplainTileCache *tile_cache)
{
g_return_if_fail (CHAMPLAIN_IS_FILE_CACHE (tile_cache));
ChamplainFileCache *file_cache = CHAMPLAIN_FILE_CACHE(tile_cache);
ChamplainFileCachePrivate *priv = file_cache->priv;
g_return_if_fail (!champlain_tile_cache_get_persistent (tile_cache));
finalize_sql (file_cache);
delete_temp_cache (file_cache);
g_free (priv->real_cache_dir);
priv->real_cache_dir = NULL;
init_cache (file_cache);
}
static void
delete_tile (ChamplainFileCache *file_cache, const gchar *filename)
......@@ -1069,45 +962,4 @@ champlain_file_cache_purge (ChamplainFileCache *file_cache)
sqlite3_free (query);
}
static void
delete_dir_recursive (GFile *parent)
{
g_return_if_fail (parent);
GError *error = NULL;
GFileEnumerator *enumerator;
GFileInfo *info;
GFile *child;
enumerator = g_file_enumerate_children (parent, "*",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error);
if (!enumerator)
{
DEBUG ("Failed to create file enumerator in delete_dir_recursive: %s", error->message);
g_error_free (error);
return;
}
info = g_file_enumerator_next_file (enumerator, NULL, NULL);
while (info && !error)
{
child = g_file_get_child (parent, g_file_info_get_name (info));
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
delete_dir_recursive (child);
error = NULL;
if (!g_file_delete (child, NULL, &error))
{
DEBUG ("Deleting tile from disk failed: %s", error->message);
g_error_free (error);
}
g_object_unref (child);
g_object_unref (info);
info = g_file_enumerator_next_file (enumerator, NULL, NULL);
}
g_file_enumerator_close (enumerator, NULL, NULL);
}
......@@ -57,7 +57,7 @@ GType champlain_file_cache_get_type (void);
ChamplainFileCache* champlain_file_cache_new (void);
ChamplainFileCache* champlain_file_cache_new_full (guint size_limit,
const gchar *cache_dir, gboolean persistent);
const gchar *cache_dir);
guint champlain_file_cache_get_size_limit (ChamplainFileCache *file_cache);
void champlain_file_cache_set_size_limit (ChamplainFileCache *file_cache,
......
......@@ -149,7 +149,7 @@ tile_rendered_cb (ChamplainTile *tile,
champlain_map_source_fill_tile (next_source, tile);
g_object_unref (map_source);
g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, data);
g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
}
static void
......
/*
* Copyright (C) 2010 Jiri Techet <techet@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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 Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define DEBUG_FLAG CHAMPLAIN_DEBUG_CACHE
#include "champlain-debug.h"
#include "champlain-memory-cache.h"
#include <glib.h>
#include <string.h>
G_DEFINE_TYPE (ChamplainMemoryCache, champlain_memory_cache, CHAMPLAIN_TYPE_TILE_CACHE);
#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAMPLAIN_TYPE_MEMORY_CACHE, ChamplainMemoryCachePrivate))
enum
{
PROP_0,
PROP_SIZE_LIMIT
};
struct _ChamplainMemoryCachePrivate
{
guint size_limit;
GQueue *queue;
};
typedef struct
{
gchar *key;
gchar *data;
guint size;
} QueueMember;
static void fill_tile (ChamplainMapSource *map_source,
ChamplainTile *tile);
static void store_tile (ChamplainTileCache *tile_cache,
ChamplainTile *tile,
const gchar *contents,
gsize size);
static void refresh_tile_time (ChamplainTileCache *tile_cache,
ChamplainTile *tile);
static void on_tile_filled (ChamplainTileCache *tile_cache,
ChamplainTile *tile);
static void
champlain_memory_cache_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (object);
switch (property_id)
{
case PROP_SIZE_LIMIT:
g_value_set_uint (value, champlain_memory_cache_get_size_limit (memory_cache));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
champlain_memory_cache_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (object);
switch (property_id)
{
case PROP_SIZE_LIMIT:
champlain_memory_cache_set_size_limit (memory_cache, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
champlain_memory_cache_dispose (GObject *object)
{
G_OBJECT_CLASS (champlain_memory_cache_parent_class)->dispose (object);
}
static void
champlain_memory_cache_finalize (GObject *object)
{
ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (object);
g_queue_free (memory_cache->priv->queue);
G_OBJECT_CLASS (champlain_memory_cache_parent_class)->finalize (object);
}
static void
champlain_memory_cache_class_init (ChamplainMemoryCacheClass *klass)
{
ChamplainMapSourceClass *map_source_class = CHAMPLAIN_MAP_SOURCE_CLASS (klass);
ChamplainTileCacheClass *tile_cache_class = CHAMPLAIN_TILE_CACHE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (ChamplainMemoryCachePrivate));
object_class->finalize = champlain_memory_cache_finalize;
object_class->dispose = champlain_memory_cache_dispose;
object_class->get_property = champlain_memory_cache_get_property;
object_class->set_property = champlain_memory_cache_set_property;
/*
* ChamplainMemoryCache:size-limit:
*
* The cache size limit in bytes.
*
* Note: this new value will not be applied until you call #champlain_cache_purge
*
* Since: 0.4
*/
pspec = g_param_spec_uint ("size-limit",
"Size Limit",
"The cache's size limit (Mb)",
1,
G_MAXINT,
100,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_SIZE_LIMIT, pspec);
tile_cache_class->store_tile = store_tile;
tile_cache_class->refresh_tile_time = refresh_tile_time;
tile_cache_class->on_tile_filled = on_tile_filled;
map_source_class->fill_tile = fill_tile;
}
ChamplainMemoryCache *
champlain_memory_cache_new (void)
{
return CHAMPLAIN_MEMORY_CACHE (g_object_new (CHAMPLAIN_TYPE_MEMORY_CACHE, NULL));
}
ChamplainMemoryCache *
champlain_memory_cache_new_full (guint size_limit)
{
ChamplainMemoryCache *cache;
cache = g_object_new (CHAMPLAIN_TYPE_MEMORY_CACHE,
"size-limit", size_limit,
NULL);
return cache;
}
static void
reload_tiles_cb (ChamplainMemoryCache *memory_cache, G_GNUC_UNUSED gpointer data)
{
g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache));
champlain_memory_cache_clean (memory_cache);
}
static void
champlain_memory_cache_init (ChamplainMemoryCache *memory_cache)
{
ChamplainMemoryCachePrivate *priv = GET_PRIVATE (memory_cache);
memory_cache->priv = priv;
g_signal_connect (memory_cache, "reload-tiles",
G_CALLBACK (reload_tiles_cb), NULL);
priv->queue = g_queue_new ();
}
guint
champlain_memory_cache_get_size_limit (ChamplainMemoryCache *memory_cache)
{
g_return_val_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache), 0);
return memory_cache->priv->size_limit;
}
void
champlain_memory_cache_set_size_limit (ChamplainMemoryCache *memory_cache,
guint size_limit)
{
g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache));
ChamplainMemoryCachePrivate *priv = memory_cache->priv;
priv->size_limit = size_limit;
g_object_notify (G_OBJECT (memory_cache), "size-limit");
}
static gchar *
generate_queue_key (ChamplainMemoryCache *memory_cache,
ChamplainTile *tile)
{
g_return_val_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (memory_cache), NULL);
g_return_val_if_fail (CHAMPLAIN_IS_TILE (tile), NULL);
ChamplainMapSource *map_source = CHAMPLAIN_MAP_SOURCE (memory_cache);
gchar *filename = g_strdup_printf ("%d/%d/%d/%s",
champlain_tile_get_zoom_level (tile),
champlain_tile_get_x (tile),
champlain_tile_get_y (tile),
champlain_map_source_get_id (map_source));
return filename;
}
static gint
compare_queue_members (const QueueMember *a, const QueueMember *b)
{
return g_strcmp0 (a->key, b->key);
}
static void
move_queue_member_to_head (GQueue *queue, GList *link)
{
g_queue_unlink (queue, link);
g_queue_push_head_link (queue, link);
}
static void
delete_queue_member (QueueMember *member, gpointer user_data)
{
if (member)
{
g_free (member->key);
g_free (member->data);
g_free (member);
}
}
static void
tile_rendered_cb (ChamplainTile *tile,
ChamplainRenderCallbackData *data,
ChamplainMapSource *map_source)
{
ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
if (!data->error)
{
if (CHAMPLAIN_IS_TILE_CACHE (next_source))
on_tile_filled (CHAMPLAIN_TILE_CACHE (next_source), tile);
champlain_tile_set_fade_in (tile, TRUE);
champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
champlain_tile_display_content (tile);
}
else if (CHAMPLAIN_IS_MAP_SOURCE (next_source))
champlain_map_source_fill_tile (next_source, tile);
g_object_unref (map_source);
g_signal_handlers_disconnect_by_func (tile, tile_rendered_cb, map_source);
}
static void
fill_tile (ChamplainMapSource *map_source,
ChamplainTile *tile)
{
g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (map_source));
g_return_if_fail (CHAMPLAIN_IS_TILE (tile));
ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);
if (champlain_tile_get_state (tile) != CHAMPLAIN_STATE_LOADED)
{
ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (map_source);
ChamplainMemoryCachePrivate *priv = memory_cache->priv;
ChamplainRenderer *renderer;
GList *link;
QueueMember key;
key.key = generate_queue_key (memory_cache, tile);
link = g_queue_find_custom (priv->queue, &key, (GCompareFunc) compare_queue_members);
if (link)
{
QueueMember *member = link->data;
move_queue_member_to_head (priv->queue, link);
renderer = champlain_map_source_get_renderer (map_source);
g_return_if_fail (CHAMPLAIN_IS_RENDERER (renderer));
g_object_ref (map_source);
g_signal_connect (tile, "render-complete", G_CALLBACK (tile_rendered_cb), map_source);
champlain_renderer_set_data (renderer, member->data, member->size);
champlain_renderer_render (renderer, tile);
return;
}
}
if (CHAMPLAIN_IS_MAP_SOURCE (next_source))
champlain_map_source_fill_tile (next_source, tile);
else if (champlain_tile_get_state (tile) == CHAMPLAIN_STATE_LOADED)
{
/* if we have some content, use the tile even if it wasn't validated */
champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
champlain_tile_display_content (tile);
}
}
static void
store_tile (ChamplainTileCache *tile_cache,
ChamplainTile *tile,
const gchar *contents,
gsize size)
{
g_return_if_fail (CHAMPLAIN_IS_MEMORY_CACHE (tile_cache));
ChamplainMemoryCache *memory_cache = CHAMPLAIN_MEMORY_CACHE (tile_cache);
ChamplainMemoryCachePrivate *priv = memory_cache->priv;
GList *link;
QueueMember key;
key.key = generate_queue_key (memory_cache, tile);
link = g_queue_find_custom (priv->queue, &key, (GCompareFunc) compare_queue_members);
if (link)
move_queue_member_to_head (priv->queue, link);
else
{
QueueMember *member;
if (priv->queue->length >= priv->size_limit