diff options
Diffstat (limited to 'libsylph/virtual.c')
-rw-r--r-- | libsylph/virtual.c | 243 |
1 files changed, 230 insertions, 13 deletions
diff --git a/libsylph/virtual.c b/libsylph/virtual.c index 985215ee..5a2778d7 100644 --- a/libsylph/virtual.c +++ b/libsylph/virtual.c @@ -42,18 +42,43 @@ #include "utils.h" typedef struct _VirtualSearchInfo VirtualSearchInfo; +typedef struct _SearchCacheInfo SearchCacheInfo; struct _VirtualSearchInfo { FilterRule *rule; GSList *mlist; + GHashTable *search_cache_table; + FILE *fp; +}; + +struct _SearchCacheInfo { + FolderItem *folder; + guint msgnum; + off_t size; + time_t mtime; + MsgFlags flags; +}; + +enum +{ + SCACHE_NOT_EXIST = 0, + SCACHE_MATCHED = 1, + SCACHE_NOT_MATCHED = 2 }; static void virtual_folder_init (Folder *folder, const gchar *name, const gchar *path); -static GSList *virtual_search_folder (FilterRule *rule, - FolderItem *item); +static GHashTable *virtual_read_search_cache + (FolderItem *item); +static void virtual_write_search_cache (FILE *fp, + FolderItem *item, + MsgInfo *msginfo, + gint matched); + +static GSList *virtual_search_folder (VirtualSearchInfo *info, + FolderItem *item); static gboolean virtual_search_recursive_func (GNode *node, gpointer data); @@ -137,7 +162,148 @@ static void virtual_folder_init(Folder *folder, const gchar *name, folder_local_folder_init(folder, name, path); } -static GSList *virtual_search_folder(FilterRule *rule, FolderItem *item) +guint sinfo_hash(gconstpointer key) +{ + const SearchCacheInfo *sinfo = key; + guint h; + + h = (guint)sinfo->folder; + h ^= sinfo->msgnum; + h ^= (guint)sinfo->size; + h ^= (guint)sinfo->mtime; + h ^= (guint)sinfo->flags.tmp_flags; + h ^= (guint)sinfo->flags.perm_flags; + + /* g_print("path: %s, n = %u, hash = %u\n", + sinfo->folder->path, sinfo->msgnum, h); */ + + return h; +} + +gint sinfo_equal(gconstpointer v, gconstpointer v2) +{ + const SearchCacheInfo *s1 = v; + const SearchCacheInfo *s2 = v2; + + return (s1->folder == s2->folder && s1->msgnum == s2->msgnum && + s1->size == s2->size && s1->mtime == s2->mtime && + s1->flags.tmp_flags == s2->flags.tmp_flags && + s1->flags.perm_flags == s2->flags.perm_flags); +} + +#define READ_CACHE_DATA_INT(n, fp) \ +{ \ + guint32 idata; \ + \ + if (fread(&idata, sizeof(idata), 1, fp) != 1) { \ + g_warning("Cache data is corrupted\n"); \ + fclose(fp); \ + return table; \ + } else \ + n = idata; \ +} + +static GHashTable *virtual_read_search_cache(FolderItem *item) +{ + GHashTable *table; + gchar *path, *file; + FILE *fp; + gchar *id; + + g_return_val_if_fail(item != NULL, NULL); + + path = folder_item_get_path(item); + file = g_strconcat(path, G_DIR_SEPARATOR_S, "search_cache", NULL); + debug_print("reading search cache: %s\n", file); + fp = procmsg_open_data_file(file, 1, DATA_READ, NULL, 0); + g_free(file); + g_free(path); + if (!fp) + return NULL; + + table = g_hash_table_new(sinfo_hash, sinfo_equal); + + while (procmsg_read_cache_data_str(fp, &id) == 0) { + FolderItem *folder; + guint32 msgnum; + off_t size; + time_t mtime; + MsgFlags flags; + gint matched; + SearchCacheInfo *sinfo; + + folder = folder_find_item_from_identifier(id); + g_free(id); + + while (fread(&msgnum, sizeof(msgnum), 1, fp) == 1) { + if (msgnum == 0) + break; + + READ_CACHE_DATA_INT(size, fp); + READ_CACHE_DATA_INT(mtime, fp); + READ_CACHE_DATA_INT(flags.tmp_flags, fp); + READ_CACHE_DATA_INT(flags.perm_flags, fp); + READ_CACHE_DATA_INT(matched, fp); + + if (folder) { + sinfo = g_new(SearchCacheInfo, 1); + sinfo->folder = folder; + sinfo->msgnum = msgnum; + sinfo->size = size; + sinfo->mtime = mtime; + sinfo->flags = flags; + g_hash_table_insert(table, sinfo, + GINT_TO_POINTER(matched)); + } + } + } + + fclose(fp); + return table; +} + +static void virtual_write_search_cache(FILE *fp, FolderItem *item, + MsgInfo *msginfo, gint matched) +{ + if (!item && !msginfo) { + WRITE_CACHE_DATA_INT(0, fp); + return; + } + + if (item) { + gchar *id; + + id = folder_item_get_identifier(item); + if (id) { + WRITE_CACHE_DATA(id, fp); + g_free(id); + } + } + + if (msginfo) { + 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->flags.tmp_flags, fp); + WRITE_CACHE_DATA_INT(msginfo->flags.perm_flags, fp); + WRITE_CACHE_DATA_INT(matched, fp); + } +} + +static void search_cache_free_func(gpointer key, gpointer value, gpointer data) +{ + g_free(key); +} + +static void virtual_search_cache_free(GHashTable *table) +{ + if (table) { + g_hash_table_foreach(table, search_cache_free_func, NULL); + g_hash_table_destroy(table); + } +} + +static GSList *virtual_search_folder(VirtualSearchInfo *info, FolderItem *item) { GSList *match_list = NULL; GSList *mlist; @@ -147,7 +313,8 @@ static GSList *virtual_search_folder(FilterRule *rule, FolderItem *item) gint count = 1, total; GTimeVal tv_prev, tv_cur; - g_return_val_if_fail(rule != NULL, NULL); + g_return_val_if_fail(info != NULL, NULL); + g_return_val_if_fail(info->rule != NULL, NULL); g_return_val_if_fail(item != NULL, NULL); g_return_val_if_fail(item->path != NULL, NULL); @@ -165,7 +332,9 @@ static GSList *virtual_search_folder(FilterRule *rule, FolderItem *item) debug_print("start query search: %s\n", item->path); - full_headers = filter_rule_requires_full_headers(rule); + full_headers = filter_rule_requires_full_headers(info->rule); + + virtual_write_search_cache(info->fp, item, NULL, 0); for (cur = mlist; cur != NULL; cur = cur->next) { MsgInfo *msginfo = (MsgInfo *)cur->data; @@ -181,6 +350,32 @@ static GSList *virtual_search_folder(FilterRule *rule, FolderItem *item) } ++count; + if (info->search_cache_table) { + gint matched; + SearchCacheInfo sinfo; + + sinfo.folder = item; + sinfo.msgnum = msginfo->msgnum; + sinfo.size = msginfo->size; + sinfo.mtime = msginfo->mtime; + sinfo.flags = msginfo->flags; + + matched = (gint)g_hash_table_lookup + (info->search_cache_table, &sinfo); + if (matched == SCACHE_MATCHED) { + match_list = g_slist_prepend + (match_list, msginfo); + cur->data = NULL; + virtual_write_search_cache(info->fp, NULL, + msginfo, matched); + continue; + } else if (matched == SCACHE_NOT_MATCHED) { + virtual_write_search_cache(info->fp, NULL, + msginfo, matched); + continue; + } + } + fltinfo.flags = msginfo->flags; if (full_headers) { gchar *file; @@ -194,14 +389,20 @@ static GSList *virtual_search_folder(FilterRule *rule, FolderItem *item) if (!hlist) continue; - if (filter_match_rule(rule, msginfo, hlist, &fltinfo)) { + if (filter_match_rule(info->rule, msginfo, hlist, &fltinfo)) { match_list = g_slist_prepend(match_list, msginfo); cur->data = NULL; + virtual_write_search_cache(info->fp, NULL, msginfo, + SCACHE_MATCHED); + } else { + virtual_write_search_cache(info->fp, NULL, msginfo, + SCACHE_NOT_MATCHED); } procheader_header_list_destroy(hlist); } + virtual_write_search_cache(info->fp, NULL, NULL, 0); procmsg_msg_list_free(mlist); return g_slist_reverse(match_list); @@ -220,7 +421,7 @@ static gboolean virtual_search_recursive_func(GNode *node, gpointer data) if (!item->path) return FALSE; - mlist = virtual_search_folder(info->rule, item); + mlist = virtual_search_folder(info, item); info->mlist = g_slist_concat(info->mlist, mlist); return FALSE; @@ -233,10 +434,12 @@ static GSList *virtual_get_msg_list(Folder *folder, FolderItem *item, GSList *flist; GSList *cur; FilterRule *rule; - gchar *rule_file; gchar *path; + gchar *rule_file; + gchar *cache_file; FolderItem *target; gint new = 0, unread = 0, total = 0; + VirtualSearchInfo info; g_return_val_if_fail(item != NULL, NULL); g_return_val_if_fail(item->stype == F_VIRTUAL, NULL); @@ -261,16 +464,30 @@ static GSList *virtual_get_msg_list(Folder *folder, FolderItem *item, goto finish; } - if (rule->recursive) { - VirtualSearchInfo info; + info.rule = rule; + info.mlist = NULL; + if (use_cache) + info.search_cache_table = virtual_read_search_cache(item); + else + info.search_cache_table = NULL; - info.rule = rule; - info.mlist = NULL; + path = folder_item_get_path(item); + cache_file = g_strconcat(path, G_DIR_SEPARATOR_S, "search_cache", NULL); + info.fp = procmsg_open_data_file(cache_file, 1, DATA_WRITE, NULL, 0); + g_free(cache_file); + g_free(path); + if (!info.fp) + goto finish; + + if (rule->recursive) { g_node_traverse(target->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, virtual_search_recursive_func, &info); mlist = info.mlist; } else - mlist = virtual_search_folder(rule, target); + mlist = virtual_search_folder(&info, target); + + fclose(info.fp); + virtual_search_cache_free(info.search_cache_table); for (cur = mlist; cur != NULL; cur = cur->next) { MsgInfo *msginfo = (MsgInfo *)cur->data; |