1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2017 ngtcp2 contributors
5 * Copyright (c) 2012 nghttp2 contributors
6 *
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:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
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.
25 */
26 #include "nghttp2_map.h"
27
28 #include <string.h>
29 #include <assert.h>
30 #include <stdio.h>
31
32 #include "nghttp2_helper.h"
33
34 #define NGHTTP2_INITIAL_TABLE_LENBITS 4
35
nghttp2_map_init(nghttp2_map * map,nghttp2_mem * mem)36 void nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
37 map->mem = mem;
38 map->tablelen = 0;
39 map->tablelenbits = 0;
40 map->table = NULL;
41 map->size = 0;
42 }
43
nghttp2_map_free(nghttp2_map * map)44 void nghttp2_map_free(nghttp2_map *map) {
45 if (!map) {
46 return;
47 }
48
49 nghttp2_mem_free(map->mem, map->table);
50 }
51
nghttp2_map_each_free(nghttp2_map * map,int (* func)(void * data,void * ptr),void * ptr)52 void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
53 void *ptr) {
54 uint32_t i;
55 nghttp2_map_bucket *bkt;
56
57 for (i = 0; i < map->tablelen; ++i) {
58 bkt = &map->table[i];
59
60 if (bkt->data == NULL) {
61 continue;
62 }
63
64 func(bkt->data, ptr);
65 }
66 }
67
nghttp2_map_each(nghttp2_map * map,int (* func)(void * data,void * ptr),void * ptr)68 int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
69 void *ptr) {
70 int rv;
71 uint32_t i;
72 nghttp2_map_bucket *bkt;
73
74 if (map->size == 0) {
75 return 0;
76 }
77
78 for (i = 0; i < map->tablelen; ++i) {
79 bkt = &map->table[i];
80
81 if (bkt->data == NULL) {
82 continue;
83 }
84
85 rv = func(bkt->data, ptr);
86 if (rv != 0) {
87 return rv;
88 }
89 }
90
91 return 0;
92 }
93
hash(nghttp2_map_key_type key)94 static uint32_t hash(nghttp2_map_key_type key) {
95 return (uint32_t)key * 2654435769u;
96 }
97
h2idx(uint32_t hash,uint32_t bits)98 static size_t h2idx(uint32_t hash, uint32_t bits) {
99 return hash >> (32 - bits);
100 }
101
distance(uint32_t tablelen,uint32_t tablelenbits,nghttp2_map_bucket * bkt,size_t idx)102 static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
103 nghttp2_map_bucket *bkt, size_t idx) {
104 return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
105 }
106
map_bucket_swap(nghttp2_map_bucket * bkt,uint32_t * phash,nghttp2_map_key_type * pkey,void ** pdata)107 static void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash,
108 nghttp2_map_key_type *pkey, void **pdata) {
109 uint32_t h = bkt->hash;
110 nghttp2_map_key_type key = bkt->key;
111 void *data = bkt->data;
112
113 bkt->hash = *phash;
114 bkt->key = *pkey;
115 bkt->data = *pdata;
116
117 *phash = h;
118 *pkey = key;
119 *pdata = data;
120 }
121
map_bucket_set_data(nghttp2_map_bucket * bkt,uint32_t hash,nghttp2_map_key_type key,void * data)122 static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash,
123 nghttp2_map_key_type key, void *data) {
124 bkt->hash = hash;
125 bkt->key = key;
126 bkt->data = data;
127 }
128
129 #ifndef WIN32
nghttp2_map_print_distance(nghttp2_map * map)130 void nghttp2_map_print_distance(nghttp2_map *map) {
131 uint32_t i;
132 size_t idx;
133 nghttp2_map_bucket *bkt;
134
135 for (i = 0; i < map->tablelen; ++i) {
136 bkt = &map->table[i];
137
138 if (bkt->data == NULL) {
139 fprintf(stderr, "@%u <EMPTY>\n", i);
140 continue;
141 }
142
143 idx = h2idx(bkt->hash, map->tablelenbits);
144 fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i,
145 bkt->hash, bkt->key, idx,
146 distance(map->tablelen, map->tablelenbits, bkt, idx));
147 }
148 }
149 #endif /* !WIN32 */
150
insert(nghttp2_map_bucket * table,uint32_t tablelen,uint32_t tablelenbits,uint32_t hash,nghttp2_map_key_type key,void * data)151 static int insert(nghttp2_map_bucket *table, uint32_t tablelen,
152 uint32_t tablelenbits, uint32_t hash,
153 nghttp2_map_key_type key, void *data) {
154 size_t idx = h2idx(hash, tablelenbits);
155 size_t d = 0, dd;
156 nghttp2_map_bucket *bkt;
157
158 for (;;) {
159 bkt = &table[idx];
160
161 if (bkt->data == NULL) {
162 map_bucket_set_data(bkt, hash, key, data);
163 return 0;
164 }
165
166 dd = distance(tablelen, tablelenbits, bkt, idx);
167 if (d > dd) {
168 map_bucket_swap(bkt, &hash, &key, &data);
169 d = dd;
170 } else if (bkt->key == key) {
171 /* TODO This check is just a waste after first swap or if this
172 function is called from map_resize. That said, there is no
173 difference with or without this conditional in performance
174 wise. */
175 return NGHTTP2_ERR_INVALID_ARGUMENT;
176 }
177
178 ++d;
179 idx = (idx + 1) & (tablelen - 1);
180 }
181 }
182
183 /* new_tablelen must be power of 2 and new_tablelen == (1 <<
184 new_tablelenbits) must hold. */
map_resize(nghttp2_map * map,uint32_t new_tablelen,uint32_t new_tablelenbits)185 static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
186 uint32_t new_tablelenbits) {
187 uint32_t i;
188 nghttp2_map_bucket *new_table;
189 nghttp2_map_bucket *bkt;
190 int rv;
191 (void)rv;
192
193 new_table =
194 nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket));
195 if (new_table == NULL) {
196 return NGHTTP2_ERR_NOMEM;
197 }
198
199 for (i = 0; i < map->tablelen; ++i) {
200 bkt = &map->table[i];
201 if (bkt->data == NULL) {
202 continue;
203 }
204 rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
205 bkt->data);
206
207 assert(0 == rv);
208 }
209
210 nghttp2_mem_free(map->mem, map->table);
211 map->tablelen = new_tablelen;
212 map->tablelenbits = new_tablelenbits;
213 map->table = new_table;
214
215 return 0;
216 }
217
nghttp2_map_insert(nghttp2_map * map,nghttp2_map_key_type key,void * data)218 int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
219 int rv;
220
221 assert(data);
222
223 /* Load factor is 0.75 */
224 if ((map->size + 1) * 4 > map->tablelen * 3) {
225 if (map->tablelen) {
226 rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
227 if (rv != 0) {
228 return rv;
229 }
230 } else {
231 rv = map_resize(map, 1 << NGHTTP2_INITIAL_TABLE_LENBITS,
232 NGHTTP2_INITIAL_TABLE_LENBITS);
233 if (rv != 0) {
234 return rv;
235 }
236 }
237 }
238
239 rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
240 data);
241 if (rv != 0) {
242 return rv;
243 }
244 ++map->size;
245 return 0;
246 }
247
nghttp2_map_find(nghttp2_map * map,nghttp2_map_key_type key)248 void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
249 uint32_t h;
250 size_t idx;
251 nghttp2_map_bucket *bkt;
252 size_t d = 0;
253
254 if (map->size == 0) {
255 return NULL;
256 }
257
258 h = hash(key);
259 idx = h2idx(h, map->tablelenbits);
260
261 for (;;) {
262 bkt = &map->table[idx];
263
264 if (bkt->data == NULL ||
265 d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
266 return NULL;
267 }
268
269 if (bkt->key == key) {
270 return bkt->data;
271 }
272
273 ++d;
274 idx = (idx + 1) & (map->tablelen - 1);
275 }
276 }
277
nghttp2_map_remove(nghttp2_map * map,nghttp2_map_key_type key)278 int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
279 uint32_t h;
280 size_t idx, didx;
281 nghttp2_map_bucket *bkt;
282 size_t d = 0;
283
284 if (map->size == 0) {
285 return NGHTTP2_ERR_INVALID_ARGUMENT;
286 }
287
288 h = hash(key);
289 idx = h2idx(h, map->tablelenbits);
290
291 for (;;) {
292 bkt = &map->table[idx];
293
294 if (bkt->data == NULL ||
295 d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
296 return NGHTTP2_ERR_INVALID_ARGUMENT;
297 }
298
299 if (bkt->key == key) {
300 map_bucket_set_data(bkt, 0, 0, NULL);
301
302 didx = idx;
303 idx = (idx + 1) & (map->tablelen - 1);
304
305 for (;;) {
306 bkt = &map->table[idx];
307 if (bkt->data == NULL ||
308 distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
309 break;
310 }
311
312 map->table[didx] = *bkt;
313 map_bucket_set_data(bkt, 0, 0, NULL);
314 didx = idx;
315
316 idx = (idx + 1) & (map->tablelen - 1);
317 }
318
319 --map->size;
320
321 return 0;
322 }
323
324 ++d;
325 idx = (idx + 1) & (map->tablelen - 1);
326 }
327 }
328
nghttp2_map_clear(nghttp2_map * map)329 void nghttp2_map_clear(nghttp2_map *map) {
330 if (map->tablelen == 0) {
331 return;
332 }
333
334 memset(map->table, 0, sizeof(*map->table) * map->tablelen);
335 map->size = 0;
336 }
337
nghttp2_map_size(nghttp2_map * map)338 size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }
339