/*****************************************************************************\ * xtree.c - functions used for tree data structure manament ***************************************************************************** * Copyright (C) 2012 CEA/DAM/DIF * * This file is part of Slurm, a resource management program. * For details, see . * Please also read the included file: DISCLAIMER. * * Slurm 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 2 of the License, or (at your option) * any later version. * * In addition, as a special exception, the copyright holders give permission * to link the code of portions of this program with the OpenSSL library under * certain conditions as described in each individual source file, and * distribute linked combinations including the two. You must obey the GNU * General Public License in all respects for all of the code used other than * OpenSSL. If you modify file(s) with this exception, you may extend this * exception to your version of the file(s), but you are not obligated to do * so. If you do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source files in * the program, then also delete it here. * * Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. \*****************************************************************************/ #include "src/common/xassert.h" #include "src/common/xmalloc.h" #include "src/common/xtree.h" /* free the node childs */ static void xtree_free_childs(xtree_t* tree, xtree_node_t* node) { xtree_node_t* current_node = node; xtree_node_t* free_later = NULL; /* if (!tree || !tree->root || !node) return; comm: not a user func */ if (current_node && current_node->start) { /* tree has childs, depth may have changed */ tree->state &= ~XTREE_STATE_DEPTHCACHED; } while (current_node) { if (current_node->start) { current_node = current_node->start; continue; } if (current_node == node) { current_node->start = current_node->end = NULL; return; } free_later = current_node; if (current_node->parent) { current_node->parent->start = current_node->next; } current_node = current_node->parent; if (tree->free) tree->free(free_later); xfree(free_later); --tree->count; } } /* tries to free the leftest leaf each time, and remove it from the tree, * then go above, since node above finish to be a leaf itself. */ void xtree_free(xtree_t* tree) { if (!tree || !tree->root) return; xtree_free_childs(tree, tree->root); if (tree->free) tree->free(tree->root); xfree(tree->root); xtree_init(tree, tree->free); } void xtree_init(xtree_t* tree, xtree_free_data_function_t freefunc) { tree->root = NULL; tree->free = freefunc; tree->count = 0; tree->depth = 0; tree->state = XTREE_STATE_DEPTHCACHED; } void xtree_set_freefunc(xtree_t* tree, xtree_free_data_function_t freefunc) { tree->free = freefunc; } xtree_node_t* xtree_get_parent(xtree_t* tree, xtree_node_t* node) { if (!node || !tree || !tree->root) return NULL; return node->parent; } uint32_t xtree_get_count(xtree_t* tree) { if (!tree) return UINT32_MAX; return tree->count; } xtree_node_t* xtree_add_child(xtree_t* tree, xtree_node_t* parent, void* data, uint8_t flags) { xtree_node_t* newnode = NULL; if (!tree || (!parent && tree->root) || (parent && !tree->root)) { return NULL; } xassert(flags & (XTREE_APPEND | XTREE_PREPEND)); newnode = xmalloc(sizeof(xtree_node_t)); newnode->data = data; newnode->parent = parent; newnode->start = NULL; newnode->end = NULL; newnode->next = NULL; newnode->previous = NULL; if (!parent) { newnode->next = NULL; newnode->previous = NULL; tree->root = newnode; tree->count = 1; tree->depth = 1; tree->state = XTREE_STATE_DEPTHCACHED; return newnode; } if (flags & XTREE_APPEND) { newnode->previous = parent->end; newnode->next = NULL; if (parent->end) { parent->end->next = newnode; } else { parent->start = newnode; } parent->end = newnode; } else { newnode->next = parent->start; newnode->previous = NULL; if (parent->start) { parent->start->previous = newnode; } else { parent->end = newnode; } parent->start = newnode; } ++tree->count; tree->state &= ~XTREE_STATE_DEPTHCACHED; if (flags & XTREE_REFRESH_DEPTH) xtree_refresh_depth(tree); return newnode; } xtree_node_t* xtree_add_sibling(xtree_t* tree, xtree_node_t* node, void* data, uint8_t flags) { xtree_node_t* newnode = NULL; xassert(flags & (XTREE_APPEND | XTREE_PREPEND)); if (!tree) return NULL; /* no node, same behaviour as add_child */ if (!node) return xtree_add_child(tree, node, data, flags); /* root node has only childs */ /* FIXME: better to call add_child instead here, or can be too * confusing ? */ if (!node->parent) return NULL; newnode = xmalloc(sizeof(xtree_node_t)); newnode->data = data; newnode->parent = node->parent; newnode->start = NULL; newnode->end = NULL; newnode->next = NULL; newnode->previous = NULL; if (flags & XTREE_APPEND) { newnode->previous = node; newnode->next = node->next; node->next = newnode; if (newnode->next) { newnode->next->previous = newnode; } else { node->parent->end = newnode; } } else { newnode->next = node; newnode->previous = node->previous; node->previous = newnode; if (newnode->previous) { newnode->previous->next = newnode; } else { node->parent->start = newnode; } } ++tree->count; tree->state &= ~XTREE_STATE_DEPTHCACHED; if (flags & XTREE_REFRESH_DEPTH) xtree_refresh_depth(tree); return newnode; } /* NOTE: 0 = no node since no depth so implies no root */ uint32_t xtree_depth_const(const xtree_t* tree) { if (tree->state & XTREE_STATE_DEPTHCACHED) return tree->depth; return xtree_depth_const_node(tree, tree->root); } static uint8_t xtree_depth_helper(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { uint32_t* max_level = (uint32_t*)arg; if (level >= *max_level) { *max_level = level; } return 1; } uint32_t xtree_depth_const_node(const xtree_t* tree, const xtree_node_t* node) { uint32_t max_level = 0; if (!tree->root) return 0; xtree_walk((xtree_t*)tree, NULL, 0, UINT32_MAX, xtree_depth_helper, &max_level); return max_level + 1; } uint32_t xtree_depth(xtree_t* tree) { xtree_refresh_depth(tree); return tree->depth; } void xtree_refresh_depth(xtree_t* tree) { if (tree->state & XTREE_STATE_DEPTHCACHED) return; tree->depth = xtree_depth_const_node(tree, tree->root); tree->state |= XTREE_STATE_DEPTHCACHED; } uint32_t xtree_node_depth(const xtree_node_t* node) { uint32_t depth = 0; while (node) { ++depth; node = node->parent; } return depth; } /* always tries to browse the tree in this order : most left child, if no * child, go to next sibling, then if no sibling, go up until a sibling is * found. */ xtree_node_t* xtree_walk(xtree_t* tree, xtree_node_t* node, uint32_t min_level, uint32_t max_level, xtree_walk_function_t action, void* arg) { xtree_node_t* current_node = NULL; uint32_t level = 0; if (!tree || !action) return NULL; if (!node) node = tree->root; current_node = node; while (current_node) { if (level >= min_level && !action(current_node, XTREE_GROWING, level, arg)) { return current_node; } /* go down and continue counting */ if (current_node->start) { if (level >= min_level && !action(current_node, XTREE_PREORDER, level, arg)) { return current_node; } if (level < max_level) { current_node = current_node->start; ++level; continue; } } else if (level >= min_level && !action(current_node, XTREE_LEAF, level, arg)) { return current_node; } /* while no next member go up */ while (!current_node->next) { current_node = current_node->parent; --level; if (!current_node) { return NULL; } else if (current_node == node) { if (level >= min_level && !action(current_node, XTREE_ENDORDER, level, arg)) { return current_node; } return NULL; } else if (level >= min_level && !action(current_node, XTREE_ENDORDER, level, arg)) { return current_node; } } /* go to next sibling */ if (current_node->next) { if ((level >= min_level) && !action(current_node->parent, XTREE_INORDER, level - 1, arg)) { return current_node; } current_node = current_node->next; } } return NULL; } struct xtree_find_st { xtree_find_compare_t compare; const void* arg; }; static uint8_t xtree_find_helper(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { struct xtree_find_st* st = (struct xtree_find_st*)arg; return st->compare(node->data, st->arg); } xtree_node_t* xtree_find(xtree_t* tree, xtree_find_compare_t compare, const void* arg) { struct xtree_find_st st; if (!tree || !compare) return NULL; st.compare = compare; st.arg = arg; return xtree_walk(tree, NULL, 0, UINT32_MAX, xtree_find_helper, &st); } xtree_node_t* xtree_delete(xtree_t* tree, xtree_node_t* node) { xtree_node_t* parent = NULL; if (!tree || !tree->root || !node) return NULL; if (node == tree->root) { xtree_free(tree); return NULL; } parent = node->parent; if (parent->start == node && parent->end == node) { parent->start = parent->end = NULL; tree->state &= ~XTREE_STATE_DEPTHCACHED; } else if (parent->start == node) { parent->start = node->next; node->next->previous = NULL; } else if (parent->end == node) { parent->end = node->previous; node->previous->next = NULL; } else { node->previous->next = node->next; node->next->previous = node->previous; } xtree_free_childs(tree, node); if (tree->free) tree->free(node); xfree(node); --tree->count; return parent; } #define XTREE_GET_PARENTS_FIRST_SIZE 64 xtree_node_t** xtree_get_parents(xtree_t* tree, xtree_node_t* node, uint32_t* size) { xtree_node_t* current_node = NULL; xtree_node_t** parents_list = NULL; uint32_t parents_size = 0; uint32_t parents_count = 0; if (!tree || !tree->root || !node || !size) return NULL; parents_size = XTREE_GET_PARENTS_FIRST_SIZE; parents_list = xmalloc(sizeof(xtree_node_t *) * parents_size); current_node = node->parent; while (current_node) { if (parents_count >= parents_size) { parents_size = parents_count*2; parents_list = (xtree_node_t**)xrealloc(parents_list, sizeof(xtree_node_t*)*parents_size); } parents_list[parents_count] = current_node; ++parents_count; current_node = current_node->parent; } if (parents_count != 0) { parents_list = (xtree_node_t**)xrealloc(parents_list, sizeof(xtree_node_t*)*(parents_count+1)); /* safety measure, can be used as strlen if users assumes it */ parents_list[parents_count] = NULL; } else { xfree(parents_list); parents_list = NULL; } *size = parents_count; return parents_list; } xtree_node_t* xtree_common(xtree_t* tree, const xtree_node_t* const* nodes, uint32_t size) { xtree_node_t* common_ancestor = NULL; xtree_node_t* current_node = NULL; uint32_t i; uint8_t found_common_ancestor; if (!tree || !tree->root || !nodes || !nodes[0] || !size || !nodes[0]->parent) return NULL; common_ancestor = nodes[0]->parent; for (i = 1; i < size && common_ancestor; ++i) { found_common_ancestor = 0; while (common_ancestor && !found_common_ancestor) { if (!nodes[i]) return common_ancestor; current_node = nodes[i]->parent; while (current_node && current_node != common_ancestor) { current_node = current_node->parent; } if (current_node != common_ancestor) { common_ancestor = common_ancestor->parent; } else { found_common_ancestor = 1; } } } return common_ancestor; } #define XTREE_GET_LEAVES_FIRST_SIZE 64 struct xtree_get_leaves_st { xtree_node_t** list; uint32_t list_count; uint32_t size; }; static uint8_t xtree_get_leaves_helper(xtree_node_t* node, uint8_t which, uint32_t level, void* arg) { struct xtree_get_leaves_st* st = (struct xtree_get_leaves_st*)arg; if (which == XTREE_LEAF) { if (st->list_count >= st->size) { st->size = st->list_count * 2; st->list = (xtree_node_t**)xrealloc(st->list, sizeof(xtree_node_t*)*st->size); } st->list[st->list_count] = node; ++st->list_count; } return 1; } xtree_node_t** xtree_get_leaves(xtree_t* tree, xtree_node_t* node, uint32_t* size) { struct xtree_get_leaves_st st; if (!tree || !size || !node) { /* testing node nulliness to return NULL since xtree_walk will * be run for root node if node == NULL and return an * unattended non null value. */ return NULL; } if (!node->start) { /* if the node is a leave itself there is no leaves descending * it, but tree walk will return the leave itself, so * returning null before. */ return NULL; } st.list_count = 0; st.size = XTREE_GET_LEAVES_FIRST_SIZE; st.list = xmalloc(sizeof(xtree_node_t *) * st.size); xtree_walk(tree, node, 0, UINT32_MAX, xtree_get_leaves_helper, &st); if (st.list_count != 0) { st.list = (xtree_node_t**)xrealloc(st.list, sizeof(xtree_node_t*)*(st.list_count+1)); /* safety measure, can be used as strlen if users assumes it */ st.list[st.list_count] = NULL; } else { xfree(st.list); st.list = NULL; } *size = st.list_count; return st.list; }