2 * Sorted array routines for CUPS.
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
13 * This file is subject to the Apple OS-Developed Software exception.
17 * Include necessary headers...
20 #include <cups/cups.h>
21 #include "string-private.h"
22 #include "debug-private.h"
23 #include "array-private.h"
30 #define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/
34 * Types and structures...
37 struct _cups_array_s /**** CUPS array structure ****/
40 * The current implementation uses an insertion sort into an array of
41 * sorted pointers. We leave the array type private/opaque so that we
42 * can change the underlying implementation without affecting the users
46 int num_elements, /* Number of array elements */
47 alloc_elements, /* Allocated array elements */
48 current, /* Current element */
49 insert, /* Last inserted element */
50 unique, /* Are all elements unique? */
51 num_saved, /* Number of saved elements */
54 void **elements; /* Array elements */
55 cups_array_func_t compare; /* Element comparison function */
56 void *data; /* User data passed to compare */
57 cups_ahash_func_t hashfunc; /* Hash function */
58 int hashsize, /* Size of hash */
59 *hash; /* Hash array */
60 cups_acopy_func_t copyfunc; /* Copy function */
61 cups_afree_func_t freefunc; /* Free function */
69 static int cups_array_add(cups_array_t *a, void *e, int insert);
70 static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff);
74 * 'cupsArrayAdd()' - Add an element to the array.
76 * When adding an element to a sorted array, non-unique elements are
77 * appended at the end of the run of identical elements. For unsorted arrays,
78 * the element is appended to the end of the array.
80 * @since CUPS 1.2/macOS 10.5@
83 int /* O - 1 on success, 0 on failure */
84 cupsArrayAdd(cups_array_t *a, /* I - Array */
85 void *e) /* I - Element */
87 DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", (void *)a, e));
90 * Range check input...
95 DEBUG_puts("3cupsArrayAdd: returning 0");
100 * Append the element...
103 return (cups_array_add(a, e, 0));
108 * '_cupsArrayAddStrings()' - Add zero or more delimited strings to an array.
110 * Note: The array MUST be created using the @link _cupsArrayNewStrings@
111 * function. Duplicate strings are NOT added. If the string pointer "s" is NULL
112 * or the empty string, no strings are added to the array.
115 int /* O - 1 on success, 0 on failure */
116 _cupsArrayAddStrings(cups_array_t *a, /* I - Array */
117 const char *s, /* I - Delimited strings or NULL */
118 char delim)/* I - Delimiter character */
120 char *buffer, /* Copy of string */
121 *start, /* Start of string */
122 *end; /* End of string */
123 int status = 1; /* Status of add */
126 DEBUG_printf(("_cupsArrayAddStrings(a=%p, s=\"%s\", delim='%c')", (void *)a, s, delim));
130 DEBUG_puts("1_cupsArrayAddStrings: Returning 0");
137 * Skip leading whitespace...
140 DEBUG_puts("1_cupsArrayAddStrings: Skipping leading whitespace.");
142 while (*s && isspace(*s & 255))
145 DEBUG_printf(("1_cupsArrayAddStrings: Remaining string \"%s\".", s));
148 if (!strchr(s, delim) &&
149 (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n'))))
152 * String doesn't contain a delimiter, so add it as a single value...
155 DEBUG_puts("1_cupsArrayAddStrings: No delimiter seen, adding a single "
158 if (!cupsArrayFind(a, (void *)s))
159 status = cupsArrayAdd(a, (void *)s);
161 else if ((buffer = strdup(s)) == NULL)
163 DEBUG_puts("1_cupsArrayAddStrings: Unable to duplicate string.");
168 for (start = end = buffer; *end; start = end)
171 * Find the end of the current delimited string and see if we need to add
177 while (*end && !isspace(*end & 255))
179 while (*end && isspace(*end & 255))
182 else if ((end = strchr(start, delim)) != NULL)
185 end = start + strlen(start);
187 DEBUG_printf(("1_cupsArrayAddStrings: Adding \"%s\", end=\"%s\"", start,
190 if (!cupsArrayFind(a, start))
191 status &= cupsArrayAdd(a, start);
197 DEBUG_printf(("1_cupsArrayAddStrings: Returning %d.", status));
204 * 'cupsArrayClear()' - Clear the array.
206 * This function is equivalent to removing all elements in the array.
207 * The caller is responsible for freeing the memory used by the
208 * elements themselves.
210 * @since CUPS 1.2/macOS 10.5@
214 cupsArrayClear(cups_array_t *a) /* I - Array */
217 * Range check input...
224 * Free the existing elements as needed..
229 int i; /* Looping var */
230 void **e; /* Current element */
232 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
233 (a->freefunc)(*e, a->data);
237 * Set the number of elements to 0; we don't actually free the memory
238 * here - that is done in cupsArrayDelete()...
250 * 'cupsArrayCount()' - Get the number of elements in the array.
252 * @since CUPS 1.2/macOS 10.5@
255 int /* O - Number of elements */
256 cupsArrayCount(cups_array_t *a) /* I - Array */
259 * Range check input...
266 * Return the number of elements...
269 return (a->num_elements);
274 * 'cupsArrayCurrent()' - Return the current element in the array.
276 * The current element is undefined until you call @link cupsArrayFind@,
277 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
279 * @since CUPS 1.2/macOS 10.5@
282 void * /* O - Element */
283 cupsArrayCurrent(cups_array_t *a) /* I - Array */
286 * Range check input...
293 * Return the current element...
296 if (a->current >= 0 && a->current < a->num_elements)
297 return (a->elements[a->current]);
304 * 'cupsArrayDelete()' - Free all memory used by the array.
306 * The caller is responsible for freeing the memory used by the
307 * elements themselves.
309 * @since CUPS 1.2/macOS 10.5@
313 cupsArrayDelete(cups_array_t *a) /* I - Array */
316 * Range check input...
323 * Free the elements if we have a free function (otherwise the caller is
324 * responsible for doing the dirty work...)
329 int i; /* Looping var */
330 void **e; /* Current element */
332 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
333 (a->freefunc)(*e, a->data);
337 * Free the array of element pointers...
340 if (a->alloc_elements)
351 * 'cupsArrayDup()' - Duplicate the array.
353 * @since CUPS 1.2/macOS 10.5@
356 cups_array_t * /* O - Duplicate array */
357 cupsArrayDup(cups_array_t *a) /* I - Array */
359 cups_array_t *da; /* Duplicate array */
363 * Range check input...
370 * Allocate memory for the array...
373 da = calloc(1, sizeof(cups_array_t));
377 da->compare = a->compare;
379 da->current = a->current;
380 da->insert = a->insert;
381 da->unique = a->unique;
382 da->num_saved = a->num_saved;
384 memcpy(da->saved, a->saved, sizeof(a->saved));
389 * Allocate memory for the elements...
392 da->elements = malloc((size_t)a->num_elements * sizeof(void *));
400 * Copy the element pointers...
406 * Use the copy function to make a copy of each element...
409 int i; /* Looping var */
411 for (i = 0; i < a->num_elements; i ++)
412 da->elements[i] = (a->copyfunc)(a->elements[i], a->data);
417 * Just copy raw pointers...
420 memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *));
423 da->num_elements = a->num_elements;
424 da->alloc_elements = a->num_elements;
428 * Return the new array...
436 * 'cupsArrayFind()' - Find an element in the array.
438 * @since CUPS 1.2/macOS 10.5@
441 void * /* O - Element found or @code NULL@ */
442 cupsArrayFind(cups_array_t *a, /* I - Array */
443 void *e) /* I - Element */
445 int current, /* Current element */
446 diff, /* Difference */
447 hash; /* Hash index */
451 * Range check input...
458 * See if we have any elements...
461 if (!a->num_elements)
465 * Yes, look for a match...
470 hash = (*(a->hashfunc))(e, a->data);
472 if (hash < 0 || hash >= a->hashsize)
474 current = a->current;
479 current = a->hash[hash];
481 if (current < 0 || current >= a->num_elements)
482 current = a->current;
487 current = a->current;
491 current = cups_array_find(a, e, current, &diff);
495 * Found a match! If the array does not contain unique values, find
496 * the first element that is the same...
499 if (!a->unique && a->compare)
502 * The array is not unique, find the first match...
505 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
510 a->current = current;
513 a->hash[hash] = current;
515 return (a->elements[current]);
531 * 'cupsArrayFirst()' - Get the first element in the array.
533 * @since CUPS 1.2/macOS 10.5@
536 void * /* O - First element or @code NULL@ if the array is empty */
537 cupsArrayFirst(cups_array_t *a) /* I - Array */
540 * Range check input...
547 * Return the first element...
552 return (cupsArrayCurrent(a));
557 * 'cupsArrayGetIndex()' - Get the index of the current element.
559 * The current element is undefined until you call @link cupsArrayFind@,
560 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
562 * @since CUPS 1.3/macOS 10.5@
565 int /* O - Index of the current element, starting at 0 */
566 cupsArrayGetIndex(cups_array_t *a) /* I - Array */
576 * 'cupsArrayGetInsert()' - Get the index of the last inserted element.
578 * @since CUPS 1.3/macOS 10.5@
581 int /* O - Index of the last inserted element, starting at 0 */
582 cupsArrayGetInsert(cups_array_t *a) /* I - Array */
592 * 'cupsArrayIndex()' - Get the N-th element in the array.
594 * @since CUPS 1.2/macOS 10.5@
597 void * /* O - N-th element or @code NULL@ */
598 cupsArrayIndex(cups_array_t *a, /* I - Array */
599 int n) /* I - Index into array, starting at 0 */
606 return (cupsArrayCurrent(a));
611 * 'cupsArrayInsert()' - Insert an element in the array.
613 * When inserting an element in a sorted array, non-unique elements are
614 * inserted at the beginning of the run of identical elements. For unsorted
615 * arrays, the element is inserted at the beginning of the array.
617 * @since CUPS 1.2/macOS 10.5@
620 int /* O - 0 on failure, 1 on success */
621 cupsArrayInsert(cups_array_t *a, /* I - Array */
622 void *e) /* I - Element */
624 DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", (void *)a, e));
627 * Range check input...
632 DEBUG_puts("3cupsArrayInsert: returning 0");
637 * Insert the element...
640 return (cups_array_add(a, e, 1));
645 * 'cupsArrayLast()' - Get the last element in the array.
647 * @since CUPS 1.2/macOS 10.5@
650 void * /* O - Last element or @code NULL@ if the array is empty */
651 cupsArrayLast(cups_array_t *a) /* I - Array */
654 * Range check input...
661 * Return the last element...
664 a->current = a->num_elements - 1;
666 return (cupsArrayCurrent(a));
671 * 'cupsArrayNew()' - Create a new array.
673 * The comparison function ("f") is used to create a sorted array. The function
674 * receives pointers to two elements and the user data pointer ("d") - the user
675 * data pointer argument can safely be omitted when not required so functions
676 * like @code strcmp@ can be used for sorted string arrays.
678 * @since CUPS 1.2/macOS 10.5@
681 cups_array_t * /* O - Array */
682 cupsArrayNew(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
683 void *d) /* I - User data pointer or @code NULL@ */
685 return (cupsArrayNew3(f, d, 0, 0, 0, 0));
690 * 'cupsArrayNew2()' - Create a new array with hash.
692 * The comparison function ("f") is used to create a sorted array. The function
693 * receives pointers to two elements and the user data pointer ("d") - the user
694 * data pointer argument can safely be omitted when not required so functions
695 * like @code strcmp@ can be used for sorted string arrays.
697 * The hash function ("h") is used to implement cached lookups with the
698 * specified hash size ("hsize").
700 * @since CUPS 1.3/macOS 10.5@
703 cups_array_t * /* O - Array */
704 cupsArrayNew2(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
705 void *d, /* I - User data or @code NULL@ */
706 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
707 int hsize) /* I - Hash size (>= 0) */
709 return (cupsArrayNew3(f, d, h, hsize, 0, 0));
714 * 'cupsArrayNew3()' - Create a new array with hash and/or free function.
716 * The comparison function ("f") is used to create a sorted array. The function
717 * receives pointers to two elements and the user data pointer ("d") - the user
718 * data pointer argument can safely be omitted when not required so functions
719 * like @code strcmp@ can be used for sorted string arrays.
721 * The hash function ("h") is used to implement cached lookups with the
722 * specified hash size ("hsize").
724 * The copy function ("cf") is used to automatically copy/retain elements when
725 * added or the array is copied.
727 * The free function ("cf") is used to automatically free/release elements when
728 * removed or the array is deleted.
730 * @since CUPS 1.5/macOS 10.7@
733 cups_array_t * /* O - Array */
734 cupsArrayNew3(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
735 void *d, /* I - User data or @code NULL@ */
736 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
737 int hsize, /* I - Hash size (>= 0) */
738 cups_acopy_func_t cf, /* I - Copy function */
739 cups_afree_func_t ff) /* I - Free function */
741 cups_array_t *a; /* Array */
745 * Allocate memory for the array...
748 a = calloc(1, sizeof(cups_array_t));
763 a->hash = malloc((size_t)hsize * sizeof(int));
771 memset(a->hash, -1, (size_t)hsize * sizeof(int));
782 * '_cupsArrayNewStrings()' - Create a new array of comma-delimited strings.
784 * Note: The array automatically manages copies of the strings passed. If the
785 * string pointer "s" is NULL or the empty string, no strings are added to the
786 * newly created array.
789 cups_array_t * /* O - Array */
790 _cupsArrayNewStrings(const char *s, /* I - Delimited strings or NULL */
791 char delim) /* I - Delimiter character */
793 cups_array_t *a; /* Array */
796 if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0,
797 (cups_acopy_func_t)_cupsStrAlloc,
798 (cups_afree_func_t)_cupsStrFree)) != NULL)
799 _cupsArrayAddStrings(a, s, delim);
806 * 'cupsArrayNext()' - Get the next element in the array.
808 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)".
810 * The next element is undefined until you call @link cupsArrayFind@,
811 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
812 * to set the current element.
814 * @since CUPS 1.2/macOS 10.5@
817 void * /* O - Next element or @code NULL@ */
818 cupsArrayNext(cups_array_t *a) /* I - Array */
821 * Range check input...
828 * Return the next element...
831 if (a->current < a->num_elements)
834 return (cupsArrayCurrent(a));
839 * 'cupsArrayPrev()' - Get the previous element in the array.
841 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)".
843 * The previous element is undefined until you call @link cupsArrayFind@,
844 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
845 * to set the current element.
847 * @since CUPS 1.2/macOS 10.5@
850 void * /* O - Previous element or @code NULL@ */
851 cupsArrayPrev(cups_array_t *a) /* I - Array */
854 * Range check input...
861 * Return the previous element...
867 return (cupsArrayCurrent(a));
872 * 'cupsArrayRemove()' - Remove an element from the array.
874 * If more than one element matches "e", only the first matching element is
877 * The caller is responsible for freeing the memory used by the
880 * @since CUPS 1.2/macOS 10.5@
883 int /* O - 1 on success, 0 on failure */
884 cupsArrayRemove(cups_array_t *a, /* I - Array */
885 void *e) /* I - Element */
887 ssize_t i, /* Looping var */
888 current; /* Current element */
889 int diff; /* Difference */
893 * Range check input...
900 * See if the element is in the array...
903 if (!a->num_elements)
906 current = cups_array_find(a, e, a->current, &diff);
911 * Yes, now remove it...
917 (a->freefunc)(a->elements[current], a->data);
919 if (current < a->num_elements)
920 memmove(a->elements + current, a->elements + current + 1,
921 (size_t)(a->num_elements - current) * sizeof(void *));
923 if (current <= a->current)
926 if (current < a->insert)
928 else if (current == a->insert)
931 for (i = 0; i < a->num_saved; i ++)
932 if (current <= a->saved[i])
935 if (a->num_elements <= 1)
943 * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
945 * @since CUPS 1.2/macOS 10.5@
948 void * /* O - New current element */
949 cupsArrayRestore(cups_array_t *a) /* I - Array */
954 if (a->num_saved <= 0)
958 a->current = a->saved[a->num_saved];
960 if (a->current >= 0 && a->current < a->num_elements)
961 return (a->elements[a->current]);
968 * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@.
970 * The current element is undefined until you call @link cupsArrayFind@,
971 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
972 * to set the current element.
974 * The save/restore stack is guaranteed to be at least 32 elements deep.
976 * @since CUPS 1.2/macOS 10.5@
979 int /* O - 1 on success, 0 on failure */
980 cupsArraySave(cups_array_t *a) /* I - Array */
985 if (a->num_saved >= _CUPS_MAXSAVE)
988 a->saved[a->num_saved] = a->current;
996 * 'cupsArrayUserData()' - Return the user data for an array.
998 * @since CUPS 1.2/macOS 10.5@
1001 void * /* O - User data */
1002 cupsArrayUserData(cups_array_t *a) /* I - Array */
1012 * 'cups_array_add()' - Insert or append an element to the array.
1014 * @since CUPS 1.2/macOS 10.5@
1017 static int /* O - 1 on success, 0 on failure */
1018 cups_array_add(cups_array_t *a, /* I - Array */
1019 void *e, /* I - Element to add */
1020 int insert) /* I - 1 = insert, 0 = append */
1022 int i, /* Looping var */
1023 current; /* Current element */
1024 int diff; /* Comparison with current element */
1027 DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", (void *)a, e, insert));
1030 * Verify we have room for the new element...
1033 if (a->num_elements >= a->alloc_elements)
1036 * Allocate additional elements; start with 16 elements, then
1037 * double the size until 1024 elements, then add 1024 elements
1041 void **temp; /* New array elements */
1042 int count; /* New allocation count */
1045 if (a->alloc_elements == 0)
1048 temp = malloc((size_t)count * sizeof(void *));
1052 if (a->alloc_elements < 1024)
1053 count = a->alloc_elements * 2;
1055 count = a->alloc_elements + 1024;
1057 temp = realloc(a->elements, (size_t)count * sizeof(void *));
1060 DEBUG_printf(("9cups_array_add: count=" CUPS_LLFMT, CUPS_LLCAST count));
1064 DEBUG_puts("9cups_array_add: allocation failed, returning 0");
1068 a->alloc_elements = count;
1073 * Find the insertion point for the new element; if there is no
1074 * compare function or elements, just add it to the beginning or end...
1077 if (!a->num_elements || !a->compare)
1080 * No elements or comparison function, insert/append as needed...
1084 current = 0; /* Insert at beginning */
1086 current = a->num_elements; /* Append to the end */
1091 * Do a binary search for the insertion point...
1094 current = cups_array_find(a, e, a->insert, &diff);
1099 * Insert after the current element...
1107 * Compared equal, make sure we add to the begining or end of
1108 * the current run of equal elements...
1116 * Insert at beginning of run...
1119 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
1126 * Append at end of run...
1133 while (current < a->num_elements &&
1134 !(*(a->compare))(e, a->elements[current], a->data));
1140 * Insert or append the element...
1143 if (current < a->num_elements)
1146 * Shift other elements to the right...
1149 memmove(a->elements + current + 1, a->elements + current,
1150 (size_t)(a->num_elements - current) * sizeof(void *));
1152 if (a->current >= current)
1155 for (i = 0; i < a->num_saved; i ++)
1156 if (a->saved[i] >= current)
1159 DEBUG_printf(("9cups_array_add: insert element at index " CUPS_LLFMT, CUPS_LLCAST current));
1163 DEBUG_printf(("9cups_array_add: append element at " CUPS_LLFMT, CUPS_LLCAST current));
1168 if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL)
1170 DEBUG_puts("8cups_array_add: Copy function returned NULL, returning 0");
1175 a->elements[current] = e;
1178 a->insert = current;
1181 for (current = 0; current < a->num_elements; current ++)
1182 DEBUG_printf(("9cups_array_add: a->elements[" CUPS_LLFMT "]=%p", CUPS_LLCAST current, a->elements[current]));
1185 DEBUG_puts("9cups_array_add: returning 1");
1192 * 'cups_array_find()' - Find an element in the array.
1195 static int /* O - Index of match */
1196 cups_array_find(cups_array_t *a, /* I - Array */
1197 void *e, /* I - Element */
1198 int prev, /* I - Previous index */
1199 int *rdiff) /* O - Difference of match */
1201 int left, /* Left side of search */
1202 right, /* Right side of search */
1203 current, /* Current element */
1204 diff; /* Comparison with current element */
1207 DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", (void *)a, e, prev, (void *)rdiff));
1212 * Do a binary search for the element...
1215 DEBUG_puts("9cups_array_find: binary search");
1217 if (prev >= 0 && prev < a->num_elements)
1220 * Start search on either side of previous...
1223 if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 ||
1224 (diff < 0 && prev == 0) ||
1225 (diff > 0 && prev == (a->num_elements - 1)))
1228 * Exact or edge match, return it!
1231 DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff));
1240 * Start with previous on right side...
1249 * Start wih previous on left side...
1253 right = a->num_elements - 1;
1259 * Start search in the middle...
1263 right = a->num_elements - 1;
1268 current = (left + right) / 2;
1269 diff = (*(a->compare))(e, a->elements[current], a->data);
1271 DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d",
1272 left, right, current, diff));
1281 while ((right - left) > 1);
1286 * Check the last 1 or 2 elements...
1289 if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0)
1293 diff = (*(a->compare))(e, a->elements[right], a->data);
1301 * Do a linear pointer search...
1304 DEBUG_puts("9cups_array_find: linear search");
1308 for (current = 0; current < a->num_elements; current ++)
1309 if (a->elements[current] == e)
1317 * Return the closest element and the difference...
1320 DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff));