Iterative treap implementation
This change implements spdylay_map functions in iterative approach instead of original recursive approach. The iterative version is overall faster than recursive one. The traversal function (spdylay_map_each) is slightly slower but it is not noticeable.
This commit is contained in:
parent
9378b74fc3
commit
24e932dfa2
|
@ -24,24 +24,18 @@
|
||||||
*/
|
*/
|
||||||
#include "spdylay_map.h"
|
#include "spdylay_map.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SUB_LEFT = 1,
|
||||||
|
SUB_RIGHT = 1 << 1,
|
||||||
|
SUB_ALL = (1 << 2) - 1
|
||||||
|
} spdylay_map_subtr;
|
||||||
|
|
||||||
void spdylay_map_init(spdylay_map *map)
|
void spdylay_map_init(spdylay_map *map)
|
||||||
{
|
{
|
||||||
map->root = NULL;
|
map->root = NULL;
|
||||||
map->size = 0;
|
map->size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spdylay_map_entry_free_recur(spdylay_map_entry *entry,
|
|
||||||
int (*func)(spdylay_map_entry *entry,
|
|
||||||
void *ptr),
|
|
||||||
void *ptr)
|
|
||||||
{
|
|
||||||
if(entry != NULL) {
|
|
||||||
spdylay_map_entry_free_recur(entry->left, func, ptr);
|
|
||||||
spdylay_map_entry_free_recur(entry->right, func, ptr);
|
|
||||||
func(entry, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void spdylay_map_free(spdylay_map *map)
|
void spdylay_map_free(spdylay_map *map)
|
||||||
{
|
{
|
||||||
map->root = NULL;
|
map->root = NULL;
|
||||||
|
@ -51,7 +45,24 @@ void spdylay_map_each_free(spdylay_map *map,
|
||||||
int (*func)(spdylay_map_entry *entry, void *ptr),
|
int (*func)(spdylay_map_entry *entry, void *ptr),
|
||||||
void *ptr)
|
void *ptr)
|
||||||
{
|
{
|
||||||
spdylay_map_entry_free_recur(map->root, func, ptr);
|
spdylay_map_entry *entry = map->root;
|
||||||
|
while(entry) {
|
||||||
|
if(entry->flags == SUB_ALL) {
|
||||||
|
spdylay_map_entry *parent = entry->parent;
|
||||||
|
func(entry, ptr);
|
||||||
|
entry = parent;
|
||||||
|
} else if(entry->flags == SUB_LEFT) {
|
||||||
|
entry->flags |= SUB_RIGHT;
|
||||||
|
if(entry->right) {
|
||||||
|
entry = entry->right;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entry->flags |= SUB_LEFT;
|
||||||
|
if(entry->left) {
|
||||||
|
entry = entry->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
map->root = NULL;
|
map->root = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +85,9 @@ static uint32_t hash32shift(uint32_t key)
|
||||||
void spdylay_map_entry_init(spdylay_map_entry *entry, key_type key)
|
void spdylay_map_entry_init(spdylay_map_entry *entry, key_type key)
|
||||||
{
|
{
|
||||||
entry->key = key;
|
entry->key = key;
|
||||||
entry->left = entry->right = NULL;
|
entry->parent = entry->left = entry->right = NULL;
|
||||||
entry->priority = hash32shift(key);
|
entry->priority = hash32shift(key);
|
||||||
|
entry->flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static spdylay_map_entry* rotate_left(spdylay_map_entry *entry)
|
static spdylay_map_entry* rotate_left(spdylay_map_entry *entry)
|
||||||
|
@ -83,6 +95,19 @@ static spdylay_map_entry* rotate_left(spdylay_map_entry *entry)
|
||||||
spdylay_map_entry *root = entry->right;
|
spdylay_map_entry *root = entry->right;
|
||||||
entry->right = root->left;
|
entry->right = root->left;
|
||||||
root->left = entry;
|
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;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,38 +116,74 @@ static spdylay_map_entry* rotate_right(spdylay_map_entry* entry)
|
||||||
spdylay_map_entry *root = entry->left;
|
spdylay_map_entry *root = entry->left;
|
||||||
entry->left = root->right;
|
entry->left = root->right;
|
||||||
root->right = entry;
|
root->right = entry;
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
static spdylay_map_entry* insert_recur(spdylay_map_entry *entry,
|
root->parent = entry->parent;
|
||||||
spdylay_map_entry *new_entry,
|
entry->parent = root;
|
||||||
int *error)
|
if(root->parent) {
|
||||||
{
|
if(root->parent->left == entry) {
|
||||||
if(entry == NULL) {
|
root->parent->left = root;
|
||||||
entry = new_entry;
|
|
||||||
} else if(new_entry->key == entry->key) {
|
|
||||||
*error = SPDYLAY_ERR_INVALID_ARGUMENT;
|
|
||||||
} else if(new_entry->key < entry->key) {
|
|
||||||
entry->left = insert_recur(entry->left, new_entry, error);
|
|
||||||
} else {
|
} else {
|
||||||
entry->right = insert_recur(entry->right, new_entry, error);
|
root->parent->right = root;
|
||||||
}
|
}
|
||||||
if(entry->left != NULL && entry->priority > entry->left->priority) {
|
|
||||||
entry = rotate_right(entry);
|
|
||||||
} else if(entry->right != NULL && entry->priority > entry->right->priority) {
|
|
||||||
entry = rotate_left(entry);
|
|
||||||
}
|
}
|
||||||
return entry;
|
if(entry->left) {
|
||||||
|
entry->left->parent = entry;
|
||||||
|
}
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
int spdylay_map_insert(spdylay_map *map, spdylay_map_entry *new_entry)
|
int spdylay_map_insert(spdylay_map *map, spdylay_map_entry *new_entry)
|
||||||
{
|
{
|
||||||
int error = 0;
|
spdylay_map_entry *entry = map->root, *parent = NULL;
|
||||||
map->root = insert_recur(map->root, new_entry, &error);
|
if(map->root == NULL) {
|
||||||
if(!error) {
|
map->root = new_entry;
|
||||||
++map->size;
|
map->size = 1;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return error;
|
/* Find position to insert. */
|
||||||
|
while(1) {
|
||||||
|
if(new_entry->key == entry->key) {
|
||||||
|
return SPDYLAY_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;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++map->size;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdylay_map_entry* spdylay_map_find(spdylay_map *map, key_type key)
|
spdylay_map_entry* spdylay_map_find(spdylay_map *map, key_type key)
|
||||||
|
@ -140,51 +201,69 @@ spdylay_map_entry* spdylay_map_find(spdylay_map *map, key_type key)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static spdylay_map_entry* remove_rotate_recur(spdylay_map_entry *entry)
|
|
||||||
{
|
|
||||||
if(entry->left == NULL) {
|
|
||||||
spdylay_map_entry *right = entry->right;
|
|
||||||
return right;
|
|
||||||
} else if(entry->right == NULL) {
|
|
||||||
spdylay_map_entry *left = entry->left;
|
|
||||||
return left;
|
|
||||||
} else if(entry->left->priority < entry->right->priority) {
|
|
||||||
entry = rotate_right(entry);
|
|
||||||
entry->right = remove_rotate_recur(entry->right);
|
|
||||||
return entry;
|
|
||||||
} else {
|
|
||||||
entry = rotate_left(entry);
|
|
||||||
entry->left = remove_rotate_recur(entry->left);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static spdylay_map_entry* remove_recur(spdylay_map_entry *entry, key_type key,
|
|
||||||
int *error)
|
|
||||||
{
|
|
||||||
if(entry == NULL) {
|
|
||||||
*error = SPDYLAY_ERR_INVALID_ARGUMENT;
|
|
||||||
} else if(key < entry->key) {
|
|
||||||
entry->left = remove_recur(entry->left, key, error);
|
|
||||||
} else if(key > entry->key) {
|
|
||||||
entry->right = remove_recur(entry->right, key, error);
|
|
||||||
} else {
|
|
||||||
entry = remove_rotate_recur(entry);
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spdylay_map_remove(spdylay_map *map, key_type key)
|
int spdylay_map_remove(spdylay_map *map, key_type key)
|
||||||
{
|
{
|
||||||
if(map->root != NULL) {
|
spdylay_map_entry *entry = map->root;
|
||||||
int error = 0;
|
|
||||||
map->root = remove_recur(map->root, key, &error);
|
if(map->root == NULL) {
|
||||||
if(!error) {
|
|
||||||
--map->size;
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
return SPDYLAY_ERR_INVALID_ARGUMENT;
|
return SPDYLAY_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
/* Locate entry to delete. */
|
||||||
|
while(entry) {
|
||||||
|
if(key < entry->key) {
|
||||||
|
entry = entry->left;
|
||||||
|
} else if(key > entry->key) {
|
||||||
|
entry = entry->right;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!entry) {
|
||||||
|
/* Not found */
|
||||||
|
return SPDYLAY_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--map->size;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t spdylay_map_size(spdylay_map *map)
|
size_t spdylay_map_size(spdylay_map *map)
|
||||||
|
@ -192,24 +271,35 @@ size_t spdylay_map_size(spdylay_map *map)
|
||||||
return map->size;
|
return map->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int for_each(spdylay_map_entry *entry,
|
|
||||||
int (*func)(spdylay_map_entry *entry, void *ptr),
|
|
||||||
void *ptr)
|
|
||||||
{
|
|
||||||
if(entry) {
|
|
||||||
int rv;
|
|
||||||
if((rv = for_each(entry->left, func, ptr)) != 0 ||
|
|
||||||
(rv = func(entry, ptr)) != 0 ||
|
|
||||||
(rv = for_each(entry->right, func, ptr)) != 0) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spdylay_map_each(spdylay_map *map,
|
int spdylay_map_each(spdylay_map *map,
|
||||||
int (*func)(spdylay_map_entry *entry, void *ptr),
|
int (*func)(spdylay_map_entry *entry, void *ptr),
|
||||||
void *ptr)
|
void *ptr)
|
||||||
{
|
{
|
||||||
return for_each(map->root, func, ptr);
|
spdylay_map_entry *entry = map->root;
|
||||||
|
while(entry) {
|
||||||
|
if(entry->flags == SUB_ALL) {
|
||||||
|
entry->flags = 0;
|
||||||
|
entry = entry->parent;
|
||||||
|
} else if(entry->flags == SUB_LEFT) {
|
||||||
|
int rv;
|
||||||
|
rv = func(entry, ptr);
|
||||||
|
if(rv != 0) {
|
||||||
|
while(entry) {
|
||||||
|
entry->flags = 0;
|
||||||
|
entry = entry->parent;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
entry->flags |= SUB_RIGHT;
|
||||||
|
if(entry->right) {
|
||||||
|
entry = entry->right;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entry->flags |= SUB_LEFT;
|
||||||
|
if(entry->left) {
|
||||||
|
entry = entry->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,9 @@ typedef uint32_t pri_type;
|
||||||
|
|
||||||
typedef struct spdylay_map_entry {
|
typedef struct spdylay_map_entry {
|
||||||
key_type key;
|
key_type key;
|
||||||
struct spdylay_map_entry *left, *right;
|
struct spdylay_map_entry *parent, *left, *right;
|
||||||
pri_type priority;
|
pri_type priority;
|
||||||
|
uint8_t flags;
|
||||||
} spdylay_map_entry;
|
} spdylay_map_entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -68,6 +68,7 @@ int main(int argc, char* argv[])
|
||||||
/* add the tests to the suite */
|
/* add the tests to the suite */
|
||||||
if(!CU_add_test(pSuite, "pq", test_spdylay_pq) ||
|
if(!CU_add_test(pSuite, "pq", test_spdylay_pq) ||
|
||||||
!CU_add_test(pSuite, "map", test_spdylay_map) ||
|
!CU_add_test(pSuite, "map", test_spdylay_map) ||
|
||||||
|
!CU_add_test(pSuite, "map_functional", test_spdylay_map_functional) ||
|
||||||
!CU_add_test(pSuite, "map_each_free", test_spdylay_map_each_free) ||
|
!CU_add_test(pSuite, "map_each_free", test_spdylay_map_each_free) ||
|
||||||
!CU_add_test(pSuite, "queue", test_spdylay_queue) ||
|
!CU_add_test(pSuite, "queue", test_spdylay_queue) ||
|
||||||
!CU_add_test(pSuite, "buffer", test_spdylay_buffer) ||
|
!CU_add_test(pSuite, "buffer", test_spdylay_buffer) ||
|
||||||
|
|
|
@ -92,6 +92,57 @@ void test_spdylay_map(void)
|
||||||
spdylay_map_free(&map);
|
spdylay_map_free(&map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void shuffle(int *a, int n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = n - 1; i >= 1; --i) {
|
||||||
|
size_t j = (int)((double)(i + 1) * rand() / (RAND_MAX + 1.0));
|
||||||
|
int t = a[j];
|
||||||
|
a[j] = a[i];
|
||||||
|
a[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eachfun(spdylay_map_entry *entry, void *ptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_ENT 6000
|
||||||
|
strentry arr[NUM_ENT];
|
||||||
|
int order[NUM_ENT];
|
||||||
|
|
||||||
|
void test_spdylay_map_functional(void)
|
||||||
|
{
|
||||||
|
spdylay_map map;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spdylay_map_init(&map);
|
||||||
|
for(i = 0; i < NUM_ENT; ++i) {
|
||||||
|
strentry_init(&arr[i], i + 1, "foo");
|
||||||
|
order[i] = i + 1;
|
||||||
|
}
|
||||||
|
// insertion
|
||||||
|
shuffle(order, NUM_ENT);
|
||||||
|
for(i = 0; i < NUM_ENT; ++i) {
|
||||||
|
CU_ASSERT(0 == spdylay_map_insert(&map, &arr[i].map_entry));
|
||||||
|
}
|
||||||
|
// traverse
|
||||||
|
spdylay_map_each(&map, eachfun, NULL);
|
||||||
|
// find
|
||||||
|
shuffle(order, NUM_ENT);
|
||||||
|
for(i = 0; i < NUM_ENT; ++i) {
|
||||||
|
spdylay_map_find(&map, order[i]);
|
||||||
|
}
|
||||||
|
// remove
|
||||||
|
shuffle(order, NUM_ENT);
|
||||||
|
for(i = 0; i < NUM_ENT; ++i) {
|
||||||
|
CU_ASSERT(0 == spdylay_map_remove(&map, order[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
spdylay_map_free(&map);
|
||||||
|
}
|
||||||
|
|
||||||
static int entry_free(spdylay_map_entry *entry, void *ptr)
|
static int entry_free(spdylay_map_entry *entry, void *ptr)
|
||||||
{
|
{
|
||||||
free(entry);
|
free(entry);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define SPDYLAY_MAP_TEST_H
|
#define SPDYLAY_MAP_TEST_H
|
||||||
|
|
||||||
void test_spdylay_map(void);
|
void test_spdylay_map(void);
|
||||||
|
void test_spdylay_map_functional(void);
|
||||||
void test_spdylay_map_each_free(void);
|
void test_spdylay_map_each_free(void);
|
||||||
|
|
||||||
#endif /* SPDYLAY_MAP_TEST_H */
|
#endif /* SPDYLAY_MAP_TEST_H */
|
||||||
|
|
Loading…
Reference in New Issue