Blob Blame History Raw
/*
 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
 * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

/*
 * Abstract:
 *	Implementation of quick map, a binary tree where the caller always
 *	provides all necessary storage.
 *
 */

/*****************************************************************************
*
* Map
*
* Map is an associative array.  By providing a key, the caller can retrieve
* an object from the map.  All objects in the map have an associated key,
* as specified by the caller when the object was inserted into the map.
* In addition to random access, the caller can traverse the map much like
* a linked list, either forwards from the first object or backwards from
* the last object.  The objects in the map are always traversed in
* order since the nodes are stored sorted.
*
* This implementation of Map uses a red black tree verified against
* Cormen-Leiserson-Rivest text, McGraw-Hill Edition, fourteenth
* printing, 1994.
*
*****************************************************************************/

#include <util/cl_qmap.h>
#include <string.h>

static inline void __cl_primitive_insert(cl_list_item_t *const p_list_item,
					 cl_list_item_t *const p_new_item)
{
        /* CL_ASSERT that a non-null pointer is provided. */
        assert(p_list_item);
        /* CL_ASSERT that a non-null pointer is provided. */
	assert(p_new_item);

	p_new_item->p_next = p_list_item;
        p_new_item->p_prev = p_list_item->p_prev;
        p_list_item->p_prev = p_new_item;
        p_new_item->p_prev->p_next = p_new_item;
}

static inline void __cl_primitive_remove(cl_list_item_t *const p_list_item)
{
        /* CL_ASSERT that a non-null pointer is provided. */
        assert(p_list_item);

        /* set the back pointer */
        p_list_item->p_next->p_prev = p_list_item->p_prev;
        /* set the next pointer */
        p_list_item->p_prev->p_next = p_list_item->p_next;

        /* if we're debugging, spruce up the pointers to help find bugs */
#if defined( _DEBUG_ )
        if (p_list_item != p_list_item->p_next) {
                p_list_item->p_next = NULL;
                p_list_item->p_prev = NULL;
        }
#endif                          /* defined( _DEBUG_ ) */
}

/******************************************************************************
 IMPLEMENTATION OF QUICK MAP
******************************************************************************/

/*
 * Get the root.
 */
static inline cl_map_item_t *__cl_map_root(const cl_qmap_t * const p_map)
{
	assert(p_map);
	return (p_map->root.p_left);
}

/*
 * Returns whether a given item is on the left of its parent.
 */
static bool __cl_map_is_left_child(const cl_map_item_t * const p_item)
{
	assert(p_item);
	assert(p_item->p_up);
	assert(p_item->p_up != p_item);

	return (p_item->p_up->p_left == p_item);
}

/*
 * Retrieve the pointer to the parent's pointer to an item.
 */
static cl_map_item_t **__cl_map_get_parent_ptr_to_item(cl_map_item_t *
						       const p_item)
{
	assert(p_item);
	assert(p_item->p_up);
	assert(p_item->p_up != p_item);

	if (__cl_map_is_left_child(p_item))
		return (&p_item->p_up->p_left);

	assert(p_item->p_up->p_right == p_item);
	return (&p_item->p_up->p_right);
}

/*
 * Rotate a node to the left.  This rotation affects the least number of links
 * between nodes and brings the level of C up by one while increasing the depth
 * of A one.  Note that the links to/from W, X, Y, and Z are not affected.
 *
 *	    R				      R
 *	    |				      |
 *	    A				      C
 *	  /   \			        /   \
 *	W       C			  A       Z
 *	       / \			 / \
 *	      B   Z			W   B
 *	     / \			   / \
 *	    X   Y			  X   Y
 */
static void __cl_map_rot_left(cl_qmap_t * const p_map,
			      cl_map_item_t * const p_item)
{
	cl_map_item_t **pp_root;

	assert(p_map);
	assert(p_item);
	assert(p_item->p_right != &p_map->nil);

	pp_root = __cl_map_get_parent_ptr_to_item(p_item);

	/* Point R to C instead of A. */
	*pp_root = p_item->p_right;
	/* Set C's parent to R. */
	(*pp_root)->p_up = p_item->p_up;

	/* Set A's right to B */
	p_item->p_right = (*pp_root)->p_left;
	/*
	 * Set B's parent to A.  We trap for B being NIL since the
	 * caller may depend on NIL not changing.
	 */
	if ((*pp_root)->p_left != &p_map->nil)
		(*pp_root)->p_left->p_up = p_item;

	/* Set C's left to A. */
	(*pp_root)->p_left = p_item;
	/* Set A's parent to C. */
	p_item->p_up = *pp_root;
}

/*
 * Rotate a node to the right.  This rotation affects the least number of links
 * between nodes and brings the level of A up by one while increasing the depth
 * of C one.  Note that the links to/from W, X, Y, and Z are not affected.
 *
 *	        R				     R
 *	        |				     |
 *	        C				     A
 *	      /   \				   /   \
 *	    A       Z			 W       C
 *	   / \    				        / \
 *	  W   B   				       B   Z
 *	     / \				      / \
 *	    X   Y				     X   Y
 */
static void __cl_map_rot_right(cl_qmap_t * const p_map,
			       cl_map_item_t * const p_item)
{
	cl_map_item_t **pp_root;

	assert(p_map);
	assert(p_item);
	assert(p_item->p_left != &p_map->nil);

	/* Point R to A instead of C. */
	pp_root = __cl_map_get_parent_ptr_to_item(p_item);
	(*pp_root) = p_item->p_left;
	/* Set A's parent to R. */
	(*pp_root)->p_up = p_item->p_up;

	/* Set C's left to B */
	p_item->p_left = (*pp_root)->p_right;
	/*
	 * Set B's parent to C.  We trap for B being NIL since the
	 * caller may depend on NIL not changing.
	 */
	if ((*pp_root)->p_right != &p_map->nil)
		(*pp_root)->p_right->p_up = p_item;

	/* Set A's right to C. */
	(*pp_root)->p_right = p_item;
	/* Set C's parent to A. */
	p_item->p_up = *pp_root;
}

void cl_qmap_init(cl_qmap_t * const p_map)
{
	assert(p_map);

	memset(p_map, 0, sizeof(cl_qmap_t));

	/* special setup for the root node */
	p_map->root.p_up = &p_map->root;
	p_map->root.p_left = &p_map->nil;
	p_map->root.p_right = &p_map->nil;
	p_map->root.color = CL_MAP_BLACK;

	/* Setup the node used as terminator for all leaves. */
	p_map->nil.p_up = &p_map->nil;
	p_map->nil.p_left = &p_map->nil;
	p_map->nil.p_right = &p_map->nil;
	p_map->nil.color = CL_MAP_BLACK;

	cl_qmap_remove_all(p_map);
}

cl_map_item_t *cl_qmap_get(const cl_qmap_t * const p_map,
			   const uint64_t key)
{
	cl_map_item_t *p_item;

	assert(p_map);

	p_item = __cl_map_root(p_map);

	while (p_item != &p_map->nil) {
		if (key == p_item->key)
			break;	/* just right */

		if (key < p_item->key)
			p_item = p_item->p_left;	/* too small */
		else
			p_item = p_item->p_right;	/* too big */
	}

	return (p_item);
}

cl_map_item_t *cl_qmap_get_next(const cl_qmap_t * const p_map,
				const uint64_t key)
{
	cl_map_item_t *p_item;
	cl_map_item_t *p_item_found;

	assert(p_map);

	p_item = __cl_map_root(p_map);
	p_item_found = (cl_map_item_t *) & p_map->nil;

	while (p_item != &p_map->nil) {
		if (key < p_item->key) {
			p_item_found = p_item;
			p_item = p_item->p_left;
		} else {
			p_item = p_item->p_right;
		}
	}

	return (p_item_found);
}

void cl_qmap_apply_func(const cl_qmap_t * const p_map,
			cl_pfn_qmap_apply_t pfn_func,
			const void *const context)
{
	cl_map_item_t *p_map_item;

	/* Note that context can have any arbitrary value. */
	assert(p_map);
	assert(pfn_func);

	p_map_item = cl_qmap_head(p_map);
	while (p_map_item != cl_qmap_end(p_map)) {
		pfn_func(p_map_item, (void *)context);
		p_map_item = cl_qmap_next(p_map_item);
	}
}

/*
 * Balance a tree starting at a given item back to the root.
 */
static void __cl_map_ins_bal(cl_qmap_t * const p_map,
			     cl_map_item_t * p_item)
{
	cl_map_item_t *p_grand_uncle;

	assert(p_map);
	assert(p_item);
	assert(p_item != &p_map->root);

	while (p_item->p_up->color == CL_MAP_RED) {
		if (__cl_map_is_left_child(p_item->p_up)) {
			p_grand_uncle = p_item->p_up->p_up->p_right;
			assert(p_grand_uncle);
			if (p_grand_uncle->color == CL_MAP_RED) {
				p_grand_uncle->color = CL_MAP_BLACK;
				p_item->p_up->color = CL_MAP_BLACK;
				p_item->p_up->p_up->color = CL_MAP_RED;
				p_item = p_item->p_up->p_up;
				continue;
			}

			if (!__cl_map_is_left_child(p_item)) {
				p_item = p_item->p_up;
				__cl_map_rot_left(p_map, p_item);
			}
			p_item->p_up->color = CL_MAP_BLACK;
			p_item->p_up->p_up->color = CL_MAP_RED;
			__cl_map_rot_right(p_map, p_item->p_up->p_up);
		} else {
			p_grand_uncle = p_item->p_up->p_up->p_left;
			assert(p_grand_uncle);
			if (p_grand_uncle->color == CL_MAP_RED) {
				p_grand_uncle->color = CL_MAP_BLACK;
				p_item->p_up->color = CL_MAP_BLACK;
				p_item->p_up->p_up->color = CL_MAP_RED;
				p_item = p_item->p_up->p_up;
				continue;
			}

			if (__cl_map_is_left_child(p_item)) {
				p_item = p_item->p_up;
				__cl_map_rot_right(p_map, p_item);
			}
			p_item->p_up->color = CL_MAP_BLACK;
			p_item->p_up->p_up->color = CL_MAP_RED;
			__cl_map_rot_left(p_map, p_item->p_up->p_up);
		}
	}
}

cl_map_item_t *cl_qmap_insert(cl_qmap_t * const p_map,
			      const uint64_t key,
			      cl_map_item_t * const p_item)
{
	cl_map_item_t *p_insert_at, *p_comp_item;

	assert(p_map);
	assert(p_item);
	assert(p_map->root.p_up == &p_map->root);
	assert(p_map->root.color != CL_MAP_RED);
	assert(p_map->nil.color != CL_MAP_RED);

	p_item->p_left = &p_map->nil;
	p_item->p_right = &p_map->nil;
	p_item->key = key;
	p_item->color = CL_MAP_RED;

	/* Find the insertion location. */
	p_insert_at = &p_map->root;
	p_comp_item = __cl_map_root(p_map);

	while (p_comp_item != &p_map->nil) {
		p_insert_at = p_comp_item;

		if (key == p_insert_at->key)
			return (p_insert_at);

		/* Traverse the tree until the correct insertion point is found. */
		if (key < p_insert_at->key)
			p_comp_item = p_insert_at->p_left;
		else
			p_comp_item = p_insert_at->p_right;
	}

	assert(p_insert_at != &p_map->nil);
	assert(p_comp_item == &p_map->nil);
	/* Insert the item. */
	if (p_insert_at == &p_map->root) {
		p_insert_at->p_left = p_item;
		/*
		 * Primitive insert places the new item in front of
		 * the existing item.
		 */
		__cl_primitive_insert(&p_map->nil.pool_item.list_item,
				      &p_item->pool_item.list_item);
	} else if (key < p_insert_at->key) {
		p_insert_at->p_left = p_item;
		/*
		 * Primitive insert places the new item in front of
		 * the existing item.
		 */
		__cl_primitive_insert(&p_insert_at->pool_item.list_item,
				      &p_item->pool_item.list_item);
	} else {
		p_insert_at->p_right = p_item;
		/*
		 * Primitive insert places the new item in front of
		 * the existing item.
		 */
		__cl_primitive_insert(p_insert_at->pool_item.list_item.p_next,
				      &p_item->pool_item.list_item);
	}
	/* Increase the count. */
	p_map->count++;

	p_item->p_up = p_insert_at;

	/*
	 * We have added depth to this section of the tree.
	 * Rebalance as necessary as we retrace our path through the tree
	 * and update colors.
	 */
	__cl_map_ins_bal(p_map, p_item);

	__cl_map_root(p_map)->color = CL_MAP_BLACK;

	/*
	 * Note that it is not necessary to re-color the nil node black because all
	 * red color assignments are made via the p_up pointer, and nil is never
	 * set as the value of a p_up pointer.
	 */

#ifdef _DEBUG_
	/* Set the pointer to the map in the map item for consistency checking. */
	p_item->p_map = p_map;
#endif

	return (p_item);
}

static void __cl_map_del_bal(cl_qmap_t * const p_map,
			     cl_map_item_t * p_item)
{
	cl_map_item_t *p_uncle;

	while ((p_item->color != CL_MAP_RED) && (p_item->p_up != &p_map->root)) {
		if (__cl_map_is_left_child(p_item)) {
			p_uncle = p_item->p_up->p_right;

			if (p_uncle->color == CL_MAP_RED) {
				p_uncle->color = CL_MAP_BLACK;
				p_item->p_up->color = CL_MAP_RED;
				__cl_map_rot_left(p_map, p_item->p_up);
				p_uncle = p_item->p_up->p_right;
			}

			if (p_uncle->p_right->color != CL_MAP_RED) {
				if (p_uncle->p_left->color != CL_MAP_RED) {
					p_uncle->color = CL_MAP_RED;
					p_item = p_item->p_up;
					continue;
				}

				p_uncle->p_left->color = CL_MAP_BLACK;
				p_uncle->color = CL_MAP_RED;
				__cl_map_rot_right(p_map, p_uncle);
				p_uncle = p_item->p_up->p_right;
			}
			p_uncle->color = p_item->p_up->color;
			p_item->p_up->color = CL_MAP_BLACK;
			p_uncle->p_right->color = CL_MAP_BLACK;
			__cl_map_rot_left(p_map, p_item->p_up);
			break;
		} else {
			p_uncle = p_item->p_up->p_left;

			if (p_uncle->color == CL_MAP_RED) {
				p_uncle->color = CL_MAP_BLACK;
				p_item->p_up->color = CL_MAP_RED;
				__cl_map_rot_right(p_map, p_item->p_up);
				p_uncle = p_item->p_up->p_left;
			}

			if (p_uncle->p_left->color != CL_MAP_RED) {
				if (p_uncle->p_right->color != CL_MAP_RED) {
					p_uncle->color = CL_MAP_RED;
					p_item = p_item->p_up;
					continue;
				}

				p_uncle->p_right->color = CL_MAP_BLACK;
				p_uncle->color = CL_MAP_RED;
				__cl_map_rot_left(p_map, p_uncle);
				p_uncle = p_item->p_up->p_left;
			}
			p_uncle->color = p_item->p_up->color;
			p_item->p_up->color = CL_MAP_BLACK;
			p_uncle->p_left->color = CL_MAP_BLACK;
			__cl_map_rot_right(p_map, p_item->p_up);
			break;
		}
	}
	p_item->color = CL_MAP_BLACK;
}

void cl_qmap_remove_item(cl_qmap_t * const p_map,
			 cl_map_item_t * const p_item)
{
	cl_map_item_t *p_child, *p_del_item;

	assert(p_map);
	assert(p_item);

	if (p_item == cl_qmap_end(p_map))
		return;

	if ((p_item->p_right == &p_map->nil) || (p_item->p_left == &p_map->nil)) {
		/* The item being removed has children on at most on side. */
		p_del_item = p_item;
	} else {
		/*
		 * The item being removed has children on both side.
		 * We select the item that will replace it.  After removing
		 * the substitute item and rebalancing, the tree will have the
		 * correct topology.  Exchanging the substitute for the item
		 * will finalize the removal.
		 */
		p_del_item = cl_qmap_next(p_item);
		assert(p_del_item != &p_map->nil);
	}

	/* Remove the item from the list. */
	__cl_primitive_remove(&p_item->pool_item.list_item);
	/* Decrement the item count. */
	p_map->count--;

	/* Get the pointer to the new root's child, if any. */
	if (p_del_item->p_left != &p_map->nil)
		p_child = p_del_item->p_left;
	else
		p_child = p_del_item->p_right;

	/*
	 * This assignment may modify the parent pointer of the nil node.
	 * This is inconsequential.
	 */
	p_child->p_up = p_del_item->p_up;
	(*__cl_map_get_parent_ptr_to_item(p_del_item)) = p_child;

	if (p_del_item->color != CL_MAP_RED)
		__cl_map_del_bal(p_map, p_child);

	/*
	 * Note that the splicing done below does not need to occur before
	 * the tree is balanced, since the actual topology changes are made by the
	 * preceding code.  The topology is preserved by the color assignment made
	 * below (reader should be reminded that p_del_item == p_item in some cases).
	 */
	if (p_del_item != p_item) {
		/*
		 * Finalize the removal of the specified item by exchanging it with
		 * the substitute which we removed above.
		 */
		p_del_item->p_up = p_item->p_up;
		p_del_item->p_left = p_item->p_left;
		p_del_item->p_right = p_item->p_right;
		(*__cl_map_get_parent_ptr_to_item(p_item)) = p_del_item;
		p_item->p_right->p_up = p_del_item;
		p_item->p_left->p_up = p_del_item;
		p_del_item->color = p_item->color;
	}

	assert(p_map->nil.color != CL_MAP_RED);

#ifdef _DEBUG_
	/* Clear the pointer to the map since the item has been removed. */
	p_item->p_map = NULL;
#endif
}

cl_map_item_t *cl_qmap_remove(cl_qmap_t * const p_map, const uint64_t key)
{
	cl_map_item_t *p_item;

	assert(p_map);

	/* Seek the node with the specified key */
	p_item = cl_qmap_get(p_map, key);

	cl_qmap_remove_item(p_map, p_item);

	return (p_item);
}

void cl_qmap_merge(cl_qmap_t * const p_dest_map,
		   cl_qmap_t * const p_src_map)
{
	cl_map_item_t *p_item, *p_item2, *p_next;

	assert(p_dest_map);
	assert(p_src_map);

	p_item = cl_qmap_head(p_src_map);

	while (p_item != cl_qmap_end(p_src_map)) {
		p_next = cl_qmap_next(p_item);

		/* Remove the item from its current map. */
		cl_qmap_remove_item(p_src_map, p_item);
		/* Insert the item into the destination map. */
		p_item2 =
		    cl_qmap_insert(p_dest_map, cl_qmap_key(p_item), p_item);
		/* Check that the item was successfully inserted. */
		if (p_item2 != p_item) {
			/* Put the item in back in the source map. */
			p_item2 =
			    cl_qmap_insert(p_src_map, cl_qmap_key(p_item),
					   p_item);
			assert(p_item2 == p_item);
		}
		p_item = p_next;
	}
}

static void __cl_qmap_delta_move(cl_qmap_t * const p_dest,
				 cl_qmap_t * const p_src,
				 cl_map_item_t ** const pp_item)
{
	cl_map_item_t __attribute__((__unused__)) *p_temp;
	cl_map_item_t *p_next;

	/*
	 * Get the next item so that we can ensure that pp_item points to
	 * a valid item upon return from the function.
	 */
	p_next = cl_qmap_next(*pp_item);
	/* Move the old item from its current map the the old map. */
	cl_qmap_remove_item(p_src, *pp_item);
	p_temp = cl_qmap_insert(p_dest, cl_qmap_key(*pp_item), *pp_item);
	/* We should never have duplicates. */
	assert(p_temp == *pp_item);
	/* Point pp_item to a valid item in the source map. */
	(*pp_item) = p_next;
}

void cl_qmap_delta(cl_qmap_t * const p_map1,
		   cl_qmap_t * const p_map2,
		   cl_qmap_t * const p_new, cl_qmap_t * const p_old)
{
	cl_map_item_t *p_item1, *p_item2;
	uint64_t key1, key2;

	assert(p_map1);
	assert(p_map2);
	assert(p_new);
	assert(p_old);
	assert(cl_is_qmap_empty(p_new));
	assert(cl_is_qmap_empty(p_old));

	p_item1 = cl_qmap_head(p_map1);
	p_item2 = cl_qmap_head(p_map2);

	while (p_item1 != cl_qmap_end(p_map1) && p_item2 != cl_qmap_end(p_map2)) {
		key1 = cl_qmap_key(p_item1);
		key2 = cl_qmap_key(p_item2);
		if (key1 < key2) {
			/* We found an old item. */
			__cl_qmap_delta_move(p_old, p_map1, &p_item1);
		} else if (key1 > key2) {
			/* We found a new item. */
			__cl_qmap_delta_move(p_new, p_map2, &p_item2);
		} else {
			/* Move both forward since they have the same key. */
			p_item1 = cl_qmap_next(p_item1);
			p_item2 = cl_qmap_next(p_item2);
		}
	}

	/* Process the remainder if the end of either source map was reached. */
	while (p_item2 != cl_qmap_end(p_map2))
		__cl_qmap_delta_move(p_new, p_map2, &p_item2);

	while (p_item1 != cl_qmap_end(p_map1))
		__cl_qmap_delta_move(p_old, p_map1, &p_item1);
}