Skip to content

Commit 327e0e3

Browse files
mattnchrisbra
authored andcommitted
patch 9.2.0096: has() function is slow due to linear feature scan
Problem: The has() function is slow because it performs a linear scan of the feature list for every call. Solution: Move common runtime checks and the patch-version parser to the beginning of the f_has() function (Yasuhiro Matsumoto). closes: #19550 Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent 962a8c7 commit 327e0e3

File tree

2 files changed

+179
-174
lines changed

2 files changed

+179
-174
lines changed

src/evalfunc.c

Lines changed: 177 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -7760,214 +7760,217 @@ f_has(typval_T *argvars, typval_T *rettv)
77607760
return;
77617761

77627762
name = tv_get_string(&argvars[0]);
7763-
for (i = 0; has_list[i].name != NULL; ++i)
7764-
if (STRICMP(name, has_list[i].name) == 0)
7763+
7764+
// Fast-path: check features not in has_list[] first to avoid the full
7765+
// linear scan for very common queries like has('patch-...').
7766+
if (STRNICMP(name, "patch", 5) == 0)
7767+
{
7768+
x = TRUE;
7769+
if (name[5] == '-'
7770+
&& STRLEN(name) >= 11
7771+
&& (name[6] >= '1' && name[6] <= '9'))
77657772
{
7766-
x = TRUE;
7767-
n = has_list[i].present;
7768-
break;
7773+
char *end;
7774+
int major, minor;
7775+
7776+
// This works for patch-8.1.2, patch-9.0.3, patch-10.0.4, etc.
7777+
// Not for patch-9.10.5.
7778+
major = (int)strtoul((char *)name + 6, &end, 10);
7779+
if (*end == '.' && vim_isdigit(end[1])
7780+
&& end[2] == '.' && vim_isdigit(end[3]))
7781+
{
7782+
minor = atoi(end + 1);
7783+
7784+
// Expect "patch-9.9.01234".
7785+
n = (major < VIM_VERSION_MAJOR
7786+
|| (major == VIM_VERSION_MAJOR
7787+
&& (minor < VIM_VERSION_MINOR
7788+
|| (minor == VIM_VERSION_MINOR
7789+
&& has_patch(atoi(end + 3))))));
7790+
}
77697791
}
7792+
else if (SAFE_isdigit(name[5]))
7793+
n = has_patch(atoi((char *)name + 5));
7794+
}
7795+
else if (STRICMP(name, "vim_starting") == 0)
7796+
{
7797+
x = TRUE;
7798+
n = (starting != 0);
7799+
}
7800+
else if (STRICMP(name, "ttyin") == 0)
7801+
{
7802+
x = TRUE;
7803+
n = mch_input_isatty();
7804+
}
7805+
else if (STRICMP(name, "ttyout") == 0)
7806+
{
7807+
x = TRUE;
7808+
n = stdout_isatty;
7809+
}
7810+
else if (STRICMP(name, "multi_byte_encoding") == 0)
7811+
{
7812+
x = TRUE;
7813+
n = has_mbyte;
7814+
}
7815+
else if (STRICMP(name, "gui_running") == 0)
7816+
{
7817+
x = TRUE;
7818+
#ifdef FEAT_GUI
7819+
n = (gui.in_use || gui.starting);
7820+
#endif
7821+
}
7822+
else if (STRICMP(name, "browse") == 0)
7823+
{
7824+
x = TRUE;
7825+
#if defined(FEAT_GUI) && defined(FEAT_BROWSE)
7826+
n = gui.in_use; // gui_mch_browse() works when GUI is running
7827+
#endif
7828+
}
7829+
else if (STRICMP(name, "syntax_items") == 0)
7830+
{
7831+
x = TRUE;
7832+
#ifdef FEAT_SYN_HL
7833+
n = syntax_present(curwin);
7834+
#endif
7835+
}
7836+
else if (STRICMP(name, "vcon") == 0)
7837+
{
7838+
x = TRUE;
7839+
#ifdef FEAT_VTP
7840+
n = is_term_win32() && has_vtp_working();
7841+
#endif
7842+
}
7843+
else if (STRICMP(name, "netbeans_enabled") == 0)
7844+
{
7845+
x = TRUE;
7846+
#ifdef FEAT_NETBEANS_INTG
7847+
n = netbeans_active();
7848+
#endif
7849+
}
7850+
else if (STRICMP(name, "mouse_gpm_enabled") == 0)
7851+
{
7852+
x = TRUE;
7853+
#ifdef FEAT_MOUSE_GPM
7854+
n = gpm_enabled();
7855+
#endif
7856+
}
7857+
else if (STRICMP(name, "conpty") == 0)
7858+
{
7859+
x = TRUE;
7860+
#if defined(FEAT_TERMINAL) && defined(MSWIN)
7861+
n = use_conpty();
7862+
#endif
7863+
}
7864+
else if (STRICMP(name, "clipboard_working") == 0)
7865+
{
7866+
x = TRUE;
7867+
#ifdef FEAT_CLIPBOARD
7868+
n = clipmethod == CLIPMETHOD_PROVIDER ? TRUE : clip_star.available;
7869+
#endif
7870+
}
7871+
else if (STRICMP(name, "unnamedplus") == 0)
7872+
{
7873+
x = TRUE;
7874+
#ifdef FEAT_CLIPBOARD
7875+
// The + register is available when clipmethod is set to a provider,
7876+
// but becomes unavailable if on a platform that doesn't support it
7877+
// and clipmethod is "none".
7878+
// (Windows, MacOS).
7879+
# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
7880+
n = TRUE;
7881+
# elif defined(FEAT_EVAL)
7882+
if (clipmethod == CLIPMETHOD_PROVIDER)
7883+
n = TRUE;
7884+
else
7885+
n = FALSE;
7886+
# else
7887+
n = FALSE;
7888+
# endif
7889+
#endif
7890+
}
77707891

7771-
// features also in has_list[] but sometimes enabled at runtime
7772-
if (x == TRUE && n == FALSE)
7892+
// Look up in has_list[] only if not already handled above.
7893+
if (x == FALSE)
77737894
{
7774-
if (0)
7895+
for (i = 0; has_list[i].name != NULL; ++i)
7896+
if (STRICMP(name, has_list[i].name) == 0)
7897+
{
7898+
x = TRUE;
7899+
n = has_list[i].present;
7900+
break;
7901+
}
7902+
7903+
// features also in has_list[] but sometimes enabled at runtime
7904+
if (x == TRUE && n == FALSE)
77757905
{
7776-
// intentionally empty
7777-
}
7906+
if (0)
7907+
{
7908+
// intentionally empty
7909+
}
77787910
#ifdef VIMDLL
7779-
else if (STRICMP(name, "filterpipe") == 0)
7780-
n = gui.in_use || gui.starting;
7911+
else if (STRICMP(name, "filterpipe") == 0)
7912+
n = gui.in_use || gui.starting;
77817913
#endif
77827914
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
7783-
else if (STRICMP(name, "iconv") == 0)
7784-
n = iconv_enabled(FALSE);
7915+
else if (STRICMP(name, "iconv") == 0)
7916+
n = iconv_enabled(FALSE);
77857917
#endif
77867918
#ifdef DYNAMIC_LUA
7787-
else if (STRICMP(name, "lua") == 0)
7788-
n = lua_enabled(FALSE);
7919+
else if (STRICMP(name, "lua") == 0)
7920+
n = lua_enabled(FALSE);
77897921
#endif
77907922
#ifdef DYNAMIC_MZSCHEME
7791-
else if (STRICMP(name, "mzscheme") == 0)
7792-
n = mzscheme_enabled(FALSE);
7923+
else if (STRICMP(name, "mzscheme") == 0)
7924+
n = mzscheme_enabled(FALSE);
77937925
#endif
77947926
#ifdef DYNAMIC_PERL
7795-
else if (STRICMP(name, "perl") == 0)
7796-
n = perl_enabled(FALSE);
7927+
else if (STRICMP(name, "perl") == 0)
7928+
n = perl_enabled(FALSE);
77977929
#endif
77987930
#ifdef DYNAMIC_PYTHON
7799-
else if (STRICMP(name, "python") == 0)
7800-
n = python_enabled(FALSE);
7931+
else if (STRICMP(name, "python") == 0)
7932+
n = python_enabled(FALSE);
78017933
#endif
78027934
#ifdef DYNAMIC_PYTHON3
7803-
else if (STRICMP(name, "python3") == 0)
7804-
n = python3_enabled(FALSE);
7935+
else if (STRICMP(name, "python3") == 0)
7936+
n = python3_enabled(FALSE);
78057937
#endif
78067938
#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3)
7807-
else if (STRICMP(name, "pythonx") == 0)
7808-
{
7939+
else if (STRICMP(name, "pythonx") == 0)
7940+
{
78097941
# if defined(DYNAMIC_PYTHON) && defined(DYNAMIC_PYTHON3)
7810-
if (p_pyx == 0)
7811-
n = python3_enabled(FALSE) || python_enabled(FALSE);
7812-
else if (p_pyx == 3)
7813-
n = python3_enabled(FALSE);
7814-
else if (p_pyx == 2)
7815-
n = python_enabled(FALSE);
7942+
if (p_pyx == 0)
7943+
n = python3_enabled(FALSE) || python_enabled(FALSE);
7944+
else if (p_pyx == 3)
7945+
n = python3_enabled(FALSE);
7946+
else if (p_pyx == 2)
7947+
n = python_enabled(FALSE);
78167948
# elif defined(DYNAMIC_PYTHON)
7817-
n = python_enabled(FALSE);
7949+
n = python_enabled(FALSE);
78187950
# elif defined(DYNAMIC_PYTHON3)
7819-
n = python3_enabled(FALSE);
7951+
n = python3_enabled(FALSE);
78207952
# endif
7821-
}
7953+
}
78227954
#endif
78237955
#ifdef DYNAMIC_RUBY
7824-
else if (STRICMP(name, "ruby") == 0)
7825-
n = ruby_enabled(FALSE);
7956+
else if (STRICMP(name, "ruby") == 0)
7957+
n = ruby_enabled(FALSE);
78267958
#endif
78277959
#ifdef DYNAMIC_TCL
7828-
else if (STRICMP(name, "tcl") == 0)
7829-
n = tcl_enabled(FALSE);
7960+
else if (STRICMP(name, "tcl") == 0)
7961+
n = tcl_enabled(FALSE);
78307962
#endif
78317963
#ifdef DYNAMIC_SODIUM
7832-
else if (STRICMP(name, "sodium") == 0)
7833-
n = sodium_enabled(FALSE);
7964+
else if (STRICMP(name, "sodium") == 0)
7965+
n = sodium_enabled(FALSE);
78347966
#endif
78357967
#if defined(FEAT_TERMINAL) && defined(MSWIN)
7836-
else if (STRICMP(name, "terminal") == 0)
7837-
n = terminal_enabled();
7968+
else if (STRICMP(name, "terminal") == 0)
7969+
n = terminal_enabled();
78387970
#endif
78397971
#ifdef DYNAMIC_GPM
7840-
else if (STRICMP(name, "mouse_gpm") == 0)
7841-
n = gpm_available();
7842-
#endif
7843-
}
7844-
7845-
// features not in has_list[]
7846-
if (x == FALSE)
7847-
{
7848-
if (STRNICMP(name, "patch", 5) == 0)
7849-
{
7850-
x = TRUE;
7851-
if (name[5] == '-'
7852-
&& STRLEN(name) >= 11
7853-
&& (name[6] >= '1' && name[6] <= '9'))
7854-
{
7855-
char *end;
7856-
int major, minor;
7857-
7858-
// This works for patch-8.1.2, patch-9.0.3, patch-10.0.4, etc.
7859-
// Not for patch-9.10.5.
7860-
major = (int)strtoul((char *)name + 6, &end, 10);
7861-
if (*end == '.' && vim_isdigit(end[1])
7862-
&& end[2] == '.' && vim_isdigit(end[3]))
7863-
{
7864-
minor = atoi(end + 1);
7865-
7866-
// Expect "patch-9.9.01234".
7867-
n = (major < VIM_VERSION_MAJOR
7868-
|| (major == VIM_VERSION_MAJOR
7869-
&& (minor < VIM_VERSION_MINOR
7870-
|| (minor == VIM_VERSION_MINOR
7871-
&& has_patch(atoi(end + 3))))));
7872-
}
7873-
}
7874-
else if (SAFE_isdigit(name[5]))
7875-
n = has_patch(atoi((char *)name + 5));
7876-
}
7877-
else if (STRICMP(name, "vim_starting") == 0)
7878-
{
7879-
x = TRUE;
7880-
n = (starting != 0);
7881-
}
7882-
else if (STRICMP(name, "ttyin") == 0)
7883-
{
7884-
x = TRUE;
7885-
n = mch_input_isatty();
7886-
}
7887-
else if (STRICMP(name, "ttyout") == 0)
7888-
{
7889-
x = TRUE;
7890-
n = stdout_isatty;
7891-
}
7892-
else if (STRICMP(name, "multi_byte_encoding") == 0)
7893-
{
7894-
x = TRUE;
7895-
n = has_mbyte;
7896-
}
7897-
else if (STRICMP(name, "gui_running") == 0)
7898-
{
7899-
x = TRUE;
7900-
#ifdef FEAT_GUI
7901-
n = (gui.in_use || gui.starting);
7902-
#endif
7903-
}
7904-
else if (STRICMP(name, "browse") == 0)
7905-
{
7906-
x = TRUE;
7907-
#if defined(FEAT_GUI) && defined(FEAT_BROWSE)
7908-
n = gui.in_use; // gui_mch_browse() works when GUI is running
7909-
#endif
7910-
}
7911-
else if (STRICMP(name, "syntax_items") == 0)
7912-
{
7913-
x = TRUE;
7914-
#ifdef FEAT_SYN_HL
7915-
n = syntax_present(curwin);
7916-
#endif
7917-
}
7918-
else if (STRICMP(name, "vcon") == 0)
7919-
{
7920-
x = TRUE;
7921-
#ifdef FEAT_VTP
7922-
n = is_term_win32() && has_vtp_working();
7923-
#endif
7924-
}
7925-
else if (STRICMP(name, "netbeans_enabled") == 0)
7926-
{
7927-
x = TRUE;
7928-
#ifdef FEAT_NETBEANS_INTG
7929-
n = netbeans_active();
7930-
#endif
7931-
}
7932-
else if (STRICMP(name, "mouse_gpm_enabled") == 0)
7933-
{
7934-
x = TRUE;
7935-
#ifdef FEAT_MOUSE_GPM
7936-
n = gpm_enabled();
7937-
#endif
7938-
}
7939-
else if (STRICMP(name, "conpty") == 0)
7940-
{
7941-
x = TRUE;
7942-
#if defined(FEAT_TERMINAL) && defined(MSWIN)
7943-
n = use_conpty();
7944-
#endif
7945-
}
7946-
else if (STRICMP(name, "clipboard_working") == 0)
7947-
{
7948-
x = TRUE;
7949-
#ifdef FEAT_CLIPBOARD
7950-
n = clipmethod == CLIPMETHOD_PROVIDER ? TRUE : clip_star.available;
7951-
#endif
7952-
}
7953-
else if (STRICMP(name, "unnamedplus") == 0)
7954-
{
7955-
x = TRUE;
7956-
#ifdef FEAT_CLIPBOARD
7957-
// The + register is available when clipmethod is set to a provider,
7958-
// but becomes unavailable if on a platform that doesn't support it
7959-
// and clipmethod is "none".
7960-
// (Windows, MacOS).
7961-
# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
7962-
n = TRUE;
7963-
# elif defined(FEAT_EVAL)
7964-
if (clipmethod == CLIPMETHOD_PROVIDER)
7965-
n = TRUE;
7966-
else
7967-
n = FALSE;
7968-
# else
7969-
n = FALSE;
7970-
# endif
7972+
else if (STRICMP(name, "mouse_gpm") == 0)
7973+
n = gpm_available();
79717974
#endif
79727975
}
79737976
}

0 commit comments

Comments
 (0)