aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/ext/census/intrusive_hash_map.h
blob: 2c7baa31fb45c1272e918a647b924acef0698a86 (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
/*
 *
 * Copyright 2017 gRPC 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.
 *
 */

#ifndef GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H
#define GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H

#include "src/core/ext/census/intrusive_hash_map_internal.h"

#ifdef __cplusplus
extern "C" {
#endif

/* intrusive_hash_map is a fast chained hash table. 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. The header field IHM_key MUST be set to a valid value before
 * insertion into the hash map or undefined behavior may occur. The header field
 * IHM_hash_link MUST 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;
 *  }
 *
 *  intrusive_hash_map hash_map;
 *  intrusive_hash_map_init(&hash_map, 4);
 *  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 provided the first 2 entries (key and
 * hash_link) are the same. These entries must be first in the user defined
 * struct. 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 (i.e. uint64_t key and hm_item * must be the first two elements in
 * that order). */
#define INTRUSIVE_HASH_MAP_HEADER \
  uint64_t IHM_key;               \
  struct hm_item *IHM_hash_link

/* 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);

/* Clears entire contents of the hash map, but leaves internal data structure
 * untouched. Second argument takes a function pointer to a function 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()). This function takes a function
 * pointer to a function 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 *));

#ifdef __cplusplus
}
#endif

#endif /* GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H */