From: Christoph Hohmann Date: Sat, 29 Jun 2002 23:48:43 +0000 (+0000) Subject: merge new cache branch X-Git-Tag: rel_0_8_0~86 X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=commitdiff_plain;h=86fe9a1d569adc9de1cea2278a9284cfe8693241;hp=30555a9068aa58ce67b73d93a79e1196eb552b15 merge new cache branch --- diff --git a/README.claws b/README.claws index 09c97f4da..b399e0667 100644 --- a/README.claws +++ b/README.claws @@ -429,6 +429,40 @@ are hardly noticeable, but deserve mentioning: 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. diff --git a/doc/man/.cvsignore b/doc/man/.cvsignore new file mode 100644 index 000000000..282522db0 --- /dev/null +++ b/doc/man/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/src/msgcache.c b/src/msgcache.c new file mode 100644 index 000000000..4f1c0aac2 --- /dev/null +++ b/src/msgcache.c @@ -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 + +#include + +#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 index 000000000..a6b69ea16 --- /dev/null +++ b/src/msgcache.h @@ -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 + +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