Port new ngtcp2 map implementation
This commit is contained in:
parent
d4fd0681ef
commit
ebad3d4755
|
@ -50,8 +50,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
|
||||||
nghttp2_mem.c \
|
nghttp2_mem.c \
|
||||||
nghttp2_http.c \
|
nghttp2_http.c \
|
||||||
nghttp2_rcbuf.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 \
|
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
|
||||||
nghttp2_frame.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_mem.h \
|
||||||
nghttp2_http.h \
|
nghttp2_http.h \
|
||||||
nghttp2_rcbuf.h \
|
nghttp2_rcbuf.h \
|
||||||
nghttp2_debug.h \
|
nghttp2_debug.h
|
||||||
nghttp2_ksl.h
|
|
||||||
|
|
||||||
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
|
||||||
libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \
|
libnghttp2_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined \
|
||||||
|
|
|
@ -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 <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -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 <config.h>
|
|
||||||
#endif /* HAVE_CONFIG_H */
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 */
|
|
|
@ -27,14 +27,16 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "nghttp2_helper.h"
|
#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) {
|
int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) {
|
||||||
map->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 =
|
map->table =
|
||||||
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
|
nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket));
|
||||||
if (map->table == NULL) {
|
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) {
|
void nghttp2_map_free(nghttp2_map *map) {
|
||||||
size_t i;
|
|
||||||
nghttp2_map_bucket *bkt;
|
|
||||||
|
|
||||||
if (!map) {
|
if (!map) {
|
||||||
return;
|
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);
|
nghttp2_mem_free(map->mem, map->table);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_map_each_free(nghttp2_map *map,
|
void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
|
||||||
void *ptr) {
|
void *ptr) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
nghttp2_map_bucket *bkt;
|
nghttp2_map_bucket *bkt;
|
||||||
nghttp2_ksl_it it;
|
|
||||||
|
|
||||||
for (i = 0; i < map->tablelen; ++i) {
|
for (i = 0; i < map->tablelen; ++i) {
|
||||||
bkt = &map->table[i];
|
bkt = &map->table[i];
|
||||||
|
|
||||||
if (bkt->ptr) {
|
if (bkt->data == NULL) {
|
||||||
func(bkt->ptr, ptr);
|
|
||||||
bkt->ptr = NULL;
|
|
||||||
assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bkt->ksl) {
|
func(bkt->data, ptr);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_map_each(nghttp2_map *map,
|
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
|
||||||
void *ptr) {
|
void *ptr) {
|
||||||
int rv;
|
int rv;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
nghttp2_map_bucket *bkt;
|
nghttp2_map_bucket *bkt;
|
||||||
nghttp2_ksl_it it;
|
|
||||||
|
|
||||||
for (i = 0; i < map->tablelen; ++i) {
|
for (i = 0; i < map->tablelen; ++i) {
|
||||||
bkt = &map->table[i];
|
bkt = &map->table[i];
|
||||||
|
|
||||||
if (bkt->ptr) {
|
if (bkt->data == NULL) {
|
||||||
rv = func(bkt->ptr, ptr);
|
|
||||||
if (rv != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
assert(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bkt->ksl) {
|
rv = func(bkt->data, ptr);
|
||||||
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) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) {
|
static uint32_t hash(nghttp2_map_key_type key) {
|
||||||
entry->key = key;
|
return (uint32_t)key * 2654435769u;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FNV1a hash */
|
static size_t h2idx(uint32_t hash, uint32_t bits) {
|
||||||
static uint32_t hash(key_type key, uint32_t mod) {
|
return hash >> (32 - bits);
|
||||||
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 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 int less(const nghttp2_ksl_key *lhs, const nghttp2_ksl_key *rhs) {
|
static void map_bucket_swap(nghttp2_map_bucket *bkt, uint32_t *phash,
|
||||||
return *(key_type *)lhs < *(key_type *)rhs;
|
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 int map_insert(nghttp2_map *map, nghttp2_map_bucket *table,
|
static void map_bucket_set_data(nghttp2_map_bucket *bkt, uint32_t hash,
|
||||||
uint32_t tablelen, nghttp2_map_entry *entry) {
|
nghttp2_map_key_type key, void *data) {
|
||||||
uint32_t h = hash(entry->key, tablelen);
|
bkt->hash = hash;
|
||||||
nghttp2_map_bucket *bkt = &table[h];
|
bkt->key = key;
|
||||||
nghttp2_mem *mem = map->mem;
|
bkt->data = data;
|
||||||
int rv;
|
}
|
||||||
|
|
||||||
if (bkt->ptr == NULL &&
|
void nghttp2_map_print_distance(nghttp2_map *map) {
|
||||||
(bkt->ksl == NULL || nghttp2_ksl_len(bkt->ksl) == 0)) {
|
uint32_t i;
|
||||||
bkt->ptr = entry;
|
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 <EMPTY>\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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bkt->ksl) {
|
dd = distance(tablelen, tablelenbits, bkt, idx);
|
||||||
bkt->ksl = nghttp2_mem_malloc(mem, sizeof(*bkt->ksl));
|
if (d > dd) {
|
||||||
if (bkt->ksl == NULL) {
|
map_bucket_swap(bkt, &hash, &key, &data);
|
||||||
return NGHTTP2_ERR_NOMEM;
|
d = dd;
|
||||||
}
|
} else if (bkt->key == key) {
|
||||||
nghttp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bkt->ptr) {
|
++d;
|
||||||
rv = nghttp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
|
idx = (idx + 1) & (tablelen - 1);
|
||||||
if (rv != 0) {
|
}
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bkt->ptr = NULL;
|
/* 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,
|
||||||
return nghttp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
|
uint32_t new_tablelenbits) {
|
||||||
}
|
|
||||||
|
|
||||||
/* new_tablelen must be power of 2 */
|
|
||||||
static int map_resize(nghttp2_map *map, uint32_t new_tablelen) {
|
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
nghttp2_map_bucket *new_table;
|
nghttp2_map_bucket *new_table;
|
||||||
nghttp2_map_bucket *bkt;
|
nghttp2_map_bucket *bkt;
|
||||||
nghttp2_ksl_it it;
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
new_table =
|
new_table =
|
||||||
|
@ -201,64 +198,38 @@ static int map_resize(nghttp2_map *map, uint32_t new_tablelen) {
|
||||||
|
|
||||||
for (i = 0; i < map->tablelen; ++i) {
|
for (i = 0; i < map->tablelen; ++i) {
|
||||||
bkt = &map->table[i];
|
bkt = &map->table[i];
|
||||||
|
if (bkt->data == NULL) {
|
||||||
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
|
||||||
|
bkt->data);
|
||||||
|
|
||||||
if (bkt->ksl) {
|
assert(0 == rv);
|
||||||
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);
|
nghttp2_mem_free(map->mem, map->table);
|
||||||
map->tablelen = new_tablelen;
|
map->tablelen = new_tablelen;
|
||||||
|
map->tablelenbits = new_tablelenbits;
|
||||||
map->table = new_table;
|
map->table = new_table;
|
||||||
|
|
||||||
return 0;
|
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_key_type key, void *data) {
|
||||||
}
|
|
||||||
|
|
||||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
|
||||||
/* Load factor is 0.75 */
|
/* Load factor is 0.75 */
|
||||||
if ((map->size + 1) * 4 > map->tablelen * 3) {
|
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) {
|
if (rv != 0) {
|
||||||
return rv;
|
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) {
|
if (rv != 0) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -266,68 +237,75 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) {
|
void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) {
|
||||||
nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
uint32_t h = hash(key);
|
||||||
nghttp2_ksl_it it;
|
size_t idx = h2idx(h, map->tablelenbits);
|
||||||
|
nghttp2_map_bucket *bkt;
|
||||||
|
size_t d = 0;
|
||||||
|
|
||||||
if (bkt->ptr) {
|
for (;;) {
|
||||||
if (bkt->ptr->key == key) {
|
bkt = &map->table[idx];
|
||||||
return bkt->ptr;
|
|
||||||
}
|
if (bkt->data == NULL ||
|
||||||
|
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bkt->ksl) {
|
if (bkt->key == key) {
|
||||||
it = nghttp2_ksl_lower_bound(bkt->ksl, &key);
|
return bkt->data;
|
||||||
if (nghttp2_ksl_it_end(&it) ||
|
|
||||||
*(key_type *)nghttp2_ksl_it_key(&it) != key) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return nghttp2_ksl_it_get(&it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
++d;
|
||||||
|
idx = (idx + 1) & (map->tablelen - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nghttp2_map_remove(nghttp2_map *map, key_type key) {
|
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
|
||||||
nghttp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)];
|
uint32_t h = hash(key);
|
||||||
int rv;
|
size_t idx = h2idx(h, map->tablelenbits), didx;
|
||||||
|
nghttp2_map_bucket *bkt;
|
||||||
|
size_t d = 0;
|
||||||
|
|
||||||
if (bkt->ptr) {
|
for (;;) {
|
||||||
if (bkt->ptr->key == key) {
|
bkt = &map->table[idx];
|
||||||
bkt->ptr = NULL;
|
|
||||||
--map->size;
|
if (bkt->data == NULL ||
|
||||||
return 0;
|
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
|
||||||
}
|
|
||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bkt->ksl) {
|
if (bkt->key == key) {
|
||||||
rv = nghttp2_ksl_remove(bkt->ksl, NULL, &key);
|
map_bucket_set_data(bkt, 0, 0, NULL);
|
||||||
if (rv != 0) {
|
|
||||||
return rv;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
--map->size;
|
--map->size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
++d;
|
||||||
|
idx = (idx + 1) & (map->tablelen - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_map_clear(nghttp2_map *map) {
|
void nghttp2_map_clear(nghttp2_map *map) {
|
||||||
uint32_t i;
|
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
|
||||||
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;
|
map->size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,30 +33,23 @@
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
#include "nghttp2_mem.h"
|
#include "nghttp2_mem.h"
|
||||||
#include "nghttp2_ksl.h"
|
|
||||||
|
|
||||||
/* Implementation of unordered map */
|
/* Implementation of unordered map */
|
||||||
|
|
||||||
typedef int32_t key_type;
|
typedef int32_t nghttp2_map_key_type;
|
||||||
|
|
||||||
typedef struct nghttp2_map_entry {
|
|
||||||
union {
|
|
||||||
key_type key;
|
|
||||||
/* we requires 8 bytes aligment */
|
|
||||||
int64_t pad;
|
|
||||||
};
|
|
||||||
} nghttp2_map_entry;
|
|
||||||
|
|
||||||
typedef struct nghttp2_map_bucket {
|
typedef struct nghttp2_map_bucket {
|
||||||
nghttp2_map_entry *ptr;
|
uint32_t hash;
|
||||||
nghttp2_ksl *ksl;
|
nghttp2_map_key_type key;
|
||||||
|
void *data;
|
||||||
} nghttp2_map_bucket;
|
} nghttp2_map_bucket;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct nghttp2_map {
|
||||||
nghttp2_map_bucket *table;
|
nghttp2_map_bucket *table;
|
||||||
nghttp2_mem *mem;
|
nghttp2_mem *mem;
|
||||||
size_t size;
|
size_t size;
|
||||||
uint32_t tablelen;
|
uint32_t tablelen;
|
||||||
|
uint32_t tablelenbits;
|
||||||
} nghttp2_map;
|
} nghttp2_map;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -80,21 +73,14 @@ void nghttp2_map_free(nghttp2_map *map);
|
||||||
/*
|
/*
|
||||||
* Deallocates each entries using |func| function and any resources
|
* Deallocates each entries using |func| function and any resources
|
||||||
* allocated for |map|. The |func| function is responsible for freeing
|
* 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.
|
* send argument. The return value of the |func| will be ignored.
|
||||||
*/
|
*/
|
||||||
void nghttp2_map_each_free(nghttp2_map *map,
|
void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
|
||||||
void *ptr);
|
void *ptr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes the |entry| with the |key|. All entries to be inserted
|
* Inserts the new |data| with the |key| to the map |map|.
|
||||||
* 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|.
|
|
||||||
*
|
*
|
||||||
* This function returns 0 if it succeeds, or one of the following
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
* negative error codes:
|
* negative error codes:
|
||||||
|
@ -104,25 +90,25 @@ void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key);
|
||||||
* NGHTTP2_ERR_NOMEM
|
* NGHTTP2_ERR_NOMEM
|
||||||
* Out of memory
|
* 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
|
* Returns the data associated by the key |key|. If there is no such
|
||||||
* entry, this function returns NULL.
|
* 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
|
* Removes the data associated by the key |key| from the |map|. The
|
||||||
* removed entry is not freed by this function.
|
* removed data is not freed by this function.
|
||||||
*
|
*
|
||||||
* This function returns 0 if it succeeds, or one of the following
|
* This function returns 0 if it succeeds, or one of the following
|
||||||
* negative error codes:
|
* negative error codes:
|
||||||
*
|
*
|
||||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
* 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|.
|
* Removes all entries from |map|.
|
||||||
|
@ -135,21 +121,22 @@ void nghttp2_map_clear(nghttp2_map *map);
|
||||||
size_t nghttp2_map_size(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|.
|
* optional user supplied pointer |ptr|.
|
||||||
*
|
*
|
||||||
* If the |func| returns 0, this function calls the |func| with the
|
* 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| for further entries and return the return value of the
|
||||||
* |func| immediately. Thus, this function returns 0 if all the
|
* |func| immediately. Thus, this function returns 0 if all the
|
||||||
* invocations of the |func| return 0, or nonzero value which the last
|
* invocations of the |func| return 0, or nonzero value which the last
|
||||||
* invocation of |func| returns.
|
* 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.
|
* nghttp2_map_each_free() instead.
|
||||||
*/
|
*/
|
||||||
int nghttp2_map_each(nghttp2_map *map,
|
int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr),
|
||||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
|
||||||
void *ptr);
|
void *ptr);
|
||||||
|
|
||||||
|
void nghttp2_map_print_distance(nghttp2_map *map);
|
||||||
|
|
||||||
#endif /* NGHTTP2_MAP_H */
|
#endif /* NGHTTP2_MAP_H */
|
||||||
|
|
|
@ -666,7 +666,7 @@ int nghttp2_session_server_new3(nghttp2_session **session_ptr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int free_streams(nghttp2_map_entry *entry, void *ptr) {
|
static int free_streams(void *entry, void *ptr) {
|
||||||
nghttp2_session *session;
|
nghttp2_session *session;
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
nghttp2_outbound_item *item;
|
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,
|
(int32_t)session->local_settings.initial_window_size,
|
||||||
stream_user_data, mem);
|
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) {
|
if (rv != 0) {
|
||||||
nghttp2_stream_free(stream);
|
nghttp2_stream_free(stream);
|
||||||
nghttp2_mem_free(mem, stream);
|
nghttp2_mem_free(mem, stream);
|
||||||
|
@ -2424,7 +2424,7 @@ static int session_call_on_frame_send(nghttp2_session *session,
|
||||||
return 0;
|
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_close_stream_on_goaway_arg *arg;
|
||||||
nghttp2_stream *stream;
|
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);
|
return nghttp2_session_on_rst_stream_received(session, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int update_remote_initial_window_size_func(nghttp2_map_entry *entry,
|
static int update_remote_initial_window_size_func(void *entry, void *ptr) {
|
||||||
void *ptr) {
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_update_window_size_arg *arg;
|
nghttp2_update_window_size_arg *arg;
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
|
@ -4248,8 +4247,7 @@ session_update_remote_initial_window_size(nghttp2_session *session,
|
||||||
update_remote_initial_window_size_func, &arg);
|
update_remote_initial_window_size_func, &arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int update_local_initial_window_size_func(nghttp2_map_entry *entry,
|
static int update_local_initial_window_size_func(void *entry, void *ptr) {
|
||||||
void *ptr) {
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_update_window_size_arg *arg;
|
nghttp2_update_window_size_arg *arg;
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
|
|
|
@ -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 weight, int32_t remote_initial_window_size,
|
||||||
int32_t local_initial_window_size,
|
int32_t local_initial_window_size,
|
||||||
void *stream_user_data, nghttp2_mem *mem) {
|
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);
|
nghttp2_pq_init(&stream->obq, stream_less, mem);
|
||||||
|
|
||||||
stream->stream_id = stream_id;
|
stream->stream_id = stream_id;
|
||||||
|
|
|
@ -135,8 +135,6 @@ typedef enum {
|
||||||
} nghttp2_http_flag;
|
} nghttp2_http_flag;
|
||||||
|
|
||||||
struct nghttp2_stream {
|
struct nghttp2_stream {
|
||||||
/* Intrusive Map */
|
|
||||||
nghttp2_map_entry map_entry;
|
|
||||||
/* Entry for dep_prev->obq */
|
/* Entry for dep_prev->obq */
|
||||||
nghttp2_pq_entry pq_entry;
|
nghttp2_pq_entry pq_entry;
|
||||||
/* Priority Queue storing direct descendant (nghttp2_stream). Only
|
/* Priority Queue storing direct descendant (nghttp2_stream). Only
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* nghttp2 - HTTP/2 C Library
|
* 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
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
@ -29,12 +30,13 @@
|
||||||
#include "nghttp2_map.h"
|
#include "nghttp2_map.h"
|
||||||
|
|
||||||
typedef struct strentry {
|
typedef struct strentry {
|
||||||
nghttp2_map_entry map_entry;
|
nghttp2_map_key_type key;
|
||||||
const char *str;
|
const char *str;
|
||||||
} strentry;
|
} strentry;
|
||||||
|
|
||||||
static void strentry_init(strentry *entry, key_type key, const char *str) {
|
static void strentry_init(strentry *entry, nghttp2_map_key_type key,
|
||||||
nghttp2_map_entry_init(&entry->map_entry, key);
|
const char *str) {
|
||||||
|
entry->key = key;
|
||||||
entry->str = str;
|
entry->str = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,23 +51,23 @@ void test_nghttp2_map(void) {
|
||||||
strentry_init(&baz, 3, "baz");
|
strentry_init(&baz, 3, "baz");
|
||||||
strentry_init(&shrubbery, 4, "shrubbery");
|
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(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0);
|
||||||
CU_ASSERT(1 == nghttp2_map_size(&map));
|
CU_ASSERT(1 == nghttp2_map_size(&map));
|
||||||
|
|
||||||
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
|
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(1 == nghttp2_map_size(&map));
|
||||||
CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0);
|
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(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(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(4 == nghttp2_map_size(&map));
|
||||||
|
|
||||||
CU_ASSERT(strcmp("baz", ((strentry *)nghttp2_map_find(&map, 3))->str) == 0);
|
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) {
|
static int eachfun(void *data, void *ptr) {
|
||||||
(void)entry;
|
(void)data;
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -114,51 +116,56 @@ static int order[NUM_ENT];
|
||||||
void test_nghttp2_map_functional(void) {
|
void test_nghttp2_map_functional(void) {
|
||||||
nghttp2_map map;
|
nghttp2_map map;
|
||||||
int i;
|
int i;
|
||||||
|
strentry *ent;
|
||||||
|
|
||||||
nghttp2_map_init(&map, nghttp2_mem_default());
|
nghttp2_map_init(&map, nghttp2_mem_default());
|
||||||
for (i = 0; i < NUM_ENT; ++i) {
|
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;
|
order[i] = i + 1;
|
||||||
}
|
}
|
||||||
/* insertion */
|
/* insertion */
|
||||||
shuffle(order, NUM_ENT);
|
shuffle(order, NUM_ENT);
|
||||||
for (i = 0; i < NUM_ENT; ++i) {
|
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 */
|
/* traverse */
|
||||||
nghttp2_map_each(&map, eachfun, NULL);
|
nghttp2_map_each(&map, eachfun, NULL);
|
||||||
/* find */
|
/* find */
|
||||||
shuffle(order, NUM_ENT);
|
shuffle(order, NUM_ENT);
|
||||||
for (i = 0; i < NUM_ENT; ++i) {
|
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 */
|
/* remove */
|
||||||
shuffle(order, NUM_ENT);
|
|
||||||
for (i = 0; i < NUM_ENT; ++i) {
|
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) */
|
/* each_free (but no op function for testing purpose) */
|
||||||
for (i = 0; i < NUM_ENT; ++i) {
|
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 */
|
/* insert once again */
|
||||||
for (i = 0; i < NUM_ENT; ++i) {
|
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_each_free(&map, eachfun, NULL);
|
||||||
nghttp2_map_free(&map);
|
nghttp2_map_free(&map);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int entry_free(nghttp2_map_entry *entry, void *ptr) {
|
static int entry_free(void *data, void *ptr) {
|
||||||
nghttp2_mem *mem = ptr;
|
const nghttp2_mem *mem = ptr;
|
||||||
|
|
||||||
mem->free(entry, NULL);
|
mem->free(data, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_nghttp2_map_each_free(void) {
|
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),
|
strentry *foo = mem->malloc(sizeof(strentry), NULL),
|
||||||
*bar = mem->malloc(sizeof(strentry), NULL),
|
*bar = mem->malloc(sizeof(strentry), NULL),
|
||||||
*baz = 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(baz, 3, "baz");
|
||||||
strentry_init(shrubbery, 4, "shrubbery");
|
strentry_init(shrubbery, 4, "shrubbery");
|
||||||
|
|
||||||
nghttp2_map_insert(&map, &foo->map_entry);
|
nghttp2_map_insert(&map, foo->key, foo);
|
||||||
nghttp2_map_insert(&map, &bar->map_entry);
|
nghttp2_map_insert(&map, bar->key, bar);
|
||||||
nghttp2_map_insert(&map, &baz->map_entry);
|
nghttp2_map_insert(&map, baz->key, baz);
|
||||||
nghttp2_map_insert(&map, &shrubbery->map_entry);
|
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);
|
nghttp2_map_free(&map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* nghttp2 - HTTP/2 C Library
|
* 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
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
@ -32,5 +33,6 @@
|
||||||
void test_nghttp2_map(void);
|
void test_nghttp2_map(void);
|
||||||
void test_nghttp2_map_functional(void);
|
void test_nghttp2_map_functional(void);
|
||||||
void test_nghttp2_map_each_free(void);
|
void test_nghttp2_map_each_free(void);
|
||||||
|
void test_nghttp2_map_clear(void);
|
||||||
|
|
||||||
#endif /* NGHTTP2_MAP_TEST_H */
|
#endif /* NGHTTP2_MAP_TEST_H */
|
||||||
|
|
Loading…
Reference in New Issue