This patch implements a page cache that is invalidated in a LRU fashion. 

Pages are added to the cache as soon as they become visible. When the cache is 
full and a new page that isn't in the cache becomes visible, the least recently 
viewed page in the cache is evicted from memory and the new one takes it's 
place.

The cache size is configurable using the page-cache-size configuration 
variable, with a default value of 15 pages. Very large values for the cache 
size are not recommended, though, as it will stress the system memory out.

The old periodic page reclaiming code is no longer necessary with this patch, 
so I removed it.

Special thanks to Ignas Anikevičius, and Sebastian Ramacher for the 
inspirations.

---
 callbacks.c     |    3 +-
 config.c        |    5 ++-
 page-widget.c   |   16 ---------
 page-widget.h   |    8 -----
 page.c          |    9 -----
 page.h          |    9 +++++
 zathura.c       |  108 +++++++++++++++++++++++++++++++++++++++----------------
 zathura.h       |   17 +++++++++
 zathurarc.5.rst |   18 ++++------
 9 files changed, 114 insertions(+), 79 deletions(-)

diff --git a/callbacks.c b/callbacks.c
index 2c63945..7c443b1 100644
--- a/callbacks.c
+++ b/callbacks.c
@@ -95,6 +95,8 @@ cb_view_vadjustment_value_changed(GtkAdjustment* 
GIRARA_UNUSED(adjustment), gpoi
 
     if (gdk_rectangle_intersect(&view_rect, &page_rect, NULL) == TRUE) {
       zathura_page_set_visibility(page, true);
+      zathura_page_widget_update_view_time(ZATHURA_PAGE(page_widget));
+      zathura_page_cache_add(zathura, page);
       if (zathura->global.update_page_number == true && updated == false
           && gdk_rectangle_intersect(&center, &page_rect, NULL) == TRUE) {
         zathura_document_set_current_page_number(zathura->document, page_id);
@@ -103,7 +105,6 @@ cb_view_vadjustment_value_changed(GtkAdjustment* 
GIRARA_UNUSED(adjustment), gpoi
     } else {
       zathura_page_set_visibility(page, false);
     }
-    zathura_page_widget_update_view_time(ZATHURA_PAGE(page_widget));
   }
 
   statusbar_page_number_update(zathura);
diff --git a/config.c b/config.c
index 2dd64df..3f49034 100644
--- a/config.c
+++ b/config.c
@@ -155,9 +155,8 @@ config_load_default(zathura_t* zathura)
   girara_setting_add(gsession, "zoom-min",              &int_value,   INT,    
false, _("Zoom minimum"), NULL, NULL);
   int_value = 1000;
   girara_setting_add(gsession, "zoom-max",              &int_value,   INT,    
false, _("Zoom maximum"), NULL, NULL);
-  int_value = 20;
-  girara_setting_add(gsession, "page-store-threshold",  &int_value,   INT,    
false, _("Life time (in seconds) of a hidden page"), NULL, NULL);
-  girara_setting_add(gsession, "page-store-interval",   &int_value,   INT,    
true,  _("Amount of seconds between each cache purge"), NULL, NULL);
+  int_value = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
+  girara_setting_add(gsession, "page-cache-size",       &int_value,  INT,    
true,  _("Maximum number of pages to keep in the cache"), NULL, NULL);
   int_value = 20;
   girara_setting_add(gsession, "jumplist-size",         &int_value,   INT,    
false, _("Number of positions to remember in the jumplist"), 
cb_jumplist_change, NULL);
 
diff --git a/page-widget.c b/page-widget.c
index 3951f63..5fa52ba 100644
--- a/page-widget.c
+++ b/page-widget.c
@@ -860,19 +860,3 @@ zathura_page_widget_update_view_time(ZathuraPage* widget)
     priv->last_view = g_get_real_time();
   }
 }
-
-void
-zathura_page_widget_purge_unused(ZathuraPage* widget, gint64 threshold)
-{
-  g_return_if_fail(ZATHURA_IS_PAGE(widget) == TRUE);
-  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
-  if (zathura_page_get_visibility(priv->page) == true || priv->surface == NULL 
|| threshold <= 0) {
-    return;
-  }
-
-  const gint64 now =  g_get_real_time();
-  if (now - priv->last_view >= threshold * G_USEC_PER_SEC) {
-    girara_debug("purge page %d from cache (unseen for %f seconds)", 
zathura_page_get_index(priv->page), ((double)now - priv->last_view) / 
G_USEC_PER_SEC);
-    zathura_page_widget_update_surface(widget, NULL);
-  }
-}
diff --git a/page-widget.h b/page-widget.h
index fe0b67a..5609e78 100644
--- a/page-widget.h
+++ b/page-widget.h
@@ -88,12 +88,4 @@ zathura_link_t* zathura_page_widget_link_get(ZathuraPage* 
widget, unsigned int i
  */
 void zathura_page_widget_update_view_time(ZathuraPage* widget);
 
-/**
- * If the page has not been viewed for some time, purge the surface.
- *
- * @param widget the widget
- * @param threshold the threshold (in seconds)
- */
-void zathura_page_widget_purge_unused(ZathuraPage* widget, gint64 threshold);
-
 #endif
diff --git a/page.c b/page.c
index d31af46..cb84313 100644
--- a/page.c
+++ b/page.c
@@ -11,15 +11,6 @@
 #include "internal.h"
 #include "types.h"
 
-struct zathura_page_s {
-  double height; /**< Page height */
-  double width; /**< Page width */
-  unsigned int index; /**< Page number */
-  void* data; /**< Custom data */
-  bool visible; /**< Page is visible */
-  zathura_document_t* document; /**< Document */
-};
-
 zathura_page_t*
 zathura_page_new(zathura_document_t* document, unsigned int index, 
zathura_error_t* error)
 {
diff --git a/page.h b/page.h
index dbac902..80d509d 100644
--- a/page.h
+++ b/page.h
@@ -8,6 +8,15 @@
 
 #include "types.h"
 
+struct zathura_page_s {
+  double height; /**< Page height */
+  double width; /**< Page width */
+  unsigned int index; /**< Page number */
+  void* data; /**< Custom data */
+  bool visible; /**< Page is visible */
+  zathura_document_t* document; /**< Document */
+};
+
 /**
  * Get the page object
  *
diff --git a/zathura.c b/zathura.c
index 8e8c4da..e650485 100644
--- a/zathura.c
+++ b/zathura.c
@@ -53,7 +53,6 @@ typedef struct position_set_delayed_s {
 } position_set_delayed_t;
 
 static gboolean document_info_open(gpointer data);
-static gboolean purge_pages(gpointer data);
 
 /* function implementation */
 zathura_t*
@@ -264,11 +263,6 @@ zathura_init(zathura_t* zathura)
   zathura->bookmarks.bookmarks = 
girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
                                  (girara_free_function_t) 
zathura_bookmark_free);
 
-  /* add even to purge old pages */
-  int interval = 30;
-  girara_setting_get(zathura->ui.session, "page-store-interval", &interval);
-  g_timeout_add_seconds(interval, purge_pages, zathura);
-
   /* jumplist */
 
   zathura->jumplist.max_size = 20;
@@ -279,6 +273,13 @@ zathura_init(zathura_t* zathura)
   zathura->jumplist.cur = NULL;
   zathura_jumplist_append_jump(zathura);
   zathura->jumplist.cur = girara_list_iterator(zathura->jumplist.list);
+
+  /* page cache */
+
+  zathura->page_cache.size = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
+  girara_setting_get(zathura->ui.session, "page-cache-size", 
&zathura->page_cache.size);
+  zathura->page_cache.cache = g_malloc(zathura->page_cache.size * 
sizeof(zathura_page_t*));
+
   return true;
 
 error_free:
@@ -353,6 +354,8 @@ zathura_free(zathura_t* zathura)
     girara_list_iterator_free(zathura->jumplist.cur);
   }
 
+  g_free(zathura->page_cache.cache);
+
   g_free(zathura);
 }
 
@@ -1066,30 +1069,6 @@ page_widget_set_mode(zathura_t* zathura, unsigned int 
pages_per_row, unsigned in
   gtk_widget_show_all(zathura->ui.page_widget);
 }
 
-static
-gboolean purge_pages(gpointer data)
-{
-  zathura_t* zathura = data;
-  if (zathura == NULL || zathura->document == NULL) {
-    return TRUE;
-  }
-
-  int threshold = 0;
-  girara_setting_get(zathura->ui.session, "page-store-threshold", &threshold);
-  if (threshold <= 0) {
-    return TRUE;
-  }
-
-  girara_debug("purging pages ...");
-  unsigned int number_of_pages = 
zathura_document_get_number_of_pages(zathura->document);
-  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
-    zathura_page_t* page   = zathura_document_get_page(zathura->document, 
page_id);
-    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
-    zathura_page_widget_purge_unused(ZATHURA_PAGE(page_widget), threshold);
-  }
-  return TRUE;
-}
-
 static gboolean
 position_set_delayed_impl(gpointer data)
 {
@@ -1215,3 +1194,72 @@ zathura_jumplist_save(zathura_t* zathura)
     cur->y = gtk_adjustment_get_value(view_vadjustment) / 
zathura_document_get_scale(zathura->document);;
   }
 }
+
+static bool
+zathura_page_cache_is_cached(zathura_t* zathura, zathura_page_t* page)
+{
+  g_return_val_if_fail(zathura != NULL, false);
+
+  unsigned int i;
+
+  for (i = 0; i < zathura->page_cache.size; ++i) {
+    if (page == zathura->page_cache.cache[i]) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static ssize_t
+zathura_page_cache_lru_invalidate(zathura_t* zathura)
+{
+   g_return_val_if_fail(zathura != NULL, -1);
+
+   ssize_t lru_index = 0;
+   guint64 view_time = 0;
+   guint64 lru_view_time = G_MAXUINT64;
+
+   for (unsigned int i = 0; i < zathura->page_cache.size; ++i) {
+     GtkWidget* page_widget = zathura_page_get_widget(zathura, 
zathura->page_cache.cache[i]);
+     g_object_get(G_OBJECT(page_widget), "last-view", &view_time, NULL);
+
+     if (view_time < lru_view_time) {
+       lru_view_time = view_time;
+       lru_index = i;
+     }
+   }
+
+   
zathura_page_widget_update_surface(ZATHURA_PAGE(zathura_page_get_widget(zathura,
 zathura->page_cache.cache[lru_index])), NULL);
+   girara_debug("Invalidated page %d at cache index %ld\n", 
zathura->page_cache.cache[lru_index]->index + 1, lru_index);
+
+   return lru_index;
+}
+
+void
+zathura_page_cache_add(zathura_t* zathura, zathura_page_t* page)
+{
+  g_return_if_fail(zathura != NULL);
+
+  static unsigned int num_cached_pages = 0;
+
+  if (zathura_page_cache_is_cached(zathura, page)) {
+    return;
+  }
+
+  if (num_cached_pages == zathura->page_cache.size) {
+    ssize_t idx = zathura_page_cache_lru_invalidate(zathura);
+
+    if (idx == -1) {
+      return;
+    }
+
+    zathura->page_cache.cache[idx] = page;
+    girara_debug("Page %d is cached at cache index %ld\n", page->index + 1, 
idx);
+    return;
+  }
+
+  zathura->page_cache.cache[num_cached_pages++] = page;
+  girara_debug("Page %d is cached at cache index %d\n", page->index + 1, 
num_cached_pages - 1);
+  return;
+}
diff --git a/zathura.h b/zathura.h
index 1520d78..77a6552 100644
--- a/zathura.h
+++ b/zathura.h
@@ -13,6 +13,8 @@
 #include <gtk/gtkx.h>
 #endif
 
+#define ZATHURA_PAGE_CACHE_DEFAULT_SIZE                15
+
 enum { NEXT, PREVIOUS, LEFT, RIGHT, UP, DOWN, BOTTOM, TOP, HIDE, HIGHLIGHT,
   DELETE_LAST_WORD, DELETE_LAST_CHAR, DEFAULT, ERROR, WARNING, NEXT_GROUP,
   PREVIOUS_GROUP, ZOOM_IN, ZOOM_OUT, ZOOM_ORIGINAL, ZOOM_SPECIFIC, FORWARD,
@@ -152,6 +154,14 @@ struct zathura_s
     gchar* file_path; /**< Save file path */
     gchar* password; /**< Save password */
   } file_monitor;
+
+  /**
+   * The page cache
+   */
+  struct {
+    zathura_page_t** cache;
+    unsigned int size;
+  } page_cache;
 };
 
 /**
@@ -366,5 +376,12 @@ void zathura_jumplist_add(zathura_t* zathura);
  */
 void zathura_jumplist_append_jump(zathura_t* zathura);
 
+/**
+ * Add a page to the page cache
+ *
+ * @param zathura The zathura session
+ * @param page The page to be cached
+ */
+void zathura_page_cache_add(zathura_t* zathura, zathura_page_t* page);
 
 #endif // ZATHURA_H
diff --git a/zathurarc.5.rst b/zathurarc.5.rst
index f317aa6..f1ee9c6 100644
--- a/zathurarc.5.rst
+++ b/zathurarc.5.rst
@@ -530,20 +530,14 @@ The page padding defines the gap in pixels between each 
rendered page.
 * Value type: Integer
 * Default value: 1
 
-page-store-threshold
-^^^^^^^^^^^^^^^^^^^^
-Pages that are not visible get unloaded after some time. Every page that has 
not
-been visible for page-store-treshold seconds will be unloaded.
-
-* Value type: Integer
-* Default value: 30
-
-page-store-interval
-^^^^^^^^^^^^^^^^^^^
-Defines the amount of seconds between the check to unload invisible pages.
+page-cache-size
+^^^^^^^^^^^^^^^
+Defines the maximum number of pages that could be kept in the page cache. When
+the cache becomes full and a new page needs to be cached, the least recently
+viewed page will be evicted from the cache to make room for the new one.
 
 * Value type: Integer
-* Default value: 30
+* Default value: 15
 
 pages-per-row
 ^^^^^^^^^^^^^
-- 
1.7.10.4

_______________________________________________
zathura mailing list
zathura@lists.pwmt.org
http://lists.pwmt.org/mailman/listinfo/zathura

Reply via email to