File:  [ELWIX - Embedded LightWeight unIX -] / gpl / axl / src / axl_stack.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Feb 17 12:50:03 2012 UTC (12 years, 8 months ago) by misho
Branches: axl, MAIN
CVS tags: HEAD, AXL0_6_7
version 0.6.7

/*
 *  LibAxl:  Another XML library
 *  Copyright (C) 2006 Advanced Software Production Line, S.L.
 *
 *  This program 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 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to the Free
 *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307 USA
 *  
 *  You may find a copy of the license under this software is released
 *  at COPYING file. This is LGPL software: you are welcome to
 *  develop proprietary applications using this library without any
 *  royalty or fee but returning back any change, improvement or
 *  addition in the form of source code, project image, documentation
 *  patches, etc. 
 *
 *  For commercial support on build XML enabled solutions contact us:
 *          
 *      Postal address:
 *         Advanced Software Production Line, S.L.
 *         Edificio Alius A, Oficina 102,
 *         C/ Antonio Suarez Nº 10,
 *         Alcalá de Henares 28802 Madrid
 *         Spain
 *
 *      Email address:
 *         info@aspl.es - http://www.aspl.es/xml
 */

#include <axl_decl.h>
#include <axl_stack.h>
#include <axl_list.h>
#include <axl_log.h>
#define LOG_DOMAIN "axl-stack"

struct _axlStack {
	axlPointer     * stack;
	int              size;
	int              items;
	axlDestroyFunc   destroy;
};

/**
 * \defgroup axl_stack_module Axl Stack: A stack built used across AXL library
 */

/** 
 * \addtogroup axl_stack_module
 * @{
 */

/** 
 * @brief Creates a new stack.
 *
 * Creates a new \ref axlStack object, which will accept to store a
 * retrieve objects in a FIFO manner.
 * 
 * @param destroy_data A function to be used to destroy data stored on
 * the stack is the stack is deallocated containing data. This
 * parameter is optional. If not provided, no automatic memory
 * deallocation will peformed.
 * 
 * @return A newly allocated stack that must be deallocated by using
 * axl_stack_destroy.
 */
axlStack * axl_stack_new (axlDestroyFunc destroy_data)
{
	axlStack * stack;

	/* create an empty stack */
	stack          = axl_new (axlStack, 1);
	/* check returned value */
	if (stack == NULL)
		return NULL;
	stack->destroy = destroy_data;

	return stack;
}

/** 
 * @brief Push data on top of the stack.
 *
 * The stack doesn't support storing NULL references. If a null
 * reference is provided the function won't perform any operation on
 * the stack.
 * 
 * @param stack The stack where the data will be pushed.
 * @param data The data to push.
 */
void       axl_stack_push (axlStack * stack, axlPointer data)
{
	axlPointer * temp;

	axl_return_if_fail (stack);
	axl_return_if_fail (data);

	/* check if we have enough space to store the pointer */
	if (stack->size == stack->items) {
		if (stack->size == 0) {
			/* ask for a single pointer */
			stack->stack = axl_new (axlPointer, 1);

			/* check allocated result */
			if (stack->stack == NULL)
				return;
		}else {
			/* ask to grow the memory already allocated */
			temp         = stack->stack;
			stack->stack = realloc (stack->stack, sizeof (axlPointer) * (stack->size + 1));
			/* check allocated value */
			if (stack->stack == NULL) {
				stack->stack = temp;
				return;
			} /* end if */

		}
		/* update the size available */
		stack->size++;
	}

	/* store the item */
	stack->stack[stack->items] = data;
	
	/* update items stored */
	stack->items++;
	
	return;
}

/** 
 * @brief Pop data from the stack.
 * 
 * @param stack The stack where the pop operation will be performed.
 * 
 * @return A \ref axlPointer containing previously pushed data or NULL
 * if fails.
 */
axlPointer axl_stack_pop  (axlStack * stack)
{
	axlPointer pointer;

	axl_return_val_if_fail (stack, NULL);

	/* do not perform any operation if the stack is empty */
	if (axl_stack_is_empty (stack))
		return NULL;

	/* reduce the items stored */
	stack->items--;

	/* return the pointer */
	pointer = stack->stack[stack->items];

	return pointer;
}

/** 
 * @brief Allows to get current current element at the top of the
 * stack without removing it.
 *
 * This function allows to perform the same operation like pop (\ref
 * axl_stack_pop) function, but without removing the element from the
 * top.
 * 
 * @param stack The stack where the element from the top will be returned.
 * 
 * @return An \ref axlPointer reference or NULL if fails.
 */
axlPointer axl_stack_peek (axlStack * stack)
{
	axl_return_val_if_fail (stack, NULL);

	/* do not perform any operation if the stack is empty */
	if (axl_stack_is_empty (stack)) {
		return NULL;
	}

	/* return the very first element without poping */
	return stack->stack[stack->items - 1];
}

/** 
 * @internal Common support function for foreach over an stack for all
 * foreach functions defined.
 */
axl_bool       __axl_stack_foreach_common (axlStack         * stack, 
					   axlStackForeach2   func,
					   axlStackForeach3   func3,
					   axlPointer         user_data, 
					   axlPointer         user_data2,
					   axlPointer         user_data3)
{
	int iterator;

	axl_return_val_if_fail (stack, axl_false);

	/* for each item inside the stack */
	iterator = 0;
	while (iterator < stack->items) {
		/* call fo the function and check returning value  */
		if (func != NULL && func (stack->stack [stack->items - iterator - 1],  user_data,  user_data2))
			return axl_false;

		/* call fo the function and check returning value  */
		if (func3 != NULL && func3 (stack->stack [stack->items - iterator - 1],  user_data,  user_data2, user_data3))
			return axl_false;

		/* update the iterator */
		iterator ++;
	}

	/* iteration performed completely */
	return axl_true;
}

/** 
 * @brief Allows to perform a foreach operation from the head of the
 * stack (the next item to be poped) to the tail of the stack (the
 * very first item pushed).
 *
 * The foreach process is non intrusive: it doesn't perform any change
 * of the stack, but allows to traverse all items in the natural order
 * in which items are stored (push) and removed (pop).
 *
 * The function provided to perform the foreach operation will be
 * called providing the stack data found, and the two user defined
 * pointers provided. 
 * 
 * @param stack The stack where the foreach operation will be
 * performed.
 *
 * @param func The foreach function to be called for each item found
 * in the stack.
 *
 * @param user_data User defined pointer to be passed to the function
 * provided.
 *
 * @param user_data2 User defined pointer to be passed to the function
 * provided.
 * 
 * @return \ref axl_true if the foreach process was performed completely
 * through all items inside the stack or \ref axl_false if not. The
 * function will also return \ref axl_false to indicate a failure the
 * stack and func parameters are null.
 */
axl_bool       axl_stack_foreach (axlStack         * stack, 
				  axlStackForeach2   func,
				  axlPointer         user_data, 
				  axlPointer         user_data2)
{
	/* call to common function */
	return __axl_stack_foreach_common (stack, func, NULL, user_data, user_data2, NULL);
}


/** 
 * @brief Allows to perform a foreach operation from the head of the
 * stack (the next item to be poped) to the tail of the stack (the
 * very first item pushed).
 *
 * The foreach process is non intrusive: it doesn't perform any change
 * of the stack, but allows to traverse all items in the natural order
 * in which items are stored (push) and removed (pop).
 *
 * The function provided to perform the foreach operation will be
 * called providing the stack data found, and the three user defined
 * pointers provided.
 * 
 * @param stack The stack where the foreach operation will be
 * performed.
 *
 * @param func The foreach function to be called for each item found
 * in the stack.
 *
 * @param user_data User defined pointer to be passed to the function
 * provided.
 *
 * @param user_data2 User defined pointer to be passed to the function
 * provided.
 *
 * @param user_data3 User defined pointer to be passed to the function
 * provided.
 * 
 * @return \ref axl_true if the foreach process was performed completely
 * through all items inside the stack or \ref axl_false if not. The
 * function will also return \ref axl_false to indicate a failure the
 * stack and func parameters are null.
 */
axl_bool       axl_stack_foreach3 (axlStack         * stack, 
				   axlStackForeach3   func,
				   axlPointer         user_data,
				   axlPointer         user_data2,
				   axlPointer         user_data3)
{
	/* call to common function */
	return __axl_stack_foreach_common (stack, NULL, func, user_data, user_data2, user_data3);
}

/** 
 * @brief Returns current stack size, that is, elements stored on the
 * stack.
 * 
 * @param stack The stack where the operation will performed.
 * 
 * @return The number of items stored.
 */
int        axl_stack_size (axlStack * stack)
{
	axl_return_val_if_fail (stack, -1);

	/* current items stored */
	return stack->items;
}


/** 
 * @brief Allows to check if the given stack is empty.
 * 
 * @param stack The stack to check.
 * 
 * @return \ref axl_true if the stack is empty or axl_false if not.
 */
axl_bool       axl_stack_is_empty (axlStack * stack)
{
	axl_return_val_if_fail (stack, axl_false);
	
	/* return if there are stored items in the stack */
	return (stack->items == 0);
}

/** 
 * @brief Destroy the given stack.
 * 
 * @param stack The stack to destroy.
 */
void       axl_stack_free (axlStack * stack)
{
	axl_return_if_fail (stack);

	/* destroy the list inside (if the destroy function is
	 * defined) */
	if (stack->destroy) {
		while (! axl_stack_is_empty (stack)) {
			
			/* destroy */
			stack->destroy (axl_stack_pop (stack));
			
		} /* end while */
	} /* end if */

	/* free stack array */
	axl_free (stack->stack);
	
	/* destroy the stack */
	axl_free (stack);

	return;
}

/* @} */

typedef struct _axlBinaryStackNode {
	int      count;
	axl_bool state;
} axlBinaryStackNode;


struct _axlBinaryStack {
	axlBinaryStackNode * last;
	axlStack           * stack;
	axlFactory         * factory;
	int                  count;
};

/**
 * \defgroup axl_binary_stack_module Axl Binary Stack: A compact binary state stack
 */

/** 
 * \addtogroup axl_binary_stack_module
 * @{
 */

/** 
 * @brief Allows to create a compact binary state stack.
 * 
 * To stack is designed to store boolean values in an efficient
 * way. In idea behind this structure is that storing 10 boolean axl_true
 * values only holds a node, with the axl_true value and the count of
 * items that previously was signaled with the same value. 
 *
 * The intention is to compact a binary succession of data into a list
 * of touples signaling the state and the number of times it was
 * signaled.
 *
 * This structure is meant to be more efficient than a plain \ref
 * axlStack but there is a case where is not more efficient, that is,
 * when all stored boolean values alternates.
 * 
 * @return A newly allocated binary stack. Push boolean values with
 * \ref axl_binary_stack_push. Pop previous stored value with \ref
 * axl_binary_stack_pop. Once finished, use \ref axl_binary_stack_free
 * to free the binary stack.
 */
axlBinaryStack * axl_binary_stack_new (void)
{
	axlBinaryStack * result;

	result           = axl_new (axlBinaryStack, 1);
	/* check allocated value */
	if (result == NULL)
		return NULL;
	result->stack    = axl_stack_new (axl_free);

	/* return stack created */
	return result;
} 

/** 
 * @brief Push a new boolean item into the binary stack.
 * 
 * @param bstack The binary stack
 *
 * @param state The state to push into the queue.
 */
void             axl_binary_stack_push (axlBinaryStack * bstack, axl_bool state)
{
	axlBinaryStackNode * node;

	/* check references */
	axl_return_if_fail (bstack);
	
	/* check basic case */
	if (bstack->last == NULL) {

	axl_binary_stack_push_create_and_return:

		/* configure last */
		node         = axl_new (axlBinaryStackNode, 1);
		/* check allocated value */
		if (node == NULL)
			return;
		node->count  = 1;
		node->state  = state;

		/* configure */
		bstack->last = node;

		/* store in the stack */
/*		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "Storing first item: 0x%x", node); */
		axl_stack_push (bstack->stack, node);
		
		/* update count */
		bstack->count++;

/*		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "Storing item state=%d, count=%d, bcount=%d",
		node->state, node->count, bstack->count); */

		return;
	} /* end if */

	/* default case, check current state */
	if (bstack->last->state == state) {

		/* found same state, increase count and return */
		bstack->last->count++;

		/* update count */
		bstack->count++;

/*		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "Found same state, reusing..state=%d, count=%d, bcount=%d",
		bstack->last->state, bstack->last->count, bstack->count); */

		return;
	} /* end if */

/*	__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "Found state changed, creating..state=%d (previuos state: %d), count=%d, bcount=%d",
	state, bstack->last->state, bstack->last->count, bstack->count); */

	/* if reached this place, create a new node */
	goto axl_binary_stack_push_create_and_return;
}

/** 
 * @brief Convenience function that allows to push a new value using
 * the last one pushed in the stack. This function allows to implement
 * the same feature provided by peeking (\ref axl_binary_stack_peek)
 * the next visible value on the stack head and then pushing it again.
 * 
 * @param bstack The binary stack where to perform the operation. 
 *
 * NOTE: The stack must not be empty before calling to this
 * function. It has to have at least one value already pushed.
 */
void             axl_binary_stack_push_the_same (axlBinaryStack * bstack)
{
	axl_return_if_fail (bstack);
	axl_return_if_fail (bstack->last);
	
	/* update stack */
	bstack->last->count++;
	bstack->count++;

	return;
}

/** 
 * @brief Allows to pop the next value from the stack.
 * 
 * @param bstack The binary stack where the next value will be
 * restored.
 * 
 * @return The next value stored in the stack. You must check if the
 * stack is empty before calling to this function since you won't be
 * able to differenciate a axl_false value stored from a axl_false value
 * returned by an error.
 */
axl_bool             axl_binary_stack_pop  (axlBinaryStack * bstack)
{
	axlBinaryStackNode     * node;
	axl_bool                 state;
	axl_return_val_if_fail (bstack, axl_false);

	/* get last value */
	node = bstack->last; 
	if (node == NULL) {
		/* return axl_false because the stack is empty */
		return axl_false;
	} /* end if */

	/* check if there are more than 1 item, that is, at least
	 * 2. */
	if (node->count > 1) {

		/* update count */
		bstack->count--;

		/* decrease the counter and return */
		node->count--;

/*		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "POP: found state with more than 1 item, poping: state=%d, count=%d, bcount=%d",
		node->state, node->count, bstack->count); */

		return node->state;

	} else if (node->count == 1) {

/*		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "POP: found state with only 1 item, poping: state=%d, count=%d, bcount=%d",
		node->state, node->count, bstack->count); */

		/* get the value */
		state = node->state;
		
		/* update the next last (remove the current last) and
		 * use the next */
		axl_stack_pop (bstack->stack);
		bstack->last = axl_stack_peek (bstack->stack);

		/* release to the factory */
/*		__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "POP: new node to use: 0x%x, about to release 0x%x", 
		bstack->last, node); */
		axl_free (node);

		/* update count */
		bstack->count--;

		node = bstack->last;
/*		if (node != NULL) {
			__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "POP: after poping: state=%d, count=%d, bcount=%d",
				   node->state, node->count, bstack->count);
		} */

		return state;
	} /* end if */

	/* this shouldn't be reached */
	return axl_false;
}

/** 
 * @brief Allows to get the current stack head value stored without
 * removing it from the stack.
 * 
 * @param bstack The binary stack where to perform the operation.
 * 
 * @return The value stored in the stack head. The stack must contain
 * at least one value, otherwise the returned value is not
 * reliable. See \ref axl_binary_stack_is_empty to avoid calling to this function.
 */
axl_bool             axl_binary_stack_peek (axlBinaryStack * bstack)
{
	axl_return_val_if_fail (bstack, axl_false);

	/* return current state */
	return (bstack->last && bstack->last->state);
}

/** 
 * @brief Check current emptyness status for the provided binary
 * stack.
 * 
 * @param bstack The binary stack to check.
 * 
 * @return axl_true if the binary stack is empty, otherwise axl_false is
 * returned.
 */
axl_bool             axl_binary_stack_is_empty (axlBinaryStack * bstack)
{
	axl_return_val_if_fail (bstack, axl_false);

	/* return current emptyness status */
	return (bstack->last == NULL && axl_stack_is_empty (bstack->stack));
}

/** 
 * @brief Allows to get current number of items stored.
 * 
 * @param bstack The binary stack to check current number of items
 * stored.
 * 
 * @return The number of items stored or -1 if it fails.
 */
int              axl_binary_stack_size     (axlBinaryStack * bstack)
{
	axl_return_val_if_fail (bstack, -1);

	/* return item count */
	return bstack->count;
}

/** 
 * @brief Release the provided binary stack.
 * 
 * @param bstack The binary stack to release.
 */
void             axl_binary_stack_free (axlBinaryStack * bstack)
{
	if (bstack == NULL)
		return;

	/* free resources */
	axl_stack_free (bstack->stack);
	axl_free (bstack);

	return;
}

/**
 * @}
 */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>