patch 8.2.4252: generating the normal command table at runtime is inefficient

Problem:    Generating the normal command table at runtime is inefficient.
Solution:   Generate the table with a Vim script and put it in a header file.
            (Yegappan Lakshmanan, closes #9648)
This commit is contained in:
Yegappan Lakshmanan
2022-01-29 13:06:40 +00:00
committed by Bram Moolenaar
parent 97c554d514
commit 4dc0dd8699
14 changed files with 400 additions and 65 deletions

View File

@ -113,6 +113,7 @@ SRC_ALL = \
src/nbdebug.h \
src/netbeans.c \
src/normal.c \
src/nv_cmdidxs.h \
src/ops.c \
src/option.c \
src/option.h \
@ -443,6 +444,7 @@ SRC_UNIX = \
src/configure \
src/configure.ac \
src/create_cmdidxs.vim \
src/create_nvcmdidxs.vim \
src/gui_at_fs.c \
src/gui_at_sb.c \
src/gui_at_sb.h \

View File

@ -292,6 +292,7 @@ inputrestore() Number restore typeahead
inputsave() Number save and clear typeahead
inputsecret({prompt} [, {text}]) String like input() but hiding the text
insert({object}, {item} [, {idx}]) List insert {item} in {object} [before {idx}]
internal_get_nv_cmdchar({idx}) Number command character at this index
interrupt() none interrupt script execution
invert({expr}) Number bitwise invert
isdirectory({directory}) Number |TRUE| if {directory} is a directory
@ -4622,6 +4623,11 @@ insert({object}, {item} [, {idx}]) *insert()*
Can also be used as a |method|: >
mylist->insert(item)
<
*internal_get_nv_cmdchar()*
internal_get_nv_cmdchar({idx})
Return the normal/visual mode command character at the
specified index. To be used only during the Vim build process.
interrupt() *interrupt()*
Interrupt script execution. It works more or less like the

View File

@ -1110,6 +1110,7 @@ Testing: *test-functions*
assert_nobeep() assert that a command does not cause a beep
assert_fails() assert that a command fails
assert_report() report a test failure
internal_get_nv_cmdchar() normal/visual command character at an index
test_alloc_fail() make memory allocation fail
test_autochdir() enable 'autochdir' during startup
test_override() test with Vim internal overrides

View File

@ -1147,6 +1147,16 @@ endif
cmdidxs: ex_cmds.h
vim --clean -X --not-a-term -u create_cmdidxs.vim
# Run vim script to generate the normal/visual mode command lookup table.
# This only needs to be run when a new normal/visual mode command has been
# added. If this fails because you don't have Vim yet:
# - change nv_cmds[] in normal.c to add the new normal/visual mode command.
# - build Vim
# - run "make nvcmdidxs" using the new Vim to generate nv_cmdidxs.h
# - rebuild Vim to use the newly generated nv_cmdidxs.h file.
nvcmdidxs: normal.c
./$(TARGET) --clean -X --not-a-term -u create_nvcmdidxs.vim
###########################################################################
INCL = vim.h alloc.h ascii.h ex_cmds.h feature.h errors.h globals.h \
keymap.h macros.h option.h os_dos.h os_win32.h proto.h regexp.h \
@ -1209,6 +1219,8 @@ $(OUTDIR)/hardcopy.o: hardcopy.c $(INCL) version.h
$(OUTDIR)/misc1.o: misc1.c $(INCL) version.h
$(OUTDIR)/normal.o: normal.c $(INCL) nv_cmdidxs.h
$(OUTDIR)/netbeans.o: netbeans.c $(INCL) version.h
$(OUTDIR)/version.o: version.c $(INCL) version.h

View File

@ -1446,6 +1446,16 @@ clean: testclean
cmdidxs: ex_cmds.h
vim --clean -X --not-a-term -u create_cmdidxs.vim
# Run vim script to generate the normal/visual mode command lookup table.
# This only needs to be run when a new normal/visual mode command has been
# added. If this fails because you don't have Vim yet:
# - change nv_cmds[] in normal.c to add the new normal/visual mode command.
# - build Vim
# - run "make nvcmdidxs" using the new Vim to generate nv_cmdidxs.h
# - rebuild Vim to use the newly generated nv_cmdidxs.h file.
nvcmdidxs: normal.c
.\$(VIM) --clean -X --not-a-term -u create_nvcmdidxs.vim
test:
cd testdir
$(MAKE) /NOLOGO -f Make_dos.mak
@ -1709,7 +1719,7 @@ $(OUTDIR)/netbeans.obj: $(OUTDIR) netbeans.c $(NBDEBUG_SRC) $(INCL) version.h
$(OUTDIR)/channel.obj: $(OUTDIR) channel.c $(INCL)
$(OUTDIR)/normal.obj: $(OUTDIR) normal.c $(INCL)
$(OUTDIR)/normal.obj: $(OUTDIR) normal.c $(INCL) nv_cmdidxs.h
$(OUTDIR)/option.obj: $(OUTDIR) option.c $(INCL) optiondefs.h

View File

@ -977,7 +977,7 @@ mbyte.obj : mbyte.c vim.h [.auto]config.h feature.h os_unix.h \
normal.obj : normal.c vim.h [.auto]config.h feature.h os_unix.h \
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
errors.h globals.h
errors.h globals.h nv_cmdidxs.h
ops.obj : ops.c vim.h [.auto]config.h feature.h os_unix.h \
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
[.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h

View File

@ -2144,6 +2144,16 @@ autoconf:
cmdidxs: ex_cmds.h
vim --clean -X --not-a-term -u create_cmdidxs.vim
# Run vim script to generate the normal/visual mode command lookup table.
# This only needs to be run when a new normal/visual mode command has been
# added. If this fails because you don't have Vim yet:
# - change nv_cmds[] in normal.c to add the new normal/visual mode command.
# - build Vim
# - run "make nvcmdidxs" using the new Vim to generate nv_cmdidxs.h
# - rebuild Vim to use the newly generated nv_cmdidxs.h file.
nvcmdidxs: normal.c
./$(VIMTARGET) --clean -X --not-a-term -u create_nvcmdidxs.vim
# The normal command to compile a .c file to its .o file.
# Without or with ALL_CFLAGS.
@ -4002,7 +4012,7 @@ objects/move.o: move.c vim.h protodef.h auto/config.h feature.h os_unix.h \
objects/normal.o: normal.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h errors.h
proto.h globals.h errors.h nv_cmdidxs.h
objects/ops.o: ops.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \

72
src/create_nvcmdidxs.vim Normal file
View File

@ -0,0 +1,72 @@
vim9script
# This script generates the table nv_cmd_idx[] which contains the index in
# nv_cmds[] table (normal.c) for each of the command character supported in
# normal/visual mode.
# This is used to speed up the command lookup in nv_cmds[].
#
# Script should be run using "make nvcmdidxs", every time the nv_cmds[] table
# in src/normal.c changes.
def Create_nvcmdidxs_table()
var nv_cmdtbl: list<dict<number>> = []
# Generate the table of normal/visual mode command characters and their
# corresponding index.
var idx: number = 0
var ch: number
while true
ch = internal_get_nv_cmdchar(idx)
if ch == -1
break
endif
add(nv_cmdtbl, {idx: idx, cmdchar: ch})
idx += 1
endwhile
# sort the table by the command character
sort(nv_cmdtbl, (a, b) => a.cmdchar - b.cmdchar)
# Compute the highest index upto which the command character can be directly
# used as an index.
var nv_max_linear: number = 0
for i in range(nv_cmdtbl->len())
if i != nv_cmdtbl[i].cmdchar
nv_max_linear = i - 1
break
endif
endfor
# Generate a header file with the table
var output: list<string> =<< trim END
/*
* Automatically generated code by the create_nvcmdidxs.vim script.
*
* Table giving the index in nv_cmds[] to lookup based on
* the command character.
*/
// nv_cmd_idx[<normal mode command character>] => nv_cmds[] index
static const unsigned short nv_cmd_idx[] =
{
END
# Add each command character in comment and the corresponding index
var tbl: list<string> = mapnew(nv_cmdtbl, (k, v) =>
' /* ' .. printf('%5d', v.cmdchar) .. ' */ ' ..
printf('%3d', v.idx) .. ','
)
output += tbl
output += [ '};', '',
'// The highest index for which',
'// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]']
output += ['static const int nv_max_linear = ' .. nv_max_linear .. ';']
writefile(output, "nv_cmdidxs.h")
enddef
Create_nvcmdidxs_table()
quit
# vim: shiftwidth=2 sts=2 expandtab

View File

@ -1737,6 +1737,8 @@ static funcentry_T global_functions[] =
ret_string, f_inputsecret},
{"insert", 2, 3, FEARG_1, arg23_insert,
ret_first_arg, f_insert},
{"internal_get_nv_cmdchar", 1, 1, FEARG_1, arg1_number,
ret_number, f_internal_get_nv_cmdchar},
{"interrupt", 0, 0, 0, NULL,
ret_void, f_interrupt},
{"invert", 1, 1, FEARG_1, arg1_number,

View File

@ -901,9 +901,6 @@ common_init(mparm_T *paramp)
qnx_init(); // PhAttach() for clipboard, (and gui)
#endif
// Init the table of Normal mode commands.
init_normal_cmds();
/*
* Allocate space for the generic buffers (needed for set_init_1() and
* emsg()).

View File

@ -19,7 +19,6 @@ static int VIsual_mode_orig = NUL; // saved Visual mode
#ifdef FEAT_EVAL
static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount);
#endif
static int nv_compare(const void *s1, const void *s2);
static void unshift_special(cmdarg_T *cap);
#ifdef FEAT_CMDL_INFO
static void del_from_showcmd(int);
@ -128,6 +127,34 @@ static void nv_drop(cmdarg_T *cap);
#endif
static void nv_cursorhold(cmdarg_T *cap);
#ifdef FEAT_GUI
#define NV_VER_SCROLLBAR nv_ver_scrollbar
#define NV_HOR_SCROLLBAR nv_hor_scrollbar
#else
#define NV_VER_SCROLLBAR nv_error
#define NV_HOR_SCROLLBAR nv_error
#endif
#ifdef FEAT_GUI_TABLINE
#define NV_TABLINE nv_tabline
#define NV_TABMENU nv_tabmenu
#else
#define NV_TABLINE nv_error
#define NV_TABMENU nv_error
#endif
#ifdef FEAT_NETBEANS_INTG
#define NV_NBCMD nv_nbcmd
#else
#define NV_NBCMD nv_error
#endif
#ifdef FEAT_DND
#define NV_DROP nv_drop
#else
#define NV_DROP nv_error
#endif
/*
* Function to be called for a Normal or Visual mode command.
* The argument is a cmdarg_T.
@ -159,8 +186,14 @@ typedef void (*nv_func_T)(cmdarg_T *cap);
/*
* This table contains one entry for every Normal or Visual mode command.
* The order doesn't matter, init_normal_cmds() will create a sorted index.
* The order doesn't matter, this will be sorted by the create_nvcmdidx.vim
* script to generate the nv_cmd_idx[] lookup table.
* It is faster when all keys from zero to '~' are present.
*
* After changing the "nv_cmds" table:
* 1. Build Vim with "make"
* 2. Run "make nvcmdidxs" to re-generate the nv_cmdidxs.h file.
* 3. Build Vim with "make" to use the newly generated index table.
*/
static const struct nv_cmd
{
@ -193,8 +226,6 @@ static const struct nv_cmd
{Ctrl_T, nv_tagpop, NV_NCW, 0},
{Ctrl_U, nv_halfpage, 0, 0},
{Ctrl_V, nv_visual, 0, FALSE},
{'V', nv_visual, 0, FALSE},
{'v', nv_visual, 0, FALSE},
{Ctrl_W, nv_window, 0, 0},
{Ctrl_X, nv_addsub, 0, 0},
{Ctrl_Y, nv_scroll_line, 0, FALSE},
@ -258,6 +289,7 @@ static const struct nv_cmd
{'S', nv_subst, NV_KEEPREG, 0},
{'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
{'U', nv_Undo, 0, 0},
{'V', nv_visual, 0, FALSE},
{'W', nv_wordcmd, 0, TRUE},
{'X', nv_abbrev, NV_KEEPREG, 0},
{'Y', nv_abbrev, NV_KEEPREG, 0},
@ -289,6 +321,7 @@ static const struct nv_cmd
{'s', nv_subst, NV_KEEPREG, 0},
{'t', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
{'u', nv_undo, 0, 0},
{'v', nv_visual, 0, FALSE},
{'w', nv_wordcmd, 0, FALSE},
{'x', nv_abbrev, NV_KEEPREG, 0},
{'y', nv_operator, 0, 0},
@ -356,20 +389,12 @@ static const struct nv_cmd
{K_F1, nv_help, NV_NCW, 0},
{K_XF1, nv_help, NV_NCW, 0},
{K_SELECT, nv_select, 0, 0},
#ifdef FEAT_GUI
{K_VER_SCROLLBAR, nv_ver_scrollbar, 0, 0},
{K_HOR_SCROLLBAR, nv_hor_scrollbar, 0, 0},
#endif
#ifdef FEAT_GUI_TABLINE
{K_TABLINE, nv_tabline, 0, 0},
{K_TABMENU, nv_tabmenu, 0, 0},
#endif
#ifdef FEAT_NETBEANS_INTG
{K_F21, nv_nbcmd, NV_NCH_ALW, 0},
#endif
#ifdef FEAT_DND
{K_DROP, nv_drop, NV_STS, 0},
#endif
{K_VER_SCROLLBAR, NV_VER_SCROLLBAR, 0, 0},
{K_HOR_SCROLLBAR, NV_HOR_SCROLLBAR, 0, 0},
{K_TABLINE, NV_TABLINE, 0, 0},
{K_TABMENU, NV_TABMENU, 0, 0},
{K_F21, NV_NBCMD, NV_NCH_ALW, 0},
{K_DROP, NV_DROP, NV_STS, 0},
{K_CURSORHOLD, nv_cursorhold, NV_KEEPREG, 0},
{K_PS, nv_edit, 0, 0},
{K_COMMAND, nv_colon, 0, 0},
@ -379,55 +404,42 @@ static const struct nv_cmd
// Number of commands in nv_cmds[].
#define NV_CMDS_SIZE ARRAY_LENGTH(nv_cmds)
#ifndef PROTO // cproto doesn't like this
// Sorted index of commands in nv_cmds[].
static short nv_cmd_idx[NV_CMDS_SIZE];
#endif
// The highest index for which
// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]
static int nv_max_linear;
// Include the lookuptable generated by create_nvcmdidx.vim.
#include "nv_cmdidxs.h"
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* Compare functions for qsort() below, that checks the command character
* through the index in nv_cmd_idx[].
*/
static int
nv_compare(const void *s1, const void *s2)
{
int c1, c2;
// The commands are sorted on absolute value.
c1 = nv_cmds[*(const short *)s1].cmd_char;
c2 = nv_cmds[*(const short *)s2].cmd_char;
if (c1 < 0)
c1 = -c1;
if (c2 < 0)
c2 = -c2;
return c1 - c2;
}
/*
* Initialize the nv_cmd_idx[] table.
* Return the command character for the given command index. This function is
* used to auto-generate nv_cmd_idx[].
*/
void
init_normal_cmds(void)
f_internal_get_nv_cmdchar(typval_T *argvars, typval_T *rettv)
{
int i;
int idx;
int cmd_char;
// Fill the index table with a one to one relation.
for (i = 0; i < (int)NV_CMDS_SIZE; ++i)
nv_cmd_idx[i] = i;
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
// Sort the commands by the command character.
qsort((void *)&nv_cmd_idx, (size_t)NV_CMDS_SIZE, sizeof(short), nv_compare);
if (check_for_number_arg(argvars, 0) == FAIL)
return;
// Find the first entry that can't be indexed by the command character.
for (i = 0; i < (int)NV_CMDS_SIZE; ++i)
if (i != nv_cmds[nv_cmd_idx[i]].cmd_char)
break;
nv_max_linear = i - 1;
idx = tv_get_number(&argvars[0]);
if (idx < 0 || idx >= (int)NV_CMDS_SIZE)
return;
cmd_char = nv_cmds[idx].cmd_char;
// We use the absolute value of the character. Special keys have a
// negative value, but are sorted on their absolute value.
if (cmd_char < 0)
cmd_char = -cmd_char;
rettv->vval.v_number = cmd_char;
return;
}
#endif
/*
* Search for a command in the commands table.

209
src/nv_cmdidxs.h Normal file
View File

@ -0,0 +1,209 @@
/*
* Automatically generated code by the create_nvcmdidxs.vim script.
*
* Table giving the index in nv_cmds[] to lookup based on
* the command character.
*/
// nv_cmd_idx[<normal mode command character>] => nv_cmds[] index
static const unsigned short nv_cmd_idx[] =
{
/* 0 */ 0,
/* 1 */ 1,
/* 2 */ 2,
/* 3 */ 3,
/* 4 */ 4,
/* 5 */ 5,
/* 6 */ 6,
/* 7 */ 7,
/* 8 */ 8,
/* 9 */ 9,
/* 10 */ 10,
/* 11 */ 11,
/* 12 */ 12,
/* 13 */ 13,
/* 14 */ 14,
/* 15 */ 15,
/* 16 */ 16,
/* 17 */ 17,
/* 18 */ 18,
/* 19 */ 19,
/* 20 */ 20,
/* 21 */ 21,
/* 22 */ 22,
/* 23 */ 23,
/* 24 */ 24,
/* 25 */ 25,
/* 26 */ 26,
/* 27 */ 27,
/* 28 */ 28,
/* 29 */ 29,
/* 30 */ 30,
/* 31 */ 31,
/* 32 */ 32,
/* 33 */ 33,
/* 34 */ 34,
/* 35 */ 35,
/* 36 */ 36,
/* 37 */ 37,
/* 38 */ 38,
/* 39 */ 39,
/* 40 */ 40,
/* 41 */ 41,
/* 42 */ 42,
/* 43 */ 43,
/* 44 */ 44,
/* 45 */ 45,
/* 46 */ 46,
/* 47 */ 47,
/* 48 */ 48,
/* 49 */ 49,
/* 50 */ 50,
/* 51 */ 51,
/* 52 */ 52,
/* 53 */ 53,
/* 54 */ 54,
/* 55 */ 55,
/* 56 */ 56,
/* 57 */ 57,
/* 58 */ 58,
/* 59 */ 59,
/* 60 */ 60,
/* 61 */ 61,
/* 62 */ 62,
/* 63 */ 63,
/* 64 */ 64,
/* 65 */ 65,
/* 66 */ 66,
/* 67 */ 67,
/* 68 */ 68,
/* 69 */ 69,
/* 70 */ 70,
/* 71 */ 71,
/* 72 */ 72,
/* 73 */ 73,
/* 74 */ 74,
/* 75 */ 75,
/* 76 */ 76,
/* 77 */ 77,
/* 78 */ 78,
/* 79 */ 79,
/* 80 */ 80,
/* 81 */ 81,
/* 82 */ 82,
/* 83 */ 83,
/* 84 */ 84,
/* 85 */ 85,
/* 86 */ 86,
/* 87 */ 87,
/* 88 */ 88,
/* 89 */ 89,
/* 90 */ 90,
/* 91 */ 91,
/* 92 */ 92,
/* 93 */ 93,
/* 94 */ 94,
/* 95 */ 95,
/* 96 */ 96,
/* 97 */ 97,
/* 98 */ 98,
/* 99 */ 99,
/* 100 */ 100,
/* 101 */ 101,
/* 102 */ 102,
/* 103 */ 103,
/* 104 */ 104,
/* 105 */ 105,
/* 106 */ 106,
/* 107 */ 107,
/* 108 */ 108,
/* 109 */ 109,
/* 110 */ 110,
/* 111 */ 111,
/* 112 */ 112,
/* 113 */ 113,
/* 114 */ 114,
/* 115 */ 115,
/* 116 */ 116,
/* 117 */ 117,
/* 118 */ 118,
/* 119 */ 119,
/* 120 */ 120,
/* 121 */ 121,
/* 122 */ 122,
/* 123 */ 123,
/* 124 */ 124,
/* 125 */ 125,
/* 126 */ 126,
/* 163 */ 127,
/* 1277 */ 156,
/* 1533 */ 158,
/* 11517 */ 132,
/* 11773 */ 134,
/* 12029 */ 135,
/* 12285 */ 138,
/* 12541 */ 139,
/* 12581 */ 180,
/* 12619 */ 174,
/* 12651 */ 181,
/* 12797 */ 140,
/* 12835 */ 175,
/* 13053 */ 141,
/* 13131 */ 166,
/* 13309 */ 142,
/* 13347 */ 160,
/* 13387 */ 170,
/* 13565 */ 143,
/* 13643 */ 168,
/* 13821 */ 150,
/* 14122 */ 171,
/* 14144 */ 169,
/* 14374 */ 179,
/* 14845 */ 182,
/* 16966 */ 188,
/* 17515 */ 177,
/* 17917 */ 133,
/* 18173 */ 136,
/* 18795 */ 152,
/* 19453 */ 129,
/* 19709 */ 128,
/* 19965 */ 130,
/* 20075 */ 167,
/* 20221 */ 131,
/* 20477 */ 153,
/* 20587 */ 165,
/* 20733 */ 178,
/* 21328 */ 191,
/* 22013 */ 161,
/* 22269 */ 164,
/* 22525 */ 176,
/* 22767 */ 187,
/* 22768 */ 186,
/* 22773 */ 183,
/* 22776 */ 185,
/* 22777 */ 184,
/* 22781 */ 172,
/* 23037 */ 144,
/* 23293 */ 145,
/* 23549 */ 146,
/* 23805 */ 147,
/* 24061 */ 148,
/* 24317 */ 149,
/* 24573 */ 189,
/* 24829 */ 190,
/* 25085 */ 151,
/* 25195 */ 154,
/* 25707 */ 157,
/* 25853 */ 137,
/* 26621 */ 192,
/* 26731 */ 173,
/* 26877 */ 193,
/* 26917 */ 163,
/* 27755 */ 159,
/* 29291 */ 162,
/* 30059 */ 155,
};
// The highest index for which
// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]
static const int nv_max_linear = 126;

View File

@ -1,5 +1,5 @@
/* normal.c */
void init_normal_cmds(void);
void f_internal_get_nv_cmdchar(typval_T *argvars, typval_T *rettv);
void normal_cmd(oparg_T *oap, int toplevel);
void check_visual_highlight(void);
void end_visual_mode(void);

View File

@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4252,
/**/
4251,
/**/