diff --git a/opal/class/Makefile.am b/opal/class/Makefile.am index c7d4783039..1044dd4573 100644 --- a/opal/class/Makefile.am +++ b/opal/class/Makefile.am @@ -24,6 +24,7 @@ headers += \ class/opal_bitmap.h \ class/opal_free_list.h \ class/opal_hash_table.h \ + class/opal_tree.h \ class/opal_list.h \ class/opal_object.h \ class/opal_graph.h\ @@ -36,6 +37,7 @@ libopen_pal_la_SOURCES += \ class/opal_bitmap.c \ class/opal_free_list.c \ class/opal_hash_table.c \ + class/opal_tree.c \ class/opal_list.c \ class/opal_object.c \ class/opal_graph.c\ diff --git a/opal/class/opal_tree.c b/opal/class/opal_tree.c new file mode 100644 index 0000000000..02013501b6 --- /dev/null +++ b/opal/class/opal_tree.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" + +#include "opal/class/opal_tree.h" +#include "opal/constants.h" + +/* + * List classes + */ + +static void opal_tree_item_construct(opal_tree_item_t*); +static void opal_tree_item_destruct(opal_tree_item_t*); + +OBJ_CLASS_INSTANCE( + opal_tree_item_t, + opal_object_t, + opal_tree_item_construct, + opal_tree_item_destruct +); + +static void opal_tree_construct(opal_tree_t*); +static void opal_tree_destruct(opal_tree_t*); + +OBJ_CLASS_INSTANCE( + opal_tree_t, + opal_object_t, + opal_tree_construct, + opal_tree_destruct +); + + +/* + * + * opal_tree_item_t interface + * + */ + +static void opal_tree_item_construct(opal_tree_item_t *item) +{ + item->opal_tree_parent = NULL; + item->opal_tree_num_ancestors = 0; + item->opal_tree_sibling_rank = 0xdeadbeef; + item->opal_tree_next_sibling = item->opal_tree_prev_sibling = NULL; + item->opal_tree_num_children = 0; + item->opal_tree_first_child = item->opal_tree_last_child = NULL; +#if OPAL_ENABLE_DEBUG + item->opal_tree_item_refcount = 0; + item->opal_tree_item_belong_to = NULL; +#endif +} + +static void opal_tree_item_destruct(opal_tree_item_t *item) +{ +#if OPAL_ENABLE_DEBUG + assert( 0 == item->opal_tree_item_refcount ); + assert( NULL == item->opal_tree_item_belong_to ); +#endif /* OPAL_ENABLE_DEBUG */ +} + + +/* + * + * opal_tree_t interface + * + */ + +static void opal_tree_construct(opal_tree_t *tree) +{ +#if OPAL_ENABLE_DEBUG + /* These refcounts should never be used in assertions because they + should never be removed from this list, added to another list, + etc. So set them to sentinel values. */ + + OBJ_CONSTRUCT( &(tree->opal_tree_sentinel), opal_tree_item_t ); + tree->opal_tree_sentinel.opal_tree_item_refcount = 1; + tree->opal_tree_sentinel.opal_tree_item_belong_to = tree; +#endif + tree->opal_tree_sentinel.opal_tree_num_ancestors = -1; + tree->opal_tree_sentinel.opal_tree_container = tree; + tree->opal_tree_sentinel.opal_tree_parent=&tree->opal_tree_sentinel; + tree->opal_tree_sentinel.opal_tree_next_sibling = + &tree->opal_tree_sentinel; + tree->opal_tree_sentinel.opal_tree_prev_sibling = + &tree->opal_tree_sentinel; + tree->opal_tree_sentinel.opal_tree_first_child = &tree->opal_tree_sentinel; + tree->opal_tree_sentinel.opal_tree_last_child = &tree->opal_tree_sentinel; + tree->opal_tree_num_items = 0; + tree->comp = NULL; + tree->serialize = NULL; + tree->deserialize = NULL; +} + +/* + * Reset all the pointers to be NULL -- do not actually destroy + * anything. + */ +static void opal_tree_destruct(opal_tree_t *tree) +{ + opal_tree_construct(tree); +} + +/* + * initialize tree container + */ +void opal_tree_init(opal_tree_t *tree, opal_tree_comp_fn_t comp, + opal_tree_item_serialize_fn_t serialize, + opal_tree_item_deserialize_fn_t deserialize) +{ + tree->comp = comp; + tree->serialize = serialize; + tree->deserialize = deserialize; +} + +/* + * count all the descendants from our level and below + */ +static int count_descendants(opal_tree_item_t* item) +{ + int current_count = 0; + + /* loop over all siblings for descendants to count */ + while (item) { + current_count += count_descendants(opal_tree_get_first_child(item)); + current_count++; /* count ourselves */ + item = opal_tree_get_next_sibling(item); + } + return(current_count); +} + +/* + * get size of tree + */ +size_t opal_tree_get_size(opal_tree_t* tree) +{ +#if OPAL_ENABLE_DEBUG + /* not sure if we really want this running in devel, as it does + * slow things down. Wanted for development of splice / join to + * make sure length was reset properly + */ + size_t check_len = 0; + opal_tree_item_t *root; + + if (!opal_tree_is_empty(tree)) { + /* tree has entries so count up items */ + root = opal_tree_get_root(tree); + check_len = count_descendants(root); + } + + if (check_len != tree->opal_tree_num_items) { + fprintf(stderr," Error :: opal_tree_get_size - opal_tree_num_items does not match actual tree length\n"); + fflush(stderr); + abort(); + } +#endif + + return tree->opal_tree_num_items; +} + +/* + * add item to parent's child list + */ +void opal_tree_add_child(opal_tree_item_t *parent_item, + opal_tree_item_t *new_item) +{ +#if OPAL_ENABLE_DEBUG + /* Spot check: ensure that this item is previously on no lists */ + + assert(0 == new_item->opal_tree_item_refcount); + assert( NULL == new_item->opal_tree_item_belong_to ); +#endif + + new_item->opal_tree_parent = parent_item; + new_item->opal_tree_num_ancestors = parent_item->opal_tree_num_ancestors+1; + if (parent_item->opal_tree_num_children) { + /* append item to end of children and sibling lists */ + new_item->opal_tree_prev_sibling = parent_item->opal_tree_last_child; + parent_item->opal_tree_last_child->opal_tree_next_sibling = new_item; + } else { + /* no children existing on parent */ + parent_item->opal_tree_first_child = + parent_item->opal_tree_last_child = + new_item; + } + parent_item->opal_tree_last_child = new_item; + parent_item->opal_tree_num_children++; + new_item->opal_tree_container = parent_item->opal_tree_container; + new_item->opal_tree_container->opal_tree_num_items++; + +#if OPAL_ENABLE_DEBUG + /* Spot check: ensure this item is only on the list that we just + appended it to */ + + OPAL_THREAD_ADD32( &(new_item->opal_tree_item_refcount), 1 ); + assert(1 == new_item->opal_tree_item_refcount); + new_item->opal_tree_item_belong_to = new_item->opal_tree_container; +#endif +} + +/* + * check to see if item is in tree + */ +static bool item_in_tree(opal_tree_item_t *item, opal_tree_item_t *search_item) +{ + bool result = false; + opal_tree_item_t *first_child; + + while (!result && item) { + /* check for item match */ + result = (item == search_item) ? true : false; + if (!result && (first_child = opal_tree_get_first_child(item))) { + /* search descendants for match */ + result = item_in_tree(first_child, search_item); + } + if (!result) { + /* didn't find match at our node or descending so check sibling */ + item = opal_tree_get_next_sibling(item); + } + } + return(result); +} + +/* + * remove item and all items below it from tree and return it to the caller + */ +opal_tree_item_t *opal_tree_remove_subtree(opal_tree_item_t *item) +{ +#if OPAL_ENABLE_DEBUG + /* validate that item does exist on tree */ + if (NULL != item->opal_tree_container) { + /* we point to a container, check if we can find item in tree */ + if (!item_in_tree(opal_tree_get_root(item->opal_tree_container), item)) { + return(NULL); + } + } else { + return (NULL); + } +#endif + + /* remove from parent */ + if (item->opal_tree_parent->opal_tree_first_child == + item) { + item->opal_tree_parent->opal_tree_first_child = + item->opal_tree_next_sibling; + } + if (item->opal_tree_parent->opal_tree_last_child == + item) { + item->opal_tree_parent->opal_tree_last_child = + item->opal_tree_prev_sibling; + } + item->opal_tree_parent->opal_tree_num_children--; + + /* remove from sibling pointers */ + if (NULL != item->opal_tree_prev_sibling) { + item->opal_tree_prev_sibling->opal_tree_next_sibling= + item->opal_tree_next_sibling; + } + if (NULL != item->opal_tree_next_sibling) { + item->opal_tree_next_sibling->opal_tree_prev_sibling= + item->opal_tree_prev_sibling; + } + item->opal_tree_prev_sibling = NULL; + item->opal_tree_next_sibling = NULL; + + /* adjust items relating to container */ + item->opal_tree_container->opal_tree_num_items -= count_descendants(item); + item->opal_tree_container = NULL; + + return(item); +} + +/* delimeter characters that mark items in a serialized stream */ +static char *start_lvl = "["; +static char *end_lvl = "]"; +static char *end_stream = "E"; + +/* + * add item to opal buffer that represents all items of a sub-tree from the + * item passed in on down. We exit out of converting tree items once we've + * done the last child of the tree_item and we are at depth 1. + */ +static int add_tree_item2buf(opal_tree_item_t *tree_item, + opal_buffer_t *buf, + opal_tree_item_serialize_fn_t fn, + int depth + ) +{ + opal_tree_item_t *first_child; + int rc; + + do { + /* add start delim to buffer */ + if (OPAL_SUCCESS != + (rc = opal_dss.pack(buf, &start_lvl, 1, OPAL_STRING))){ + return(rc); + } + /* add item to opal buffer from class creator */ + fn(tree_item, buf); + + if ((first_child = opal_tree_get_first_child(tree_item))) { + /* add items for our children */ + if (OPAL_SUCCESS != + (rc = add_tree_item2buf(first_child, buf, fn, depth+1))){ + return(rc); + } + if (OPAL_SUCCESS != + (rc = opal_dss.pack(buf, &end_lvl, 1, OPAL_STRING))){ + return(rc); + } + } else { + /* end item entry */ + if (OPAL_SUCCESS != + (rc = opal_dss.pack(buf, &end_lvl, 1, OPAL_STRING))){ + return(rc); + } + } + + /* advance to next sibling, if none we'll drop out of + * loop and return to our parent + */ + tree_item = opal_tree_get_next_sibling(tree_item); + } while (tree_item && 1 < depth); + + return(OPAL_SUCCESS); +} + +/* + * serialize tree data + */ +int opal_tree_serialize(opal_tree_item_t *start_item, opal_buffer_t *buffer) +{ + int rc; + + if (OPAL_SUCCESS != + (rc = add_tree_item2buf(start_item, buffer, + start_item->opal_tree_container->serialize, + 1))){ + return(rc); + } + if (OPAL_SUCCESS != + (rc = opal_dss.pack(buffer, &end_stream, 1, OPAL_STRING))){ + return(rc); + } + return(OPAL_SUCCESS); +} + +static int deserialize_add_tree_item(opal_buffer_t *data, + opal_tree_item_t *parent_item, + opal_tree_item_deserialize_fn_t deserialize, + char *curr_delim, + int depth) +{ + int idx = 1, rc; + opal_tree_item_t *new_item = NULL; + int level = 0; /* 0 - one up 1 - curr, 2 - one down */ + + if (!curr_delim) { + if (OPAL_SUCCESS != + (rc = opal_dss.unpack(data, &curr_delim, &idx, OPAL_STRING))) { + return(rc); + } + } + while(curr_delim[0] != end_stream[0]) { + if (curr_delim[0] == start_lvl[0]) { + level++; + } else { + level--; + } + + switch (level) { + case 0: + if (1 < depth) { + /* done with this level go up one level */ + return(OPAL_SUCCESS); + } + break; + case 1: + /* add found child at this level */ + deserialize(data, &new_item); + opal_tree_add_child(parent_item, new_item); + break; + case 2: + /* need to add child one level down */ + deserialize_add_tree_item(data, new_item, deserialize, curr_delim, + depth+1); + level--; + break; + } + if (OPAL_SUCCESS != + (rc = opal_dss.unpack(data, &curr_delim, &idx, OPAL_STRING))) { + return(rc); + } + } + return(OPAL_SUCCESS); +} + +/* + * deserialize tree data + */ +int opal_tree_deserialize(opal_buffer_t *serialized_data, + opal_tree_item_t *start_item) +{ + deserialize_add_tree_item(serialized_data, + start_item, + start_item->opal_tree_container->deserialize, + NULL, + 1); + return OPAL_SUCCESS; +} + +/* + * search myself, descendants and siblings for item matching key + */ +static opal_tree_item_t *find_in_descendants(opal_tree_item_t* item, void *key) +{ + opal_tree_item_t *result = NULL, *first_child; + + while (!result && item) { + /* check for item match */ + result = (item->opal_tree_container->comp(item, key) == 0) ? + item : NULL; + if (!result && (first_child = opal_tree_get_first_child(item))) { + /* search descendants for match */ + result = find_in_descendants(first_child, key); + } + if (!result) { + /* didn't find match at our node or descending so check sibling */ + item = opal_tree_get_next_sibling(item); + } + } + return(result); +} + +/* + * return next tree item that matches key + */ +opal_tree_item_t *opal_tree_find_with(opal_tree_item_t *item, void *key) +{ + opal_tree_item_t *root, *curr_item = item, *result = NULL; + + if (!opal_tree_is_empty(item->opal_tree_container)) { + /* check my descendant for a match */ + result = find_in_descendants(opal_tree_get_first_child(item), key); + + if (!result) { + /* check my siblings for match */ + if (NULL != (curr_item = opal_tree_get_next_sibling(curr_item))) { + result = find_in_descendants(curr_item, key); + } + } + + /* check my ancestors (uncles) for match */ + root = opal_tree_get_root(item->opal_tree_container); + curr_item = item; + while (!result && curr_item && curr_item->opal_tree_num_ancestors > 0){ + curr_item = opal_tree_get_next_sibling(item->opal_tree_parent); + while (NULL == curr_item && + item->opal_tree_parent->opal_tree_num_ancestors > 0) { + item = item->opal_tree_parent; + curr_item = opal_tree_get_next_sibling(item->opal_tree_parent); + } + if (curr_item) { + /* search ancestors descendants for match */ + result = find_in_descendants(curr_item, key); + } + } + } + + return(result); +} diff --git a/opal/class/opal_tree.h b/opal/class/opal_tree.h new file mode 100644 index 0000000000..69953df0b0 --- /dev/null +++ b/opal/class/opal_tree.h @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ +/** + * @file + * + * The opal_tree_t interface is used to provide a generic + * tree list container for Open MPI. It was inspired by the opal_list_t + * interface but instead of organizing items in a doubly-linked list + * fashion, we order them in a finite tree structure. + * + * The general idea is a user creates an class instance that has two + * components. A tree structure component as defined by opal_tree_item_t + * that links all the items together to form the tree. Then there is + * a user specific data component which the user defines what is stored at + * each item. When a user create a type to be used for a OBJ_CLASS_INSTANCE + * it will contain the opal_tree_item_t followed by any user specific + * data. Then the opal_tree_item_t objects can be put in an + * opal_tree_t. Hence, you create a new type that derives from + * opal_tree_item_t; this new type can then be used with opal_tree_t + * containers. + * + * NOTE: opal_tree_item_t instances can only be on \em one tree at a + * time. Specifically, if you add an opal_tree_item_t to one tree, + * and then add it to another tree (without first removing it from the + * first tree), you will effectively be hosing the first tree. You + * have been warned. + * + * If OPAL_ENABLE_DEBUG is true, a bunch of checks occur, including + * some spot checks for a debugging reference count in an attempt to + * ensure that an opal_tree_item_t is only one *one* tree at a time. + * Given the highly concurrent nature of this class, these spot checks + * cannot guarantee that an item is only one tree at a time. + * Specifically, since it is a desirable attribute of this class to + * not use locks for normal operations, it is possible that two + * threads may [erroneously] modify an opal_tree_item_t concurrently. + * + * The only way to guarantee that a debugging reference count is valid + * for the duration of an operation is to lock the item_t during the + * operation. But this fundamentally changes the desirable attribute + * of this class (i.e., no locks). So all we can do is spot-check the + * reference count in a bunch of places and check that it is still the + * value that we think it should be. But this doesn't mean that you + * can run into "unlucky" cases where two threads are concurrently + * modifying an item_t, but all the spot checks still return the + * "right" values. All we can do is hope that we have enough spot + * checks to statistically drive down the possibility of the unlucky + * cases happening. + */ + +#ifndef OPAL_TREE_H +#define OPAL_TREE_H + +#include "opal_config.h" +#include +#include +#include "opal/class/opal_object.h" +#include "opal/dss/dss.h" + +#if OPAL_ENABLE_DEBUG +/* Need atomics for debugging (reference counting) */ +#include "opal/sys/atomic.h" +#include "opal/threads/mutex.h" +#endif + +BEGIN_C_DECLS + +/** + * \internal + * + * The class for the tree container. + */ +OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_tree_t); +/** + * \internal + * + * Base class for items that are put in tree containers. + */ +OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_tree_item_t); + +/** + * \internal + * + * Struct of an opal_tree_item_t + */ +typedef struct opal_tree_item_t +{ + /** Generic parent class for all Open MPI objects */ + opal_object_t super; + /** Pointer to the tree this item belongs to */ + struct opal_tree_t *opal_tree_container; + /* Parent info */ + /** Pointer to parent tree item */ + struct opal_tree_item_t *opal_tree_parent; + /** Depth from the root item in tree */ + unsigned opal_tree_num_ancestors; + /* Logical rank we are compared to other siblings */ + unsigned opal_tree_sibling_rank; + /** Pointer to next item below same parent (or NULL if this is the + leftmost sibling) */ + struct opal_tree_item_t *opal_tree_next_sibling; + /** Pointer to previous item below same parent (or NULL if this is + the rightmost sibling) */ + struct opal_tree_item_t *opal_tree_prev_sibling; + + /* Children info */ + /** Number of children */ + unsigned opal_tree_num_children; + /** Pointer to first child item, or NULL if there are no children */ + struct opal_tree_item_t *opal_tree_first_child; + /** Pointer to last child item, or NULL if there are no children */ + struct opal_tree_item_t *opal_tree_last_child; + +#if OPAL_ENABLE_DEBUG + /** Atomic reference count for debugging */ + int32_t opal_tree_item_refcount; + /** The tree this item belong to */ + struct opal_tree_t* opal_tree_item_belong_to; +#endif +} opal_tree_item_t; + +/** + * Check to see if item's user specific data matches key + * + * @param item - The item we want to check to see if it matches key + * @param key - The opaque key that we want the match function to check + * in the item's user specific data. + * + * @returns 0 - If item's user specific data matches key + * @returns non-zero - If item's user specific data does not match key + * + * This function is implemented by the code that constructs the tree + * and initialized the pointer by the call to opal_tree_init. + * + */ +typedef int (*opal_tree_comp_fn_t)(opal_tree_item_t *item, void *key); + +/** + * The serialize function typedef. This function is called by the + * opal tree serialize code to serialize a tree item's user specific + * data of a class type. + * + * @params item - item to serialize the user specific data from + * @params buffer - the opal_buffer_t to store the serialized data in. + * + * @returns OPAL_SUCCESS - when successfully serialized item + */ +typedef int (*opal_tree_item_serialize_fn_t)(opal_tree_item_t *item, + opal_buffer_t *buffer); + +/** + * The deserialize function typedef. This function is called by the + * opal tree deserialize code to deserialize a tree item's user + * specific data. + * + * @params buffer - the opal_buffer_t to deserialized data. + * @params item - item to store the deserialized data into the user + * specific data + * + * @returns OPAL_SUCCESS - when successfully deserialized item + */ +typedef int (*opal_tree_item_deserialize_fn_t)(opal_buffer_t *buffer, + opal_tree_item_t **item); + +/** + * \internal + * + * Struct of an opal_tree_t + * + */ +typedef struct opal_tree_t +{ + /** Generic parent class for all Open MPI objects */ + opal_object_t super; + /** Guard item of the tree that child points to root */ + opal_tree_item_t opal_tree_sentinel; + /** Quick reference to the number of items in the tree */ + volatile size_t opal_tree_num_items; + /** Function to compare two items in tree */ + opal_tree_comp_fn_t comp; + /** Function to serialize tree item data */ + opal_tree_item_serialize_fn_t serialize; + /** Function to deserialize tree item data */ + opal_tree_item_deserialize_fn_t deserialize; +} opal_tree_t; + +/** Macros to access items in the tree */ + +/** + * Get the parent of item in the tree. + * + * @param item A tree item. + * + * @returns The parent item in the tree + * + * This function is safe to be called with a null item pointer. + */ +static inline opal_tree_item_t *opal_tree_get_parent(opal_tree_item_t *item) +{ + return ((item) ? item->opal_tree_parent : NULL); +} + +/** + * Get the next sibling item in the tree. + * + * @param item A tree item. + * + * @returns The next sibling item in the tree + * + * This function is safe to be called with a null item pointer. + */ +static inline opal_tree_item_t *opal_tree_get_next_sibling(opal_tree_item_t + *item) +{ + return ((item) ? item->opal_tree_next_sibling : NULL); +} + + +/** + * Get the previous sibling item in the tree. + * + * @param item A tree item. + * + * @returns The previous sibling item in the tree + * + * This function is safe to be called with a null item pointer. + */ +static inline opal_tree_item_t *opal_tree_get_prev_sibling(opal_tree_item_t + *item) +{ + return ((item) ? item->opal_tree_prev_sibling : NULL); +} + +/** + * Get the first child item in the tree. + * + * @param item A tree item. + * + * @returns The first child item in the tree + * + * This function is safe to be called with a null item pointer. + * + */ +static inline opal_tree_item_t *opal_tree_get_first_child(opal_tree_item_t + *item) +{ + return ((item) ? item->opal_tree_first_child : NULL); +} + +/** + * Get the last child item in the tree. + * + * @param item A tree item. + * + * @returns The last child item in the tree + * + * This function is safe to be called with a null item pointer. + * + */ +static inline opal_tree_item_t *opal_tree_get_last_child(opal_tree_item_t + *item) +{ + return ((item) ? item->opal_tree_last_child : NULL); +} + +/** + * Check for empty tree + * + * @param tree The tree container + * + * @returns true if tree's size is 0, false otherwise + * + * This is an O(1) operation. + * + * This is an inlined function in compilers that support inlining, + * so it's usually a cheap operation. + * + */ +static inline bool opal_tree_is_empty(opal_tree_t* tree) +{ +#if OPAL_ENABLE_DEBUG + /* Spot check that the tree is a non-null pointer */ + assert(NULL != tree); +#endif + return (tree->opal_tree_sentinel.opal_tree_first_child == + &(tree->opal_tree_sentinel) ? true : false); +} + + +/** + * Return the root item on the tree (does not remove it). + * + * @param tree The tree container + * + * @returns A pointer to the first item in the tree + * + * This is an O(1) operation to return the first item in the tree. + * + * This is an inlined function in compilers that support inlining, so + * it's usually a cheap operation. + * + */ +static inline opal_tree_item_t* opal_tree_get_root(opal_tree_t* tree) +{ + opal_tree_item_t* item; +#if OPAL_ENABLE_DEBUG + assert(NULL != tree); +#endif + item = tree->opal_tree_sentinel.opal_tree_first_child; +#if OPAL_ENABLE_DEBUG + /* Spot check: ensure that the first item is only on one list */ + assert(1 == item->opal_tree_item_refcount); + assert(tree == item->opal_tree_item_belong_to ); +#endif + return item; +} + +/** + * Return the number of items in a tree + * + * @param tree The tree container + * + * @returns The size of the tree (size_t) + * + * This is an O(1) (in non-debug mode) lookup to return the + * size of the list. + */ +OPAL_DECLSPEC size_t opal_tree_get_size(opal_tree_t* tree); + + +/* Functions to manage the tree */ +/** + * Initialize tree container; must be called before using + * the tree. + * + * @param tree The tree to initialize + * @param comp Comparison function to attach to tree. + * @param serialize Serialization function to attach to tree. + * @param deserialize De-serialization function to attach to tree. + * + */ +OPAL_DECLSPEC void opal_tree_init(opal_tree_t *tree, + opal_tree_comp_fn_t comp, + opal_tree_item_serialize_fn_t serialize, + opal_tree_item_deserialize_fn_t deserialize); + +/** + * Add new item as child to its parent item + * + * @param parent_item pointer to what parent the new item belongs to + * @param new_item the item to be added as a child to parent_item + * + * The new_item is added at the end of the child list of the parent_item. + */ +OPAL_DECLSPEC void opal_tree_add_child(opal_tree_item_t *parent_item, + opal_tree_item_t *new_item); + +/** + * Remove an item and everything below from a tree. + * + * @param item The item at the top of subtree to remove + * + * @returns A pointer to the item on the list previous to the one + * that was removed. + * + * This is an O(1) operation to remove an item from the tree. The + * item and all children below it will be removed from the tree. This + * means the item's siblings pointers and potentially the parents first + * and last pointers will be updated to skip over the item. The tree container + * will also have its num_items adjusted to reflect the number of items + * that were removed. The tree item (and all children below it) that is + * returned is now "owned" by the caller -- they are responsible for + * OBJ_RELEASE()'ing it. + * + * With ENABLE_DEBUG on this routine will validate whether the item is actually + * in the tree before doing pointer manipulation. + */ +OPAL_DECLSPEC opal_tree_item_t *opal_tree_remove_subtree(opal_tree_item_t *item); +/** + * Serialize tree data + * + * @param start_item The item of a tree to start serializing data + * @param buffer The opal buffer that contains the serialized + * data stream of the tree + * + * @returns OPAL_SUCCESS if data has been successfully converted. + * + * This routine walks the tree starting at start_item until it has serialized + * all children items of start_item and creates a bytestream of data, + * using the opal_dss.pack routine, that can be sent over a network. + * The format of the bytestream represents the tree parent/child relationship + * of each item in the tree plus the data inside the tree. This routine calls + * the tree's serialization method to serialize the user specific data for + * each item. + * + */ +OPAL_DECLSPEC int opal_tree_serialize(opal_tree_item_t *start_item, + opal_buffer_t *buffer); + +/** + * De-serialize tree data + * + * @param buffer The opal buffer that is to be deserialized + * @param start_item The item in the tree the data should be + * deserialized into + * + * @returns Status of call OPAL_SUCCESS if everything worked + * + * This routine takes a bytestream that was created by the + * opal_tree_serialize() function and deserializes it into the + * tree given. If the tree already has data in it, this routine + * will start adding the new data as a new child of the root + * item. This routine calls the tree's de-serialization + * method to deserialize the user specific data for each item. + * + */ +OPAL_DECLSPEC int opal_tree_deserialize(opal_buffer_t *buffer, + opal_tree_item_t *start_item); + +/* Functions to search for items on tree */ +/** + * Return the next tree item that matches key provided + * + * @param item The item to start the find from + * @param key the key we are wanting to match with + * + * @returns A pointer to the next item that in the tree (starting from item) + * that matches the key based on a depth first search of the tree. A null + * pointer is returned if we've reached the end of the tree and have not + * matched the key. + * + * This routine uses the tree container's comp function to determine the + * whether there is a match between the key and each item we search in the + * tree. This means the actual tree type constructed determines how the + * compare is done with the key. In the case no compare routine is given + * and NULL pointer is always returned for this function. + * + */ +OPAL_DECLSPEC opal_tree_item_t *opal_tree_find_with(opal_tree_item_t *item, + void *key); +END_C_DECLS + +#endif /* OPAL_TREE_H */ diff --git a/test/class/Makefile.am b/test/class/Makefile.am index 752d55d45f..20de5634cc 100644 --- a/test/class/Makefile.am +++ b/test/class/Makefile.am @@ -27,6 +27,7 @@ endif check_PROGRAMS = \ $(REQUIRES_OMPI) opal_bitmap \ opal_hash_table \ + opal_tree \ opal_list \ opal_value_array \ opal_pointer_array @@ -47,6 +48,12 @@ opal_list_LDADD = \ $(top_builddir)/test/support/libsupport.a opal_list_DEPENDENCIES = $(opal_list_LDADD) +opal_tree_SOURCES = opal_tree.c +opal_tree_LDADD = \ + $(top_builddir)/opal/libopen-pal.la \ + $(top_builddir)/test/support/libsupport.a +opal_tree_DEPENDENCIES = $(opal_tree_LDADD) + opal_hash_table_SOURCES = opal_hash_table.c opal_hash_table_LDADD = \ $(top_builddir)/opal/libopen-pal.la \ diff --git a/test/class/opal_tree.c b/test/class/opal_tree.c new file mode 100644 index 0000000000..918436290b --- /dev/null +++ b/test/class/opal_tree.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#include "opal_config.h" +#include + +#include "support.h" +#include "opal/class/opal_tree.h" +#include "opal/runtime/opal.h" +#include "opal/constants.h" + +#include +#include + +/* + * Data type used for testing + */ +typedef struct test_data { + /* tree data structure */ + opal_tree_item_t tree_element; + /* test data */ + size_t data; +} test_data_t; + +OBJ_CLASS_INSTANCE(test_data_t, + opal_tree_item_t, + NULL, NULL); + +static void check_descendants(opal_tree_item_t* item, unsigned *data, + unsigned level, int *err_order, + int *err_ancestor); +static int test_comp(opal_tree_item_t *item, void *key); +static int test_serialize(opal_tree_item_t *item, opal_buffer_t *buffer); +static int test_deserialize(opal_buffer_t *serial_data, + opal_tree_item_t **item); + +int main(int argc, char **argv) +{ + /* local variables */ + opal_tree_t tree, x; + opal_buffer_t *serial_tree; + size_t i, j, tree_size, size_levels, size_elements, total_elements; + int err_order, err_ancestor, err_parent, rc; + unsigned key; + test_data_t *elements; + opal_tree_item_t *item, *rm_item; + + rc = opal_init(&argc, &argv); + test_verify_int(OPAL_SUCCESS, rc); + if (OPAL_SUCCESS != rc) { + test_finalize(); + exit(1); + } + + test_init("opal_tree_t"); + + /* initialize tree */ + + OBJ_CONSTRUCT(&tree, opal_tree_t); + opal_tree_init(&tree, &test_comp, &test_serialize, &test_deserialize); + OBJ_CONSTRUCT(&x, opal_tree_t); + opal_tree_init(&x, &test_comp, &test_serialize, &test_deserialize); + + /* check length of tree */ + tree_size=opal_tree_get_size(&tree); + if( 0 == tree_size ) { + test_success(); + } else { + test_failure(" opal_tree_get_size"); + } + + /* check for empty */ + if (opal_tree_is_empty(&tree)) { + test_success(); + } else { + test_failure(" opal_tree_is_empty(empty tree)"); + } + + /* create test elements */ + size_levels = 4; + size_elements=4; + total_elements = size_elements * size_levels; + elements=(test_data_t *)malloc(sizeof(test_data_t)*total_elements); + assert(elements); + for(i=0 ; i < total_elements; i++) { + OBJ_CONSTRUCT(elements + i, test_data_t); + (elements+i)->data=i; + } + + /* check get_root */ + item = opal_tree_get_root(&tree); + + + /* populate a 4 level tree (this is weighted to the left side) */ + for (i = 0; i < size_levels; i++) { + for(j=0 ; j < size_elements ; j++) { + opal_tree_add_child(item,(opal_tree_item_t *)(elements+ + (i*size_elements)+ + j)); + } + item = opal_tree_get_first_child(item); + } + + /* checking for tree size */ + tree_size=opal_tree_get_size(&tree); + if( tree_size == total_elements ) { + test_success(); + } else { + test_failure(" count off for populating 4 level tree"); + } + + /* checking for empty on non-empty tree */ + if (!opal_tree_is_empty(&tree)) { + test_success(); + } else { + test_failure(" opal_tree_is_empty(non-empty tree)"); + } + + /* check that we have correct tree ordering */ + err_order = 0; + err_ancestor = 0; + err_parent = 0; + if (!opal_tree_is_empty(&tree)) { + item = opal_tree_get_root(&tree); + i = 0; + check_descendants(item, (unsigned *)&i, 0, &err_order, &err_ancestor); + } + + if (!err_order) { + test_success(); + } else { + test_failure(" order values incorrect"); + } + if (!err_ancestor) { + test_success(); + } else { + test_failure(" invalid ancestor count"); + } + + /* test matching code */ + /* check for invalid matching */ + key = 444; + item = opal_tree_find_with(opal_tree_get_root(&tree), (void*)&key); + if (NULL == item) { + test_success(); + } else { + test_failure(" failed invalid matching item test"); + } + + /* check matching, note nest tests because they rely on previous tests */ + /* check for valid matching descendants */ + key = 4; + item = opal_tree_find_with(opal_tree_get_root(&tree), (void*)&key); + if (NULL != item && ((test_data_t*)item)->data == key) { + test_success(); + /* check for valid matching siblings */ + key = 7; + item = opal_tree_find_with(item, (void*)&key); + if (NULL != item && ((test_data_t*)item)->data == key) { + test_success(); + /* check for valid matching ancestors */ + key = 2; + item = opal_tree_find_with(item, (void*)&key); + if (NULL != item && ((test_data_t*)item)->data == key) { + test_success(); + } else { + test_failure(" failed valid matching ancestors test"); + } + } else { + test_failure(" failed valid matching siblings test"); + } + } else { + test_failure(" failed valid matching descendants test"); + } + + /* check subtree removal */ + /* find the first key = 3 item and remove it */ + key = 8; + tree_size=opal_tree_get_size(&tree); + item = opal_tree_find_with(opal_tree_get_root(&tree), (void*)&key); + rm_item = opal_tree_remove_subtree(item); + /* validate the tree count adjusted */ + if (5 != (tree_size - opal_tree_get_size(&tree))) { + test_failure(" failed subtree removal tree size test"); + } else { + /* validate cannot find children in tree */ + key = 13; + if (NULL != + opal_tree_find_with(opal_tree_get_root(&tree), (void*)&key)) { + test_failure(" failed subtree removal item children removed test"); + } else { + /* validate cannot find the item */ + key = 8; + if (NULL != + opal_tree_find_with(opal_tree_get_root(&tree), (void*)&key)) { + test_failure(" failed subtree removal item removed test"); + } else { + test_success(); + } + } + } + + /* check serialization-deserialization */ + /* serialize tree */ + serial_tree = OBJ_NEW(opal_buffer_t); + + if (OPAL_SUCCESS == opal_tree_serialize(opal_tree_get_root(&tree), + serial_tree)) { + opal_tree_t tmp_tree; + opal_buffer_t *serial2_tree; + + /* create new tree */ + OBJ_CONSTRUCT(&tmp_tree, opal_tree_t); + opal_tree_init(&tmp_tree, &test_comp, &test_serialize, + &test_deserialize); + + /* deserialize tree */ + opal_tree_deserialize(serial_tree, &(tmp_tree.opal_tree_sentinel)); + /* serialize tmp tree */ + serial2_tree = OBJ_NEW(opal_buffer_t); + if (OPAL_SUCCESS == opal_tree_serialize(opal_tree_get_root(&tmp_tree), + serial2_tree)) { + void *payload1, *payload2; + int32_t size1, size2; + + /* compare new with original serialization */ + opal_dss.unload(serial_tree, &payload1, &size1); + opal_dss.unload(serial2_tree, &payload2, &size2); + if (size1 == size2) { + if (0 == memcmp(payload1, payload2, size1)) { + test_success(); + } else { + test_failure(" failed tree deserialization data compare"); + } + } else { + test_failure(" failed tree deserialization size compare"); + } + } else { + test_failure(" failed tree second pass serialization"); + } + } else { + test_failure(" failed tree serialization"); + } + + if (NULL != elements) free(elements); + + opal_finalize(); + + return test_finalize(); +} + +/* + * check all the descendants from our level and below for correct data and + * level. Note this will traverse the tree in a weird fashion where you + * go across all siblings and then start searching down the last siblings + * children. As the current tests are set up if one populated more than just + * the left sided children things will probably fail. + */ +static void check_descendants(opal_tree_item_t* item, + unsigned *data, + unsigned level, + int *err_order, int *err_ancestor) +{ + test_data_t *ele; + + /* loop over all siblings and then down first child */ + while (item) { + /* check item for correctness */ + ele = (test_data_t *)item; + if (ele->data != *data) { + (*err_order)++; + } + if (item->opal_tree_num_ancestors != level) { + (*err_ancestor)++; + } + (*data)++; + check_descendants(opal_tree_get_next_sibling(item), data, level, + err_order, err_ancestor); + item = opal_tree_get_first_child(item); + level++; + } + return; +} + +static int test_comp(opal_tree_item_t *item, void *key) +{ + if (((test_data_t *)item)->data > *((unsigned *) key)) { + return(1); + } + if (((test_data_t *)item)->data < *((unsigned *) key)) { + return(-1); + } + return(0); +} + +static int test_serialize(opal_tree_item_t *item, opal_buffer_t *buffer) +{ + test_data_t *ele = (test_data_t *)item; + + return(opal_dss.pack(buffer, &ele->data, 1, OPAL_INT32)); +} + +static int test_deserialize(opal_buffer_t *serial_data, opal_tree_item_t **item) +{ + int rc = OPAL_SUCCESS, idx = 1; + test_data_t *ele; + + ele = (test_data_t *)malloc(sizeof(test_data_t)); + OBJ_CONSTRUCT(ele, test_data_t); + if (OPAL_SUCCESS == (rc = opal_dss.unpack(serial_data, &ele->data, &idx, + OPAL_INT32))) { + *item = (opal_tree_item_t*)ele; + } else { + *item = NULL; + } + return(rc); +}