diff --git a/src/port.c b/src/port.c index 18fe6a1..62b76d8 100644 --- a/src/port.c +++ b/src/port.c @@ -347,11 +347,9 @@ int ep_port_register_socket_handle(ep_port_t* port_info, return 0; } -int ep_port_unregister_socket_handle(ep_port_t* port_info, - ep_sock_t* sock_info) { - if (tree_del(&port_info->sock_tree, &sock_info->tree_node) < 0) - return_error(-1, ERROR_NOT_FOUND); - return 0; +void ep_port_unregister_socket_handle(ep_port_t* port_info, + ep_sock_t* sock_info) { + tree_del(&port_info->sock_tree, &sock_info->tree_node); } ep_sock_t* ep_port_find_socket(ep_port_t* port_info, SOCKET socket) { diff --git a/src/port.h b/src/port.h index eefd423..326c93f 100644 --- a/src/port.h +++ b/src/port.h @@ -51,8 +51,8 @@ WEPOLL_INTERNAL void ep_port_release_poll_group(ep_port_t* port_info, WEPOLL_INTERNAL int ep_port_register_socket_handle(ep_port_t* port_info, ep_sock_t* sock_info, SOCKET socket); -WEPOLL_INTERNAL int ep_port_unregister_socket_handle(ep_port_t* port_info, - ep_sock_t* sock_info); +WEPOLL_INTERNAL void ep_port_unregister_socket_handle(ep_port_t* port_info, + ep_sock_t* sock_info); WEPOLL_INTERNAL ep_sock_t* ep_port_find_socket(ep_port_t* port_info, SOCKET socket); diff --git a/src/tree.c b/src/tree.c index d742b95..7d304ec 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,23 +1,56 @@ -#include +#include +#include +#include +#include -#include "error.h" -#include "rb.h" #include "tree.h" -#include "util.h" -static inline int _tree_compare(tree_node_t* a, tree_node_t* b) { - if (a->key < b->key) - return -1; - else if (a->key > b->key) - return 1; - else - return 0; +static void _tree_rotate_left(tree_t* tree, tree_node_t* node) { + tree_node_t* p = node; + tree_node_t* q = node->right; + tree_node_t* parent = p->parent; + + if (parent) { + if (parent->left == p) + parent->left = q; + else + parent->right = q; + } else { + tree->root = q; + } + + q->parent = parent; + p->parent = q; + p->right = q->left; + if (p->right) + p->right->parent = p; + q->left = p; } -RB_GENERATE_STATIC(tree, tree_node, node, _tree_compare); +static void _tree_rotate_right(tree_t* tree, tree_node_t* node) { + tree_node_t* p = node; + tree_node_t* q = node->left; + tree_node_t* parent = p->parent; + + if (parent) { + if (parent->left == p) + parent->left = q; + else + parent->right = q; + } else { + tree->root = q; + } + + q->parent = parent; + p->parent = q; + p->left = q->right; + if (p->left) + p->left->parent = p; + q->right = p; +} void tree_init(tree_t* tree) { - RB_INIT(tree); + memset(tree, 0, sizeof *tree); } void tree_node_init(tree_node_t* node) { @@ -25,39 +58,210 @@ void tree_node_init(tree_node_t* node) { } int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { - tree_node_t* existing_node; + tree_node_t* parent; + tree_node_t* grandparent; + tree_node_t* uncle; + + parent = tree->root; + if (parent) { + for (;;) { + if (key < parent->key) { + if (parent->left) { + parent = parent->left; + } else { + parent->left = node; + break; + } + } else if (key > parent->key) { + if (parent->right) { + parent = parent->right; + } else { + parent->right = node; + break; + } + } else { + return -1; + } + } + } else { + tree->root = node; + } node->key = key; - existing_node = RB_INSERT(tree, tree, node); + node->left = node->right = NULL; + node->parent = parent; + node->red = true; - if (existing_node != NULL) - return -1; + while (parent && parent->red) { + grandparent = parent->parent; + if (parent == grandparent->left) { + uncle = grandparent->right; + if (uncle && uncle->red) { + parent->red = uncle->red = false; + grandparent->red = true; + node = grandparent; + } else { + if (node == parent->right) { + _tree_rotate_left(tree, parent); + node = parent; + parent = node->parent; + } + parent->red = false; + grandparent->red = true; + _tree_rotate_right(tree, grandparent); + } + } else { + uncle = grandparent->left; + if (uncle && uncle->red) { + parent->red = uncle->red = false; + grandparent->red = true; + node = grandparent; + } else { + if (node == parent->left) { + _tree_rotate_right(tree, parent); + node = parent; + parent = node->parent; + } + parent->red = false; + grandparent->red = true; + _tree_rotate_left(tree, grandparent); + } + } + parent = node->parent; + } + tree->root->red = false; return 0; } -int tree_del(tree_t* tree, tree_node_t* node) { - tree_node_t* removed_node; +void tree_del(tree_t* tree, tree_node_t* node) { + tree_node_t* parent = node->parent; + tree_node_t* left = node->left; + tree_node_t* right = node->right; + tree_node_t* next; + tree_node_t* sibling; + bool red; - removed_node = RB_REMOVE(tree, tree, node); + if (!left) { + next = right; + } else if (!right) { + next = left; + } else { + next = right; + while (next->left) + next = next->left; + } - if (removed_node == NULL) - return -1; - else - assert(removed_node == node); + if (parent) { + if (parent->left == node) + parent->left = next; + else + parent->right = next; + } else { + tree->root = next; + } - return 0; + if (left && right) { + red = next->red; + next->red = node->red; + next->left = left; + left->parent = next; + if (next != right) { + parent = next->parent; + next->parent = node->parent; + node = next->right; + parent->left = node; + next->right = right; + right->parent = next; + } else { + next->parent = parent; + parent = next; + node = next->right; + } + } else { + red = node->red; + node = next; + } + + if (node) + node->parent = parent; + if (red) + return; + if (node && node->red) { + node->red = false; + return; + } + + do { + if (node == tree->root) + break; + if (node == parent->left) { + sibling = parent->right; + if (sibling->red) { + sibling->red = false; + parent->red = true; + _tree_rotate_left(tree, parent); + sibling = parent->right; + } + if ((sibling->left && sibling->left->red) || + (sibling->right && sibling->right->red)) { + if (!sibling->right || !sibling->right->red) { + sibling->left->red = false; + sibling->red = true; + _tree_rotate_right(tree, sibling); + sibling = parent->right; + } + sibling->red = parent->red; + parent->red = sibling->right->red = false; + _tree_rotate_left(tree, parent); + node = tree->root; + break; + } + } else { + sibling = parent->left; + if (sibling->red) { + sibling->red = false; + parent->red = true; + _tree_rotate_right(tree, parent); + sibling = parent->left; + } + if ((sibling->left && sibling->left->red) || + (sibling->right && sibling->right->red)) { + if (!sibling->left || !sibling->left->red) { + sibling->right->red = false; + sibling->red = true; + _tree_rotate_left(tree, sibling); + sibling = parent->left; + } + sibling->red = parent->red; + parent->red = sibling->left->red = false; + _tree_rotate_right(tree, parent); + node = tree->root; + break; + } + } + sibling->red = true; + node = parent; + parent = parent->parent; + } while (!node->red); + + if (node) + node->red = false; } -tree_node_t* tree_find(tree_t* tree, uintptr_t key) { - tree_node_t lookup; - - memset(&lookup, 0, sizeof lookup); - lookup.key = key; - - return RB_FIND(tree, tree, &lookup); +tree_node_t* tree_find(const tree_t* tree, uintptr_t key) { + tree_node_t* node = tree->root; + while (node) { + if (key < node->key) + node = node->left; + else if (key > node->key) + node = node->right; + else + return node; + } + return NULL; } -tree_node_t* tree_root(tree_t* tree) { - return RB_ROOT(tree); +tree_node_t* tree_root(const tree_t* tree) { + return tree->root; } diff --git a/src/tree.h b/src/tree.h index b9857dc..d1c58ea 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,28 +1,39 @@ #ifndef WEPOLL_TREE_H_ #define WEPOLL_TREE_H_ +#include +#include + #include "internal.h" -#include "rb.h" +#include "util.h" /* NB: the tree functions do not set errno or LastError when they fail. Each of * the API functions has at most one failure mode. It is up to the caller to * set an appropriate error code when necessary. */ -typedef RB_HEAD(tree, tree_node) tree_t; +typedef struct tree tree_t; +typedef struct tree_node tree_node_t; + +typedef struct tree { + tree_node_t* root; +} tree_t; typedef struct tree_node { - RB_ENTRY(tree_node) node; + tree_node_t* left; + tree_node_t* right; + tree_node_t* parent; uintptr_t key; + bool red; } tree_node_t; WEPOLL_INTERNAL void tree_init(tree_t* tree); WEPOLL_INTERNAL void tree_node_init(tree_node_t* node); WEPOLL_INTERNAL int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key); -WEPOLL_INTERNAL int tree_del(tree_t* tree, tree_node_t* node); +WEPOLL_INTERNAL void tree_del(tree_t* tree, tree_node_t* node); -WEPOLL_INTERNAL tree_node_t* tree_find(tree_t* tree, uintptr_t key); -WEPOLL_INTERNAL tree_node_t* tree_root(tree_t* tree); +WEPOLL_INTERNAL tree_node_t* tree_find(const tree_t* tree, uintptr_t key); +WEPOLL_INTERNAL tree_node_t* tree_root(const tree_t* tree); #endif /* WEPOLL_TREE_H_ */ diff --git a/test/test-tree.c b/test/test-tree.c new file mode 100644 index 0000000..dd26596 --- /dev/null +++ b/test/test-tree.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include + +#include "test-util.h" +#include "tree.h" +#include "util.h" + +#define NODE_COUNT 1000 +static_assert(NODE_COUNT <= RAND_MAX, "NODE_COUNT too high"); + +typedef void (*test_op_t)(tree_t* tree, uintptr_t key); + +static size_t count_subtree(const tree_node_t* node) { + if (node == NULL) + return 0; + else + return 1 + count_subtree(node->left) + count_subtree(node->right); +} + +static size_t count_tree(const tree_t* tree) { + return count_subtree(tree_root(tree)); +} + +static size_t check_subtree_structure(const tree_node_t* node) { + size_t black_height_left; + size_t black_height_right; + + if (!node) + return 0; + + black_height_left = check_subtree_structure(node->left); + black_height_right = check_subtree_structure(node->right); + check(black_height_left == black_height_right); + + if (node->red) { + check(!node->left || !node->left->red); + check(!node->right || !node->right->red); + return black_height_left; + } else { + return black_height_left + 1; + } +} + +static void check_tree_structure(const tree_t* tree) { + check_subtree_structure(tree_root(tree)); +} + +static void check_tree_count(const tree_t* tree, size_t expected_count) { + size_t count = count_tree(tree); + check(count == expected_count); +} + +static void keys_increasing(tree_t* tree, test_op_t op) { + ssize_t i; + for (i = 0; i < NODE_COUNT; i++) + op(tree, i); +} + +static void keys_decreasing(tree_t* tree, test_op_t op) { + ssize_t i; + for (i = NODE_COUNT - 1; i >= 0; i--) + op(tree, i); +} + +static void keys_random(tree_t* tree, test_op_t op) { + uintptr_t keys[NODE_COUNT]; + uintptr_t index, key; + ssize_t left; + + for (index = 0; index < NODE_COUNT; index++) + keys[index] = index; + + for (left = NODE_COUNT - 1; left >= 0; left--) { + index = left > 0 ? rand() % left : 0; + key = keys[index]; + keys[index] = keys[left]; + op(tree, key); + }; +} + +static void add(tree_t* tree, uintptr_t key) { + tree_node_t* node; + size_t before_count; + int r; + + before_count = count_tree(tree); + + node = malloc(sizeof *node); + check(node != NULL); + tree_node_init(node); + + r = tree_add(tree, node, key); + check(r == 0); + check(node->key == key); + + check_tree_structure(tree); + check_tree_count(tree, before_count + 1); +} + +static void add_error(tree_t* tree, uintptr_t key) { + tree_node_t node; + size_t before_count; + int r; + + before_count = count_tree(tree); + + tree_node_init(&node); + r = tree_add(tree, &node, key); + check(r == -1); + + check_tree_structure(tree); + check_tree_count(tree, before_count); +} + +static void find_del(tree_t* tree, uintptr_t key) { + tree_node_t* node; + size_t before_count; + + before_count = count_tree(tree); + + node = tree_find(tree, key); + check(node != NULL); + check(node->key == key); + + tree_del(tree, node); + free(node); + + check_tree_structure(tree); + check_tree_count(tree, before_count - 1); +} + +static void find_error(tree_t* tree, uintptr_t key) { + tree_node_t* node; + size_t before_count; + + before_count = count_tree(tree); + + node = tree_find(tree, key); + check(node == NULL); + + check_tree_structure(tree); + check_tree_count(tree, before_count); +} + +int main(void) { + tree_t tree; + tree_init(&tree); + + keys_increasing(&tree, add); + check_tree_count(&tree, NODE_COUNT); + keys_increasing(&tree, add_error); + keys_increasing(&tree, find_del); + check_tree_count(&tree, 0); + keys_increasing(&tree, find_error); + + keys_decreasing(&tree, add); + check_tree_count(&tree, NODE_COUNT); + keys_decreasing(&tree, add_error); + keys_decreasing(&tree, find_del); + check_tree_count(&tree, 0); + keys_decreasing(&tree, find_error); + + keys_random(&tree, add); + check_tree_count(&tree, NODE_COUNT); + keys_random(&tree, add_error); + keys_random(&tree, find_del); + check_tree_count(&tree, 0); + keys_random(&tree, find_error); + + keys_random(&tree, add); + check_tree_count(&tree, NODE_COUNT); + keys_increasing(&tree, add_error); + keys_decreasing(&tree, find_del); + check_tree_count(&tree, 0); + keys_random(&tree, find_error); +}