changeset 34757:63e3334bcadb v9.1.0256

patch 9.1.0256: Finding autocmd events is inefficient Commit: https://github.com/vim/vim/commit/78d742ab8845578f78039ddd71a6444c6929257c Author: John Marriott <basilisk@internode.on.net> Date: Tue Apr 2 20:26:01 2024 +0200 patch 9.1.0256: Finding autocmd events is inefficient Problem: Finding autocmd events is inefficient Solution: Use binary search to find events, cache last found events, avoid use of strlen(), add SessionWritePost autocmd, fix test_codestyle and avoid endless loop (John Marriott) closes: #14287 Signed-off-by: John Marriott <basilisk@internode.on.net> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Tue, 02 Apr 2024 20:45:03 +0200
parents 317067ddcba2
children 8b2823d22946
files src/autocmd.c src/misc2.c src/proto/misc2.pro src/structs.h src/testdir/test_codestyle.vim src/version.c
diffstat 6 files changed, 312 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -74,153 +74,166 @@ typedef struct AutoPat
     char	    last;		// last pattern for apply_autocmds()
 } AutoPat;
 
-static struct event_name
-{
-    char	*name;	// event name
-    event_T	event;	// event number
-} event_names[] =
-{
-    {"BufAdd",		EVENT_BUFADD},
-    {"BufCreate",	EVENT_BUFADD},
-    {"BufDelete",	EVENT_BUFDELETE},
-    {"BufEnter",	EVENT_BUFENTER},
-    {"BufFilePost",	EVENT_BUFFILEPOST},
-    {"BufFilePre",	EVENT_BUFFILEPRE},
-    {"BufHidden",	EVENT_BUFHIDDEN},
-    {"BufLeave",	EVENT_BUFLEAVE},
-    {"BufNew",		EVENT_BUFNEW},
-    {"BufNewFile",	EVENT_BUFNEWFILE},
-    {"BufRead",		EVENT_BUFREADPOST},
-    {"BufReadCmd",	EVENT_BUFREADCMD},
-    {"BufReadPost",	EVENT_BUFREADPOST},
-    {"BufReadPre",	EVENT_BUFREADPRE},
-    {"BufUnload",	EVENT_BUFUNLOAD},
-    {"BufWinEnter",	EVENT_BUFWINENTER},
-    {"BufWinLeave",	EVENT_BUFWINLEAVE},
-    {"BufWipeout",	EVENT_BUFWIPEOUT},
-    {"BufWrite",	EVENT_BUFWRITEPRE},
-    {"BufWritePost",	EVENT_BUFWRITEPOST},
-    {"BufWritePre",	EVENT_BUFWRITEPRE},
-    {"BufWriteCmd",	EVENT_BUFWRITECMD},
-    {"CmdlineChanged",	EVENT_CMDLINECHANGED},
-    {"CmdlineEnter",	EVENT_CMDLINEENTER},
-    {"CmdlineLeave",	EVENT_CMDLINELEAVE},
-    {"CmdwinEnter",	EVENT_CMDWINENTER},
-    {"CmdwinLeave",	EVENT_CMDWINLEAVE},
-    {"CmdUndefined",	EVENT_CMDUNDEFINED},
-    {"ColorScheme",	EVENT_COLORSCHEME},
-    {"ColorSchemePre",	EVENT_COLORSCHEMEPRE},
-    {"CompleteChanged",	EVENT_COMPLETECHANGED},
-    {"CompleteDone",	EVENT_COMPLETEDONE},
-    {"CompleteDonePre",	EVENT_COMPLETEDONEPRE},
-    {"CursorHold",	EVENT_CURSORHOLD},
-    {"CursorHoldI",	EVENT_CURSORHOLDI},
-    {"CursorMoved",	EVENT_CURSORMOVED},
-    {"CursorMovedI",	EVENT_CURSORMOVEDI},
-    {"DiffUpdated",	EVENT_DIFFUPDATED},
-    {"DirChanged",	EVENT_DIRCHANGED},
-    {"DirChangedPre",	EVENT_DIRCHANGEDPRE},
-    {"EncodingChanged",	EVENT_ENCODINGCHANGED},
-    {"ExitPre",		EVENT_EXITPRE},
-    {"FileEncoding",	EVENT_ENCODINGCHANGED},
-    {"FileAppendPost",	EVENT_FILEAPPENDPOST},
-    {"FileAppendPre",	EVENT_FILEAPPENDPRE},
-    {"FileAppendCmd",	EVENT_FILEAPPENDCMD},
-    {"FileChangedShell",EVENT_FILECHANGEDSHELL},
-    {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST},
-    {"FileChangedRO",	EVENT_FILECHANGEDRO},
-    {"FileReadPost",	EVENT_FILEREADPOST},
-    {"FileReadPre",	EVENT_FILEREADPRE},
-    {"FileReadCmd",	EVENT_FILEREADCMD},
-    {"FileType",	EVENT_FILETYPE},
-    {"FileWritePost",	EVENT_FILEWRITEPOST},
-    {"FileWritePre",	EVENT_FILEWRITEPRE},
-    {"FileWriteCmd",	EVENT_FILEWRITECMD},
-    {"FilterReadPost",	EVENT_FILTERREADPOST},
-    {"FilterReadPre",	EVENT_FILTERREADPRE},
-    {"FilterWritePost",	EVENT_FILTERWRITEPOST},
-    {"FilterWritePre",	EVENT_FILTERWRITEPRE},
-    {"FocusGained",	EVENT_FOCUSGAINED},
-    {"FocusLost",	EVENT_FOCUSLOST},
-    {"FuncUndefined",	EVENT_FUNCUNDEFINED},
-    {"GUIEnter",	EVENT_GUIENTER},
-    {"GUIFailed",	EVENT_GUIFAILED},
-    {"InsertChange",	EVENT_INSERTCHANGE},
-    {"InsertEnter",	EVENT_INSERTENTER},
-    {"InsertLeave",	EVENT_INSERTLEAVE},
-    {"InsertLeavePre",	EVENT_INSERTLEAVEPRE},
-    {"InsertCharPre",	EVENT_INSERTCHARPRE},
-    {"MenuPopup",	EVENT_MENUPOPUP},
-    {"ModeChanged",	EVENT_MODECHANGED},
-    {"OptionSet",	EVENT_OPTIONSET},
-    {"QuickFixCmdPost",	EVENT_QUICKFIXCMDPOST},
-    {"QuickFixCmdPre",	EVENT_QUICKFIXCMDPRE},
-    {"QuitPre",		EVENT_QUITPRE},
-    {"RemoteReply",	EVENT_REMOTEREPLY},
-    {"SafeState",	EVENT_SAFESTATE},
-    {"SafeStateAgain",	EVENT_SAFESTATEAGAIN},
-    {"SessionLoadPost",	EVENT_SESSIONLOADPOST},
-    {"SessionWritePost",EVENT_SESSIONWRITEPOST},
-    {"ShellCmdPost",	EVENT_SHELLCMDPOST},
-    {"ShellFilterPost",	EVENT_SHELLFILTERPOST},
-    {"SigUSR1",		EVENT_SIGUSR1},
-    {"SourceCmd",	EVENT_SOURCECMD},
-    {"SourcePre",	EVENT_SOURCEPRE},
-    {"SourcePost",	EVENT_SOURCEPOST},
-    {"SpellFileMissing",EVENT_SPELLFILEMISSING},
-    {"StdinReadPost",	EVENT_STDINREADPOST},
-    {"StdinReadPre",	EVENT_STDINREADPRE},
-    {"SwapExists",	EVENT_SWAPEXISTS},
-    {"Syntax",		EVENT_SYNTAX},
-    {"TabNew",		EVENT_TABNEW},
-    {"TabClosed",	EVENT_TABCLOSED},
-    {"TabEnter",	EVENT_TABENTER},
-    {"TabLeave",	EVENT_TABLEAVE},
-    {"TermChanged",	EVENT_TERMCHANGED},
-    {"TerminalOpen",	EVENT_TERMINALOPEN},
-    {"TerminalWinOpen", EVENT_TERMINALWINOPEN},
-    {"TermResponse",	EVENT_TERMRESPONSE},
-    {"TermResponseAll",	EVENT_TERMRESPONSEALL},
-    {"TextChanged",	EVENT_TEXTCHANGED},
-    {"TextChangedI",	EVENT_TEXTCHANGEDI},
-    {"TextChangedP",	EVENT_TEXTCHANGEDP},
-    {"TextChangedT",	EVENT_TEXTCHANGEDT},
-    {"User",		EVENT_USER},
-    {"VimEnter",	EVENT_VIMENTER},
-    {"VimLeave",	EVENT_VIMLEAVE},
-    {"VimLeavePre",	EVENT_VIMLEAVEPRE},
-    {"WinNewPre",	EVENT_WINNEWPRE},
-    {"WinNew",		EVENT_WINNEW},
-    {"WinClosed",	EVENT_WINCLOSED},
-    {"WinEnter",	EVENT_WINENTER},
-    {"WinLeave",	EVENT_WINLEAVE},
-    {"WinResized",	EVENT_WINRESIZED},
-    {"WinScrolled",	EVENT_WINSCROLLED},
-    {"VimResized",	EVENT_VIMRESIZED},
-    {"TextYankPost",	EVENT_TEXTYANKPOST},
-    {"VimSuspend",	EVENT_VIMSUSPEND},
-    {"VimResume",	EVENT_VIMRESUME},
-    {NULL,		(event_T)0}
+//
+// special cases:
+// BufNewFile and BufRead are searched for ALOT (especially at startup)
+// so we pre-determine their index into the event_tab[] table for fast access.
+// Keep these values in sync with event_tab[]!
+#define BUFNEWFILE_INDEX 9
+#define BUFREAD_INDEX 10
+
+// must be sorted by the 'value' field because it is used by bsearch()!
+static keyvalue_T event_tab[] = {
+    KEYVALUE_ENTRY(EVENT_BUFADD, "BufAdd"),
+    KEYVALUE_ENTRY(EVENT_BUFADD, "BufCreate"),
+    KEYVALUE_ENTRY(EVENT_BUFDELETE, "BufDelete"),
+    KEYVALUE_ENTRY(EVENT_BUFENTER, "BufEnter"),
+    KEYVALUE_ENTRY(EVENT_BUFFILEPOST, "BufFilePost"),
+    KEYVALUE_ENTRY(EVENT_BUFFILEPRE, "BufFilePre"),
+    KEYVALUE_ENTRY(EVENT_BUFHIDDEN, "BufHidden"),
+    KEYVALUE_ENTRY(EVENT_BUFLEAVE, "BufLeave"),
+    KEYVALUE_ENTRY(EVENT_BUFNEW, "BufNew"),
+    KEYVALUE_ENTRY(EVENT_BUFNEWFILE, "BufNewFile"),	// BUFNEWFILE_INDEX
+    KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufRead"),	// BUFREAD_INDEX
+    KEYVALUE_ENTRY(EVENT_BUFREADCMD, "BufReadCmd"),
+    KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufReadPost"),
+    KEYVALUE_ENTRY(EVENT_BUFREADPRE, "BufReadPre"),
+    KEYVALUE_ENTRY(EVENT_BUFUNLOAD, "BufUnload"),
+    KEYVALUE_ENTRY(EVENT_BUFWINENTER, "BufWinEnter"),
+    KEYVALUE_ENTRY(EVENT_BUFWINLEAVE, "BufWinLeave"),
+    KEYVALUE_ENTRY(EVENT_BUFWIPEOUT, "BufWipeout"),
+    KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWrite"),
+    KEYVALUE_ENTRY(EVENT_BUFWRITECMD, "BufWriteCmd"),
+    KEYVALUE_ENTRY(EVENT_BUFWRITEPOST, "BufWritePost"),
+    KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWritePre"),
+    KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
+    KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
+    KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
+    KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
+    KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
+    KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
+    KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"),
+    KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"),
+    KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"),
+    KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"),
+    KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"),
+    KEYVALUE_ENTRY(EVENT_CURSORHOLD, "CursorHold"),
+    KEYVALUE_ENTRY(EVENT_CURSORHOLDI, "CursorHoldI"),
+    KEYVALUE_ENTRY(EVENT_CURSORMOVED, "CursorMoved"),
+    KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"),
+    KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"),
+    KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"),
+    KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"),
+    KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"),
+    KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"),
+    KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"),
+    KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"),
+    KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"),
+    KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"),
+    KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"),
+    KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"),
+    KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"),
+    KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"),
+    KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"),
+    KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"),
+    KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"),
+    KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"),
+    KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"),
+    KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"),
+    KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"),
+    KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"),
+    KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"),
+    KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"),
+    KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"),
+    KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"),
+    KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"),
+    KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"),
+    KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"),
+    KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"),
+    KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"),
+    KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"),
+    KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"),
+    KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"),
+    KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"),
+    KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"),
+    KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"),
+    KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"),
+    KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"),
+    KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"),
+    KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"),
+    KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"),
+    KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"),
+    KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"),
+    KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"),
+    KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"),
+    KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"),
+    KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"),
+    KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"),
+    KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"),
+    KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"),
+    KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"),
+    KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"),
+    KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"),
+    KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
+    KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
+    KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
+    KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
+    KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
+    KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
+    KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"),
+    KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"),
+    KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"),
+    KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"),
+    KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"),
+    KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"),
+    KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"),
+    KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"),
+    KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"),
+    KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"),
+    KEYVALUE_ENTRY(EVENT_USER, "User"),
+    KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
+    KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"),
+    KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"),
+    KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"),
+    KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"),
+    KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"),
+    KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"),
+    KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"),
+    KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"),
+    KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"),
+    KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"),
+    KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"),
+    KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled")
 };
 
-static AutoPat *first_autopat[NUM_EVENTS] =
-{
+static AutoPat *first_autopat[NUM_EVENTS] = {
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-static AutoPat *last_autopat[NUM_EVENTS] =
-{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static AutoPat *last_autopat[NUM_EVENTS] = {
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL
 };
 
 #define AUGROUP_DEFAULT    (-1)	    // default autocmd group
@@ -266,6 +279,7 @@ static int current_augroup = AUGROUP_DEF
 
 static int au_need_clean = FALSE;   // need to delete marked patterns
 
+static event_T event_name2nr(char_u *start, char_u **end);
 static char_u *event_nr2name(event_T event);
 static int au_get_grouparg(char_u **argp);
 static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags);
@@ -681,24 +695,35 @@ is_aucmd_win(win_T *win)
 event_name2nr(char_u *start, char_u **end)
 {
     char_u	*p;
-    int		i;
-    int		len;
+    keyvalue_T target;
+    keyvalue_T *entry;
+    static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX];
+    static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX];
 
     // the event name ends with end of line, '|', a blank or a comma
     for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p)
 	;
-    for (i = 0; event_names[i].name != NULL; ++i)
-    {
-	len = (int)STRLEN(event_names[i].name);
-	if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
-	    break;
-    }
+
+    target.key = 0;
+    target.value = (char *)start;
+    target.length = (size_t)(p - start);
+
+    // special cases:
+    // BufNewFile and BufRead are searched for ALOT (especially at startup)
+    // so we check for them first.
+    if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0)
+	entry = bufnewfile;
+    else
+    if (cmp_keyvalue_value_ni(&target, bufread) == 0)
+	entry = bufread;
+    else
+	entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
+
     if (*p == ',')
 	++p;
     *end = p;
-    if (event_names[i].name == NULL)
-	return NUM_EVENTS;
-    return event_names[i].event;
+
+    return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key;
 }
 
 /*
@@ -708,11 +733,52 @@ event_name2nr(char_u *start, char_u **en
 event_nr2name(event_T event)
 {
     int	    i;
-
-    for (i = 0; event_names[i].name != NULL; ++i)
-	if (event_names[i].event == event)
-	    return (char_u *)event_names[i].name;
-    return (char_u *)"Unknown";
+#define CACHE_SIZE 12
+    static int cache_tab[CACHE_SIZE];
+    static int cache_last_index = -1;
+
+    if (cache_last_index < 0)
+    {
+	for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i)
+	    cache_tab[i] = -1;
+	cache_last_index = ARRAY_LENGTH(cache_tab) - 1;
+    }
+
+    // first look in the cache
+    // the cache is circular. to search it we start at the most recent entry
+    // and go backwards wrapping around when we get to index 0.
+    for (i = cache_last_index; cache_tab[i] >= 0; )
+    {
+	if ((event_T)event_tab[cache_tab[i]].key == event)
+	    return (char_u *)event_tab[cache_tab[i]].value;
+
+	if (i == 0)
+	    i = ARRAY_LENGTH(cache_tab) - 1;
+	else
+	    --i;
+
+	// are we back at the start?
+	if (i == cache_last_index)
+	    break;
+    }
+
+    // look in the event table itself
+    for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i)
+    {
+	if ((event_T)event_tab[i].key == event)
+	{
+	    // store the found entry in the next position in the cache,
+	    // wrapping around when we get to the maximum index.
+	    if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1)
+		cache_last_index = 0;
+	    else
+		++cache_last_index;
+	    cache_tab[cache_last_index] = i;
+	    break;
+	}
+    }
+
+    return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" : (char_u *)event_tab[i].value;
 }
 
 /*
@@ -806,12 +872,14 @@ au_event_disable(char *what)
 {
     char_u	*new_ei;
     char_u	*save_ei;
-
-    save_ei = vim_strsave(p_ei);
+    size_t	p_ei_len;
+
+    p_ei_len = STRLEN(p_ei);
+    save_ei = vim_strnsave(p_ei, p_ei_len);
     if (save_ei == NULL)
 	return NULL;
 
-    new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
+    new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what));
     if (new_ei == NULL)
     {
 	vim_free(save_ei);
@@ -2774,6 +2842,8 @@ set_context_in_autocmd(
     char_u *
 get_event_name(expand_T *xp UNUSED, int idx)
 {
+    int i;
+
     if (idx < augroups.ga_len)		// First list group names, if wanted
     {
 	if (!include_groups || AUGROUP_NAME(idx) == NULL
@@ -2781,7 +2851,12 @@ get_event_name(expand_T *xp UNUSED, int 
 	    return (char_u *)"";	// skip deleted entries
 	return AUGROUP_NAME(idx);	// return a name
     }
-    return (char_u *)event_names[idx - augroups.ga_len].name;
+
+    i = idx - augroups.ga_len;
+    if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab))
+	return NULL;
+
+    return (char_u *)event_tab[i].value;
 }
 
 /*
@@ -2791,7 +2866,10 @@ get_event_name(expand_T *xp UNUSED, int 
     char_u *
 get_event_name_no_group(expand_T *xp UNUSED, int idx)
 {
-    return (char_u *)event_names[idx].name;
+    if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab))
+	return NULL;
+
+    return (char_u *)event_tab[idx].value;
 }
 
 
@@ -3251,8 +3329,6 @@ f_autocmd_get(typval_T *argvars, typval_
 	// return only the autocmds for the specified event
 	if (dict_has_key(argvars[0].vval.v_dict, "event"))
 	{
-	    int		i;
-
 	    name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE);
 	    if (name == NULL)
 		return;
@@ -3261,16 +3337,20 @@ f_autocmd_get(typval_T *argvars, typval_
 		event_arg = NUM_EVENTS;
 	    else
 	    {
-		for (i = 0; event_names[i].name != NULL; i++)
-		    if (STRICMP(event_names[i].name, name) == 0)
-			break;
-		if (event_names[i].name == NULL)
+		keyvalue_T target;
+		keyvalue_T *entry;
+
+		target.key = 0;
+		target.value = (char *)name;
+		target.length = (int)STRLEN(target.value);
+		entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni);
+		if (entry == NULL)
 		{
 		    semsg(_(e_no_such_event_str), name);
 		    vim_free(name);
 		    return;
 		}
-		event_arg = event_names[i].event;
+		event_arg = (event_T)entry->key;
 	    }
 	    vim_free(name);
 	}
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -3069,3 +3069,45 @@ get_special_pty_type(void)
     return 0;
 #endif
 }
+
+// compare two keyvalue_T structs by case sensitive value
+    int
+cmp_keyvalue_value(const void *a, const void *b)
+{
+    keyvalue_T *kv1 = (keyvalue_T *)a;
+    keyvalue_T *kv2 = (keyvalue_T *)b;
+
+    return STRCMP(kv1->value, kv2->value);
+}
+
+// compare two keyvalue_T structs by value with length
+    int
+cmp_keyvalue_value_n(const void *a, const void *b)
+{
+    keyvalue_T *kv1 = (keyvalue_T *)a;
+    keyvalue_T *kv2 = (keyvalue_T *)b;
+
+    return STRNCMP(kv1->value, kv2->value, MAX(kv1->length, kv2->length));
+}
+
+// compare two keyvalue_T structs by case insensitive value
+    int
+cmp_keyvalue_value_i(const void *a, const void *b)
+{
+    keyvalue_T *kv1 = (keyvalue_T *)a;
+    keyvalue_T *kv2 = (keyvalue_T *)b;
+
+    return STRICMP(kv1->value, kv2->value);
+}
+
+// compare two keyvalue_T structs by case insensitive value
+// with length
+    int
+cmp_keyvalue_value_ni(const void *a, const void *b)
+{
+    keyvalue_T *kv1 = (keyvalue_T *)a;
+    keyvalue_T *kv2 = (keyvalue_T *)b;
+
+    return STRNICMP(kv1->value, kv2->value, MAX(kv1->length, kv2->length));
+}
+
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -61,4 +61,8 @@ int mch_parse_cmd(char_u *cmd, int use_s
 int build_argv_from_string(char_u *cmd, char ***argv, int *argc);
 int build_argv_from_list(list_T *l, char ***argv, int *argc);
 int get_special_pty_type(void);
+int cmp_keyvalue_value(const void *a, const void *b);
+int cmp_keyvalue_value_n(const void *a, const void *b);
+int cmp_keyvalue_value_i(const void *a, const void *b);
+int cmp_keyvalue_value_ni(const void *a, const void *b);
 /* vim: set ft=c : */
--- a/src/structs.h
+++ b/src/structs.h
@@ -5060,3 +5060,18 @@ typedef struct {
     linenr_T	spv_capcol_lnum;    // line number for "cap_col"
 #endif
 } spellvars_T;
+
+// Return the length of a string literal
+#define STRLEN_LITERAL(s) (sizeof(s) - 1)
+
+// Store a key/value pair
+typedef struct
+{
+    int	    key;        // the key
+    char    *value;     // the value string
+    size_t  length;     // length of the value string
+} keyvalue_T;
+
+#define KEYVALUE_ENTRY(k, v) \
+    {(k), (v), STRLEN_LITERAL(v)}
+
--- a/src/testdir/test_codestyle.vim
+++ b/src/testdir/test_codestyle.vim
@@ -7,13 +7,17 @@ def s:ReportError(fname: string, lnum: n
 enddef
 
 def s:PerformCheck(fname: string, pattern: string, msg: string, skip: string)
+  var prev_lnum = 1
   var lnum = 1
   while (lnum > 0)
     cursor(lnum, 1)
     lnum = search(pattern, 'W', 0, 0, skip)
-    ReportError(fname, lnum, msg)
+    if (prev_lnum == lnum)
+      break
+    endif
+    prev_lnum = lnum
     if (lnum > 0)
-      lnum += 1
+      ReportError(fname, lnum, msg)
     endif
   endwhile
 enddef
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    256,
+/**/
     255,
 /**/
     254,