nextcloud-desktop/csync/src/std/c_rbtree.h
2014-01-13 14:39:50 +01:00

312 lines
10 KiB
C

/*
* cynapses libc functions
*
* Copyright (c) 2003-2004 by Andrew Suffield <asuffield@debian.org>
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file c_rbtree.h
*
* @brief Interface of the cynapses libc red-black tree implementation
*
* A red-black tree is a type of self-balancing binary search tree. It is
* complex, but has good worst-case running time for its operations and is
* efficient in practice: it can search, insert, and delete in O(log n)
* time, where n is the number of elements in the tree.
*
* In red-black trees, the leaf nodes are not relevant and do not contain
* data. Therefore we use a sentinal node to save memory. All references
* from internal nodes to leaf nodes instead point to the sentinel node.
*
* In a red-black tree each node has a color attribute, the value of which
* is either red or black. In addition to the ordinary requirements imposed
* on binary search trees, the following additional requirements of any
* valid red-black tree apply:
*
* 1. A node is either red or black.
* 2. The root is black.
* 3. All leaves are black, even when the parent is black
* (The leaves are the null children.)
* 4. Both children of every red node are black.
* 5. Every simple path from a node to a descendant leaf contains the same
* number of black nodes, either counting or not counting the null black
* nodes. (Counting or not counting the null black nodes does not affect
* the structure as long as the choice is used consistently.).
*
* These constraints enforce a critical property of red-black trees: that the
* longest path from the root to a leaf is no more than twice as long as the
* shortest path from the root to a leaf in that tree. The result is that the
* tree is roughly balanced. Since operations such as inserting, deleting, and
* finding values requires worst-case time proportional to the height of the
* tree, this theoretical upper bound on the height allows red-black trees to
* be efficient in the worst-case, unlike ordinary binary search trees.
*
* http://en.wikipedia.org/wiki/Red-black_tree
*
* @defgroup cynRBTreeInternals cynapses libc red-black tree functions
* @ingroup cynLibraryAPI
*
* @{
*/
#ifndef _C_RBTREE_H
#define _C_RBTREE_H
/* Forward declarations */
struct c_rbtree_s; typedef struct c_rbtree_s c_rbtree_t;
struct c_rbnode_s; typedef struct c_rbnode_s c_rbnode_t;
/**
* Define the two colors for the red-black tree
*/
enum xrbcolor_e { BLACK = 0, RED }; typedef enum xrbcolor_e xrbcolor_t;
/**
* @brief Callback function to compare a key with the data from a
* red-black tree node.
*
* @param key key as a generic pointer
* @param data data as a generic pointer
*
* @return It returns an integer less than, equal to, or greater than zero
* depending on the key or data you use. The function is similar
* to strcmp().
*/
typedef int c_rbtree_compare_func(const void *key, const void *data);
/**
* @brief Visit function for the c_rbtree_walk() function.
*
* This function will be called by c_rbtree_walk() for every node. It is up to
* the developer what the function does. The fist parameter is a node object
* the second can be of any kind.
*
* @param obj The node data that will be passed by c_rbtree_walk().
* @param data Generic data pointer.
*
* @return 0 on success, < 0 on error. You should set errno.
*
*/
typedef int c_rbtree_visit_func(void *, void *);
/**
* Structure that represents a red-black tree
*/
struct c_rbtree_s {
c_rbnode_t *root;
c_rbtree_compare_func *key_compare;
c_rbtree_compare_func *data_compare;
size_t size;
};
/**
* Structure that represents a node of a red-black tree
*/
struct c_rbnode_s {
c_rbtree_t *tree;
c_rbnode_t *left;
c_rbnode_t *right;
c_rbnode_t *parent;
void *data;
xrbcolor_t color;
};
/**
* @brief Create the red-black tree
*
* @param rbtree The pointer to assign the allocated memory.
*
* @param key_compare Callback function to compare a key with the data
* inside a reb-black tree node.
*
* @param data_compare Callback function to compare a key as data with thee
* data inside a red-black tree node.
*
* @return 0 on success, -1 if an error occured with errno set.
*/
int c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare);
/**
* @brief Duplicate a red-black tree.
*
* @param tree Tree to duplicate.
*
* @return Pointer to a new allocated duplicated rbtree. NULL if an error
* occured.
*/
c_rbtree_t *c_rbtree_dup(const c_rbtree_t *tree);
/**
* @brief Free the structure of a red-black tree.
*
* You should call c_rbtree_destroy() before you call this function.
*
* @param tree The tree to free.
*
* @return 0 on success, less than 0 if an error occured.
*/
int c_rbtree_free(c_rbtree_t *tree);
/**
* @brief Destroy the content and the nodes of an red-black tree.
*
* This is far from the most efficient way to walk a tree, but it is
* the *safest* way to destroy a tree - the destructor can do almost
* anything (as long as it does not create an infinite loop) to the
* tree structure without risk.
*
* If for some strange reason you need a faster destructor (think
* twice - speed and memory deallocation don't mix well) then consider
* stashing an llist of dataects and destroying that instead, and just
* using c_rbtree_free() on your tree.
*
* @param T The tree to destroy.
* @param DESTRUCTOR The destructor to call on a node to destroy.
*/
#define c_rbtree_destroy(T, DESTRUCTOR) \
do { \
if (T) { \
c_rbnode_t *_c_rbtree_temp; \
while ((_c_rbtree_temp = c_rbtree_head(T))) { \
(DESTRUCTOR)(_c_rbtree_temp->data); \
if (_c_rbtree_temp == c_rbtree_head(T)) { \
c_rbtree_node_delete(_c_rbtree_temp); \
} \
} \
} \
SAFE_FREE(T); \
} while (0);
/**
* @brief Inserts a node into a red black tree.
*
* @param tree The red black tree to insert the node.
* @param data The data to insert into the tree.
*
* @return 0 on success, 1 if a duplicate has been found and < 0 if an error
* occured with errno set.
* EINVAL if a null pointer has been passed as the tree.
* ENOMEM if there is no memory left.
*/
int c_rbtree_insert(c_rbtree_t *tree, void *data);
/**
* @brief Find a node in a red-black tree.
*
* c_rbtree_find() is searching for the given key in a red-black tree and
* returns the node if the key has been found.
*
* @param tree The tree to search.
* @param key The key to search for.
*
* @return If the key was found the node will be returned. On error NULL
* will be returned.
*/
c_rbnode_t *c_rbtree_find(c_rbtree_t *tree, const void *key);
/**
* @brief Get the head of the red-black tree.
*
* @param tree The tree to get the head for.
*
* @return The head node. NULL if an error occured.
*/
c_rbnode_t *c_rbtree_head(c_rbtree_t *tree);
/**
* @brief Get the tail of the red-black tree.
*
* @param tree The tree to get the tail for.
*
* @return The tail node. NULL if an error occured.
*/
c_rbnode_t *c_rbtree_tail(c_rbtree_t *tree);
/**
* @brief Get the size of the red-black tree.
*
* @param T The tree to get the size from.
*
* @return The size of the red-black tree.
*/
#define c_rbtree_size(T) (T) == NULL ? 0 : ((T)->size)
/**
* @brief Walk over a red-black tree.
*
* Walk over a red-black tree calling a visitor function for each node found.
*
* @param tree Tree to walk.
* @param data Data which should be passed to the visitor function.
* @param visitor Visitor function. This will be called for each node passed.
*
* @return 0 on sucess, less than 0 if an error occured.
*/
int c_rbtree_walk(c_rbtree_t *tree, void *data, c_rbtree_visit_func *visitor);
/**
* @brief Delete a node in a red-black tree.
*
* @param node Node which should be deleted.
*
* @return 0 on success, -1 if an error occured.
*/
int c_rbtree_node_delete(c_rbnode_t *node);
/**
* @brief Get the next node.
*
* @param node The node of which you want the next node.
*
* @return The next node, NULL if an error occured.
*/
c_rbnode_t *c_rbtree_node_next(c_rbnode_t *node);
/**
* @brief Get the previous node.
*
* @param node The node of which you want the previous node.
*
* @return The previous node, NULL if an error occured.
*/
c_rbnode_t *c_rbtree_node_prev(c_rbnode_t *node);
/**
* @brief Get the data of a node.
*
* @param N The node to get the data from.
*
* @return The data, NULL if an error occured.
*/
#define c_rbtree_node_data(N) ((N) ? ((N)->data) : NULL)
/**
* @brief Perform a sanity check for a red-black tree.
*
* This is mostly for testing purposes.
*
* @param tree The tree to check.
*
* @return 0 on success, less than 0 if an error occured.
*/
int c_rbtree_check_sanity(c_rbtree_t *tree);
/**
* }@
*/
#endif /* _C_RBTREE_H */