hush: optimize #[#] and %[%] for speed. size -2 bytes.

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
Denys Vlasenko 2010-09-04 21:21:07 +02:00
parent e298ce69ba
commit 701e127f7d
3 changed files with 89 additions and 72 deletions

View File

@ -2829,23 +2829,21 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
* Then var's value is matched to it and matching part removed. * Then var's value is matched to it and matching part removed.
*/ */
if (val) { if (val) {
bool match_at_left; char *exp_exp_word;
char *loc; char *loc;
scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); unsigned scan_flags = pick_scan(exp_op, *exp_word);
if (exp_op == *exp_word) /* ## or %% */ if (exp_op == *exp_word) /* ## or %% */
exp_word++; exp_word++;
val = to_be_freed = xstrdup(val); val = to_be_freed = xstrdup(val);
{ exp_exp_word = expand_pseudo_dquoted(exp_word);
char *exp_exp_word = expand_pseudo_dquoted(exp_word); if (exp_exp_word)
if (exp_exp_word) exp_word = exp_exp_word;
exp_word = exp_exp_word; loc = scan_and_match(to_be_freed, exp_word, scan_flags);
loc = scan(to_be_freed, exp_word, match_at_left); //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
//bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", // exp_op, to_be_freed, exp_word, loc);
// exp_op, to_be_freed, exp_word, loc); free(exp_exp_word);
free(exp_exp_word);
}
if (loc) { /* match was found */ if (loc) { /* match was found */
if (match_at_left) /* # or ## */ if (scan_flags & SCAN_MATCH_LEFT_HALF) /* # or ## */
val = loc; val = loc;
else /* % or %% */ else /* % or %% */
*loc = '\0'; *loc = '\0';

View File

@ -18,6 +18,9 @@
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
# include <unistd.h> # include <unistd.h>
# define FAST_FUNC /* nothing */
# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
#else #else
# include "libbb.h" # include "libbb.h"
#endif #endif
@ -26,16 +29,48 @@
#define pmatch(a, b) !fnmatch((a), (b), 0) #define pmatch(a, b) !fnmatch((a), (b), 0)
char *scanleft(char *string, char *pattern, bool match_at_left) char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
{ {
char c; char *loc;
char *loc = string; char *end;
unsigned len = strlen(string);
int early_exit;
while (1) { /* We can stop the scan early only if the string part
* we are matching against is shrinking, and the pattern has
* an unquoted "star" at the corresponding end. There are two cases.
* Case 1:
* "qwerty" does not match against pattern "*zy",
* no point in trying to match "werty", "erty" etc:
*/
early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
if (flags & SCAN_MOVE_FROM_LEFT) {
loc = string;
end = string + len + 1;
} else {
loc = string + len;
end = string - 1;
if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
/* Case 2:
* "qwerty" does not match against pattern "qz*",
* no point in trying to match "qwert", "qwer" etc:
*/
const char *p = pattern + strlen(pattern);
if (--p >= pattern && *p == '*') {
early_exit = 1;
while (--p >= pattern && *p == '\\')
early_exit ^= 1;
}
}
}
while (loc != end) {
char c;
int match; int match;
c = *loc; c = *loc;
if (match_at_left) { if (flags & SCAN_MATCH_LEFT_HALF) {
*loc = '\0'; *loc = '\0';
match = pmatch(pattern, string); match = pmatch(pattern, string);
*loc = c; *loc = c;
@ -44,33 +79,19 @@ char *scanleft(char *string, char *pattern, bool match_at_left)
} }
if (match) if (match)
return loc; return loc;
if (!c) if (early_exit) {
return NULL; #ifdef STANDALONE
loc++; printf("(early exit) ");
} #endif
} break;
char *scanright(char *string, char *pattern, bool match_at_left)
{
char c;
char *loc = string + strlen(string);
while (loc >= string) {
int match;
c = *loc;
if (match_at_left) {
*loc = '\0';
match = pmatch(pattern, string);
*loc = c;
} else {
match = pmatch(pattern, loc);
} }
if (match)
return loc;
loc--;
}
if (flags & SCAN_MOVE_FROM_LEFT) {
loc++;
} else {
loc--;
}
}
return NULL; return NULL;
} }
@ -80,12 +101,11 @@ int main(int argc, char *argv[])
char *string; char *string;
char *op; char *op;
char *pattern; char *pattern;
bool match_at_left;
char *loc; char *loc;
int i; setvbuf(stdout, NULL, _IONBF, 0);
if (argc == 1) { if (!argv[1]) {
puts( puts(
"Usage: match <test> [test...]\n\n" "Usage: match <test> [test...]\n\n"
"Where a <test> is the form: <string><op><match>\n" "Where a <test> is the form: <string><op><match>\n"
@ -95,36 +115,34 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
for (i = 1; i < argc; ++i) { while (*++argv) {
size_t off; size_t off;
scan_t scan; unsigned scan_flags;
printf("'%s': ", argv[i]); string = *argv;
string = strdup(argv[i]);
off = strcspn(string, "#%"); off = strcspn(string, "#%");
if (!off) { if (!off) {
printf("invalid format\n"); printf("invalid format\n");
free(string);
continue; continue;
} }
op = string + off; op = string + off;
scan = pick_scan(op[0], op[1], &match_at_left); scan_flags = pick_scan(op[0], op[1]);
printf("'%s': flags:%x, ", string, scan_flags);
pattern = op + 1; pattern = op + 1;
if (op[0] == op[1]) if (op[0] == op[1])
op[1] = '\0', ++pattern; pattern++;
op[0] = '\0'; op[0] = '\0';
loc = scan(string, pattern, match_at_left); loc = scan_and_match(string, pattern, scan_flags);
if (match_at_left) { if (scan_flags & SCAN_MATCH_LEFT_HALF) {
printf("'%s'\n", loc); printf("'%s'\n", loc);
} else { } else {
*loc = '\0'; if (loc)
*loc = '\0';
printf("'%s'\n", string); printf("'%s'\n", string);
} }
free(string);
} }
return 0; return 0;

View File

@ -7,25 +7,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
//TODO! Why ash.c still uses internal version?! //TODO! Why ash.c still uses internal version?!
typedef char *(*scan_t)(char *string, char *match, bool match_at_left); enum {
SCAN_MOVE_FROM_LEFT = (1 << 0),
SCAN_MOVE_FROM_RIGHT = (1 << 1),
SCAN_MATCH_LEFT_HALF = (1 << 2),
SCAN_MATCH_RIGHT_HALF = (1 << 3),
};
char *scanleft(char *string, char *match, bool match_at_left); char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags);
char *scanright(char *string, char *match, bool match_at_left);
static inline scan_t pick_scan(char op1, char op2, bool *match_at_left) static inline unsigned pick_scan(char op1, char op2)
{ {
/* # - scanleft unsigned scan_flags;
* ## - scanright
* % - scanright
* %% - scanleft
*/
if (op1 == '#') { if (op1 == '#') {
*match_at_left = true; scan_flags = SCAN_MATCH_LEFT_HALF +
return op1 == op2 ? scanright : scanleft; (op1 == op2 ? SCAN_MOVE_FROM_RIGHT : SCAN_MOVE_FROM_LEFT);
} else { } else { /* % */
*match_at_left = false; scan_flags = SCAN_MATCH_RIGHT_HALF +
return op1 == op2 ? scanleft : scanright; (op1 == op2 ? SCAN_MOVE_FROM_LEFT : SCAN_MOVE_FROM_RIGHT);
} }
return scan_flags;
} }
POP_SAVED_FUNCTION_VISIBILITY POP_SAVED_FUNCTION_VISIBILITY