| Index: third_party/grpc/src/core/statistics/hash_table.c
|
| diff --git a/third_party/grpc/src/core/statistics/hash_table.c b/third_party/grpc/src/core/statistics/hash_table.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0cadcd4740218f18abcb2f0811e5eb6929e04bd7
|
| --- /dev/null
|
| +++ b/third_party/grpc/src/core/statistics/hash_table.c
|
| @@ -0,0 +1,303 @@
|
| +/*
|
| + *
|
| + * Copyright 2015, Google Inc.
|
| + * All rights reserved.
|
| + *
|
| + * Redistribution and use in source and binary forms, with or without
|
| + * modification, are permitted provided that the following conditions are
|
| + * met:
|
| + *
|
| + * * Redistributions of source code must retain the above copyright
|
| + * notice, this list of conditions and the following disclaimer.
|
| + * * 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.
|
| + * * Neither the name of Google Inc. nor the names of its
|
| + * contributors may be used to endorse or promote products derived from
|
| + * this software without specific prior written permission.
|
| + *
|
| + * 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 "src/core/statistics/hash_table.h"
|
| +
|
| +#include <stdio.h>
|
| +#include <stddef.h>
|
| +
|
| +#include <grpc/support/log.h>
|
| +#include <grpc/support/alloc.h>
|
| +#include <grpc/support/port_platform.h>
|
| +
|
| +#define CENSUS_HT_NUM_BUCKETS 1999
|
| +
|
| +/* A single hash table data entry */
|
| +typedef struct ht_entry {
|
| + census_ht_key key;
|
| + void *data;
|
| + struct ht_entry *next;
|
| +} ht_entry;
|
| +
|
| +/* hash table bucket */
|
| +typedef struct bucket {
|
| + /* NULL if bucket is empty */
|
| + ht_entry *next;
|
| + /* -1 if all buckets are empty. */
|
| + int32_t prev_non_empty_bucket;
|
| + /* -1 if all buckets are empty. */
|
| + int32_t next_non_empty_bucket;
|
| +} bucket;
|
| +
|
| +struct unresizable_hash_table {
|
| + /* Number of entries in the table */
|
| + size_t size;
|
| + /* Number of buckets */
|
| + uint32_t num_buckets;
|
| + /* Array of buckets initialized at creation time. Memory consumption is
|
| + 16 bytes per bucket on a 64-bit platform. */
|
| + bucket *buckets;
|
| + /* Index of the first non-empty bucket. -1 iff size == 0. */
|
| + int32_t first_non_empty_bucket;
|
| + /* Index of the last non_empty bucket. -1 iff size == 0. */
|
| + int32_t last_non_empty_bucket;
|
| + /* Immutable options of this hash table, initialized at creation time. */
|
| + census_ht_option options;
|
| +};
|
| +
|
| +typedef struct entry_locator {
|
| + int32_t bucket_idx;
|
| + int is_first_in_chain;
|
| + int found;
|
| + ht_entry *prev_entry;
|
| +} entry_locator;
|
| +
|
| +/* Asserts if option is not valid. */
|
| +void check_options(const census_ht_option *option) {
|
| + GPR_ASSERT(option != NULL);
|
| + GPR_ASSERT(option->num_buckets > 0);
|
| + GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 ||
|
| + option->key_type == CENSUS_HT_POINTER);
|
| + if (option->key_type == CENSUS_HT_UINT64) {
|
| + GPR_ASSERT(option->hash == NULL);
|
| + } else if (option->key_type == CENSUS_HT_POINTER) {
|
| + GPR_ASSERT(option->hash != NULL);
|
| + GPR_ASSERT(option->compare_keys != NULL);
|
| + }
|
| +}
|
| +
|
| +#define REMOVE_NEXT(options, ptr) \
|
| + do { \
|
| + ht_entry *tmp = (ptr)->next; \
|
| + (ptr)->next = tmp->next; \
|
| + delete_entry(options, tmp); \
|
| + } while (0)
|
| +
|
| +static void delete_entry(const census_ht_option *opt, ht_entry *p) {
|
| + if (opt->delete_data != NULL) {
|
| + opt->delete_data(p->data);
|
| + }
|
| + if (opt->delete_key != NULL) {
|
| + opt->delete_key(p->key.ptr);
|
| + }
|
| + gpr_free(p);
|
| +}
|
| +
|
| +static uint64_t hash(const census_ht_option *opt, census_ht_key key) {
|
| + return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr);
|
| +}
|
| +
|
| +census_ht *census_ht_create(const census_ht_option *option) {
|
| + int i;
|
| + census_ht *ret = NULL;
|
| + check_options(option);
|
| + ret = (census_ht *)gpr_malloc(sizeof(census_ht));
|
| + ret->size = 0;
|
| + ret->num_buckets = option->num_buckets;
|
| + ret->buckets = (bucket *)gpr_malloc(sizeof(bucket) * ret->num_buckets);
|
| + ret->options = *option;
|
| + /* initialize each bucket */
|
| + for (i = 0; i < ret->options.num_buckets; i++) {
|
| + ret->buckets[i].prev_non_empty_bucket = -1;
|
| + ret->buckets[i].next_non_empty_bucket = -1;
|
| + ret->buckets[i].next = NULL;
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +static int32_t find_bucket_idx(const census_ht *ht, census_ht_key key) {
|
| + return hash(&ht->options, key) % ht->num_buckets;
|
| +}
|
| +
|
| +static int keys_match(const census_ht_option *opt, const ht_entry *p,
|
| + const census_ht_key key) {
|
| + GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 ||
|
| + opt->key_type == CENSUS_HT_POINTER);
|
| + if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val;
|
| + return !opt->compare_keys((p->key).ptr, key.ptr);
|
| +}
|
| +
|
| +static entry_locator ht_find(const census_ht *ht, census_ht_key key) {
|
| + entry_locator loc = {0, 0, 0, NULL};
|
| + int32_t idx = 0;
|
| + ht_entry *ptr = NULL;
|
| + GPR_ASSERT(ht != NULL);
|
| + idx = find_bucket_idx(ht, key);
|
| + ptr = ht->buckets[idx].next;
|
| + if (ptr == NULL) {
|
| + /* bucket is empty */
|
| + return loc;
|
| + }
|
| + if (keys_match(&ht->options, ptr, key)) {
|
| + loc.bucket_idx = idx;
|
| + loc.is_first_in_chain = 1;
|
| + loc.found = 1;
|
| + return loc;
|
| + } else {
|
| + for (; ptr->next != NULL; ptr = ptr->next) {
|
| + if (keys_match(&ht->options, ptr->next, key)) {
|
| + loc.bucket_idx = idx;
|
| + loc.is_first_in_chain = 0;
|
| + loc.found = 1;
|
| + loc.prev_entry = ptr;
|
| + return loc;
|
| + }
|
| + }
|
| + }
|
| + /* Could not find the key */
|
| + return loc;
|
| +}
|
| +
|
| +void *census_ht_find(const census_ht *ht, census_ht_key key) {
|
| + entry_locator loc = ht_find(ht, key);
|
| + if (loc.found == 0) {
|
| + return NULL;
|
| + }
|
| + return loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next->data
|
| + : loc.prev_entry->next->data;
|
| +}
|
| +
|
| +void census_ht_insert(census_ht *ht, census_ht_key key, void *data) {
|
| + int32_t idx = find_bucket_idx(ht, key);
|
| + ht_entry *ptr = NULL;
|
| + entry_locator loc = ht_find(ht, key);
|
| + if (loc.found) {
|
| + /* Replace old value with new value. */
|
| + ptr = loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next
|
| + : loc.prev_entry->next;
|
| + if (ht->options.delete_data != NULL) {
|
| + ht->options.delete_data(ptr->data);
|
| + }
|
| + ptr->data = data;
|
| + return;
|
| + }
|
| +
|
| + /* first entry in the table. */
|
| + if (ht->size == 0) {
|
| + ht->buckets[idx].next_non_empty_bucket = -1;
|
| + ht->buckets[idx].prev_non_empty_bucket = -1;
|
| + ht->first_non_empty_bucket = idx;
|
| + ht->last_non_empty_bucket = idx;
|
| + } else if (ht->buckets[idx].next == NULL) {
|
| + /* first entry in the bucket. */
|
| + ht->buckets[ht->last_non_empty_bucket].next_non_empty_bucket = idx;
|
| + ht->buckets[idx].prev_non_empty_bucket = ht->last_non_empty_bucket;
|
| + ht->buckets[idx].next_non_empty_bucket = -1;
|
| + ht->last_non_empty_bucket = idx;
|
| + }
|
| + ptr = (ht_entry *)gpr_malloc(sizeof(ht_entry));
|
| + ptr->key = key;
|
| + ptr->data = data;
|
| + ptr->next = ht->buckets[idx].next;
|
| + ht->buckets[idx].next = ptr;
|
| + ht->size++;
|
| +}
|
| +
|
| +void census_ht_erase(census_ht *ht, census_ht_key key) {
|
| + entry_locator loc = ht_find(ht, key);
|
| + if (loc.found == 0) {
|
| + /* noop if not found */
|
| + return;
|
| + }
|
| + ht->size--;
|
| + if (loc.is_first_in_chain) {
|
| + bucket *b = &ht->buckets[loc.bucket_idx];
|
| + GPR_ASSERT(b->next != NULL);
|
| + /* The only entry in the bucket */
|
| + if (b->next->next == NULL) {
|
| + int prev = b->prev_non_empty_bucket;
|
| + int next = b->next_non_empty_bucket;
|
| + if (prev != -1) {
|
| + ht->buckets[prev].next_non_empty_bucket = next;
|
| + } else {
|
| + ht->first_non_empty_bucket = next;
|
| + }
|
| + if (next != -1) {
|
| + ht->buckets[next].prev_non_empty_bucket = prev;
|
| + } else {
|
| + ht->last_non_empty_bucket = prev;
|
| + }
|
| + }
|
| + REMOVE_NEXT(&ht->options, b);
|
| + } else {
|
| + GPR_ASSERT(loc.prev_entry->next != NULL);
|
| + REMOVE_NEXT(&ht->options, loc.prev_entry);
|
| + }
|
| +}
|
| +
|
| +/* Returns NULL if input table is empty. */
|
| +census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num) {
|
| + census_ht_kv *ret = NULL;
|
| + int i = 0;
|
| + int32_t idx = -1;
|
| + GPR_ASSERT(ht != NULL && num != NULL);
|
| + *num = ht->size;
|
| + if (*num == 0) {
|
| + return NULL;
|
| + }
|
| +
|
| + ret = (census_ht_kv *)gpr_malloc(sizeof(census_ht_kv) * ht->size);
|
| + idx = ht->first_non_empty_bucket;
|
| + while (idx >= 0) {
|
| + ht_entry *ptr = ht->buckets[idx].next;
|
| + for (; ptr != NULL; ptr = ptr->next) {
|
| + ret[i].k = ptr->key;
|
| + ret[i].v = ptr->data;
|
| + i++;
|
| + }
|
| + idx = ht->buckets[idx].next_non_empty_bucket;
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +static void ht_delete_entry_chain(const census_ht_option *options,
|
| + ht_entry *first) {
|
| + if (first == NULL) {
|
| + return;
|
| + }
|
| + if (first->next != NULL) {
|
| + ht_delete_entry_chain(options, first->next);
|
| + }
|
| + delete_entry(options, first);
|
| +}
|
| +
|
| +void census_ht_destroy(census_ht *ht) {
|
| + unsigned i;
|
| + for (i = 0; i < ht->num_buckets; ++i) {
|
| + ht_delete_entry_chain(&ht->options, ht->buckets[i].next);
|
| + }
|
| + gpr_free(ht->buckets);
|
| + gpr_free(ht);
|
| +}
|
| +
|
| +size_t census_ht_get_size(const census_ht *ht) { return ht->size; }
|
|
|