changed from symbolic links to actual files

pull/10920/head
Vizerai 8 years ago
parent 26e85fe0d7
commit 6903d74238
  1. 1
      src/core/ext/census/intrusive_hash_map.c
  2. 303
      src/core/ext/census/intrusive_hash_map.c
  3. 1
      src/core/ext/census/intrusive_hash_map.h
  4. 169
      src/core/ext/census/intrusive_hash_map.h
  5. 1
      test/core/census/intrusive_hash_map_test.c
  6. 272
      test/core/census/intrusive_hash_map_test.c

@ -1 +0,0 @@
/google/src/cloud/jsking/cppTraceImpl/google3/experimental/users/jsking/intrusive_hash_map.c

@ -0,0 +1,303 @@
/*
* Copyright 2017 Google Inc.
*
* 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 "src/core/ext/census/intrusive_hash_map.h"
#include <string.h>
extern bool hm_index_compare(const hm_index *A, const hm_index *B);
/* Simple hashing function that takes lower 32 bits. */
static inline uint32_t chunked_vector_hasher(uint64_t key) {
return (uint32_t)key;
}
/* Vector chunks are 1MiB divided by pointer size. */
static const size_t VECTOR_CHUNK_SIZE = (1 << 20) / sizeof(void *);
/* Helper functions which return buckets from the chunked vector. These are
meant for internal use only within the intrusive_hash_map data structure. */
static inline void **get_mutable_bucket(const chunked_vector *buckets,
uint32_t index) {
if (index < VECTOR_CHUNK_SIZE) {
return &buckets->first_[index];
}
size_t rest_index = (index - VECTOR_CHUNK_SIZE) / VECTOR_CHUNK_SIZE;
return &buckets->rest_[rest_index][index % VECTOR_CHUNK_SIZE];
}
static inline void *get_bucket(const chunked_vector *buckets, uint32_t index) {
if (index < VECTOR_CHUNK_SIZE) {
return buckets->first_[index];
}
size_t rest_index = (index - VECTOR_CHUNK_SIZE) / VECTOR_CHUNK_SIZE;
return buckets->rest_[rest_index][index % VECTOR_CHUNK_SIZE];
}
/* Helper function. */
static inline size_t RestSize(const chunked_vector *vec) {
return (vec->size_ <= VECTOR_CHUNK_SIZE)
? 0
: (vec->size_ - VECTOR_CHUNK_SIZE - 1) / VECTOR_CHUNK_SIZE + 1;
}
/* Initialize chunked vector to size of 0. */
static void chunked_vector_init(chunked_vector *vec) {
vec->size_ = 0;
vec->first_ = NULL;
vec->rest_ = NULL;
}
/* Clear chunked vector and free all memory that has been allocated then
initialize chunked vector. */
static void chunked_vector_clear(chunked_vector *vec) {
if (vec->first_ != NULL) {
gpr_free(vec->first_);
}
if (vec->rest_ != NULL) {
size_t rest_size = RestSize(vec);
for (uint32_t i = 0; i < rest_size; ++i) {
if (vec->rest_[i] != NULL) {
gpr_free(vec->rest_[i]);
}
}
gpr_free(vec->rest_);
}
chunked_vector_init(vec);
}
/* Clear chunked vector and then resize it to n entries. Allow the first 1MB to
be read w/o an extra cache miss. The rest of the elements are stored in an
array of arrays to avoid large mallocs. */
static void chunked_vector_reset(chunked_vector *vec, size_t n) {
chunked_vector_clear(vec);
vec->size_ = n;
if (n <= VECTOR_CHUNK_SIZE) {
vec->first_ = (void **)gpr_malloc(sizeof(void *) * n);
memset(vec->first_, 0, sizeof(void *) * n);
} else {
vec->first_ = (void **)gpr_malloc(sizeof(void *) * VECTOR_CHUNK_SIZE);
memset(vec->first_, 0, sizeof(void *) * VECTOR_CHUNK_SIZE);
size_t rest_size = RestSize(vec);
vec->rest_ = (void ***)gpr_malloc(sizeof(void **) * rest_size);
memset(vec->rest_, 0, sizeof(void **) * rest_size);
int i = 0;
n -= VECTOR_CHUNK_SIZE;
while (n > 0) {
size_t this_size = GPR_MIN(n, VECTOR_CHUNK_SIZE);
vec->rest_[i] = (void **)gpr_malloc(sizeof(void *) * this_size);
memset(vec->rest_[i], 0, sizeof(void *) * this_size);
n -= this_size;
++i;
}
}
}
void intrusive_hash_map_init(intrusive_hash_map *hash_map,
uint32_t initial_log2_table_size) {
hash_map->log2_num_buckets = initial_log2_table_size;
hash_map->num_items = 0;
uint32_t num_buckets = (uint32_t)1 << hash_map->log2_num_buckets;
hash_map->extend_threshold = num_buckets >> 1;
chunked_vector_init(&hash_map->buckets);
chunked_vector_reset(&hash_map->buckets, num_buckets);
hash_map->hash_mask = num_buckets - 1;
}
bool intrusive_hash_map_empty(const intrusive_hash_map *hash_map) {
return hash_map->num_items == 0;
}
size_t intrusive_hash_map_size(const intrusive_hash_map *hash_map) {
return hash_map->num_items;
}
void intrusive_hash_map_end(const intrusive_hash_map *hash_map, hm_index *idx) {
idx->bucket_index = (uint32_t)hash_map->buckets.size_;
GPR_ASSERT(idx->bucket_index <= UINT32_MAX);
idx->item = NULL;
}
void intrusive_hash_map_next(const intrusive_hash_map *hash_map,
hm_index *idx) {
idx->item = idx->item->hash_link;
while (idx->item == NULL) {
idx->bucket_index++;
if (idx->bucket_index >= hash_map->buckets.size_) {
/* Reached end of table. */
idx->item = NULL;
return;
}
idx->item = (hm_item *)get_bucket(&hash_map->buckets, idx->bucket_index);
}
}
void intrusive_hash_map_begin(const intrusive_hash_map *hash_map,
hm_index *idx) {
for (uint32_t i = 0; i < hash_map->buckets.size_; ++i) {
if (get_bucket(&hash_map->buckets, i) != NULL) {
idx->bucket_index = i;
idx->item = (hm_item *)get_bucket(&hash_map->buckets, i);
return;
}
}
intrusive_hash_map_end(hash_map, idx);
}
hm_item *intrusive_hash_map_find(const intrusive_hash_map *hash_map,
uint64_t key) {
uint32_t index = chunked_vector_hasher(key) & hash_map->hash_mask;
hm_item *p = (hm_item *)get_bucket(&hash_map->buckets, index);
while (p != NULL) {
if (key == p->key) {
return p;
}
p = p->hash_link;
}
return NULL;
}
hm_item *intrusive_hash_map_erase(intrusive_hash_map *hash_map, uint64_t key) {
uint32_t index = chunked_vector_hasher(key) & hash_map->hash_mask;
hm_item **slot = (hm_item **)get_mutable_bucket(&hash_map->buckets, index);
hm_item *p = *slot;
if (p == NULL) {
return NULL;
}
if (key == p->key) {
*slot = p->hash_link;
p->hash_link = NULL;
hash_map->num_items--;
return p;
}
hm_item *prev = p;
p = p->hash_link;
while (p) {
if (key == p->key) {
prev->hash_link = p->hash_link;
p->hash_link = NULL;
hash_map->num_items--;
return p;
}
prev = p;
p = p->hash_link;
}
return NULL;
}
/* Insert an hm_item* into the underlying chunked vector. hash_mask is
* array_size-1. Returns true if it is a new hm_item and false if the hm_item
* already existed.
*/
static inline bool intrusive_hash_map_internal_insert(chunked_vector *buckets,
uint32_t hash_mask,
hm_item *item) {
const uint64_t key = item->key;
uint32_t index = chunked_vector_hasher(key) & hash_mask;
hm_item **slot = (hm_item **)get_mutable_bucket(buckets, index);
hm_item *p = *slot;
item->hash_link = p;
/* Check to see if key already exists. */
while (p) {
if (p->key == key) {
return false;
}
p = p->hash_link;
}
/* Otherwise add new entry. */
*slot = item;
return true;
}
/* Extend the allocated number of elements in the hash map by a factor of 2. */
void intrusive_hash_map_extend(intrusive_hash_map *hash_map) {
uint32_t new_log2_num_buckets = 1 + hash_map->log2_num_buckets;
uint32_t new_num_buckets = (uint32_t)1 << new_log2_num_buckets;
GPR_ASSERT(new_num_buckets <= UINT32_MAX && new_num_buckets > 0);
chunked_vector new_buckets;
chunked_vector_init(&new_buckets);
chunked_vector_reset(&new_buckets, new_num_buckets);
uint32_t new_hash_mask = new_num_buckets - 1;
hm_index cur_idx;
hm_index end_idx;
intrusive_hash_map_end(hash_map, &end_idx);
intrusive_hash_map_begin(hash_map, &cur_idx);
while (!hm_index_compare(&cur_idx, &end_idx)) {
hm_item *new_item = cur_idx.item;
intrusive_hash_map_next(hash_map, &cur_idx);
intrusive_hash_map_internal_insert(&new_buckets, new_hash_mask, new_item);
}
/* Set values for new chunked_vector. extend_threshold is set to half of
* new_num_buckets. */
hash_map->log2_num_buckets = new_log2_num_buckets;
chunked_vector_clear(&hash_map->buckets);
hash_map->buckets = new_buckets;
hash_map->hash_mask = new_hash_mask;
hash_map->extend_threshold = new_num_buckets >> 1;
}
/* Insert a hm_item. The hm_item must remain live until it is removed from the
table. This object does not take the ownership of hm_item. The caller must
remove this hm_item from the table and delete it before this table is
deleted. If hm_item exists already num_items is not changed. */
bool intrusive_hash_map_insert(intrusive_hash_map *hash_map, hm_item *item) {
if (hash_map->num_items >= hash_map->extend_threshold) {
intrusive_hash_map_extend(hash_map);
}
if (intrusive_hash_map_internal_insert(&hash_map->buckets,
hash_map->hash_mask, item)) {
hash_map->num_items++;
return true;
}
return false;
}
void intrusive_hash_map_clear(intrusive_hash_map *hash_map,
void (*free_object)(void *)) {
hm_index cur;
hm_index end;
intrusive_hash_map_end(hash_map, &end);
intrusive_hash_map_begin(hash_map, &cur);
while (!hm_index_compare(&cur, &end)) {
hm_index next = cur;
intrusive_hash_map_next(hash_map, &next);
if (cur.item != NULL) {
hm_item *item = intrusive_hash_map_erase(hash_map, cur.item->key);
(*free_object)((void *)item);
gpr_free(item);
}
cur = next;
}
}
void intrusive_hash_map_free(intrusive_hash_map *hash_map,
void (*free_object)(void *)) {
intrusive_hash_map_clear(hash_map, (*free_object));
hash_map->num_items = 0;
hash_map->extend_threshold = 0;
hash_map->log2_num_buckets = 0;
hash_map->hash_mask = 0;
chunked_vector_clear(&hash_map->buckets);
}

@ -1 +0,0 @@
/google/src/cloud/jsking/cppTraceImpl/google3/experimental/users/jsking/intrusive_hash_map.h

@ -0,0 +1,169 @@
/*
* Copyright 2017 Google Inc.
*
* 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.
*/
#ifndef GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H
#define GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
#include <stdbool.h>
/* intrusive_hash_map is a fast chained hash table. It is almost always faster
* than STL hash_map, since this hash map avoids malloc and free during insert
* and erase. This hash map is faster than a dense hash map when the application
* calls insert and erase more often than find. When the workload is dominated
* by find() a dense hash map may be faster.
*
* intrusive_hash_map uses an intrusive header placed within a user defined
* struct. IHM_key MUST be set to a valid value before insertion into the hash
* map or undefined behavior may occur. IHM_hash_link needs to be set to NULL
* initially.
*
* EXAMPLE USAGE:
*
* typedef struct string_item {
* INTRUSIVE_HASH_MAP_HEADER;
* // User data.
* char *str_buf;
* uint16_t len;
* } string_item;
*
* static string_item *make_string_item(uint64_t key, const char *buf,
* uint16_t len) {
* string_item *item = (string_item *)gpr_malloc(sizeof(string_item));
* item->IHM_key = key;
* item->IHM_hash_link = NULL;
* item->len = len;
* item->str_buf = (char *)malloc(len);
* memcpy(item->str_buf, buf, len);
* return item;
* }
*
* string_item *new_item1 = make_string_item(10, "test1", 5);
* bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item1);
*
* string_item *item1 =
* (string_item *)intrusive_hash_map_find(&hash_map, 10);
*/
/* Hash map item. Stores key and a pointer to the actual object. A user defined
* version of this can be passed in as long as the first 2 entries (key and
* hash_link) are the same. Pointer to struct will need to be cast as
* (hm_item *) when passed to hash map. This allows it to be intrusive. */
typedef struct hm_item {
uint64_t key;
struct hm_item *hash_link;
/* Optional user defined data after this. */
} hm_item;
/* Macro provided for ease of use. This must be first in the user defined
* struct. */
#define INTRUSIVE_HASH_MAP_HEADER \
uint64_t IHM_key; \
struct hm_item *IHM_hash_link
/* The chunked vector is a data structure that allocates buckets for use in the
* hash map. ChunkedVector is logically equivalent to T*[N] (cast void* as
* T*). It's internally implemented as an array of 1MB arrays to avoid
* allocating large consecutive memory chunks. This is an internal data
* structure that should never be accessed directly. */
typedef struct chunked_vector {
size_t size_;
void **first_;
void ***rest_;
} chunked_vector;
/* Core intrusive hash map data structure. All internal elements are managed by
* functions and should not be altered manually. intrusive_hash_map_init()
* must first be called before an intrusive_hash_map can be used. */
typedef struct intrusive_hash_map {
uint32_t num_items;
uint32_t extend_threshold;
uint32_t log2_num_buckets;
uint32_t hash_mask;
chunked_vector buckets;
} intrusive_hash_map;
/* Index struct which acts as a pseudo-iterator within the hash map. */
typedef struct hm_index {
uint32_t bucket_index; // hash map bucket index.
hm_item *item; // Pointer to hm_item within the hash map.
} hm_index;
/* Returns true if two hm_indices point to the same object within the hash map
* and false otherwise. */
inline bool hm_index_compare(const hm_index *A, const hm_index *B) {
return (A->item == B->item && A->bucket_index == B->bucket_index);
}
/* Helper functions for iterating over the hash map. */
/* On return idx will contain an invalid index which is always equal to
* hash_map->buckets.size_ */
void intrusive_hash_map_end(const intrusive_hash_map *hash_map, hm_index *idx);
/* Iterates index to the next valid entry in the hash map and stores the
* index within idx. If end of table is reached, idx will contain the same
* values as if intrusive_hash_map_end() was called. */
void intrusive_hash_map_next(const intrusive_hash_map *hash_map, hm_index *idx);
/* On return, idx will contain the index of the first non-null entry in the hash
* map. If the hash map is empty, idx will contain the same values as if
* intrusive_hash_map_end() was called. */
void intrusive_hash_map_begin(const intrusive_hash_map *hash_map,
hm_index *idx);
/* Initialize intrusive hash map data structure. This must be called before
* the hash map can be used. The initial size of an intrusive hash map will be
* 2^initial_log2_map_size (valid range is [0, 31]). */
void intrusive_hash_map_init(intrusive_hash_map *hash_map,
uint32_t initial_log2_map_size);
/* Returns true if the hash map is empty and false otherwise. */
bool intrusive_hash_map_empty(const intrusive_hash_map *hash_map);
/* Returns the number of elements currently in the hash map. */
size_t intrusive_hash_map_size(const intrusive_hash_map *hash_map);
/* Find a hm_item within the hash map by key. Returns NULL if item was not
* found. */
hm_item *intrusive_hash_map_find(const intrusive_hash_map *hash_map,
uint64_t key);
/* Erase the hm_item that corresponds with key. If the hm_item is found, return
* the pointer to the hm_item. Else returns NULL. */
hm_item *intrusive_hash_map_erase(intrusive_hash_map *hash_map, uint64_t key);
/* Attempts to insert a new hm_item into the hash map. If an element with the
* same key already exists, it will not insert the new item and return false.
* Otherwise, it will insert the new item and return true. */
bool intrusive_hash_map_insert(intrusive_hash_map *hash_map, hm_item *item);
/* Clear entire contents of the hash map, but leaves internal data structure
* untouched. Second argument takes a function pointer to a method that will
* free the object designated by the user and pointed to by hash_map->value. */
void intrusive_hash_map_clear(intrusive_hash_map *hash_map,
void (*free_object)(void *));
/* Erase all contents of hash map and free the memory. Hash map is invalid
* after calling this function and cannot be used until it has been
* reinitialized (intrusive_hash_map_init()). takes a function pointer to a
* method that will free the object designated by the user and pointed to by
* hash_map->value.*/
void intrusive_hash_map_free(intrusive_hash_map *hash_map,
void (*free_object)(void *));
#endif

@ -1 +0,0 @@
/google/src/cloud/jsking/cppTraceImpl/google3/experimental/users/jsking/intrusive_hash_map_test.c

@ -0,0 +1,272 @@
/*
* Copyright 2017 Google Inc.
*
* 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 "src/core/ext/census/intrusive_hash_map.h"
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
#include "test/core/util/test_config.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* The initial size of an intrusive hash map will be 2 to this power. */
static const uint32_t kInitialLog2Size = 4;
typedef struct object { uint64_t val; } object;
inline object *make_new_object(uint64_t val) {
object *obj = (object *)gpr_malloc(sizeof(object));
obj->val = val;
return obj;
}
typedef struct ptr_item {
INTRUSIVE_HASH_MAP_HEADER;
object *obj;
} ptr_item;
/* Helper function that creates a new hash map item. It is up to the user to
* free the item that was allocated. */
inline ptr_item *make_ptr_item(uint64_t key, uint64_t value) {
ptr_item *new_item = (ptr_item *)gpr_malloc(sizeof(ptr_item));
new_item->IHM_key = key;
new_item->IHM_hash_link = NULL;
new_item->obj = make_new_object(value);
return new_item;
}
static void free_ptr_item(void *ptr) { gpr_free(((ptr_item *)ptr)->obj); }
typedef struct string_item {
INTRUSIVE_HASH_MAP_HEADER;
// User data.
char buf[32];
uint16_t len;
} string_item;
static string_item *make_string_item(uint64_t key, const char *buf,
uint16_t len) {
string_item *item = (string_item *)gpr_malloc(sizeof(string_item));
item->IHM_key = key;
item->IHM_hash_link = NULL;
item->len = len;
memcpy(item->buf, buf, sizeof(char) * len);
return item;
}
static bool compare_string_item(const string_item *A, const string_item *B) {
if (A->IHM_key != B->IHM_key || A->len != B->len)
return false;
else {
for (int i = 0; i < A->len; ++i) {
if (A->buf[i] != B->buf[i]) return false;
}
}
return true;
}
void test_empty() {
intrusive_hash_map hash_map;
intrusive_hash_map_init(&hash_map, kInitialLog2Size);
GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map));
GPR_ASSERT(intrusive_hash_map_empty(&hash_map));
intrusive_hash_map_free(&hash_map, NULL);
}
void test_basic() {
intrusive_hash_map hash_map;
intrusive_hash_map_init(&hash_map, kInitialLog2Size);
ptr_item *new_item = make_ptr_item(10, 20);
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item);
GPR_ASSERT(ok);
ptr_item *item1 =
(ptr_item *)intrusive_hash_map_find(&hash_map, (uint64_t)10);
GPR_ASSERT(item1->obj->val == 20);
GPR_ASSERT(item1 == new_item);
ptr_item *item2 =
(ptr_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)10);
GPR_ASSERT(item2 == new_item);
gpr_free(new_item->obj);
gpr_free(new_item);
GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map));
intrusive_hash_map_free(&hash_map, &free_ptr_item);
}
void test_basic2() {
intrusive_hash_map hash_map;
intrusive_hash_map_init(&hash_map, kInitialLog2Size);
string_item *new_item1 = make_string_item(10, "test1", 5);
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item1);
GPR_ASSERT(ok);
string_item *new_item2 = make_string_item(20, "test2", 5);
ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item2);
GPR_ASSERT(ok);
string_item *item1 =
(string_item *)intrusive_hash_map_find(&hash_map, (uint64_t)10);
GPR_ASSERT(compare_string_item(new_item1, item1));
GPR_ASSERT(item1 == new_item1);
string_item *item2 =
(string_item *)intrusive_hash_map_find(&hash_map, (uint64_t)20);
GPR_ASSERT(compare_string_item(new_item2, item2));
GPR_ASSERT(item2 == new_item2);
item1 = (string_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)10);
GPR_ASSERT(item1 == new_item1);
item2 = (string_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)20);
GPR_ASSERT(item2 == new_item2);
gpr_free(new_item1);
gpr_free(new_item2);
GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map));
intrusive_hash_map_free(&hash_map, NULL);
}
// Test resetting and clearing the hash map.
void test_reset_clear() {
intrusive_hash_map hash_map;
intrusive_hash_map_init(&hash_map, kInitialLog2Size);
// Add some data to the hash_map.
for (uint64_t i = 0; i < 3; ++i) {
intrusive_hash_map_insert(&hash_map, (hm_item *)make_ptr_item(i, i));
}
GPR_ASSERT(3 == intrusive_hash_map_size(&hash_map));
// Test find.
for (uint64_t i = 0; i < 3; ++i) {
ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, i);
GPR_ASSERT(item != NULL);
GPR_ASSERT(item->IHM_key == i && item->obj->val == i);
}
intrusive_hash_map_clear(&hash_map, &free_ptr_item);
GPR_ASSERT(intrusive_hash_map_empty(&hash_map));
intrusive_hash_map_free(&hash_map, &free_ptr_item);
}
// Check that the hash_map contains every key between [min_value, max_value]
// (inclusive).
void check_hash_map_values(intrusive_hash_map *hash_map, uint64_t min_value,
uint64_t max_value) {
GPR_ASSERT(intrusive_hash_map_size(hash_map) == max_value - min_value + 1);
for (uint64_t i = min_value; i <= max_value; ++i) {
ptr_item *item = (ptr_item *)intrusive_hash_map_find(hash_map, i);
GPR_ASSERT(item != NULL);
GPR_ASSERT(item->obj->val == i);
}
}
// Add many items and cause the hash_map to extend.
void test_extend() {
intrusive_hash_map hash_map;
intrusive_hash_map_init(&hash_map, kInitialLog2Size);
const uint64_t kNumValues = (1 << 16);
for (uint64_t i = 0; i < kNumValues; ++i) {
ptr_item *item = make_ptr_item(i, i);
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)item);
GPR_ASSERT(ok);
if (i % 1000 == 0) {
check_hash_map_values(&hash_map, 0, i);
}
}
for (uint64_t i = 0; i < kNumValues; ++i) {
ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, i);
GPR_ASSERT(item != NULL);
GPR_ASSERT(item->IHM_key == i && item->obj->val == i);
ptr_item *item2 = (ptr_item *)intrusive_hash_map_erase(&hash_map, i);
GPR_ASSERT(item == item2);
gpr_free(item->obj);
gpr_free(item);
}
GPR_ASSERT(intrusive_hash_map_empty(&hash_map));
intrusive_hash_map_free(&hash_map, &free_ptr_item);
}
void test_stress() {
intrusive_hash_map hash_map;
intrusive_hash_map_init(&hash_map, kInitialLog2Size);
size_t n = 0;
for (uint64_t i = 0; i < 1000000; ++i) {
int op = rand() & 0x1;
switch (op) {
case 0: {
uint64_t key = (uint64_t)(rand() % 10000);
ptr_item *item = make_ptr_item(key, key);
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)item);
if (ok) {
n++;
} else {
gpr_free(item->obj);
gpr_free(item);
}
break;
}
case 1: {
uint64_t key = (uint64_t)(rand() % 10000);
ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, key);
if (item != NULL) {
n--;
GPR_ASSERT(key == item->obj->val);
ptr_item *item2 =
(ptr_item *)intrusive_hash_map_erase(&hash_map, key);
GPR_ASSERT(item == item2);
gpr_free(item->obj);
gpr_free(item);
}
break;
}
}
}
// Check size
GPR_ASSERT(n == intrusive_hash_map_size(&hash_map));
// Clean the hash_map up.
intrusive_hash_map_clear(&hash_map, &free_ptr_item);
GPR_ASSERT(intrusive_hash_map_empty(&hash_map));
intrusive_hash_map_free(&hash_map, &free_ptr_item);
}
int main(int argc, char **argv) {
grpc_test_init(argc, argv);
gpr_time_init();
srand((unsigned)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
test_empty();
test_basic();
test_basic2();
test_reset_clear();
test_extend();
test_stress();
return 0;
}
Loading…
Cancel
Save