aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/ext/census/intrusive_hash_map.h
blob: 900e4368c9662378f8715f8ae99f5a04b3f89ecc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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