From: David Lamparter Date: Tue, 5 May 2015 09:10:20 +0000 (+0200) Subject: lib/cli: reduce strcmp in CLI hot paths X-Git-Tag: frr-2.0-rc1~571 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=c117e02796a8c1d32379b9eb2e7c444fdeeb60d6;p=mirror%2Ffrr.git lib/cli: reduce strcmp in CLI hot paths Er, no idea how anyone could ever have thought that it would be a good idea to have a zillion of strcmp() calls in the CLI's active paths, just to compare against things like "A.B.C.D". Reduces 40k prefix list load time from 1.65s to 1.23s (1.34:1). Acked-by: Paul Jakma [v2: killed CMDS_* macros] Signed-off-by: David Lamparter (cherry picked from commit 10bac80195cf5a781da6e4415e6580fd7080f734) --- diff --git a/lib/command.c b/lib/command.c index 958d0eb244..1e18e25026 100644 --- a/lib/command.c +++ b/lib/command.c @@ -507,6 +507,25 @@ format_parser_read_word(struct format_parser_state *state) token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); token->type = TOKEN_TERMINAL; + if (cmd[0] == '[') + token->terminal = TERMINAL_OPTION; + else if ((cmd[0] >= 'A' && cmd[0] <= 'Z') || (cmd[0] == '<')) + token->terminal = TERMINAL_VARIABLE; + else if (cmd[0] == '.') + token->terminal = TERMINAL_VARARG; + else if (cmd[0] == '<') + token->terminal = TERMINAL_RANGE; + else if (strcmp (cmd, "A.B.C.D") == 0) + token->terminal = TERMINAL_IPV4; + else if (strcmp (cmd, "A.B.C.D/M") == 0) + token->terminal = TERMINAL_IPV4_PREFIX; + else if (strcmp (cmd, "X:X::X:X") == 0) + token->terminal = TERMINAL_IPV6; + else if (strcmp (cmd, "X:X::X:X/M") == 0) + token->terminal = TERMINAL_IPV6_PREFIX; + else + token->terminal = TERMINAL_LITERAL; + token->cmd = cmd; token->desc = format_parser_desc_str(state); vector_set(state->curvect, token); @@ -1173,59 +1192,61 @@ cmd_word_match(struct cmd_token *token, if (!word) return no_match; - if (CMD_VARARG(str)) - { - return vararg_match; - } - else if (CMD_RANGE(str)) - { - if (cmd_range_match(str, word)) - return range_match; - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6(str)) - { - match_type = cmd_ipv6_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_match; - } - else if (CMD_IPV6_PREFIX(str)) - { - match_type = cmd_ipv6_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_prefix_match; - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4(str)) - { - match_type = cmd_ipv4_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_match; - } - else if (CMD_IPV4_PREFIX(str)) + switch (token->terminal) { - match_type = cmd_ipv4_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) + case TERMINAL_VARARG: + return vararg_match; + + case TERMINAL_RANGE: + if (cmd_range_match(str, word)) + return range_match; + break; + + case TERMINAL_IPV6: + match_type = cmd_ipv6_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_prefix_match; - } - else if (CMD_OPTION(str) || CMD_VARIABLE(str)) - { - return extend_match; - } - else - { - if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) - { - if (!strcmp(str, word)) - return exact_match; - return partly_match; - } - if (filter == FILTER_STRICT && !strcmp(str, word)) - return exact_match; + return ipv6_match; + break; + + case TERMINAL_IPV6_PREFIX: + match_type = cmd_ipv6_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_prefix_match; + break; + + case TERMINAL_IPV4: + match_type = cmd_ipv4_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_match; + break; + + case TERMINAL_IPV4_PREFIX: + match_type = cmd_ipv4_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_prefix_match; + break; + + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + return extend_match; + + case TERMINAL_LITERAL: + if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) + { + if (!strcmp(str, word)) + return exact_match; + return partly_match; + } + if (filter == FILTER_STRICT && !strcmp(str, word)) + return exact_match; + break; + + default: + assert (0); } return no_match; @@ -1309,7 +1330,7 @@ cmd_matcher_match_terminal(struct cmd_matcher *matcher, if (!cmd_matcher_words_left(matcher)) { - if (CMD_OPTION(token->cmd)) + if (token->terminal == TERMINAL_OPTION) return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ else return MATCHER_INCOMPLETE; @@ -1322,9 +1343,9 @@ cmd_matcher_match_terminal(struct cmd_matcher *matcher, /* We have to record the input word as argument if it matched * against a variable. */ - if (CMD_VARARG(token->cmd) - || CMD_VARIABLE(token->cmd) - || CMD_OPTION(token->cmd)) + if (token->terminal == TERMINAL_VARARG + || token->terminal == TERMINAL_VARIABLE + || token->terminal == TERMINAL_OPTION) { if (push_argument(argc, argv, word)) return MATCHER_EXCEED_ARGC_MAX; @@ -1335,7 +1356,7 @@ cmd_matcher_match_terminal(struct cmd_matcher *matcher, matcher->word_index++; /* A vararg token should consume all left over words as arguments */ - if (CMD_VARARG(token->cmd)) + if (token->terminal == TERMINAL_VARARG) while (cmd_matcher_words_left(matcher)) { word = cmd_matcher_get_word(matcher); @@ -1566,9 +1587,9 @@ cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, { word_token = vector_slot(keyword_vector, j); if ((word_token->type == TOKEN_TERMINAL - && (CMD_VARARG(word_token->cmd) - || CMD_VARIABLE(word_token->cmd) - || CMD_OPTION(word_token->cmd))) + && (word_token->terminal == TERMINAL_VARARG + || word_token->terminal == TERMINAL_VARIABLE + || word_token->terminal == TERMINAL_OPTION)) || word_token->type == TOKEN_MULTIPLE) { if (push_argument(argc, argv, NULL)) @@ -1854,12 +1875,14 @@ is_cmd_ambiguous (vector cmd_vector, switch (type) { case exact_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!(cmd_token->terminal == TERMINAL_OPTION + || cmd_token->terminal == TERMINAL_VARIABLE) && strcmp (command, str) == 0) match++; break; case partly_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!(cmd_token->terminal == TERMINAL_OPTION + || cmd_token->terminal == TERMINAL_VARIABLE) && strncmp (command, str, strlen (command)) == 0) { if (matched && strcmp (matched, str) != 0) @@ -1881,7 +1904,7 @@ is_cmd_ambiguous (vector cmd_vector, break; #ifdef HAVE_IPV6 case ipv6_match: - if (CMD_IPV6 (str)) + if (cmd_token->terminal == TERMINAL_IPV6) match++; break; case ipv6_prefix_match: @@ -1895,7 +1918,7 @@ is_cmd_ambiguous (vector cmd_vector, break; #endif /* HAVE_IPV6 */ case ipv4_match: - if (CMD_IPV4 (str)) + if (cmd_token->terminal == TERMINAL_IPV4) match++; break; case ipv4_prefix_match: @@ -1908,7 +1931,8 @@ is_cmd_ambiguous (vector cmd_vector, } break; case extend_match: - if (CMD_OPTION (str) || CMD_VARIABLE (str)) + if (cmd_token->terminal == TERMINAL_OPTION + || cmd_token->terminal == TERMINAL_VARIABLE) match++; break; case no_match: @@ -1924,12 +1948,23 @@ is_cmd_ambiguous (vector cmd_vector, /* If src matches dst return dst string, otherwise return NULL */ static const char * -cmd_entry_function (const char *src, const char *dst) +cmd_entry_function (const char *src, struct cmd_token *token) { + const char *dst = token->cmd; + /* Skip variable arguments. */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || - CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) - return NULL; + switch (token->terminal) + { + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + case TERMINAL_VARARG: + case TERMINAL_IPV4: + case TERMINAL_IPV4_PREFIX: + case TERMINAL_RANGE: + return NULL; + default: + break; + } /* In case of 'command \t', given src is NULL string. */ if (src == NULL) @@ -1946,65 +1981,63 @@ cmd_entry_function (const char *src, const char *dst) /* This version will return the dst string always if it is CMD_VARIABLE for '?' key processing */ static const char * -cmd_entry_function_desc (const char *src, const char *dst) +cmd_entry_function_desc (const char *src, struct cmd_token *token) { - if (CMD_VARARG (dst)) - return dst; - - if (CMD_RANGE (dst)) - { - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - } - -#ifdef HAVE_IPV6 - if (CMD_IPV6 (dst)) - { - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV6_PREFIX (dst)) - { - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - } -#endif /* HAVE_IPV6 */ + const char *dst = token->cmd; - if (CMD_IPV4 (dst)) + switch (token->terminal) { - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV4_PREFIX (dst)) - { - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; + case TERMINAL_VARARG: + return dst; + + case TERMINAL_RANGE: + if (cmd_range_match (dst, src)) + return dst; + else + return NULL; + + case TERMINAL_IPV6: + if (cmd_ipv6_match (src)) + return dst; + else + return NULL; + + case TERMINAL_IPV6_PREFIX: + if (cmd_ipv6_prefix_match (src)) + return dst; + else + return NULL; + + case TERMINAL_IPV4: + if (cmd_ipv4_match (src)) + return dst; + else + return NULL; + + case TERMINAL_IPV4_PREFIX: + if (cmd_ipv4_prefix_match (src)) + return dst; + else + return NULL; + + /* Optional or variable commands always match on '?' */ + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + return dst; + + case TERMINAL_LITERAL: + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp (src, dst, strlen (src)) == 0) + return dst; + else + return NULL; + + default: + assert(0); } - - /* Optional or variable commands always match on '?' */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) - return dst; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - return NULL; } /** @@ -2223,7 +2256,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) struct cmd_token *token = vector_slot(match_vector, j); const char *string; - string = cmd_entry_function_desc(command, token->cmd); + string = cmd_entry_function_desc(command, token); if (string && desc_unique_string(matchvec, string)) vector_set(matchvec, token); } @@ -2436,7 +2469,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib if ((token = vector_slot (match_vector, j))) { string = cmd_entry_function (vector_slot (vline, index), - token->cmd); + token); if (string && cmd_unique_string (matchvec, string)) vector_set (matchvec, (islib != 0 ? XSTRDUP (MTYPE_TMP, string) : @@ -4135,6 +4168,7 @@ cmd_init (int terminal) { command_cr = XSTRDUP(MTYPE_CMD_TOKENS, ""); token_cr.type = TOKEN_TERMINAL; + token_cr.terminal = TERMINAL_LITERAL; token_cr.cmd = command_cr; token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); diff --git a/lib/command.h b/lib/command.h index 50c07b1460..56c1caceb2 100644 --- a/lib/command.h +++ b/lib/command.h @@ -153,10 +153,25 @@ enum cmd_token_type TOKEN_KEYWORD, }; +enum cmd_terminal_type +{ + _TERMINAL_BUG = 0, + TERMINAL_LITERAL, + TERMINAL_OPTION, + TERMINAL_VARIABLE, + TERMINAL_VARARG, + TERMINAL_RANGE, + TERMINAL_IPV4, + TERMINAL_IPV4_PREFIX, + TERMINAL_IPV6, + TERMINAL_IPV6_PREFIX, +}; + /* Command description structure. */ struct cmd_token { enum cmd_token_type type; + enum cmd_terminal_type terminal; /* Used for type == MULTIPLE */ vector multiple; /* vector of cmd_token, type == FINAL */ @@ -455,16 +470,6 @@ struct cmd_token #define CMD_CREATE_STR_HELPER(s) #s #define CMD_RANGE_STR(a,s) "<" CMD_CREATE_STR(a) "-" CMD_CREATE_STR(s) ">" -#define CMD_OPTION(S) ((S[0]) == '[') -#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) -#define CMD_VARARG(S) ((S[0]) == '.') -#define CMD_RANGE(S) ((S[0] == '<')) - -#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) -#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) -#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) -#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) - /* Common descriptions. */ #define SHOW_STR "Show running system information\n" #define IP_STR "IP information\n"