Blame SOURCES/make-4.3-filter-out.patch

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");