• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
nghttp2_map_print_distance(nghttp2_map * map)129 void nghttp2_map_print_distance(nghttp2_map *map) {
130   uint32_t i;
131   size_t idx;
132   nghttp2_map_bucket *bkt;
133 
134   for (i = 0; i < map->tablelen; ++i) {
135     bkt = &map->table[i];
136 
137     if (bkt->data == NULL) {
138       fprintf(stderr, "@%u <EMPTY>\n", i);
139       continue;
140     }
141 
142     idx = h2idx(bkt->hash, map->tablelenbits);
143     fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i,
144             bkt->hash, bkt->key, idx,
145             distance(map->tablelen, map->tablelenbits, bkt, idx));
146   }
147 }
148 
insert(nghttp2_map_bucket * table,uint32_t tablelen,uint32_t tablelenbits,uint32_t hash,nghttp2_map_key_type key,void * data)149 static int insert(nghttp2_map_bucket *table, uint32_t tablelen,
150                   uint32_t tablelenbits, uint32_t hash,
151                   nghttp2_map_key_type key, void *data) {
152   size_t idx = h2idx(hash, tablelenbits);
153   size_t d = 0, dd;
154   nghttp2_map_bucket *bkt;
155 
156   for (;;) {
157     bkt = &table[idx];
158 
159     if (bkt->data == NULL) {
160       map_bucket_set_data(bkt, hash, key, data);
161       return 0;
162     }
163 
164     dd = distance(tablelen, tablelenbits, bkt, idx);
165     if (d > dd) {
166       map_bucket_swap(bkt, &hash, &key, &data);
167       d = dd;
168     } else if (bkt->key == key) {
169       /* TODO This check is just a waste after first swap or if this
170          function is called from map_resize.  That said, there is no
171          difference with or without this conditional in performance
172          wise. */
173       return NGHTTP2_ERR_INVALID_ARGUMENT;
174     }
175 
176     ++d;
177     idx = (idx + 1) & (tablelen - 1);
178   }
179 }
180 
181 /* new_tablelen must be power of 2 and new_tablelen == (1 <<
182    new_tablelenbits) must hold. */
map_resize(nghttp2_map * map,uint32_t new_tablelen,uint32_t new_tablelenbits)183 static int map_resize(nghttp2_map *map, uint32_t new_tablelen,
184                       uint32_t new_tablelenbits) {
185   uint32_t i;
186   nghttp2_map_bucket *new_table;
187   nghttp2_map_bucket *bkt;
188   int rv;
189   (void)rv;
190 
191   new_table =
192       nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket));
193   if (new_table == NULL) {
194     return NGHTTP2_ERR_NOMEM;
195   }
196 
197   for (i = 0; i < map->tablelen; ++i) {
198     bkt = &map->table[i];
199     if (bkt->data == NULL) {
200       continue;
201     }
202     rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
203                 bkt->data);
204 
205     assert(0 == rv);
206   }
207 
208   nghttp2_mem_free(map->mem, map->table);
209   map->tablelen = new_tablelen;
210   map->tablelenbits = new_tablelenbits;
211   map->table = new_table;
212 
213   return 0;
214 }
215 
nghttp2_map_insert(nghttp2_map * map,nghttp2_map_key_type key,void * data)216 int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
217   int rv;
218 
219   assert(data);
220 
221   /* Load factor is 0.75 */
222   if ((map->size + 1) * 4 > map->tablelen * 3) {
223     if (map->tablelen) {
224       rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
225       if (rv != 0) {
226         return rv;
227       }
228     } else {
229       rv = map_resize(map, 1 << NGHTTP2_INITIAL_TABLE_LENBITS,
230                       NGHTTP2_INITIAL_TABLE_LENBITS);
231       if (rv != 0) {
232         return rv;
233       }
234     }
235   }
236 
237   rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
238               data);
239   if (rv != 0) {
240     return rv;
241   }
242   ++map->size;
243   return 0;
244 }
245 
nghttp2_map_find(nghttp2_map * map,nghttp2_map_key_type key)246 void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
247   uint32_t h;
248   size_t idx;
249   nghttp2_map_bucket *bkt;
250   size_t d = 0;
251 
252   if (map->size == 0) {
253     return NULL;
254   }
255 
256   h = hash(key);
257   idx = h2idx(h, map->tablelenbits);
258 
259   for (;;) {
260     bkt = &map->table[idx];
261 
262     if (bkt->data == NULL ||
263         d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
264       return NULL;
265     }
266 
267     if (bkt->key == key) {
268       return bkt->data;
269     }
270 
271     ++d;
272     idx = (idx + 1) & (map->tablelen - 1);
273   }
274 }
275 
nghttp2_map_remove(nghttp2_map * map,nghttp2_map_key_type key)276 int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
277   uint32_t h;
278   size_t idx, didx;
279   nghttp2_map_bucket *bkt;
280   size_t d = 0;
281 
282   if (map->size == 0) {
283     return NGHTTP2_ERR_INVALID_ARGUMENT;
284   }
285 
286   h = hash(key);
287   idx = h2idx(h, map->tablelenbits);
288 
289   for (;;) {
290     bkt = &map->table[idx];
291 
292     if (bkt->data == NULL ||
293         d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
294       return NGHTTP2_ERR_INVALID_ARGUMENT;
295     }
296 
297     if (bkt->key == key) {
298       map_bucket_set_data(bkt, 0, 0, NULL);
299 
300       didx = idx;
301       idx = (idx + 1) & (map->tablelen - 1);
302 
303       for (;;) {
304         bkt = &map->table[idx];
305         if (bkt->data == NULL ||
306             distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
307           break;
308         }
309 
310         map->table[didx] = *bkt;
311         map_bucket_set_data(bkt, 0, 0, NULL);
312         didx = idx;
313 
314         idx = (idx + 1) & (map->tablelen - 1);
315       }
316 
317       --map->size;
318 
319       return 0;
320     }
321 
322     ++d;
323     idx = (idx + 1) & (map->tablelen - 1);
324   }
325 }
326 
nghttp2_map_clear(nghttp2_map * map)327 void nghttp2_map_clear(nghttp2_map *map) {
328   if (map->tablelen == 0) {
329     return;
330   }
331 
332   memset(map->table, 0, sizeof(*map->table) * map->tablelen);
333   map->size = 0;
334 }
335 
nghttp2_map_size(nghttp2_map * map)336 size_t nghttp2_map_size(nghttp2_map *map) { return map->size; }
337