diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-04 21:21:07 +0200 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-04 21:21:07 +0200 |
commit | 701e127f7d892909a58c6f3333e23588ccef9e22 (patch) | |
tree | 9d756fca6a1ef496dbf02529e090f2f711a6c261 | |
parent | e298ce69baef029f3951dd1d5ed50fdbc6c55c80 (diff) | |
download | busybox-w32-701e127f7d892909a58c6f3333e23588ccef9e22.tar.gz busybox-w32-701e127f7d892909a58c6f3333e23588ccef9e22.tar.bz2 busybox-w32-701e127f7d892909a58c6f3333e23588ccef9e22.zip |
hush: optimize #[#] and %[%] for speed. size -2 bytes.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r-- | shell/hush.c | 22 | ||||
-rw-r--r-- | shell/match.c | 106 | ||||
-rw-r--r-- | shell/match.h | 29 |
3 files changed, 87 insertions, 70 deletions
diff --git a/shell/hush.c b/shell/hush.c index d3dab5863..4f80b7d83 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -2829,23 +2829,21 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2829 | * Then var's value is matched to it and matching part removed. | 2829 | * Then var's value is matched to it and matching part removed. |
2830 | */ | 2830 | */ |
2831 | if (val) { | 2831 | if (val) { |
2832 | bool match_at_left; | 2832 | char *exp_exp_word; |
2833 | char *loc; | 2833 | char *loc; |
2834 | scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); | 2834 | unsigned scan_flags = pick_scan(exp_op, *exp_word); |
2835 | if (exp_op == *exp_word) /* ## or %% */ | 2835 | if (exp_op == *exp_word) /* ## or %% */ |
2836 | exp_word++; | 2836 | exp_word++; |
2837 | val = to_be_freed = xstrdup(val); | 2837 | val = to_be_freed = xstrdup(val); |
2838 | { | 2838 | exp_exp_word = expand_pseudo_dquoted(exp_word); |
2839 | char *exp_exp_word = expand_pseudo_dquoted(exp_word); | 2839 | if (exp_exp_word) |
2840 | if (exp_exp_word) | 2840 | exp_word = exp_exp_word; |
2841 | exp_word = exp_exp_word; | 2841 | loc = scan_and_match(to_be_freed, exp_word, scan_flags); |
2842 | loc = scan(to_be_freed, exp_word, match_at_left); | 2842 | //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", |
2843 | //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", | 2843 | // exp_op, to_be_freed, exp_word, loc); |
2844 | // exp_op, to_be_freed, exp_word, loc); | 2844 | free(exp_exp_word); |
2845 | free(exp_exp_word); | ||
2846 | } | ||
2847 | if (loc) { /* match was found */ | 2845 | if (loc) { /* match was found */ |
2848 | if (match_at_left) /* # or ## */ | 2846 | if (scan_flags & SCAN_MATCH_LEFT_HALF) /* # or ## */ |
2849 | val = loc; | 2847 | val = loc; |
2850 | else /* % or %% */ | 2848 | else /* % or %% */ |
2851 | *loc = '\0'; | 2849 | *loc = '\0'; |
diff --git a/shell/match.c b/shell/match.c index 01b843918..e77c5d732 100644 --- a/shell/match.c +++ b/shell/match.c | |||
@@ -18,6 +18,9 @@ | |||
18 | # include <stdlib.h> | 18 | # include <stdlib.h> |
19 | # include <string.h> | 19 | # include <string.h> |
20 | # include <unistd.h> | 20 | # include <unistd.h> |
21 | # define FAST_FUNC /* nothing */ | ||
22 | # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */ | ||
23 | # define POP_SAVED_FUNCTION_VISIBILITY /* nothing */ | ||
21 | #else | 24 | #else |
22 | # include "libbb.h" | 25 | # include "libbb.h" |
23 | #endif | 26 | #endif |
@@ -26,16 +29,48 @@ | |||
26 | 29 | ||
27 | #define pmatch(a, b) !fnmatch((a), (b), 0) | 30 | #define pmatch(a, b) !fnmatch((a), (b), 0) |
28 | 31 | ||
29 | char *scanleft(char *string, char *pattern, bool match_at_left) | 32 | char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags) |
30 | { | 33 | { |
31 | char c; | 34 | char *loc; |
32 | char *loc = string; | 35 | char *end; |
36 | unsigned len = strlen(string); | ||
37 | int early_exit; | ||
38 | |||
39 | /* We can stop the scan early only if the string part | ||
40 | * we are matching against is shrinking, and the pattern has | ||
41 | * an unquoted "star" at the corresponding end. There are two cases. | ||
42 | * Case 1: | ||
43 | * "qwerty" does not match against pattern "*zy", | ||
44 | * no point in trying to match "werty", "erty" etc: | ||
45 | */ | ||
46 | early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*'); | ||
47 | |||
48 | if (flags & SCAN_MOVE_FROM_LEFT) { | ||
49 | loc = string; | ||
50 | end = string + len + 1; | ||
51 | } else { | ||
52 | loc = string + len; | ||
53 | end = string - 1; | ||
54 | if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) { | ||
55 | /* Case 2: | ||
56 | * "qwerty" does not match against pattern "qz*", | ||
57 | * no point in trying to match "qwert", "qwer" etc: | ||
58 | */ | ||
59 | const char *p = pattern + strlen(pattern); | ||
60 | if (--p >= pattern && *p == '*') { | ||
61 | early_exit = 1; | ||
62 | while (--p >= pattern && *p == '\\') | ||
63 | early_exit ^= 1; | ||
64 | } | ||
65 | } | ||
66 | } | ||
33 | 67 | ||
34 | while (1) { | 68 | while (loc != end) { |
69 | char c; | ||
35 | int match; | 70 | int match; |
36 | 71 | ||
37 | c = *loc; | 72 | c = *loc; |
38 | if (match_at_left) { | 73 | if (flags & SCAN_MATCH_LEFT_HALF) { |
39 | *loc = '\0'; | 74 | *loc = '\0'; |
40 | match = pmatch(pattern, string); | 75 | match = pmatch(pattern, string); |
41 | *loc = c; | 76 | *loc = c; |
@@ -44,33 +79,19 @@ char *scanleft(char *string, char *pattern, bool match_at_left) | |||
44 | } | 79 | } |
45 | if (match) | 80 | if (match) |
46 | return loc; | 81 | return loc; |
47 | if (!c) | 82 | if (early_exit) { |
48 | return NULL; | 83 | #ifdef STANDALONE |
49 | loc++; | 84 | printf("(early exit) "); |
50 | } | 85 | #endif |
51 | } | 86 | break; |
52 | 87 | } | |
53 | char *scanright(char *string, char *pattern, bool match_at_left) | ||
54 | { | ||
55 | char c; | ||
56 | char *loc = string + strlen(string); | ||
57 | |||
58 | while (loc >= string) { | ||
59 | int match; | ||
60 | 88 | ||
61 | c = *loc; | 89 | if (flags & SCAN_MOVE_FROM_LEFT) { |
62 | if (match_at_left) { | 90 | loc++; |
63 | *loc = '\0'; | ||
64 | match = pmatch(pattern, string); | ||
65 | *loc = c; | ||
66 | } else { | 91 | } else { |
67 | match = pmatch(pattern, loc); | 92 | loc--; |
68 | } | 93 | } |
69 | if (match) | ||
70 | return loc; | ||
71 | loc--; | ||
72 | } | 94 | } |
73 | |||
74 | return NULL; | 95 | return NULL; |
75 | } | 96 | } |
76 | 97 | ||
@@ -80,12 +101,11 @@ int main(int argc, char *argv[]) | |||
80 | char *string; | 101 | char *string; |
81 | char *op; | 102 | char *op; |
82 | char *pattern; | 103 | char *pattern; |
83 | bool match_at_left; | ||
84 | char *loc; | 104 | char *loc; |
85 | 105 | ||
86 | int i; | 106 | setvbuf(stdout, NULL, _IONBF, 0); |
87 | 107 | ||
88 | if (argc == 1) { | 108 | if (!argv[1]) { |
89 | puts( | 109 | puts( |
90 | "Usage: match <test> [test...]\n\n" | 110 | "Usage: match <test> [test...]\n\n" |
91 | "Where a <test> is the form: <string><op><match>\n" | 111 | "Where a <test> is the form: <string><op><match>\n" |
@@ -95,36 +115,34 @@ int main(int argc, char *argv[]) | |||
95 | return 1; | 115 | return 1; |
96 | } | 116 | } |
97 | 117 | ||
98 | for (i = 1; i < argc; ++i) { | 118 | while (*++argv) { |
99 | size_t off; | 119 | size_t off; |
100 | scan_t scan; | 120 | unsigned scan_flags; |
101 | |||
102 | printf("'%s': ", argv[i]); | ||
103 | 121 | ||
104 | string = strdup(argv[i]); | 122 | string = *argv; |
105 | off = strcspn(string, "#%"); | 123 | off = strcspn(string, "#%"); |
106 | if (!off) { | 124 | if (!off) { |
107 | printf("invalid format\n"); | 125 | printf("invalid format\n"); |
108 | free(string); | ||
109 | continue; | 126 | continue; |
110 | } | 127 | } |
111 | op = string + off; | 128 | op = string + off; |
112 | scan = pick_scan(op[0], op[1], &match_at_left); | 129 | scan_flags = pick_scan(op[0], op[1]); |
130 | |||
131 | printf("'%s': flags:%x, ", string, scan_flags); | ||
113 | pattern = op + 1; | 132 | pattern = op + 1; |
114 | if (op[0] == op[1]) | 133 | if (op[0] == op[1]) |
115 | op[1] = '\0', ++pattern; | 134 | pattern++; |
116 | op[0] = '\0'; | 135 | op[0] = '\0'; |
117 | 136 | ||
118 | loc = scan(string, pattern, match_at_left); | 137 | loc = scan_and_match(string, pattern, scan_flags); |
119 | 138 | ||
120 | if (match_at_left) { | 139 | if (scan_flags & SCAN_MATCH_LEFT_HALF) { |
121 | printf("'%s'\n", loc); | 140 | printf("'%s'\n", loc); |
122 | } else { | 141 | } else { |
123 | *loc = '\0'; | 142 | if (loc) |
143 | *loc = '\0'; | ||
124 | printf("'%s'\n", string); | 144 | printf("'%s'\n", string); |
125 | } | 145 | } |
126 | |||
127 | free(string); | ||
128 | } | 146 | } |
129 | 147 | ||
130 | return 0; | 148 | return 0; |
diff --git a/shell/match.h b/shell/match.h index c022ceb25..aa393ed1a 100644 --- a/shell/match.h +++ b/shell/match.h | |||
@@ -7,25 +7,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
7 | 7 | ||
8 | //TODO! Why ash.c still uses internal version?! | 8 | //TODO! Why ash.c still uses internal version?! |
9 | 9 | ||
10 | typedef char *(*scan_t)(char *string, char *match, bool match_at_left); | 10 | enum { |
11 | SCAN_MOVE_FROM_LEFT = (1 << 0), | ||
12 | SCAN_MOVE_FROM_RIGHT = (1 << 1), | ||
13 | SCAN_MATCH_LEFT_HALF = (1 << 2), | ||
14 | SCAN_MATCH_RIGHT_HALF = (1 << 3), | ||
15 | }; | ||
11 | 16 | ||
12 | char *scanleft(char *string, char *match, bool match_at_left); | 17 | char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags); |
13 | char *scanright(char *string, char *match, bool match_at_left); | ||
14 | 18 | ||
15 | static inline scan_t pick_scan(char op1, char op2, bool *match_at_left) | 19 | static inline unsigned pick_scan(char op1, char op2) |
16 | { | 20 | { |
17 | /* # - scanleft | 21 | unsigned scan_flags; |
18 | * ## - scanright | ||
19 | * % - scanright | ||
20 | * %% - scanleft | ||
21 | */ | ||
22 | if (op1 == '#') { | 22 | if (op1 == '#') { |
23 | *match_at_left = true; | 23 | scan_flags = SCAN_MATCH_LEFT_HALF + |
24 | return op1 == op2 ? scanright : scanleft; | 24 | (op1 == op2 ? SCAN_MOVE_FROM_RIGHT : SCAN_MOVE_FROM_LEFT); |
25 | } else { | 25 | } else { /* % */ |
26 | *match_at_left = false; | 26 | scan_flags = SCAN_MATCH_RIGHT_HALF + |
27 | return op1 == op2 ? scanleft : scanright; | 27 | (op1 == op2 ? SCAN_MOVE_FROM_LEFT : SCAN_MOVE_FROM_RIGHT); |
28 | } | 28 | } |
29 | return scan_flags; | ||
29 | } | 30 | } |
30 | 31 | ||
31 | POP_SAVED_FUNCTION_VISIBILITY | 32 | POP_SAVED_FUNCTION_VISIBILITY |