/* * LibAxl: Another XML library * Copyright (C) 2013 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 #include #include #include #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; } /** * @} */