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