From 73687215b4d97828412a579cc5757d1b14bf5732 Mon Sep 17 00:00:00 2001 From: Dan Staples Date: Thu, 23 Oct 2014 20:14:08 -0400 Subject: [PATCH 1/6] added re-entrant function for iterating through keys of a tree --- src/tree.c | 39 +++++++++++++++++++++++++++++++++++++++ src/tree.h | 3 +++ 2 files changed, 42 insertions(+) diff --git a/src/tree.c b/src/tree.c index 68daafa..3ca76a3 100644 --- a/src/tree.c +++ b/src/tree.c @@ -869,3 +869,42 @@ co_tree_print_raw(co_obj_t *tree) error: return 0; } + +static co_obj_t * +_co_tree_next_r(_treenode_t *current, _treenode_t *previous) +{ + if (!current) return NULL; + if (!previous) { // first recurse + return _co_tree_next_r(current->parent, current); // go up + } else if (previous && previous == current->parent && current->low) { + return _co_tree_next_r(current->low, current); // go low + } else if (previous && (previous == current->parent || previous == current->low) && current->equal) { + return _co_tree_next_r(current->equal, current); // go equal + } else if (previous && (previous == current->low || previous == current->equal) && current->high) { + return _co_tree_next_r(current->high, current); // go high + } else if (previous && previous == current->parent) { // reached leaf + if (!current->key) ERROR("Reached leaf node without key"); + return current->key; + } else { // return current key or go up + if (current->key) + return current->key; + else if (current->parent) + return _co_tree_next_r(current->parent, current); + } + return NULL; +} + +co_obj_t * +co_tree_next(const co_obj_t *tree, co_obj_t *key) +{ + char *key_str = NULL; + _treenode_t *key_node = NULL; + size_t klen = co_obj_data(&key_str, key); + if(CO_TYPE(tree) == _tree16) { + key_node = co_tree_find_node(((co_tree16_t *)tree)->root, key_str, klen); + } else if (CO_TYPE(tree) == _tree32) { + key_node = co_tree_find_node(((co_tree32_t *)tree)->root, key_str, klen); + } + + return _co_tree_next_r(key_node, NULL); +} \ No newline at end of file diff --git a/src/tree.h b/src/tree.h index 896082f..203ea09 100644 --- a/src/tree.h +++ b/src/tree.h @@ -44,6 +44,7 @@ typedef struct _treenode_t _treenode_t; struct _treenode_t { char splitchar; + _treenode_t *parent; _treenode_t *low; _treenode_t *equal; _treenode_t *high; @@ -229,4 +230,6 @@ int co_tree_print(co_obj_t *tree); */ int co_tree_print_raw(co_obj_t *tree); +co_obj_t *co_tree_next(const co_obj_t *tree, co_obj_t *key); + #endif From fbc4f03ed5f288ef4d3e86b76500b14d22d7bd11 Mon Sep 17 00:00:00 2001 From: Dan Staples Date: Thu, 23 Oct 2014 20:24:01 -0400 Subject: [PATCH 2/6] attach treenodes to their parent --- src/tree.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/tree.c b/src/tree.c index 3ca76a3..2f9c546 100644 --- a/src/tree.c +++ b/src/tree.c @@ -246,32 +246,33 @@ co_tree_delete(co_obj_t *root, const char *key, const size_t klen) } static inline _treenode_t * -_co_tree_insert_r(_treenode_t *root, _treenode_t *current, const char *orig_key, const size_t orig_klen, const char *key, const size_t klen, co_obj_t *value, bool safe) +_co_tree_insert_r(_treenode_t *parent, _treenode_t *current, const char *orig_key, const size_t orig_klen, const char *key, const size_t klen, co_obj_t *value, bool safe) { if (current == NULL) { current = (_treenode_t *) h_calloc(1, sizeof(_treenode_t)); - if(root == NULL) + if(parent == NULL) { - root = current; + parent = current; } else { - hattach(current, root); + current->parent = parent; + hattach(current, parent); } current->splitchar = *key; } if (*key < current->splitchar) { - current->low = _co_tree_insert_r(root, current->low, orig_key, orig_klen, key, klen, value, safe); + current->low = _co_tree_insert_r(current, current->low, orig_key, orig_klen, key, klen, value, safe); } else if (*key == current->splitchar) { if (klen > 1) { // not done yet, keep going but one less - current->equal = _co_tree_insert_r(root, current->equal, orig_key, orig_klen, key+1, klen - 1, value, safe); + current->equal = _co_tree_insert_r(current, current->equal, orig_key, orig_klen, key+1, klen - 1, value, safe); } else { @@ -296,7 +297,7 @@ _co_tree_insert_r(_treenode_t *root, _treenode_t *current, const char *orig_key, } else { - current->high = _co_tree_insert_r(root, current->high, orig_key, orig_klen, key, klen, value, safe); + current->high = _co_tree_insert_r(current, current->high, orig_key, orig_klen, key, klen, value, safe); } return current; @@ -900,11 +901,7 @@ co_tree_next(const co_obj_t *tree, co_obj_t *key) char *key_str = NULL; _treenode_t *key_node = NULL; size_t klen = co_obj_data(&key_str, key); - if(CO_TYPE(tree) == _tree16) { - key_node = co_tree_find_node(((co_tree16_t *)tree)->root, key_str, klen); - } else if (CO_TYPE(tree) == _tree32) { - key_node = co_tree_find_node(((co_tree32_t *)tree)->root, key_str, klen); - } + key_node = co_tree_find_node(co_tree_root(tree), key_str, klen); return _co_tree_next_r(key_node, NULL); } \ No newline at end of file From 7d18bfa0760641c21d0fbae8f7c24ab5ae98593b Mon Sep 17 00:00:00 2001 From: Dan Staples Date: Fri, 24 Oct 2014 11:28:15 -0400 Subject: [PATCH 3/6] fixed co_tree_next algorithm --- src/tree.c | 61 +++++++++++++++++++++++++++++++++--------------------- src/tree.h | 5 +++++ 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/tree.c b/src/tree.c index 2f9c546..ff445b4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -872,36 +872,49 @@ co_tree_print_raw(co_obj_t *tree) } static co_obj_t * -_co_tree_next_r(_treenode_t *current, _treenode_t *previous) +_co_tree_next_r(_treenode_t *root, _treenode_t *current, _treenode_t *previous) { if (!current) return NULL; - if (!previous) { // first recurse - return _co_tree_next_r(current->parent, current); // go up - } else if (previous && previous == current->parent && current->low) { - return _co_tree_next_r(current->low, current); // go low - } else if (previous && (previous == current->parent || previous == current->low) && current->equal) { - return _co_tree_next_r(current->equal, current); // go equal - } else if (previous && (previous == current->low || previous == current->equal) && current->high) { - return _co_tree_next_r(current->high, current); // go high - } else if (previous && previous == current->parent) { // reached leaf - if (!current->key) ERROR("Reached leaf node without key"); - return current->key; - } else { // return current key or go up - if (current->key) - return current->key; - else if (current->parent) - return _co_tree_next_r(current->parent, current); - } - return NULL; + _treenode_t *parent = current->parent, + *low = current->low, + *equal = current->equal, + *high = current->high; + co_obj_t *key = current->key; + + if (previous && key && (previous == parent || (previous == low && parent == root))) + return key; + else if (low && (!previous || (previous && previous == parent))) + return _co_tree_next_r(root, low, current); // go low + else if (equal && (!previous || (previous && (previous == parent || previous == low)))) + return _co_tree_next_r(root, equal, current); // go equal + else if (high && previous && previous == equal) + return _co_tree_next_r(root, high, current); // go high + else if (parent) + return _co_tree_next_r(root, parent, current); // go up + else + return key; // if NULL, tree walk has completed } co_obj_t * co_tree_next(const co_obj_t *tree, co_obj_t *key) { - char *key_str = NULL; - _treenode_t *key_node = NULL; - size_t klen = co_obj_data(&key_str, key); - key_node = co_tree_find_node(co_tree_root(tree), key_str, klen); + if (!IS_TREE(tree)) { + ERROR("Invalid tree"); + return NULL; + } + + _treenode_t *key_node = NULL, + *root = co_tree_root(tree); - return _co_tree_next_r(key_node, NULL); + if (key) { + char *key_str = NULL; + size_t klen = co_obj_data(&key_str, key); + key_node = co_tree_find_node(root, key_str, klen); + } else { + key_node = root; + } + + co_obj_t *ret = _co_tree_next_r(root, key_node, NULL); + if (ret != key) return ret; + return NULL; } \ No newline at end of file diff --git a/src/tree.h b/src/tree.h index 203ea09..7282e59 100644 --- a/src/tree.h +++ b/src/tree.h @@ -230,6 +230,11 @@ int co_tree_print(co_obj_t *tree); */ int co_tree_print_raw(co_obj_t *tree); +/** + * @brief get the next key in the tree + * @param tree tree object to search + * @param key previous key returned from co_tree_next + */ co_obj_t *co_tree_next(const co_obj_t *tree, co_obj_t *key); #endif From d2f554898cdbc807a7b5c7b5481052e19ad0bd42 Mon Sep 17 00:00:00 2001 From: Dan Staples Date: Fri, 24 Oct 2014 11:28:27 -0400 Subject: [PATCH 4/6] add co_tree_next unit test --- tests/tree-next.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 tests/tree-next.cpp diff --git a/tests/tree-next.cpp b/tests/tree-next.cpp new file mode 100644 index 0000000..a6886bb --- /dev/null +++ b/tests/tree-next.cpp @@ -0,0 +1,159 @@ +/* vim: set ts=2 expandtab: */ +/** + * @file test.cpp + * @brief + * + * @author Josh King (jheretic), jking@chambana.net + * + * @internal + * Created 11/17/2013 06:01:11 PM + * Compiler gcc/g++ + * Organization The Open Technology Institute + * Copyright Copyright (c) 2013, Josh King + * + * This file is part of Commotion, Copyright (c) 2013, Josh King + * + * Commotion is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * Commotion 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Commotion. If not, see . + * + * ===================================================================================== + */ +extern "C" { +#include "../src/obj.h" +#include "../src/tree.h" +} +#include "gtest/gtest.h" + +class TreeNextTest : public ::testing::Test +{ + protected: + co_obj_t *tree16; + co_obj_t *tree32; + int strs_len = 40; + char *strs[40] = { + "bbbb", + "bbba", + "bbbc", + "bbab", + "bbaa", + "bbac", + "bbcb", + "bbca", + "bbcc", + "abb", + "aba", + "abc", + "aab", + "aaa", + "aac", + "acb", + "aca", + "acc", + "cbb", + "cba", + "cbc", + "cab", + "caa", + "cac", + "ccb", + "cca", + "ccc", + "b", + "bb", + "bba", + "bbb", + "bbc", + "a", + "aa", + "ab", + "ac", + "c", + "ca", + "cb", + "cc" + }; + + //tests + void Test(); + + TreeNextTest() + { + tree16 = co_tree16_create(); + tree32 = co_tree32_create(); + + for (int i = 0; i < strs_len; i++) { + co_obj_t *str16 = co_str8_create(strs[i],strlen(strs[i]) + 1,0), + *str32 = co_str8_create(strs[i],strlen(strs[i]) + 1,0); + co_tree_insert(tree16,strs[i],strlen(strs[i]) + 1,str16); + co_tree_insert(tree32,strs[i],strlen(strs[i]) + 1,str32); + } + } + + virtual void SetUp() + { + } + + ~TreeNextTest() + { + co_obj_free(tree16); + co_obj_free(tree32); + } +}; + +/** + * Compare strings alphabetically, used in qsort + */ +static int +cmpstringp(const void *p1, const void *p2) +{ + /* The actual arguments to this function are "pointers to + * pointers to char", but strcmp(3) arguments are "pointers + * to char", hence the following cast plus dereference */ + return strcmp(* (char * const *) p1, * (char * const *) p2); +} + +void TreeNextTest::Test() +{ + co_obj_t *key = NULL; + int i = 0; + + /* Sort test strings into alphabetical order */ + qsort(strs,strs_len,sizeof(char*),cmpstringp); + + for (key = co_tree_next(tree16, NULL), i = 0; i < strs_len; key = co_tree_next(tree16, key), i++) { + if (!key) + co_tree_print(tree16); + ASSERT_TRUE(key); + EXPECT_STREQ(co_obj_data_ptr(key), strs[i]); + co_obj_t *val = co_tree_find(tree16, strs[i], strlen(strs[i]) + 1); + ASSERT_TRUE(val); + EXPECT_STREQ(co_obj_data_ptr(val), strs[i]); + } + EXPECT_EQ(i,strs_len); + + for (key = co_tree_next(tree32, NULL), i = 0; i < strs_len; key = co_tree_next(tree32, key), i++) { + if (!key) + co_tree_print(tree32); + ASSERT_TRUE(key); + EXPECT_STREQ(co_obj_data_ptr(key), strs[i]); + co_obj_t *val = co_tree_find(tree32, strs[i], strlen(strs[i]) + 1); + ASSERT_TRUE(val); + EXPECT_STREQ(co_obj_data_ptr(val), strs[i]); + } + EXPECT_EQ(i,strs_len); +} + +TEST_F(TreeNextTest, TreeNextTest) +{ + Test(); +} From b14fe1a82ce4aa014b6f8298678550fef9bda324 Mon Sep 17 00:00:00 2001 From: Dan Staples Date: Fri, 24 Oct 2014 11:29:02 -0400 Subject: [PATCH 5/6] update documentation of co_tree_next --- src/tree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.h b/src/tree.h index 7282e59..f97c810 100644 --- a/src/tree.h +++ b/src/tree.h @@ -231,7 +231,7 @@ int co_tree_print(co_obj_t *tree); int co_tree_print_raw(co_obj_t *tree); /** - * @brief get the next key in the tree + * @brief get the next key in the tree, in alphabetical order * @param tree tree object to search * @param key previous key returned from co_tree_next */ From a61b11b0ecf4d343395f9c1640240cc28af1760e Mon Sep 17 00:00:00 2001 From: Dan Staples Date: Fri, 24 Oct 2014 14:42:12 -0400 Subject: [PATCH 6/6] error checking in co_tree_next --- src/tree.c | 4 ++++ src/tree.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tree.c b/src/tree.c index ff445b4..310d1cb 100644 --- a/src/tree.c +++ b/src/tree.c @@ -910,6 +910,10 @@ co_tree_next(const co_obj_t *tree, co_obj_t *key) char *key_str = NULL; size_t klen = co_obj_data(&key_str, key); key_node = co_tree_find_node(root, key_str, klen); + if (!key_node) { + ERROR("Key not found in tree"); + return NULL; + } } else { key_node = root; } diff --git a/src/tree.h b/src/tree.h index f97c810..eb0bfd4 100644 --- a/src/tree.h +++ b/src/tree.h @@ -233,7 +233,7 @@ int co_tree_print_raw(co_obj_t *tree); /** * @brief get the next key in the tree, in alphabetical order * @param tree tree object to search - * @param key previous key returned from co_tree_next + * @param key previous key returned from co_tree_next, or NULL to get first item */ co_obj_t *co_tree_next(const co_obj_t *tree, co_obj_t *key);