mutt stable branch with some hacks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a pattern_cache_t to speed up a few repeated matches.

Vincent Lefèvre reported experiencing an index display performance
issue. This occurred with messages containing many recipients. He
had many index color lines containing ~l. The ~l ended up being run
over and over on these messages, resulting in a noticable slowdown
displaying the index.

This patch adds caching for just a few of the pattern operations (~l,
~u, ~p, ~P) that are potentially expensive and also don't have
arguments. The caching is only enabled for operations repeatedly
matching against the same message: color, hooks, scoring.

The caching is fairly targeted, but isn't that invasive or
complicated.

+124 -25
+8 -4
curs_main.c
··· 376 376 377 377 if (mutt_pattern_exec (ctx->limit_pattern, 378 378 MUTT_MATCH_FULL_ADDRESS, 379 - ctx, ctx->hdrs[j])) 379 + ctx, ctx->hdrs[j], NULL)) 380 380 { 381 381 assert (ctx->vcount < ctx->msgcount); 382 382 ctx->hdrs[j]->virtual = ctx->vcount; ··· 2441 2441 void mutt_set_header_color (CONTEXT *ctx, HEADER *curhdr) 2442 2442 { 2443 2443 COLOR_LINE *color; 2444 + pattern_cache_t cache; 2444 2445 2445 2446 if (!curhdr) 2446 2447 return; 2447 2448 2449 + memset (&cache, 0, sizeof (cache)); 2450 + 2448 2451 for (color = ColorIndexList; color; color = color->next) 2449 - if (mutt_pattern_exec (color->color_pattern, MUTT_MATCH_FULL_ADDRESS, ctx, curhdr)) 2450 - { 2452 + if (mutt_pattern_exec (color->color_pattern, MUTT_MATCH_FULL_ADDRESS, ctx, curhdr, 2453 + &cache)) 2454 + { 2451 2455 curhdr->pair = color->pair; 2452 2456 return; 2453 - } 2457 + } 2454 2458 curhdr->pair = ColorDefs[MT_COLOR_NORMAL]; 2455 2459 }
+11 -2
hook.c
··· 360 360 { 361 361 BUFFER err, token; 362 362 HOOK *hook; 363 + pattern_cache_t cache; 363 364 364 365 current_hook_type = type; 365 366 ··· 367 368 err.dsize = STRING; 368 369 err.data = safe_malloc (err.dsize); 369 370 mutt_buffer_init (&token); 371 + memset (&cache, 0, sizeof (cache)); 370 372 for (hook = Hooks; hook; hook = hook->next) 371 373 { 372 374 if(!hook->command) 373 375 continue; 374 376 375 377 if (hook->type & type) 376 - if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not) 378 + if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not) 379 + { 377 380 if (mutt_parse_rc_line (hook->command, &token, &err) != 0) 378 381 { 379 382 FREE (&token.data); ··· 384 387 385 388 return; 386 389 } 390 + /* Executing arbitrary commands could affect the pattern results, 391 + * so the cache has to be wiped */ 392 + memset (&cache, 0, sizeof (cache)); 393 + } 387 394 } 388 395 FREE (&token.data); 389 396 FREE (&err.data); ··· 395 402 mutt_addr_hook (char *path, size_t pathlen, int type, CONTEXT *ctx, HEADER *hdr) 396 403 { 397 404 HOOK *hook; 405 + pattern_cache_t cache; 398 406 407 + memset (&cache, 0, sizeof (cache)); 399 408 /* determine if a matching hook exists */ 400 409 for (hook = Hooks; hook; hook = hook->next) 401 410 { ··· 403 412 continue; 404 413 405 414 if (hook->type & type) 406 - if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not) 415 + if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not) 407 416 { 408 417 mutt_make_string (path, pathlen, hook->command, ctx, hdr); 409 418 return 0;
+17
mutt.h
··· 874 874 } p; 875 875 } pattern_t; 876 876 877 + /* This is used when a message is repeatedly pattern matched against. 878 + * e.g. for color, scoring, hooks. It caches a few of the potentially slow 879 + * operations. 880 + * Each entry has a value of 0 = unset, 1 = false, 2 = true 881 + */ 882 + typedef struct 883 + { 884 + int list_all; /* ^~l */ 885 + int list_one; /* ~l */ 886 + int sub_all; /* ^~u */ 887 + int sub_one; /* ~u */ 888 + int pers_recip_all; /* ^~p */ 889 + int pers_recip_one; /* ~p */ 890 + int pers_from_all; /* ^~P */ 891 + int pers_from_one; /* ~P */ 892 + } pattern_cache_t; 893 + 877 894 /* ACL Rights */ 878 895 enum 879 896 {
+84 -17
pattern.c
··· 995 995 } 996 996 997 997 static int 998 - perform_and (pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr) 998 + perform_and (pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr, pattern_cache_t *cache) 999 999 { 1000 1000 for (; pat; pat = pat->next) 1001 - if (mutt_pattern_exec (pat, flags, ctx, hdr) <= 0) 1001 + if (mutt_pattern_exec (pat, flags, ctx, hdr, cache) <= 0) 1002 1002 return 0; 1003 1003 return 1; 1004 1004 } 1005 1005 1006 1006 static int 1007 - perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr) 1007 + perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr, pattern_cache_t *cache) 1008 1008 { 1009 1009 for (; pat; pat = pat->next) 1010 - if (mutt_pattern_exec (pat, flags, ctx, hdr) > 0) 1010 + if (mutt_pattern_exec (pat, flags, ctx, hdr, cache) > 0) 1011 1011 return 1; 1012 1012 return 0; 1013 1013 } ··· 1094 1094 return 0; 1095 1095 h = t->message; 1096 1096 if(h) 1097 - if(mutt_pattern_exec(pat, flags, ctx, h)) 1097 + if(mutt_pattern_exec(pat, flags, ctx, h, NULL)) 1098 1098 return 1; 1099 1099 1100 1100 if(up && (a=match_threadcomplete(pat, flags, ctx, t->parent,1,1,1,0))) ··· 1108 1108 return 0; 1109 1109 } 1110 1110 1111 - /* flags 1112 - MUTT_MATCH_FULL_ADDRESS match both personal and machine address */ 1111 + 1112 + /* Sets a value in the pattern_cache_t cache entry. 1113 + * Normalizes the "true" value to 2. */ 1114 + static void set_pattern_cache_value (int *cache_entry, int value) 1115 + { 1116 + *cache_entry = value ? 2 : 1; 1117 + } 1118 + 1119 + /* Returns 1 if the cache value is set and has a true value. 1120 + * 0 otherwise (even if unset!) */ 1121 + static int get_pattern_cache_value (int cache_entry) 1122 + { 1123 + return cache_entry == 2; 1124 + } 1125 + 1126 + static int is_pattern_cache_set (int cache_entry) 1127 + { 1128 + return cache_entry != 0; 1129 + } 1130 + 1131 + 1132 + /* 1133 + * flags: MUTT_MATCH_FULL_ADDRESS - match both personal and machine address 1134 + * cache: For repeated matches against the same HEADER, passing in non-NULL will 1135 + * store some of the cacheable pattern matches in this structure. */ 1113 1136 int 1114 - mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h) 1137 + mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h, 1138 + pattern_cache_t *cache) 1115 1139 { 1140 + int result; 1141 + int *cache_entry; 1142 + 1116 1143 switch (pat->op) 1117 1144 { 1118 1145 case MUTT_AND: 1119 - return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0)); 1146 + return (pat->not ^ (perform_and (pat->child, flags, ctx, h, cache) > 0)); 1120 1147 case MUTT_OR: 1121 - return (pat->not ^ (perform_or (pat->child, flags, ctx, h) > 0)); 1148 + return (pat->not ^ (perform_or (pat->child, flags, ctx, h, cache) > 0)); 1122 1149 case MUTT_THREAD: 1123 1150 return (pat->not ^ match_threadcomplete(pat->child, flags, ctx, h->thread, 1, 1, 1, 1)); 1124 1151 case MUTT_ALL: ··· 1198 1225 return (pat->not ^ match_adrlist (pat, flags & MUTT_MATCH_FULL_ADDRESS, 1199 1226 2, h->env->to, h->env->cc)); 1200 1227 case MUTT_LIST: /* known list, subscribed or not */ 1201 - return (pat->not ^ mutt_is_list_cc (pat->alladdr, h->env->to, h->env->cc)); 1228 + if (cache) 1229 + { 1230 + cache_entry = pat->alladdr ? &cache->list_all : &cache->list_one; 1231 + if (!is_pattern_cache_set (*cache_entry)) 1232 + set_pattern_cache_value (cache_entry, 1233 + mutt_is_list_cc (pat->alladdr, h->env->to, h->env->cc)); 1234 + result = get_pattern_cache_value (*cache_entry); 1235 + } 1236 + else 1237 + result = mutt_is_list_cc (pat->alladdr, h->env->to, h->env->cc); 1238 + return (pat->not ^ result); 1202 1239 case MUTT_SUBSCRIBED_LIST: 1203 - return (pat->not ^ mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc)); 1240 + if (cache) 1241 + { 1242 + cache_entry = pat->alladdr ? &cache->sub_all : &cache->sub_one; 1243 + if (!is_pattern_cache_set (*cache_entry)) 1244 + set_pattern_cache_value (cache_entry, 1245 + mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc)); 1246 + result = get_pattern_cache_value (*cache_entry); 1247 + } 1248 + else 1249 + result = mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc); 1250 + return (pat->not ^ result); 1204 1251 case MUTT_PERSONAL_RECIP: 1205 - return (pat->not ^ match_user (pat->alladdr, h->env->to, h->env->cc)); 1252 + if (cache) 1253 + { 1254 + cache_entry = pat->alladdr ? &cache->pers_recip_all : &cache->pers_recip_one; 1255 + if (!is_pattern_cache_set (*cache_entry)) 1256 + set_pattern_cache_value (cache_entry, 1257 + match_user (pat->alladdr, h->env->to, h->env->cc)); 1258 + result = get_pattern_cache_value (*cache_entry); 1259 + } 1260 + else 1261 + result = match_user (pat->alladdr, h->env->to, h->env->cc); 1262 + return (pat->not ^ result); 1206 1263 case MUTT_PERSONAL_FROM: 1207 - return (pat->not ^ match_user (pat->alladdr, h->env->from, NULL)); 1264 + if (cache) 1265 + { 1266 + cache_entry = pat->alladdr ? &cache->pers_from_all : &cache->pers_from_one; 1267 + if (!is_pattern_cache_set (*cache_entry)) 1268 + set_pattern_cache_value (cache_entry, 1269 + match_user (pat->alladdr, h->env->from, NULL)); 1270 + result = get_pattern_cache_value (*cache_entry); 1271 + } 1272 + else 1273 + result = match_user (pat->alladdr, h->env->from, NULL); 1274 + return (pat->not ^ result); 1208 1275 case MUTT_COLLAPSED: 1209 1276 return (pat->not ^ (h->collapsed && h->num_hidden > 1)); 1210 1277 case MUTT_CRYPT_SIGN: ··· 1364 1431 Context->hdrs[i]->limited = 0; 1365 1432 Context->hdrs[i]->collapsed = 0; 1366 1433 Context->hdrs[i]->num_hidden = 0; 1367 - if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[i])) 1434 + if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[i], NULL)) 1368 1435 { 1369 1436 Context->hdrs[i]->virtual = Context->vcount; 1370 1437 Context->hdrs[i]->limited = 1; ··· 1380 1447 for (i = 0; i < Context->vcount; i++) 1381 1448 { 1382 1449 mutt_progress_update (&progress, i, -1); 1383 - if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[Context->v2r[i]])) 1450 + if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[Context->v2r[i]], NULL)) 1384 1451 { 1385 1452 switch (op) 1386 1453 { ··· 1540 1607 { 1541 1608 /* remember that we've already searched this message */ 1542 1609 h->searched = 1; 1543 - if ((h->matched = (mutt_pattern_exec (SearchPattern, MUTT_MATCH_FULL_ADDRESS, Context, h) > 0))) 1610 + if ((h->matched = (mutt_pattern_exec (SearchPattern, MUTT_MATCH_FULL_ADDRESS, Context, h, NULL) > 0))) 1544 1611 { 1545 1612 mutt_clear_error(); 1546 1613 if (msg && *msg)
+1 -1
protos.h
··· 406 406 407 407 #define new_pattern() safe_calloc(1, sizeof (pattern_t)) 408 408 409 - int mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h); 409 + int mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h, pattern_cache_t *); 410 410 pattern_t *mutt_pattern_comp (/* const */ char *s, int flags, BUFFER *err); 411 411 void mutt_check_simple (char *s, size_t len, const char *simple); 412 412 void mutt_pattern_free (pattern_t **pat);
+3 -1
score.c
··· 129 129 void mutt_score_message (CONTEXT *ctx, HEADER *hdr, int upd_ctx) 130 130 { 131 131 SCORE *tmp; 132 + pattern_cache_t cache; 132 133 134 + memset (&cache, 0, sizeof (cache)); 133 135 hdr->score = 0; /* in case of re-scoring */ 134 136 for (tmp = Score; tmp; tmp = tmp->next) 135 137 { 136 - if (mutt_pattern_exec (tmp->pat, 0, NULL, hdr) > 0) 138 + if (mutt_pattern_exec (tmp->pat, 0, NULL, hdr, &cache) > 0) 137 139 { 138 140 if (tmp->exact || tmp->val == 9999 || tmp->val == -9999) 139 141 {