From ebad3d475590cbd539880a6841572cc666e1ab1c Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 13 May 2021 15:01:58 +0900 Subject: [PATCH] Port new ngtcp2 map implementation --- lib/Makefile.am | 6 +- lib/nghttp2_ksl.c | 707 --------------------------------------- lib/nghttp2_ksl.h | 315 ----------------- lib/nghttp2_map.c | 356 +++++++++----------- lib/nghttp2_map.h | 59 ++-- lib/nghttp2_session.c | 12 +- lib/nghttp2_stream.c | 1 - lib/nghttp2_stream.h | 2 - tests/nghttp2_map_test.c | 79 +++-- tests/nghttp2_map_test.h | 4 +- 10 files changed, 252 insertions(+), 1289 deletions(-) delete mode 100644 lib/nghttp2_ksl.c delete mode 100644 lib/nghttp2_ksl.h diff --git a/lib/Makefile.am b/lib/Makefile.am index bb2691f0..1e1f2484 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -50,8 +50,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ - nghttp2_debug.c \ - nghttp2_ksl.c + nghttp2_debug.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_frame.h \ @@ -67,8 +66,7 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_mem.h \ nghttp2_http.h \ nghttp2_rcbuf.h \ - nghttp2_debug.h \ - nghttp2_ksl.h + nghttp2_debug.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \ diff --git a/lib/nghttp2_ksl.c b/lib/nghttp2_ksl.c deleted file mode 100644 index dffd1cbc..00000000 --- a/lib/nghttp2_ksl.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2020 nghttp2 contributors - * Copyright (c) 2018 ngtcp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_ksl.h" - -#include -#include -#include -#include - -#include "nghttp2_mem.h" - -static size_t ksl_nodelen(size_t keylen) { - return (sizeof(nghttp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; -} - -static size_t ksl_blklen(size_t nodelen) { - return sizeof(nghttp2_ksl_blk) + nodelen * NGHTTP2_KSL_MAX_NBLK - - sizeof(uint64_t); -} - -/* - * ksl_node_set_key sets |key| to |node|. - */ -static void ksl_node_set_key(nghttp2_ksl *ksl, nghttp2_ksl_node *node, - const void *key) { - memcpy(node->key, key, ksl->keylen); -} - -int nghttp2_ksl_init(nghttp2_ksl *ksl, nghttp2_ksl_compar compar, size_t keylen, - nghttp2_mem *mem) { - size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - nghttp2_ksl_blk *head; - - ksl->head = nghttp2_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGHTTP2_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; - ksl->compar = compar; - ksl->keylen = keylen; - ksl->nodelen = nodelen; - ksl->n = 0; - ksl->mem = mem; - - head = ksl->head; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; - - return 0; -} - -/* - * ksl_free_blk frees |blk| recursively. - */ -static void ksl_free_blk(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk) { - size_t i; - - if (!blk->leaf) { - for (i = 0; i < blk->n; ++i) { - ksl_free_blk(ksl, nghttp2_ksl_nth_node(ksl, blk, i)->blk); - } - } - - nghttp2_mem_free(ksl->mem, blk); -} - -void nghttp2_ksl_free(nghttp2_ksl *ksl) { - if (!ksl) { - return; - } - - ksl_free_blk(ksl, ksl->head); -} - -/* - * ksl_split_blk splits |blk| into 2 nghttp2_ksl_blk objects. The new - * nghttp2_ksl_blk is always the "right" block. - * - * It returns the pointer to the nghttp2_ksl_blk created which is the - * located at the right of |blk|, or NULL which indicates out of - * memory error. - */ -static nghttp2_ksl_blk *ksl_split_blk(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk) { - nghttp2_ksl_blk *rblk; - - rblk = nghttp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); - if (rblk == NULL) { - return NULL; - } - - rblk->next = blk->next; - blk->next = rblk; - if (rblk->next) { - rblk->next->prev = rblk; - } else if (ksl->back == blk) { - ksl->back = rblk; - } - rblk->prev = blk; - rblk->leaf = blk->leaf; - - rblk->n = blk->n / 2; - - memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), - ksl->nodelen * rblk->n); - - blk->n -= rblk->n; - - assert(blk->n >= NGHTTP2_KSL_MIN_NBLK); - assert(rblk->n >= NGHTTP2_KSL_MIN_NBLK); - - return rblk; -} - -/* - * ksl_split_node splits a node included in |blk| at the position |i| - * into 2 adjacent nodes. The new node is always inserted at the - * position |i+1|. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int ksl_split_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { - nghttp2_ksl_node *node; - nghttp2_ksl_blk *lblk = nghttp2_ksl_nth_node(ksl, blk, i)->blk, *rblk; - - rblk = ksl_split_blk(ksl, lblk); - if (rblk == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - memmove(blk->nodes + (i + 2) * ksl->nodelen, - blk->nodes + (i + 1) * ksl->nodelen, - ksl->nodelen * (blk->n - (i + 1))); - - node = nghttp2_ksl_nth_node(ksl, blk, i + 1); - node->blk = rblk; - ++blk->n; - ksl_node_set_key(ksl, node, - nghttp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); - - node = nghttp2_ksl_nth_node(ksl, blk, i); - ksl_node_set_key(ksl, node, - nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - - return 0; -} - -/* - * ksl_split_head splits a head (root) block. It increases the height - * of skip list by 1. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int ksl_split_head(nghttp2_ksl *ksl) { - nghttp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; - nghttp2_ksl_node *node; - - rblk = ksl_split_blk(ksl, ksl->head); - if (rblk == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - lblk = ksl->head; - - nhead = nghttp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); - if (nhead == NULL) { - nghttp2_mem_free(ksl->mem, rblk); - return NGHTTP2_ERR_NOMEM; - } - nhead->next = nhead->prev = NULL; - nhead->n = 2; - nhead->leaf = 0; - - node = nghttp2_ksl_nth_node(ksl, nhead, 0); - ksl_node_set_key(ksl, node, - nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - node->blk = lblk; - - node = nghttp2_ksl_nth_node(ksl, nhead, 1); - ksl_node_set_key(ksl, node, - nghttp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); - node->blk = rblk; - - ksl->head = nhead; - - return 0; -} - -/* - * insert_node inserts a node whose key is |key| with the associated - * |data| at the index of |i|. This function assumes that the number - * of nodes contained by |blk| is strictly less than - * NGHTTP2_KSL_MAX_NBLK. - */ -static void ksl_insert_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i, - const nghttp2_ksl_key *key, void *data) { - nghttp2_ksl_node *node; - - assert(blk->n < NGHTTP2_KSL_MAX_NBLK); - - memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, - ksl->nodelen * (blk->n - i)); - - node = nghttp2_ksl_nth_node(ksl, blk, i); - ksl_node_set_key(ksl, node, key); - node->data = data; - - ++blk->n; -} - -static size_t ksl_bsearch(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, - const nghttp2_ksl_key *key, - nghttp2_ksl_compar compar) { - ssize_t left = -1, right = (ssize_t)blk->n, mid; - nghttp2_ksl_node *node; - - while (right - left > 1) { - mid = (left + right) / 2; - node = nghttp2_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((nghttp2_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } - - return (size_t)right; -} - -int nghttp2_ksl_insert(nghttp2_ksl *ksl, nghttp2_ksl_it *it, - const nghttp2_ksl_key *key, void *data) { - nghttp2_ksl_blk *blk = ksl->head; - nghttp2_ksl_node *node; - size_t i; - int rv; - - if (blk->n == NGHTTP2_KSL_MAX_NBLK) { - rv = ksl_split_head(ksl); - if (rv != 0) { - return rv; - } - blk = ksl->head; - } - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (blk->leaf) { - if (i < blk->n && - !ksl->compar(key, nghttp2_ksl_nth_node(ksl, blk, i)->key)) { - if (it) { - *it = nghttp2_ksl_end(ksl); - } - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - ksl_insert_node(ksl, blk, i, key, data); - ++ksl->n; - if (it) { - nghttp2_ksl_it_init(it, ksl, blk, i); - } - return 0; - } - - if (i == blk->n) { - /* This insertion extends the largest key in this subtree. */ - for (; !blk->leaf;) { - node = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1); - if (node->blk->n == NGHTTP2_KSL_MAX_NBLK) { - rv = ksl_split_node(ksl, blk, blk->n - 1); - if (rv != 0) { - return rv; - } - node = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1); - } - ksl_node_set_key(ksl, node, key); - blk = node->blk; - } - ksl_insert_node(ksl, blk, blk->n, key, data); - ++ksl->n; - if (it) { - nghttp2_ksl_it_init(it, ksl, blk, blk->n - 1); - } - return 0; - } - - node = nghttp2_ksl_nth_node(ksl, blk, i); - - if (node->blk->n == NGHTTP2_KSL_MAX_NBLK) { - rv = ksl_split_node(ksl, blk, i); - if (rv != 0) { - return rv; - } - if (ksl->compar((nghttp2_ksl_key *)node->key, key)) { - node = nghttp2_ksl_nth_node(ksl, blk, i + 1); - if (ksl->compar((nghttp2_ksl_key *)node->key, key)) { - ksl_node_set_key(ksl, node, key); - } - } - } - - blk = node->blk; - } -} - -/* - * ksl_remove_node removes the node included in |blk| at the index of - * |i|. - */ -static void ksl_remove_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { - memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, - ksl->nodelen * (blk->n - (i + 1))); - - --blk->n; -} - -/* - * ksl_merge_node merges 2 nodes which are the nodes at the index of - * |i| and |i + 1|. - * - * If |blk| is the direct descendant of head (root) block and the head - * block contains just 2 nodes, the merged block becomes head block, - * which decreases the height of |ksl| by 1. - * - * This function returns the pointer to the merged block. - */ -static nghttp2_ksl_blk *ksl_merge_node(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, - size_t i) { - nghttp2_ksl_blk *lblk, *rblk; - - assert(i + 1 < blk->n); - - lblk = nghttp2_ksl_nth_node(ksl, blk, i)->blk; - rblk = nghttp2_ksl_nth_node(ksl, blk, i + 1)->blk; - - assert(lblk->n + rblk->n < NGHTTP2_KSL_MAX_NBLK); - - memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, - ksl->nodelen * rblk->n); - - lblk->n += rblk->n; - lblk->next = rblk->next; - if (lblk->next) { - lblk->next->prev = lblk; - } else if (ksl->back == rblk) { - ksl->back = lblk; - } - - nghttp2_mem_free(ksl->mem, rblk); - - if (ksl->head == blk && blk->n == 2) { - nghttp2_mem_free(ksl->mem, ksl->head); - ksl->head = lblk; - } else { - ksl_remove_node(ksl, blk, i + 1); - ksl_node_set_key(ksl, nghttp2_ksl_nth_node(ksl, blk, i), - nghttp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); - } - - return lblk; -} - -/* - * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to - * blk->nodes[i - 1]->blk->nodes. - */ -static void ksl_shift_left(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { - nghttp2_ksl_node *lnode, *rnode, *dest, *src; - - assert(i > 0); - - lnode = nghttp2_ksl_nth_node(ksl, blk, i - 1); - rnode = nghttp2_ksl_nth_node(ksl, blk, i); - - assert(lnode->blk->n < NGHTTP2_KSL_MAX_NBLK); - assert(rnode->blk->n > NGHTTP2_KSL_MIN_NBLK); - - dest = nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n); - src = nghttp2_ksl_nth_node(ksl, rnode->blk, 0); - - memcpy(dest, src, ksl->nodelen); - ksl_node_set_key(ksl, lnode, dest->key); - ++lnode->blk->n; - - --rnode->blk->n; - memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen, - ksl->nodelen * rnode->blk->n); -} - -/* - * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to - * blk->nodes[i + 1]->blk->nodes. - */ -static void ksl_shift_right(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t i) { - nghttp2_ksl_node *lnode, *rnode, *dest, *src; - - assert(i < blk->n - 1); - - lnode = nghttp2_ksl_nth_node(ksl, blk, i); - rnode = nghttp2_ksl_nth_node(ksl, blk, i + 1); - - assert(lnode->blk->n > NGHTTP2_KSL_MIN_NBLK); - assert(rnode->blk->n < NGHTTP2_KSL_MAX_NBLK); - - memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes, - ksl->nodelen * rnode->blk->n); - ++rnode->blk->n; - - dest = nghttp2_ksl_nth_node(ksl, rnode->blk, 0); - src = nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); - - memcpy(dest, src, ksl->nodelen); - - --lnode->blk->n; - ksl_node_set_key( - ksl, lnode, - nghttp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); -} - -/* - * key_equal returns nonzero if |lhs| and |rhs| are equal using the - * function |compar|. - */ -static int key_equal(nghttp2_ksl_compar compar, const nghttp2_ksl_key *lhs, - const nghttp2_ksl_key *rhs) { - return !compar(lhs, rhs) && !compar(rhs, lhs); -} - -int nghttp2_ksl_remove(nghttp2_ksl *ksl, nghttp2_ksl_it *it, - const nghttp2_ksl_key *key) { - nghttp2_ksl_blk *blk = ksl->head; - nghttp2_ksl_node *node; - size_t i; - - if (!blk->leaf && blk->n == 2 && - nghttp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP2_KSL_MIN_NBLK && - nghttp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP2_KSL_MIN_NBLK) { - blk = ksl_merge_node(ksl, ksl->head, 0); - } - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (i == blk->n) { - if (it) { - *it = nghttp2_ksl_end(ksl); - } - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (blk->leaf) { - if (ksl->compar(key, nghttp2_ksl_nth_node(ksl, blk, i)->key)) { - if (it) { - *it = nghttp2_ksl_end(ksl); - } - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - ksl_remove_node(ksl, blk, i); - --ksl->n; - if (it) { - if (blk->n == i && blk->next) { - nghttp2_ksl_it_init(it, ksl, blk->next, 0); - } else { - nghttp2_ksl_it_init(it, ksl, blk, i); - } - } - return 0; - } - - node = nghttp2_ksl_nth_node(ksl, blk, i); - - if (node->blk->n == NGHTTP2_KSL_MIN_NBLK) { - if (i > 0 && nghttp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > - NGHTTP2_KSL_MIN_NBLK) { - ksl_shift_right(ksl, blk, i - 1); - blk = node->blk; - } else if (i + 1 < blk->n && - nghttp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > - NGHTTP2_KSL_MIN_NBLK) { - ksl_shift_left(ksl, blk, i + 1); - blk = node->blk; - } else if (i > 0) { - blk = ksl_merge_node(ksl, blk, i - 1); - } else { - assert(i + 1 < blk->n); - blk = ksl_merge_node(ksl, blk, i); - } - } else { - blk = node->blk; - } - } -} - -nghttp2_ksl_it nghttp2_ksl_lower_bound(nghttp2_ksl *ksl, - const nghttp2_ksl_key *key) { - nghttp2_ksl_blk *blk = ksl->head; - nghttp2_ksl_it it; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, key, ksl->compar); - - if (blk->leaf) { - if (i == blk->n && blk->next) { - blk = blk->next; - i = 0; - } - nghttp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - - if (i == blk->n) { - /* This happens if descendant has smaller key. Fast forward to - find last node in this subtree. */ - for (; !blk->leaf; blk = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) - ; - if (blk->next) { - blk = blk->next; - i = 0; - } else { - i = blk->n; - } - nghttp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - blk = nghttp2_ksl_nth_node(ksl, blk, i)->blk; - } -} - -nghttp2_ksl_it nghttp2_ksl_lower_bound_compar(nghttp2_ksl *ksl, - const nghttp2_ksl_key *key, - nghttp2_ksl_compar compar) { - nghttp2_ksl_blk *blk = ksl->head; - nghttp2_ksl_it it; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, key, compar); - - if (blk->leaf) { - if (i == blk->n && blk->next) { - blk = blk->next; - i = 0; - } - nghttp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - - if (i == blk->n) { - /* This happens if descendant has smaller key. Fast forward to - find last node in this subtree. */ - for (; !blk->leaf; blk = nghttp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) - ; - if (blk->next) { - blk = blk->next; - i = 0; - } else { - i = blk->n; - } - nghttp2_ksl_it_init(&it, ksl, blk, i); - return it; - } - blk = nghttp2_ksl_nth_node(ksl, blk, i)->blk; - } -} - -void nghttp2_ksl_update_key(nghttp2_ksl *ksl, const nghttp2_ksl_key *old_key, - const nghttp2_ksl_key *new_key) { - nghttp2_ksl_blk *blk = ksl->head; - nghttp2_ksl_node *node; - size_t i; - - for (;;) { - i = ksl_bsearch(ksl, blk, old_key, ksl->compar); - - assert(i < blk->n); - node = nghttp2_ksl_nth_node(ksl, blk, i); - - if (blk->leaf) { - assert(key_equal(ksl->compar, (nghttp2_ksl_key *)node->key, old_key)); - ksl_node_set_key(ksl, node, new_key); - return; - } - - if (key_equal(ksl->compar, (nghttp2_ksl_key *)node->key, old_key) || - ksl->compar((nghttp2_ksl_key *)node->key, new_key)) { - ksl_node_set_key(ksl, node, new_key); - } - - blk = node->blk; - } -} - -static void ksl_print(nghttp2_ksl *ksl, nghttp2_ksl_blk *blk, size_t level) { - size_t i; - nghttp2_ksl_node *node; - - fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); - - if (blk->leaf) { - for (i = 0; i < blk->n; ++i) { - node = nghttp2_ksl_nth_node(ksl, blk, i); - fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); - } - fprintf(stderr, "\n"); - return; - } - - for (i = 0; i < blk->n; ++i) { - ksl_print(ksl, nghttp2_ksl_nth_node(ksl, blk, i)->blk, level + 1); - } -} - -size_t nghttp2_ksl_len(nghttp2_ksl *ksl) { return ksl->n; } - -void nghttp2_ksl_clear(nghttp2_ksl *ksl) { - size_t i; - nghttp2_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, nghttp2_ksl_nth_node(ksl, ksl->head, i)->blk); - } - } - - ksl->front = ksl->back = ksl->head; - ksl->n = 0; - - head = ksl->head; - - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; -} - -void nghttp2_ksl_print(nghttp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } - -nghttp2_ksl_it nghttp2_ksl_begin(const nghttp2_ksl *ksl) { - nghttp2_ksl_it it; - nghttp2_ksl_it_init(&it, ksl, ksl->front, 0); - return it; -} - -nghttp2_ksl_it nghttp2_ksl_end(const nghttp2_ksl *ksl) { - nghttp2_ksl_it it; - nghttp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); - return it; -} - -void nghttp2_ksl_it_init(nghttp2_ksl_it *it, const nghttp2_ksl *ksl, - nghttp2_ksl_blk *blk, size_t i) { - it->ksl = ksl; - it->blk = blk; - it->i = i; -} - -void *nghttp2_ksl_it_get(const nghttp2_ksl_it *it) { - assert(it->i < it->blk->n); - return nghttp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - -void nghttp2_ksl_it_prev(nghttp2_ksl_it *it) { - assert(!nghttp2_ksl_it_begin(it)); - - if (it->i == 0) { - it->blk = it->blk->prev; - it->i = it->blk->n - 1; - } else { - --it->i; - } -} - -int nghttp2_ksl_it_begin(const nghttp2_ksl_it *it) { - return it->i == 0 && it->blk->prev == NULL; -} diff --git a/lib/nghttp2_ksl.h b/lib/nghttp2_ksl.h deleted file mode 100644 index 39d900ff..00000000 --- a/lib/nghttp2_ksl.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2020 nghttp2 contributors - * Copyright (c) 2018 ngtcp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_KSL_H -#define NGHTTP2_KSL_H - -#ifdef HAVE_CONFIG_H -# include -#endif /* HAVE_CONFIG_H */ - -#include - -#include - -/* - * Skip List using single key instead of range. - */ - -#define NGHTTP2_KSL_DEGR 16 -/* NGHTTP2_KSL_MAX_NBLK is the maximum number of nodes which a single - block can contain. */ -#define NGHTTP2_KSL_MAX_NBLK (2 * NGHTTP2_KSL_DEGR - 1) -/* NGHTTP2_KSL_MIN_NBLK is the minimum number of nodes which a single - block other than root must contains. */ -#define NGHTTP2_KSL_MIN_NBLK (NGHTTP2_KSL_DEGR - 1) - -/* - * nghttp2_ksl_key represents key in nghttp2_ksl. - */ -typedef void nghttp2_ksl_key; - -struct nghttp2_ksl_node; -typedef struct nghttp2_ksl_node nghttp2_ksl_node; - -struct nghttp2_ksl_blk; -typedef struct nghttp2_ksl_blk nghttp2_ksl_blk; - -/* - * nghttp2_ksl_node is a node which contains either nghttp2_ksl_blk or - * opaque data. If a node is an internal node, it contains - * nghttp2_ksl_blk. Otherwise, it has data. The key is stored at the - * location starting at key. - */ -struct nghttp2_ksl_node { - union { - nghttp2_ksl_blk *blk; - void *data; - }; - union { - uint64_t align; - /* key is a buffer to include key associated to this node. - Because the length of key is unknown until nghttp2_ksl_init is - called, the actual buffer will be allocated after this - field. */ - uint8_t key[1]; - }; -}; - -/* - * nghttp2_ksl_blk contains nghttp2_ksl_node objects. - */ -struct nghttp2_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - nghttp2_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - nghttp2_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - size_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - int leaf; - union { - uint64_t align; - /* nodes is a buffer to contain NGHTTP2_KSL_MAX_NBLK - nghttp2_ksl_node objects. Because nghttp2_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until nghttp2_ksl_init is - called. */ - uint8_t nodes[1]; - }; -}; - -/* - * nghttp2_ksl_compar is a function type which returns nonzero if key - * |lhs| should be placed before |rhs|. It returns 0 otherwise. - */ -typedef int (*nghttp2_ksl_compar)(const nghttp2_ksl_key *lhs, - const nghttp2_ksl_key *rhs); - -struct nghttp2_ksl; -typedef struct nghttp2_ksl nghttp2_ksl; - -struct nghttp2_ksl_it; -typedef struct nghttp2_ksl_it nghttp2_ksl_it; - -/* - * nghttp2_ksl_it is a forward iterator to iterate nodes. - */ -struct nghttp2_ksl_it { - const nghttp2_ksl *ksl; - nghttp2_ksl_blk *blk; - size_t i; -}; - -/* - * nghttp2_ksl is a deterministic paged skip list. - */ -struct nghttp2_ksl { - /* head points to the root block. */ - nghttp2_ksl_blk *head; - /* front points to the first leaf block. */ - nghttp2_ksl_blk *front; - /* back points to the last leaf block. */ - nghttp2_ksl_blk *back; - nghttp2_ksl_compar compar; - size_t n; - /* keylen is the size of key */ - size_t keylen; - /* nodelen is the actual size of nghttp2_ksl_node including key - storage. */ - size_t nodelen; - nghttp2_mem *mem; -}; - -/* - * nghttp2_ksl_init initializes |ksl|. |compar| specifies compare - * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_ksl_init(nghttp2_ksl *ksl, nghttp2_ksl_compar compar, size_t keylen, - nghttp2_mem *mem); - -/* - * nghttp2_ksl_free frees resources allocated for |ksl|. If |ksl| is - * NULL, this function does nothing. It does not free the memory - * region pointed by |ksl| itself. - */ -void nghttp2_ksl_free(nghttp2_ksl *ksl); - -/* - * nghttp2_ksl_insert inserts |key| with its associated |data|. On - * successful insertion, the iterator points to the inserted node is - * stored in |*it|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * |key| already exists. - */ -int nghttp2_ksl_insert(nghttp2_ksl *ksl, nghttp2_ksl_it *it, - const nghttp2_ksl_key *key, void *data); - -/* - * nghttp2_ksl_remove removes the |key| from |ksl|. - * - * This function assigns the iterator to |*it|, which points to the - * node which is located at the right next of the removed node if |it| - * is not NULL. If |key| is not found, no deletion takes place and - * the return value of nghttp2_ksl_end(ksl) is assigned to |*it|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * |key| does not exist. - */ -int nghttp2_ksl_remove(nghttp2_ksl *ksl, nghttp2_ksl_it *it, - const nghttp2_ksl_key *key); - -/* - * nghttp2_ksl_lower_bound returns the iterator which points to the - * first node which has the key which is equal to |key| or the last - * node which satisfies !compar(&node->key, key). If there is no such - * node, it returns the iterator which satisfies nghttp2_ksl_it_end(it) - * != 0. - */ -nghttp2_ksl_it nghttp2_ksl_lower_bound(nghttp2_ksl *ksl, - const nghttp2_ksl_key *key); - -/* - * nghttp2_ksl_lower_bound_compar works like nghttp2_ksl_lower_bound, - * but it takes custom function |compar| to do lower bound search. - */ -nghttp2_ksl_it nghttp2_ksl_lower_bound_compar(nghttp2_ksl *ksl, - const nghttp2_ksl_key *key, - nghttp2_ksl_compar compar); - -/* - * nghttp2_ksl_update_key replaces the key of nodes which has |old_key| - * with |new_key|. |new_key| must be strictly greater than the - * previous node and strictly smaller than the next node. - */ -void nghttp2_ksl_update_key(nghttp2_ksl *ksl, const nghttp2_ksl_key *old_key, - const nghttp2_ksl_key *new_key); - -/* - * nghttp2_ksl_begin returns the iterator which points to the first - * node. If there is no node in |ksl|, it returns the iterator which - * satisfies nghttp2_ksl_it_end(it) != 0. - */ -nghttp2_ksl_it nghttp2_ksl_begin(const nghttp2_ksl *ksl); - -/* - * nghttp2_ksl_end returns the iterator which points to the node - * following the last node. The returned object satisfies - * nghttp2_ksl_it_end(). If there is no node in |ksl|, it returns the - * iterator which satisfies nghttp2_ksl_it_begin(it) != 0. - */ -nghttp2_ksl_it nghttp2_ksl_end(const nghttp2_ksl *ksl); - -/* - * nghttp2_ksl_len returns the number of elements stored in |ksl|. - */ -size_t nghttp2_ksl_len(nghttp2_ksl *ksl); - -/* - * nghttp2_ksl_clear removes all elements stored in |ksl|. - */ -void nghttp2_ksl_clear(nghttp2_ksl *ksl); - -/* - * nghttp2_ksl_nth_node returns the |n|th node under |blk|. - */ -#define nghttp2_ksl_nth_node(KSL, BLK, N) \ - ((nghttp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) - -/* - * nghttp2_ksl_print prints its internal state in stderr. It assumes - * that the key is of type int64_t. This function should be used for - * the debugging purpose only. - */ -void nghttp2_ksl_print(nghttp2_ksl *ksl); - -/* - * nghttp2_ksl_it_init initializes |it|. - */ -void nghttp2_ksl_it_init(nghttp2_ksl_it *it, const nghttp2_ksl *ksl, - nghttp2_ksl_blk *blk, size_t i); - -/* - * nghttp2_ksl_it_get returns the data associated to the node which - * |it| points to. It is undefined to call this function when - * nghttp2_ksl_it_end(it) returns nonzero. - */ -void *nghttp2_ksl_it_get(const nghttp2_ksl_it *it); - -/* - * nghttp2_ksl_it_next advances the iterator by one. It is undefined - * if this function is called when nghttp2_ksl_it_end(it) returns - * nonzero. - */ -#define nghttp2_ksl_it_next(IT) \ - (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ - ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ - : 0) - -/* - * nghttp2_ksl_it_prev moves backward the iterator by one. It is - * undefined if this function is called when nghttp2_ksl_it_begin(it) - * returns nonzero. - */ -void nghttp2_ksl_it_prev(nghttp2_ksl_it *it); - -/* - * nghttp2_ksl_it_end returns nonzero if |it| points to the beyond the - * last node. - */ -#define nghttp2_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) - -/* - * nghttp2_ksl_it_begin returns nonzero if |it| points to the first - * node. |it| might satisfy both nghttp2_ksl_it_begin(&it) and - * nghttp2_ksl_it_end(&it) if the skip list has no node. - */ -int nghttp2_ksl_it_begin(const nghttp2_ksl_it *it); - -/* - * nghttp2_ksl_key returns the key of the node which |it| points to. - * It is undefined to call this function when nghttp2_ksl_it_end(it) - * returns nonzero. - */ -#define nghttp2_ksl_it_key(IT) \ - ((nghttp2_ksl_key *)nghttp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) - -#endif /* NGHTTP2_KSL_H */ diff --git a/lib/nghttp2_map.c b/lib/nghttp2_map.c index e4e2b099..5aab90b4 100644 --- a/lib/nghttp2_map.c +++ b/lib/nghttp2_map.c @@ -27,14 +27,16 @@ #include #include +#include #include "nghttp2_helper.h" -#define INITIAL_TABLE_LENGTH 256 +#define NGHTTP2_INITIAL_TABLE_LENBITS 8 int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; + map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS; + map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS; map->table = nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket)); if (map->table == NULL) { @@ -47,150 +49,145 @@ int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { } void nghttp2_map_free(nghttp2_map *map) { - size_t i; - nghttp2_map_bucket *bkt; - if (!map) { return; } - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp2_ksl_free(bkt->ksl); - nghttp2_mem_free(map->mem, bkt->ksl); - } - } - nghttp2_mem_free(map->mem, map->table); } -void nghttp2_map_each_free(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), +void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { uint32_t i; nghttp2_map_bucket *bkt; - nghttp2_ksl_it it; for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it); - nghttp2_ksl_it_next(&it)) { - func(nghttp2_ksl_it_get(&it), ptr); - } - - nghttp2_ksl_free(bkt->ksl); - nghttp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } + func(bkt->data, ptr); } } -int nghttp2_map_each(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), +int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { int rv; uint32_t i; nghttp2_map_bucket *bkt; - nghttp2_ksl_it it; for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it); - nghttp2_ksl_it_next(&it)) { - rv = func(nghttp2_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } - } - } - return 0; -} - -void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { - entry->key = key; -} - -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; - - p = (uint8_t *)&key; - end = p + sizeof(key_type); - - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } - - return h & (mod - 1); -} - -static int less(const nghttp2_ksl_key *lhs, const nghttp2_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; -} - -static int map_insert(nghttp2_map *map, nghttp2_map_bucket *table, - uint32_t tablelen, nghttp2_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - nghttp2_map_bucket *bkt = &table[h]; - nghttp2_mem *mem = map->mem; - int rv; - - if (bkt->ptr == NULL && - (bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } - - if (!bkt->ksl) { - bkt->ksl = nghttp2_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGHTTP2_ERR_NOMEM; - } - nghttp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem); - } - - if (bkt->ptr) { - rv = nghttp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); + rv = func(bkt->data, ptr); if (rv != 0) { return rv; } - - bkt->ptr = NULL; } - return nghttp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); + return 0; } -/* new_tablelen must be power of 2 */ -static int map_resize(nghttp2_map *map, uint32_t new_tablelen) { +static uint32_t hash(nghttp2_map_key_type key) { + return (uint32_t)key * 2654435769u; +} + +static size_t h2idx(uint32_t hash, uint32_t bits) { + return hash >> (32 - bits); +} + +static size_t distance(uint32_t tablelen, uint32_t tablelenbits, + nghttp2_map_bucket *bkt, size_t idx) { + return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); +} + +static void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash, + nghttp2_map_key_type *pkey, void **pdata) { + uint32_t h = bkt->hash; + nghttp2_map_key_type key = bkt->key; + void *data = bkt->data; + + bkt->hash = *phash; + bkt->key = *pkey; + bkt->data = *pdata; + + *phash = h; + *pkey = key; + *pdata = data; +} + +static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash, + nghttp2_map_key_type key, void *data) { + bkt->hash = hash; + bkt->key = key; + bkt->data = data; +} + +void nghttp2_map_print_distance(nghttp2_map *map) { + uint32_t i; + size_t idx; + nghttp2_map_bucket *bkt; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->data == NULL) { + fprintf(stderr, "@%u \n", i); + continue; + } + + idx = h2idx(bkt->hash, map->tablelenbits); + fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i, + bkt->hash, bkt->key, idx, + distance(map->tablelen, map->tablelenbits, bkt, idx)); + } +} + +static int insert(nghttp2_map_bucket *table, uint32_t tablelen, + uint32_t tablelenbits, uint32_t hash, + nghttp2_map_key_type key, void *data) { + size_t idx = h2idx(hash, tablelenbits); + size_t d = 0, dd; + nghttp2_map_bucket *bkt; + + for (;;) { + bkt = &table[idx]; + + if (bkt->data == NULL) { + map_bucket_set_data(bkt, hash, key, data); + return 0; + } + + dd = distance(tablelen, tablelenbits, bkt, idx); + if (d > dd) { + map_bucket_swap(bkt, &hash, &key, &data); + d = dd; + } else if (bkt->key == key) { + /* TODO This check is just a waste after first swap or if this + function is called from map_resize. That said, there is no + difference with or without this conditional in performance + wise. */ + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + ++d; + idx = (idx + 1) & (tablelen - 1); + } +} + +/* new_tablelen must be power of 2 and new_tablelen == (1 << + new_tablelenbits) must hold. */ +static int map_resize(nghttp2_map *map, uint32_t new_tablelen, + uint32_t new_tablelenbits) { uint32_t i; nghttp2_map_bucket *new_table; nghttp2_map_bucket *bkt; - nghttp2_ksl_it it; int rv; new_table = @@ -201,64 +198,38 @@ static int map_resize(nghttp2_map *map, uint32_t new_tablelen) { for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } + rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, + bkt->data); - if (bkt->ksl) { - for (it = nghttp2_ksl_begin(bkt->ksl); !nghttp2_ksl_it_end(&it); - nghttp2_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, nghttp2_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp2_ksl_free(bkt->ksl); - nghttp2_mem_free(map->mem, bkt->ksl); - } + assert(0 == rv); } nghttp2_mem_free(map->mem, map->table); map->tablelen = new_tablelen; + map->tablelenbits = new_tablelenbits; map->table = new_table; return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - nghttp2_ksl_free(bkt->ksl); - nghttp2_mem_free(map->mem, bkt->ksl); - } - } - - return rv; } -int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { int rv; + assert(data); + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); + rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); if (rv != 0) { return rv; } } - rv = map_insert(map, map->table, map->tablelen, new_entry); + + rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, + data); if (rv != 0) { return rv; } @@ -266,68 +237,75 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { return 0; } -nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { - nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - nghttp2_ksl_it it; +void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { + uint32_t h = hash(key); + size_t idx = h2idx(h, map->tablelenbits); + nghttp2_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } - return NULL; - } + for (;;) { + bkt = &map->table[idx]; - if (bkt->ksl) { - it = nghttp2_ksl_lower_bound(bkt->ksl, &key); - if (nghttp2_ksl_it_end(&it) || - *(key_type *)nghttp2_ksl_it_key(&it) != key) { + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NULL; } - return nghttp2_ksl_it_get(&it); - } - return NULL; + if (bkt->key == key) { + return bkt->data; + } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } } -int nghttp2_map_remove(nghttp2_map *map, key_type key) { - nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; +int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { + uint32_t h = hash(key); + size_t idx = h2idx(h, map->tablelenbits), didx; + nghttp2_map_bucket *bkt; + size_t d = 0; + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (bkt->key == key) { + map_bucket_set_data(bkt, 0, 0, NULL); + + didx = idx; + idx = (idx + 1) & (map->tablelen - 1); + + for (;;) { + bkt = &map->table[idx]; + if (bkt->data == NULL || + distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) { + break; + } + + map->table[didx] = *bkt; + map_bucket_set_data(bkt, 0, 0, NULL); + didx = idx; + + idx = (idx + 1) & (map->tablelen - 1); + } - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; --map->size; + return 0; } - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - if (bkt->ksl) { - rv = nghttp2_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; - } - --map->size; - return 0; + ++d; + idx = (idx + 1) & (map->tablelen - 1); } - - return NGHTTP2_ERR_INVALID_ARGUMENT; } void nghttp2_map_clear(nghttp2_map *map) { - uint32_t i; - nghttp2_map_bucket *bkt; - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - nghttp2_ksl_free(bkt->ksl); - nghttp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } - } - + memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } diff --git a/lib/nghttp2_map.h b/lib/nghttp2_map.h index 90bee1ec..1419a09a 100644 --- a/lib/nghttp2_map.h +++ b/lib/nghttp2_map.h @@ -33,30 +33,23 @@ #include #include "nghttp2_mem.h" -#include "nghttp2_ksl.h" /* Implementation of unordered map */ -typedef int32_t key_type; - -typedef struct nghttp2_map_entry { - union { - key_type key; - /* we requires 8 bytes aligment */ - int64_t pad; - }; -} nghttp2_map_entry; +typedef int32_t nghttp2_map_key_type; typedef struct nghttp2_map_bucket { - nghttp2_map_entry *ptr; - nghttp2_ksl *ksl; + uint32_t hash; + nghttp2_map_key_type key; + void *data; } nghttp2_map_bucket; -typedef struct { +typedef struct nghttp2_map { nghttp2_map_bucket *table; nghttp2_mem *mem; size_t size; uint32_t tablelen; + uint32_t tablelenbits; } nghttp2_map; /* @@ -80,21 +73,14 @@ void nghttp2_map_free(nghttp2_map *map); /* * Deallocates each entries using |func| function and any resources * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as + * given the |data| object. The |ptr| will be passed to the |func| as * send argument. The return value of the |func| will be ignored. */ -void nghttp2_map_each_free(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), +void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr), void *ptr); /* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. + * Inserts the new |data| with the |key| to the map |map|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -104,25 +90,25 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key); * NGHTTP2_ERR_NOMEM * Out of memory */ -int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry); +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data); /* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. + * Returns the data associated by the key |key|. If there is no such + * data, this function returns NULL. */ -nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); +void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key); /* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. + * Removes the data associated by the key |key| from the |map|. The + * removed data is not freed by this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. + * The data associated by |key| does not exist. */ -int nghttp2_map_remove(nghttp2_map *map, key_type key); +int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key); /* * Removes all entries from |map|. @@ -135,21 +121,22 @@ void nghttp2_map_clear(nghttp2_map *map); size_t nghttp2_map_size(nghttp2_map *map); /* - * Applies the function |func| to each entry in the |map| with the + * Applies the function |func| to each data in the |map| with the * optional user supplied pointer |ptr|. * * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the + * next data. If the |func| returns nonzero, it will not call the * |func| for further entries and return the return value of the * |func| immediately. Thus, this function returns 0 if all the * invocations of the |func| return 0, or nonzero value which the last * invocation of |func| returns. * - * Don't use this function to free each entry. Use + * Don't use this function to free each data. Use * nghttp2_map_each_free() instead. */ -int nghttp2_map_each(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), +int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr), void *ptr); +void nghttp2_map_print_distance(nghttp2_map *map); + #endif /* NGHTTP2_MAP_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 2e7e907f..1a2b00d3 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -666,7 +666,7 @@ int nghttp2_session_server_new3(nghttp2_session **session_ptr, return 0; } -static int free_streams(nghttp2_map_entry *entry, void *ptr) { +static int free_streams(void *entry, void *ptr) { nghttp2_session *session; nghttp2_stream *stream; nghttp2_outbound_item *item; @@ -1102,7 +1102,7 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, (int32_t)session->local_settings.initial_window_size, stream_user_data, mem); - rv = nghttp2_map_insert(&session->streams, &stream->map_entry); + rv = nghttp2_map_insert(&session->streams, stream_id, stream); if (rv != 0) { nghttp2_stream_free(stream); nghttp2_mem_free(mem, stream); @@ -2424,7 +2424,7 @@ static int session_call_on_frame_send(nghttp2_session *session, return 0; } -static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) { +static int find_stream_on_goaway_func(void *entry, void *ptr) { nghttp2_close_stream_on_goaway_arg *arg; nghttp2_stream *stream; @@ -4194,8 +4194,7 @@ static int session_process_rst_stream_frame(nghttp2_session *session) { return nghttp2_session_on_rst_stream_received(session, frame); } -static int update_remote_initial_window_size_func(nghttp2_map_entry *entry, - void *ptr) { +static int update_remote_initial_window_size_func(void *entry, void *ptr) { int rv; nghttp2_update_window_size_arg *arg; nghttp2_stream *stream; @@ -4248,8 +4247,7 @@ session_update_remote_initial_window_size(nghttp2_session *session, update_remote_initial_window_size_func, &arg); } -static int update_local_initial_window_size_func(nghttp2_map_entry *entry, - void *ptr) { +static int update_local_initial_window_size_func(void *entry, void *ptr) { int rv; nghttp2_update_window_size_arg *arg; nghttp2_stream *stream; diff --git a/lib/nghttp2_stream.c b/lib/nghttp2_stream.c index dc3a6b11..96e1d9fe 100644 --- a/lib/nghttp2_stream.c +++ b/lib/nghttp2_stream.c @@ -62,7 +62,6 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, int32_t weight, int32_t remote_initial_window_size, int32_t local_initial_window_size, void *stream_user_data, nghttp2_mem *mem) { - nghttp2_map_entry_init(&stream->map_entry, (key_type)stream_id); nghttp2_pq_init(&stream->obq, stream_less, mem); stream->stream_id = stream_id; diff --git a/lib/nghttp2_stream.h b/lib/nghttp2_stream.h index a1b807d2..2846c6aa 100644 --- a/lib/nghttp2_stream.h +++ b/lib/nghttp2_stream.h @@ -135,8 +135,6 @@ typedef enum { } nghttp2_http_flag; struct nghttp2_stream { - /* Intrusive Map */ - nghttp2_map_entry map_entry; /* Entry for dep_prev->obq */ nghttp2_pq_entry pq_entry; /* Priority Queue storing direct descendant (nghttp2_stream). Only diff --git a/tests/nghttp2_map_test.c b/tests/nghttp2_map_test.c index c5e9de31..9a87eff5 100644 --- a/tests/nghttp2_map_test.c +++ b/tests/nghttp2_map_test.c @@ -1,7 +1,8 @@ /* * nghttp2 - HTTP/2 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -29,12 +30,13 @@ #include "nghttp2_map.h" typedef struct strentry { - nghttp2_map_entry map_entry; + nghttp2_map_key_type key; const char *str; } strentry; -static void strentry_init(strentry *entry, key_type key, const char *str) { - nghttp2_map_entry_init(&entry->map_entry, key); +static void strentry_init(strentry *entry, nghttp2_map_key_type key, + const char *str) { + entry->key = key; entry->str = str; } @@ -49,23 +51,23 @@ void test_nghttp2_map(void) { strentry_init(&baz, 3, "baz"); strentry_init(&shrubbery, 4, "shrubbery"); - CU_ASSERT(0 == nghttp2_map_insert(&map, &foo.map_entry)); + CU_ASSERT(0 == nghttp2_map_insert(&map, foo.key, &foo)); CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); CU_ASSERT(1 == nghttp2_map_size(&map)); CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_map_insert(&map, &FOO.map_entry)); + nghttp2_map_insert(&map, FOO.key, &FOO)); CU_ASSERT(1 == nghttp2_map_size(&map)); CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); - CU_ASSERT(0 == nghttp2_map_insert(&map, &bar.map_entry)); + CU_ASSERT(0 == nghttp2_map_insert(&map, bar.key, &bar)); CU_ASSERT(2 == nghttp2_map_size(&map)); - CU_ASSERT(0 == nghttp2_map_insert(&map, &baz.map_entry)); + CU_ASSERT(0 == nghttp2_map_insert(&map, baz.key, &baz)); CU_ASSERT(3 == nghttp2_map_size(&map)); - CU_ASSERT(0 == nghttp2_map_insert(&map, &shrubbery.map_entry)); + CU_ASSERT(0 == nghttp2_map_insert(&map, shrubbery.key, &shrubbery)); CU_ASSERT(4 == nghttp2_map_size(&map)); CU_ASSERT(strcmp("baz", ((strentry *)nghttp2_map_find(&map, 3))->str) == 0); @@ -100,8 +102,8 @@ static void shuffle(int *a, int n) { } } -static int eachfun(nghttp2_map_entry *entry, void *ptr) { - (void)entry; +static int eachfun(void *data, void *ptr) { + (void)data; (void)ptr; return 0; @@ -114,51 +116,56 @@ static int order[NUM_ENT]; void test_nghttp2_map_functional(void) { nghttp2_map map; int i; + strentry *ent; nghttp2_map_init(&map, nghttp2_mem_default()); for (i = 0; i < NUM_ENT; ++i) { - strentry_init(&arr[i], i + 1, "foo"); + strentry_init(&arr[i], (nghttp2_map_key_type)(i + 1), "foo"); order[i] = i + 1; } /* insertion */ shuffle(order, NUM_ENT); for (i = 0; i < NUM_ENT; ++i) { - CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[order[i] - 1].map_entry)); + ent = &arr[order[i] - 1]; + CU_ASSERT(0 == nghttp2_map_insert(&map, ent->key, ent)); } + + CU_ASSERT(NUM_ENT == nghttp2_map_size(&map)); + /* traverse */ nghttp2_map_each(&map, eachfun, NULL); /* find */ shuffle(order, NUM_ENT); for (i = 0; i < NUM_ENT; ++i) { - nghttp2_map_find(&map, order[i]); + CU_ASSERT(NULL != nghttp2_map_find(&map, (nghttp2_map_key_type)order[i])); } /* remove */ - shuffle(order, NUM_ENT); for (i = 0; i < NUM_ENT; ++i) { - CU_ASSERT(0 == nghttp2_map_remove(&map, order[i])); + CU_ASSERT(0 == nghttp2_map_remove(&map, (nghttp2_map_key_type)order[i])); } /* each_free (but no op function for testing purpose) */ for (i = 0; i < NUM_ENT; ++i) { - strentry_init(&arr[i], i + 1, "foo"); + strentry_init(&arr[i], (nghttp2_map_key_type)(i + 1), "foo"); } /* insert once again */ for (i = 0; i < NUM_ENT; ++i) { - CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry)); + ent = &arr[i]; + CU_ASSERT(0 == nghttp2_map_insert(&map, ent->key, ent)); } nghttp2_map_each_free(&map, eachfun, NULL); nghttp2_map_free(&map); } -static int entry_free(nghttp2_map_entry *entry, void *ptr) { - nghttp2_mem *mem = ptr; +static int entry_free(void *data, void *ptr) { + const nghttp2_mem *mem = ptr; - mem->free(entry, NULL); + mem->free(data, NULL); return 0; } void test_nghttp2_map_each_free(void) { - nghttp2_mem *mem = nghttp2_mem_default(); + const nghttp2_mem *mem = nghttp2_mem_default(); strentry *foo = mem->malloc(sizeof(strentry), NULL), *bar = mem->malloc(sizeof(strentry), NULL), *baz = mem->malloc(sizeof(strentry), NULL), @@ -171,11 +178,29 @@ void test_nghttp2_map_each_free(void) { strentry_init(baz, 3, "baz"); strentry_init(shrubbery, 4, "shrubbery"); - nghttp2_map_insert(&map, &foo->map_entry); - nghttp2_map_insert(&map, &bar->map_entry); - nghttp2_map_insert(&map, &baz->map_entry); - nghttp2_map_insert(&map, &shrubbery->map_entry); + nghttp2_map_insert(&map, foo->key, foo); + nghttp2_map_insert(&map, bar->key, bar); + nghttp2_map_insert(&map, baz->key, baz); + nghttp2_map_insert(&map, shrubbery->key, shrubbery); + + nghttp2_map_each_free(&map, entry_free, (void *)mem); + nghttp2_map_free(&map); +} + +void test_nghttp2_map_clear(void) { + nghttp2_mem *mem = nghttp2_mem_default(); + nghttp2_map map; + strentry foo; + + strentry_init(&foo, 1, "foo"); + + nghttp2_map_init(&map, mem); + + CU_ASSERT(0 == nghttp2_map_insert(&map, foo.key, &foo)); + + nghttp2_map_clear(&map); + + CU_ASSERT(0 == nghttp2_map_size(&map)); - nghttp2_map_each_free(&map, entry_free, mem); nghttp2_map_free(&map); } diff --git a/tests/nghttp2_map_test.h b/tests/nghttp2_map_test.h index 62ae54b3..235624de 100644 --- a/tests/nghttp2_map_test.h +++ b/tests/nghttp2_map_test.h @@ -1,7 +1,8 @@ /* * nghttp2 - HTTP/2 C Library * - * Copyright (c) 2012 Tatsuhiro Tsujikawa + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,5 +33,6 @@ void test_nghttp2_map(void); void test_nghttp2_map_functional(void); void test_nghttp2_map_each_free(void); +void test_nghttp2_map_clear(void); #endif /* NGHTTP2_MAP_TEST_H */