[libshumate] network-tile-source: Use its own file cache



commit 764c3a731860cdce378da701ba7cc770c57cb9f0
Author: James Westman <james jwestman net>
Date:   Thu Mar 18 14:17:59 2021 -0500

    network-tile-source: Use its own file cache
    
    ShumateNetworkTileSource now uses its own file cache, rather than
    relying on a file cache in the map chain. This simplifies the map chain.

 shumate/shumate-map-source-factory.c  |   7 +-
 shumate/shumate-network-tile-source.c | 228 ++++++++++++++++++++++++++--------
 2 files changed, 177 insertions(+), 58 deletions(-)
---
diff --git a/shumate/shumate-map-source-factory.c b/shumate/shumate-map-source-factory.c
index 73dd210..41084eb 100644
--- a/shumate/shumate-map-source-factory.c
+++ b/shumate/shumate-map-source-factory.c
@@ -41,7 +41,6 @@
 #include "shumate-debug.h"
 
 #include "shumate.h"
-#include "shumate-file-cache.h"
 #include "shumate-enum-types.h"
 #include "shumate-map-source.h"
 #include "shumate-marshal.h"
@@ -299,7 +298,7 @@ shumate_map_source_factory_create (ShumateMapSourceFactory *factory,
  * Creates a cached map source.
  *
  * Returns: (transfer none): a ready to use #ShumateMapSourceChain consisting of
- * #ShumateMemoryCache, #ShumateFileCache, #ShumateMapSource matching the given name, and
+ * #ShumateMemoryCache, #ShumateMapSource matching the given name, and
  * an error tile source created with shumate_map_source_factory_create_error_source ().
  * Returns NULL if the source with the given name doesn't exist.
  */
@@ -311,7 +310,6 @@ shumate_map_source_factory_create_cached_source (ShumateMapSourceFactory *factor
   ShumateMapSource *tile_source;
   ShumateMapSource *error_source;
   ShumateMapSource *memory_cache;
-  ShumateMapSource *file_cache;
   guint tile_size;
 
   g_return_val_if_fail (SHUMATE_IS_MAP_SOURCE_FACTORY (factory), NULL);
@@ -323,14 +321,11 @@ shumate_map_source_factory_create_cached_source (ShumateMapSourceFactory *factor
   tile_size = shumate_map_source_get_tile_size (tile_source);
   error_source = shumate_map_source_factory_create_error_source (factory, tile_size);
 
-  file_cache = SHUMATE_MAP_SOURCE (shumate_file_cache_new_full (100000000, id, NULL));
-
   memory_cache = SHUMATE_MAP_SOURCE (shumate_memory_cache_new_full (100));
 
   source_chain = shumate_map_source_chain_new ();
   shumate_map_source_chain_push (source_chain, error_source);
   shumate_map_source_chain_push (source_chain, tile_source);
-  shumate_map_source_chain_push (source_chain, file_cache);
   shumate_map_source_chain_push (source_chain, memory_cache);
 
   return SHUMATE_MAP_SOURCE (source_chain);
diff --git a/shumate/shumate-network-tile-source.c b/shumate/shumate-network-tile-source.c
index b9014c3..9f79ff9 100644
--- a/shumate/shumate-network-tile-source.c
+++ b/shumate/shumate-network-tile-source.c
@@ -62,7 +62,8 @@ enum
   PROP_OFFLINE,
   PROP_PROXY_URI,
   PROP_MAX_CONNS,
-  PROP_USER_AGENT
+  PROP_USER_AGENT,
+  PROP_FILE_CACHE,
 };
 
 typedef struct
@@ -72,6 +73,7 @@ typedef struct
   char *proxy_uri;
   SoupSession *soup_session;
   int max_conns;
+  ShumateFileCache *file_cache;
 } ShumateNetworkTileSourcePrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (ShumateNetworkTileSource, shumate_network_tile_source, SHUMATE_TYPE_TILE_SOURCE);
@@ -87,6 +89,7 @@ typedef struct
   GCancellable *cancellable;
   ShumateTile *tile;
   SoupMessage *msg;
+  GBytes *cached_data;
 } TileLoadedData;
 
 typedef struct
@@ -110,6 +113,21 @@ static char *get_tile_uri (ShumateNetworkTileSource *source,
     int y,
     int z);
 
+
+static void
+shumate_network_tile_source_constructed (GObject *object)
+{
+  ShumateNetworkTileSource *self = SHUMATE_NETWORK_TILE_SOURCE (object);
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
+  const char *id = shumate_map_source_get_id (SHUMATE_MAP_SOURCE (self));
+
+  priv->file_cache = shumate_file_cache_new_full (100000000, id, NULL);
+  g_object_notify (object, "file-cache");
+
+  G_OBJECT_CLASS (shumate_network_tile_source_parent_class)->constructed (object);
+}
+
+
 static void
 shumate_network_tile_source_get_property (GObject *object,
     guint prop_id,
@@ -137,6 +155,10 @@ shumate_network_tile_source_get_property (GObject *object,
       g_value_set_int (value, priv->max_conns);
       break;
 
+    case PROP_FILE_CACHE:
+      g_value_set_object (value, priv->file_cache);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -203,6 +225,7 @@ shumate_network_tile_source_finalize (GObject *object)
 
   g_clear_pointer (&priv->uri_format, g_free);
   g_clear_pointer (&priv->proxy_uri, g_free);
+  g_clear_object (&priv->file_cache);
 
   G_OBJECT_CLASS (shumate_network_tile_source_parent_class)->finalize (object);
 }
@@ -214,6 +237,7 @@ shumate_network_tile_source_class_init (ShumateNetworkTileSourceClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GParamSpec *pspec;
 
+  object_class->constructed = shumate_network_tile_source_constructed;
   object_class->finalize = shumate_network_tile_source_finalize;
   object_class->dispose = shumate_network_tile_source_dispose;
   object_class->get_property = shumate_network_tile_source_get_property;
@@ -289,6 +313,18 @@ shumate_network_tile_source_class_init (ShumateNetworkTileSourceClass *klass)
         G_PARAM_WRITABLE);
 
   g_object_class_install_property (object_class, PROP_USER_AGENT, pspec);
+
+  /**
+   * ShumateNetworkTileSource:file-cache:
+   *
+   * The cache where downloaded tiles are stored.
+   */
+  pspec = g_param_spec_object("file-cache",
+                              "File Cache",
+                              "Cache for storing tile data",
+                              SHUMATE_TYPE_FILE_CACHE,
+                              G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+  g_object_class_install_property (object_class, PROP_FILE_CACHE, pspec);
 }
 
 
@@ -639,6 +675,7 @@ on_pixbuf_created (GObject      *source_object,
 {
   TileRenderedData *rendered_data = (TileRenderedData *) user_data;
   g_autoptr(ShumateNetworkTileSource) self = g_steal_pointer (&rendered_data->self);
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
   g_autoptr(GCancellable) cancellable = g_steal_pointer (&rendered_data->cancellable);
   g_autoptr(ShumateTile) tile = g_steal_pointer (&rendered_data->tile);
   g_autoptr(GError) error = NULL;
@@ -671,15 +708,20 @@ on_pixbuf_created (GObject      *source_object,
   if (etag != NULL)
     shumate_tile_set_etag (tile, etag);
 
-  if (tile_cache)
+  g_autofree char *buffer = NULL;
+  gsize buffer_size;
+  if (!gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &buffer_size, "png", &error, NULL))
+    g_warning ("Unable to export tile: %s", error->message);
+  else
     {
-      g_autoptr(GError) error = NULL;
-      g_autofree char *buffer = NULL;
-      gsize buffer_size;
-      if (!gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &buffer_size, "png", &error, NULL))
-        g_warning ("Unable to export tile: %s", error->message);
-      else
+      g_autoptr(GBytes) bytes = NULL;
+
+      if (tile_cache)
         shumate_tile_cache_store_tile (tile_cache, tile, buffer, buffer_size);
+
+      bytes = g_bytes_new_take (buffer, buffer_size);
+      buffer = NULL;
+      shumate_file_cache_store_tile_async (priv->file_cache, tile, bytes, etag, cancellable, NULL, NULL);
     }
 
   texture = gdk_texture_new_for_pixbuf (pixbuf);
@@ -688,6 +730,24 @@ on_pixbuf_created (GObject      *source_object,
   shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
 }
 
+static void
+on_pixbuf_created_from_cache (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  g_autoptr(ShumateTile) tile = SHUMATE_TILE (user_data);
+  g_autoptr(GdkTexture) texture = NULL;
+  g_autoptr(GdkPixbuf) pixbuf = NULL;
+
+  pixbuf = gdk_pixbuf_new_from_stream_finish (res, NULL);
+
+  if (pixbuf == NULL)
+    return;
+
+  texture = gdk_texture_new_for_pixbuf (pixbuf);
+  shumate_tile_set_texture (tile, texture);
+  shumate_tile_set_fade_in (tile, TRUE);
+  shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
+}
+
 static void
 on_message_sent (GObject *source_object,
                  GAsyncResult *res,
@@ -698,6 +758,7 @@ on_message_sent (GObject *source_object,
   g_autoptr(ShumateNetworkTileSource) self = g_steal_pointer (&callback_data->self);
   g_autoptr(GCancellable) cancellable = g_steal_pointer (&callback_data->cancellable);
   g_autoptr(ShumateTile) tile = g_steal_pointer (&callback_data->tile);
+  g_autoptr(GBytes) cached_data = g_steal_pointer (&callback_data->cached_data);
   g_autoptr(GInputStream) input_stream = NULL;
   g_autoptr(GError) error = NULL;
   ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (self);
@@ -726,12 +787,17 @@ on_message_sent (GObject *source_object,
 
   if (msg->status_code == SOUP_STATUS_NOT_MODIFIED)
     {
+      g_autoptr(GInputStream) cache_stream = NULL;
+
       if (tile_cache)
         shumate_tile_cache_refresh_tile_time (tile_cache, tile);
 
+      shumate_tile_cache_refresh_tile_time (SHUMATE_TILE_CACHE (priv->file_cache), tile);
+
+      cache_stream = g_memory_input_stream_new_from_bytes (cached_data);
+      gdk_pixbuf_new_from_stream_async (cache_stream, cancellable, on_pixbuf_created_from_cache, 
g_object_ref (tile));
+
       g_signal_handlers_disconnect_by_func (tile, tile_state_notify, cancellable);
-      shumate_tile_set_fade_in (tile, TRUE);
-      shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
       return;
     }
 
@@ -791,6 +857,24 @@ get_modified_time_string (ShumateTile *tile)
 }
 
 
+typedef struct {
+  ShumateNetworkTileSource *self;
+  ShumateTile *tile;
+  GCancellable *cancellable;
+} FillTileData;
+
+static void
+fill_tile_data_free (FillTileData *data)
+{
+  g_clear_object (&data->self);
+  g_clear_object (&data->tile);
+  g_clear_object (&data->cancellable);
+  g_free (data);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (FillTileData, fill_tile_data_free);
+
+static void on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user_data);
+
 static void
 fill_tile (ShumateMapSource *map_source,
            ShumateTile      *tile,
@@ -798,6 +882,7 @@ fill_tile (ShumateMapSource *map_source,
 {
   ShumateNetworkTileSource *tile_source = SHUMATE_NETWORK_TILE_SOURCE (map_source);
   ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (tile_source);
+  FillTileData *data;
 
   g_return_if_fail (SHUMATE_IS_NETWORK_TILE_SOURCE (map_source));
   g_return_if_fail (SHUMATE_IS_TILE (tile));
@@ -805,57 +890,96 @@ fill_tile (ShumateMapSource *map_source,
   if (shumate_tile_get_state (tile) == SHUMATE_STATE_DONE)
     return;
 
-  if (!priv->offline)
+  if (priv->offline)
     {
-      TileLoadedData *callback_data = g_slice_new0 (TileLoadedData);
-      g_autofree char *uri = NULL;
+      ShumateMapSource *next_source = shumate_map_source_get_next_source (map_source);
+
+      if (SHUMATE_IS_MAP_SOURCE (next_source))
+        shumate_map_source_fill_tile (next_source, tile, cancellable);
+
+      return;
+    }
+
+  data = g_new0 (FillTileData, 1);
+  data->self = g_object_ref (tile_source);
+  data->tile = g_object_ref (tile);
+  data->cancellable = g_object_ref (cancellable);
+
+  shumate_file_cache_get_tile_async (priv->file_cache, tile, cancellable, on_file_cache_get_tile, data);
+}
+
+static void
+on_file_cache_get_tile (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  g_autoptr(FillTileData) data = user_data;
+  ShumateNetworkTileSourcePrivate *priv = shumate_network_tile_source_get_instance_private (data->self);
+  TileLoadedData *callback_data;
+  g_autofree char *uri = NULL;
+  g_autofree char *etag = NULL;
+  g_autoptr(GBytes) bytes = NULL;
 
-      uri = get_tile_uri (tile_source,
-            shumate_tile_get_x (tile),
-            shumate_tile_get_y (tile),
-            shumate_tile_get_zoom_level (tile));
+  bytes = shumate_file_cache_get_tile_finish (SHUMATE_FILE_CACHE (source_object), &etag, res, NULL);
 
-      callback_data->tile = g_object_ref (tile);
-      callback_data->self = g_object_ref (tile_source);
-      if (cancellable)
-        callback_data->cancellable = g_object_ref (cancellable);
-      callback_data->msg = soup_message_new (SOUP_METHOD_GET, uri);
+  if (bytes)
+    {
+      g_autoptr(GInputStream) input_stream = NULL;
+      ShumateTileCache *tile_cache = shumate_tile_source_get_cache (SHUMATE_TILE_SOURCE (data->self));
 
-      if (shumate_tile_get_state (tile) == SHUMATE_STATE_LOADED)
+      if (tile_cache)
         {
-          /* validate tile */
-
-          const char *etag = shumate_tile_get_etag (tile);
-          char *date = get_modified_time_string (tile);
-
-          /* If an etag is available, only use it.
-           * OSM servers seems to send now as the modified time for all tiles
-           * Omarender servers set the modified time correctly
-           */
-          if (etag)
-            {
-              DEBUG ("If-None-Match: %s", etag);
-              soup_message_headers_append (callback_data->msg->request_headers,
-                  "If-None-Match", etag);
-            }
-          else if (date)
-            {
-              DEBUG ("If-Modified-Since %s", date);
-              soup_message_headers_append (callback_data->msg->request_headers,
-                  "If-Modified-Since", date);
-            }
-
-          g_free (date);
+          gsize size;
+          gconstpointer buffer = g_bytes_get_data (bytes, &size);
+          shumate_tile_cache_store_tile (tile_cache, data->tile, buffer, size);
         }
 
-      g_signal_connect_object (tile, "notify::state", G_CALLBACK (tile_state_notify), cancellable, 0);
-      soup_session_send_async (priv->soup_session, callback_data->msg, cancellable, on_message_sent, 
callback_data);
+      if (etag == NULL)
+        {
+          /* No need to fetch new data from the network. Just fill the tile
+           * directly from the cache. */
+          input_stream = g_memory_input_stream_new_from_bytes (bytes);
+          gdk_pixbuf_new_from_stream_async (input_stream, data->cancellable, on_pixbuf_created_from_cache, 
g_object_ref (data->tile));
+          return;
+        }
     }
-  else
+
+  uri = get_tile_uri (data->self,
+        shumate_tile_get_x (data->tile),
+        shumate_tile_get_y (data->tile),
+        shumate_tile_get_zoom_level (data->tile));
+
+  callback_data = g_slice_new0 (TileLoadedData);
+  callback_data->tile = g_object_ref (data->tile);
+  callback_data->self = g_object_ref (data->self);
+  if (data->cancellable)
+    callback_data->cancellable = g_object_ref (data->cancellable);
+  if (bytes)
+    callback_data->cached_data = g_bytes_ref (bytes);
+  callback_data->msg = soup_message_new (SOUP_METHOD_GET, uri);
+
+  if (etag || shumate_tile_get_state (data->tile) == SHUMATE_STATE_LOADED)
     {
-      ShumateMapSource *next_source = shumate_map_source_get_next_source (map_source);
+      char *date = get_modified_time_string (data->tile);
 
-      if (SHUMATE_IS_MAP_SOURCE (next_source))
-        shumate_map_source_fill_tile (next_source, tile, cancellable);
+      /* If an etag is available, only use it.
+       * OSM servers seems to send now as the modified time for all tiles
+       * Omarender servers set the modified time correctly
+       */
+      if (etag)
+        {
+          DEBUG ("If-None-Match: %s", etag);
+          soup_message_headers_append (callback_data->msg->request_headers,
+              "If-None-Match", etag);
+        }
+      else if (date)
+        {
+          DEBUG ("If-Modified-Since %s", date);
+          soup_message_headers_append (callback_data->msg->request_headers,
+              "If-Modified-Since", date);
+        }
+
+      g_free (date);
     }
+
+  g_signal_connect_object (data->tile, "notify::state", G_CALLBACK (tile_state_notify), data->cancellable, 
0);
+  soup_session_send_async (priv->soup_session, callback_data->msg, data->cancellable, on_message_sent, 
callback_data);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]