2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2017 ngtcp2 contributors
5 * Copyright (c) 2012 nghttp2 contributors
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #include "nghttp2_map.h"
32 #include "nghttp2_helper.h"
34 #define NGHTTP2_INITIAL_TABLE_LENBITS 8
36 int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
38 map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS;
39 map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS;
41 nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
42 if (map->table == NULL) {
43 return NGHTTP2_ERR_NOMEM;
51 void nghttp2_map_free(nghttp2_map *map) {
56 nghttp2_mem_free(map->mem, map->table);
59 void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
62 nghttp2_map_bucket *bkt;
64 for (i = 0; i < map->tablelen; ++i) {
67 if (bkt->data == NULL) {
75 int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
79 nghttp2_map_bucket *bkt;
81 for (i = 0; i < map->tablelen; ++i) {
84 if (bkt->data == NULL) {
88 rv = func(bkt->data, ptr);
97 static uint32_t hash(nghttp2_map_key_type key) {
98 return (uint32_t)key * 2654435769u;
101 static size_t h2idx(uint32_t hash, uint32_t bits) {
102 return hash >> (32 - bits);
105 static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
106 nghttp2_map_bucket *bkt, size_t idx) {
107 return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
110 static void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash,
111 nghttp2_map_key_type *pkey, void **pdata) {
112 uint32_t h = bkt->hash;
113 nghttp2_map_key_type key = bkt->key;
114 void *data = bkt->data;
125 static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash,
126 nghttp2_map_key_type key, void *data) {
132 void nghttp2_map_print_distance(nghttp2_map *map) {
135 nghttp2_map_bucket *bkt;
137 for (i = 0; i < map->tablelen; ++i) {
138 bkt = &map->table[i];
140 if (bkt->data == NULL) {
141 fprintf(stderr, "@%u <EMPTY>\n", i);
145 idx = h2idx(bkt->hash, map->tablelenbits);
146 fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i,
147 bkt->hash, bkt->key, idx,
148 distance(map->tablelen, map->tablelenbits, bkt, idx));
152 static int insert(nghttp2_map_bucket *table, uint32_t tablelen,
153 uint32_t tablelenbits, uint32_t hash,
154 nghttp2_map_key_type key, void *data) {
155 size_t idx = h2idx(hash, tablelenbits);
157 nghttp2_map_bucket *bkt;
162 if (bkt->data == NULL) {
163 map_bucket_set_data(bkt, hash, key, data);
167 dd = distance(tablelen, tablelenbits, bkt, idx);
169 map_bucket_swap(bkt, &hash, &key, &data);
171 } else if (bkt->key == key) {
172 /* TODO This check is just a waste after first swap or if this
173 function is called from map_resize. That said, there is no
174 difference with or without this conditional in performance
176 return NGHTTP2_ERR_INVALID_ARGUMENT;
180 idx = (idx + 1) & (tablelen - 1);
184 /* new_tablelen must be power of 2 and new_tablelen == (1 <<
185 new_tablelenbits) must hold. */
186 static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
187 uint32_t new_tablelenbits) {
189 nghttp2_map_bucket *new_table;
190 nghttp2_map_bucket *bkt;
195 nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket));
196 if (new_table == NULL) {
197 return NGHTTP2_ERR_NOMEM;
200 for (i = 0; i < map->tablelen; ++i) {
201 bkt = &map->table[i];
202 if (bkt->data == NULL) {
205 rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
211 nghttp2_mem_free(map->mem, map->table);
212 map->tablelen = new_tablelen;
213 map->tablelenbits = new_tablelenbits;
214 map->table = new_table;
219 int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
224 /* Load factor is 0.75 */
225 if ((map->size + 1) * 4 > map->tablelen * 3) {
226 rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
232 rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
241 void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
242 uint32_t h = hash(key);
243 size_t idx = h2idx(h, map->tablelenbits);
244 nghttp2_map_bucket *bkt;
248 bkt = &map->table[idx];
250 if (bkt->data == NULL ||
251 d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
255 if (bkt->key == key) {
260 idx = (idx + 1) & (map->tablelen - 1);
264 int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
265 uint32_t h = hash(key);
266 size_t idx = h2idx(h, map->tablelenbits), didx;
267 nghttp2_map_bucket *bkt;
271 bkt = &map->table[idx];
273 if (bkt->data == NULL ||
274 d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
275 return NGHTTP2_ERR_INVALID_ARGUMENT;
278 if (bkt->key == key) {
279 map_bucket_set_data(bkt, 0, 0, NULL);
282 idx = (idx + 1) & (map->tablelen - 1);
285 bkt = &map->table[idx];
286 if (bkt->data == NULL ||
287 distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
291 map->table[didx] = *bkt;
292 map_bucket_set_data(bkt, 0, 0, NULL);
295 idx = (idx + 1) & (map->tablelen - 1);
304 idx = (idx + 1) & (map->tablelen - 1);
308 void nghttp2_map_clear(nghttp2_map *map) {
309 memset(map->table, 0, sizeof(*map->table) * map->tablelen);
313 size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }