From 5aaeb550b76f063ffddef8587d9d91f7fddc4406 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Oct 2021 03:32:20 +0200 Subject: [PATCH] ash: speed up ${x//\*/|} too, make it independent of ASH_OPTIMIZE_FOR_SIZE function old new delta subevalvar 1503 1545 +42 Signed-off-by: Denys Vlasenko --- shell/ash.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index e8ec0b1a6..199975191 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7251,7 +7251,7 @@ subevalvar(char *start, char *str, int strloc, #if BASH_PATTERN_SUBST workloc = expdest - (char *)stackblock(); if (subtype == VSREPLACE || subtype == VSREPLACEALL) { - size_t no_meta_len; + size_t no_meta_len, first_escaped; int len; char *idx, *end; @@ -7269,28 +7269,34 @@ subevalvar(char *start, char *str, int strloc, if (str[0] == '\0') goto out1; - no_meta_len = (ENABLE_ASH_OPTIMIZE_FOR_SIZE || strpbrk(str, "*?[\\")) ? 0 : strlen(str); + first_escaped = (str[0] == '\\' && str[1]); + /* "first_escaped" trick allows to treat e.g. "\*no_glob_chars" + * as literal too (as it is semi-common, and easy to accomodate + * by just using str + 1). + */ + no_meta_len = strpbrk(str + first_escaped * 2, "*?[\\") ? 0 : strlen(str); len = 0; idx = startp; end = str - 1; while (idx <= end) { try_to_match: if (no_meta_len == 0) { - /* pattern has meta chars, have to glob; or ENABLE_ASH_OPTIMIZE_FOR_SIZE */ + /* pattern has meta chars, have to glob */ loc = scanright(idx, rmesc, rmescend, str, quotes, /*match_at_start:*/ 1); } else { /* Testcase for very slow replace (performs about 22k replaces): * x=:::::::::::::::::::::: * x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;echo ${#x} * echo "${x//:/|}" + * To test "first_escaped" logic, replace : with *. */ - if (strncmp(rmesc, str, no_meta_len) != 0) + if (strncmp(rmesc, str + first_escaped, no_meta_len - first_escaped) != 0) goto no_match; loc = idx; if (!quotes) { - loc += no_meta_len; + loc += no_meta_len - first_escaped; } else { - size_t n = no_meta_len; + size_t n = no_meta_len - first_escaped; do { if ((unsigned char)*loc == CTLESC) loc++;