diff options
Diffstat (limited to '')
-rw-r--r-- | fluent-bit/src/flb_hash_table.c | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/fluent-bit/src/flb_hash_table.c b/fluent-bit/src/flb_hash_table.c new file mode 100644 index 00000000..d31c4259 --- /dev/null +++ b/fluent-bit/src/flb_hash_table.c @@ -0,0 +1,539 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2022 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fluent-bit/flb_info.h> +#include <fluent-bit/flb_mem.h> +#include <fluent-bit/flb_hash_table.h> +#include <fluent-bit/flb_log.h> +#include <fluent-bit/flb_str.h> + +#include <cfl/cfl.h> + +static inline void flb_hash_table_entry_free(struct flb_hash_table *ht, + struct flb_hash_table_entry *entry) +{ + mk_list_del(&entry->_head); + mk_list_del(&entry->_head_parent); + entry->table->count--; + ht->total_count--; + flb_free(entry->key); + if (entry->val && entry->val_size > 0) { + flb_free(entry->val); + } + flb_free(entry); +} + +struct flb_hash_table *flb_hash_table_create(int evict_mode, size_t size, int max_entries) +{ + int i; + struct flb_hash_table_chain *tmp; + struct flb_hash_table *ht; + + if (size <= 0) { + return NULL; + } + + ht = flb_malloc(sizeof(struct flb_hash_table)); + if (!ht) { + flb_errno(); + return NULL; + } + + mk_list_init(&ht->entries); + ht->evict_mode = evict_mode; + ht->max_entries = max_entries; + ht->size = size; + ht->total_count = 0; + ht->cache_ttl = 0; + ht->table = flb_calloc(1, sizeof(struct flb_hash_table_chain) * size); + if (!ht->table) { + flb_errno(); + flb_free(ht); + return NULL; + } + + /* Initialize chains list head */ + for (i = 0; i < size; i++) { + tmp = &ht->table[i]; + tmp->count = 0; + mk_list_init(&tmp->chains); + } + + return ht; +} + +struct flb_hash_table *flb_hash_table_create_with_ttl(int cache_ttl, int evict_mode, + size_t size, int max_entries) +{ + struct flb_hash_table *ht; + + ht = flb_hash_table_create(evict_mode, size, max_entries); + if (!ht) { + flb_errno(); + return NULL; + } + + ht->cache_ttl = cache_ttl; + return ht; +} + +int flb_hash_table_del_ptr(struct flb_hash_table *ht, const char *key, int key_len, + void *ptr) +{ + int id; + uint64_t hash; + struct mk_list *head; + struct flb_hash_table_entry *entry = NULL; + struct flb_hash_table_chain *table; + + /* Generate hash number */ + hash = cfl_hash_64bits(key, key_len); + id = (hash % ht->size); + + /* Link the new entry in our table at the end of the list */ + table = &ht->table[id]; + + mk_list_foreach(head, &table->chains) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head); + if (strncmp(entry->key, key, key_len) == 0 && entry->val == ptr) { + break; + } + entry = NULL; + } + + if (!entry) { + return -1; + } + + /* delete the entry */ + flb_hash_table_entry_free(ht, entry); + return 0; +} + + +void flb_hash_table_destroy(struct flb_hash_table *ht) +{ + int i; + struct mk_list *tmp; + struct mk_list *head; + struct flb_hash_table_entry *entry; + struct flb_hash_table_chain *table; + + for (i = 0; i < ht->size; i++) { + table = &ht->table[i]; + mk_list_foreach_safe(head, tmp, &table->chains) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head); + flb_hash_table_entry_free(ht, entry); + } + } + + flb_free(ht->table); + flb_free(ht); +} + +static void flb_hash_table_evict_random(struct flb_hash_table *ht) +{ + int id; + int count = 0; + struct mk_list *tmp; + struct mk_list *head; + struct flb_hash_table_entry *entry; + + id = random() % ht->total_count; + mk_list_foreach_safe(head, tmp, &ht->entries) { + if (id == count) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head_parent); + flb_hash_table_entry_free(ht, entry); + break; + } + count++; + } +} + +static void flb_hash_table_evict_less_used(struct flb_hash_table *ht) +{ + struct mk_list *head; + struct flb_hash_table_entry *entry; + struct flb_hash_table_entry *entry_less_used = NULL; + + mk_list_foreach(head, &ht->entries) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head_parent); + if (!entry_less_used) { + entry_less_used = entry; + } + else if (entry->hits < entry_less_used->hits) { + entry_less_used = entry; + } + } + + flb_hash_table_entry_free(ht, entry_less_used); +} + +static void flb_hash_table_evict_older(struct flb_hash_table *ht) +{ + struct flb_hash_table_entry *entry; + + entry = mk_list_entry_first(&ht->entries, struct flb_hash_table_entry, _head_parent); + flb_hash_table_entry_free(ht, entry); +} + +static struct flb_hash_table_entry *hash_get_entry(struct flb_hash_table *ht, + const char *key, int key_len, int *out_id) +{ + int id; + uint64_t hash; + struct mk_list *head; + struct flb_hash_table_chain *table; + struct flb_hash_table_entry *entry; + + if (!key || key_len <= 0) { + return NULL; + } + + hash = cfl_hash_64bits(key, key_len); + id = (hash % ht->size); + + table = &ht->table[id]; + if (table->count == 0) { + return NULL; + } + + if (table->count == 1) { + entry = mk_list_entry_first(&table->chains, + struct flb_hash_table_entry, _head); + + if (entry->key_len != key_len + || strncmp(entry->key, key, key_len) != 0) { + entry = NULL; + } + } + else { + /* Iterate entries */ + mk_list_foreach(head, &table->chains) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head); + if (entry->key_len != key_len) { + entry = NULL; + continue; + } + + if (strncmp(entry->key, key, key_len) == 0) { + break; + } + + entry = NULL; + } + } + + if (entry) { + *out_id = id; + } + + return entry; +} + +static int entry_set_value(struct flb_hash_table_entry *entry, void *val, size_t val_size) +{ + char *ptr; + + /* + * If the entry already contains a previous value in the heap, just remove + * the previously assigned memory. + */ + if (entry->val_size > 0) { + flb_free(entry->val); + } + + /* + * Now set the new value. If val_size > 0, we create a new memory area, otherwise + * it means the caller just wants to store a pointer address, no allocation + * is required. + */ + if (val_size > 0) { + entry->val = flb_malloc(val_size + 1); + if (!entry->val) { + flb_errno(); + return -1; + } + + /* + * Copy the buffer and append a NULL byte in case the caller set and + * expects a string. + */ + memcpy(entry->val, val, val_size); + ptr = (char *) entry->val; + ptr[val_size] = '\0'; + entry->val_size = val_size; + } + else { + /* just do a reference */ + entry->val = val; + entry->val_size = -1; + } + + entry->created = time(NULL); + + return 0; +} + +int flb_hash_table_add(struct flb_hash_table *ht, const char *key, int key_len, + void *val, ssize_t val_size) +{ + int id; + int ret; + uint64_t hash; + struct flb_hash_table_entry *entry; + struct flb_hash_table_chain *table; + + if (!key || key_len <= 0) { + return -1; + } + + /* Check capacity */ + if (ht->max_entries > 0 && ht->total_count >= ht->max_entries) { + if (ht->evict_mode == FLB_HASH_TABLE_EVICT_NONE) { + /* Do nothing */ + } + else if (ht->evict_mode == FLB_HASH_TABLE_EVICT_OLDER) { + flb_hash_table_evict_older(ht); + } + else if (ht->evict_mode == FLB_HASH_TABLE_EVICT_LESS_USED) { + flb_hash_table_evict_less_used(ht); + } + else if (ht->evict_mode == FLB_HASH_TABLE_EVICT_RANDOM) { + flb_hash_table_evict_random(ht); + } + } + + /* Check if this is a replacement */ + entry = hash_get_entry(ht, key, key_len, &id); + if (entry) { + /* + * The key already exists, just perform a value replacement, check if the + * value refers to our own previous allocation. + */ + ret = entry_set_value(entry, val, val_size); + if (ret == -1) { + return -1; + } + + return id; + } + + /* + * Below is just code to handle the creation of a new entry in the table + */ + + /* Generate hash number */ + hash = cfl_hash_64bits(key, key_len); + id = (hash % ht->size); + + /* Allocate the entry */ + entry = flb_calloc(1, sizeof(struct flb_hash_table_entry)); + if (!entry) { + flb_errno(); + return -1; + } + entry->created = time(NULL); + entry->hash = hash; + entry->hits = 0; + + /* Store the key and value as a new memory region */ + entry->key = flb_strndup(key, key_len); + entry->key_len = key_len; + entry->val_size = 0; + + /* store or reference the value */ + ret = entry_set_value(entry, val, val_size); + if (ret == -1) { + flb_free(entry); + return -1; + } + + /* Link the new entry in our table at the end of the list */ + table = &ht->table[id]; + entry->table = table; + + /* Add the new entry */ + mk_list_add(&entry->_head, &table->chains); + mk_list_add(&entry->_head_parent, &ht->entries); + + /* Update counters */ + table->count++; + ht->total_count++; + + return id; +} + +int flb_hash_table_get(struct flb_hash_table *ht, + const char *key, int key_len, + void **out_buf, size_t *out_size) +{ + int id; + struct flb_hash_table_entry *entry; + time_t expiration; + + entry = hash_get_entry(ht, key, key_len, &id); + if (!entry) { + return -1; + } + + if (ht->cache_ttl > 0) { + expiration = entry->created + ht->cache_ttl; + if (time(NULL) > expiration) { + flb_hash_table_entry_free(ht, entry); + return -1; + } + } + + entry->hits++; + *out_buf = entry->val; + *out_size = entry->val_size; + + return id; +} + +/* check if a hash exists */ +int flb_hash_table_exists(struct flb_hash_table *ht, uint64_t hash) +{ + int id; + struct mk_list *head; + struct flb_hash_table_chain *table; + struct flb_hash_table_entry *entry; + + id = (hash % ht->size); + table = &ht->table[id]; + + /* Iterate entries */ + mk_list_foreach(head, &table->chains) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head); + if (entry->hash == hash) { + return FLB_TRUE; + } + } + + return FLB_FALSE; +} + +/* + * Get an entry based in the table id. Note that a table id might have multiple + * entries so the 'key' parameter is required to get an exact match. + */ +int flb_hash_table_get_by_id(struct flb_hash_table *ht, int id, + const char *key, + const char **out_buf, size_t * out_size) +{ + struct mk_list *head; + struct flb_hash_table_entry *entry = NULL; + struct flb_hash_table_chain *table; + + if (ht->size <= id) { + return -1; + } + + table = &ht->table[id]; + if (table->count == 0) { + return -1; + } + + if (table->count == 1) { + entry = mk_list_entry_first(&table->chains, + struct flb_hash_table_entry, _head); + } + else { + mk_list_foreach(head, &table->chains) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head); + if (strcmp(entry->key, key) == 0) { + break; + } + entry = NULL; + } + } + + if (!entry) { + return -1; + } + + *out_buf = entry->val; + *out_size = entry->val_size; + + return 0; +} + +void *flb_hash_table_get_ptr(struct flb_hash_table *ht, const char *key, int key_len) +{ + int id; + struct flb_hash_table_entry *entry; + + entry = hash_get_entry(ht, key, key_len, &id); + if (!entry) { + return NULL; + } + + entry->hits++; + return entry->val; +} + +int flb_hash_table_del(struct flb_hash_table *ht, const char *key) +{ + int id; + int len; + uint64_t hash; + struct mk_list *head; + struct flb_hash_table_entry *entry = NULL; + struct flb_hash_table_chain *table; + + if (!key) { + return -1; + } + + len = strlen(key); + if (len == 0) { + return -1; + } + + hash = cfl_hash_64bits(key, len); + id = (hash % ht->size); + + table = &ht->table[id]; + if (table->count == 1) { + entry = mk_list_entry_first(&table->chains, + struct flb_hash_table_entry, + _head); + if (strcmp(entry->key, key) != 0) { + entry = NULL; + } + } + else { + mk_list_foreach(head, &table->chains) { + entry = mk_list_entry(head, struct flb_hash_table_entry, _head); + if (strcmp(entry->key, key) == 0) { + break; + } + entry = NULL; + } + } + + if (!entry) { + return -1; + } + + flb_hash_table_entry_free(ht, entry); + + return 0; +} |