In most cases, find_char_unquote has a single stopchar. In that case we can look for it using strchr's optimized implementation.
The resulting speedup on QEMU's noop build is 4.4% (from 14.5 seconds to 13.8). * read.c (find_char_unquote): Rename to find_map_unquote. Replace with an implementation optimized for the case where the stopchar is a singleton. Adjust all callers. --- read.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/read.c b/read.c index 0883100..c2d42d2 100644 --- a/read.c +++ b/read.c @@ -153,7 +153,8 @@ static void record_target_var (struct nameseq *filenames, char *defn, static enum make_word_type get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length); static void remove_comments (char *line); -static char *find_char_unquote (char *string, int map); +static char *find_map_unquote (char *string, int map); +static char *find_char_unquote (char *string, int stop); static char *unescape_char (char *string, int c); @@ -1008,7 +1009,7 @@ eval (struct ebuffer *ebuf, int set_default) /* Search the line for an unquoted ; that is not after an unquoted #. */ - cmdleft = find_char_unquote (line, MAP_SEMI|MAP_COMMENT|MAP_VARIABLE); + cmdleft = find_map_unquote (line, MAP_SEMI|MAP_COMMENT|MAP_VARIABLE); if (cmdleft != 0 && *cmdleft == '#') { /* We found a comment before a semicolon. */ @@ -1055,7 +1056,7 @@ eval (struct ebuffer *ebuf, int set_default) if (cmdleft == 0) { /* Look for a semicolon in the expanded line. */ - cmdleft = find_char_unquote (p2, MAP_SEMI); + cmdleft = find_char_unquote (p2, ';'); if (cmdleft != 0) { @@ -1082,7 +1083,7 @@ eval (struct ebuffer *ebuf, int set_default) } } - colonp = find_char_unquote (p2, MAP_COLON); + colonp = find_char_unquote (p2, ':'); #ifdef HAVE_DOS_PATHS /* The drive spec brain-damage strikes again... */ /* Note that the only separators of targets in this context @@ -1091,7 +1092,7 @@ eval (struct ebuffer *ebuf, int set_default) while (colonp && (colonp[1] == '/' || colonp[1] == '\\') && colonp > p2 && isalpha ((unsigned char)colonp[-1]) && (colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0)) - colonp = find_char_unquote (colonp + 1, MAP_COLON); + colonp = find_char_unquote (colonp + 1, ':'); #endif if (colonp != 0) break; @@ -1184,7 +1185,7 @@ eval (struct ebuffer *ebuf, int set_default) /* This is a normal target, _not_ a target-specific variable. Unquote any = in the dependency list. */ - find_char_unquote (lb_next, MAP_EQUALS); + find_char_unquote (lb_next, '='); /* Remember the command prefix for this target. */ prefix = cmd_prefix; @@ -1202,7 +1203,7 @@ eval (struct ebuffer *ebuf, int set_default) /* Look for a semicolon in the expanded line. */ if (cmdleft == 0) { - cmdleft = find_char_unquote (p2, MAP_SEMI); + cmdleft = find_char_unquote (p2, ';'); if (cmdleft != 0) *(cmdleft++) = '\0'; } @@ -1405,7 +1406,7 @@ remove_comments (char *line) { char *comment; - comment = find_char_unquote (line, MAP_COMMENT); + comment = find_char_unquote (line, '#'); if (comment != 0) /* Cut off the line at the #. */ @@ -2234,7 +2235,7 @@ record_files (struct nameseq *filenames, const char *pattern, STOPCHAR _cannot_ be '$' if IGNOREVARS is true. */ static char * -find_char_unquote (char *string, int map) +find_map_unquote (char *string, int map) { unsigned int string_len = 0; char *p = string; @@ -2315,6 +2316,46 @@ find_char_unquote (char *string, int map) return 0; } +static char * +find_char_unquote (char *string, int stop) +{ + unsigned int string_len = 0; + char *p = string; + + while (1) + { + p = strchr(p, stop); + + if (!p) + return NULL; + + if (p > string && p[-1] == '\\') + { + /* Search for more backslashes. */ + int i = -2; + while (&p[i] >= string && p[i] == '\\') + --i; + ++i; + /* Only compute the length if really needed. */ + if (string_len == 0) + string_len = strlen (string); + /* The number of backslashes is now -I. + Copy P over itself to swallow half of them. */ + memmove (&p[i], &p[i/2], (string_len - (p - string)) - (i/2) + 1); + p += i/2; + if (i % 2 == 0) + /* All the backslashes quoted each other; the STOPCHAR was + unquoted. */ + return p; + + /* The STOPCHAR was quoted by a backslash. Look for another. */ + } + else + /* No backslash in sight. */ + return p; + } +} + /* Unescape a character in a string. The string is compressed onto itself. */ static char * @@ -2368,7 +2409,7 @@ unescape_char (char *string, int c) char * find_percent (char *pattern) { - return find_char_unquote (pattern, MAP_PERCENT); + return find_char_unquote (pattern, '%'); } /* Search STRING for an unquoted % and handle quoting. Returns a pointer to @@ -3072,7 +3113,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap, /* There are names left, so find the end of the next name. Throughout this iteration S points to the start. */ s = p; - p = find_char_unquote (p, stopmap|MAP_VMSCOMMA|MAP_BLANK); + p = find_map_unquote (p, stopmap|MAP_VMSCOMMA|MAP_BLANK); #ifdef VMS /* convert comma separated list to space separated */ if (p && *p == ',') @@ -3081,7 +3122,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap, #ifdef _AMIGA if (p && STOP_SET (*p, stopmap & MAP_COLON) && !(ISSPACE (p[1]) || !p[1] || ISSPACE (p[-1]))) - p = find_char_unquote (p+1, stopmap|MAP_VMSCOMMA|MAP_BLANK); + p = find_map_unquote (p+1, stopmap|MAP_VMSCOMMA|MAP_BLANK); #endif #ifdef HAVE_DOS_PATHS /* For DOS paths, skip a "C:\..." or a "C:/..." until we find the @@ -3091,7 +3132,7 @@ parse_file_seq (char **stringp, unsigned int size, int stopmap, if (stopmap | MAP_COLON) while (p != 0 && !ISSPACE (*p) && (p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1])) - p = find_char_unquote (p + 1, stopmap|MAP_VMSCOMMA|MAP_BLANK); + p = find_map_unquote (p + 1, stopmap|MAP_VMSCOMMA|MAP_BLANK); #endif if (p == 0) p = s + strlen (s); -- 2.7.4 _______________________________________________ Bug-make mailing list Bug-make@gnu.org https://lists.gnu.org/mailman/listinfo/bug-make