merge new cache branch
authorChristoph Hohmann <reboot@gmx.ch>
Sat, 29 Jun 2002 23:48:43 +0000 (23:48 +0000)
committerChristoph Hohmann <reboot@gmx.ch>
Sat, 29 Jun 2002 23:48:43 +0000 (23:48 +0000)
README.claws
doc/man/.cvsignore [new file with mode: 0644]
src/msgcache.c [new file with mode: 0644]
src/msgcache.h [new file with mode: 0644]

index 09c97f4..b399e06 100644 (file)
@@ -429,6 +429,40 @@ are hardly noticeable, but deserve mentioning:
     Another example for the Sylpheed mailing list (not claws!) is:
     \[sylpheed:[0-9]{5}\]
 
     Another example for the Sylpheed mailing list (not claws!) is:
     \[sylpheed:[0-9]{5}\]
 
+* new cache
+    New cache is  a new data cache structure for sylpheed, that will
+    solve many of the problems sylpheed currently has with updates to
+    flags.  But you will also notice a large speed gain when you open
+    these folders. 
+
+    New cache uses two new configuration parameters that can be
+    adjusted in ~/.sylpheed/sylpheedrc (no gui for them available yet).
+
+    cache_max_mem_usage                (default: 4096)
+       the maximum kB of memory sylpheed should use.
+       It will try to keep the memory usage below this
+       value, but it will always use the assigned
+       amount of memory for speed gain.
+
+    cache_min_keep_time                (default: 15)
+       the minimum time in minutes sylpheed will keep
+       the folder cache in memory. If a cache is more
+       recent than this time it will not be freed even
+       if the memory usage is above the maximum. You
+       should probably set this value higher than your
+       mail check interval. Otherwise the cache will
+       always be freed between checks even if the folder
+       is accessed on every check, which will cause much
+       disk IO.
+
+    The check if memory can be freed is currently done after the
+    active folder has been changed or whenever a new cache is read,
+    i.e. triggered by mail incorporation.
+
+    New mails in MH folders are not detected automatically like before,
+    when you enter the folder. You have to update the folder manually,
+    or activate the auto update setting in the options.
+
 There are a lot more options. If you find one, don't hesitate to
 mention it.
 
 There are a lot more options. If you find one, don't hesitate to
 mention it.
 
diff --git a/doc/man/.cvsignore b/doc/man/.cvsignore
new file mode 100644 (file)
index 0000000..282522d
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/src/msgcache.c b/src/msgcache.c
new file mode 100644 (file)
index 0000000..4f1c0aa
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "defs.h"
+
+#include <glib.h>
+
+#include <time.h>
+
+#include "intl.h"
+#include "msgcache.h"
+#include "utils.h"
+#include "procmsg.h"
+
+struct _MsgCache {
+       GHashTable      *msgnum_table;
+       guint            memusage;
+       time_t           last_access;
+};
+
+MsgCache *msgcache_new()
+{
+       MsgCache *cache;
+       
+       cache = g_new0(MsgCache, 1),
+       cache->msgnum_table = g_hash_table_new(NULL, NULL);
+       cache->last_access = time(NULL);
+
+       return cache;
+}
+
+static gboolean msgcache_msginfo_free_func(gpointer num, gpointer msginfo, gpointer user_data)
+{
+       procmsg_msginfo_free((MsgInfo *)msginfo);
+       return TRUE;
+}                                                                                        
+
+void msgcache_destroy(MsgCache *cache)
+{
+       g_return_if_fail(cache != NULL);
+
+       g_hash_table_foreach_remove(cache->msgnum_table, msgcache_msginfo_free_func, NULL);
+       g_hash_table_destroy(cache->msgnum_table);
+       g_free(cache);
+}
+
+void msgcache_add_msg(MsgCache *cache, MsgInfo *msginfo) 
+{
+       MsgInfo *newmsginfo;
+
+       g_return_if_fail(cache != NULL);
+       g_return_if_fail(msginfo != NULL);
+
+       newmsginfo = procmsg_msginfo_new_ref(msginfo);
+       g_hash_table_insert(cache->msgnum_table, GINT_TO_POINTER(msginfo->msgnum), newmsginfo);
+       cache->memusage += procmsg_msginfo_memusage(msginfo);
+       cache->last_access = time(NULL);
+
+       debug_print(_("Cache size: %d messages, %d byte\n"), g_hash_table_size(cache->msgnum_table), cache->memusage);
+}
+
+void msgcache_remove_msg(MsgCache *cache, guint msgnum)
+{
+       MsgInfo *msginfo;
+
+       g_return_if_fail(cache != NULL);
+       g_return_if_fail(msgnum > 0);
+
+       msginfo = (MsgInfo *) g_hash_table_lookup(cache->msgnum_table, GINT_TO_POINTER(msgnum));
+       if(!msginfo)
+               return;
+
+       cache->memusage -= procmsg_msginfo_memusage(msginfo);
+       procmsg_msginfo_free(msginfo);
+       g_hash_table_remove(cache->msgnum_table, GINT_TO_POINTER(msgnum));
+       cache->last_access = time(NULL);
+
+       debug_print(_("Cache size: %d messages, %d byte\n"), g_hash_table_size(cache->msgnum_table), cache->memusage);
+}
+
+void msgcache_update_msg(MsgCache *cache, MsgInfo *msginfo)
+{
+       MsgInfo *oldmsginfo, *newmsginfo;
+       
+       g_return_if_fail(cache != NULL);
+       g_return_if_fail(msginfo != NULL);
+
+       oldmsginfo = g_hash_table_lookup(cache->msgnum_table, GINT_TO_POINTER(msginfo->msgnum));
+       if(msginfo) {
+               g_hash_table_remove(cache->msgnum_table, GINT_TO_POINTER(oldmsginfo->msgnum));
+               procmsg_msginfo_free(oldmsginfo);
+       }
+       cache->memusage -= procmsg_msginfo_memusage(oldmsginfo);
+
+       newmsginfo = procmsg_msginfo_new_ref(msginfo);
+       g_hash_table_insert(cache->msgnum_table, GINT_TO_POINTER(msginfo->msgnum), newmsginfo);
+       cache->memusage += procmsg_msginfo_memusage(newmsginfo);
+       cache->last_access = time(NULL);
+       
+       debug_print(_("Cache size: %d messages, %d byte\n"), g_hash_table_size(cache->msgnum_table), cache->memusage);
+
+       return;
+}
+
+static gint msgcache_read_cache_data_str(FILE *fp, gchar **str)
+{
+       gchar buf[BUFFSIZE];
+       gint ret = 0;
+       size_t len;
+
+       if (fread(&len, sizeof(len), 1, fp) == 1) {
+               if (len < 0)
+                       ret = -1;
+               else {
+                       gchar *tmp = NULL;
+
+                       while (len > 0) {
+                               size_t size = MIN(len, BUFFSIZE - 1);
+
+                               if (fread(buf, size, 1, fp) != 1) {
+                                       ret = -1;
+                                       if (tmp) g_free(tmp);
+                                       *str = NULL;
+                                       break;
+                               }
+
+                               buf[size] = '\0';
+                               if (tmp) {
+                                       *str = g_strconcat(tmp, buf, NULL);
+                                       g_free(tmp);
+                                       tmp = *str;
+                               } else
+                                       tmp = *str = g_strdup(buf);
+
+                               len -= size;
+                       }
+               }
+       } else
+               ret = -1;
+
+       if (ret < 0)
+               g_warning(_("Cache data is corrupted\n"));
+
+       return ret;
+}
+
+
+#define READ_CACHE_DATA(data, fp) \
+{ \
+       if (msgcache_read_cache_data_str(fp, &data) < 0) { \
+               procmsg_msginfo_free(msginfo); \
+               error = TRUE; \
+               break; \
+       } \
+}
+
+#define READ_CACHE_DATA_INT(n, fp) \
+{ \
+       if (fread(&n, sizeof(n), 1, fp) != 1) { \
+               g_warning(_("Cache data is corrupted\n")); \
+               procmsg_msginfo_free(msginfo); \
+               error = TRUE; \
+               break; \
+       } \
+}
+
+MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
+{
+       MsgCache *cache;
+       FILE *fp;
+       MsgInfo *msginfo;
+/*     MsgFlags default_flags; */
+       gchar file_buf[BUFFSIZE];
+       gint ver;
+       guint num;
+       gboolean error = FALSE;
+
+       g_return_val_if_fail(cache_file != NULL, NULL);
+       g_return_val_if_fail(item != NULL, NULL);
+
+       if ((fp = fopen(cache_file, "r")) == NULL) {
+               debug_print(_("\tNo cache file\n"));
+               return NULL;
+       }
+       setvbuf(fp, file_buf, _IOFBF, sizeof(file_buf));
+
+       debug_print(_("\tReading message cache from %s...\n"), cache_file);
+
+       /* compare cache version */
+       if (fread(&ver, sizeof(ver), 1, fp) != 1 ||
+           CACHE_VERSION != ver) {
+               debug_print(_("Cache version is different. Discarding it.\n"));
+               fclose(fp);
+               return NULL;
+       }
+
+       cache = msgcache_new();
+
+       g_hash_table_freeze(cache->msgnum_table);
+
+       while (fread(&num, sizeof(num), 1, fp) == 1) {
+               msginfo = procmsg_msginfo_new();
+               msginfo->msgnum = num;
+               READ_CACHE_DATA_INT(msginfo->size, fp);
+               READ_CACHE_DATA_INT(msginfo->mtime, fp);
+               READ_CACHE_DATA_INT(msginfo->date_t, fp);
+               READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
+
+               READ_CACHE_DATA(msginfo->fromname, fp);
+
+               READ_CACHE_DATA(msginfo->date, fp);
+               READ_CACHE_DATA(msginfo->from, fp);
+               READ_CACHE_DATA(msginfo->to, fp);
+               READ_CACHE_DATA(msginfo->cc, fp);
+               READ_CACHE_DATA(msginfo->newsgroups, fp);
+               READ_CACHE_DATA(msginfo->subject, fp);
+               READ_CACHE_DATA(msginfo->msgid, fp);
+               READ_CACHE_DATA(msginfo->inreplyto, fp);
+               READ_CACHE_DATA(msginfo->references, fp);
+               READ_CACHE_DATA(msginfo->xref, fp);
+
+/*
+               MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags);
+               MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags);
+*/
+               msginfo->folder = item;
+
+               g_hash_table_insert(cache->msgnum_table, GINT_TO_POINTER(msginfo->msgnum), msginfo);
+               cache->memusage += procmsg_msginfo_memusage(msginfo);
+       }
+       fclose(fp);
+
+       if(error) {
+               g_hash_table_thaw(cache->msgnum_table);
+               msgcache_destroy(cache);
+               return NULL;
+       }
+
+       cache->last_access = time(NULL);
+       g_hash_table_thaw(cache->msgnum_table);
+
+       debug_print(_("done. (%d items read)\n"), g_hash_table_size(cache->msgnum_table));
+       debug_print(_("Cache size: %d messages, %d byte\n"), g_hash_table_size(cache->msgnum_table), cache->memusage);
+
+       return cache;
+}
+
+void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
+{
+       FILE *fp;
+       MsgInfo *msginfo;
+       MsgPermFlags perm_flags;
+       gint ver;
+       guint num;
+
+       if ((fp = fopen(mark_file, "r")) == NULL) {
+               debug_print(_("Mark file not found.\n"));
+               return;
+       } else if (fread(&ver, sizeof(ver), 1, fp) != 1 || MARK_VERSION != ver) {
+               debug_print(_("Mark version is different (%d != %d). "
+                             "Discarding it.\n"), ver, MARK_VERSION);
+       } else {
+               debug_print(_("\tReading message marks from %s...\n"), mark_file);
+
+               while (fread(&num, sizeof(num), 1, fp) == 1) {
+                       if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) break;
+
+                       msginfo = g_hash_table_lookup(cache->msgnum_table, GUINT_TO_POINTER(num));
+                       if(msginfo) {
+                               msginfo->flags.perm_flags = perm_flags;
+                       }
+               }
+       }
+       fclose(fp);
+}
+
+#define WRITE_CACHE_DATA_INT(n, fp) \
+       fwrite(&n, sizeof(n), 1, fp)
+
+#define WRITE_CACHE_DATA(data, fp) \
+{ \
+       gint len; \
+ \
+       if (data == NULL || (len = strlen(data)) == 0) { \
+               len = 0; \
+               WRITE_CACHE_DATA_INT(len, fp); \
+       } else { \
+               len = strlen(data); \
+               WRITE_CACHE_DATA_INT(len, fp); \
+               fwrite(data, len, 1, fp); \
+       } \
+}
+
+void msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
+{
+       MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
+
+       WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
+       WRITE_CACHE_DATA_INT(msginfo->size, fp);
+       WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
+       WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
+       WRITE_CACHE_DATA_INT(flags, fp);
+
+       WRITE_CACHE_DATA(msginfo->fromname, fp);
+
+       WRITE_CACHE_DATA(msginfo->date, fp);
+       WRITE_CACHE_DATA(msginfo->from, fp);
+       WRITE_CACHE_DATA(msginfo->to, fp);
+       WRITE_CACHE_DATA(msginfo->cc, fp);
+       WRITE_CACHE_DATA(msginfo->newsgroups, fp);
+       WRITE_CACHE_DATA(msginfo->subject, fp);
+       WRITE_CACHE_DATA(msginfo->msgid, fp);
+       WRITE_CACHE_DATA(msginfo->inreplyto, fp);
+       WRITE_CACHE_DATA(msginfo->references, fp);
+       WRITE_CACHE_DATA(msginfo->xref, fp);
+}
+
+static void msgcache_write_flags(MsgInfo *msginfo, FILE *fp)
+{
+       MsgPermFlags flags = msginfo->flags.perm_flags;
+
+       WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
+       WRITE_CACHE_DATA_INT(flags, fp);
+}
+
+struct write_fps
+{
+       FILE *cache_fp;
+       FILE *mark_fp;
+};
+
+static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data)
+{
+       MsgInfo *msginfo;
+       struct write_fps *write_fps;
+       
+       msginfo = (MsgInfo *)value;
+       write_fps = user_data;
+
+       msgcache_write_cache(msginfo, write_fps->cache_fp);
+       msgcache_write_flags(msginfo, write_fps->mark_fp);
+}
+
+gint msgcache_write(const gchar *cache_file, const gchar *mark_file, MsgCache *cache)
+{
+       FILE *fp;
+       struct write_fps write_fps;
+       gint ver;
+
+       g_return_val_if_fail(cache_file != NULL, -1);
+       g_return_val_if_fail(mark_file != NULL, -1);
+       g_return_val_if_fail(cache != NULL, -1);
+
+       debug_print(_("\tWriting message cache to %s and %s...\n"), cache_file, mark_file);
+
+       if ((fp = fopen(cache_file, "w")) == NULL) {
+               FILE_OP_ERROR(cache_file, "fopen");
+               return -1;
+       }
+       if (change_file_mode_rw(fp, cache_file) < 0)
+               FILE_OP_ERROR(cache_file, "chmod");
+
+       ver = CACHE_VERSION;
+       WRITE_CACHE_DATA_INT(ver, fp);  
+       write_fps.cache_fp = fp;
+
+       if ((fp = fopen(mark_file, "w")) == NULL) {
+               FILE_OP_ERROR(mark_file, "fopen");
+               fclose(write_fps.cache_fp);
+               return -1;
+       }
+
+       ver = MARK_VERSION;
+       WRITE_CACHE_DATA_INT(ver, fp);
+       write_fps.mark_fp = fp;
+
+       g_hash_table_foreach(cache->msgnum_table, msgcache_write_func, (gpointer)&write_fps);
+
+       fclose(write_fps.cache_fp);
+       fclose(write_fps.mark_fp);
+
+       cache->last_access = time(NULL);
+
+       debug_print(_("done.\n"));
+}
+
+MsgInfo *msgcache_get_msg(MsgCache *cache, guint num)
+{
+       MsgInfo *msginfo;
+
+       g_return_val_if_fail(cache != NULL, NULL);
+
+       msginfo = g_hash_table_lookup(cache->msgnum_table, GINT_TO_POINTER(num));
+       if(!msginfo)
+               return NULL;
+       cache->last_access = time(NULL);
+       
+       return procmsg_msginfo_new_ref(msginfo);
+}
+
+static void msgcache_get_msg_list_func(gpointer key, gpointer value, gpointer user_data)
+{
+       GSList **listptr = user_data;
+       MsgInfo *msginfo = value;
+
+       *listptr = g_slist_prepend(*listptr, procmsg_msginfo_new_ref(msginfo));
+}
+
+GSList *msgcache_get_msg_list(MsgCache *cache)
+{
+       GSList *msg_list = NULL;
+
+       g_return_val_if_fail(cache != NULL, NULL);
+
+       g_hash_table_foreach((GHashTable *)cache->msgnum_table, msgcache_get_msg_list_func, (gpointer)&msg_list);       
+       cache->last_access = time(NULL);
+
+       return msg_list;
+}
+
+time_t msgcache_get_last_access_time(MsgCache *cache)
+{
+       g_return_val_if_fail(cache != NULL, 0);
+       
+       return cache->last_access;
+}
+
+gint msgcache_get_memory_usage(MsgCache *cache)
+{
+       g_return_val_if_fail(cache != NULL, 0);
+
+       return cache->memusage;
+}
diff --git a/src/msgcache.h b/src/msgcache.h
new file mode 100644 (file)
index 0000000..a6b69ea
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2001 Hiroyuki Yamamoto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MSGCACHE_H__
+#define __MSGCACHE_H__
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+
+typedef struct _MsgCache MsgCache;
+
+#include "procmsg.h"
+#include "folder.h"
+
+MsgCache   *msgcache_new                       ();
+void       msgcache_destroy                    (MsgCache *cache);
+MsgCache   *msgcache_read_cache                        (FolderItem *item, const gchar *cache_file);
+void       msgcache_read_mark                  (MsgCache *cache, const gchar *mark_file);
+gint       msgcache_write                      (const gchar *cache_file, const gchar *mark_file, MsgCache *cache);
+void       msgcache_add_msg                    (MsgCache *cache, MsgInfo *msginfo);
+void       msgcache_remove_msg                 (MsgCache *cache, guint num);
+void       msgcache_update_msg                 (MsgCache *cache, MsgInfo *msginfo);
+MsgInfo           *msgcache_get_msg                    (MsgCache *cache, guint num);
+GSList    *msgcache_get_msg_list               (MsgCache *cache);
+time_t     msgcache_get_last_access_time       (MsgCache *cache);
+gint       msgcache_get_memory_usage           (MsgCache *cache);
+
+#endif