diff options
Diffstat (limited to 'fluent-bit/lib/librdkafka-2.1.0/src/rdmap.c')
-rw-r--r-- | fluent-bit/lib/librdkafka-2.1.0/src/rdmap.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/fluent-bit/lib/librdkafka-2.1.0/src/rdmap.c b/fluent-bit/lib/librdkafka-2.1.0/src/rdmap.c new file mode 100644 index 00000000..4b854703 --- /dev/null +++ b/fluent-bit/lib/librdkafka-2.1.0/src/rdmap.c @@ -0,0 +1,487 @@ +/* + * librdkafka - The Apache Kafka C/C++ library + * + * Copyright (c) 2020 Magnus Edenhill + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "rd.h" +#include "rdsysqueue.h" +#include "rdstring.h" +#include "rdmap.h" + + +static RD_INLINE int rd_map_elem_cmp(const rd_map_elem_t *a, + const rd_map_elem_t *b, + const rd_map_t *rmap) { + int r = a->hash - b->hash; + if (r != 0) + return r; + return rmap->rmap_cmp(a->key, b->key); +} + +static void rd_map_elem_destroy(rd_map_t *rmap, rd_map_elem_t *elem) { + rd_assert(rmap->rmap_cnt > 0); + rmap->rmap_cnt--; + if (rmap->rmap_destroy_key) + rmap->rmap_destroy_key((void *)elem->key); + if (rmap->rmap_destroy_value) + rmap->rmap_destroy_value((void *)elem->value); + LIST_REMOVE(elem, hlink); + LIST_REMOVE(elem, link); + rd_free(elem); +} + +static rd_map_elem_t * +rd_map_find(const rd_map_t *rmap, int *bktp, const rd_map_elem_t *skel) { + int bkt = skel->hash % rmap->rmap_buckets.cnt; + rd_map_elem_t *elem; + + if (bktp) + *bktp = bkt; + + LIST_FOREACH(elem, &rmap->rmap_buckets.p[bkt], hlink) { + if (!rd_map_elem_cmp(skel, elem, rmap)) + return elem; + } + + return NULL; +} + + +/** + * @brief Create and return new element based on \p skel without value set. + */ +static rd_map_elem_t * +rd_map_insert(rd_map_t *rmap, int bkt, const rd_map_elem_t *skel) { + rd_map_elem_t *elem; + + elem = rd_calloc(1, sizeof(*elem)); + elem->hash = skel->hash; + elem->key = skel->key; /* takes ownership of key */ + LIST_INSERT_HEAD(&rmap->rmap_buckets.p[bkt], elem, hlink); + LIST_INSERT_HEAD(&rmap->rmap_iter, elem, link); + rmap->rmap_cnt++; + + return elem; +} + + +rd_map_elem_t *rd_map_set(rd_map_t *rmap, void *key, void *value) { + rd_map_elem_t skel = {.key = key, .hash = rmap->rmap_hash(key)}; + rd_map_elem_t *elem; + int bkt; + + if (!(elem = rd_map_find(rmap, &bkt, &skel))) { + elem = rd_map_insert(rmap, bkt, &skel); + } else { + if (elem->value && rmap->rmap_destroy_value) + rmap->rmap_destroy_value((void *)elem->value); + if (rmap->rmap_destroy_key) + rmap->rmap_destroy_key(key); + } + + elem->value = value; /* takes ownership of value */ + + return elem; +} + + +void *rd_map_get(const rd_map_t *rmap, const void *key) { + const rd_map_elem_t skel = {.key = (void *)key, + .hash = rmap->rmap_hash(key)}; + rd_map_elem_t *elem; + + if (!(elem = rd_map_find(rmap, NULL, &skel))) + return NULL; + + return (void *)elem->value; +} + + +void rd_map_delete(rd_map_t *rmap, const void *key) { + const rd_map_elem_t skel = {.key = (void *)key, + .hash = rmap->rmap_hash(key)}; + rd_map_elem_t *elem; + int bkt; + + if (!(elem = rd_map_find(rmap, &bkt, &skel))) + return; + + rd_map_elem_destroy(rmap, elem); +} + + +void rd_map_copy(rd_map_t *dst, + const rd_map_t *src, + rd_map_copy_t *key_copy, + rd_map_copy_t *value_copy) { + const rd_map_elem_t *elem; + + RD_MAP_FOREACH_ELEM(elem, src) { + rd_map_set( + dst, key_copy ? key_copy(elem->key) : (void *)elem->key, + value_copy ? value_copy(elem->value) : (void *)elem->value); + } +} + + +void rd_map_iter_begin(const rd_map_t *rmap, const rd_map_elem_t **elem) { + *elem = LIST_FIRST(&rmap->rmap_iter); +} + +size_t rd_map_cnt(const rd_map_t *rmap) { + return (size_t)rmap->rmap_cnt; +} + +rd_bool_t rd_map_is_empty(const rd_map_t *rmap) { + return rmap->rmap_cnt == 0; +} + + +/** + * @brief Calculates the number of desired buckets and returns + * a struct with pre-allocated buckets. + */ +struct rd_map_buckets rd_map_alloc_buckets(size_t expected_cnt) { + static const int max_depth = 15; + static const int bucket_sizes[] = { + 5, 11, 23, 47, 97, 199, /* default */ + 409, 823, 1741, 3469, 6949, 14033, + 28411, 57557, 116731, 236897, -1}; + struct rd_map_buckets buckets = RD_ZERO_INIT; + int i; + + if (!expected_cnt) { + buckets.cnt = 199; + } else { + /* Strive for an average (at expected element count) depth + * of 15 elements per bucket, but limit the maximum + * bucket count to the maximum value in bucket_sizes above. + * When a real need arise we'll change this to a dynamically + * growing hash map instead, but this will do for now. */ + buckets.cnt = bucket_sizes[0]; + for (i = 1; bucket_sizes[i] != -1 && + (int)expected_cnt / max_depth > bucket_sizes[i]; + i++) + buckets.cnt = bucket_sizes[i]; + } + + rd_assert(buckets.cnt > 0); + + buckets.p = rd_calloc(buckets.cnt, sizeof(*buckets.p)); + + return buckets; +} + + +void rd_map_init(rd_map_t *rmap, + size_t expected_cnt, + int (*cmp)(const void *a, const void *b), + unsigned int (*hash)(const void *key), + void (*destroy_key)(void *key), + void (*destroy_value)(void *value)) { + + memset(rmap, 0, sizeof(*rmap)); + rmap->rmap_buckets = rd_map_alloc_buckets(expected_cnt); + rmap->rmap_cmp = cmp; + rmap->rmap_hash = hash; + rmap->rmap_destroy_key = destroy_key; + rmap->rmap_destroy_value = destroy_value; +} + +void rd_map_clear(rd_map_t *rmap) { + rd_map_elem_t *elem; + + while ((elem = LIST_FIRST(&rmap->rmap_iter))) + rd_map_elem_destroy(rmap, elem); +} + +void rd_map_destroy(rd_map_t *rmap) { + rd_map_clear(rmap); + rd_free(rmap->rmap_buckets.p); +} + + +int rd_map_str_cmp(const void *a, const void *b) { + return strcmp((const char *)a, (const char *)b); +} + +/** + * @brief A djb2 string hasher. + */ +unsigned int rd_map_str_hash(const void *key) { + const char *str = key; + return rd_string_hash(str, -1); +} + + + +/** + * @name Unit tests + * + */ +#include "rdtime.h" +#include "rdunittest.h" +#include "rdcrc32.h" + + +/** + * Typed hash maps + */ + +/* Complex key type */ +struct mykey { + int k; + int something_else; /* Ignored by comparator and hasher below */ +}; + +/* Key comparator */ +static int mykey_cmp(const void *_a, const void *_b) { + const struct mykey *a = _a, *b = _b; + return a->k - b->k; +} + +/* Key hasher */ +static unsigned int mykey_hash(const void *_key) { + const struct mykey *key = _key; + return (unsigned int)key->k; +} + +/* Complex value type */ +struct person { + char *name; + char *surname; +}; + +/* Define typed hash map type */ +typedef RD_MAP_TYPE(const struct mykey *, + const struct person *) ut_my_typed_map_t; + + +/** + * @brief Test typed hash map with pre-defined type. + */ +static int unittest_typed_map(void) { + ut_my_typed_map_t rmap = + RD_MAP_INITIALIZER(0, mykey_cmp, mykey_hash, NULL, NULL); + ut_my_typed_map_t dup = + RD_MAP_INITIALIZER(0, mykey_cmp, mykey_hash, NULL, NULL); + struct mykey k1 = {1}; + struct mykey k2 = {2}; + struct person v1 = {"Roy", "McPhearsome"}; + struct person v2 = {"Hedvig", "Lindahl"}; + const struct mykey *key; + const struct person *value; + + RD_MAP_SET(&rmap, &k1, &v1); + RD_MAP_SET(&rmap, &k2, &v2); + + value = RD_MAP_GET(&rmap, &k2); + RD_UT_ASSERT(value == &v2, "mismatch"); + + RD_MAP_FOREACH(key, value, &rmap) { + RD_UT_SAY("enumerated key %d person %s %s", key->k, value->name, + value->surname); + } + + RD_MAP_COPY(&dup, &rmap, NULL, NULL); + + RD_MAP_DELETE(&rmap, &k1); + value = RD_MAP_GET(&rmap, &k1); + RD_UT_ASSERT(value == NULL, "expected no k1"); + + value = RD_MAP_GET(&dup, &k1); + RD_UT_ASSERT(value == &v1, "copied map: k1 mismatch"); + value = RD_MAP_GET(&dup, &k2); + RD_UT_ASSERT(value == &v2, "copied map: k2 mismatch"); + + RD_MAP_DESTROY(&rmap); + RD_MAP_DESTROY(&dup); + + RD_UT_PASS(); +} + + +static int person_cmp(const void *_a, const void *_b) { + const struct person *a = _a, *b = _b; + int r; + if ((r = strcmp(a->name, b->name))) + return r; + return strcmp(a->surname, b->surname); +} +static unsigned int person_hash(const void *_key) { + const struct person *key = _key; + return 31 * rd_map_str_hash(key->name) + rd_map_str_hash(key->surname); +} + +/** + * @brief Test typed hash map with locally defined type. + */ +static int unittest_typed_map2(void) { + RD_MAP_LOCAL_INITIALIZER(usermap, 3, const char *, + const struct person *, rd_map_str_cmp, + rd_map_str_hash, NULL, NULL); + RD_MAP_LOCAL_INITIALIZER(personmap, 3, const struct person *, + const char *, person_cmp, person_hash, NULL, + NULL); + struct person p1 = {"Magnus", "Lundstrom"}; + struct person p2 = {"Peppy", "Popperpappies"}; + const char *user; + const struct person *person; + + /* Populate user -> person map */ + RD_MAP_SET(&usermap, "user1234", &p1); + RD_MAP_SET(&usermap, "user9999999999", &p2); + + person = RD_MAP_GET(&usermap, "user1234"); + + + RD_UT_ASSERT(person == &p1, "mismatch"); + + RD_MAP_FOREACH(user, person, &usermap) { + /* Populate reverse name -> user map */ + RD_MAP_SET(&personmap, person, user); + } + + RD_MAP_FOREACH(person, user, &personmap) { + /* Just reference the memory to catch memory errors.*/ + RD_UT_ASSERT(strlen(person->name) > 0 && + strlen(person->surname) > 0 && + strlen(user) > 0, + "bug"); + } + + RD_MAP_DESTROY(&usermap); + RD_MAP_DESTROY(&personmap); + + return 0; +} + + +/** + * @brief Untyped hash map. + * + * This is a more thorough test of the underlying hash map implementation. + */ +static int unittest_untyped_map(void) { + rd_map_t rmap; + int pass, i, r; + int cnt = 100000; + int exp_cnt = 0, get_cnt = 0, iter_cnt = 0; + const rd_map_elem_t *elem; + rd_ts_t ts = rd_clock(); + rd_ts_t ts_get = 0; + + rd_map_init(&rmap, cnt, rd_map_str_cmp, rd_map_str_hash, rd_free, + rd_free); + + /* pass 0 is set,delete,overwrite,get + * pass 1-5 is get */ + for (pass = 0; pass < 6; pass++) { + if (pass == 1) + ts_get = rd_clock(); + + for (i = 1; i < cnt; i++) { + char key[10]; + char val[64]; + const char *val2; + rd_bool_t do_delete = !(i % 13); + rd_bool_t overwrite = !do_delete && !(i % 5); + + rd_snprintf(key, sizeof(key), "key%d", i); + rd_snprintf(val, sizeof(val), "VALUE=%d!", i); + + if (pass == 0) { + rd_map_set(&rmap, rd_strdup(key), + rd_strdup(val)); + + if (do_delete) + rd_map_delete(&rmap, key); + } + + if (overwrite) { + rd_snprintf(val, sizeof(val), "OVERWRITE=%d!", + i); + if (pass == 0) + rd_map_set(&rmap, rd_strdup(key), + rd_strdup(val)); + } + + val2 = rd_map_get(&rmap, key); + + if (do_delete) + RD_UT_ASSERT(!val2, + "map_get pass %d " + "returned value %s " + "for deleted key %s", + pass, val2, key); + else + RD_UT_ASSERT(val2 && !strcmp(val, val2), + "map_get pass %d: " + "expected value %s, not %s, " + "for key %s", + pass, val, val2 ? val2 : "NULL", + key); + + if (pass == 0 && !do_delete) + exp_cnt++; + } + + if (pass >= 1) + get_cnt += cnt; + } + + ts_get = rd_clock() - ts_get; + RD_UT_SAY("%d map_get iterations took %.3fms = %" PRId64 "us/get", + get_cnt, (float)ts_get / 1000.0, ts_get / get_cnt); + + RD_MAP_FOREACH_ELEM(elem, &rmap) { + iter_cnt++; + } + + r = (int)rd_map_cnt(&rmap); + RD_UT_ASSERT(r == exp_cnt, "expected %d map entries, not %d", exp_cnt, + r); + + RD_UT_ASSERT(r == iter_cnt, + "map_cnt() = %d, iteration gave %d elements", r, iter_cnt); + + rd_map_destroy(&rmap); + + ts = rd_clock() - ts; + RD_UT_SAY("Total time over %d entries took %.3fms", cnt, + (float)ts / 1000.0); + + RD_UT_PASS(); +} + + +int unittest_map(void) { + int fails = 0; + fails += unittest_untyped_map(); + fails += unittest_typed_map(); + fails += unittest_typed_map2(); + return 0; +} |