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