From d9887a4fd4119fc9d103173776dd947635e5d29b Mon Sep 17 00:00:00 2001 From: Pierre-Luc Beaudoin Date: Sat, 28 Mar 2009 19:25:48 +0200 Subject: [PATCH] Introduce ChamplainCache to deal with caching tiles --- champlain/Makefile.am | 9 +- champlain/champlain-cache.c | 224 +++++++++++++++++++++++ champlain/champlain-cache.h | 75 ++++++++ champlain/champlain-network-map-source.c | 68 ++----- champlain/champlain-tile.c | 79 +++++++- champlain/champlain-tile.h | 5 + 6 files changed, 404 insertions(+), 56 deletions(-) create mode 100644 champlain/champlain-cache.c create mode 100644 champlain/champlain-cache.h diff --git a/champlain/Makefile.am b/champlain/Makefile.am index 40bdb32..7efe081 100644 --- a/champlain/Makefile.am +++ b/champlain/Makefile.am @@ -42,7 +42,8 @@ libchamplain_0_3_la_SOURCES = \ champlain-zoom-level.c \ champlain-tile.c \ champlain-map-source.c \ - champlain-network-map-source.c + champlain-network-map-source.c \ + champlain-cache.c noinst_HEADERS = \ champlain-debug.h \ @@ -56,7 +57,8 @@ noinst_HEADERS = \ champlain-enum-types.h \ champlain-map-source.h \ champlain-network-map-source.h \ - champlain-version.h + champlain-version.h \ + champlain-cache.h libchamplain_include_HEADERS = \ champlain.h \ @@ -70,7 +72,8 @@ libchamplain_include_HEADERS = \ champlain-zoom-level.h \ champlain-base-marker.h \ champlain-marker.h \ - champlain-version.h + champlain-version.h \ + champlain-cache.h libchamplain_0_3_la_LIBADD = $(DEPS_LIBS) $(SOUP_LIBS) ../tidy/libtidy-1.0.la diff --git a/champlain/champlain-cache.c b/champlain/champlain-cache.c new file mode 100644 index 0000000..2576672 --- /dev/null +++ b/champlain/champlain-cache.c @@ -0,0 +1,224 @@ +/* +* Copyright (C) 2008 Pierre-Luc Beaudoin + * + * 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 + */ + +#include "champlain-cache.h" + +#include "champlain-enum-types.h" +#include "champlain-private.h" + +#include +#include + +G_DEFINE_TYPE (ChamplainCache, champlain_cache, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CHAMPLAIN_TYPE_CACHE, ChamplainCachePrivate)) + +enum +{ + PROP_0, + PROP_SIZE_LIMIT, +}; + +typedef struct _ChamplainCachePrivate ChamplainCachePrivate; + +struct _ChamplainCachePrivate { + guint size_limit; +}; + +static void +champlain_cache_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + ChamplainCache *self = CHAMPLAIN_CACHE (object); + switch (property_id) + { + case PROP_SIZE_LIMIT: + g_value_set_uint (value, champlain_cache_get_size_limit (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +champlain_cache_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ChamplainCache *self = CHAMPLAIN_CACHE (object); + switch (property_id) + { + case PROP_SIZE_LIMIT: + champlain_cache_set_size_limit (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +champlain_cache_dispose (GObject *object) +{ +// ChamplainCachePrivate *priv = GET_PRIVATE (object); + + G_OBJECT_CLASS (champlain_cache_parent_class)->dispose (object); +} + +static void +champlain_cache_finalize (GObject *object) +{ + G_OBJECT_CLASS (champlain_cache_parent_class)->finalize (object); +} + +static void +champlain_cache_class_init (ChamplainCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ChamplainCachePrivate)); + + object_class->get_property = champlain_cache_get_property; + object_class->set_property = champlain_cache_set_property; + object_class->dispose = champlain_cache_dispose; + object_class->finalize = champlain_cache_finalize; + + g_object_class_install_property (object_class, + PROP_SIZE_LIMIT, + g_param_spec_uint ("size-limit", + "Size Limit", + "The cache's size limit (Mb)", + 1, + G_MAXINT, + 10, + G_PARAM_READWRITE)); +} + +static void +champlain_cache_init (ChamplainCache *self) +{ + champlain_cache_set_size_limit (self, 10); +} + +ChamplainCache* +champlain_cache_get_default (void) +{ + static ChamplainCache * instance = NULL; + + if (G_UNLIKELY (instance == NULL)) + { + instance = g_object_new (CHAMPLAIN_TYPE_CACHE, NULL); + return instance; + } + + return g_object_ref (instance); +} + +guint +champlain_cache_get_size_limit (ChamplainCache *self) +{ + g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE); + + ChamplainCachePrivate *priv = GET_PRIVATE (self); + + return priv->size_limit; +} + +void +champlain_cache_set_size_limit (ChamplainCache *self, + guint size_limit) +{ + g_return_if_fail(CHAMPLAIN_CACHE (self)); + + ChamplainCachePrivate *priv = GET_PRIVATE (self); + + priv->size_limit = size_limit; + g_object_notify (G_OBJECT (self), "size-limit"); +} + +gboolean +champlain_cache_fill_tile (ChamplainCache *self, + ChamplainTile *tile) +{ + g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE); + g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE); + + GFileInfo *info; + GFile *file; + GError *error = NULL; + ClutterActor *actor; + const gchar *etag = NULL; + const gchar *filename; + GTimeVal *modified_time = g_new0 (GTimeVal, 1); + + filename = champlain_tile_get_filename (tile); + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + return FALSE; + + file = g_file_new_for_path (filename); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_ETAG_VALUE, + G_FILE_QUERY_INFO_NONE, NULL, NULL); + g_file_info_get_modification_time (info, modified_time); + champlain_tile_set_modified_time (tile, modified_time); + + //TODO: retrieve etag + if (etag != NULL) + champlain_tile_set_etag (tile, etag); + + /* Load the cached version */ + actor = clutter_texture_new_from_file (filename, &error); + champlain_tile_set_actor (tile, actor); + + g_object_unref (file); + g_object_unref (info); + + return TRUE; +} + +gboolean +champlain_cache_tile_is_expired (ChamplainCache *self, + ChamplainTile *tile) +{ + g_return_val_if_fail(CHAMPLAIN_CACHE (self), FALSE); + g_return_val_if_fail(CHAMPLAIN_TILE (tile), FALSE); + + GTimeVal *now = g_new0 (GTimeVal, 1); + const GTimeVal *modified_time = champlain_tile_get_modified_time (tile); + gboolean validate_cache = FALSE; + + g_get_current_time (now); + g_time_val_add (now, (-60ul * 60ul * 1000ul * 1000ul)); // Cache expires 1 hour + validate_cache = modified_time->tv_sec < now->tv_sec; + + g_free (now); + return validate_cache; +} + +void +champlain_cache_update_tile (ChamplainCache *self, + ChamplainTile *tile) +{ + + //Save the etag + +} diff --git a/champlain/champlain-cache.h b/champlain/champlain-cache.h new file mode 100644 index 0000000..6fbe7c4 --- /dev/null +++ b/champlain/champlain-cache.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 Pierre-Luc Beaudoin + * + * 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 + */ + +#ifndef CHAMPLAIN_CACHE_H +#define CHAMPLAIN_CACHE_H + +#include + +#include "champlain-tile.h" + +#include + +G_BEGIN_DECLS + +#define CHAMPLAIN_TYPE_CACHE champlain_cache_get_type() + +#define CHAMPLAIN_CACHE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHAMPLAIN_TYPE_CACHE, ChamplainCache)) + +#define CHAMPLAIN_CACHE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), CHAMPLAIN_TYPE_CACHE, ChamplainCacheClass)) + +#define CHAMPLAIN_IS_CACHE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CHAMPLAIN_TYPE_CACHE)) + +#define CHAMPLAIN_IS_CACHE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), CHAMPLAIN_TYPE_CACHE)) + +#define CHAMPLAIN_CACHE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), CHAMPLAIN_TYPE_CACHE, ChamplainCacheClass)) + +typedef struct { + GObject parent; +} ChamplainCache; + +typedef struct { + GObjectClass parent_class; +} ChamplainCacheClass; + +GType champlain_cache_get_type (void); + +ChamplainCache* champlain_cache_get_default (void); + +void champlain_cache_update_tile (ChamplainCache *self, + ChamplainTile *tile); +gboolean champlain_cache_fill_tile (ChamplainCache *self, + ChamplainTile *tile); +gboolean champlain_cache_tile_is_expired (ChamplainCache *self, + ChamplainTile *tile); + +void champlain_cache_set_size_limit (ChamplainCache *self, + guint size_limit); +guint champlain_cache_get_size_limit (ChamplainCache *self); + +void champlain_cache_purge (ChamplainCache *self); + +G_END_DECLS + +#endif /* CHAMPLAIN_CACHE_H */ + diff --git a/champlain/champlain-network-map-source.c b/champlain/champlain-network-map-source.c index 3b107a5..1793408 100644 --- a/champlain/champlain-network-map-source.c +++ b/champlain/champlain-network-map-source.c @@ -24,6 +24,7 @@ #include "champlain-debug.h" #include "champlain.h" +#include "champlain-cache.h" #include "champlain-defines.h" #include "champlain-enum-types.h" #include "champlain-map-source.h" @@ -413,6 +414,7 @@ file_loaded_cb (SoupSession *session, ClutterActor *actor, *previous_actor = NULL; GFile *file; GFileInfo *info; + ChamplainCache *cache = champlain_cache_get_default (); filename = champlain_tile_get_filename (ctx->tile); @@ -496,16 +498,9 @@ file_loaded_cb (SoupSession *session, if (etag != NULL) { - file = g_file_new_for_path (filename); - info = g_file_query_info (file, G_FILE_ATTRIBUTE_ETAG_VALUE, - G_FILE_QUERY_INFO_NONE, NULL, NULL); - g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag); - g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, NULL, - NULL); - - g_object_unref (file); - g_object_unref (info); + champlain_tile_set_etag (ctx->tile, etag); } + champlain_cache_update_tile (cache, ctx->tile); /* Load the image into clutter */ GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); @@ -558,50 +553,21 @@ champlain_network_map_source_get_tile (ChamplainMapSource *map_source, gchar* filename; gboolean in_cache = FALSE; gboolean validate_cache = FALSE; - GTimeVal *modified_time = g_new0 (GTimeVal, 1); - gchar * etag = NULL; ChamplainNetworkMapSource *network_map_source = CHAMPLAIN_NETWORK_MAP_SOURCE (map_source); ChamplainNetworkMapSourcePrivate *priv = network_map_source->priv; + ChamplainCache *cache = champlain_cache_get_default (); /* Try the cached version first */ filename = get_filename (network_map_source, zoom_level, tile); champlain_tile_set_filename (tile, filename); champlain_tile_set_size (tile, champlain_map_source_get_tile_size (map_source)); - if (g_file_test (filename, G_FILE_TEST_EXISTS)) + in_cache = champlain_cache_fill_tile (cache, tile); + + if (in_cache == TRUE) { - GTimeVal *now = g_new0 (GTimeVal, 1); - GFileInfo *info; - GFile *file; - GError *error = NULL; - ClutterActor *actor; - const gchar *etag_tmp; - - in_cache = TRUE; - - /* Verify since when is the file in cache */ - file = g_file_new_for_path (filename); - info = g_file_query_info (file, - G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_ETAG_VALUE, - G_FILE_QUERY_INFO_NONE, NULL, NULL); - g_file_info_get_modification_time (info, modified_time); - etag_tmp = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE); - if (etag_tmp != NULL) - etag = g_strdup (etag_tmp); - - g_get_current_time (now); - g_time_val_add (now, (-60ul * 60ul * 1000ul * 1000ul)); // Cache expires 1 hour - validate_cache = modified_time->tv_sec < now->tv_sec; - validate_cache = TRUE; - - g_object_unref (file); - g_object_unref (info); - g_free (now); - - /* Load the cached version */ - actor = clutter_texture_new_from_file (filename, &error); - champlain_tile_set_actor (tile, actor); + validate_cache = champlain_cache_tile_is_expired (cache, tile); if (validate_cache == TRUE) champlain_tile_set_state (tile, CHAMPLAIN_STATE_VALIDATING_CACHE); @@ -646,22 +612,22 @@ champlain_network_map_source_get_tile (ChamplainMapSource *map_source, if (in_cache == TRUE) { - char value [100]; - struct tm *other_time = gmtime (&modified_time->tv_sec); - - strftime (value, 100, "%a, %d %b %Y %T %Z", other_time); + const gchar *etag; + gchar *date; - DEBUG("If-Modified-Since %s", value); + date = champlain_tile_get_modified_time_string (tile); + DEBUG("If-Modified-Since %s", date); soup_message_headers_append (msg->request_headers, - "If-Modified-Since", value); + "If-Modified-Since", date); + g_free (date); + etag = champlain_tile_get_etag (tile); if (etag != NULL) { DEBUG("If-None-Match: %s", etag); soup_message_headers_append (msg->request_headers, "If-None-Match", etag); } - } soup_session_queue_message (soup_session, msg, @@ -673,7 +639,5 @@ champlain_network_map_source_get_tile (ChamplainMapSource *map_source, /* If a tile is neither in cache or can be fetched, do nothing, it'll show up * as empty */ - g_free (modified_time); - g_free (etag); } diff --git a/champlain/champlain-tile.c b/champlain/champlain-tile.c index 0c928be..bdde921 100644 --- a/champlain/champlain-tile.c +++ b/champlain/champlain-tile.c @@ -43,7 +43,8 @@ enum PROP_URI, PROP_FILENAME, PROP_STATE, - PROP_ACTOR + PROP_ACTOR, + PROP_ETAG }; typedef struct _ChamplainTilePrivate ChamplainTilePrivate; @@ -59,6 +60,9 @@ struct _ChamplainTilePrivate { ChamplainState state; gchar *filename; ClutterActor *actor; + + GTimeVal *modified_time; + gchar* etag; }; static void @@ -94,6 +98,9 @@ champlain_tile_get_property (GObject *object, case PROP_ACTOR: g_value_set_object (value, champlain_tile_get_actor (self)); break; + case PROP_ETAG: + g_value_set_string (value, champlain_tile_get_etag (self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -132,6 +139,9 @@ champlain_tile_set_property (GObject *object, case PROP_ACTOR: champlain_tile_set_actor (self, g_value_get_object (value)); break; + case PROP_ETAG: + champlain_tile_set_etag (self, g_value_get_string (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -244,6 +254,15 @@ champlain_tile_class_init (ChamplainTileClass *klass) "The tile's actor", CLUTTER_TYPE_ACTOR, G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ETAG, + g_param_spec_string ("etag", + "Entity Tag", + "The entity tag of the tile", + NULL, + G_PARAM_READWRITE)); + } static void @@ -258,6 +277,7 @@ champlain_tile_init (ChamplainTile *self) priv->size = 0; priv->uri = g_strdup (""); priv->filename = g_strdup (""); + priv->etag = NULL; /* Can't call champlain_tile_set_actor (self, NULL); because the assertion will fail */ priv->actor = NULL; @@ -456,3 +476,60 @@ champlain_tile_set_actor (ChamplainTile *self, ClutterActor *actor) priv->actor = g_object_ref (actor); g_object_notify (G_OBJECT (self), "actor"); } + +const GTimeVal * +champlain_tile_get_modified_time (ChamplainTile *self) +{ + g_return_val_if_fail (CHAMPLAIN_TILE(self), NULL); + + ChamplainTilePrivate *priv = GET_PRIVATE (self); + + return priv->modified_time; +} + +void +champlain_tile_set_modified_time (ChamplainTile *self, + GTimeVal *time) +{ + g_return_if_fail (CHAMPLAIN_TILE(self)); + g_return_if_fail (time != NULL); + + ChamplainTilePrivate *priv = GET_PRIVATE (self); + + priv->modified_time = time; +} + +char * +champlain_tile_get_modified_time_string (ChamplainTile *self) +{ + g_return_val_if_fail(CHAMPLAIN_TILE(self), NULL); + ChamplainTilePrivate *priv = GET_PRIVATE (self); + + struct tm *other_time = gmtime (&priv->modified_time->tv_sec); + char value [100]; + + strftime (value, 100, "%a, %d %b %Y %T %Z", other_time); + + return g_strdup (value); +} + +const char * +champlain_tile_get_etag (ChamplainTile *self) +{ + g_return_val_if_fail(CHAMPLAIN_TILE(self), ""); + ChamplainTilePrivate *priv = GET_PRIVATE (self); + + return priv->etag; +} + +void +champlain_tile_set_etag (ChamplainTile *self, + const gchar *etag) +{ + g_return_if_fail(CHAMPLAIN_TILE(self)); + + ChamplainTilePrivate *priv = GET_PRIVATE (self); + + priv->etag = g_strdup (etag); + g_object_notify (G_OBJECT (self), "etag"); +} diff --git a/champlain/champlain-tile.h b/champlain/champlain-tile.h index 1100b69..63bb639 100644 --- a/champlain/champlain-tile.h +++ b/champlain/champlain-tile.h @@ -64,6 +64,9 @@ ChamplainState champlain_tile_get_state (ChamplainTile *self); G_CONST_RETURN gchar * champlain_tile_get_uri (ChamplainTile *self); G_CONST_RETURN gchar * champlain_tile_get_filename (ChamplainTile *self); ClutterActor * champlain_tile_get_actor (ChamplainTile *self); +const GTimeVal * champlain_tile_get_modified_time (ChamplainTile *self); +char * champlain_tile_get_modified_time_string (ChamplainTile *self); +const char * champlain_tile_get_etag (ChamplainTile *self); void champlain_tile_set_x (ChamplainTile *self, gint x); void champlain_tile_set_y (ChamplainTile *self, gint y); @@ -73,6 +76,8 @@ void champlain_tile_set_state (ChamplainTile *self, ChamplainState state); void champlain_tile_set_uri (ChamplainTile *self, const gchar *uri); void champlain_tile_set_filename (ChamplainTile *self, const gchar *filename); void champlain_tile_set_actor (ChamplainTile *self, ClutterActor* actor); +void champlain_tile_set_etag (ChamplainTile *self, const gchar *etag); +void champlain_tile_set_modified_time (ChamplainTile *self, GTimeVal *time); G_END_DECLS -- 2.39.5