2 * "$Id: array.c 7616 2008-05-28 00:34:13Z mike $"
4 * Sorted array routines for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * cupsArrayAdd() - Add an element to the array.
20 * cupsArrayClear() - Clear the array.
21 * cupsArrayCount() - Get the number of elements in the array.
22 * cupsArrayCurrent() - Return the current element in the array.
23 * cupsArrayDelete() - Free all memory used by the array.
24 * cupsArrayDup() - Duplicate the array.
25 * cupsArrayFind() - Find an element in the array.
26 * cupsArrayFirst() - Get the first element in the array.
27 * cupsArrayGetIndex() - Get the index of the current element.
28 * cupsArrayGetInsert() - Get the index of the last inserted element.
29 * cupsArrayIndex() - Get the N-th element in the array.
30 * cupsArrayInsert() - Insert an element in the array.
31 * cupsArrayLast() - Get the last element in the array.
32 * cupsArrayNew() - Create a new array.
33 * cupsArrayNext() - Get the next element in the array.
34 * cupsArrayPrev() - Get the previous element in the array.
35 * cupsArrayRemove() - Remove an element from the array.
36 * cupsArrayRestore() - Reset the current element to the last cupsArraySave.
37 * cupsArraySave() - Mark the current element for a later
39 * cupsArrayUserData() - Return the user data for an array.
40 * cups_array_add() - Insert or append an element to the array...
41 * cups_array_find() - Find an element in the array...
45 * Include necessary headers...
57 #define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/
61 * Types and structures...
64 struct _cups_array_s
/**** CUPS array structure ****/
67 * The current implementation uses an insertion sort into an array of
68 * sorted pointers. We leave the array type private/opaque so that we
69 * can change the underlying implementation without affecting the users
73 int num_elements
, /* Number of array elements */
74 alloc_elements
, /* Allocated array elements */
75 current
, /* Current element */
76 insert
, /* Last inserted element */
77 unique
, /* Are all elements unique? */
78 num_saved
, /* Number of saved elements */
81 void **elements
; /* Array elements */
82 cups_array_func_t compare
; /* Element comparison function */
83 void *data
; /* User data passed to compare */
84 cups_ahash_func_t hashfunc
; /* Hash function */
85 int hashsize
, /* Size of hash */
86 *hash
; /* Hash array */
94 static int cups_array_add(cups_array_t
*a
, void *e
, int insert
);
95 static int cups_array_find(cups_array_t
*a
, void *e
, int prev
, int *rdiff
);
99 * 'cupsArrayAdd()' - Add an element to the array.
101 * When adding an element to a sorted array, non-unique elements are
102 * appended at the end of the run of identical elements. For unsorted arrays,
103 * the element is appended to the end of the array.
108 int /* O - 1 on success, 0 on failure */
109 cupsArrayAdd(cups_array_t
*a
, /* I - Array */
110 void *e
) /* I - Element */
112 DEBUG_printf(("cupsArrayAdd(a=%p, e=%p)\n", a
, e
));
115 * Range check input...
120 DEBUG_puts("cupsArrayAdd: returning 0");
125 * Append the element...
128 return (cups_array_add(a
, e
, 0));
133 * 'cupsArrayClear()' - Clear the array.
135 * This function is equivalent to removing all elements in the array.
136 * The caller is responsible for freeing the memory used by the
137 * elements themselves.
143 cupsArrayClear(cups_array_t
*a
) /* I - Array */
146 * Range check input...
153 * Set the number of elements to 0; we don't actually free the memory
154 * here - that is done in cupsArrayDelete()...
166 * 'cupsArrayCount()' - Get the number of elements in the array.
171 int /* O - Number of elements */
172 cupsArrayCount(cups_array_t
*a
) /* I - Array */
175 * Range check input...
182 * Return the number of elements...
185 return (a
->num_elements
);
190 * 'cupsArrayCurrent()' - Return the current element in the array.
192 * The current element is undefined until you call @link cupsArrayFind@,
193 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
198 void * /* O - Element */
199 cupsArrayCurrent(cups_array_t
*a
) /* I - Array */
202 * Range check input...
209 * Return the current element...
212 if (a
->current
>= 0 && a
->current
< a
->num_elements
)
213 return (a
->elements
[a
->current
]);
220 * 'cupsArrayDelete()' - Free all memory used by the array.
222 * The caller is responsible for freeing the memory used by the
223 * elements themselves.
229 cupsArrayDelete(cups_array_t
*a
) /* I - Array */
232 * Range check input...
239 * Free the array of element pointers - the caller is responsible
240 * for freeing the elements themselves...
243 if (a
->alloc_elements
)
254 * 'cupsArrayDup()' - Duplicate the array.
259 cups_array_t
* /* O - Duplicate array */
260 cupsArrayDup(cups_array_t
*a
) /* I - Array */
262 cups_array_t
*da
; /* Duplicate array */
266 * Range check input...
273 * Allocate memory for the array...
276 da
= calloc(1, sizeof(cups_array_t
));
280 da
->compare
= a
->compare
;
282 da
->current
= a
->current
;
283 da
->insert
= a
->insert
;
284 da
->unique
= a
->unique
;
285 da
->num_saved
= a
->num_saved
;
287 memcpy(da
->saved
, a
->saved
, sizeof(a
->saved
));
292 * Allocate memory for the elements...
295 da
->elements
= malloc(a
->num_elements
* sizeof(void *));
303 * Copy the element pointers...
306 memcpy(da
->elements
, a
->elements
, a
->num_elements
* sizeof(void *));
307 da
->num_elements
= a
->num_elements
;
308 da
->alloc_elements
= a
->num_elements
;
312 * Return the new array...
320 * 'cupsArrayFind()' - Find an element in the array.
325 void * /* O - Element found or @code NULL@ */
326 cupsArrayFind(cups_array_t
*a
, /* I - Array */
327 void *e
) /* I - Element */
329 int current
, /* Current element */
330 diff
, /* Difference */
331 hash
; /* Hash index */
335 * Range check input...
342 * See if we have any elements...
345 if (!a
->num_elements
)
349 * Yes, look for a match...
354 hash
= (*(a
->hashfunc
))(e
, a
->data
);
356 if (hash
< 0 || hash
>= a
->hashsize
)
358 current
= a
->current
;
363 current
= a
->hash
[hash
];
365 if (current
< 0 || current
>= a
->num_elements
)
366 current
= a
->current
;
371 current
= a
->current
;
375 current
= cups_array_find(a
, e
, current
, &diff
);
379 * Found a match! If the array does not contain unique values, find
380 * the first element that is the same...
383 if (!a
->unique
&& a
->compare
)
386 * The array is not unique, find the first match...
389 while (current
> 0 && !(*(a
->compare
))(e
, a
->elements
[current
- 1],
394 a
->current
= current
;
397 a
->hash
[hash
] = current
;
399 return (a
->elements
[current
]);
415 * 'cupsArrayFirst()' - Get the first element in the array.
420 void * /* O - First element or @code NULL@ if the array is empty */
421 cupsArrayFirst(cups_array_t
*a
) /* I - Array */
424 * Range check input...
431 * Return the first element...
436 return (cupsArrayCurrent(a
));
441 * 'cupsArrayGetIndex()' - Get the index of the current element.
443 * The current element is undefined until you call @link cupsArrayFind@,
444 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
449 int /* O - Index of the current element, starting at 0 */
450 cupsArrayGetIndex(cups_array_t
*a
) /* I - Array */
460 * 'cupsArrayGetInsert()' - Get the index of the last inserted element.
465 int /* O - Index of the last inserted element, starting at 0 */
466 cupsArrayGetInsert(cups_array_t
*a
) /* I - Array */
476 * 'cupsArrayIndex()' - Get the N-th element in the array.
481 void * /* O - N-th element or @code NULL@ */
482 cupsArrayIndex(cups_array_t
*a
, /* I - Array */
483 int n
) /* I - Index into array, starting at 0 */
490 return (cupsArrayCurrent(a
));
495 * 'cupsArrayInsert()' - Insert an element in the array.
497 * When inserting an element in a sorted array, non-unique elements are
498 * inserted at the beginning of the run of identical elements. For unsorted
499 * arrays, the element is inserted at the beginning of the array.
504 int /* O - 0 on failure, 1 on success */
505 cupsArrayInsert(cups_array_t
*a
, /* I - Array */
506 void *e
) /* I - Element */
508 DEBUG_printf(("cupsArrayInsert(a=%p, e=%p)\n", a
, e
));
511 * Range check input...
516 DEBUG_puts("cupsArrayInsert: returning 0");
521 * Insert the element...
524 return (cups_array_add(a
, e
, 1));
529 * 'cupsArrayLast()' - Get the last element in the array.
534 void * /* O - Last element or @code NULL@ if the array is empty */
535 cupsArrayLast(cups_array_t
*a
) /* I - Array */
538 * Range check input...
545 * Return the last element...
548 a
->current
= a
->num_elements
- 1;
550 return (cupsArrayCurrent(a
));
555 * 'cupsArrayNew()' - Create a new array.
557 * The comparison function ("f") is used to create a sorted array. The function
558 * receives pointers to two elements and the user data pointer ("d") - the user
559 * data pointer argument can safely be omitted when not required so functions
560 * like @code strcmp@ can be used for sorted string arrays.
565 cups_array_t
* /* O - Array */
566 cupsArrayNew(cups_array_func_t f
, /* I - Comparison function or @code NULL@ for an unsorted array */
567 void *d
) /* I - User data pointer or @code NULL@ */
569 return (cupsArrayNew2(f
, d
, 0, 0));
574 * 'cupsArrayNew2()' - Create a new array with hash.
576 * The comparison function ("f") is used to create a sorted array. The function
577 * receives pointers to two elements and the user data pointer ("d") - the user
578 * data pointer argument can safely be omitted when not required so functions
579 * like @code strcmp@ can be used for sorted string arrays.
581 * The hash function ("h") is used to implement cached lookups with the
582 * specified hash size ("hsize").
587 cups_array_t
* /* O - Array */
588 cupsArrayNew2(cups_array_func_t f
, /* I - Comparison function or @code NULL@ for an unsorted array */
589 void *d
, /* I - User data or @code NULL@ */
590 cups_ahash_func_t h
, /* I - Hash function or @code NULL@ for unhashed lookups */
591 int hsize
) /* I - Hash size (>= 0) */
593 cups_array_t
*a
; /* Array */
597 * Allocate memory for the array...
600 a
= calloc(1, sizeof(cups_array_t
));
615 a
->hash
= malloc(hsize
* sizeof(int));
623 memset(a
->hash
, -1, hsize
* sizeof(int));
631 * 'cupsArrayNext()' - Get the next element in the array.
633 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)".
635 * The next element is undefined until you call @link cupsArrayFind@,
636 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
637 * to set the current element.
642 void * /* O - Next element or @code NULL@ */
643 cupsArrayNext(cups_array_t
*a
) /* I - Array */
646 * Range check input...
653 * Return the next element...
656 if (a
->current
< a
->num_elements
)
659 return (cupsArrayCurrent(a
));
664 * 'cupsArrayPrev()' - Get the previous element in the array.
666 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)".
668 * The previous element is undefined until you call @link cupsArrayFind@,
669 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
670 * to set the current element.
675 void * /* O - Previous element or @code NULL@ */
676 cupsArrayPrev(cups_array_t
*a
) /* I - Array */
679 * Range check input...
686 * Return the previous element...
692 return (cupsArrayCurrent(a
));
697 * 'cupsArrayRemove()' - Remove an element from the array.
699 * If more than one element matches "e", only the first matching element is
702 * The caller is responsible for freeing the memory used by the
708 int /* O - 1 on success, 0 on failure */
709 cupsArrayRemove(cups_array_t
*a
, /* I - Array */
710 void *e
) /* I - Element */
712 int i
, /* Looping var */
713 current
, /* Current element */
714 diff
; /* Difference */
718 * Range check input...
725 * See if the element is in the array...
728 if (!a
->num_elements
)
731 current
= cups_array_find(a
, e
, a
->current
, &diff
);
736 * Yes, now remove it...
741 if (current
< a
->num_elements
)
742 memmove(a
->elements
+ current
, a
->elements
+ current
+ 1,
743 (a
->num_elements
- current
) * sizeof(void *));
745 if (current
<= a
->current
)
748 if (current
< a
->insert
)
750 else if (current
== a
->insert
)
753 for (i
= 0; i
< a
->num_saved
; i
++)
754 if (current
<= a
->saved
[i
])
757 if (a
->num_elements
<= 1)
765 * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
770 void * /* O - New current element */
771 cupsArrayRestore(cups_array_t
*a
) /* I - Array */
776 if (a
->num_saved
<= 0)
780 a
->current
= a
->saved
[a
->num_saved
];
782 if (a
->current
>= 0 && a
->current
< a
->num_elements
)
783 return (a
->elements
[a
->current
]);
790 * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@.
792 * The current element is undefined until you call @link cupsArrayFind@,
793 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
794 * to set the current element.
796 * The save/restore stack is guaranteed to be at least 32 elements deep.
801 int /* O - 1 on success, 0 on failure */
802 cupsArraySave(cups_array_t
*a
) /* I - Array */
807 if (a
->num_saved
>= _CUPS_MAXSAVE
)
810 a
->saved
[a
->num_saved
] = a
->current
;
818 * 'cupsArrayUserData()' - Return the user data for an array.
823 void * /* O - User data */
824 cupsArrayUserData(cups_array_t
*a
) /* I - Array */
834 * 'cups_array_add()' - Insert or append an element to the array...
839 static int /* O - 1 on success, 0 on failure */
840 cups_array_add(cups_array_t
*a
, /* I - Array */
841 void *e
, /* I - Element to add */
842 int insert
) /* I - 1 = insert, 0 = append */
844 int i
, /* Looping var */
845 current
, /* Current element */
846 diff
; /* Comparison with current element */
849 DEBUG_printf(("cups_array_add(a=%p, e=%p, insert=%d)\n", a
, e
, insert
));
852 * Verify we have room for the new element...
855 if (a
->num_elements
>= a
->alloc_elements
)
858 * Allocate additional elements; start with 16 elements, then
859 * double the size until 1024 elements, then add 1024 elements
863 void **temp
; /* New array elements */
864 int count
; /* New allocation count */
867 if (a
->alloc_elements
== 0)
870 temp
= malloc(count
* sizeof(void *));
874 if (a
->alloc_elements
< 1024)
875 count
= a
->alloc_elements
* 2;
877 count
= a
->alloc_elements
+ 1024;
879 temp
= realloc(a
->elements
, count
* sizeof(void *));
882 DEBUG_printf(("cups_array_add: count=%d\n", count
));
886 DEBUG_puts("cupsAddAdd: allocation failed, returning 0");
890 a
->alloc_elements
= count
;
895 * Find the insertion point for the new element; if there is no
896 * compare function or elements, just add it to the beginning or end...
899 if (!a
->num_elements
|| !a
->compare
)
902 * No elements or comparison function, insert/append as needed...
906 current
= 0; /* Insert at beginning */
908 current
= a
->num_elements
; /* Append to the end */
913 * Do a binary search for the insertion point...
916 current
= cups_array_find(a
, e
, a
->insert
, &diff
);
921 * Insert after the current element...
929 * Compared equal, make sure we add to the begining or end of
930 * the current run of equal elements...
938 * Insert at beginning of run...
941 while (current
> 0 && !(*(a
->compare
))(e
, a
->elements
[current
- 1],
948 * Append at end of run...
955 while (current
< a
->num_elements
&&
956 !(*(a
->compare
))(e
, a
->elements
[current
], a
->data
));
962 * Insert or append the element...
965 if (current
< a
->num_elements
)
968 * Shift other elements to the right...
971 memmove(a
->elements
+ current
+ 1, a
->elements
+ current
,
972 (a
->num_elements
- current
) * sizeof(void *));
974 if (a
->current
>= current
)
977 for (i
= 0; i
< a
->num_saved
; i
++)
978 if (a
->saved
[i
] >= current
)
981 DEBUG_printf(("cups_array_add: insert element at index %d...\n", current
));
985 DEBUG_printf(("cups_array_add: append element at %d...\n", current
));
988 a
->elements
[current
] = e
;
993 for (current
= 0; current
< a
->num_elements
; current
++)
994 DEBUG_printf(("cups_array_add: a->elements[%d]=%p\n", current
,
995 a
->elements
[current
]));
998 DEBUG_puts("cups_array_add: returning 1");
1005 * 'cups_array_find()' - Find an element in the array...
1010 static int /* O - Index of match */
1011 cups_array_find(cups_array_t
*a
, /* I - Array */
1012 void *e
, /* I - Element */
1013 int prev
, /* I - Previous index */
1014 int *rdiff
) /* O - Difference of match */
1016 int left
, /* Left side of search */
1017 right
, /* Right side of search */
1018 current
, /* Current element */
1019 diff
; /* Comparison with current element */
1022 DEBUG_printf(("cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)\n", a
, e
, prev
,
1028 * Do a binary search for the element...
1031 DEBUG_puts("cups_array_find: binary search");
1033 if (prev
>= 0 && prev
< a
->num_elements
)
1036 * Start search on either side of previous...
1039 if ((diff
= (*(a
->compare
))(e
, a
->elements
[prev
], a
->data
)) == 0 ||
1040 (diff
< 0 && prev
== 0) ||
1041 (diff
> 0 && prev
== (a
->num_elements
- 1)))
1044 * Exact or edge match, return it!
1047 DEBUG_printf(("cups_array_find: Returning %d, diff=%d\n", prev
, diff
));
1056 * Start with previous on right side...
1065 * Start wih previous on left side...
1069 right
= a
->num_elements
- 1;
1075 * Start search in the middle...
1079 right
= a
->num_elements
- 1;
1084 current
= (left
+ right
) / 2;
1085 diff
= (*(a
->compare
))(e
, a
->elements
[current
], a
->data
);
1087 DEBUG_printf(("cups_array_find: left=%d, right=%d, current=%d, diff=%d\n",
1088 left
, right
, current
, diff
));
1097 while ((right
- left
) > 1);
1102 * Check the last 1 or 2 elements...
1105 if ((diff
= (*(a
->compare
))(e
, a
->elements
[left
], a
->data
)) <= 0)
1109 diff
= (*(a
->compare
))(e
, a
->elements
[right
], a
->data
);
1117 * Do a linear pointer search...
1120 DEBUG_puts("cups_array_find: linear search");
1124 for (current
= 0; current
< a
->num_elements
; current
++)
1125 if (a
->elements
[current
] == e
)
1133 * Return the closest element and the difference...
1136 DEBUG_printf(("cups_array_find: Returning %d, diff=%d\n", current
, diff
));
1145 * End of "$Id: array.c 7616 2008-05-28 00:34:13Z mike $".