diff --git a/include/fluent-bit/flb_hash.h b/include/fluent-bit/flb_hash.h index d8f3b4c6961..5164d6f0340 100644 --- a/include/fluent-bit/flb_hash.h +++ b/include/fluent-bit/flb_hash.h @@ -41,6 +41,7 @@ struct flb_hash_table { struct flb_hash { size_t size; + size_t count; struct flb_hash_table *table; }; diff --git a/src/flb_hash.c b/src/flb_hash.c index a3124fe598b..457a56f862d 100644 --- a/src/flb_hash.c +++ b/src/flb_hash.c @@ -109,6 +109,7 @@ struct flb_hash *flb_hash_create(size_t size) } ht->size = size; + ht->count = 0; ht->table = flb_calloc(1, sizeof(struct flb_hash_table) * size); if (!ht->table) { flb_errno(); @@ -147,6 +148,29 @@ void flb_hash_destroy(struct flb_hash *ht) flb_free(ht); } +static void flb_hash_prune(struct flb_hash *ht) +{ + int id; + struct mk_list *tmp; + struct mk_list *head; + struct flb_hash_table *table; + struct flb_hash_entry *old; + + /* Remove some prior entries if hash table is full */ + if (ht->count >= ht->size) { + /* If full, remove all the prior values in a random chain */ + /* Assuming a good hash distribution this should only remove a few entries */ + id = random() % ht->size; + table = &ht->table[id]; + mk_list_foreach_safe(head, tmp, &table->chains) { + old = mk_list_entry(head, struct flb_hash_entry, _head); + flb_hash_entry_free(old); + table->count--; + ht->count--; + } + } +} + int flb_hash_add(struct flb_hash *ht, char *key, int key_len, char *val, size_t val_size) { @@ -162,6 +186,8 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len, return -1; } + flb_hash_prune(ht); + /* Generate hash number */ hash = gen_hash(key, key_len); id = (hash % ht->size); @@ -204,6 +230,7 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len, if (strcmp(old->key, entry->key) == 0) { flb_hash_entry_free(old); table->count--; + ht->count--; break; } } @@ -211,6 +238,7 @@ int flb_hash_add(struct flb_hash *ht, char *key, int key_len, } table->count++; + ht->count++; return id; } @@ -359,6 +387,7 @@ int flb_hash_del(struct flb_hash *ht, char *key) flb_hash_entry_free(entry); table->count--; + ht->count--; return 0; } diff --git a/tests/internal/hashtable.c b/tests/internal/hashtable.c index 0a8a3b4a2c2..6c25a4d3271 100644 --- a/tests/internal/hashtable.c +++ b/tests/internal/hashtable.c @@ -145,7 +145,7 @@ void test_chaining() int i; int inserts = 0; int count; - int total = 0; + int chains = 0; struct map *m; struct mk_list *head; struct flb_hash_table *table; @@ -154,7 +154,7 @@ void test_chaining() ht = flb_hash_create(8); TEST_CHECK(ht != NULL); - for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) { + for (i = 0; i < 8; i++) { m = &entries[i]; ht_add(ht, m->key, m->val); inserts++; @@ -168,11 +168,13 @@ void test_chaining() } TEST_CHECK(count == table->count); - total += count; + if (count > 0) { + chains++; + } } /* Tests diff between total, new minus 3 overrides */ - TEST_CHECK(total == inserts - 3); + TEST_CHECK(chains == inserts - 3); flb_hash_destroy(ht); } @@ -214,6 +216,31 @@ void test_delete_all() flb_hash_destroy(ht); } +void test_bounded_size() +{ + int ret; + char *out_buf; + size_t out_size; + struct flb_hash *ht; + + ht = flb_hash_create(1); + TEST_CHECK(ht != NULL); + + ret = ht_add(ht, "key1", "value1"); + TEST_CHECK(ret != -1); + + ret = ht_add(ht, "key2", "value2"); + TEST_CHECK(ret != -1); + + ret = flb_hash_get(ht, "key1", 4, &out_buf, &out_size); + TEST_CHECK(ret == -1); + + ret = flb_hash_get(ht, "key2", 4, &out_buf, &out_size); + TEST_CHECK(ret >= 0); + + flb_hash_destroy(ht); +} + TEST_LIST = { { "zero_size", test_create_zero }, { "single", test_single }, @@ -221,5 +248,6 @@ TEST_LIST = { { "medium_table", test_medium_table }, { "chaining_count", test_chaining }, { "delete_all", test_delete_all }, + { "bounded_size", test_bounded_size }, { 0 } };