[lvm-devel] master - commands: optimize binary search
Zdenek Kabelac
zkabelac at fedoraproject.org
Fri Feb 17 12:25:36 UTC 2017
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=772834e40aef44a8f7bc884050e1b88087073706
Commit: 772834e40aef44a8f7bc884050e1b88087073706
Parent: 582a272b3f401e1bb6eafaa52d0ed1d318d04a9f
Author: Zdenek Kabelac <zkabelac at redhat.com>
AuthorDate: Fri Feb 17 10:00:43 2017 +0100
Committer: Zdenek Kabelac <zkabelac at redhat.com>
CommitterDate: Fri Feb 17 13:20:55 2017 +0100
commands: optimize binary search
Since there is a lot of options and lot of searches,
use binary search to keep strcmp at minimum.
The interesting part is - alphabetically sorted array contains
duplicates and some of them are not the 'right anwer', so
after we find matching string but not matching long_ARG,
we may need to check if the surrouding strings are the right matching
one.
The single loops is used also for strictly define --foo_long
(i.e. --stripes) and just differs at final part.
TODO1: replace strstr call with some flag (just like short_opt).
TODO2: drop '--' from being stored and tests by strcmp.
---
tools/command.c | 79 +++++++++++++++++++++++++++++++-----------------------
1 files changed, 45 insertions(+), 34 deletions(-)
diff --git a/tools/command.c b/tools/command.c
index e5a4640..b202878 100644
--- a/tools/command.c
+++ b/tools/command.c
@@ -359,46 +359,57 @@ static int opt_str_to_num(struct command *cmd, char *str)
char long_name[MAX_LONG_OPT_NAME_LEN];
char *p;
int i;
+ int first = 0, last = ARG_COUNT - 1, middle;
- /*
- * --foo_long means there are two args entries
- * for --foo, one with a short option and one
- * without, and we want the one without the
- * short option.
- */
- if (strstr(str, "_long")) {
- memset(long_name, 0, sizeof(long_name));
- strncpy(long_name, str, MAX_LONG_OPT_NAME_LEN-1);
- if ((p = strstr(long_name, "_long")))
- *p = '\0';
-
- for (i = 0; i < ARG_COUNT; i++) {
- if (!opt_names[i].long_opt)
- continue;
- /* skip anything with a short opt */
- if (opt_names[i].short_opt)
- continue;
- if (!strcmp(opt_names[i].long_opt, long_name))
- return opt_names[i].opt_enum;
- }
+ dm_strncpy(long_name, str, sizeof(long_name));
- log_error("Parsing command defs: unknown opt str: %s %s", str, long_name);
- cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR;
- return ARG_UNUSED;
- }
+ if ((p = strstr(long_name, "_long")))
+ /*
+ * --foo_long means there are two args entries
+ * for --foo, one with a short option and one
+ * without, and we want the one without the
+ * short option (== 0).
+ */
+ *p = '\0';
- for (i = 0; i < ARG_COUNT; i++) {
- if (!opt_names[i].long_opt)
- continue;
- /* These are only selected using --foo_long */
- if (strstr(opt_names[i].name, "_long_ARG"))
- continue;
- if (!strcmp(opt_names[i].long_opt, str))
- return opt_names[i].opt_enum;
+ /* Binary search in sorted array of long options (with duplicates) */
+ while (first <= last) {
+ middle = first + (last - first) / 2;
+ if ((i = strcmp(opt_names_alpha[middle]->long_opt, long_name)) < 0)
+ first = middle + 1;
+ else if (i > 0)
+ last = middle - 1;
+ else {
+ /* Matching long option string found.
+ * As sorted array contains duplicates, we need to also
+ * check left & right side for possible match
+ */
+ for (i = middle;;) {
+ if ((!p && !strstr(opt_names_alpha[i]->name, "_long_ARG")) ||
+ (p && !opt_names_alpha[i]->short_opt))
+ return opt_names_alpha[i]->opt_enum; /* Found */
+ /* Check if there is something on the 'left-side' */
+ if ((i <= first) || strcmp(opt_names_alpha[--i]->long_opt, long_name))
+ break;
+ }
+
+ /* Nothing on the left, so look on the 'right-side' */
+ for (i = middle + 1; i <= last; ++i) {
+ if (strcmp(opt_names_alpha[i]->long_opt, long_name))
+ break;
+ if ((!p && !strstr(opt_names_alpha[i]->name, "_long_ARG")) ||
+ (p && !opt_names_alpha[i]->short_opt))
+ return opt_names_alpha[i]->opt_enum; /* Found */
+ }
+
+ break; /* Nothing... */
+ }
}
- log_error("Parsing command defs: unknown opt str: \"%s\"", str);
+ log_error("Parsing command defs: unknown opt str: \"%s\"%s%s.",
+ str, p ? " ": "", p ? long_name : "");
cmd->cmd_flags |= CMD_FLAG_PARSE_ERROR;
+
return ARG_UNUSED;
}
More information about the lvm-devel
mailing list