nghttp2_map: Implement hash table
This commit is contained in:
parent
1f0dfd4316
commit
a3c888d7d1
|
@ -24,227 +24,135 @@
|
|||
*/
|
||||
#include "nghttp2_map.h"
|
||||
|
||||
void nghttp2_map_init(nghttp2_map *map)
|
||||
#include <string.h>
|
||||
|
||||
#define INITIAL_TABLE_LENGTH 16
|
||||
|
||||
#define LOAD_FACTOR 75
|
||||
|
||||
int nghttp2_map_init(nghttp2_map *map)
|
||||
{
|
||||
map->root = NULL;
|
||||
map->tablelen = INITIAL_TABLE_LENGTH;
|
||||
map->table = malloc(sizeof(nghttp2_map_entry*) * map->tablelen);
|
||||
if(map->table == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
memset(map->table, 0, sizeof(nghttp2_map_entry*) * map->tablelen);
|
||||
map->size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_map_free(nghttp2_map *map)
|
||||
{
|
||||
map->root = NULL;
|
||||
}
|
||||
|
||||
/* Find left most node, which is not necessarily a leaf. */
|
||||
static nghttp2_map_entry* find_left_most(nghttp2_map_entry *entry)
|
||||
{
|
||||
while(entry->left) {
|
||||
entry = entry->left;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Find left most leaf. */
|
||||
static nghttp2_map_entry* find_left_most_leaf(nghttp2_map_entry *entry)
|
||||
{
|
||||
for(;;) {
|
||||
entry = find_left_most(entry);
|
||||
if(entry->right) {
|
||||
entry = entry->right;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Returns next node in postorder traversal. Returns NULL if there is
|
||||
no next node. */
|
||||
static nghttp2_map_entry* traverse_postorder(nghttp2_map_entry *parent,
|
||||
nghttp2_map_entry *entry)
|
||||
{
|
||||
if(!parent) {
|
||||
return NULL;
|
||||
}
|
||||
if(parent->left == entry) {
|
||||
if(parent->right) {
|
||||
return find_left_most_leaf(parent->right);
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
free(map->table);
|
||||
}
|
||||
|
||||
void nghttp2_map_each_free(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void *ptr)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
if(!map->root) {
|
||||
return;
|
||||
}
|
||||
entry = find_left_most_leaf(map->root);
|
||||
while(entry) {
|
||||
nghttp2_map_entry *parent = entry->parent;
|
||||
/* Ignore return value. */
|
||||
for(entry = map->table[i]; entry;) {
|
||||
nghttp2_map_entry *next = entry->next;
|
||||
func(entry, ptr);
|
||||
/* entry has been deleted. */
|
||||
entry = traverse_postorder(parent, entry);
|
||||
entry = next;
|
||||
}
|
||||
map->root = NULL;
|
||||
}
|
||||
|
||||
/* Returns next node in inorder traversal. Returns NULL if there is no
|
||||
next node. */
|
||||
static nghttp2_map_entry* traverse_inorder(nghttp2_map_entry *entry)
|
||||
{
|
||||
nghttp2_map_entry *parent;
|
||||
if(entry->right) {
|
||||
return find_left_most(entry->right);
|
||||
map->table[i] = NULL;
|
||||
}
|
||||
parent = entry->parent;
|
||||
while(parent && parent->right == entry) {
|
||||
entry = entry->parent;
|
||||
parent = parent->parent;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
int nghttp2_map_each(nghttp2_map *map,
|
||||
int (*func)(nghttp2_map_entry *entry, void *ptr),
|
||||
void *ptr)
|
||||
{
|
||||
int rv;
|
||||
size_t i;
|
||||
for(i = 0; i < map->tablelen; ++i) {
|
||||
nghttp2_map_entry *entry;
|
||||
if(!map->root) {
|
||||
return 0;
|
||||
}
|
||||
entry = find_left_most(map->root);
|
||||
while(entry) {
|
||||
int rv = func(entry, ptr);
|
||||
for(entry = map->table[i]; entry; entry = entry->next) {
|
||||
rv = func(entry, ptr);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
entry = traverse_inorder(entry);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 32 bit Mix Functions by Thomas Wang
|
||||
*
|
||||
* http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
*/
|
||||
static uint32_t hash32shift(uint32_t key)
|
||||
{
|
||||
key = ~key + (key << 15); /* key = (key << 15) - key - 1; */
|
||||
key = key ^ (key >> 12);
|
||||
key = key + (key << 2);
|
||||
key = key ^ (key >> 4);
|
||||
key = key * 2057; /* key = (key + (key << 3)) + (key << 11); */
|
||||
key = key ^ (key >> 16);
|
||||
return key;
|
||||
}
|
||||
|
||||
void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key)
|
||||
{
|
||||
entry->key = key;
|
||||
entry->parent = entry->left = entry->right = NULL;
|
||||
entry->priority = hash32shift(key);
|
||||
entry->next = NULL;
|
||||
}
|
||||
|
||||
static nghttp2_map_entry* rotate_left(nghttp2_map_entry *entry)
|
||||
/* Same hash function in openjdk HashMap source code. */
|
||||
/* The |mod| must be power of 2 */
|
||||
static int32_t hash(int32_t h, int32_t mod)
|
||||
{
|
||||
nghttp2_map_entry *root = entry->right;
|
||||
entry->right = root->left;
|
||||
root->left = entry;
|
||||
|
||||
root->parent = entry->parent;
|
||||
entry->parent = root;
|
||||
if(root->parent) {
|
||||
if(root->parent->left == entry) {
|
||||
root->parent->left = root;
|
||||
} else {
|
||||
root->parent->right = root;
|
||||
}
|
||||
}
|
||||
if(entry->right) {
|
||||
entry->right->parent = entry;
|
||||
}
|
||||
return root;
|
||||
h ^= (h >> 20) ^ (h >> 12);
|
||||
return (h ^ (h >> 7) ^ (h >> 4)) & (mod - 1);
|
||||
}
|
||||
|
||||
static nghttp2_map_entry* rotate_right(nghttp2_map_entry* entry)
|
||||
static int insert(nghttp2_map_entry **table, size_t tablelen,
|
||||
nghttp2_map_entry *entry)
|
||||
{
|
||||
nghttp2_map_entry *root = entry->left;
|
||||
entry->left = root->right;
|
||||
root->right = entry;
|
||||
|
||||
root->parent = entry->parent;
|
||||
entry->parent = root;
|
||||
if(root->parent) {
|
||||
if(root->parent->left == entry) {
|
||||
root->parent->left = root;
|
||||
int32_t h = hash(entry->key, tablelen);
|
||||
if(table[h] == NULL) {
|
||||
table[h] = entry;
|
||||
} else {
|
||||
root->parent->right = root;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if(entry->left) {
|
||||
entry->left->parent = entry;
|
||||
entry->next = table[h];
|
||||
table[h] = entry;
|
||||
}
|
||||
return root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new_tablelen must be power of 2 */
|
||||
static int resize(nghttp2_map *map, size_t new_tablelen)
|
||||
{
|
||||
size_t i;
|
||||
nghttp2_map_entry **new_table;
|
||||
new_table = malloc(sizeof(nghttp2_map_entry*) * new_tablelen);
|
||||
if(new_table == NULL) {
|
||||
return NGHTTP2_ERR_NOMEM;
|
||||
}
|
||||
memset(new_table, 0, sizeof(nghttp2_map_entry*) * new_tablelen);
|
||||
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;
|
||||
}
|
||||
}
|
||||
free(map->table);
|
||||
map->tablelen = new_tablelen;
|
||||
map->table = new_table;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry)
|
||||
{
|
||||
nghttp2_map_entry *entry = map->root, *parent = NULL;
|
||||
if(map->root == NULL) {
|
||||
map->root = new_entry;
|
||||
map->size = 1;
|
||||
return 0;
|
||||
}
|
||||
/* Find position to insert. */
|
||||
while(1) {
|
||||
if(new_entry->key == entry->key) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
} else {
|
||||
if(new_entry->key < entry->key) {
|
||||
if(entry->left) {
|
||||
entry = entry->left;
|
||||
} else {
|
||||
parent = entry;
|
||||
parent->left = new_entry;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(entry->right) {
|
||||
entry = entry->right;
|
||||
} else {
|
||||
parent = entry;
|
||||
parent->right = new_entry;
|
||||
break;
|
||||
int rv;
|
||||
if(100 * (map->size + 1) > map->tablelen * LOAD_FACTOR) {
|
||||
rv = resize(map, map->tablelen * 2);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
new_entry->parent = parent;
|
||||
|
||||
/* Rotate tree to satisfy heap property. */
|
||||
for(entry = parent; ; entry = entry->parent) {
|
||||
if(entry->left && entry->priority > entry->left->priority) {
|
||||
entry = rotate_right(entry);
|
||||
} else if(entry->right && entry->priority > entry->right->priority) {
|
||||
entry = rotate_left(entry);
|
||||
} else {
|
||||
/* At this point, tree forms heap. */
|
||||
break;
|
||||
}
|
||||
/* If no parent is assigned, then it is a root node. */
|
||||
if(!entry->parent) {
|
||||
map->root = entry;
|
||||
break;
|
||||
}
|
||||
rv = insert(map->table, map->tablelen, new_entry);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
++map->size;
|
||||
return 0;
|
||||
|
@ -252,13 +160,11 @@ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry)
|
|||
|
||||
nghttp2_map_entry* nghttp2_map_find(nghttp2_map *map, key_type key)
|
||||
{
|
||||
nghttp2_map_entry *entry = map->root;
|
||||
while(entry != NULL) {
|
||||
if(key < entry->key) {
|
||||
entry = entry->left;
|
||||
} else if(key > entry->key) {
|
||||
entry = entry->right;
|
||||
} else {
|
||||
int32_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;
|
||||
}
|
||||
}
|
||||
|
@ -267,67 +173,23 @@ nghttp2_map_entry* nghttp2_map_find(nghttp2_map *map, key_type key)
|
|||
|
||||
int nghttp2_map_remove(nghttp2_map *map, key_type key)
|
||||
{
|
||||
nghttp2_map_entry *entry = map->root;
|
||||
|
||||
if(map->root == NULL) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
/* Locate entry to delete. */
|
||||
while(entry) {
|
||||
if(key < entry->key) {
|
||||
entry = entry->left;
|
||||
} else if(key > entry->key) {
|
||||
entry = entry->right;
|
||||
int32_t h;
|
||||
nghttp2_map_entry *entry, *prev;
|
||||
h = hash(key, map->tablelen);
|
||||
prev = NULL;
|
||||
for(entry = map->table[h]; entry; entry = entry->next) {
|
||||
if(entry->key == key) {
|
||||
if(prev == NULL) {
|
||||
map->table[h] = entry->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!entry) {
|
||||
/* Not found */
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
/* Rotate and bubble down to satisfy heap property. */
|
||||
for(;;) {
|
||||
if(!entry->left) {
|
||||
if(!entry->parent) {
|
||||
map->root = entry->right;
|
||||
} else if(entry->parent->left == entry) {
|
||||
entry->parent->left = entry->right;
|
||||
} else {
|
||||
entry->parent->right = entry->right;
|
||||
}
|
||||
if(entry->right) {
|
||||
entry->right->parent = entry->parent;
|
||||
}
|
||||
break;
|
||||
} else if(!entry->right) {
|
||||
if(!entry->parent) {
|
||||
map->root = entry->left;
|
||||
} else if(entry->parent->left == entry) {
|
||||
entry->parent->left = entry->left;
|
||||
} else {
|
||||
entry->parent->right = entry->left;
|
||||
}
|
||||
if(entry->left) {
|
||||
entry->left->parent = entry->parent;
|
||||
}
|
||||
break;
|
||||
} else if(entry->left->priority < entry->right->priority) {
|
||||
entry = rotate_right(entry);
|
||||
if(!entry->parent) {
|
||||
map->root = entry;
|
||||
}
|
||||
entry = entry->right;
|
||||
} else {
|
||||
entry = rotate_left(entry);
|
||||
if(!entry->parent) {
|
||||
map->root = entry;
|
||||
}
|
||||
entry = entry->left;
|
||||
}
|
||||
prev->next = entry->next;
|
||||
}
|
||||
--map->size;
|
||||
return 0;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
size_t nghttp2_map_size(nghttp2_map *map)
|
||||
|
|
|
@ -32,26 +32,31 @@
|
|||
#include <nghttp2/nghttp2.h>
|
||||
#include "nghttp2_int.h"
|
||||
|
||||
/* Implementation of ordered map */
|
||||
/* Implementation of unordered map */
|
||||
|
||||
typedef uint32_t key_type;
|
||||
typedef uint32_t pri_type;
|
||||
|
||||
typedef struct nghttp2_map_entry {
|
||||
key_type key;
|
||||
struct nghttp2_map_entry *parent, *left, *right;
|
||||
pri_type priority;
|
||||
struct nghttp2_map_entry *next;
|
||||
} nghttp2_map_entry;
|
||||
|
||||
typedef struct {
|
||||
nghttp2_map_entry *root;
|
||||
nghttp2_map_entry **table;
|
||||
size_t tablelen;
|
||||
size_t size;
|
||||
} nghttp2_map;
|
||||
|
||||
/*
|
||||
* Initializes the map |map|.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_map_init(nghttp2_map *map);
|
||||
int nghttp2_map_init(nghttp2_map *map);
|
||||
|
||||
/*
|
||||
* Deallocates any resources allocated for |map|. The stored entries
|
||||
|
@ -80,10 +85,12 @@ 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
|
||||
* negative error code:
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_INVALID_ARGUMENT
|
||||
* The item associated by |key| already exists.
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry);
|
||||
|
||||
|
|
|
@ -233,7 +233,10 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
|
|||
if(r != 0) {
|
||||
goto fail_hd_inflater;
|
||||
}
|
||||
nghttp2_map_init(&(*session_ptr)->streams);
|
||||
r = nghttp2_map_init(&(*session_ptr)->streams);
|
||||
if(r != 0) {
|
||||
goto fail_map;
|
||||
}
|
||||
r = nghttp2_pq_init(&(*session_ptr)->ob_pq, nghttp2_outbound_item_compar);
|
||||
if(r != 0) {
|
||||
goto fail_ob_pq;
|
||||
|
@ -294,7 +297,8 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
|
|||
fail_ob_ss_pq:
|
||||
nghttp2_pq_free(&(*session_ptr)->ob_pq);
|
||||
fail_ob_pq:
|
||||
/* No need to free (*session_ptr)->streams) here. */
|
||||
nghttp2_map_free(&(*session_ptr)->streams);
|
||||
fail_map:
|
||||
nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
|
||||
fail_hd_inflater:
|
||||
nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
|
||||
|
@ -389,6 +393,7 @@ void nghttp2_session_del(nghttp2_session *session)
|
|||
free(session->inflight_iv);
|
||||
nghttp2_inbound_frame_reset(session);
|
||||
nghttp2_map_each_free(&session->streams, nghttp2_free_streams, NULL);
|
||||
nghttp2_map_free(&session->streams);
|
||||
nghttp2_session_ob_pq_free(&session->ob_pq);
|
||||
nghttp2_session_ob_pq_free(&session->ob_ss_pq);
|
||||
nghttp2_hd_deflate_free(&session->hd_deflater);
|
||||
|
|
|
@ -149,6 +149,7 @@ void test_nghttp2_map_functional(void)
|
|||
CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry));
|
||||
}
|
||||
nghttp2_map_each_free(&map, eachfun, NULL);
|
||||
nghttp2_map_free(&map);
|
||||
}
|
||||
|
||||
static int entry_free(nghttp2_map_entry *entry, void *ptr)
|
||||
|
@ -177,4 +178,5 @@ void test_nghttp2_map_each_free(void)
|
|||
nghttp2_map_insert(&map, &shrubbery->map_entry);
|
||||
|
||||
nghttp2_map_each_free(&map, entry_free, NULL);
|
||||
nghttp2_map_free(&map);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue