blob: ea1ee28b3bf2955660c83cad4a3bd7e11bb009bd [file] [log] [blame]
/* libnih
*
* tree.c - generic binary tree implementation
*
* Copyright © 2007 Scott James Remnant <scott@netsplit.com>.
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nih/macros.h>
#include <nih/logging.h>
#include <nih/alloc.h>
#include "tree.h"
/**
* nih_tree_init:
* @tree: tree node to be initialised.
*
* Initialise an already allocated tree node, once done it can be used
* as the start of a new binary tree or added to an existing tree.
**/
void
nih_tree_init (NihTree *tree)
{
nih_assert (tree != NULL);
tree->parent = tree->left = tree->right = NULL;
}
/**
* nih_tree_new:
* @parent: parent of new node.
*
* Allocates a new tree structure, usually used as the root of a new
* binary tree. You may prefer to allocate the NihTree structure statically
* and use nih_tree_init() to initialise it instead.
*
* The structure is allocated using nih_alloc() so can be used as a context
* to other allocations.
*
* If @parent is not NULL, it should be a pointer to another allocated
* block which will be used as the parent for this block. When @parent
* is freed, the returned string will be freed too. If you have clean-up
* that would need to be run, you can assign a destructor function using
* the nih_alloc_set_destructor() function.
*
* Returns: the new tree node or NULL if the allocation failed.
**/
NihTree *
nih_tree_new (const void *parent)
{
NihTree *tree;
tree = nih_new (parent, NihTree);
if (! tree)
return NULL;
nih_tree_init (tree);
return tree;
}
/**
* nih_tree_entry_new:
* @parent: parent of new node.
*
* Allocates a new tree entry structure, leaving the caller to set the
* data of the entry.
*
* The structure is allocated using nih_alloc() so can be used as a context
* to other allocations.
*
* If @parent is not NULL, it should be a pointer to another allocated
* block which will be used as the parent for this block. When @parent
* is freed, the returned string will be freed too. If you have clean-up
* that would need to be run, you can assign a destructor function using
* the nih_alloc_set_destructor() function.
*
* Returns: the new tree node or NULL if the allocation failed.
**/
NihTreeEntry *
nih_tree_entry_new (const void *parent)
{
NihTreeEntry *tree;
tree = nih_new (parent, NihTreeEntry);
if (! tree)
return NULL;
nih_tree_init (&tree->node);
tree->data = NULL;
return tree;
}
/**
* nih_tree_add:
* @tree: node in the destination tree,
* @node: node to be added to the tree,
* @where: where @node should be added.
*
* Adds @node to a new binary tree, either as a child of or, or replacing,
* the existing node @tree. The exact position is determined by @where,
* which may be NIH_TREE_LEFT or NIH_TREE_RIGHT to indicate that @node
* should be a child of @tree or NIH_TREE_HERE to indicate that @node
* should replace @tree.
*
* If @node is already in another tree it is removed so there is no need
* to call nih_tree_remove() before this function. There is also no
* requirement that the trees be different, so this can be used to reorder
* a tree.
*
* Returns: node replaced by @node, normally NULL.
**/
NihTree *
nih_tree_add (NihTree *tree,
NihTree *node,
NihTreeWhere where)
{
NihTree *replaced = NULL;
nih_assert (tree != NULL);
if (node)
nih_tree_remove (node);
if (where == NIH_TREE_LEFT) {
replaced = tree->left;
if (replaced)
replaced->parent = NULL;
tree->left = node;
if (node)
node->parent = tree;
} else if (where == NIH_TREE_RIGHT) {
replaced = tree->right;
if (replaced)
replaced->parent = NULL;
tree->right = node;
if (node)
node->parent = tree;
}
return replaced;
}
/**
* nih_tree_remove:
* @node: node to be removed.
*
* Removes @node and its children from the containing tree. Neither the
* node nor children are freed, and the children are not unlinked from the
* node. Instead the node is returned so that it can be added to another
* tree (through there's no need to call nih_tree_remove() first if you
* wanted to do that) or used as the root of a new tree.
*
* Returns: @node as a root node.
**/
NihTree *
nih_tree_remove (NihTree *node)
{
nih_assert (node != NULL);
if (node->parent) {
if (node->parent->left == node) {
node->parent->left = NULL;
} else if (node->parent->right == node) {
node->parent->right = NULL;
}
node->parent = NULL;
}
return node;
}
/**
* nih_tree_unlink:
* @node: node to be removed.
*
* Removes @node from its containing tree, as nih_tree_remove() does, but
* also unlinks the node's children from itself so that they don't have
* a dangling pointer.
*
* Returns: @node.
**/
NihTree *
nih_tree_unlink (NihTree *node)
{
nih_assert (node != NULL);
nih_tree_remove (node);
if (node->left)
node->left->parent = NULL;
if (node->right)
node->right->parent = NULL;
node->left = node->right = NULL;
return node;
}
/**
* nih_tree_destructor:
* @node: node to be removed.
*
* Removes @node from its containing tree, intended to be used as an
* nih_alloc() destructor so that the node is automatically removed if
* it is freed.
*
* Returns: zero.
**/
int
nih_tree_destructor (NihTree *node)
{
nih_assert (node != NULL);
nih_tree_unlink (node);
return 0;
}
/**
* nih_tree_free:
* @node: node to be removed and freed.
*
* Removes @node from its containing tree and frees the memory allocated
* for it. @node must have been previously allocated using nih_alloc().
*
* Children of @node are not automatically freed unless they are allocated
* as nih_alloc() children of @node.
*
* You must also take care of freeing the data attached to the node yourself
* by either freeing it before calling this function or allocating it using
* the node as the context.
*
* Returns: return value from destructor, or 0.
**/
int
nih_tree_free (NihTree *node)
{
nih_assert (node != NULL);
nih_tree_unlink (node);
return nih_free (node);
}
/**
* nih_tree_next:
* @tree: tree to iterate,
* @node: node just visited.
*
* Iterates the @tree in-order non-recursively; to obtain the first node,
* @tree should be set to the root of the tree and @node should be NULL.
* Then for subsequent nodes, @node should be the previous return value
* from this function.
*
* Returns: next in-order node within @tree or NULL if no further nodes.
**/
NihTree *
nih_tree_next (NihTree *tree,
NihTree *node)
{
NihTree *prev;
nih_assert (tree != NULL);
if (node) {
prev = node;
if (node->right) {
node = node->right;
} else {
if (node == tree)
return NULL;
node = node->parent;
}
} else {
prev = tree->parent;
node = tree;
}
while (node) {
NihTree *tmp = node;
if ((prev == node->parent) && node->left) {
node = node->left;
} else if (node->right && (prev == node->right)) {
if (node == tree)
return NULL;
node = node->parent;
} else {
return node;
}
prev = tmp;
}
return NULL;
}
/**
* nih_tree_prev:
* @tree: tree to iterate,
* @node: node just visited.
*
* Reverse-iterates the @tree in-order non-recursively; to obtain the last
* node, @tree should be set to the root of the tree and @node should be NULL.
* Then for subsequent nodes, @node should be the previous return value
* from this function.
*
* Returns: previous in-order node within @tree or NULL if no further nodes.
**/
NihTree *
nih_tree_prev (NihTree *tree,
NihTree *node)
{
NihTree *prev;
nih_assert (tree != NULL);
if (node) {
prev = node;
if (node->left) {
node = node->left;
} else {
if (node == tree)
return NULL;
node = node->parent;
}
} else {
prev = tree->parent;
node = tree;
}
while (node) {
NihTree *tmp = node;
if ((prev == node->parent) && node->right) {
node = node->right;
} else if (node->left && (prev == node->left)) {
if (node == tree)
return NULL;
node = node->parent;
} else {
return node;
}
prev = tmp;
}
return NULL;
}
/**
* nih_tree_next_pre:
* @tree: tree to iterate,
* @node: node just visited.
*
* Iterates the @tree in-order non-recursively; to obtain the first node,
* @tree should be set to the root of the tree and @node should be NULL.
* Then for subsequent nodes, @node should be the previous return value
* from this function.
*
* Returns: next in-order node within @tree or NULL if no further nodes.
**/
NihTree *
nih_tree_next_pre (NihTree *tree,
NihTree *node)
{
NihTree *prev;
nih_assert (tree != NULL);
if (node) {
prev = node;
if (node->left) {
return node->left;
} else if (node->right) {
return node->right;
} else {
if (node == tree)
return NULL;
node = node->parent;
}
} else {
return tree;
}
while (node) {
NihTree *tmp = node;
if ((prev != node->right) && node->right) {
return node->right;
} else {
if (node == tree)
return NULL;
node = node->parent;
}
prev = tmp;
}
return NULL;
}
/**
* nih_tree_prev_pre:
* @tree: tree to iterate,
* @node: node just visited.
*
* Reverse-iterates the @tree in-order non-recursively; to obtain the last
* node, @tree should be set to the root of the tree and @node should be NULL.
* Then for subsequent nodes, @node should be the previous return value
* from this function.
*
* Returns: previous in-order node within @tree or NULL if no further nodes.
**/
NihTree *
nih_tree_prev_pre (NihTree *tree,
NihTree *node)
{
NihTree *prev;
nih_assert (tree != NULL);
if (node) {
prev = node;
if (node == tree)
return NULL;
node = node->parent;
} else {
prev = tree->parent;
node = tree;
}
while (node) {
NihTree *tmp = node;
if ((prev == node->parent) && node->right) {
node = node->right;
} else if ((prev != node->left) && node->left) {
node = node->left;
} else {
return node;
}
prev = tmp;
}
return NULL;
}
/**
* nih_tree_next_post:
* @tree: tree to iterate,
* @node: node just visited.
*
* Iterates the @tree in-order non-recursively; to obtain the first node,
* @tree should be set to the root of the tree and @node should be NULL.
* Then for subsequent nodes, @node should be the previous return value
* from this function.
*
* Returns: next in-order node within @tree or NULL if no further nodes.
**/
NihTree *
nih_tree_next_post (NihTree *tree,
NihTree *node)
{
NihTree *prev;
nih_assert (tree != NULL);
if (node) {
prev = node;
if (node == tree)
return NULL;
node = node->parent;
} else {
prev = tree->parent;
node = tree;
}
while (node) {
NihTree *tmp = node;
if ((prev == node->parent) && node->left) {
node = node->left;
} else if ((prev != node->right) && node->right) {
node = node->right;
} else {
return node;
}
prev = tmp;
}
return NULL;
}
/**
* nih_tree_prev_post:
* @tree: tree to iterate,
* @node: node just visited.
*
* Reverse-iterates the @tree in-order non-recursively; to obtain the last
* node, @tree should be set to the root of the tree and @node should be NULL.
* Then for subsequent nodes, @node should be the previous return value
* from this function.
*
* Returns: previous in-order node within @tree or NULL if no further nodes.
**/
NihTree *
nih_tree_prev_post (NihTree *tree,
NihTree *node)
{
NihTree *prev;
nih_assert (tree != NULL);
if (node) {
prev = node;
if (node->right) {
return node->right;
} else if (node->left) {
return node->left;
} else {
if (node == tree)
return NULL;
node = node->parent;
}
} else {
return tree;
}
while (node) {
NihTree *tmp = node;
if ((prev != node->left) && node->left) {
return node->left;
} else {
if (node == tree)
return NULL;
node = node->parent;
}
prev = tmp;
}
return NULL;
}