+ return;
+ }
+ g_object_unref(loader);
+ g_slice_free1(_entry->data_sz, _entry->data);
+ _entry->data = NULL;
+ _entry->data_sz = 0;
+ }
+ _entry->pixbuf = NULL;
+ _entry->size = _entry->data_sz;
+}
+
+static void map_cache_entry_free_pixbuf(MapCacheEntry *_entry){
+ if(_entry->pixbuf!=NULL)
+ {
+ g_object_unref(_entry->pixbuf);
+ _entry->pixbuf = NULL;
+ }
+}
+
+static void map_cache_entry_free(MapCacheEntry *_entry){
+ if(_entry->list >= 0)
+ map_cache_list_remove(_map_cache.lists+_entry->list, _entry);
+ map_cache_entry_free_pixbuf(_entry);
+ g_slice_free1(_entry->data_sz, _entry->data);
+ g_slice_free(MapCacheEntry, _entry);
+}
+
+static gboolean
+map_cache_replace(size_t _size, gboolean _b2)
+{
+ gboolean ret;
+ size_t total_size;
+ total_size = _map_cache.lists[0].size+_map_cache.lists[1].data_sz
+ +_map_cache.lists[2].size+_map_cache.lists[3].data_sz;
+ ret = FALSE;
+ while(total_size+_size > _map_cache.cache_size)
+ {
+ MapCacheEntry *entry;
+ int list;
+ if(_map_cache.lists[0].tail != NULL &&
+ (_map_cache.lists[0].size > _map_cache.p ||
+ (_b2 && _map_cache.lists[0].size == _map_cache.p)))
+ list = 0;
+ else
+ list = 2;
+ entry = _map_cache.lists[list].tail;
+ if(entry == NULL)
+ break;
+ map_cache_list_remove(_map_cache.lists+list, entry);
+ map_cache_list_prepend(_map_cache.lists, list+1, entry);
+ total_size -= entry->size - entry->data_sz;
+ ret = TRUE;
+ _b2 = FALSE;
+ }
+ return ret;
+}
+
+static void
+map_cache_evict(size_t _size)
+{
+ size_t total_size;
+ size_t max_size;
+ total_size = _map_cache.lists[0].size+_map_cache.lists[1].size
+ +_map_cache.lists[2].size+_map_cache.lists[3].size;
+ max_size = _map_cache.cache_size<<1;
+ for(;;)
+ {
+ if(_map_cache.lists[0].size+_map_cache.lists[1].size+_size >
+ _map_cache.cache_size)
+ {
+ if(_map_cache.lists[1].tail != NULL)
+ {
+ g_hash_table_remove(_map_cache.entries,
+ &_map_cache.lists[1].tail->key);
+ map_cache_replace(_size, FALSE);
+ }
+ else if(_map_cache.lists[0].tail != NULL)
+ {
+ g_hash_table_remove(_map_cache.entries,
+ &_map_cache.lists[0].tail->key);
+ }
+ else break;
+ }
+ else if(total_size+_size > _map_cache.cache_size)
+ {
+ if(total_size+_size > max_size &&
+ _map_cache.lists[3].tail != NULL)
+ {
+ g_hash_table_remove(_map_cache.entries,
+ &_map_cache.lists[3].tail->key);
+ map_cache_replace(_size, FALSE);
+ }
+ else if(!map_cache_replace(_size, FALSE))
+ break;
+ }
+ else break;
+ total_size = _map_cache.lists[0].size+_map_cache.lists[1].size
+ +_map_cache.lists[2].size+_map_cache.lists[3].size;
+ }
+}
+
+static GdkPixbuf *
+map_cache_get(RepoData *repo, gint zoom, gint tilex, gint tiley)
+{
+ MapCacheKey key;
+ MapCacheEntry *entry;
+ key.repo = repo;
+ key.zoom = zoom;
+ key.tilex = tilex;
+ key.tiley = tiley;
+ entry = (MapCacheEntry *)g_hash_table_lookup(_map_cache.entries, &key);
+ if(entry != NULL)
+ {
+ map_cache_list_remove(_map_cache.lists+entry->list, entry);
+ if(entry->pixbuf == NULL)
+ {
+ size_t bsize;
+ size_t dp;
+ map_cache_entry_make_pixbuf(entry);
+ bsize = _map_cache.lists[entry->list].size+entry->size;
+ if(bsize < 1)
+ bsize = 1;
+ dp = _map_cache.lists[entry->list^2].size/bsize;
+ if(dp < 1)
+ dp = 1;
+ if(entry->list == 1)
+ {
+ _map_cache.p += dp;
+ if(_map_cache.p > _map_cache.cache_size)
+ _map_cache.p = _map_cache.cache_size;
+ map_cache_replace(entry->size, FALSE);
+ }
+ else
+ {
+ if(dp > _map_cache.p)
+ _map_cache.p = 0;
+ else
+ _map_cache.p -= dp;
+ map_cache_replace(entry->size, TRUE);
+ }
+ _map_cache.bhits++;
+ }
+ else
+ _map_cache.thits++;
+ map_cache_list_prepend(_map_cache.lists, 2, entry);
+ }
+ else
+ {
+ gchar *data;
+ guint data_sz;
+ data_sz = mapdb_get_data(repo, zoom, tilex, tiley, &data);
+ entry = g_slice_new(MapCacheEntry);
+ *&entry->key = *&key;
+ entry->data = data;
+ entry->data_sz = data_sz;
+ map_cache_entry_make_pixbuf(entry);
+ map_cache_evict(entry->size);
+ map_cache_list_prepend(_map_cache.lists, 0, entry);
+ g_hash_table_insert(_map_cache.entries, &entry->key, entry);
+ _map_cache.misses++;
+ }
+ if(entry->pixbuf != NULL)
+ g_object_ref(entry->pixbuf);
+ return entry->pixbuf;
+}
+
+static void
+map_cache_update(RepoData *repo, gint zoom, gint tilex, gint tiley,
+ gchar *data,guint size)
+{
+ MapCacheKey key;
+ MapCacheEntry *entry;
+ key.repo = repo;
+ key.zoom = zoom;
+ key.tilex = tilex;
+ key.tiley = tiley;
+ entry = (MapCacheEntry *)g_hash_table_lookup(_map_cache.entries, &key);
+ if(entry != NULL)
+ {
+ g_slice_free1(entry->data_sz, entry->data);
+ entry->data = g_slice_alloc(size);
+ memcpy(entry->data, data, size);
+ entry->data_sz = size;
+ if(entry->pixbuf != NULL)
+ {
+ map_cache_entry_free_pixbuf(entry);
+ map_cache_list_remove(_map_cache.lists+entry->list, entry);
+ map_cache_list_prepend(_map_cache.lists, entry->list+1, entry);
+ }
+ }
+}
+
+static void
+map_cache_remove(RepoData *repo, gint zoom, gint tilex, gint tiley)
+{
+ MapCacheKey key;
+ key.repo = repo;
+ key.zoom = zoom;
+ key.tilex = tilex;
+ key.tiley = tiley;
+ g_hash_table_remove(_map_cache.entries, &key);
+}
+
+void
+map_cache_init(size_t cache_size)
+{
+ g_mutex_lock(_mapdb_mutex);
+ if(_map_cache.entries == NULL)
+ _map_cache.entries = g_hash_table_new_full(map_cache_key_hash,
+ map_cache_key_equal, NULL, (GDestroyNotify)map_cache_entry_free);
+ _map_cache.cache_size = cache_size;
+ if(_map_cache.p > cache_size)
+ _map_cache.p = cache_size;
+ map_cache_evict(0);
+ g_mutex_unlock(_mapdb_mutex);
+}
+
+size_t
+map_cache_resize(size_t cache_size)
+{
+ size_t total_size;
+ g_mutex_lock(_mapdb_mutex);
+ _map_cache.cache_size = cache_size;
+ total_size = _map_cache.lists[0].size+_map_cache.lists[1].data_sz
+ +_map_cache.lists[2].size+_map_cache.lists[3].data_sz;
+ g_mutex_unlock(_mapdb_mutex);
+ return total_size;
+}
+
+void
+map_cache_destroy(void)
+{
+ g_mutex_lock(_mapdb_mutex);
+ if(_map_cache.entries != NULL)
+ {
+ g_hash_table_destroy(_map_cache.entries);
+ _map_cache.entries = NULL;
+ printf("thits: %u (%0.2f%%) bhits: %u (%0.2f%%) "
+ "misses: %u (%0.2f%%)\n",
+ _map_cache.thits, 100*_map_cache.thits/(double)(
+ _map_cache.thits+_map_cache.bhits+_map_cache.misses),
+ _map_cache.bhits, 100*_map_cache.bhits/(double)(
+ _map_cache.thits+_map_cache.bhits+_map_cache.misses),
+ _map_cache.misses, 100*_map_cache.misses/(double)(
+ _map_cache.thits+_map_cache.bhits+_map_cache.misses));
+ }
+ g_mutex_unlock(_mapdb_mutex);
+}
+
+gboolean
+mapdb_exists(RepoData *repo, gint zoom, gint tilex, gint tiley)
+{
+ gboolean exists;
+ vprintf("%s(%s, %d, %d, %d)\n", __PRETTY_FUNCTION__,
+ repo->name, zoom, tilex, tiley);
+
+ g_mutex_lock(_mapdb_mutex);
+
+ if(!repo->db)
+ {
+ /* There is no cache. Return FALSE. */
+ g_mutex_unlock(_mapdb_mutex);
+ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
+ return FALSE;
+ }
+
+ /* Search the cache first. */
+ {
+ MapCacheKey key;
+ MapCacheEntry *entry;
+ key.repo = repo;
+ key.zoom = zoom;
+ key.tilex = tilex;
+ key.tiley = tiley;
+ entry = (MapCacheEntry *)g_hash_table_lookup(_map_cache.entries, &key);
+ if(entry != NULL)
+ {
+ gboolean ret;
+ ret = entry->data != NULL;
+ g_mutex_unlock(_mapdb_mutex);
+ return ret;