11cb0ef41Sopenharmony_ci/* 21cb0ef41Sopenharmony_ci * nghttp2 - HTTP/2 C Library 31cb0ef41Sopenharmony_ci * 41cb0ef41Sopenharmony_ci * Copyright (c) 2017 ngtcp2 contributors 51cb0ef41Sopenharmony_ci * Copyright (c) 2012 nghttp2 contributors 61cb0ef41Sopenharmony_ci * 71cb0ef41Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 81cb0ef41Sopenharmony_ci * a copy of this software and associated documentation files (the 91cb0ef41Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 101cb0ef41Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 111cb0ef41Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to 121cb0ef41Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 131cb0ef41Sopenharmony_ci * the following conditions: 141cb0ef41Sopenharmony_ci * 151cb0ef41Sopenharmony_ci * The above copyright notice and this permission notice shall be 161cb0ef41Sopenharmony_ci * included in all copies or substantial portions of the Software. 171cb0ef41Sopenharmony_ci * 181cb0ef41Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 191cb0ef41Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 201cb0ef41Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 211cb0ef41Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 221cb0ef41Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 231cb0ef41Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 241cb0ef41Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 251cb0ef41Sopenharmony_ci */ 261cb0ef41Sopenharmony_ci#include "nghttp2_map.h" 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci#include <string.h> 291cb0ef41Sopenharmony_ci#include <assert.h> 301cb0ef41Sopenharmony_ci#include <stdio.h> 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ci#include "nghttp2_helper.h" 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ci#define NGHTTP2_INITIAL_TABLE_LENBITS 4 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_civoid nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { 371cb0ef41Sopenharmony_ci map->mem = mem; 381cb0ef41Sopenharmony_ci map->tablelen = 0; 391cb0ef41Sopenharmony_ci map->tablelenbits = 0; 401cb0ef41Sopenharmony_ci map->table = NULL; 411cb0ef41Sopenharmony_ci map->size = 0; 421cb0ef41Sopenharmony_ci} 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_civoid nghttp2_map_free(nghttp2_map *map) { 451cb0ef41Sopenharmony_ci if (!map) { 461cb0ef41Sopenharmony_ci return; 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci nghttp2_mem_free(map->mem, map->table); 501cb0ef41Sopenharmony_ci} 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_civoid nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr), 531cb0ef41Sopenharmony_ci void *ptr) { 541cb0ef41Sopenharmony_ci uint32_t i; 551cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci for (i = 0; i < map->tablelen; ++i) { 581cb0ef41Sopenharmony_ci bkt = &map->table[i]; 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_ci if (bkt->data == NULL) { 611cb0ef41Sopenharmony_ci continue; 621cb0ef41Sopenharmony_ci } 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci func(bkt->data, ptr); 651cb0ef41Sopenharmony_ci } 661cb0ef41Sopenharmony_ci} 671cb0ef41Sopenharmony_ci 681cb0ef41Sopenharmony_ciint nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr), 691cb0ef41Sopenharmony_ci void *ptr) { 701cb0ef41Sopenharmony_ci int rv; 711cb0ef41Sopenharmony_ci uint32_t i; 721cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci if (map->size == 0) { 751cb0ef41Sopenharmony_ci return 0; 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci for (i = 0; i < map->tablelen; ++i) { 791cb0ef41Sopenharmony_ci bkt = &map->table[i]; 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci if (bkt->data == NULL) { 821cb0ef41Sopenharmony_ci continue; 831cb0ef41Sopenharmony_ci } 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci rv = func(bkt->data, ptr); 861cb0ef41Sopenharmony_ci if (rv != 0) { 871cb0ef41Sopenharmony_ci return rv; 881cb0ef41Sopenharmony_ci } 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci return 0; 921cb0ef41Sopenharmony_ci} 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_cistatic uint32_t hash(nghttp2_map_key_type key) { 951cb0ef41Sopenharmony_ci return (uint32_t)key * 2654435769u; 961cb0ef41Sopenharmony_ci} 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_cistatic size_t h2idx(uint32_t hash, uint32_t bits) { 991cb0ef41Sopenharmony_ci return hash >> (32 - bits); 1001cb0ef41Sopenharmony_ci} 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_cistatic size_t distance(uint32_t tablelen, uint32_t tablelenbits, 1031cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt, size_t idx) { 1041cb0ef41Sopenharmony_ci return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); 1051cb0ef41Sopenharmony_ci} 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_cistatic void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash, 1081cb0ef41Sopenharmony_ci nghttp2_map_key_type *pkey, void **pdata) { 1091cb0ef41Sopenharmony_ci uint32_t h = bkt->hash; 1101cb0ef41Sopenharmony_ci nghttp2_map_key_type key = bkt->key; 1111cb0ef41Sopenharmony_ci void *data = bkt->data; 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci bkt->hash = *phash; 1141cb0ef41Sopenharmony_ci bkt->key = *pkey; 1151cb0ef41Sopenharmony_ci bkt->data = *pdata; 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci *phash = h; 1181cb0ef41Sopenharmony_ci *pkey = key; 1191cb0ef41Sopenharmony_ci *pdata = data; 1201cb0ef41Sopenharmony_ci} 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_cistatic void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash, 1231cb0ef41Sopenharmony_ci nghttp2_map_key_type key, void *data) { 1241cb0ef41Sopenharmony_ci bkt->hash = hash; 1251cb0ef41Sopenharmony_ci bkt->key = key; 1261cb0ef41Sopenharmony_ci bkt->data = data; 1271cb0ef41Sopenharmony_ci} 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_civoid nghttp2_map_print_distance(nghttp2_map *map) { 1301cb0ef41Sopenharmony_ci uint32_t i; 1311cb0ef41Sopenharmony_ci size_t idx; 1321cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci for (i = 0; i < map->tablelen; ++i) { 1351cb0ef41Sopenharmony_ci bkt = &map->table[i]; 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ci if (bkt->data == NULL) { 1381cb0ef41Sopenharmony_ci fprintf(stderr, "@%u <EMPTY>\n", i); 1391cb0ef41Sopenharmony_ci continue; 1401cb0ef41Sopenharmony_ci } 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci idx = h2idx(bkt->hash, map->tablelenbits); 1431cb0ef41Sopenharmony_ci fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i, 1441cb0ef41Sopenharmony_ci bkt->hash, bkt->key, idx, 1451cb0ef41Sopenharmony_ci distance(map->tablelen, map->tablelenbits, bkt, idx)); 1461cb0ef41Sopenharmony_ci } 1471cb0ef41Sopenharmony_ci} 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_cistatic int insert(nghttp2_map_bucket *table, uint32_t tablelen, 1501cb0ef41Sopenharmony_ci uint32_t tablelenbits, uint32_t hash, 1511cb0ef41Sopenharmony_ci nghttp2_map_key_type key, void *data) { 1521cb0ef41Sopenharmony_ci size_t idx = h2idx(hash, tablelenbits); 1531cb0ef41Sopenharmony_ci size_t d = 0, dd; 1541cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci for (;;) { 1571cb0ef41Sopenharmony_ci bkt = &table[idx]; 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci if (bkt->data == NULL) { 1601cb0ef41Sopenharmony_ci map_bucket_set_data(bkt, hash, key, data); 1611cb0ef41Sopenharmony_ci return 0; 1621cb0ef41Sopenharmony_ci } 1631cb0ef41Sopenharmony_ci 1641cb0ef41Sopenharmony_ci dd = distance(tablelen, tablelenbits, bkt, idx); 1651cb0ef41Sopenharmony_ci if (d > dd) { 1661cb0ef41Sopenharmony_ci map_bucket_swap(bkt, &hash, &key, &data); 1671cb0ef41Sopenharmony_ci d = dd; 1681cb0ef41Sopenharmony_ci } else if (bkt->key == key) { 1691cb0ef41Sopenharmony_ci /* TODO This check is just a waste after first swap or if this 1701cb0ef41Sopenharmony_ci function is called from map_resize. That said, there is no 1711cb0ef41Sopenharmony_ci difference with or without this conditional in performance 1721cb0ef41Sopenharmony_ci wise. */ 1731cb0ef41Sopenharmony_ci return NGHTTP2_ERR_INVALID_ARGUMENT; 1741cb0ef41Sopenharmony_ci } 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci ++d; 1771cb0ef41Sopenharmony_ci idx = (idx + 1) & (tablelen - 1); 1781cb0ef41Sopenharmony_ci } 1791cb0ef41Sopenharmony_ci} 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci/* new_tablelen must be power of 2 and new_tablelen == (1 << 1821cb0ef41Sopenharmony_ci new_tablelenbits) must hold. */ 1831cb0ef41Sopenharmony_cistatic int map_resize(nghttp2_map *map, uint32_t new_tablelen, 1841cb0ef41Sopenharmony_ci uint32_t new_tablelenbits) { 1851cb0ef41Sopenharmony_ci uint32_t i; 1861cb0ef41Sopenharmony_ci nghttp2_map_bucket *new_table; 1871cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 1881cb0ef41Sopenharmony_ci int rv; 1891cb0ef41Sopenharmony_ci (void)rv; 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci new_table = 1921cb0ef41Sopenharmony_ci nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket)); 1931cb0ef41Sopenharmony_ci if (new_table == NULL) { 1941cb0ef41Sopenharmony_ci return NGHTTP2_ERR_NOMEM; 1951cb0ef41Sopenharmony_ci } 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_ci for (i = 0; i < map->tablelen; ++i) { 1981cb0ef41Sopenharmony_ci bkt = &map->table[i]; 1991cb0ef41Sopenharmony_ci if (bkt->data == NULL) { 2001cb0ef41Sopenharmony_ci continue; 2011cb0ef41Sopenharmony_ci } 2021cb0ef41Sopenharmony_ci rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, 2031cb0ef41Sopenharmony_ci bkt->data); 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ci assert(0 == rv); 2061cb0ef41Sopenharmony_ci } 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci nghttp2_mem_free(map->mem, map->table); 2091cb0ef41Sopenharmony_ci map->tablelen = new_tablelen; 2101cb0ef41Sopenharmony_ci map->tablelenbits = new_tablelenbits; 2111cb0ef41Sopenharmony_ci map->table = new_table; 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci return 0; 2141cb0ef41Sopenharmony_ci} 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ciint nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { 2171cb0ef41Sopenharmony_ci int rv; 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci assert(data); 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci /* Load factor is 0.75 */ 2221cb0ef41Sopenharmony_ci if ((map->size + 1) * 4 > map->tablelen * 3) { 2231cb0ef41Sopenharmony_ci if (map->tablelen) { 2241cb0ef41Sopenharmony_ci rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); 2251cb0ef41Sopenharmony_ci if (rv != 0) { 2261cb0ef41Sopenharmony_ci return rv; 2271cb0ef41Sopenharmony_ci } 2281cb0ef41Sopenharmony_ci } else { 2291cb0ef41Sopenharmony_ci rv = map_resize(map, 1 << NGHTTP2_INITIAL_TABLE_LENBITS, 2301cb0ef41Sopenharmony_ci NGHTTP2_INITIAL_TABLE_LENBITS); 2311cb0ef41Sopenharmony_ci if (rv != 0) { 2321cb0ef41Sopenharmony_ci return rv; 2331cb0ef41Sopenharmony_ci } 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci } 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, 2381cb0ef41Sopenharmony_ci data); 2391cb0ef41Sopenharmony_ci if (rv != 0) { 2401cb0ef41Sopenharmony_ci return rv; 2411cb0ef41Sopenharmony_ci } 2421cb0ef41Sopenharmony_ci ++map->size; 2431cb0ef41Sopenharmony_ci return 0; 2441cb0ef41Sopenharmony_ci} 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_civoid *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { 2471cb0ef41Sopenharmony_ci uint32_t h; 2481cb0ef41Sopenharmony_ci size_t idx; 2491cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 2501cb0ef41Sopenharmony_ci size_t d = 0; 2511cb0ef41Sopenharmony_ci 2521cb0ef41Sopenharmony_ci if (map->size == 0) { 2531cb0ef41Sopenharmony_ci return NULL; 2541cb0ef41Sopenharmony_ci } 2551cb0ef41Sopenharmony_ci 2561cb0ef41Sopenharmony_ci h = hash(key); 2571cb0ef41Sopenharmony_ci idx = h2idx(h, map->tablelenbits); 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci for (;;) { 2601cb0ef41Sopenharmony_ci bkt = &map->table[idx]; 2611cb0ef41Sopenharmony_ci 2621cb0ef41Sopenharmony_ci if (bkt->data == NULL || 2631cb0ef41Sopenharmony_ci d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { 2641cb0ef41Sopenharmony_ci return NULL; 2651cb0ef41Sopenharmony_ci } 2661cb0ef41Sopenharmony_ci 2671cb0ef41Sopenharmony_ci if (bkt->key == key) { 2681cb0ef41Sopenharmony_ci return bkt->data; 2691cb0ef41Sopenharmony_ci } 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci ++d; 2721cb0ef41Sopenharmony_ci idx = (idx + 1) & (map->tablelen - 1); 2731cb0ef41Sopenharmony_ci } 2741cb0ef41Sopenharmony_ci} 2751cb0ef41Sopenharmony_ci 2761cb0ef41Sopenharmony_ciint nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { 2771cb0ef41Sopenharmony_ci uint32_t h; 2781cb0ef41Sopenharmony_ci size_t idx, didx; 2791cb0ef41Sopenharmony_ci nghttp2_map_bucket *bkt; 2801cb0ef41Sopenharmony_ci size_t d = 0; 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci if (map->size == 0) { 2831cb0ef41Sopenharmony_ci return NGHTTP2_ERR_INVALID_ARGUMENT; 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci 2861cb0ef41Sopenharmony_ci h = hash(key); 2871cb0ef41Sopenharmony_ci idx = h2idx(h, map->tablelenbits); 2881cb0ef41Sopenharmony_ci 2891cb0ef41Sopenharmony_ci for (;;) { 2901cb0ef41Sopenharmony_ci bkt = &map->table[idx]; 2911cb0ef41Sopenharmony_ci 2921cb0ef41Sopenharmony_ci if (bkt->data == NULL || 2931cb0ef41Sopenharmony_ci d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { 2941cb0ef41Sopenharmony_ci return NGHTTP2_ERR_INVALID_ARGUMENT; 2951cb0ef41Sopenharmony_ci } 2961cb0ef41Sopenharmony_ci 2971cb0ef41Sopenharmony_ci if (bkt->key == key) { 2981cb0ef41Sopenharmony_ci map_bucket_set_data(bkt, 0, 0, NULL); 2991cb0ef41Sopenharmony_ci 3001cb0ef41Sopenharmony_ci didx = idx; 3011cb0ef41Sopenharmony_ci idx = (idx + 1) & (map->tablelen - 1); 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci for (;;) { 3041cb0ef41Sopenharmony_ci bkt = &map->table[idx]; 3051cb0ef41Sopenharmony_ci if (bkt->data == NULL || 3061cb0ef41Sopenharmony_ci distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) { 3071cb0ef41Sopenharmony_ci break; 3081cb0ef41Sopenharmony_ci } 3091cb0ef41Sopenharmony_ci 3101cb0ef41Sopenharmony_ci map->table[didx] = *bkt; 3111cb0ef41Sopenharmony_ci map_bucket_set_data(bkt, 0, 0, NULL); 3121cb0ef41Sopenharmony_ci didx = idx; 3131cb0ef41Sopenharmony_ci 3141cb0ef41Sopenharmony_ci idx = (idx + 1) & (map->tablelen - 1); 3151cb0ef41Sopenharmony_ci } 3161cb0ef41Sopenharmony_ci 3171cb0ef41Sopenharmony_ci --map->size; 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci return 0; 3201cb0ef41Sopenharmony_ci } 3211cb0ef41Sopenharmony_ci 3221cb0ef41Sopenharmony_ci ++d; 3231cb0ef41Sopenharmony_ci idx = (idx + 1) & (map->tablelen - 1); 3241cb0ef41Sopenharmony_ci } 3251cb0ef41Sopenharmony_ci} 3261cb0ef41Sopenharmony_ci 3271cb0ef41Sopenharmony_civoid nghttp2_map_clear(nghttp2_map *map) { 3281cb0ef41Sopenharmony_ci if (map->tablelen == 0) { 3291cb0ef41Sopenharmony_ci return; 3301cb0ef41Sopenharmony_ci } 3311cb0ef41Sopenharmony_ci 3321cb0ef41Sopenharmony_ci memset(map->table, 0, sizeof(*map->table) * map->tablelen); 3331cb0ef41Sopenharmony_ci map->size = 0; 3341cb0ef41Sopenharmony_ci} 3351cb0ef41Sopenharmony_ci 3361cb0ef41Sopenharmony_cisize_t nghttp2_map_size(nghttp2_map *map) { return map->size; } 337