Mercurial > vim
changeset 27447:4050f0554902 v8.2.4252
patch 8.2.4252: generating the normal command table at runtime is inefficient
Commit: https://github.com/vim/vim/commit/4dc0dd869972ddafc7d9ee5ea765645b818a6dc9
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Sat Jan 29 13:06:40 2022 +0000
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)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 29 Jan 2022 14:15:04 +0100 |
parents | d5dcb8d0cf5e |
children | ca9bc0a44d8a |
files | Filelist runtime/doc/builtin.txt runtime/doc/usr_41.txt src/Make_cyg_ming.mak src/Make_mvc.mak src/Make_vms.mms src/Makefile src/create_nvcmdidxs.vim src/evalfunc.c src/main.c src/normal.c src/nv_cmdidxs.h src/proto/normal.pro src/version.c |
diffstat | 14 files changed, 407 insertions(+), 72 deletions(-) [+] |
line wrap: on
line diff
--- a/Filelist +++ b/Filelist @@ -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 \
--- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -292,6 +292,7 @@ inputrestore() Number restore typeahea 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}]) *in 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
--- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -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
--- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -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 @@ endif $(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
--- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -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 @@ lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).l $(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
--- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -977,7 +977,7 @@ mbyte.obj : mbyte.c vim.h [.auto]config. 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
--- a/src/Makefile +++ b/src/Makefile @@ -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 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 \
new file mode 100644 --- /dev/null +++ b/src/create_nvcmdidxs.vim @@ -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
--- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -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,
--- a/src/main.c +++ b/src/main.c @@ -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()).
--- a/src/normal.c +++ b/src/normal.c @@ -19,7 +19,6 @@ static int VIsual_mode_orig = NUL; // s #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; - -/* - * 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. +// Include the lookuptable generated by create_nvcmdidx.vim. +#include "nv_cmdidxs.h" + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Return the command character for the given command index. This function is + * used to auto-generate nv_cmd_idx[]. */ void -init_normal_cmds(void) -{ - int i; - - // Fill the index table with a one to one relation. - for (i = 0; i < (int)NV_CMDS_SIZE; ++i) - nv_cmd_idx[i] = i; - - // Sort the commands by the command character. - qsort((void *)&nv_cmd_idx, (size_t)NV_CMDS_SIZE, sizeof(short), nv_compare); - - // 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; -} +f_internal_get_nv_cmdchar(typval_T *argvars, typval_T *rettv) +{ + int idx; + int cmd_char; + + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = -1; + + if (check_for_number_arg(argvars, 0) == FAIL) + return; + + 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.
new file mode 100644 --- /dev/null +++ b/src/nv_cmdidxs.h @@ -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;