
/*
 * $Header: /home/rockware/cvs-main/operator/c-src/dpa.c,v 1.1 1995/03/16 11:27:09 erez Exp $
 * ----------------------------------------------------------------------------
 * Dynamic Pointers Array.
 * This new general array type has the following structure:
 * First the dpa struct then right after it in memory (without any alignment
 * problems) the pointers.
 *
 * Written by Erez Strauss. 1994.
 *
 * $Log: dpa.c,v $
 * Revision 1.1  1995/03/16  11:27:09  erez
 * Initial revision
 *
 * Revision 2.1  1994/08/01  16:07:31  erez
 * (1)(BGFX) bsearch - computed the index incorrectly.
 *
 * Revision 2.0  1994/07/31  09:47:23  erez
 * This is a new version that moved to the tool library.
 *
 * Revision 1.1  1994/07/31  08:01:50  erez
 * (1)(U) More comments. (2) Moved to library.
 *
 * Revision 1.7  1994/07/06  06:41:42  erez
 * (1)(N) add_list - new function, add a list of pointers, it is also
 *  possible to add nulls into the array.
 *
 * Revision 1.6  1994/05/23  10:25:24  erez
 * (1) (N) insert function was written to insert a value in specific location
 *   in the array (moves up some other elements).
 * (2) (N) remove_no_order function - remove an entry in constant time.
 *   By moving the last entry to the place of the one to be removed.
 *
 * Revision 1.5  1994/05/13  08:19:52  erez
 * (1) (N) The dpa_dup were added to the header file.
 * (2) (U) The memcpy also copy the last null entry.
 *
 * Revision 1.4  1994/05/09  10:57:36  erez
 * (1) Functions comments were added to each and every function.
 * (2) The dpa_bsearch function return index into the dpa.
 *
 * Revision 1.3  1994/05/04  12:08:17  erez
 * (1) dpa indexes are long from now on (maybe non portable),
 * (2) new indentation.
 *
 * Revision 1.2  1994/04/24  06:56:58  erez
 * New dpa function: dpa_merge() - the function create a new dpa that contains
 * the values from both arrays.
 *
 * Revision 1.1.1.1  1994/04/17  15:48:27  erez
 * Busses View first working version
 *
 * ----------------------------------------------------------------------------
 */

# if ! defined ( NO_IDENT )
#   if defined ( __GNUC__ )
#     ident "@(#)$Id: dpa.c,v 1.1 1995/03/16 11:27:09 erez Exp $"
#   else /* __GNUC__ */
      static const char file_id[] = "@(#)$Id: dpa.c,v 1.1 1995/03/16 11:27:09 erez Exp $";
#   endif /* __GNUC__ */
# endif /* NO_IDENT */

/* functions file */

# include <stdio.h>
# include <stdlib.h>
# include <memory.h>
# include <stdarg.h>

# include "dpa.h"

/*
 * Define the Byte Difference between the self pointer and the user void*
 * pointer. Using this bytes difference between the two pointer assure no
 * alignment problems.
 */

# define SLF2VP_BD  (((sizeof (dpa) - 1) / sizeof (void*) + 1) * sizeof (void*))
# define DPA_CHUNK_SIZE 10

/* ____________________________________________________________________________
 *
 * Function name: dpa_new
 * Purpose: Create new Dynamic Pointers Array.
 * Description: This functions calls malloc to allocate the needed memory
 *     for at least count elements and the two hidden counters.
 * Return value: The new dpa, or null in case of failed malloc call.
 * Input: the amount of entries in the new dpa (there will be additional null
 *        entry at the end)
 *
 * Note: The return value is not the first value in the malloced block.
 *       The difference between the two addresses is SLF2VP_BD.
 * ____________________________________________________________________________
 */

void**
dpa_new (long count)
    {
    unsigned long  m_size;
    dpa*           self;

    if (count <= 0)
        count = 0;
    m_size = SLF2VP_BD + (count + 1) * sizeof (void**);
    if (!(self = (dpa*)malloc (m_size)))
        {
        fprintf (stderr, "dpa_new: Error - null malloc.\n");
        return 0;
        }
    memset (self, 0, m_size);
    self->allocated = count;
    self->in_use = 0;
    return (void**)((char*)self + SLF2VP_BD);
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_delete
 * Purpose: Erase the pointers array from the memory.
 * Description: The function first calculate the address of the malloced block
 *     and calls free. the functions also check that the last entry is null as
 *     returned from dpa_new.
 * Return value: none.
 * Input: The dpa to be deleted.
 *
 * Note: After call to dpa_delete the pointer is no more valid for any use.
 * ____________________________________________________________________________
 */

void
dpa_delete (void** vp)
    {
    dpa* self = (dpa*)((char*)vp - SLF2VP_BD);

    if (!vp || !self)
        {
        fprintf (stderr, "dpa_delete: Error - null pointer.\n");
        }
    else
        {
        if (vp[self->allocated])
            {
            fprintf (stderr, "Warning: Last entry is no more null ?!\n");
            }
        free ((void*)self);
        }
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_dup
 * Purpose: Create new array, a copy of given vp.
 * Description: The function malloc enough memory to hold all the used entries
 *     in the given vp (which is assumed to be dpa). Then it copies all the
 *     entries to the new dpa.
 * Return value: The new initialized dpa.
 * Input: vp - void pointer, returned by the dpa_new or dpa_dup.
 *
 * Note: it copy only in_use entries and not all the unused entries.
 * ____________________________________________________________________________
 */

void**
dpa_dup (void** vp)
    {
    dpa*    self = (dpa*)((char*)vp - SLF2VP_BD);
    dpa*    new_self;
    void**  new_vp;

    if (!vp || !self)
        {
        fprintf (stderr, "dpa_add: Error - null pointer.\n");
        return 0;
        }
    new_vp = dpa_new (self->in_use);
    new_self = (dpa*)((char*)new_vp - SLF2VP_BD);

    memcpy (new_vp, vp, (self->in_use + 1) * sizeof (void*));
    new_self->in_use = self->in_use;

    return new_vp;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_add
 * Purpose: Add entry to the dpa.
 * Description: The function check that there is enough place in the dpa to add
 *     the new entry. When there is no enough place the function allocates
 *     new memory block and then copies the old block to the new one before
 *     adding the new entry.
 * Return value: The dpa with one more entry.
 * Input: a - the new entry.
 * Update: vp - void pointer, dpa.
 *
 * Note: The value that was give by vp might be destroyed because of limited
 *     block size. The return value should be used instead, so whenever using
 *     this function, the call should look like this:
 *     vp = dpa_add (vp, additional_entry)
 * ____________________________________________________________________________
 */

void**
dpa_add (void** vp, void* a)
    {
    dpa*    self = (dpa*)((char*)vp - SLF2VP_BD);
    dpa*    new_self;
    void**  new_vp;

    if (!vp || !self)
        {
        fprintf (stderr, "dpa_add: Error - null pointer.\n");
        return 0;
        }
    if (self->in_use >= self->allocated)
        {
        new_vp = dpa_new (self->allocated + DPA_CHUNK_SIZE);
        if (!new_vp)
            {
            fprintf (stderr, "dpa_add: Error - null new self.\n");
            return 0;
            }
        memcpy (new_vp, vp, self->in_use * sizeof (void*));
        new_self = (dpa*)((char*)new_vp - SLF2VP_BD);
        new_self->in_use = self->in_use;
        free (self);
        self = new_self;
        vp = new_vp;
        }
    vp[self->in_use++] = a;
    return vp;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_insert
 * Purpose: insert a new value into a dpa in specific location.
 * Description: The function is very similar to dpa_add.
 *     The function first checks that there is enough memory for the new value
 *     Then it move the other values one place up. and inset the new value
 *     to the proper place.
 * Return value: The new dpa.
 * Input: index - where to put the new value, [0..in_use-1]
 *        a - the value of the new entry.
 * Update: vp - a dpa to be updated.
 *
 * Note: there should be a single function for increase.
 * ____________________________________________________________________________
 */

void**
dpa_insert (void** vp, long index, void* a)
    {
    dpa*    self = (dpa*)((char*)vp - SLF2VP_BD);
    int     i;

    if (!vp || !self)
        {
        fprintf (stderr, "dpa_insert: Error - null pointer.\n");
        return vp;
        }
    if (index == self->in_use)
        {
        return dpa_add (vp, a);
        }
    if (index < 0 || index > self->in_use)
        {
        fprintf (stderr, "dpa_insert: Error - index out of range.\n");
        return vp;
        }
    if (self->in_use >= self->allocated)
        {
        dpa*    new_self;
        void**  new_vp;

        new_vp = dpa_new (self->allocated + DPA_CHUNK_SIZE);
        if (!new_vp)
            {
            fprintf (stderr, "dpa_insert: Error - null new self.\n");
            return vp;
            }
        memcpy (new_vp, vp, self->in_use * sizeof (void*));
        new_self = (dpa*)((char*)new_vp - SLF2VP_BD);
        new_self->in_use = self->in_use;
        free (self);
        self = new_self;
        vp = new_vp;
        }
    for (i = self->in_use - 1; i >= index; i--)
        {
        vp[i + 1] = vp[i];
        }
    self->in_use++;
    vp[index] = a;
    return vp;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_remove
 * Purpose: remove an entry form the dpa.
 * Description: The function removes the entry by copying all the entries with
 *     higher index in the dpa one place down. As the dpa_new return the dpa
 *     The last entry in the array is null. after moving the entries the
 *     function decrease the in_use counter.
 * Return value: The same dpa with one less entry.
 * Input: index - an index to the entry to be removed.
 * Update: vp - dpa to be updated.
 *
 * Note: in future version the return value might change the dpa address when
 *     there is big difference between the in_use and allocated. So any call to
 *     dpa_remove should look like this:
 *     vp = dpa_remove (vp, index_to_remove)
 * ____________________________________________________________________________
 */

void**
dpa_remove (void** vp, long index)
    {
    dpa*  self = (dpa*)((char*)vp - SLF2VP_BD);
    long  i;

    if (!vp || !self)
        {
        fprintf (stderr, "dpa_remove: Error - null pointer.\n");
        return vp;
        }
    if (self->in_use <= index || 0 > index)
        {
        fprintf (stderr, "dpa_remove: Error - index out of range %ld.\n"
                 , (long)index);
        return vp;
        }
    for (i = index; i < self->in_use; i++)
        vp[i] = vp[i+1];
    self->in_use--;
    return vp;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_remove_no_order
 * Purpose: remove an entry form the dpa, without keeping the order.
 * Description: The function removes one entry from the dpa and moves the last
 *     entry to that empty location. It does so in o(C), with no dependency on
 *     the number of entries in the dpa.
 * Return value: The same dpa, in the future it can pack the dpa to smaller
 *     size and realloc the dpa.
 * Input: index to the entry to be removed.
 * Update: vp - the dpa.
 * ____________________________________________________________________________
 */

void**
dpa_remove_no_order (void** vp, long index)
    {
    dpa*  self = (dpa*)((char*)vp - SLF2VP_BD);

    if (!vp || !self)
        {
        fprintf (stderr, "dpa_remove: Error - null pointer.\n");
        return vp;
        }
    if (self->in_use <= index || 0 > index)
        {
        fprintf (stderr, "dpa_remove: Error - index out of range %ld.\n"
                 , (long)index);
        return vp;
        }
    vp[index] = vp[self->in_use - 1];
    vp[self->in_use - 1] = vp[self->in_use];
    self->in_use--;
    return vp;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_in_use
 * Purpose: Return the counter of number of entries in use in the dpa.
 * Description: Calculate the address of the structure and return the in_use
 *     field.
 * Return value: the in_use field.
 * Input: vp - dpa.
 * ____________________________________________________________________________
 */

long
dpa_in_use (void** vp)
    {
    dpa* self = (dpa*)((char*)vp - SLF2VP_BD);

    return self->in_use;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_merge
 * Purpose: Create new dpa with entries from two other dpas.
 * Description: The function calls to dpa_new with count of entries equal to
 *     the sum of the two in_use fields. After creating the new dpa the entries
 *     values is copied to the new dpa. The last step is to update the in_use
 *     field of the new dpa.
 * Return value: new new dpa with all the entries.
 * Input: vp_a, vp_b - the two dpas.
 *
 * Note: doesn't delete the arguments
 * ____________________________________________________________________________
 */

void**
dpa_merge (void** vp_a, void** vp_b)
    {
    dpa*  self_a = (dpa*)((char*)vp_a - SLF2VP_BD);
    dpa*  self_b = (dpa*)((char*)vp_b - SLF2VP_BD);
    void* new_vp = dpa_new (self_a->in_use + self_b->in_use);
    dpa*  new_self = (dpa*)((char*)new_vp - SLF2VP_BD);


    memcpy (new_vp, vp_a, self_a->in_use * sizeof (void*));
    memcpy ((char*)new_vp + self_a->in_use * sizeof (void*)
            , vp_b
            , self_b->in_use * sizeof (void*));

    new_self->in_use = self_a->in_use + self_b->in_use;

    return (void**)((char*)new_self + SLF2VP_BD);
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_sort
 * Purpose: sort the entries in the dpa according to give compare function.
 * Description: The function calls the qsort function.
 * Return value: none.
 * Input: compare_function - the function that define the order.
 * Update: vp - the dpa is updated.
 * ____________________________________________________________________________
 */

void
dpa_sort (void** vp, int (*compare_function)(void**, void**))
    {
    dpa* self = (dpa*)((char*)vp - SLF2VP_BD);

    qsort ((char*)vp, self->in_use, sizeof (void*), (int(*)(const void *, const void *))compare_function);
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_bsearch
 * Purpose: Find a value in a sorted dpa.
 * Description: call the bsearch function with the needed arguments.
 * Return value: an index of the searched entry.
 * Input: vp - dpa.
 *        value - the value we search for.
 *        compare_function - the function used to compare two entries.
 *
 * Note: The compare function MUST be the same one that used to sort the dpa.
 * ____________________________________________________________________________
 */

long
dpa_bsearch (void** vp, void* value, int (*compare_function)(void**, void**))
    {
    dpa* self = (dpa*)((char*)vp - SLF2VP_BD);

    return (void**)bsearch ((char*)value, (char*)vp, self->in_use, sizeof (void*), (int(*)(const void *, const void *))compare_function) - vp;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_search
 * Purpose: search a value in the dpa.
 * Description: the function runs all over the array to find the wanted entry.
 * Return value: The index of the entry that was searched.
 * Input: vp - dpa.
 *        val - the searched value.
 *
 * Note: in case the dpa is sorted, you can use the dpa_bsearch.
 * ____________________________________________________________________________
 */

long
dpa_search (void** vp, void* val)
    {
    long i, n;

    for (i = 0, n = dpa_in_use (vp); i < n; i++)
        if (vp[i] == val)
            {
            return i;
            }
    return -1;
    }

/* ____________________________________________________________________________
 *
 * Function name: dpa_add_list
 * Purpose: add a list of vectors into a dpa.
 * Description: The function get a list of pointers that terminated by a
 *     given value.
 * Return value: the new dpa with the old and new pointers
 * Input: last_value - the value that is used to terminate the pointers
 *     arguments list (can be null or non null value).
 * Update: vp - a pointer to the dpa (the void** value),
 *
 * Note: 
 *     Current implementation is just calling dpa_add, in the future it
 *     might increase the size of the dpa in one step.
 * ____________________________________________________________________________
 */

void**
dpa_add_list (void** vp, void* last_value, void* first, ...)
    {
    void* val;
    va_list  vap;

    va_start (vap, first);
    for (val = first; val != last_value; val = va_arg (vap, void*))
        {
        vp = dpa_add (vp, val);
        }
    va_end (vap);
    return vp;
    }

/* This Main is used for testing this dpa package. */

# if defined ( STAND_ALONE )

# include <string.h>

int
charp_strcmp (char** a, char** b)
    { return strcmp (*a, *b);}

/* ____________________________________________________________________________
 *
 * Function name: main
 * Purpose: to test all the functions of the dpa "calss",
 *     to check the dpa, you should run this main under Purify checking.
 * Description: Calls different functions.
 * Return value: 0
 * Output: information is output to the screen about the different steps in
 * the test.
 * ____________________________________________________________________________
 */

int
main (void)
    {
    void** strings;
    void** t;
    char   buff[20];
    int    i;

    strings = dpa_new (1);

    strings = dpa_add (strings, "First");
    strings = dpa_add (strings, "Second");
    strings = dpa_add (strings, "3rd");

    for (t = strings; *t; t++)
        {
        printf ("%s\n", (char*)*t);
        }
    
    dpa_sort (strings, (int(*)(void**,void**))charp_strcmp);

    printf ("after sort\n");

    for (t = strings; *t; t++)
        {
        printf ("%s\n", (char*)*t);
        }
    
    printf ("%ld\n", (long)dpa_in_use (strings));

    dpa_remove (strings, 1);
    dpa_delete (strings);

    /* Second Test */

    strings = dpa_new (1);
    for (i = 0; i < 100; i++)
        {
        sprintf (buff, "X-%ld", (long)i);
        strings = dpa_add (strings, strdup (buff));
        }
    free (strings[0]);
    strings = dpa_remove (strings, 0);
    free (strings[0]);
    strings = dpa_remove (strings, 0);

    free (strings[dpa_in_use (strings) / 2]);
    strings = dpa_remove (strings, dpa_in_use (strings) / 2);
    free (strings[dpa_in_use (strings) / 2]);
    strings = dpa_remove (strings, dpa_in_use (strings) / 2);

    free (strings[dpa_in_use (strings) - 1]);
    strings = dpa_remove (strings, dpa_in_use (strings) - 1);
    free (strings[dpa_in_use (strings) - 1]);
    strings = dpa_remove (strings, dpa_in_use (strings) - 1);

    for (i = 0; i < dpa_in_use (strings); i++)
        {
        printf ("%4ld: %s\n", (long)i, (char*)strings[i]);
        }

    t = dpa_dup (strings);

    for (i = dpa_in_use (strings); i > 0; i--)
        {
        free (strings[0]);
        strings = dpa_remove (strings, 0);
        }
    dpa_delete (strings);
    dpa_delete (t);

    printf ("Insert testing ...\n");
    
    strings = dpa_new (0);
    strings = dpa_insert (strings, 0, "insetr#1");
    strings = dpa_insert (strings, 0, "insetr#2");
    strings = dpa_insert (strings, 0, "insetr#3");
    strings = dpa_insert (strings, 0, "insetr#4");
    strings = dpa_insert (strings, 0, "insetr#5");
    strings = dpa_insert (strings, 0, "insetr#6");
    strings = dpa_remove_no_order (strings, 0);
    
    for (i = 0; i < dpa_in_use (strings); i++)
        {
        printf ("%4ld: %s\n", (long)i, (char*)strings[i]);
        }
    dpa_delete (strings);
    
    return 0;
    }

# endif/* STAND_ALONE - test */

/* End of file: dpa.c ______________________________________________________ */
