diff --git a/legacy/eina/src/include/Makefile.am b/legacy/eina/src/include/Makefile.am index f553975fef..f2ac6f3f33 100644 --- a/legacy/eina/src/include/Makefile.am +++ b/legacy/eina/src/include/Makefile.am @@ -21,6 +21,7 @@ eina_stringshare.h \ eina_inline_list.x \ eina_accessor.h \ eina_convert.h \ +eina_rbtree.h \ eina_iterator.h installed_mainheaderdir = $(prefix)/include/eina-@VMAJ@ diff --git a/legacy/eina/src/include/eina_rbtree.h b/legacy/eina/src/include/eina_rbtree.h new file mode 100644 index 0000000000..117b79f412 --- /dev/null +++ b/legacy/eina/src/include/eina_rbtree.h @@ -0,0 +1,58 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifndef EINA_RBTREE_H__ +#define EINA_RBTREE_H__ + +#include "eina_types.h" +#include "eina_error.h" +#include "eina_iterator.h" + +typedef enum { + EINA_RBTREE_RED, + EINA_RBTREE_BLACK +} Eina_Rbtree_Color; + +typedef enum { + EINA_RBTREE_LEFT = 0, + EINA_RBTREE_RIGHT = 1 +} Eina_Rbtree_Direction; + +typedef struct _Eina_Rbtree Eina_Rbtree; +struct _Eina_Rbtree +{ + Eina_Rbtree *son[2]; + + Eina_Rbtree_Color color : 1; +}; + +typedef Eina_Rbtree_Direction (*Eina_Rbtree_Cmp_Node_Cb)(const Eina_Rbtree *left, const Eina_Rbtree *right); +#define EINA_RBTREE_CMP_NODE_CB(Function) ((Eina_Rbtree_Cmp_Node_Cb) Function) + +typedef int (*Eina_Rbtree_Cmp_Key_Cb)(const Eina_Rbtree *node, const void *key, int length); +#define EINA_RBTREE_CMP_KEY_CB(Function) ((Eina_Rbtree_Cmp_Key_Cb) Function) + +EAPI Eina_Rbtree *eina_rbtree_inline_insert(Eina_Rbtree *root, Eina_Rbtree *node, Eina_Rbtree_Cmp_Node_Cb cmp); +EAPI Eina_Rbtree *eina_rbtree_inline_remove(Eina_Rbtree *root, Eina_Rbtree *node, Eina_Rbtree_Cmp_Node_Cb cmp); +EAPI Eina_Rbtree *eina_rbtree_inline_lookup(Eina_Rbtree *root, const void *key, int length, Eina_Rbtree_Cmp_Key_Cb cmp); + +EAPI Eina_Iterator *eina_rbtree_iterator_prefix(const Eina_Rbtree *root); +EAPI Eina_Iterator *eina_rbtree_iterator_infix(const Eina_Rbtree *root); +EAPI Eina_Iterator *eina_rbtree_iterator_postfix(const Eina_Rbtree *root); + +#endif diff --git a/legacy/eina/src/lib/Makefile.am b/legacy/eina/src/lib/Makefile.am index c232145c7e..a360cb2ec4 100644 --- a/legacy/eina/src/lib/Makefile.am +++ b/legacy/eina/src/lib/Makefile.am @@ -26,6 +26,7 @@ eina_counter.c \ eina_iterator.c \ eina_accessor.c \ eina_convert.c \ +eina_rbtree.c \ eina_stringshare.c libeina_la_LIBADD = -ldl -lrt @COVERAGE_LIBS@ -lm diff --git a/legacy/eina/src/lib/eina_rbtree.c b/legacy/eina/src/lib/eina_rbtree.c new file mode 100644 index 0000000000..60f02a04fe --- /dev/null +++ b/legacy/eina/src/lib/eina_rbtree.c @@ -0,0 +1,444 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#include +#include +#include + +#include "eina_rbtree.h" +#include "eina_array.h" +#include "eina_private.h" + +/*============================================================================* + * Local * + *============================================================================*/ +#define EINA_RBTREE_ITERATOR_PREFIX_MASK 0x1 +#define EINA_RBTREE_ITERATOR_INFIX_MASK 0x2 +#define EINA_RBTREE_ITERATOR_POSTFIX_MASK 0x4 + +typedef struct _Eina_Iterator_Rbtree Eina_Iterator_Rbtree; +typedef struct _Eina_Iterator_Rbtree_List Eina_Iterator_Rbtree_List; + +struct _Eina_Iterator_Rbtree +{ + Eina_Iterator iterator; + + Eina_Array *stack; + + unsigned char mask; +}; + +struct _Eina_Iterator_Rbtree_List +{ + Eina_Rbtree *tree; + + Eina_Rbtree_Direction dir : 1; + Eina_Bool up : 1; +}; + +static Eina_Iterator_Rbtree_List * +_eina_rbtree_iterator_list_new(const Eina_Rbtree *tree) +{ + Eina_Iterator_Rbtree_List *new; + + new = malloc(sizeof (Eina_Iterator_Rbtree_List)); + if (!new) return NULL; + + new->tree = (Eina_Rbtree*) tree; + new->dir = EINA_RBTREE_RIGHT; + new->up = EINA_FALSE; + + return new; +} + +static Eina_Rbtree * +_eina_rbtree_iterator_get_content(Eina_Iterator_Rbtree *it) +{ + if (eina_array_count(it->stack) <= 0) return NULL; + return eina_array_get(it->stack, eina_array_count(it->stack) - 1); +} + +static void +_eina_rbtree_iterator_free(Eina_Iterator_Rbtree *it) +{ + Eina_Iterator_Rbtree_List *item; + Eina_Array_Iterator et; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(it->stack, i, item, et) + free(item); + + eina_array_free(it->stack); + free(it); +} + +static Eina_Bool +_eina_rbtree_iterator_next(Eina_Iterator_Rbtree *it, void **data) +{ + Eina_Iterator_Rbtree_List *last; + Eina_Iterator_Rbtree_List *new; + Eina_Rbtree *tree; + + if (eina_array_count(it->stack) <= 0) return EINA_FALSE; + + last = eina_array_get(it->stack, eina_array_count(it->stack) - 1); + tree = last->tree; + + if (last->tree == NULL || last->up == EINA_TRUE) + { + last = eina_array_pop(it->stack); + while (last->dir == EINA_RBTREE_LEFT + || last->tree == NULL) + { + if (tree) + if ((it->mask & EINA_RBTREE_ITERATOR_POSTFIX_MASK) == EINA_RBTREE_ITERATOR_POSTFIX_MASK) + { + free(last); + + if (eina_array_count(it->stack) > 0) + { + last = eina_array_get(it->stack, eina_array_count(it->stack) - 1); + last->up = EINA_TRUE; + } + + goto onfix; + } + + free(last); + + last = eina_array_pop(it->stack); + if (!last) return EINA_FALSE; + tree = last->tree; + } + + last->dir = EINA_RBTREE_LEFT; + last->up = EINA_FALSE; + + eina_array_push(it->stack, last); + + if ((it->mask & EINA_RBTREE_ITERATOR_INFIX_MASK) == EINA_RBTREE_ITERATOR_INFIX_MASK) + goto onfix; + } + + new = _eina_rbtree_iterator_list_new(last->tree->son[last->dir]); + if (!new) return EINA_FALSE; + eina_array_push(it->stack, new); + + if (last->dir == EINA_RBTREE_RIGHT) + if ((it->mask & EINA_RBTREE_ITERATOR_PREFIX_MASK) == EINA_RBTREE_ITERATOR_PREFIX_MASK) + goto onfix; + + return _eina_rbtree_iterator_next(it, data); + + onfix: + if (data) *data = tree; + return EINA_TRUE; +} + +static Eina_Iterator * +_eina_rbtree_iterator_build(const Eina_Rbtree *root, unsigned char mask) +{ + Eina_Iterator_Rbtree_List *first; + Eina_Iterator_Rbtree *it; + + if (!root) return NULL; + + it = calloc(1, sizeof (Eina_Iterator_Rbtree)); + if (!it) return NULL; + + it->stack = eina_array_new(8); + if (!it->stack) goto on_error; + + first = _eina_rbtree_iterator_list_new(root); + if (!first) goto on_error; + eina_array_push(it->stack, first); + + it->mask = mask; + + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_rbtree_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_rbtree_iterator_get_content); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_rbtree_iterator_free); + + return &it->iterator; + + on_error: + if (it && it->stack) eina_array_free(it->stack); + if (it) free(it); + + return NULL; +} + +/* + * Thanks to Julienne Walker public domain tutorial. + * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx + */ + +static void +_eina_rbtree_node_init(Eina_Rbtree *node) +{ + if (!node) return ; + + node->son[0] = NULL; + node->son[1] = NULL; + + node->color = EINA_RBTREE_RED; +} + +static inline Eina_Bool +_eina_rbtree_is_red(Eina_Rbtree *node) +{ + return node != NULL && node->color == EINA_RBTREE_RED; +} + +static inline Eina_Rbtree * +_eina_rbtree_inline_single_rotation(Eina_Rbtree *node, Eina_Rbtree_Direction dir) +{ + Eina_Rbtree *save = node->son[!dir]; + + node->son[!dir] = save->son[dir]; + save->son[dir] = node; + + node->color = EINA_RBTREE_RED; + save->color = EINA_RBTREE_BLACK; + + return save; +} + +static inline Eina_Rbtree * +_eina_rbtree_inline_double_rotation(Eina_Rbtree *node, Eina_Rbtree_Direction dir) +{ + node->son[!dir] = _eina_rbtree_inline_single_rotation(node->son[!dir], !dir); + return _eina_rbtree_inline_single_rotation(node, dir); +} + +/*============================================================================* + * Global * + *============================================================================*/ +/*============================================================================* + * API * + *============================================================================*/ +EAPI Eina_Rbtree * +eina_rbtree_inline_insert(Eina_Rbtree *root, Eina_Rbtree *node, Eina_Rbtree_Cmp_Node_Cb cmp) +{ + Eina_Rbtree head; + Eina_Rbtree *g, *t; /* Grandparent & parent */ + Eina_Rbtree *p, *q; /* Iterator & parent */ + Eina_Rbtree_Direction dir, last; + + if (!node) return root; + + _eina_rbtree_node_init(node); + + if (!root) + { + root = node; + goto end_add; + } + + memset(&head, 0, sizeof (Eina_Rbtree)); + dir = EINA_RBTREE_LEFT; + + /* Set up helpers */ + t = &head; + g = p = NULL; + q = t->son[1] = root; + + /* Search down the tree */ + for (;;) + { + if (q == NULL) + { + /* Insert new node at the bottom */ + p->son[dir] = q = node; + } + else if (_eina_rbtree_is_red(q->son[0]) + && _eina_rbtree_is_red(q->son[1])) + { + /* Color flip */ + q->color = EINA_RBTREE_RED; + q->son[0]->color = EINA_RBTREE_BLACK; + q->son[1]->color = EINA_RBTREE_BLACK; + } + + /* Fix red violation */ + if (_eina_rbtree_is_red(q) && _eina_rbtree_is_red(p)) + { + Eina_Rbtree_Direction dir2; + + dir2 = (t->son[1] == g) ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT; + + if (q == p->son[last]) + t->son[dir2] = _eina_rbtree_inline_single_rotation(g, !last); + else + t->son[dir2] = _eina_rbtree_inline_double_rotation(g, !last); + } + + /* Stop if found */ + if (q == node) + break; + + last = dir; + dir = cmp(q, node); + + /* Update helpers */ + if ( g != NULL ) + t = g; + g = p, p = q; + q = q->son[dir]; + } + + root = head.son[1]; + + end_add: + /* Make root black */ + root->color = EINA_RBTREE_BLACK; + + return root; +} + +EAPI Eina_Rbtree * +eina_rbtree_inline_remove(Eina_Rbtree *root, Eina_Rbtree *node, Eina_Rbtree_Cmp_Node_Cb cmp) +{ + Eina_Rbtree head; + Eina_Rbtree *q, *p, *g; + Eina_Rbtree *f = NULL; + Eina_Rbtree_Direction dir; + + if (!root || !node) return root; + + memset(&head, 0, sizeof(Eina_Rbtree)); + + dir = EINA_RBTREE_RIGHT; + q = &head; + g = p = NULL; + q->son[EINA_RBTREE_RIGHT] = root; + + /* Search and push a red down */ + while (q->son[dir] != NULL) + { + Eina_Rbtree_Direction last = dir; + + /* Update helpers */ + g = p; p = q; + q = q->son[dir]; + dir = cmp(q, node); + + /* Save parent node found */ + if (q == node) + f = p; + + /* Push the red node down */ + if (!_eina_rbtree_is_red(q) + && !_eina_rbtree_is_red(q->son[dir])) + { + if (_eina_rbtree_is_red(q->son[!dir])) + q = p->son[last] = _eina_rbtree_inline_single_rotation(q, dir); + else if (!_eina_rbtree_is_red(q->son[!dir])) { + Eina_Rbtree *s = p->son[!last]; + + if (s != NULL) + { + if (!_eina_rbtree_is_red(s->son[!last]) + && !_eina_rbtree_is_red(s->son[last])) + { + /* Color flip */ + p->color = EINA_RBTREE_BLACK; + p->son[0]->color = EINA_RBTREE_RED; + p->son[1]->color = EINA_RBTREE_RED; + } + else + { + Eina_Rbtree_Direction dir2; + + dir2 = g->son[1] == p ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT; + + if (_eina_rbtree_is_red(s->son[last])) + g->son[dir2] = _eina_rbtree_inline_double_rotation(g->son[dir2], last); + else if (_eina_rbtree_is_red(s->son[!last])) + g->son[dir2] = _eina_rbtree_inline_single_rotation(g->son[dir2], last); + + /* Ensure correct coloring */ + q->color = g->son[dir2]->color = EINA_RBTREE_RED; + g->son[dir2]->son[EINA_RBTREE_LEFT]->color = EINA_RBTREE_BLACK; + g->son[dir2]->son[EINA_RBTREE_RIGHT]->color = EINA_RBTREE_BLACK; + } + } + } + } + } + + /* Replace and remove if found */ + if (f != NULL) + { + /* 'q' should take the place of 'node' parent */ + f->son[f->son[1] == node] = q; + + /* Switch the link from the parent to q's son */ + p->son[p->son[1] == q] = q->son[q->son[0] == NULL]; + + /* Put q at the place of node */ + q->son[0] = node->son[0]; + q->son[1] = node->son[1]; + q->color = node->color; + + /* Reset node link */ + node->son[0] = NULL; + node->son[1] = NULL; + } + + root = head.son[1]; + if (root != NULL) + root->color = EINA_RBTREE_BLACK; + + return root; +} + +EAPI Eina_Rbtree * +eina_rbtree_inline_lookup(Eina_Rbtree *root, const void *key, int length, Eina_Rbtree_Cmp_Key_Cb cmp) +{ + int result; + + while (root) + { + result = cmp(root, key, length); + if (result == 0) return root; + + root = root->son[result < 0 ? 0 : 1]; + } + + return NULL; +} + +EAPI Eina_Iterator * +eina_rbtree_iterator_prefix(const Eina_Rbtree *root) +{ + return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_PREFIX_MASK); +} + +EAPI Eina_Iterator * +eina_rbtree_iterator_infix(const Eina_Rbtree *root) +{ + return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_INFIX_MASK); +} + +EAPI Eina_Iterator * +eina_rbtree_iterator_postfix(const Eina_Rbtree *root) +{ + return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_POSTFIX_MASK); +} + diff --git a/legacy/eina/src/tests/eina_suite.c b/legacy/eina/src/tests/eina_suite.c index c58159af0b..f0073c365a 100644 --- a/legacy/eina/src/tests/eina_suite.c +++ b/legacy/eina/src/tests/eina_suite.c @@ -40,6 +40,7 @@ static const Eina_Test_Case etc[] = { { "Accessor", eina_test_accessor }, { "Module", eina_test_module }, { "Convert", eina_test_convert }, +/* { "Rbtree", eina_test_rbtree }, */ { NULL, NULL } }; diff --git a/legacy/eina/src/tests/eina_suite.h b/legacy/eina/src/tests/eina_suite.h index 97ee12dd4f..173542b256 100644 --- a/legacy/eina/src/tests/eina_suite.h +++ b/legacy/eina/src/tests/eina_suite.h @@ -39,5 +39,6 @@ void eina_test_iterator(TCase *tc); void eina_test_accessor(TCase *tc); void eina_test_module(TCase *tc); void eina_test_convert(TCase *tc); +void eina_test_rbtree(TCase *tc); #endif /* EINA_SUITE_H_ */ diff --git a/legacy/eina/src/tests/eina_test_iterator.c b/legacy/eina/src/tests/eina_test_iterator.c index e1b7ee4f82..074b03fb35 100644 --- a/legacy/eina/src/tests/eina_test_iterator.c +++ b/legacy/eina/src/tests/eina_test_iterator.c @@ -27,6 +27,7 @@ #include "eina_hash.h" #include "eina_inlist.h" #include "eina_list.h" +#include "eina_rbtree.h" #include "eina_private.h" static Eina_Bool @@ -270,6 +271,132 @@ START_TEST(eina_iterator_list_simple) } END_TEST +typedef struct _Eina_Rbtree_Int Eina_Rbtree_Int; +struct _Eina_Rbtree_Int +{ + Eina_Rbtree node; + int value; +}; + +static Eina_Rbtree_Direction +eina_rbtree_int_cmp(const Eina_Rbtree_Int *left, const Eina_Rbtree_Int *right) +{ + if (!left) return EINA_RBTREE_RIGHT; + if (!right) return EINA_RBTREE_LEFT; + + if (left->value < right->value) return EINA_RBTREE_LEFT; + return EINA_RBTREE_RIGHT; +} + +static Eina_Rbtree * +_eina_rbtree_int_new(int value) +{ + Eina_Rbtree_Int *it; + + it = malloc(sizeof (Eina_Rbtree_Int)); + fail_if(!it); + + it->value = value; + + return &it->node; +} + +static Eina_Bool +eina_iterator_rbtree_data_check_sorted(__UNUSED__ const Eina_List *list, Eina_Rbtree_Int *data, int *fdata) +{ + switch (*fdata) + { + case 0: fail_if(data->value != 10); break; + case 1: fail_if(data->value != 27); break; + case 2: fail_if(data->value != 42); break; + case 3: fail_if(data->value != 69); break; + case 4: fail_if(data->value != 1337); break; + } + + (*fdata)++; + + return EINA_TRUE; +} + +static Eina_Bool +eina_iterator_rbtree_data_check_prefix(__UNUSED__ const Eina_List *list, Eina_Rbtree_Int *data, int *fdata) +{ + switch (*fdata) + { + case 0: fail_if(data->value != 27); break; + case 1: fail_if(data->value != 10); break; + case 2: fail_if(data->value != 69); break; + case 3: fail_if(data->value != 42); break; + case 4: fail_if(data->value != 1337); break; + } + + (*fdata)++; + + return EINA_TRUE; +} + +static Eina_Bool +eina_iterator_rbtree_data_check_postfix(__UNUSED__ const Eina_List *list, Eina_Rbtree_Int *data, int *fdata) +{ + switch (*fdata) + { + case 0: fail_if(data->value != 10); break; + case 1: fail_if(data->value != 42); break; + case 2: fail_if(data->value != 1337); break; + case 3: fail_if(data->value != 69); break; + case 4: fail_if(data->value != 27); break; + } + + (*fdata)++; + + return EINA_TRUE; +} + +START_TEST(eina_iterator_rbtree_simple) +{ + Eina_Rbtree *root = NULL; + Eina_Iterator *it; + int i; + + root = eina_rbtree_inline_insert(NULL, _eina_rbtree_int_new(10), EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + fail_if(!root); + + root = eina_rbtree_inline_insert(root, _eina_rbtree_int_new(1337), EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + fail_if(!root); + + root = eina_rbtree_inline_insert(root, _eina_rbtree_int_new(27), EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + fail_if(!root); + + root = eina_rbtree_inline_insert(root, _eina_rbtree_int_new(69), EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + fail_if(!root); + + root = eina_rbtree_inline_insert(root, _eina_rbtree_int_new(42), EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + fail_if(!root); + + i = 0; + it = eina_rbtree_iterator_prefix(root); + fail_if(!it); + + eina_iterator_foreach(it, EINA_EACH(eina_iterator_rbtree_data_check_prefix), &i); + eina_iterator_free(it); + + /* This will return the item sorted. */ + i = 0; + it = eina_rbtree_iterator_infix(root); + fail_if(!it); + + eina_iterator_foreach(it, EINA_EACH(eina_iterator_rbtree_data_check_sorted), &i); + eina_iterator_free(it); + + i = 0; + it = eina_rbtree_iterator_postfix(root); + fail_if(!it); + + eina_iterator_foreach(it, EINA_EACH(eina_iterator_rbtree_data_check_postfix), &i); + eina_iterator_free(it); +} +END_TEST + void eina_test_iterator(TCase *tc) { @@ -277,4 +404,5 @@ eina_test_iterator(TCase *tc) tcase_add_test(tc, eina_iterator_hash_simple); tcase_add_test(tc, eina_iterator_inlist_simple); tcase_add_test(tc, eina_iterator_list_simple); + tcase_add_test(tc, eina_iterator_rbtree_simple); } diff --git a/legacy/eina/src/tests/eina_test_rbtree.c b/legacy/eina/src/tests/eina_test_rbtree.c new file mode 100644 index 0000000000..1d7b58dafe --- /dev/null +++ b/legacy/eina/src/tests/eina_test_rbtree.c @@ -0,0 +1,198 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "eina_array.h" +#include "eina_suite.h" +#include "eina_rbtree.h" + +static inline Eina_Bool +_eina_rbtree_is_red(Eina_Rbtree *tree) +{ + return tree != NULL && tree->color == EINA_RBTREE_RED; +} + +static int +_eina_rbtree_black_height(Eina_Rbtree *tree, Eina_Rbtree_Cmp_Node_Cb cmp) +{ + Eina_Rbtree *left; + Eina_Rbtree *right; + Eina_Rbtree_Direction dir; + int left_height; + int right_height; + + if (!tree) return 1; + + left = tree->son[EINA_RBTREE_LEFT]; + right = tree->son[EINA_RBTREE_RIGHT]; + + /* Consecutive red links. */ + fail_if(_eina_rbtree_is_red(tree) && (_eina_rbtree_is_red(left) || _eina_rbtree_is_red(right))); + + left_height = _eina_rbtree_black_height(left, cmp); + right_height = _eina_rbtree_black_height(right, cmp); + + /* Check binary search tree. */ + if (left) + { + dir = cmp(tree, left); + fail_if(dir != EINA_RBTREE_LEFT); + } + + if (right) + { + dir = cmp(tree, right); + fail_if(dir != EINA_RBTREE_RIGHT); + } + + /* Check black height */ + if (left_height != right_height) + fprintf(stderr, "%i != %i\n", left_height, right_height); + fail_if(left_height != right_height); + + return _eina_rbtree_is_red(tree) ? left_height : left_height + 1; +} + +typedef struct _Eina_Rbtree_Int Eina_Rbtree_Int; +struct _Eina_Rbtree_Int +{ + Eina_Rbtree node; + int value; +}; + +static Eina_Rbtree_Direction +eina_rbtree_int_cmp(const Eina_Rbtree_Int *left, const Eina_Rbtree_Int *right) +{ + if (!left) return EINA_RBTREE_RIGHT; + if (!right) return EINA_RBTREE_LEFT; + + if (left->value < right->value) return EINA_RBTREE_LEFT; + return EINA_RBTREE_RIGHT; +} + +static int +eina_rbtree_int_key(const Eina_Rbtree_Int *node, const int *key, __UNUSED__ int length) +{ + if (!node) return 1; + return node->value - *key; +} + +static Eina_Rbtree_Int * +_eina_rbtree_int_new(int value) +{ + Eina_Rbtree_Int *it; + + it = malloc(sizeof (Eina_Rbtree_Int)); + fail_if(!it); + + it->value = value; + + return it; +} + +START_TEST(eina_rbtree_insertion) +{ + Eina_Rbtree_Int *root = NULL; + Eina_Rbtree_Int *item; + int i; + + srand(time(NULL)); + + for (i = 0; i < 500; ++i) + { + item = _eina_rbtree_int_new(rand()); + root = (Eina_Rbtree_Int*) eina_rbtree_inline_insert(&root->node, &item->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + } + + _eina_rbtree_black_height(&root->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); +} +END_TEST + +START_TEST(eina_rbtree_lookup) +{ + Eina_Rbtree_Int *root = NULL; + Eina_Rbtree_Int *item; + int list[] = { 50, 100, 10, 43, 23 }; + unsigned int i; + + for (i = 0; i < sizeof (list) / sizeof (int); ++i) + { + item = _eina_rbtree_int_new(list[i]); + root = (Eina_Rbtree_Int*) eina_rbtree_inline_insert(&root->node, &item->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + } + + item = (Eina_Rbtree_Int*) eina_rbtree_inline_lookup(&root->node, &list[0], sizeof(int), EINA_RBTREE_CMP_KEY_CB(eina_rbtree_int_key)); + fail_if(!item); + + i = 42; + item = (Eina_Rbtree_Int*) eina_rbtree_inline_lookup(&root->node, &i, sizeof(int), EINA_RBTREE_CMP_KEY_CB(eina_rbtree_int_key)); + fail_if(item); +} +END_TEST + +START_TEST(eina_rbtree_remove) +{ + Eina_Rbtree_Int *root = NULL; + Eina_Rbtree_Int *item; + Eina_Array *ea; + Eina_Array_Iterator it; + unsigned int i; + + eina_array_init(); + + ea = eina_array_new(11); + fail_if(!ea); + + srand(time(NULL)); + + for (i = 0; i < 100; ++i) + { + item = _eina_rbtree_int_new(rand()); + eina_array_push(ea, item); + root = (Eina_Rbtree_Int*) eina_rbtree_inline_insert(&root->node, &item->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + } + + _eina_rbtree_black_height(&root->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + + EINA_ARRAY_ITER_NEXT(ea, i, item, it) + { + root = (Eina_Rbtree_Int*) eina_rbtree_inline_remove(&root->node, &item->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + _eina_rbtree_black_height(&root->node, EINA_RBTREE_CMP_NODE_CB(eina_rbtree_int_cmp)); + } + + fail_if(root != NULL); + + eina_array_shutdown(); +} +END_TEST + +void +eina_test_rbtree(TCase *tc) +{ + tcase_add_test(tc, eina_rbtree_insertion); + tcase_add_test(tc, eina_rbtree_lookup); + tcase_add_test(tc, eina_rbtree_remove); +} +