/lib/darray.c
C | 668 lines | 465 code | 75 blank | 128 comment | 102 complexity | facf2b80695deedf06a9c4d3fb78c062 MD5 | raw file
- /* darray.c -- dynamic arrays handling
- Copyright (c) 1996-99 Akim Demaille, Miguel Santana
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, 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 General Public License for more details.
- You should have received a copy of the GNU 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. */
- /* Author: Akim Demaille <demaille@inf.enst.fr> */
- #include <system.h>
- #include "darray.h"
- int da_exit_error = 1; /* exit value when encounters *
- * an error */
- #define QSORT_INSERT_SORT_LIMIT 37 /* Bellow, insert sort is used */
- #define QSORT_STACK 100
- #define DA_SWAP(a,i,j) \
- do { \
- tmp = a->content [i]; \
- a->content [i] = a->content [j]; \
- a->content [j] = tmp ; \
- } while (0)
- /*
- * Create a dynamic array
- */
- struct darray *
- da_new (const char * name, size_t size,
- enum da_growth growth, size_t increment,
- da_print_func_t self_print,
- da_cmp_func_t cmp)
- {
- struct darray * res;
- /* No longer relevant: size_t cannot be null */
- if (size == 0)
- error (da_exit_error, 0, "invalid size for dynamic array `%s': %d",
- name, size);
- if (increment == 0 && growth != da_steady)
- error (da_exit_error, 0, "invalid increment for dynamic array `%s': %d",
- name, increment);
- res = XMALLOC (struct darray, 1);
- res->name = name;
- res->original_size = size;
- res->size = size;
- res->content = XCALLOC (void *, res->size);
- res->growth = growth;
- res->increment = increment;
- res->len = 0;
- /* Routines */
- res->self_print = self_print;
- res->cmp = cmp;
- return res;
- }
- static inline void
- _da_erase (struct darray * arr)
- {
- if (arr) {
- XFREE (arr->content);
- free (arr);
- }
- }
- void
- da_erase (struct darray * arr)
- {
- _da_erase (arr);
- }
- /*
- * Set length of ARR to 0, and free with FREE_FUNC if non NULL
- */
- static inline void
- _da_free_content (struct darray * arr, da_map_func_t free_func)
- {
- size_t i;
- if (free_func)
- for (i = 0 ; i < arr->len ; i++)
- (*free_func) (arr->content [i]);
- arr->len = 0;
- }
- void
- da_free_content (struct darray * arr, da_map_func_t free_func)
- {
- _da_free_content (arr, free_func);
- }
- /*
- * Set length of ARR to 0, and free with FREE_FUNC if non NULL
- * and free the structure
- */
- void
- da_free (struct darray * arr, da_map_func_t free_func)
- {
- _da_free_content (arr, free_func);
- _da_erase (arr);
- }
- /*
- * Report the status of the array
- */
- void
- da_print_stats (struct darray * arr, FILE * stream)
- {
- const char * cp = NULL;
- fprintf (stream, _("Dynamic array `%s':\n"), arr->name);
- fprintf (stream, _("\tload: %d/%d (%2.1f%%)\n"),
- arr->len, arr->size, 100.0 * arr->len / arr->size);
- switch (arr->growth) {
- case da_steady:
- /* growth is steady, i.e., it cannot grow, it is constant */
- cp = "[const]";
- break;
- case da_linear:
- /* growth is linear. eg. 2, 4, 6, 8 */
- cp = "+=";
- break;
- case da_geometrical:
- /* growth is exponential. eg. 2, 4, 8, 16 */
- cp = "*=";
- break;
- default:
- abort ();
- }
- fprintf (stream, _("\toriginal size: %d, growth: %s %d\n"),
- arr->original_size, cp, arr->increment);
- }
- /*
- * Resize, unless too small to fit
- */
- void
- da_resize (struct darray * arr, size_t size)
- {
- if (arr->len + 1 < size)
- {
- arr->size = size;
- arr->content = XREALLOC (arr->content, void *, arr->size);
- }
- }
- /*
- * Make a dyn. array bigger
- */
- void
- da_grow (struct darray * arr)
- {
- switch (arr->growth) {
- case da_steady:
- return;
- case da_linear:
- arr->size += arr->increment;
- break;
- case da_geometrical:
- arr->size *= arr->increment;
- break;
- default:
- abort ();
- }
- arr->content = XREALLOC (arr->content, void *, arr->size);
- }
- /*
- * Make a clone
- */
- struct darray *
- da_clone (struct darray * array)
- {
- struct darray * res;
- res = CLONE (array);
- res->content = CCLONE (array->content, array->len);
- return res;
- }
- /*
- * Is it sorted?
- */
- int
- da_is_sorted (struct darray * arr)
- {
- size_t i;
- for (i = 1 ; i < arr->len ; i++)
- if (arr->cmp (arr->content [i], arr->content [i - 1]) < 0)
- return 0;
- return 1;
- }
- /*
- * Are two darray equal (pointer-wise)?
- */
- int
- da_equal (struct darray * ar1, struct darray * ar2)
- {
- size_t i;
- if (ar1->len != ar2->len)
- return 0;
- for (i = 0 ; i< ar1->len ; i++)
- if (ar1->content [i] != ar2->content [i])
- return 0;
- return 1;
- }
- /*
- * Do two arrays have same semantics (wrt cmp) content?
- * (ar1->cmp is used for the comparison)
- */
- int
- da_cmp_equal (struct darray * ar1, struct darray * ar2)
- {
- size_t i;
- if (ar1->len != ar2->len)
- return 0;
- for (i = 0 ; i< ar1->len ; i++)
- if (ar1->cmp (ar1->content [i], ar2->content [i]))
- return 0;
- return 1;
- }
- /*
- * Where is STUFF in ARR (equal in the sense of self_cmp)
- * -1 if is not in.
- */
- int
- da_where (struct darray * arr, const void * stuff)
- {
- size_t i;
- for (i = 0 ; i < arr->len ; i++)
- if (!arr->cmp (arr->content[i], stuff))
- return (int) i;
- return -1;
- }
- /*
- * Does this stuff is selfcmp equal to an item of the darray?
- */
- int
- da_includes (struct darray * arr, const void * stuff)
- {
- return (da_where (arr, stuff) != -1);
- }
- /*
- * Append an element
- */
- void
- da_append (struct darray * arr, void * elem)
- {
- if (da_is_full (arr))
- da_grow (arr);
- arr->content [arr->len++] = elem;
- }
- /*
- * Insert an element at a given place.
- */
- void
- da_insert_at (struct darray * arr, void * elem, size_t where)
- {
- size_t i;
- if (where > arr->len)
- error (da_exit_error, 0, "can't insert at %d in darray %s [0,%d]\n",
- where, arr->name, arr->len - 1);
- if (da_is_full (arr))
- da_grow (arr);
- for (i = arr->len ; where < i ; i--)
- arr->content [i] = arr->content [i - 1];
- arr->content [ where ] = elem;
- arr->len ++;
- }
- /*
- * Remove an element at a given place.
- */
- void
- da_remove_at (struct darray * arr, size_t where, da_map_func_t free_func)
- {
- size_t i;
- if (where >= arr->len)
- error (da_exit_error, 0, "can't remove at %d in darray %s [0,%d]\n",
- where, arr->name, arr->len - 1);
- if (free_func)
- (*free_func) (arr->content [where]);
- for (i = where + 1 ; i < arr->len ; i++)
- arr->content [i - 1] = arr->content [i];
- arr->len --;
- }
- /*
- * Concat the second in the first
- */
- void
- da_concat (struct darray * arr, struct darray * arr2)
- {
- size_t i;
- size_t len = arr->len + arr2->len;
- if (len > arr->size) {
- arr->size = len + 1;
- arr->content = XREALLOC (arr->content, void *, arr->size);
- }
- for (i = 0 ; i < arr2->len ; i++)
- arr->content [arr->len++] = arr2->content[i];
- }
- /*
- * Prefix the content of ARR by that of ARR2
- */
- void
- da_prefix (struct darray * arr, struct darray * arr2)
- {
- int i;
- size_t len = arr->len + arr2->len;
- if (len > arr->size) {
- arr->size = len + 1;
- arr->content = XREALLOC (arr->content, void *, arr->size);
- }
- /* Move the content of ARR */
- for (i = (int) arr->len - 1 ; i >= 0 ; i--)
- arr->content [ i + arr2->len ] = arr->content [ i ];
- /* Copy the content of ARR2 */
- for (i = 0 ; i < (int) arr2->len ; i++)
- arr->content [ i ] = arr2->content[ i ];
- arr->len += arr2->len;
- }
- /*
- * Implementation of QSORT as given by Sedgewick
- */
- void
- da_qsort (struct darray * arr)
- {
- int ir, j, k, l, i;
- int jstack, *istack;
- void * a, * tmp;
- /* Do not sort an empty array */
- if (arr->len <= 1)
- return;
- istack = XMALLOC (int, QSORT_STACK);
- ir = arr->len - 1;
- l = 0;
- jstack = 0;
- for (;;) {
- if (ir - l < QSORT_INSERT_SORT_LIMIT)
- { /* Insertion sort is then prefered */
- for (j = l + 1 ; j <= ir ; j++) {
- a = arr->content [j];
- for (i = j - 1 ; i >= l ; i--) {
- if (arr->cmp (arr->content [i], a) <= 0)
- break;
- arr->content [i + 1] = arr->content [i];
- }
- arr->content [i + 1] = a;
- }
- if (jstack == 0)
- break;
- ir = istack [jstack--];
- l = istack [jstack--];
- }
- else
- {
- k = (l + ir) / 2;
- DA_SWAP (arr, k, l + 1);
- if (arr->cmp (arr->content [l], arr->content [ir]) > 0)
- DA_SWAP (arr, l, ir);
- if (arr->cmp (arr->content [l + 1], arr->content [ir]) > 0)
- DA_SWAP (arr, l + 1, ir);
- if (arr->cmp (arr->content [l], arr->content [l + 1]) > 0)
- DA_SWAP (arr, l, l + 1);
- i = l + 1;
- j = ir;
- a = arr->content [l + 1];
- for (;;) {
- do i++; while (arr->cmp (arr->content [i], a) < 0);
- do j--; while (arr->cmp (arr->content [j], a) > 0);
- if (j < i)
- break; /* Partion is completed */
- DA_SWAP (arr, i, j);
- }
- arr->content [l + 1] = arr->content [j];
- arr->content [j] = a;
- jstack += 2;
- /* Push pointers to larger subarry on stack.
- * Process smaller subarrays now */
- if (jstack > QSORT_STACK)
- error (da_exit_error, 0, "da_qsort: QSORT_STACK too small (%d)",
- QSORT_STACK);
- if (ir - i + 1 >= j - l) {
- istack [jstack] = ir;
- istack [jstack - 1] = i;
- ir = j - 1;
- } else {
- istack [jstack] = j - 1;
- istack [jstack - 1] = l;
- l = i;
- }
- }
- }
- free (istack);
- }
- /*
- * Implementation of QSORT as given by Sedgewick
- */
- void
- da_qsort_with_arg (struct darray * arr, da_cmp_arg_func_t cmp,
- const void * arg)
- {
- int ir, j, k, l, i;
- int jstack, *istack;
- void * a, * tmp;
- /* Do not sort an empty array */
- if (arr->len <= 1)
- return;
- istack = XMALLOC (int, QSORT_STACK);
- ir = arr->len - 1;
- l = 0;
- jstack = 0;
- for (;;) {
- if (ir - l < QSORT_INSERT_SORT_LIMIT)
- { /* Insertion sort is then prefered */
- for (j = l + 1 ; j <= ir ; j++) {
- a = arr->content [j];
- for (i = j - 1 ; i >= l ; i--) {
- if (cmp (arr->content [i], a, arg) <= 0)
- break;
- arr->content [i + 1] = arr->content [i];
- }
- arr->content [i + 1] = a;
- }
- if (jstack == 0)
- break;
- ir = istack [jstack--];
- l = istack [jstack--];
- }
- else
- {
- k = (l + ir) / 2;
- DA_SWAP (arr, k, l + 1);
- if (cmp (arr->content [l], arr->content [ir], arg) > 0)
- DA_SWAP (arr, l, ir);
- if (cmp (arr->content [l + 1], arr->content [ir], arg) > 0)
- DA_SWAP (arr, l + 1, ir);
- if (cmp (arr->content [l], arr->content [l + 1], arg) > 0)
- DA_SWAP (arr, l, l + 1);
- i = l + 1;
- j = ir;
- a = arr->content [l + 1];
- for (;;) {
- do i++; while (cmp (arr->content [i], a, arg) < 0);
- do j--; while (cmp (arr->content [j], a, arg) > 0);
- if (j < i)
- break; /* Partion is completed */
- DA_SWAP (arr, i, j);
- }
- arr->content [l + 1] = arr->content [j];
- arr->content [j] = a;
- jstack += 2;
- /* Push pointers to larger subarry on stack.
- * Process smaller subarrays now */
- if (jstack > QSORT_STACK)
- error (da_exit_error, 0, "da_qsort: QSORT_STACK too small (%d)",
- QSORT_STACK);
- if (ir - i + 1 >= j - l) {
- istack [jstack] = ir;
- istack [jstack - 1] = i;
- ir = j - 1;
- } else {
- istack [jstack] = j - 1;
- istack [jstack - 1] = l;
- l = i;
- }
- }
- }
- free (istack);
- }
- /*
- * Leave the first of each doubles
- */
- void
- da_unique (struct darray * arr, da_map_func_t free_func)
- {
- size_t c;
- c = 1;
- while (c < arr->len) {
- if (arr->cmp (arr->content [c - 1], arr->content[c]) == 0)
- da_remove_at (arr, c, free_func);
- else
- c++;
- }
- }
- /*
- * Merge A2 into A1. Both *are sorted*.
- * In the result there are never two equal entries
- * (in the sense of self_cmp).
- *
- * In case of conflict (equal entries from the point of view
- * of a1->cmp),
- * - if POLICY == da_1_wins, keep that of A1
- * - if POLICY == da_2_wins, keep that of A2
- *
- * If there are doubles in a1 and/or in a2, they still will be doubles
- * in the returned result.
- */
- void
- da_merge (struct darray * a1, struct darray * a2,
- da_map_func_t free_func, enum da_include_policy policy)
- {
- size_t c1, c2; /* Counters on a1, and a2 */
- c1 = c2 = 0;
- while ((c1 != a1->len) || (c2 != a2->len))
- {
- /* Leave what is in a1 as long as it is strictly smaller than the
- * next item of a2 */
- while ((c1 < a1->len)
- && ((c2 == a2->len)
- || (a1->cmp (a1->content [c1], a2->content [c2]) < 0)))
- c1 ++;
- /* Skip whatever appears in a1, but is in a2 too */
- while ((c1 < a1->len) && (c2 < a2->len)
- && (a1->cmp (a1->content [c1], a2->content [c2]) == 0))
- if (policy == da_1_wins)
- {
- if (free_func)
- da_remove_at (a2, c2, free_func);
- else
- c2++;
- }
- else
- {
- if (free_func)
- da_remove_at (a1, c1, free_func);
- else
- c1++;
- }
- /* Take what is is a2 as long as it is smaller or equal to
- * what appeared last in a1 */
- while ((c2 < a2->len)
- && ((c1 == a1->len)
- || (a1->cmp (a1->content [c1], a2->content [c2]) >= 0)))
- da_insert_at (a1, a2->content [c2++], c1);
- }
- }
- /*
- * Dump on stderr the content
- */
- void
- da_self_print (struct darray * arr, FILE * stream)
- {
- size_t i;
- fprintf (stream, _("Dynamic array `%s':\n"), arr->name);
- if (!arr->self_print)
- abort ();
- for (i = 0 ; i < arr->len ; i++) {
- fprintf (stream, "[%2d] = ", i);
- arr->self_print (arr->content [i], stream);
- fprintf (stream, "\n");
- }
- }
- /*
- * For each item of ARR, call FN (ITEM)
- */
- void
- da_map (struct darray * arr, da_map_func_t fn)
- {
- size_t i;
- for (i = 0 ; i < arr->len ; i++)
- (*fn) (arr->content [i]);
- }
- /*
- * Idem, but with an argument
- */
- void
- da_maparg (struct darray * arr, da_maparg_func_t func, void * arg)
- {
- size_t i;
- for (i = 0 ; i < arr->len ; i++)
- (*func) (arr->content [i], arg);
- }
- /*
- * Some helping routines for special darray cases
- */
- /*
- * darray of strings
- */
- int
- da_str_cmp (const char * s1, const char * s2)
- {
- return strcmp (s1, s2);
- }
- void
- da_str_print (const char * s1, FILE * stream)
- {
- fputs ((const char *) s1, stream);
- }
- void
- da_str_printnl (const char * s1, FILE * stream)
- {
- fputs ((const char *) s1, stream);
- putc ('\n', stream);
- }