]> git.ipfire.org Git - pakfire.git/commitdiff
parser: Improve performance of expanding strings
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 3 Mar 2021 11:43:20 +0000 (11:43 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 3 Mar 2021 11:43:20 +0000 (11:43 +0000)
By using pakfire_string_replace, all occurrences of a variable will be
replaced at once until no variables are being left.

It can still happen, that the same variable is being matched later again
(multiple times even), but it looks to me like adding any caching
mechanism adds a lot more overhead.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/parser.c

index 44bf29aa0af11eca038f123237d958549f3ca422..cceaed924b184e2baba297481e467efe0ac5984d 100644 (file)
@@ -319,6 +319,21 @@ static struct pakfire_parser_declaration* pakfire_parser_find_declaration(
        return d;
 }
 
+static char* extract_string(const char* buffer, const regmatch_t* match) {
+       // Determine the length of the string
+       size_t l = match->rm_eo - match->rm_so;
+
+       // Allocate sufficient memory
+       char* s = malloc(l + 1);
+       if (!s)
+               return NULL;
+
+       // Copy string
+       snprintf(s, l + 1, "%s", buffer + match->rm_so);
+
+       return s;
+}
+
 PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser,
                const char* namespace, const char* value) {
        // Return NULL when the value is NULL
@@ -345,6 +360,8 @@ PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser,
        // Create a working copy of the string we are expanding
        char* buffer = strdup(value);
 
+       char* variable = NULL;
+       char* pattern = NULL;
        const size_t max_groups = 2;
        regmatch_t groups[max_groups];
 
@@ -359,58 +376,68 @@ PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser,
                        break;
                }
 
-               // Set offsets to the matched variable name
-               off_t start = groups[1].rm_so, end = groups[1].rm_eo;
-
-               // Get the name of the variable
-               char* variable = malloc(end - start + 1);
-               snprintf(variable, end - start + 1, "%s", buffer + start);
+               // Find the variable name
+               variable = extract_string(buffer, &groups[1]);
+               if (!variable)
+                       goto ERROR;
 
                DEBUG(parser->pakfire, "Expanding variable: %s\n", variable);
 
                // Search for a declaration of this variable
-               struct pakfire_parser_declaration* v =
+               struct pakfire_parser_declaration* d =
                        pakfire_parser_find_declaration(parser, namespace, variable);
 
-               const char* value = NULL;
-               if (v && v->value) {
-                       DEBUG(parser->pakfire, "Replacing %%{%s} with %s = '%s'\n",
-                               variable, v->name, v->value);
+               // What is its value?
+               const char* repl = NULL;
+               if (d && d->value) {
+                       DEBUG(parser->pakfire, "Replacing %%{%s} with %s.%s = '%s'\n", variable,
+                               d->namespace, d->name, d->value);
 
-                       value = v->value;
+                       repl = d->value;
                } else {
-                       DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n",
-                               variable);
+                       DEBUG(parser->pakfire, "Replacing %%{%s} with an empty string\n", variable);
+
+                       repl = "";
                }
 
-               // Reset offsets to the whole matched string
-               start = groups[0].rm_so; end = groups[0].rm_eo;
+               // Find the entire matched pattern
+               pattern = extract_string(buffer, &groups[0]);
+               if (!pattern)
+                       goto ERROR;
 
-               // Length of the new buffer
-               size_t length = strlen(buffer) - (end - start) + ((value) ? strlen(value) : 0);
+               // Replace all occurrences
+               char* tmp = pakfire_string_replace(buffer, pattern, repl);
+               if (!tmp)
+                       goto ERROR;
 
-               char* b = malloc(length + 1);
+               // Replace buffer
+               free(buffer);
+               buffer = tmp;
 
-               // Copy buffer up to the beginning of the match
-               snprintf(b, start + 1, "%s", buffer);
+               // Free resources
+               free(variable);
+               free(pattern);
+               variable = pattern = NULL;
 
-               // Append the new value (if any)
-               if (value)
-                       strcat(b, value);
-
-               // Append the rest of the buffer
-               if (buffer + end)
-                       strcat(b, buffer + end);
+               DEBUG(parser->pakfire, "New buffer: %s\n", buffer);
+       }
 
-               DEBUG(parser->pakfire, "New buffer: %s\n", b);
+       goto OUT;
 
-               // Drop old buffer
+ERROR:
+       if (buffer) {
                free(buffer);
-               buffer = b;
+               buffer = NULL;
        }
 
+OUT:
        regfree(&preg);
 
+       if (variable)
+               free(variable);
+       if (pattern)
+               free(pattern);
+
        return buffer;
 }