nghttp2_map: Implement hash table

This commit is contained in:
Tatsuhiro Tsujikawa 2013-12-05 00:41:42 +09:00
parent 1f0dfd4316
commit a3c888d7d1
4 changed files with 129 additions and 253 deletions

View File

@ -24,227 +24,135 @@
*/ */
#include "nghttp2_map.h" #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; map->size = 0;
return 0;
} }
void nghttp2_map_free(nghttp2_map *map) void nghttp2_map_free(nghttp2_map *map)
{ {
map->root = NULL; free(map->table);
}
/* 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;
}
} }
void nghttp2_map_each_free(nghttp2_map *map, void nghttp2_map_each_free(nghttp2_map *map,
int (*func)(nghttp2_map_entry *entry, void *ptr), int (*func)(nghttp2_map_entry *entry, void *ptr),
void *ptr) void *ptr)
{ {
nghttp2_map_entry *entry; size_t i;
if(!map->root) { for(i = 0; i < map->tablelen; ++i) {
return; nghttp2_map_entry *entry;
for(entry = map->table[i]; entry;) {
nghttp2_map_entry *next = entry->next;
func(entry, ptr);
entry = next;
}
map->table[i] = NULL;
} }
entry = find_left_most_leaf(map->root);
while(entry) {
nghttp2_map_entry *parent = entry->parent;
/* Ignore return value. */
func(entry, ptr);
/* entry has been deleted. */
entry = traverse_postorder(parent, entry);
}
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);
}
parent = entry->parent;
while(parent && parent->right == entry) {
entry = entry->parent;
parent = parent->parent;
}
return parent;
} }
int nghttp2_map_each(nghttp2_map *map, int nghttp2_map_each(nghttp2_map *map,
int (*func)(nghttp2_map_entry *entry, void *ptr), int (*func)(nghttp2_map_entry *entry, void *ptr),
void *ptr) void *ptr)
{ {
nghttp2_map_entry *entry; int rv;
if(!map->root) { size_t i;
return 0; for(i = 0; i < map->tablelen; ++i) {
} nghttp2_map_entry *entry;
entry = find_left_most(map->root); for(entry = map->table[i]; entry; entry = entry->next) {
while(entry) { rv = func(entry, ptr);
int rv = func(entry, ptr); if(rv != 0) {
if(rv != 0) { return rv;
return rv; }
} }
entry = traverse_inorder(entry);
} }
return 0; 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) void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key)
{ {
entry->key = key; entry->key = key;
entry->parent = entry->left = entry->right = NULL; entry->next = NULL;
entry->priority = hash32shift(key);
} }
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; h ^= (h >> 20) ^ (h >> 12);
entry->right = root->left; return (h ^ (h >> 7) ^ (h >> 4)) & (mod - 1);
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;
} }
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; int32_t h = hash(entry->key, tablelen);
entry->left = root->right; if(table[h] == NULL) {
root->right = entry; 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;
}
return 0;
}
root->parent = entry->parent; /* new_tablelen must be power of 2 */
entry->parent = root; static int resize(nghttp2_map *map, size_t new_tablelen)
if(root->parent) { {
if(root->parent->left == entry) { size_t i;
root->parent->left = root; nghttp2_map_entry **new_table;
} else { new_table = malloc(sizeof(nghttp2_map_entry*) * new_tablelen);
root->parent->right = root; 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;
} }
} }
if(entry->left) { free(map->table);
entry->left->parent = entry; map->tablelen = new_tablelen;
} map->table = new_table;
return root; return 0;
} }
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry)
{ {
nghttp2_map_entry *entry = map->root, *parent = NULL; int rv;
if(map->root == NULL) { if(100 * (map->size + 1) > map->tablelen * LOAD_FACTOR) {
map->root = new_entry; rv = resize(map, map->tablelen * 2);
map->size = 1; if(rv != 0) {
return 0; return rv;
}
/* 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;
}
}
} }
} }
new_entry->parent = parent; rv = insert(map->table, map->tablelen, new_entry);
if(rv != 0) {
/* Rotate tree to satisfy heap property. */ return rv;
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;
}
} }
++map->size; ++map->size;
return 0; 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* nghttp2_map_find(nghttp2_map *map, key_type key)
{ {
nghttp2_map_entry *entry = map->root; int32_t h;
while(entry != NULL) { nghttp2_map_entry *entry;
if(key < entry->key) { h = hash(key, map->tablelen);
entry = entry->left; for(entry = map->table[h]; entry; entry = entry->next) {
} else if(key > entry->key) { if(entry->key == key) {
entry = entry->right;
} else {
return entry; 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) int nghttp2_map_remove(nghttp2_map *map, key_type key)
{ {
nghttp2_map_entry *entry = map->root; int32_t h;
nghttp2_map_entry *entry, *prev;
if(map->root == NULL) { h = hash(key, map->tablelen);
return NGHTTP2_ERR_INVALID_ARGUMENT; prev = NULL;
} for(entry = map->table[h]; entry; entry = entry->next) {
/* Locate entry to delete. */ if(entry->key == key) {
while(entry) { if(prev == NULL) {
if(key < entry->key) { map->table[h] = entry->next;
entry = entry->left;
} else if(key > entry->key) {
entry = entry->right;
} 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 { } else {
entry->parent->right = entry->right; prev->next = entry->next;
} }
if(entry->right) { --map->size;
entry->right->parent = entry->parent; return 0;
}
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 = entry;
} }
--map->size; return NGHTTP2_ERR_INVALID_ARGUMENT;
return 0;
} }
size_t nghttp2_map_size(nghttp2_map *map) size_t nghttp2_map_size(nghttp2_map *map)

View File

@ -32,26 +32,31 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include "nghttp2_int.h" #include "nghttp2_int.h"
/* Implementation of ordered map */ /* Implementation of unordered map */
typedef uint32_t key_type; typedef uint32_t key_type;
typedef uint32_t pri_type;
typedef struct nghttp2_map_entry { typedef struct nghttp2_map_entry {
key_type key; key_type key;
struct nghttp2_map_entry *parent, *left, *right; struct nghttp2_map_entry *next;
pri_type priority;
} nghttp2_map_entry; } nghttp2_map_entry;
typedef struct { typedef struct {
nghttp2_map_entry *root; nghttp2_map_entry **table;
size_t tablelen;
size_t size; size_t size;
} nghttp2_map; } nghttp2_map;
/* /*
* Initializes the map |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 * 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|. * 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 code: * negative error codes:
* *
* NGHTTP2_ERR_INVALID_ARGUMENT * NGHTTP2_ERR_INVALID_ARGUMENT
* The item associated by |key| already exists. * The item associated by |key| already exists.
* NGHTTP2_ERR_NOMEM
* Out of memory
*/ */
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry); int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry);

View File

@ -233,7 +233,10 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
if(r != 0) { if(r != 0) {
goto fail_hd_inflater; 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); r = nghttp2_pq_init(&(*session_ptr)->ob_pq, nghttp2_outbound_item_compar);
if(r != 0) { if(r != 0) {
goto fail_ob_pq; goto fail_ob_pq;
@ -294,7 +297,8 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
fail_ob_ss_pq: fail_ob_ss_pq:
nghttp2_pq_free(&(*session_ptr)->ob_pq); nghttp2_pq_free(&(*session_ptr)->ob_pq);
fail_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); nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater);
fail_hd_inflater: fail_hd_inflater:
nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater);
@ -389,6 +393,7 @@ void nghttp2_session_del(nghttp2_session *session)
free(session->inflight_iv); free(session->inflight_iv);
nghttp2_inbound_frame_reset(session); nghttp2_inbound_frame_reset(session);
nghttp2_map_each_free(&session->streams, nghttp2_free_streams, NULL); 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_pq);
nghttp2_session_ob_pq_free(&session->ob_ss_pq); nghttp2_session_ob_pq_free(&session->ob_ss_pq);
nghttp2_hd_deflate_free(&session->hd_deflater); nghttp2_hd_deflate_free(&session->hd_deflater);
@ -397,7 +402,7 @@ void nghttp2_session_del(nghttp2_session *session)
free(session->aob.framebuf); free(session->aob.framebuf);
free(session->nvbuf); free(session->nvbuf);
free(session->iframe.buf); free(session->iframe.buf);
free(session); free(session);
} }
static int outbound_item_update_pri static int outbound_item_update_pri

View File

@ -149,6 +149,7 @@ void test_nghttp2_map_functional(void)
CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry)); CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry));
} }
nghttp2_map_each_free(&map, eachfun, NULL); nghttp2_map_each_free(&map, eachfun, NULL);
nghttp2_map_free(&map);
} }
static int entry_free(nghttp2_map_entry *entry, void *ptr) 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_insert(&map, &shrubbery->map_entry);
nghttp2_map_each_free(&map, entry_free, NULL); nghttp2_map_each_free(&map, entry_free, NULL);
nghttp2_map_free(&map);
} }