]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/array.c
cbe52e59f6e2826c594cf3121b748992e8dce90f
[thirdparty/cups.git] / cups / array.c
1 /*
2 * "$Id$"
3 *
4 * Sorted array routines for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
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/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18 /*
19 * Include necessary headers...
20 */
21
22 #include <cups/cups.h>
23 #include "string-private.h"
24 #include "debug-private.h"
25 #include "array-private.h"
26
27
28 /*
29 * Limits...
30 */
31
32 #define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/
33
34
35 /*
36 * Types and structures...
37 */
38
39 struct _cups_array_s /**** CUPS array structure ****/
40 {
41 /*
42 * The current implementation uses an insertion sort into an array of
43 * sorted pointers. We leave the array type private/opaque so that we
44 * can change the underlying implementation without affecting the users
45 * of this API.
46 */
47
48 int num_elements, /* Number of array elements */
49 alloc_elements, /* Allocated array elements */
50 current, /* Current element */
51 insert, /* Last inserted element */
52 unique, /* Are all elements unique? */
53 num_saved, /* Number of saved elements */
54 saved[_CUPS_MAXSAVE];
55 /* Saved elements */
56 void **elements; /* Array elements */
57 cups_array_func_t compare; /* Element comparison function */
58 void *data; /* User data passed to compare */
59 cups_ahash_func_t hashfunc; /* Hash function */
60 int hashsize, /* Size of hash */
61 *hash; /* Hash array */
62 cups_acopy_func_t copyfunc; /* Copy function */
63 cups_afree_func_t freefunc; /* Free function */
64 };
65
66
67 /*
68 * Local functions...
69 */
70
71 static int cups_array_add(cups_array_t *a, void *e, int insert);
72 static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff);
73
74
75 /*
76 * 'cupsArrayAdd()' - Add an element to the array.
77 *
78 * When adding an element to a sorted array, non-unique elements are
79 * appended at the end of the run of identical elements. For unsorted arrays,
80 * the element is appended to the end of the array.
81 *
82 * @since CUPS 1.2/OS X 10.5@
83 */
84
85 int /* O - 1 on success, 0 on failure */
86 cupsArrayAdd(cups_array_t *a, /* I - Array */
87 void *e) /* I - Element */
88 {
89 DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", (void *)a, e));
90
91 /*
92 * Range check input...
93 */
94
95 if (!a || !e)
96 {
97 DEBUG_puts("3cupsArrayAdd: returning 0");
98 return (0);
99 }
100
101 /*
102 * Append the element...
103 */
104
105 return (cups_array_add(a, e, 0));
106 }
107
108
109 /*
110 * '_cupsArrayAddStrings()' - Add zero or more delimited strings to an array.
111 *
112 * Note: The array MUST be created using the @link _cupsArrayNewStrings@
113 * function. Duplicate strings are NOT added. If the string pointer "s" is NULL
114 * or the empty string, no strings are added to the array.
115 */
116
117 int /* O - 1 on success, 0 on failure */
118 _cupsArrayAddStrings(cups_array_t *a, /* I - Array */
119 const char *s, /* I - Delimited strings or NULL */
120 char delim)/* I - Delimiter character */
121 {
122 char *buffer, /* Copy of string */
123 *start, /* Start of string */
124 *end; /* End of string */
125 int status = 1; /* Status of add */
126
127
128 DEBUG_printf(("_cupsArrayAddStrings(a=%p, s=\"%s\", delim='%c')", (void *)a, s, delim));
129
130 if (!a || !s || !*s)
131 {
132 DEBUG_puts("1_cupsArrayAddStrings: Returning 0");
133 return (0);
134 }
135
136 if (delim == ' ')
137 {
138 /*
139 * Skip leading whitespace...
140 */
141
142 DEBUG_puts("1_cupsArrayAddStrings: Skipping leading whitespace.");
143
144 while (*s && isspace(*s & 255))
145 s ++;
146
147 DEBUG_printf(("1_cupsArrayAddStrings: Remaining string \"%s\".", s));
148 }
149
150 if (!strchr(s, delim) &&
151 (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n'))))
152 {
153 /*
154 * String doesn't contain a delimiter, so add it as a single value...
155 */
156
157 DEBUG_puts("1_cupsArrayAddStrings: No delimiter seen, adding a single "
158 "value.");
159
160 if (!cupsArrayFind(a, (void *)s))
161 status = cupsArrayAdd(a, (void *)s);
162 }
163 else if ((buffer = strdup(s)) == NULL)
164 {
165 DEBUG_puts("1_cupsArrayAddStrings: Unable to duplicate string.");
166 status = 0;
167 }
168 else
169 {
170 for (start = end = buffer; *end; start = end)
171 {
172 /*
173 * Find the end of the current delimited string and see if we need to add
174 * it...
175 */
176
177 if (delim == ' ')
178 {
179 while (*end && !isspace(*end & 255))
180 end ++;
181 while (*end && isspace(*end & 255))
182 *end++ = '\0';
183 }
184 else if ((end = strchr(start, delim)) != NULL)
185 *end++ = '\0';
186 else
187 end = start + strlen(start);
188
189 DEBUG_printf(("1_cupsArrayAddStrings: Adding \"%s\", end=\"%s\"", start,
190 end));
191
192 if (!cupsArrayFind(a, start))
193 status &= cupsArrayAdd(a, start);
194 }
195
196 free(buffer);
197 }
198
199 DEBUG_printf(("1_cupsArrayAddStrings: Returning %d.", status));
200
201 return (status);
202 }
203
204
205 /*
206 * 'cupsArrayClear()' - Clear the array.
207 *
208 * This function is equivalent to removing all elements in the array.
209 * The caller is responsible for freeing the memory used by the
210 * elements themselves.
211 *
212 * @since CUPS 1.2/OS X 10.5@
213 */
214
215 void
216 cupsArrayClear(cups_array_t *a) /* I - Array */
217 {
218 /*
219 * Range check input...
220 */
221
222 if (!a)
223 return;
224
225 /*
226 * Free the existing elements as needed..
227 */
228
229 if (a->freefunc)
230 {
231 int i; /* Looping var */
232 void **e; /* Current element */
233
234 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
235 (a->freefunc)(*e, a->data);
236 }
237
238 /*
239 * Set the number of elements to 0; we don't actually free the memory
240 * here - that is done in cupsArrayDelete()...
241 */
242
243 a->num_elements = 0;
244 a->current = -1;
245 a->insert = -1;
246 a->unique = 1;
247 a->num_saved = 0;
248 }
249
250
251 /*
252 * 'cupsArrayCount()' - Get the number of elements in the array.
253 *
254 * @since CUPS 1.2/OS X 10.5@
255 */
256
257 int /* O - Number of elements */
258 cupsArrayCount(cups_array_t *a) /* I - Array */
259 {
260 /*
261 * Range check input...
262 */
263
264 if (!a)
265 return (0);
266
267 /*
268 * Return the number of elements...
269 */
270
271 return (a->num_elements);
272 }
273
274
275 /*
276 * 'cupsArrayCurrent()' - Return the current element in the array.
277 *
278 * The current element is undefined until you call @link cupsArrayFind@,
279 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
280 *
281 * @since CUPS 1.2/OS X 10.5@
282 */
283
284 void * /* O - Element */
285 cupsArrayCurrent(cups_array_t *a) /* I - Array */
286 {
287 /*
288 * Range check input...
289 */
290
291 if (!a)
292 return (NULL);
293
294 /*
295 * Return the current element...
296 */
297
298 if (a->current >= 0 && a->current < a->num_elements)
299 return (a->elements[a->current]);
300 else
301 return (NULL);
302 }
303
304
305 /*
306 * 'cupsArrayDelete()' - Free all memory used by the array.
307 *
308 * The caller is responsible for freeing the memory used by the
309 * elements themselves.
310 *
311 * @since CUPS 1.2/OS X 10.5@
312 */
313
314 void
315 cupsArrayDelete(cups_array_t *a) /* I - Array */
316 {
317 /*
318 * Range check input...
319 */
320
321 if (!a)
322 return;
323
324 /*
325 * Free the elements if we have a free function (otherwise the caller is
326 * responsible for doing the dirty work...)
327 */
328
329 if (a->freefunc)
330 {
331 int i; /* Looping var */
332 void **e; /* Current element */
333
334 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
335 (a->freefunc)(*e, a->data);
336 }
337
338 /*
339 * Free the array of element pointers...
340 */
341
342 if (a->alloc_elements)
343 free(a->elements);
344
345 if (a->hashsize)
346 free(a->hash);
347
348 free(a);
349 }
350
351
352 /*
353 * 'cupsArrayDup()' - Duplicate the array.
354 *
355 * @since CUPS 1.2/OS X 10.5@
356 */
357
358 cups_array_t * /* O - Duplicate array */
359 cupsArrayDup(cups_array_t *a) /* I - Array */
360 {
361 cups_array_t *da; /* Duplicate array */
362
363
364 /*
365 * Range check input...
366 */
367
368 if (!a)
369 return (NULL);
370
371 /*
372 * Allocate memory for the array...
373 */
374
375 da = calloc(1, sizeof(cups_array_t));
376 if (!da)
377 return (NULL);
378
379 da->compare = a->compare;
380 da->data = a->data;
381 da->current = a->current;
382 da->insert = a->insert;
383 da->unique = a->unique;
384 da->num_saved = a->num_saved;
385
386 memcpy(da->saved, a->saved, sizeof(a->saved));
387
388 if (a->num_elements)
389 {
390 /*
391 * Allocate memory for the elements...
392 */
393
394 da->elements = malloc((size_t)a->num_elements * sizeof(void *));
395 if (!da->elements)
396 {
397 free(da);
398 return (NULL);
399 }
400
401 /*
402 * Copy the element pointers...
403 */
404
405 if (a->copyfunc)
406 {
407 /*
408 * Use the copy function to make a copy of each element...
409 */
410
411 int i; /* Looping var */
412
413 for (i = 0; i < a->num_elements; i ++)
414 da->elements[i] = (a->copyfunc)(a->elements[i], a->data);
415 }
416 else
417 {
418 /*
419 * Just copy raw pointers...
420 */
421
422 memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *));
423 }
424
425 da->num_elements = a->num_elements;
426 da->alloc_elements = a->num_elements;
427 }
428
429 /*
430 * Return the new array...
431 */
432
433 return (da);
434 }
435
436
437 /*
438 * 'cupsArrayFind()' - Find an element in the array.
439 *
440 * @since CUPS 1.2/OS X 10.5@
441 */
442
443 void * /* O - Element found or @code NULL@ */
444 cupsArrayFind(cups_array_t *a, /* I - Array */
445 void *e) /* I - Element */
446 {
447 int current, /* Current element */
448 diff, /* Difference */
449 hash; /* Hash index */
450
451
452 /*
453 * Range check input...
454 */
455
456 if (!a || !e)
457 return (NULL);
458
459 /*
460 * See if we have any elements...
461 */
462
463 if (!a->num_elements)
464 return (NULL);
465
466 /*
467 * Yes, look for a match...
468 */
469
470 if (a->hash)
471 {
472 hash = (*(a->hashfunc))(e, a->data);
473
474 if (hash < 0 || hash >= a->hashsize)
475 {
476 current = a->current;
477 hash = -1;
478 }
479 else
480 {
481 current = a->hash[hash];
482
483 if (current < 0 || current >= a->num_elements)
484 current = a->current;
485 }
486 }
487 else
488 {
489 current = a->current;
490 hash = -1;
491 }
492
493 current = cups_array_find(a, e, current, &diff);
494 if (!diff)
495 {
496 /*
497 * Found a match! If the array does not contain unique values, find
498 * the first element that is the same...
499 */
500
501 if (!a->unique && a->compare)
502 {
503 /*
504 * The array is not unique, find the first match...
505 */
506
507 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
508 a->data))
509 current --;
510 }
511
512 a->current = current;
513
514 if (hash >= 0)
515 a->hash[hash] = current;
516
517 return (a->elements[current]);
518 }
519 else
520 {
521 /*
522 * No match...
523 */
524
525 a->current = -1;
526
527 return (NULL);
528 }
529 }
530
531
532 /*
533 * 'cupsArrayFirst()' - Get the first element in the array.
534 *
535 * @since CUPS 1.2/OS X 10.5@
536 */
537
538 void * /* O - First element or @code NULL@ if the array is empty */
539 cupsArrayFirst(cups_array_t *a) /* I - Array */
540 {
541 /*
542 * Range check input...
543 */
544
545 if (!a)
546 return (NULL);
547
548 /*
549 * Return the first element...
550 */
551
552 a->current = 0;
553
554 return (cupsArrayCurrent(a));
555 }
556
557
558 /*
559 * 'cupsArrayGetIndex()' - Get the index of the current element.
560 *
561 * The current element is undefined until you call @link cupsArrayFind@,
562 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
563 *
564 * @since CUPS 1.3/OS X 10.5@
565 */
566
567 int /* O - Index of the current element, starting at 0 */
568 cupsArrayGetIndex(cups_array_t *a) /* I - Array */
569 {
570 if (!a)
571 return (-1);
572 else
573 return (a->current);
574 }
575
576
577 /*
578 * 'cupsArrayGetInsert()' - Get the index of the last inserted element.
579 *
580 * @since CUPS 1.3/OS X 10.5@
581 */
582
583 int /* O - Index of the last inserted element, starting at 0 */
584 cupsArrayGetInsert(cups_array_t *a) /* I - Array */
585 {
586 if (!a)
587 return (-1);
588 else
589 return (a->insert);
590 }
591
592
593 /*
594 * 'cupsArrayIndex()' - Get the N-th element in the array.
595 *
596 * @since CUPS 1.2/OS X 10.5@
597 */
598
599 void * /* O - N-th element or @code NULL@ */
600 cupsArrayIndex(cups_array_t *a, /* I - Array */
601 int n) /* I - Index into array, starting at 0 */
602 {
603 if (!a)
604 return (NULL);
605
606 a->current = n;
607
608 return (cupsArrayCurrent(a));
609 }
610
611
612 /*
613 * 'cupsArrayInsert()' - Insert an element in the array.
614 *
615 * When inserting an element in a sorted array, non-unique elements are
616 * inserted at the beginning of the run of identical elements. For unsorted
617 * arrays, the element is inserted at the beginning of the array.
618 *
619 * @since CUPS 1.2/OS X 10.5@
620 */
621
622 int /* O - 0 on failure, 1 on success */
623 cupsArrayInsert(cups_array_t *a, /* I - Array */
624 void *e) /* I - Element */
625 {
626 DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", (void *)a, e));
627
628 /*
629 * Range check input...
630 */
631
632 if (!a || !e)
633 {
634 DEBUG_puts("3cupsArrayInsert: returning 0");
635 return (0);
636 }
637
638 /*
639 * Insert the element...
640 */
641
642 return (cups_array_add(a, e, 1));
643 }
644
645
646 /*
647 * 'cupsArrayLast()' - Get the last element in the array.
648 *
649 * @since CUPS 1.2/OS X 10.5@
650 */
651
652 void * /* O - Last element or @code NULL@ if the array is empty */
653 cupsArrayLast(cups_array_t *a) /* I - Array */
654 {
655 /*
656 * Range check input...
657 */
658
659 if (!a)
660 return (NULL);
661
662 /*
663 * Return the last element...
664 */
665
666 a->current = a->num_elements - 1;
667
668 return (cupsArrayCurrent(a));
669 }
670
671
672 /*
673 * 'cupsArrayNew()' - Create a new array.
674 *
675 * The comparison function ("f") is used to create a sorted array. The function
676 * receives pointers to two elements and the user data pointer ("d") - the user
677 * data pointer argument can safely be omitted when not required so functions
678 * like @code strcmp@ can be used for sorted string arrays.
679 *
680 * @since CUPS 1.2/OS X 10.5@
681 */
682
683 cups_array_t * /* O - Array */
684 cupsArrayNew(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
685 void *d) /* I - User data pointer or @code NULL@ */
686 {
687 return (cupsArrayNew3(f, d, 0, 0, 0, 0));
688 }
689
690
691 /*
692 * 'cupsArrayNew2()' - Create a new array with hash.
693 *
694 * The comparison function ("f") is used to create a sorted array. The function
695 * receives pointers to two elements and the user data pointer ("d") - the user
696 * data pointer argument can safely be omitted when not required so functions
697 * like @code strcmp@ can be used for sorted string arrays.
698 *
699 * The hash function ("h") is used to implement cached lookups with the
700 * specified hash size ("hsize").
701 *
702 * @since CUPS 1.3/OS X 10.5@
703 */
704
705 cups_array_t * /* O - Array */
706 cupsArrayNew2(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
707 void *d, /* I - User data or @code NULL@ */
708 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
709 int hsize) /* I - Hash size (>= 0) */
710 {
711 return (cupsArrayNew3(f, d, h, hsize, 0, 0));
712 }
713
714
715 /*
716 * 'cupsArrayNew3()' - Create a new array with hash and/or free function.
717 *
718 * The comparison function ("f") is used to create a sorted array. The function
719 * receives pointers to two elements and the user data pointer ("d") - the user
720 * data pointer argument can safely be omitted when not required so functions
721 * like @code strcmp@ can be used for sorted string arrays.
722 *
723 * The hash function ("h") is used to implement cached lookups with the
724 * specified hash size ("hsize").
725 *
726 * The copy function ("cf") is used to automatically copy/retain elements when
727 * added or the array is copied.
728 *
729 * The free function ("cf") is used to automatically free/release elements when
730 * removed or the array is deleted.
731 *
732 * @since CUPS 1.5/OS X 10.7@
733 */
734
735 cups_array_t * /* O - Array */
736 cupsArrayNew3(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
737 void *d, /* I - User data or @code NULL@ */
738 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
739 int hsize, /* I - Hash size (>= 0) */
740 cups_acopy_func_t cf, /* I - Copy function */
741 cups_afree_func_t ff) /* I - Free function */
742 {
743 cups_array_t *a; /* Array */
744
745
746 /*
747 * Allocate memory for the array...
748 */
749
750 a = calloc(1, sizeof(cups_array_t));
751 if (!a)
752 return (NULL);
753
754 a->compare = f;
755 a->data = d;
756 a->current = -1;
757 a->insert = -1;
758 a->num_saved = 0;
759 a->unique = 1;
760
761 if (hsize > 0 && h)
762 {
763 a->hashfunc = h;
764 a->hashsize = hsize;
765 a->hash = malloc((size_t)hsize * sizeof(int));
766
767 if (!a->hash)
768 {
769 free(a);
770 return (NULL);
771 }
772
773 memset(a->hash, -1, (size_t)hsize * sizeof(int));
774 }
775
776 a->copyfunc = cf;
777 a->freefunc = ff;
778
779 return (a);
780 }
781
782
783 /*
784 * '_cupsArrayNewStrings()' - Create a new array of comma-delimited strings.
785 *
786 * Note: The array automatically manages copies of the strings passed. If the
787 * string pointer "s" is NULL or the empty string, no strings are added to the
788 * newly created array.
789 */
790
791 cups_array_t * /* O - Array */
792 _cupsArrayNewStrings(const char *s, /* I - Delimited strings or NULL */
793 char delim) /* I - Delimiter character */
794 {
795 cups_array_t *a; /* Array */
796
797
798 if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0,
799 (cups_acopy_func_t)_cupsStrAlloc,
800 (cups_afree_func_t)_cupsStrFree)) != NULL)
801 _cupsArrayAddStrings(a, s, delim);
802
803 return (a);
804 }
805
806
807 /*
808 * 'cupsArrayNext()' - Get the next element in the array.
809 *
810 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)".
811 *
812 * The next element is undefined until you call @link cupsArrayFind@,
813 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
814 * to set the current element.
815 *
816 * @since CUPS 1.2/OS X 10.5@
817 */
818
819 void * /* O - Next element or @code NULL@ */
820 cupsArrayNext(cups_array_t *a) /* I - Array */
821 {
822 /*
823 * Range check input...
824 */
825
826 if (!a)
827 return (NULL);
828
829 /*
830 * Return the next element...
831 */
832
833 if (a->current < a->num_elements)
834 a->current ++;
835
836 return (cupsArrayCurrent(a));
837 }
838
839
840 /*
841 * 'cupsArrayPrev()' - Get the previous element in the array.
842 *
843 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)".
844 *
845 * The previous element is undefined until you call @link cupsArrayFind@,
846 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
847 * to set the current element.
848 *
849 * @since CUPS 1.2/OS X 10.5@
850 */
851
852 void * /* O - Previous element or @code NULL@ */
853 cupsArrayPrev(cups_array_t *a) /* I - Array */
854 {
855 /*
856 * Range check input...
857 */
858
859 if (!a)
860 return (NULL);
861
862 /*
863 * Return the previous element...
864 */
865
866 if (a->current >= 0)
867 a->current --;
868
869 return (cupsArrayCurrent(a));
870 }
871
872
873 /*
874 * 'cupsArrayRemove()' - Remove an element from the array.
875 *
876 * If more than one element matches "e", only the first matching element is
877 * removed.
878 *
879 * The caller is responsible for freeing the memory used by the
880 * removed element.
881 *
882 * @since CUPS 1.2/OS X 10.5@
883 */
884
885 int /* O - 1 on success, 0 on failure */
886 cupsArrayRemove(cups_array_t *a, /* I - Array */
887 void *e) /* I - Element */
888 {
889 ssize_t i, /* Looping var */
890 current; /* Current element */
891 int diff; /* Difference */
892
893
894 /*
895 * Range check input...
896 */
897
898 if (!a || !e)
899 return (0);
900
901 /*
902 * See if the element is in the array...
903 */
904
905 if (!a->num_elements)
906 return (0);
907
908 current = cups_array_find(a, e, a->current, &diff);
909 if (diff)
910 return (0);
911
912 /*
913 * Yes, now remove it...
914 */
915
916 a->num_elements --;
917
918 if (a->freefunc)
919 (a->freefunc)(a->elements[current], a->data);
920
921 if (current < a->num_elements)
922 memmove(a->elements + current, a->elements + current + 1,
923 (size_t)(a->num_elements - current) * sizeof(void *));
924
925 if (current <= a->current)
926 a->current --;
927
928 if (current < a->insert)
929 a->insert --;
930 else if (current == a->insert)
931 a->insert = -1;
932
933 for (i = 0; i < a->num_saved; i ++)
934 if (current <= a->saved[i])
935 a->saved[i] --;
936
937 if (a->num_elements <= 1)
938 a->unique = 1;
939
940 return (1);
941 }
942
943
944 /*
945 * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
946 *
947 * @since CUPS 1.2/OS X 10.5@
948 */
949
950 void * /* O - New current element */
951 cupsArrayRestore(cups_array_t *a) /* I - Array */
952 {
953 if (!a)
954 return (NULL);
955
956 if (a->num_saved <= 0)
957 return (NULL);
958
959 a->num_saved --;
960 a->current = a->saved[a->num_saved];
961
962 if (a->current >= 0 && a->current < a->num_elements)
963 return (a->elements[a->current]);
964 else
965 return (NULL);
966 }
967
968
969 /*
970 * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@.
971 *
972 * The current element is undefined until you call @link cupsArrayFind@,
973 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
974 * to set the current element.
975 *
976 * The save/restore stack is guaranteed to be at least 32 elements deep.
977 *
978 * @since CUPS 1.2/OS X 10.5@
979 */
980
981 int /* O - 1 on success, 0 on failure */
982 cupsArraySave(cups_array_t *a) /* I - Array */
983 {
984 if (!a)
985 return (0);
986
987 if (a->num_saved >= _CUPS_MAXSAVE)
988 return (0);
989
990 a->saved[a->num_saved] = a->current;
991 a->num_saved ++;
992
993 return (1);
994 }
995
996
997 /*
998 * 'cupsArrayUserData()' - Return the user data for an array.
999 *
1000 * @since CUPS 1.2/OS X 10.5@
1001 */
1002
1003 void * /* O - User data */
1004 cupsArrayUserData(cups_array_t *a) /* I - Array */
1005 {
1006 if (a)
1007 return (a->data);
1008 else
1009 return (NULL);
1010 }
1011
1012
1013 /*
1014 * 'cups_array_add()' - Insert or append an element to the array.
1015 *
1016 * @since CUPS 1.2/OS X 10.5@
1017 */
1018
1019 static int /* O - 1 on success, 0 on failure */
1020 cups_array_add(cups_array_t *a, /* I - Array */
1021 void *e, /* I - Element to add */
1022 int insert) /* I - 1 = insert, 0 = append */
1023 {
1024 int i, /* Looping var */
1025 current; /* Current element */
1026 int diff; /* Comparison with current element */
1027
1028
1029 DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", (void *)a, e, insert));
1030
1031 /*
1032 * Verify we have room for the new element...
1033 */
1034
1035 if (a->num_elements >= a->alloc_elements)
1036 {
1037 /*
1038 * Allocate additional elements; start with 16 elements, then
1039 * double the size until 1024 elements, then add 1024 elements
1040 * thereafter...
1041 */
1042
1043 void **temp; /* New array elements */
1044 int count; /* New allocation count */
1045
1046
1047 if (a->alloc_elements == 0)
1048 {
1049 count = 16;
1050 temp = malloc((size_t)count * sizeof(void *));
1051 }
1052 else
1053 {
1054 if (a->alloc_elements < 1024)
1055 count = a->alloc_elements * 2;
1056 else
1057 count = a->alloc_elements + 1024;
1058
1059 temp = realloc(a->elements, (size_t)count * sizeof(void *));
1060 }
1061
1062 DEBUG_printf(("9cups_array_add: count=" CUPS_LLFMT, CUPS_LLCAST count));
1063
1064 if (!temp)
1065 {
1066 DEBUG_puts("9cups_array_add: allocation failed, returning 0");
1067 return (0);
1068 }
1069
1070 a->alloc_elements = count;
1071 a->elements = temp;
1072 }
1073
1074 /*
1075 * Find the insertion point for the new element; if there is no
1076 * compare function or elements, just add it to the beginning or end...
1077 */
1078
1079 if (!a->num_elements || !a->compare)
1080 {
1081 /*
1082 * No elements or comparison function, insert/append as needed...
1083 */
1084
1085 if (insert)
1086 current = 0; /* Insert at beginning */
1087 else
1088 current = a->num_elements; /* Append to the end */
1089 }
1090 else
1091 {
1092 /*
1093 * Do a binary search for the insertion point...
1094 */
1095
1096 current = cups_array_find(a, e, a->insert, &diff);
1097
1098 if (diff > 0)
1099 {
1100 /*
1101 * Insert after the current element...
1102 */
1103
1104 current ++;
1105 }
1106 else if (!diff)
1107 {
1108 /*
1109 * Compared equal, make sure we add to the begining or end of
1110 * the current run of equal elements...
1111 */
1112
1113 a->unique = 0;
1114
1115 if (insert)
1116 {
1117 /*
1118 * Insert at beginning of run...
1119 */
1120
1121 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
1122 a->data))
1123 current --;
1124 }
1125 else
1126 {
1127 /*
1128 * Append at end of run...
1129 */
1130
1131 do
1132 {
1133 current ++;
1134 }
1135 while (current < a->num_elements &&
1136 !(*(a->compare))(e, a->elements[current], a->data));
1137 }
1138 }
1139 }
1140
1141 /*
1142 * Insert or append the element...
1143 */
1144
1145 if (current < a->num_elements)
1146 {
1147 /*
1148 * Shift other elements to the right...
1149 */
1150
1151 memmove(a->elements + current + 1, a->elements + current,
1152 (size_t)(a->num_elements - current) * sizeof(void *));
1153
1154 if (a->current >= current)
1155 a->current ++;
1156
1157 for (i = 0; i < a->num_saved; i ++)
1158 if (a->saved[i] >= current)
1159 a->saved[i] ++;
1160
1161 DEBUG_printf(("9cups_array_add: insert element at index " CUPS_LLFMT, CUPS_LLCAST current));
1162 }
1163 #ifdef DEBUG
1164 else
1165 DEBUG_printf(("9cups_array_add: append element at " CUPS_LLFMT, CUPS_LLCAST current));
1166 #endif /* DEBUG */
1167
1168 if (a->copyfunc)
1169 {
1170 if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL)
1171 {
1172 DEBUG_puts("8cups_array_add: Copy function returned NULL, returning 0");
1173 return (0);
1174 }
1175 }
1176 else
1177 a->elements[current] = e;
1178
1179 a->num_elements ++;
1180 a->insert = current;
1181
1182 #ifdef DEBUG
1183 for (current = 0; current < a->num_elements; current ++)
1184 DEBUG_printf(("9cups_array_add: a->elements[" CUPS_LLFMT "]=%p", CUPS_LLCAST current, a->elements[current]));
1185 #endif /* DEBUG */
1186
1187 DEBUG_puts("9cups_array_add: returning 1");
1188
1189 return (1);
1190 }
1191
1192
1193 /*
1194 * 'cups_array_find()' - Find an element in the array.
1195 */
1196
1197 static int /* O - Index of match */
1198 cups_array_find(cups_array_t *a, /* I - Array */
1199 void *e, /* I - Element */
1200 int prev, /* I - Previous index */
1201 int *rdiff) /* O - Difference of match */
1202 {
1203 int left, /* Left side of search */
1204 right, /* Right side of search */
1205 current, /* Current element */
1206 diff; /* Comparison with current element */
1207
1208
1209 DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", (void *)a, e, prev, (void *)rdiff));
1210
1211 if (a->compare)
1212 {
1213 /*
1214 * Do a binary search for the element...
1215 */
1216
1217 DEBUG_puts("9cups_array_find: binary search");
1218
1219 if (prev >= 0 && prev < a->num_elements)
1220 {
1221 /*
1222 * Start search on either side of previous...
1223 */
1224
1225 if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 ||
1226 (diff < 0 && prev == 0) ||
1227 (diff > 0 && prev == (a->num_elements - 1)))
1228 {
1229 /*
1230 * Exact or edge match, return it!
1231 */
1232
1233 DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff));
1234
1235 *rdiff = diff;
1236
1237 return (prev);
1238 }
1239 else if (diff < 0)
1240 {
1241 /*
1242 * Start with previous on right side...
1243 */
1244
1245 left = 0;
1246 right = prev;
1247 }
1248 else
1249 {
1250 /*
1251 * Start wih previous on left side...
1252 */
1253
1254 left = prev;
1255 right = a->num_elements - 1;
1256 }
1257 }
1258 else
1259 {
1260 /*
1261 * Start search in the middle...
1262 */
1263
1264 left = 0;
1265 right = a->num_elements - 1;
1266 }
1267
1268 do
1269 {
1270 current = (left + right) / 2;
1271 diff = (*(a->compare))(e, a->elements[current], a->data);
1272
1273 DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d",
1274 left, right, current, diff));
1275
1276 if (diff == 0)
1277 break;
1278 else if (diff < 0)
1279 right = current;
1280 else
1281 left = current;
1282 }
1283 while ((right - left) > 1);
1284
1285 if (diff != 0)
1286 {
1287 /*
1288 * Check the last 1 or 2 elements...
1289 */
1290
1291 if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0)
1292 current = left;
1293 else
1294 {
1295 diff = (*(a->compare))(e, a->elements[right], a->data);
1296 current = right;
1297 }
1298 }
1299 }
1300 else
1301 {
1302 /*
1303 * Do a linear pointer search...
1304 */
1305
1306 DEBUG_puts("9cups_array_find: linear search");
1307
1308 diff = 1;
1309
1310 for (current = 0; current < a->num_elements; current ++)
1311 if (a->elements[current] == e)
1312 {
1313 diff = 0;
1314 break;
1315 }
1316 }
1317
1318 /*
1319 * Return the closest element and the difference...
1320 */
1321
1322 DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff));
1323
1324 *rdiff = diff;
1325
1326 return (current);
1327 }
1328
1329
1330 /*
1331 * End of "$Id$".
1332 */