Module Name:    src
Committed By:   rillig
Date:           Mon Apr 12 18:48:00 UTC 2021

Modified Files:
        src/usr.bin/make: str.h var.c

Log Message:
make: reduce memory allocation and strlen calls in modifier ':from=to'

Previously, SysVMatch was quite verbose and felt like hand-optimized
assembler code, which made it difficult to discover the underlying idea
of the code.

All this code was replaced with two simple calls to Substring_HasPrefix
and Substring_HasSuffix.  Now that the operands of that modifier are no
longer passed as C strings, there is no need to collect all information
in a single scan through the word and the pattern.

It was not necessary to call Var_Subst unconditionally.  Calling it only
when the string contains a '$' saves another memory allocation and two
string copies (because of the Buf_DoneDataCompact).

No functional change.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/usr.bin/make/str.h
cvs rdiff -u -r1.924 -r1.925 src/usr.bin/make/var.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/make/str.h
diff -u src/usr.bin/make/str.h:1.5 src/usr.bin/make/str.h:1.6
--- src/usr.bin/make/str.h:1.5	Sun Apr 11 22:53:45 2021
+++ src/usr.bin/make/str.h	Mon Apr 12 18:48:00 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: str.h,v 1.5 2021/04/11 22:53:45 rillig Exp $	*/
+/*	$NetBSD: str.h,v 1.6 2021/04/12 18:48:00 rillig Exp $	*/
 
 /*
  Copyright (c) 2021 Roland Illig <ril...@netbsd.org>
@@ -190,6 +190,21 @@ Substring_Sub(Substring sub, size_t star
 	return Substring_Init(sub.start + start, sub.start + end);
 }
 
+MAKE_INLINE bool
+Substring_HasPrefix(Substring sub, Substring prefix)
+{
+	return Substring_Length(sub) >= Substring_Length(prefix) &&
+	       memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
+}
+
+MAKE_INLINE bool
+Substring_HasSuffix(Substring sub, Substring suffix)
+{
+	size_t suffixLen = Substring_Length(suffix);
+	return Substring_Length(sub) >= suffixLen &&
+	       memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
+}
+
 MAKE_INLINE FStr
 Substring_Str(Substring sub)
 {
@@ -197,6 +212,17 @@ Substring_Str(Substring sub)
 }
 
 MAKE_INLINE const char *
+Substring_SkipFirst(Substring sub, char ch)
+{
+	const char *p;
+
+	for (p = sub.start; p != sub.end; p++)
+		if (*p == ch)
+			return p + 1;
+	return sub.start;
+}
+
+MAKE_INLINE const char *
 Substring_LastIndex(Substring sub, char ch)
 {
 	const char *p;

Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.924 src/usr.bin/make/var.c:1.925
--- src/usr.bin/make/var.c:1.924	Mon Apr 12 13:28:35 2021
+++ src/usr.bin/make/var.c	Mon Apr 12 18:48:00 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: var.c,v 1.924 2021/04/12 13:28:35 rillig Exp $	*/
+/*	$NetBSD: var.c,v 1.925 2021/04/12 18:48:00 rillig Exp $	*/
 
 /*
  * Copyright (c) 1988, 1989, 1990, 1993
@@ -140,7 +140,7 @@
 #include "metachar.h"
 
 /*	"@(#)var.c	8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.924 2021/04/12 13:28:35 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.925 2021/04/12 18:48:00 rillig Exp $");
 
 /*
  * Variables are defined using one of the VAR=value assignments.  Their
@@ -1441,69 +1441,11 @@ ModifyWord_NoMatch(Substring word, SepBu
 }
 
 #ifdef SYSVVARSUB
-
-/*
- * Check word against pattern for a match (% is a wildcard).
- *
- * Input:
- *	word		Word to examine
- *	pattern		Pattern to examine against
- *
- * Results:
- *	Returns the start of the match, or NULL.
- *	out_match_len returns the length of the match, if any.
- *	out_hasPercent returns whether the pattern contains a percent.
- */
-/* TODO: migrate to using Substring */
-static const char *
-SysVMatch(const char *word, const char *pattern,
-	  size_t *out_match_len, bool *out_hasPercent)
-{
-	const char *p = pattern;
-	const char *w = word;
-	const char *percent;
-	size_t w_len;
-	size_t p_len;
-	const char *w_tail;
-
-	*out_hasPercent = false;
-	percent = strchr(p, '%');
-	if (percent != NULL) {		/* ${VAR:...%...=...} */
-		*out_hasPercent = true;
-		if (w[0] == '\0')
-			return NULL;	/* empty word does not match pattern */
-
-		/* check that the prefix matches */
-		for (; p != percent && *w != '\0' && *w == *p; w++, p++)
-			continue;
-		if (p != percent)
-			return NULL;	/* No match */
-
-		p++;		/* Skip the percent */
-		if (*p == '\0') {
-			/* No more pattern, return the rest of the string */
-			*out_match_len = strlen(w);
-			return w;
-		}
-	}
-
-	/* Test whether the tail matches */
-	w_len = strlen(w);
-	p_len = strlen(p);
-	if (w_len < p_len)
-		return NULL;
-
-	w_tail = w + w_len - p_len;
-	if (memcmp(p, w_tail, p_len) != 0)
-		return NULL;
-
-	*out_match_len = (size_t)(w_tail - w);
-	return w;
-}
-
 struct ModifyWord_SYSVSubstArgs {
 	GNode *scope;
-	const char *lhs;
+	Substring lhsPrefix;
+	bool lhsPercent;
+	Substring lhsSuffix;
 	const char *rhs;
 };
 
@@ -1512,43 +1454,40 @@ static void
 ModifyWord_SYSVSubst(Substring word, SepBuf *buf, void *data)
 {
 	const struct ModifyWord_SYSVSubstArgs *args = data;
-	char *rhs_expanded;
-	const char *rhs;
+	FStr rhs;
+	char *rhsExp;
 	const char *percent;
-	size_t match_len;
-	bool lhsPercent;
-	const char *match;
 
-	assert(word.end[0] == '\0');	/* assume null-terminated word */
-	match = SysVMatch(word.start, args->lhs, &match_len, &lhsPercent);
-	if (match == NULL) {
-		SepBuf_AddSubstring(buf, word);
+	if (Substring_IsEmpty(word))
 		return;
-	}
 
-	/*
-	 * Append rhs to the buffer, substituting the first '%' with the
-	 * match, but only if the lhs had a '%' as well.
-	 */
-
-	(void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded);
-	/* TODO: handle errors */
+	if (!Substring_HasPrefix(word, args->lhsPrefix))
+		goto no_match;
+	if (!Substring_HasSuffix(word, args->lhsSuffix))
+		goto no_match;
+
+	rhs = FStr_InitRefer(args->rhs);
+	if (strchr(rhs.str, '$') != NULL) {
+		(void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhsExp);
+		/* TODO: handle errors */
+		rhs = FStr_InitOwn(rhsExp);
+	}
 
-	rhs = rhs_expanded;
-	percent = strchr(rhs, '%');
+	percent = args->lhsPercent ? strchr(rhs.str, '%') : NULL;
 
-	if (percent != NULL && lhsPercent) {
-		/* Copy the prefix of the replacement pattern */
-		SepBuf_AddBytesBetween(buf, rhs, percent);
-		rhs = percent + 1;
-	}
-	if (percent != NULL || !lhsPercent)
-		SepBuf_AddBytes(buf, match, match_len);
+	if (percent != NULL)
+		SepBuf_AddBytesBetween(buf, rhs.str, percent);
+	if (percent != NULL || !args->lhsPercent)
+		SepBuf_AddBytesBetween(buf,
+		    word.start + Substring_Length(args->lhsPrefix),
+		    word.end - Substring_Length(args->lhsSuffix));
+	SepBuf_AddStr(buf, percent != NULL ? percent + 1 : rhs.str);
 
-	/* Append the suffix of the replacement pattern */
-	SepBuf_AddStr(buf, rhs);
+	FStr_Done(&rhs);
+	return;
 
-	free(rhs_expanded);
+no_match:
+	SepBuf_AddSubstring(buf, word);
 }
 #endif
 
@@ -3646,8 +3585,11 @@ ApplyModifier_SysV(const char **pp, ModC
 {
 	Expr *expr = ch->expr;
 	VarParseResult res;
-	LazyBuf buf;
-	FStr lhs, rhs;
+	LazyBuf lhsBuf, rhsBuf;
+	FStr rhs;
+	struct ModifyWord_SYSVSubstArgs args;
+	Substring lhs;
+	const char *lhsSuffix;
 
 	const char *mod = *pp;
 	bool eqFound = false;
@@ -3672,33 +3614,38 @@ ApplyModifier_SysV(const char **pp, ModC
 	if (*p != ch->endc || !eqFound)
 		return AMR_UNKNOWN;
 
-	res = ParseModifierPart(pp, '=', expr->emode, ch, &buf);
+	res = ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf);
 	if (res != VPR_OK)
 		return AMR_CLEANUP;
-	lhs = LazyBuf_DoneGet(&buf);
 
 	/* The SysV modifier lasts until the end of the variable expression. */
-	res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf);
+	res = ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf);
 	if (res != VPR_OK) {
-		FStr_Done(&lhs);
+		LazyBuf_Done(&lhsBuf);
 		return AMR_CLEANUP;
 	}
-	rhs = LazyBuf_DoneGet(&buf);
+	rhs = LazyBuf_DoneGet(&rhsBuf);
 
 	(*pp)--;		/* Go back to the ch->endc. */
 
-	if (lhs.str[0] == '\0' && expr->value.str[0] == '\0') {
-		/* Do not turn an empty expression into non-empty. */
-	} else {
-		struct ModifyWord_SYSVSubstArgs args;
+	/* Do not turn an empty expression into non-empty. */
+	if (lhsBuf.len == 0 && expr->value.str[0] == '\0')
+		goto done;
 
-		args.scope = expr->scope;
-		args.lhs = lhs.str;
-		args.rhs = rhs.str;
-		ModifyWords(ch, ModifyWord_SYSVSubst, &args, ch->oneBigWord);
-	}
-	FStr_Done(&lhs);
-	FStr_Done(&rhs);
+	lhs = LazyBuf_Get(&lhsBuf);
+	lhsSuffix = Substring_SkipFirst(lhs, '%');
+
+	args.scope = expr->scope;
+	args.lhsPrefix = Substring_Init(lhs.start,
+	    lhsSuffix != lhs.start ? lhsSuffix - 1 : lhs.start);
+	args.lhsPercent = lhsSuffix != lhs.start;
+	args.lhsSuffix = Substring_Init(lhsSuffix, lhs.end);
+	args.rhs = rhs.str;
+
+	ModifyWords(ch, ModifyWord_SYSVSubst, &args, ch->oneBigWord);
+
+done:
+	LazyBuf_Done(&lhsBuf);
 	return AMR_OK;
 }
 #endif

Reply via email to