Coverage Report

Created: 2025-10-13 06:27

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