hush: optimize #[#] and %[%] for speed. size -2 bytes.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
parent
e298ce69ba
commit
701e127f7d
22
shell/hush.c
22
shell/hush.c
@ -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';
|
||||||
|
110
shell/match.c
110
shell/match.c
@ -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;
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user