From 1ce628529b5d59a0bc9ba9f70a15a85a24b74afa Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 26 Jul 2020 14:00:45 +0900 Subject: [PATCH] nghttp2_map backed by nghttp2_ksl --- lib/Makefile.am | 6 +- lib/nghttp2_ksl.c | 707 ++++++++++++++++++++++++++++++++++++++++++++++ lib/nghttp2_ksl.h | 315 +++++++++++++++++++++ lib/nghttp2_map.c | 262 +++++++++++++---- lib/nghttp2_map.h | 18 +- 5 files changed, 1245 insertions(+), 63 deletions(-) create mode 100644 lib/nghttp2_ksl.c create mode 100644 lib/nghttp2_ksl.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 24a5bd62..63fa0fa8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -49,7 +49,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_mem.c \ nghttp2_http.c \ nghttp2_rcbuf.c \ - nghttp2_debug.c + nghttp2_debug.c \ + nghttp2_ksl.c HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_frame.h \ @@ -65,7 +66,8 @@ 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_debug.h \ + nghttp2_ksl.h libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) libnghttp2_la_LDFLAGS = -no-undefined \ diff --git a/lib/nghttp2_ksl.c b/lib/nghttp2_ksl.c new file mode 100644 index 00000000..dffd1cbc --- /dev/null +++ b/lib/nghttp2_ksl.c @@ -0,0 +1,707 @@ +/* + * 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 new file mode 100644 index 00000000..39d900ff --- /dev/null +++ b/lib/nghttp2_ksl.h @@ -0,0 +1,315 @@ +/* + * 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 4d9f97b4..d12a1f47 100644 --- a/lib/nghttp2_map.c +++ b/lib/nghttp2_map.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 @@ -25,6 +26,9 @@ #include "nghttp2_map.h" #include +#include + +#include "nghttp2_helper.h" #define INITIAL_TABLE_LENGTH 256 @@ -32,7 +36,7 @@ int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { map->mem = mem; map->tablelen = INITIAL_TABLE_LENGTH; map->table = - nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *)); + nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket)); if (map->table == NULL) { return NGHTTP2_ERR_NOMEM; } @@ -43,6 +47,21 @@ 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); } @@ -50,14 +69,29 @@ void nghttp2_map_each_free(nghttp2_map *map, int (*func)(nghttp2_map_entry *entry, void *ptr), void *ptr) { uint32_t i; + nghttp2_map_bucket *bkt; + nghttp2_ksl_it it; + for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry;) { - nghttp2_map_entry *next = entry->next; - func(entry, ptr); - entry = next; + bkt = &map->table[i]; + + if (bkt->ptr) { + func(bkt->ptr, ptr); + bkt->ptr = NULL; + assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0); + 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; } - map->table[i] = NULL; } } @@ -66,13 +100,29 @@ int nghttp2_map_each(nghttp2_map *map, void *ptr) { int rv; uint32_t i; + nghttp2_map_bucket *bkt; + nghttp2_ksl_it it; + for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry; entry = entry->next) { - rv = func(entry, ptr); + 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); + 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; @@ -83,72 +133,133 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { entry->next = NULL; } -/* Same hash function in android HashMap source code. */ -/* The |mod| must be power of 2 */ -static uint32_t hash(int32_t key, uint32_t mod) { - uint32_t h = (uint32_t)key; - h ^= (h >> 20) ^ (h >> 12); - h ^= (h >> 7) ^ (h >> 4); +/* 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 insert(nghttp2_map_entry **table, uint32_t tablelen, - nghttp2_map_entry *entry) { +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); - if (table[h] == NULL) { - table[h] = entry; - } else { - nghttp2_map_entry *p; - /* We won't allow duplicated key, so check it out. */ - for (p = table[h]; p; p = p->next) { - if (p->key == entry->key) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - } - entry->next = table[h]; - table[h] = entry; + 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; } - 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); + if (rv != 0) { + return rv; + } + + bkt->ptr = NULL; + } + + return nghttp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); } /* new_tablelen must be power of 2 */ -static int resize(nghttp2_map *map, uint32_t new_tablelen) { +static int map_resize(nghttp2_map *map, uint32_t new_tablelen) { uint32_t i; - nghttp2_map_entry **new_table; + nghttp2_map_bucket *new_table; + nghttp2_map_bucket *bkt; + nghttp2_ksl_it it; + int rv; new_table = - nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *)); + nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket)); if (new_table == NULL) { return NGHTTP2_ERR_NOMEM; } for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry;) { - nghttp2_map_entry *next = entry->next; - entry->next = NULL; - /* This function must succeed */ - insert(new_table, new_tablelen, entry); - entry = next; + 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); + continue; + } + + 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); + } + } + nghttp2_mem_free(map->mem, map->table); map->tablelen = new_tablelen; 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 rv; + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = resize(map, map->tablelen * 2); + rv = map_resize(map, map->tablelen * 2); if (rv != 0) { return rv; } } - rv = insert(map->table, map->tablelen, new_entry); + rv = map_insert(map, map->table, map->tablelen, new_entry); if (rv != 0) { return rv; } @@ -157,33 +268,68 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { } nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { - uint32_t h; - nghttp2_map_entry *entry; - h = hash(key, map->tablelen); - for (entry = map->table[h]; entry; entry = entry->next) { - if (entry->key == key) { - return entry; + nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + nghttp2_ksl_it it; + + if (bkt->ptr) { + if (bkt->ptr->key == key) { + return bkt->ptr; } + return NULL; } + + 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) { + return NULL; + } + return nghttp2_ksl_it_get(&it); + } + return NULL; } int nghttp2_map_remove(nghttp2_map *map, key_type key) { - uint32_t h; - nghttp2_map_entry **dst; + nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + int rv; - h = hash(key, map->tablelen); - - for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { - if ((*dst)->key != key) { - continue; + if (bkt->ptr) { + if (bkt->ptr->key == key) { + bkt->ptr = NULL; + --map->size; + return 0; } + return NGHTTP2_ERR_INVALID_ARGUMENT; + } - *dst = (*dst)->next; + if (bkt->ksl) { + rv = nghttp2_ksl_remove(bkt->ksl, NULL, &key); + if (rv != 0) { + return rv; + } --map->size; return 0; } + 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; + } + } + + map->size = 0; +} + size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/lib/nghttp2_map.h b/lib/nghttp2_map.h index f6e29e35..2cf59f69 100644 --- a/lib/nghttp2_map.h +++ b/lib/nghttp2_map.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 @@ -30,8 +31,9 @@ #endif /* HAVE_CONFIG_H */ #include -#include "nghttp2_int.h" + #include "nghttp2_mem.h" +#include "nghttp2_ksl.h" /* Implementation of unordered map */ @@ -46,8 +48,13 @@ typedef struct nghttp2_map_entry { #endif } nghttp2_map_entry; +typedef struct nghttp2_map_bucket { + nghttp2_map_entry *ptr; + nghttp2_ksl *ksl; +} nghttp2_map_bucket; + typedef struct { - nghttp2_map_entry **table; + nghttp2_map_bucket *table; nghttp2_mem *mem; size_t size; uint32_t tablelen; @@ -118,6 +125,11 @@ nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); */ int nghttp2_map_remove(nghttp2_map *map, key_type key); +/* + * Removes all entries from |map|. + */ +void nghttp2_map_clear(nghttp2_map *map); + /* * Returns the number of items stored in the map |map|. */