/*************************************************************************
  (c) Copyright.  Digital Equipment Corporation, 1995.  All Rights
  Reserved.

  Permission is hereby granted to use, copy, modify, or enhance this 
  software freely, as long as the foregoing copyright of Digital Equipment
  Corporation and this notice are retained on the software.  This 
  software may not be distributed or sublicensed for a fee.  Digital      
  makes this software available "AS IS" and without warranties of any
  kind.  
 *************************************************************************/
/*
 * Marko Kiiskila carnil@cs.tut.fi 
 * 
 * Tampere University of Technology - Telecommunications Laboratory
 *
 * Permission to use, copy, modify and distribute this
 * software and its documentation is hereby granted,
 * provided that both the copyright notice and this
 * permission notice appear in all copies of the software,
 * derivative works or modified versions, and any portions
 * thereof, that both notices appear in supporting
 * documentation, and that the use of this software is
 * acknowledged in any publications resulting from using
 * the software.
 * 
 * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
 * SOFTWARE.
 * 
 */

/*
* Module Name:
*   utl.h
*   
* Abstract:
*   This is the interface for the basic functions that are provided by the OS.
*
* Authors:
*   DMW - Douglas M. Washabaugh
*   JAD - John A. Denisco
*   TLR - Theodore L. Ross
*
* Modification History:
*   Date       Name  Description 
*   08-Aug-94  DMW   Created.
*   04-Oct-94  DMW   Changed ULONG to UINT32.
*   19-Oct-94  TLR   Added Simple List Utilities.
*   26-Oct-94  TLR   Updated Simple List Utilities based on review, retested.
*   10-Nov-94  DMW   Added the buffer descriptor and basic access macros.
*   05-Dec-94  DMW   Added an ALIGN macro.
*   11-Jan-95  TLR   Added utl_list_insert_after
*   12-Jan-95  DMW   Added MIN and MAX macros.
*   18-Jan-95  JAD   Moved BUFFER_DESCR and associated maros from here to
*                    ss_ptsp.c (signaling module).
*   22-Mar-95  TLR   Added utl_buff_zero, utl_buff_copy, and utl_list_dealloc.
*   23-Mar-95  DMW   Changed name of utl_buff_zero and utl_buff_copy to 
*                    utl_mem_zero and utl_mem_copy to avoid confusion with
*                    operating system buffers.
*
* Description:
*   This module contains general utility routines that are independent of the
*   operating system.
*
*   Many of the functions deal with setting default values.  This assists in
*   the situation that the caller has a value with several distinct fields,
*   all of which but one must be left with their "current" (default) values.
*   Without a helper routine, the caller should have to perform shifts, and
*   "and" operations.
* 
*/

/* Define minimum and maximum macros.  Each return the value of the min or max. */

#ifndef VW_DEFS
#define MAX(A,B) ((A) > (B) ? (A) : (B))
#define MIN(A,B) ((A) < (B) ? (A) : (B))
#endif

/* SIMPLE_LIST
 *
 *  SIMPLE_LIST is a macro that creates a list of elements of a certain type.
 *  The macro has two arguments:
 *
 *      type    - The data type for elements that will be stored in the list.
 *      name    - The name of the resulting list type.  
 *
 *  For a structure to be stored in a simple list, its first member must be a
 *  pointer to that same kind of structure.  For example, a list of names
 *  would use a structure like the following:
 *
 *      struct name_tag
 *         {
 *         struct name_tag *p_next;
 *         char            *text;
 *         } NAME_RECORD;
 *
 *  To create a type for a simple list of names, the following declaration
 *  can be used:
 *
 *      SIMPLE_LIST (NAME_RECORD, NAME_LIST);
 *
 *  NAME_LIST is now a type for a list of elements of type NAME_RECORD.  An
 *  example of its usage is:
 *
 *      NAME_LIST   list_of_names;
 *
 */
#define SIMPLE_LIST(type, name) typedef struct     \
                                   {               \
                                   type   *p_root; \
                                   type   *p_tmp;  \
                                   int     size;   \
                                   } name

/*++
* =========================
* = utl_list_init (macro) =
* =========================
*
* Overview:
*   Initializes an instance of a simple list.  Simple lists MUST be
*   initialized before they can be used for any other purpose.
*
* Arguments:
*   list    - (IO) Simple List to be initialized.
* 
* Returns:
*   None
*
* Preconditions:
*   None
*
* Postconditions:
*   The simple list contains zero entries.
--*/
#define utl_list_init(list) \
   {                        \
   (list).p_root = NULL;    \
   (list).size   = 0;       \
   }

/*++
* ========================
* = utl_list_add (macro) =
* ========================
*
* Overview:
*   Adds a single element to a simple list.  The element is added to the
*   head of the list.
*
* Arguments:
*   list        - (IO) Simple list to be modified (not a pointer to the list).
*   p_element   - (IN) Pointer to the element that is to be added to the list.
* 
* Returns:
*   None
*
* Preconditions:
*   None
*
* Postconditions:
*   The element is a member of the list.
*
*   The size of the list stored in the list header has been incremented by one.
--*/
#define utl_list_add(list, p_element)   \
   {                                    \
   (p_element)->p_next = (list).p_root; \
   (list).p_root       = p_element;     \
   (list).size        += 1;             \
   }

/*++
* =================================
* = utl_list_insert_after (macro) =
* =================================
*
* Overview:
*   Adds a single element to a simple list.  The element is inserted after
*   a specified key element.
*
* Usage:
*   This macro may be used to insert an element into an ordered list.
*   The following is an example of such an application:
*
*       typedef struct _int_tag
*          {
*          struct _int_tag  *p_next;
*          int               data;
*          } INT_RECORD;
*
*       SIMPLE_LIST (INT_RECORD, INT_LIST);
*       .
*       .
*       .
*       INT_RECORD  *p_current, *p_prev;
*       INT_LIST    int_list
*
*       p_prev = NULL;
*       utl_list_traverse (int_list, p_current);
*          {
*          if (p_new_data->data <= p_current->data)
*             {
*             utl_list_insert_after (int_list, p_prev, p_new_data);
*             break;
*             }
*          p_prev = p_current;
*          }
*       if (p_current == NULL)
*          utl_list_insert_after (int_list, p_prev, p_new_data);
*
* Arguments:
*   list        - (IO) Simple list to be modified (not a pointer to the list).
*   p_key       - (IN) Pointer to the key element.
*   p_element   - (IN) Pointer to the element that is to be added to the list.
* 
* Returns:
*   None
*
* Preconditions:
*   p_key points to an element that is a member of the list or is NULL.
*
* Postconditions:
*   The element is a member of the list and directly follows the key element.
*   If the key pointer is NULL, the new element is at the head of the list.
*
*   The size of the list stored in the list header has been incremented by one.
--*/
#define utl_list_insert_after(list, p_key, p_element) \
   {                                                  \
   if ((p_key) == NULL)                               \
      {                                               \
      (p_element)->p_next = (list).p_root;            \
      (list).p_root       = (p_element);              \
      }                                               \
   else                                               \
      {                                               \
      (p_element)->p_next = (p_key)->p_next;          \
      (p_key)->p_next     = (p_element);              \
      }                                               \
   (list).size        += 1;                           \
   }

/*++
* ===========================
* = utl_list_delete (macro) =
* ===========================
*
* Overview:
*   Deletes a single element from a simple list.  The element to be deleted
*   is identified by its address.
*
* Arguments:
*   list        - (IO) Simple list to be modified.
*   p_element   - (IN) Pointer to the element that is to be removed from
*                      the list.
*
* Returns:
*   None
*
* Preconditions:
*   None
*
* Postconditions:
*   The element is a not member of the list.
*
*   If the element was a member of the list, the size of the list has been
*   decremented  by one.  Otherwise, nothing happens.
--*/
#define utl_list_delete(list, p_element)                                     \
	{                                                                         \
	if ((list).p_root == p_element)                                           \
		{                                                                      \
		/* First, handle the case in which the element to be deleted is at */  \
		/* the root of the list.                                           */  \
		(list).p_root = (p_element)->p_next;                                   \
		(list).size  -= 1;                                                     \
		}                                                                      \
	else                                                                      \
		{                                                                      \
		/* Handle the case in which the element to be deleted is deeper */     \
		/* into the list than the root element.                         */     \
		(list).p_tmp = (list).p_root;                                          \
		while (((list).p_tmp != NULL) && ((list).p_tmp->p_next != p_element))  \
			(list).p_tmp = (list).p_tmp->p_next;                                \
																									  \
		/* If the target element has been found (not the end of the list), */  \
		/* unlink the target element and decrement the size by 1.          */  \
		if ((list).p_tmp != NULL)                                              \
			{                                                                   \
			(list).p_tmp->p_next = (p_element)->p_next;                         \
			(list).size         -= 1;                                           \
			}                                                                   \
		}                                                                      \
	}

/*++
* =============================
* = utl_list_find_del (macro) =
* =============================
*
* Overview:
*   Removes a single element from a simple list.  The element to be removed
*   is identified by its address. If the element is found on the list is
*   found "found" is set to TRUE, if not it is set to FALSE.
*
* Arguments:
*   list        - (IO)  Simple list to be modified.
*   p_element   - (IN)  Pointer to the element that is to be removed from
*                       the list.
*   found       - (OUT) TRUE iff the element on the list is found.
*
* Returns:
*   None
*
* Preconditions:
*   None
*
* Postconditions:
*   The element is a not member of the list.
*
*   If the element was a member of the list, the size of the list has been
*   decremented  by one.  Otherwise, nothing happens.
--*/

#define utl_list_find_del(list, p_element, found)                                     \
	{                                                                         \
	if ((list).p_root == p_element)                                           \
		{                                                                      \
		/* First, handle the case in which the element to be deleted is at */  \
		/* the root of the list.                                           */  \
		(list).p_root = (p_element)->p_next;                                   \
		(list).size  -= 1;                                                     \
		found = TRUE;                                                          \
		}                                                                      \
	else                                                                      \
		{                                                                      \
		/* Handle the case in which the element to be deleted is deeper */     \
		/* into the list than the root element.                         */     \
		(list).p_tmp = (list).p_root;                                          \
		while (((list).p_tmp != NULL) && ((list).p_tmp->p_next != p_element))  \
			(list).p_tmp = (list).p_tmp->p_next;                                \
																									  \
		/* If the target element has been found (not the end of the list), */  \
		/* unlink the target element and decrement the size by 1.          */  \
		if ((list).p_tmp != NULL)                                              \
			{                                                                   \
			(list).p_tmp->p_next = (p_element)->p_next;                         \
			(list).size         -= 1;                                           \
			found = TRUE;                                                       \
			}                                                                   \
		else                                                                   \
			{                                                                   \
			found = FALSE;                                                      \
			}                                                                   \
		}                                                                      \
	}

/*++
* =========================
* = utl_list_find (macro) =
* =========================
*
* Overview:
*   Finds a specified element on a list. If the element is found on the
*   list is "found" is set to TRUE, if not it is set to FALSE.
*
* Arguments:
*   list        - (IO)  Simple list to be examined.
*   p_element   - (IN)  Pointer to the element that is to be tested for.
*   found       - (OUT) TRUE iff the element on the list is found.
*
* Returns:
*   None
*
--*/

#define utl_list_find(list, p_element, found)                                     \
	{                                                                         \
	if ((list).p_root == p_element)                                           \
		{                                                                      \
		/* First, handle the case in which the element to be found is at   */  \
		/* the root of the list.                                           */  \
		found = TRUE;                                                          \
		}                                                                      \
	else                                                                      \
		{                                                                      \
		/* Handle the case in which the element to be deleted is deeper */     \
		/* into the list than the root element.                         */     \
		(list).p_tmp = (list).p_root;                                          \
		while (((list).p_tmp != NULL) && ((list).p_tmp->p_next != p_element))  \
			(list).p_tmp = (list).p_tmp->p_next;                                \
																									  \
		/* If the target element has been found (not the end of the list), */  \
		/* set the flag.                                                   */  \
		if ((list).p_tmp != NULL)                                              \
			found = TRUE;                                                       \
		else                                                                   \
			found = FALSE;                                                      \
		}                                                                      \
	}

/*++
* =============================
* = utl_list_traverse (macro) =
* =============================
*
* Overview:
*   Allows a simple list to be easily traversed.  This macro is not in the
*   form of a function but in the form of a loop.
*
* Usage:
*   The utl_list_traverse macro should be used as a loop construct much like
*   a 'for' loop is used.  utl_list_traverse must be followed by a statement
*   or bracketed group of statements.  For example:
*
*       NAME_LIST    name_list;
*       NAME_RECORD *p_name_record;
*
*       printf ("Names in the List:\n");
*       utl_list_traverse (name_list, p_name_record)
*          {
*          printf ("  %s\n", p_name_record->text);
*          }
*
*   Note that if the list is empty, the body of the loop will not execute.
*
*   Do NOT use the utl_list_traverse macro to iteratively deallocate all of
*   the elements in the list!  Use the utl_list_dealloc macro for this.
*
* Arguments:
*   list    - (IN)  The simple list to be traversed.
*
*   p_index - (OUT) The loop index.  This is a pointer to the structure stored
*                   in the list.  The loop executes once for each element
*                   in the list and the p_index points to the element being
*                   processed for each iteration.
* 
* Returns:
*   Not Applicable
*
* Preconditions:
*   None
*
* Postconditions:
*   None
--*/
#define utl_list_traverse(list, p_index) for (p_index  = (list).p_root; \
                                              p_index != NULL;          \
                                              p_index  = (p_index)->p_next)

/*++
* ============================
* = utl_list_dealloc (macro) =
* ============================
*
* Overview:
*   Delete and deallocate (using os_mem_dealloc) all elements in the simple
*   list.
*
*   Note that if the list elements contain pointers to allocated data, this
*   data will be lost.  First use utl_list_traverse to deallocate all pointers
*   within the element structure.  Use utl_list_dealloc to deallocate the
*   actual element structures within the list.
*
* Arguments:
*   os_handle  - (IN)  Handle of the appropriate utl_os instance.
*   list       - (IN)  Simple list to be cleared.
* 
* Returns:
*   Not Applicable
*
* Preconditions:
*   None
*
* Postconditions:
*   The list is empty and each element has been deallocated.
--*/
#define utl_list_dealloc(list)       \
   {                                            \
   while ((list).p_root)                        \
      {                                         \
      (list).p_tmp  = (list).p_root;            \
      (list).p_root = (list).p_root->p_next;    \
      os_mem_dealloc ((list).p_tmp); \
      (list).size  -= 1;                        \
      }                                         \
   }


