[git commit] ash: speed up ${x//\*/|} too, make it independent of ASH_OPTIMIZE_FOR_SIZE

Denys Vlasenko vda.linux at googlemail.com
Sat Oct 9 01:33:22 UTC 2021


commit: https://git.busybox.net/busybox/commit/?id=5aaeb550b76f063ffddef8587d9d91f7fddc4406
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
subevalvar                                          1503    1545     +42

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 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++;


More information about the busybox-cvs mailing list