]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
config: Optimize config_filter lookups with a hash table
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 28 Nov 2024 11:28:45 +0000 (13:28 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 17 Jan 2025 08:40:01 +0000 (10:40 +0200)
src/config/config-filter.c
src/config/config-filter.h
src/config/config-parser-private.h
src/config/config-parser.c

index 3ce3c119ec17093be99f62fa7203d1101ac620fe..717b739ffc3ac2de5037b49abc36732e57ee6abe 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "array.h"
+#include "crc32.h"
 #include "settings-parser.h"
 #include "master-service-settings.h"
 #include "config-parser.h"
@@ -162,6 +163,40 @@ bool config_filters_equal(const struct config_filter *f1,
        return config_filters_equal_without_defaults(f1, f2);
 }
 
+static unsigned int
+config_filter_hash_crc(const struct config_filter *filter, uint32_t crc)
+{
+       if (filter->protocol != NULL)
+               crc = crc32_str_more(crc, filter->protocol);
+       if (filter->remote_bits > 0) {
+               crc = crc32_data_more(crc, &filter->remote_bits,
+                                     sizeof(filter->remote_bits));
+               crc = crc32_data_more(crc, &filter->remote_net,
+                                     sizeof(filter->remote_net));
+       }
+       if (filter->local_bits > 0) {
+               crc = crc32_data_more(crc, &filter->local_bits,
+                                     sizeof(filter->local_bits));
+               crc = crc32_data_more(crc, &filter->local_net,
+                                     sizeof(filter->local_net));
+       }
+       if (filter->local_name != NULL)
+               crc = crc32_str_more(crc, filter->local_name);
+       if (filter->filter_name != NULL) {
+               crc = crc32_str_more(crc, filter->filter_name);
+               if (filter->filter_name_array)
+                       crc = crc32_data_more(crc, "1", 1);
+       }
+       return filter->parent == NULL ? crc :
+               config_filter_hash_crc(filter->parent, crc);
+}
+
+unsigned int config_filter_hash(const struct config_filter *filter)
+{
+       uint32_t crc = filter->default_settings ? 1 : 0;
+       return config_filter_hash_crc(filter, crc);
+}
+
 bool config_filter_is_empty(const struct config_filter *filter)
 {
        return config_filters_equal(filter, &empty_filter);
index 8b9b7dad943f89fcc15aaf1a8da4119c13302c03..f2df2127411eb30e06d1b699a8fcccc6df34b28a 100644 (file)
@@ -75,6 +75,8 @@ bool config_filters_equal(const struct config_filter *f1,
                          const struct config_filter *f2);
 bool config_filters_equal_no_recursion(const struct config_filter *f1,
                                       const struct config_filter *f2);
+/* Returns hash of the filter and its parents. */
+unsigned int config_filter_hash(const struct config_filter *filter);
 /* Returns TRUE if filter is empty, and it has no parent filters, and it has
    default_settings=FALSE. */
 bool config_filter_is_empty(const struct config_filter *filter);
index 64e1635360489b505e14dba1f6b8bc0502b85524..8b052bca6d8499fda16633f6187923916d0da9c1 100644 (file)
@@ -80,6 +80,8 @@ struct config_parser_context {
 
        HASH_TABLE(const char *, struct config_parser_key *) all_keys;
        ARRAY(struct config_filter_parser *) all_filter_parsers;
+       HASH_TABLE(struct config_filter *,
+                  struct config_filter_parser *) all_filter_parsers_hash;
        struct config_module_parser *root_module_parsers;
        struct config_section_stack *cur_section;
        struct input_stack *cur_input;
index 477aa8cee4211f4d10aba874bdb420ba01dae095..c808685210f6aeed46272781379a839d73d9947b 100644 (file)
@@ -1065,6 +1065,11 @@ config_add_new_parser(struct config_parser_context *ctx,
                config_module_parsers_init(ctx->pool);
        array_push_back(&ctx->all_filter_parsers, &filter_parser);
 
+       if (ctx->all_filter_parsers_hash._table != NULL) {
+               hash_table_insert(ctx->all_filter_parsers_hash,
+                                 &filter_parser->filter, filter_parser);
+       }
+
        if (parent_filter_parser != NULL) {
                filter_parser->parent = parent_filter_parser;
                DLLIST2_APPEND(&parent_filter_parser->children_head,
@@ -1092,6 +1097,13 @@ config_add_new_section(struct config_parser_context *ctx)
 static struct config_filter_parser *
 config_filter_parser_find(struct config_parser_context *ctx,
                          const struct config_filter *filter)
+{
+       return hash_table_lookup(ctx->all_filter_parsers_hash, filter);
+}
+
+static struct config_filter_parser *
+config_filter_parser_find_slow(struct config_parser_context *ctx,
+                              const struct config_filter *filter)
 {
        struct config_filter_parser *filter_parser;
 
@@ -1440,7 +1452,7 @@ config_filter_parser_check(struct config_parser_context *ctx,
        struct config_filter default_filter = filter_parser->filter;
        default_filter.default_settings = TRUE;
        struct config_filter_parser *default_filter_parser =
-               config_filter_parser_find(ctx, &default_filter);
+               config_filter_parser_find_slow(ctx, &default_filter);
        default_p = default_filter_parser == NULL ? NULL :
                default_filter_parser->module_parsers;
 
@@ -2229,6 +2241,10 @@ config_parse_finish(struct config_parser_context *ctx,
        new_config->filter_parsers = array_front(&ctx->all_filter_parsers);
        new_config->module_parsers = ctx->root_module_parsers;
 
+       /* Destroy it here, so config filter tree merging no longer attempts
+          to update it. */
+       hash_table_destroy(&ctx->all_filter_parsers_hash);
+
        if (ret == 0) {
                ret = config_parse_finish_includes(ctx, new_config,
                        (flags & CONFIG_PARSE_FLAG_MERGE_GROUP_FILTERS) != 0,
@@ -2724,6 +2740,13 @@ config_parser_get_filter_name_prefixes(struct config_parser_context *ctx)
                ctx->filter_name_prefixes = array_front(&filter_name_prefixes);
 }
 
+static int
+config_filters_cmp(const struct config_filter *f1,
+                  const struct config_filter *f2)
+{
+       return config_filters_equal(f1, f2) ? 0 : 1;
+}
+
 int config_parse_file(const char *path, enum config_parse_flags flags,
                      const struct config_filter *dump_filter,
                      struct config_parsed **config_r,
@@ -2793,6 +2816,8 @@ int config_parse_file(const char *path, enum config_parse_flags flags,
        hash_table_create(&ctx.seen_settings, ctx.pool, 0, str_hash, strcmp);
 
        p_array_init(&ctx.all_filter_parsers, ctx.pool, 128);
+       hash_table_create(&ctx.all_filter_parsers_hash, ctx.pool, 0,
+                         config_filter_hash, config_filters_cmp);
        ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1);
        /* Global settings filter must be the first. */
        struct config_filter root_filter = { };
@@ -2907,6 +2932,7 @@ prevfile:
 
        hash_table_destroy(&ctx.seen_settings);
        hash_table_destroy(&ctx.all_keys);
+       hash_table_destroy(&ctx.all_filter_parsers_hash);
        str_free(&full_line);
        pool_unref(&ctx.pool);
        return ret < 0 ? ret : 1;