|
|
5605af |
From e49e11e069fe7f214263be1782242b9b50f71eaa Mon Sep 17 00:00:00 2001
|
|
|
5605af |
From: Paul Smith <psmith@gnu.org>
|
|
|
5605af |
Date: Thu, 12 Nov 2020 17:00:39 -0500
|
|
|
5605af |
Subject: [SV 59093] Rewrite filter/filter-out to avoid large stack usage
|
|
|
5605af |
|
|
|
5605af |
* src/function.c (func_filter_filterout): Allocate arrays to hold
|
|
|
5605af |
pattern and word information rather than creating linked lists on
|
|
|
5605af |
the stack.
|
|
|
5605af |
* tests/scripts/functions/filter-out: Test large filters.
|
|
|
5605af |
|
|
|
5605af |
diff --git a/src/function.c b/src/function.c
|
|
|
5605af |
index 0917e0cd..5edfe8b3 100644
|
|
|
5605af |
--- a/src/function.c
|
|
|
5605af |
+++ b/src/function.c
|
|
|
5605af |
@@ -910,7 +910,6 @@ func_foreach (char *o, char **argv, const char *funcname UNUSED)
|
|
|
5605af |
|
|
|
5605af |
struct a_word
|
|
|
5605af |
{
|
|
|
5605af |
- struct a_word *next;
|
|
|
5605af |
struct a_word *chain;
|
|
|
5605af |
char *str;
|
|
|
5605af |
size_t length;
|
|
|
5605af |
@@ -941,7 +940,6 @@ a_word_hash_cmp (const void *x, const void *y)
|
|
|
5605af |
|
|
|
5605af |
struct a_pattern
|
|
|
5605af |
{
|
|
|
5605af |
- struct a_pattern *next;
|
|
|
5605af |
char *str;
|
|
|
5605af |
char *percent;
|
|
|
5605af |
size_t length;
|
|
|
5605af |
@@ -950,78 +948,84 @@ struct a_pattern
|
|
|
5605af |
static char *
|
|
|
5605af |
func_filter_filterout (char *o, char **argv, const char *funcname)
|
|
|
5605af |
{
|
|
|
5605af |
- struct a_word *wordhead;
|
|
|
5605af |
- struct a_word **wordtail;
|
|
|
5605af |
+ struct a_word *words;
|
|
|
5605af |
+ struct a_word *word_end;
|
|
|
5605af |
struct a_word *wp;
|
|
|
5605af |
- struct a_pattern *pathead;
|
|
|
5605af |
- struct a_pattern **pattail;
|
|
|
5605af |
+ struct a_pattern *patterns;
|
|
|
5605af |
+ struct a_pattern *pat_end;
|
|
|
5605af |
struct a_pattern *pp;
|
|
|
5605af |
+ size_t pat_count = 0, word_count = 0;
|
|
|
5605af |
|
|
|
5605af |
struct hash_table a_word_table;
|
|
|
5605af |
int is_filter = funcname[CSTRLEN ("filter")] == '\0';
|
|
|
5605af |
- const char *pat_iterator = argv[0];
|
|
|
5605af |
- const char *word_iterator = argv[1];
|
|
|
5605af |
+ const char *cp;
|
|
|
5605af |
int literals = 0;
|
|
|
5605af |
- int words = 0;
|
|
|
5605af |
int hashing = 0;
|
|
|
5605af |
char *p;
|
|
|
5605af |
size_t len;
|
|
|
5605af |
+ int doneany = 0;
|
|
|
5605af |
|
|
|
5605af |
- /* Chop ARGV[0] up into patterns to match against the words.
|
|
|
5605af |
- We don't need to preserve it because our caller frees all the
|
|
|
5605af |
- argument memory anyway. */
|
|
|
5605af |
+ /* Find the number of words and get memory for them. */
|
|
|
5605af |
+ cp = argv[1];
|
|
|
5605af |
+ while ((p = find_next_token (&cp, NULL)) != 0)
|
|
|
5605af |
+ ++word_count;
|
|
|
5605af |
|
|
|
5605af |
- pattail = &pathead;
|
|
|
5605af |
- while ((p = find_next_token (&pat_iterator, &len)) != 0)
|
|
|
5605af |
- {
|
|
|
5605af |
- struct a_pattern *pat = alloca (sizeof (struct a_pattern));
|
|
|
5605af |
+ if (!word_count)
|
|
|
5605af |
+ return o;
|
|
|
5605af |
+
|
|
|
5605af |
+ words = xcalloc (word_count * sizeof (struct a_word));
|
|
|
5605af |
+ word_end = words + word_count;
|
|
|
5605af |
|
|
|
5605af |
- *pattail = pat;
|
|
|
5605af |
- pattail = &pat->next;
|
|
|
5605af |
+ /* Find the number of patterns and get memory for them. */
|
|
|
5605af |
+ cp = argv[0];
|
|
|
5605af |
+ while ((p = find_next_token (&cp, NULL)) != 0)
|
|
|
5605af |
+ ++pat_count;
|
|
|
5605af |
|
|
|
5605af |
- if (*pat_iterator != '\0')
|
|
|
5605af |
- ++pat_iterator;
|
|
|
5605af |
+ patterns = xcalloc (pat_count * sizeof (struct a_pattern));
|
|
|
5605af |
+ pat_end = patterns + pat_count;
|
|
|
5605af |
+
|
|
|
5605af |
+ /* Chop argv[0] up into patterns to match against the words. */
|
|
|
5605af |
+
|
|
|
5605af |
+ cp = argv[0];
|
|
|
5605af |
+ pp = patterns;
|
|
|
5605af |
+ while ((p = find_next_token (&cp, &len)) != 0)
|
|
|
5605af |
+ {
|
|
|
5605af |
+ if (*cp != '\0')
|
|
|
5605af |
+ ++cp;
|
|
|
5605af |
|
|
|
5605af |
- pat->str = p;
|
|
|
5605af |
p[len] = '\0';
|
|
|
5605af |
- pat->percent = find_percent (p);
|
|
|
5605af |
- if (pat->percent == 0)
|
|
|
5605af |
+ pp->str = p;
|
|
|
5605af |
+ pp->percent = find_percent (p);
|
|
|
5605af |
+ if (pp->percent == 0)
|
|
|
5605af |
literals++;
|
|
|
5605af |
-
|
|
|
5605af |
/* find_percent() might shorten the string so LEN is wrong. */
|
|
|
5605af |
- pat->length = strlen (pat->str);
|
|
|
5605af |
+ pp->length = strlen (pp->str);
|
|
|
5605af |
+
|
|
|
5605af |
+ ++pp;
|
|
|
5605af |
}
|
|
|
5605af |
- *pattail = 0;
|
|
|
5605af |
|
|
|
5605af |
/* Chop ARGV[1] up into words to match against the patterns. */
|
|
|
5605af |
|
|
|
5605af |
- wordtail = &wordhead;
|
|
|
5605af |
- while ((p = find_next_token (&word_iterator, &len)) != 0)
|
|
|
5605af |
+ cp = argv[1];
|
|
|
5605af |
+ wp = words;
|
|
|
5605af |
+ while ((p = find_next_token (&cp, &len)) != 0)
|
|
|
5605af |
{
|
|
|
5605af |
- struct a_word *word = alloca (sizeof (struct a_word));
|
|
|
5605af |
-
|
|
|
5605af |
- *wordtail = word;
|
|
|
5605af |
- wordtail = &word->next;
|
|
|
5605af |
-
|
|
|
5605af |
- if (*word_iterator != '\0')
|
|
|
5605af |
- ++word_iterator;
|
|
|
5605af |
+ if (*cp != '\0')
|
|
|
5605af |
+ ++cp;
|
|
|
5605af |
|
|
|
5605af |
p[len] = '\0';
|
|
|
5605af |
- word->str = p;
|
|
|
5605af |
- word->length = len;
|
|
|
5605af |
- word->matched = 0;
|
|
|
5605af |
- word->chain = 0;
|
|
|
5605af |
- words++;
|
|
|
5605af |
+ wp->str = p;
|
|
|
5605af |
+ wp->length = len;
|
|
|
5605af |
+ ++wp;
|
|
|
5605af |
}
|
|
|
5605af |
- *wordtail = 0;
|
|
|
5605af |
|
|
|
5605af |
/* Only use a hash table if arg list lengths justifies the cost. */
|
|
|
5605af |
- hashing = (literals >= 2 && (literals * words) >= 10);
|
|
|
5605af |
+ hashing = (literals > 1 && (literals * word_count) >= 10);
|
|
|
5605af |
if (hashing)
|
|
|
5605af |
{
|
|
|
5605af |
- hash_init (&a_word_table, words, a_word_hash_1, a_word_hash_2,
|
|
|
5605af |
+ hash_init (&a_word_table, word_count, a_word_hash_1, a_word_hash_2,
|
|
|
5605af |
a_word_hash_cmp);
|
|
|
5605af |
- for (wp = wordhead; wp != 0; wp = wp->next)
|
|
|
5605af |
+ for (wp = words; wp < word_end; ++wp)
|
|
|
5605af |
{
|
|
|
5605af |
struct a_word *owp = hash_insert (&a_word_table, wp);
|
|
|
5605af |
if (owp)
|
|
|
5605af |
@@ -1029,51 +1033,49 @@ func_filter_filterout (char *o, char **argv, const char *funcname)
|
|
|
5605af |
}
|
|
|
5605af |
}
|
|
|
5605af |
|
|
|
5605af |
- if (words)
|
|
|
5605af |
+ /* Run each pattern through the words, killing words. */
|
|
|
5605af |
+ for (pp = patterns; pp < pat_end; ++pp)
|
|
|
5605af |
{
|
|
|
5605af |
- int doneany = 0;
|
|
|
5605af |
-
|
|
|
5605af |
- /* Run each pattern through the words, killing words. */
|
|
|
5605af |
- for (pp = pathead; pp != 0; pp = pp->next)
|
|
|
5605af |
+ if (pp->percent)
|
|
|
5605af |
+ for (wp = words; wp < word_end; ++wp)
|
|
|
5605af |
+ wp->matched |= pattern_matches (pp->str, pp->percent, wp->str);
|
|
|
5605af |
+ else if (hashing)
|
|
|
5605af |
{
|
|
|
5605af |
- if (pp->percent)
|
|
|
5605af |
- for (wp = wordhead; wp != 0; wp = wp->next)
|
|
|
5605af |
- wp->matched |= pattern_matches (pp->str, pp->percent, wp->str);
|
|
|
5605af |
- else if (hashing)
|
|
|
5605af |
+ struct a_word a_word_key;
|
|
|
5605af |
+ a_word_key.str = pp->str;
|
|
|
5605af |
+ a_word_key.length = pp->length;
|
|
|
5605af |
+ wp = hash_find_item (&a_word_table, &a_word_key);
|
|
|
5605af |
+ while (wp)
|
|
|
5605af |
{
|
|
|
5605af |
- struct a_word a_word_key;
|
|
|
5605af |
- a_word_key.str = pp->str;
|
|
|
5605af |
- a_word_key.length = pp->length;
|
|
|
5605af |
- wp = hash_find_item (&a_word_table, &a_word_key);
|
|
|
5605af |
- while (wp)
|
|
|
5605af |
- {
|
|
|
5605af |
- wp->matched |= 1;
|
|
|
5605af |
- wp = wp->chain;
|
|
|
5605af |
- }
|
|
|
5605af |
+ wp->matched |= 1;
|
|
|
5605af |
+ wp = wp->chain;
|
|
|
5605af |
}
|
|
|
5605af |
- else
|
|
|
5605af |
- for (wp = wordhead; wp != 0; wp = wp->next)
|
|
|
5605af |
- wp->matched |= (wp->length == pp->length
|
|
|
5605af |
- && strneq (pp->str, wp->str, wp->length));
|
|
|
5605af |
}
|
|
|
5605af |
+ else
|
|
|
5605af |
+ for (wp = words; wp < word_end; ++wp)
|
|
|
5605af |
+ wp->matched |= (wp->length == pp->length
|
|
|
5605af |
+ && strneq (pp->str, wp->str, wp->length));
|
|
|
5605af |
+ }
|
|
|
5605af |
|
|
|
5605af |
- /* Output the words that matched (or didn't, for filter-out). */
|
|
|
5605af |
- for (wp = wordhead; wp != 0; wp = wp->next)
|
|
|
5605af |
- if (is_filter ? wp->matched : !wp->matched)
|
|
|
5605af |
- {
|
|
|
5605af |
- o = variable_buffer_output (o, wp->str, strlen (wp->str));
|
|
|
5605af |
- o = variable_buffer_output (o, " ", 1);
|
|
|
5605af |
- doneany = 1;
|
|
|
5605af |
- }
|
|
|
5605af |
+ /* Output the words that matched (or didn't, for filter-out). */
|
|
|
5605af |
+ for (wp = words; wp < word_end; ++wp)
|
|
|
5605af |
+ if (is_filter ? wp->matched : !wp->matched)
|
|
|
5605af |
+ {
|
|
|
5605af |
+ o = variable_buffer_output (o, wp->str, strlen (wp->str));
|
|
|
5605af |
+ o = variable_buffer_output (o, " ", 1);
|
|
|
5605af |
+ doneany = 1;
|
|
|
5605af |
+ }
|
|
|
5605af |
|
|
|
5605af |
- if (doneany)
|
|
|
5605af |
- /* Kill the last space. */
|
|
|
5605af |
- --o;
|
|
|
5605af |
- }
|
|
|
5605af |
+ if (doneany)
|
|
|
5605af |
+ /* Kill the last space. */
|
|
|
5605af |
+ --o;
|
|
|
5605af |
|
|
|
5605af |
if (hashing)
|
|
|
5605af |
hash_free (&a_word_table, 0);
|
|
|
5605af |
|
|
|
5605af |
+ free (patterns);
|
|
|
5605af |
+ free (words);
|
|
|
5605af |
+
|
|
|
5605af |
return o;
|
|
|
5605af |
}
|
|
|
5605af |
|
|
|
5605af |
diff --git a/tests/scripts/functions/filter-out b/tests/scripts/functions/filter-out
|
|
|
5605af |
index 1fe4819d..dec5343e 100644
|
|
|
5605af |
--- a/tests/scripts/functions/filter-out
|
|
|
5605af |
+++ b/tests/scripts/functions/filter-out
|
|
|
5605af |
@@ -27,6 +27,22 @@ all: ; @echo '$(files1) $(files2)'
|
|
|
5605af |
!,
|
|
|
5605af |
'', "foo.elc foo.elc\n");
|
|
|
5605af |
|
|
|
5605af |
+# Force use of hash (see function.c:func_filter_filterout for params)
|
|
|
5605af |
+
|
|
|
5605af |
+my $base = 'foo.1 foo.2 foo.3 foo.4 foo.5 foo.6 foo.7 foo.8 foo.9 foo.10';
|
|
|
5605af |
+
|
|
|
5605af |
+my $base10 = join(' ', ($base) x 10);
|
|
|
5605af |
+my $out3 = join(' ', ('foo.3') x 10);
|
|
|
5605af |
+my $out456 = join(' ', ('foo.4 foo.5 foo.6') x 10);
|
|
|
5605af |
+
|
|
|
5605af |
+run_make_test("words := $base10" . q!
|
|
|
5605af |
+files1 := $(filter %.3, $(words))
|
|
|
5605af |
+files2 := $(filter %.4 foo.5 foo.6, $(words))
|
|
|
5605af |
+all: ; @echo '$(files1) $(files2)'
|
|
|
5605af |
+!,
|
|
|
5605af |
+ '', "$out3 $out456\n");
|
|
|
5605af |
+
|
|
|
5605af |
+
|
|
|
5605af |
# Escaped patterns
|
|
|
5605af |
run_make_test(q!all:;@echo '$(filter foo\%bar,foo%bar fooXbar)'!,
|
|
|
5605af |
'', "foo%bar\n");
|