/src/libcups/cups/array.c
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Sorted array routines for CUPS. |
3 | | // |
4 | | // Copyright © 2021-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 | 0 | #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 | | |
35 | | size_t num_elements, // Number of array elements |
36 | | alloc_elements, // Allocated array elements |
37 | | current, // Current element |
38 | | insert, // Last inserted element |
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 | | bool unique; // Are all elements unique? |
45 | | void *data; // User data passed to compare |
46 | | cups_ahash_cb_t hashfunc; // Hash function |
47 | | size_t hashsize, // Size of hash |
48 | | *hash; // Hash array |
49 | | cups_acopy_cb_t copyfunc; // Copy function |
50 | | cups_afree_cb_t freefunc; // Free function |
51 | | }; |
52 | | |
53 | | |
54 | | // |
55 | | // Local functions... |
56 | | // |
57 | | |
58 | | static bool cups_array_add(cups_array_t *a, void *e, bool insert); |
59 | | static size_t cups_array_find(cups_array_t *a, void *e, size_t prev, int *rdiff); |
60 | | |
61 | | |
62 | | // |
63 | | // 'cupsArrayAdd()' - Add an element to an array. |
64 | | // |
65 | | // This function adds an element to an array. When adding an element to a |
66 | | // sorted array, non-unique elements are appended at the end of the run of |
67 | | // identical elements. For unsorted arrays, the element is appended to the end |
68 | | // of the array. |
69 | | // |
70 | | |
71 | | bool // O - `true` on success, `false` on failure |
72 | | cupsArrayAdd(cups_array_t *a, // I - Array |
73 | | void *e) // I - Element |
74 | 563k | { |
75 | | // Range check input... |
76 | 563k | if (!a || !e) |
77 | 0 | return (false); |
78 | | |
79 | | // Append the element... |
80 | 563k | return (cups_array_add(a, e, false)); |
81 | 563k | } |
82 | | |
83 | | |
84 | | // |
85 | | // 'cupsArrayAddStrings()' - Add zero or more delimited strings to an array. |
86 | | // |
87 | | // This function adds zero or more delimited strings to an array created using |
88 | | // the @link cupsArrayNewStrings@ function. Duplicate strings are *not* added. |
89 | | // If the string pointer "s" is `NULL` or the empty string, no strings are |
90 | | // added to the array. If "delim" is the space character, then all whitespace |
91 | | // is recognized as a delimiter. |
92 | | // |
93 | | // Strings can be delimited by quotes ("foo", 'bar') and curly braces ("{foo}"), |
94 | | // and characters can be escaped using the backslash (\) character. Outer |
95 | | // quotes are stripped but inner ones are preserved. |
96 | | // |
97 | | |
98 | | bool // O - `true` on success, `false` on failure |
99 | | cupsArrayAddStrings(cups_array_t *a, // I - Array |
100 | | const char *s, // I - Delimited strings |
101 | | char delim) // I - Delimiter character |
102 | 0 | { |
103 | 0 | char *buffer, // Copy of string |
104 | 0 | *start, // Start of string |
105 | 0 | *end; // End of string |
106 | 0 | bool status = true; // Status of add |
107 | 0 | char stack[_CUPS_MAXSAVE]; // Quoting stack |
108 | 0 | int spos = -1; // Stack position |
109 | | |
110 | | |
111 | | // Range check input... |
112 | 0 | if (!a) |
113 | 0 | return (false); |
114 | | |
115 | 0 | if (!a || !s || !*s || !delim) |
116 | 0 | return (true); |
117 | | |
118 | 0 | if (delim == ' ') |
119 | 0 | { |
120 | | // Skip leading whitespace... |
121 | 0 | while (*s && isspace(*s & 255)) |
122 | 0 | s ++; |
123 | 0 | } |
124 | |
|
125 | 0 | if (!strchr(s, delim) && (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n'))) && *s != '\'' && *s != '\"') |
126 | 0 | { |
127 | | // String doesn't contain a delimiter, so add it as a single value... |
128 | 0 | if (!cupsArrayFind(a, (void *)s)) |
129 | 0 | status = cupsArrayAdd(a, (void *)s); |
130 | 0 | } |
131 | 0 | else if ((buffer = strdup(s)) == NULL) |
132 | 0 | { |
133 | 0 | status = false; |
134 | 0 | } |
135 | 0 | else |
136 | 0 | { |
137 | 0 | for (start = end = buffer; *end; start = end) |
138 | 0 | { |
139 | | // Find the end of the current delimited string and see if we need to add it... |
140 | 0 | while (*end) |
141 | 0 | { |
142 | 0 | if (*end == '\\' && end[1]) |
143 | 0 | { |
144 | | // Skip escaped character... |
145 | 0 | _cups_strcpy(end, end + 1); |
146 | 0 | end ++; |
147 | 0 | } |
148 | 0 | else if (spos >= 0 && *end == stack[spos]) |
149 | 0 | { |
150 | | // End of quoted value... |
151 | 0 | spos --; |
152 | 0 | if (spos >= 0 || *end == '}') |
153 | 0 | end ++; |
154 | 0 | else |
155 | 0 | _cups_strcpy(end, end + 1); |
156 | 0 | } |
157 | 0 | else if (*end == '{' && spos < (int)(sizeof(stack) - 1)) |
158 | 0 | { |
159 | | // Value in curly braces... |
160 | 0 | spos ++; |
161 | 0 | stack[spos] = '}'; |
162 | 0 | } |
163 | 0 | else if ((*end == '\'' || *end == '\"') && spos < (int)(sizeof(stack) - 1)) |
164 | 0 | { |
165 | | // Value in quotes... |
166 | 0 | spos ++; |
167 | 0 | stack[spos] = *end; |
168 | 0 | if (spos == 0) |
169 | 0 | _cups_strcpy(end, end + 1); |
170 | 0 | } |
171 | 0 | else if (*end == delim || (delim == ' ' && isspace(*end & 255))) |
172 | 0 | { |
173 | | // Delimiter |
174 | 0 | *end++ = '\0'; |
175 | 0 | break; |
176 | 0 | } |
177 | 0 | else |
178 | 0 | end ++; |
179 | 0 | } |
180 | |
|
181 | 0 | if (!cupsArrayFind(a, start)) |
182 | 0 | status &= cupsArrayAdd(a, start); |
183 | 0 | } |
184 | |
|
185 | 0 | free(buffer); |
186 | 0 | } |
187 | |
|
188 | 0 | return (status); |
189 | 0 | } |
190 | | |
191 | | |
192 | | // |
193 | | // 'cupsArrayClear()' - Clear an array. |
194 | | // |
195 | | // This function is equivalent to removing all elements in the array, so the |
196 | | // free callback (if any) is called for each element that is removed. |
197 | | // |
198 | | |
199 | | void |
200 | | cupsArrayClear(cups_array_t *a) // I - Array |
201 | 0 | { |
202 | | // Range check input... |
203 | 0 | if (!a) |
204 | 0 | return; |
205 | | |
206 | | // Free the existing elements as needed.. |
207 | 0 | if (a->freefunc) |
208 | 0 | { |
209 | 0 | size_t i; // Looping var |
210 | 0 | void **e; // Current element |
211 | |
|
212 | 0 | for (i = a->num_elements, e = a->elements; i > 0; i --, e ++) |
213 | 0 | (a->freefunc)(*e, a->data); |
214 | 0 | } |
215 | | |
216 | | // Set the number of elements to 0; we don't actually free the memory |
217 | | // here - that is done in cupsArrayDelete()... |
218 | 0 | a->num_elements = 0; |
219 | 0 | a->current = SIZE_MAX; |
220 | 0 | a->insert = SIZE_MAX; |
221 | 0 | a->unique = true; |
222 | 0 | a->num_saved = 0; |
223 | 0 | } |
224 | | |
225 | | |
226 | | // |
227 | | // 'cupsArrayDelete()' - Free all memory used by an array. |
228 | | // |
229 | | // This function frees all memory used by an array. The free callback (if any) |
230 | | // is called for each element in the array. |
231 | | // |
232 | | |
233 | | void |
234 | | cupsArrayDelete(cups_array_t *a) // I - Array |
235 | 0 | { |
236 | | // Range check input... |
237 | 0 | if (!a) |
238 | 0 | return; |
239 | | |
240 | | // Clear the array... |
241 | 0 | cupsArrayClear(a); |
242 | | |
243 | | // Free the other buffers... |
244 | 0 | free(a->elements); |
245 | 0 | free(a->hash); |
246 | 0 | free(a); |
247 | 0 | } |
248 | | |
249 | | |
250 | | // |
251 | | // 'cupsArrayDup()' - Duplicate an array. |
252 | | // |
253 | | |
254 | | cups_array_t * // O - Duplicate array |
255 | | cupsArrayDup(cups_array_t *a) // I - Array |
256 | 0 | { |
257 | 0 | cups_array_t *da; // Duplicate array |
258 | | |
259 | | |
260 | | // Range check input... |
261 | 0 | if (!a) |
262 | 0 | return (NULL); |
263 | | |
264 | | // Allocate memory for the array... |
265 | 0 | da = calloc(1, sizeof(cups_array_t)); |
266 | 0 | if (!da) |
267 | 0 | return (NULL); |
268 | | |
269 | 0 | da->compare = a->compare; |
270 | 0 | da->copyfunc = a->copyfunc; |
271 | 0 | da->freefunc = a->freefunc; |
272 | 0 | da->data = a->data; |
273 | 0 | da->current = a->current; |
274 | 0 | da->insert = a->insert; |
275 | 0 | da->unique = a->unique; |
276 | 0 | da->num_saved = a->num_saved; |
277 | |
|
278 | 0 | memcpy(da->saved, a->saved, sizeof(a->saved)); |
279 | |
|
280 | 0 | if (a->num_elements) |
281 | 0 | { |
282 | | // Allocate memory for the elements... |
283 | 0 | da->elements = malloc((size_t)a->num_elements * sizeof(void *)); |
284 | 0 | if (!da->elements) |
285 | 0 | { |
286 | 0 | free(da); |
287 | 0 | return (NULL); |
288 | 0 | } |
289 | | |
290 | | // Copy the element pointers... |
291 | 0 | if (a->copyfunc) |
292 | 0 | { |
293 | | // Use the copy function to make a copy of each element... |
294 | 0 | size_t i; // Looping var |
295 | |
|
296 | 0 | for (i = 0; i < a->num_elements; i ++) |
297 | 0 | da->elements[i] = (a->copyfunc)(a->elements[i], a->data); |
298 | 0 | } |
299 | 0 | else |
300 | 0 | { |
301 | | // Just copy raw pointers... |
302 | 0 | memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *)); |
303 | 0 | } |
304 | |
|
305 | 0 | da->num_elements = a->num_elements; |
306 | 0 | da->alloc_elements = a->num_elements; |
307 | 0 | } |
308 | | |
309 | | // Return the new array... |
310 | 0 | return (da); |
311 | 0 | } |
312 | | |
313 | | |
314 | | // |
315 | | // 'cupsArrayFind()' - Find an element in an array. |
316 | | // |
317 | | |
318 | | void * // O - Element found or @code NULL@ |
319 | | cupsArrayFind(cups_array_t *a, // I - Array |
320 | | void *e) // I - Element |
321 | 37.2M | { |
322 | 37.2M | size_t current, // Current element |
323 | 37.2M | hash; // Hash index |
324 | 37.2M | int diff; // Difference |
325 | | |
326 | | |
327 | | // Range check input... |
328 | 37.2M | if (!a || !a->num_elements || !e) |
329 | 1 | return (NULL); |
330 | | |
331 | | // Look for a match... |
332 | 37.2M | if (a->hash) |
333 | 0 | { |
334 | 0 | if ((hash = (*(a->hashfunc))(e, a->data)) >= a->hashsize) |
335 | 0 | { |
336 | 0 | current = a->current; |
337 | 0 | hash = SIZE_MAX; |
338 | 0 | } |
339 | 0 | else if ((current = a->hash[hash]) >= a->num_elements) |
340 | 0 | current = a->current; |
341 | 0 | } |
342 | 37.2M | else |
343 | 37.2M | { |
344 | 37.2M | current = a->current; |
345 | 37.2M | hash = SIZE_MAX; |
346 | 37.2M | } |
347 | | |
348 | 37.2M | current = cups_array_find(a, e, current, &diff); |
349 | 37.2M | if (!diff) |
350 | 36.6M | { |
351 | | // Found a match! If the array does not contain unique values, find the |
352 | | // first element that is the same... |
353 | 36.6M | if (!a->unique && a->compare) |
354 | 0 | { |
355 | | // The array is not unique, find the first match... |
356 | 0 | while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], a->data)) |
357 | 0 | current --; |
358 | 0 | } |
359 | | |
360 | 36.6M | a->current = current; |
361 | | |
362 | 36.6M | if (hash < a->hashsize) |
363 | 0 | a->hash[hash] = current; |
364 | | |
365 | 36.6M | return (a->elements[current]); |
366 | 36.6M | } |
367 | 563k | else |
368 | 563k | { |
369 | | // No match... |
370 | 563k | a->current = SIZE_MAX; |
371 | | |
372 | 563k | return (NULL); |
373 | 563k | } |
374 | 37.2M | } |
375 | | |
376 | | |
377 | | // |
378 | | // 'cupsArrayGetCount()' - Get the number of elements in an array. |
379 | | // |
380 | | |
381 | | size_t // O - Number of elements |
382 | | cupsArrayGetCount(cups_array_t *a) // I - Array |
383 | 0 | { |
384 | 0 | return (a ? a->num_elements : 0); |
385 | 0 | } |
386 | | |
387 | | |
388 | | // |
389 | | // 'cupsArrayGetCurrent()' - Return the current element in an array. |
390 | | // |
391 | | // This function returns the current element in an array. The current element |
392 | | // is undefined until you call @link cupsArrayFind@, @link cupsArrayGetElement@, |
393 | | // @link cupsArrayGetFirst@, or @link cupsArrayGetLast@. |
394 | | // |
395 | | |
396 | | void * // O - Element |
397 | | cupsArrayGetCurrent(cups_array_t *a) // I - Array |
398 | 0 | { |
399 | | // Range check input... |
400 | 0 | if (!a) |
401 | 0 | return (NULL); |
402 | | |
403 | | // Return the current element... |
404 | 0 | if (a->current < a->num_elements) |
405 | 0 | return (a->elements[a->current]); |
406 | 0 | else |
407 | 0 | return (NULL); |
408 | 0 | } |
409 | | |
410 | | |
411 | | // |
412 | | // 'cupsArrayGetFirst()' - Get the first element in an array. |
413 | | // |
414 | | |
415 | | void * // O - First element or `NULL` if the array is empty |
416 | | cupsArrayGetFirst(cups_array_t *a) // I - Array |
417 | 0 | { |
418 | 0 | return (cupsArrayGetElement(a, 0)); |
419 | 0 | } |
420 | | |
421 | | |
422 | | // |
423 | | // 'cupsArrayGetIndex()' - Get the index of the current element. |
424 | | // |
425 | | // This function returns the index of the current element or `SIZE_MAX` if |
426 | | // there is no current element. The current element is undefined until you call |
427 | | // @link cupsArrayFind@, @link cupsArrayGetElement@, @link cupsArrayGetFirst@, |
428 | | // or @link cupsArrayGetLast@. |
429 | | // |
430 | | |
431 | | size_t // O - Index of the current element, starting at 0 |
432 | | cupsArrayGetIndex(cups_array_t *a) // I - Array |
433 | 0 | { |
434 | 0 | return (a ? a->current : SIZE_MAX); |
435 | 0 | } |
436 | | |
437 | | |
438 | | // |
439 | | // 'cupsArrayGetInsert()' - Get the index of the last added or inserted element. |
440 | | // |
441 | | // This function returns the index of the last added or inserted element or |
442 | | // `SIZE_MAX` if no elements have been added or inserted. |
443 | | // |
444 | | |
445 | | size_t // O - Index of the last added or inserted element, starting at 0 |
446 | | cupsArrayGetInsert(cups_array_t *a) // I - Array |
447 | 0 | { |
448 | 0 | return (a ? a->insert : SIZE_MAX); |
449 | 0 | } |
450 | | |
451 | | |
452 | | // |
453 | | // 'cupsArrayGetElement()' - Get the N-th element in the array. |
454 | | // |
455 | | |
456 | | void * // O - N-th element or `NULL` |
457 | | cupsArrayGetElement(cups_array_t *a, // I - Array |
458 | | size_t n) // I - Index into array, starting at 0 |
459 | 0 | { |
460 | | // Range check input... |
461 | 0 | if (!a || n >= a->num_elements) |
462 | 0 | return (NULL); |
463 | | |
464 | 0 | a->current = n; |
465 | |
|
466 | 0 | return (a->elements[n]); |
467 | 0 | } |
468 | | |
469 | | |
470 | | // |
471 | | // 'cupsArrayGetLast()' - Get the last element in the array. |
472 | | // |
473 | | |
474 | | void * // O - Last element or`NULL` if the array is empty |
475 | | cupsArrayGetLast(cups_array_t *a) // I - Array |
476 | 0 | { |
477 | | // Range check input... |
478 | 0 | if (!a || a->num_elements == 0) |
479 | 0 | return (NULL); |
480 | | |
481 | | // Return the last element... |
482 | 0 | return (cupsArrayGetElement(a, a->num_elements - 1)); |
483 | 0 | } |
484 | | |
485 | | |
486 | | // |
487 | | // 'cupsArrayGetNext()' - Get the next element in an array. |
488 | | // |
489 | | // This function returns the next element in an array. The next element is |
490 | | // undefined until you call @link cupsArrayFind@, @link cupsArrayGetElement@, |
491 | | // @link cupsArrayGetFirst@, or @link cupsArrayGetLast@ to set the current |
492 | | // element. |
493 | | // |
494 | | |
495 | | void * // O - Next element or @code NULL@ |
496 | | cupsArrayGetNext(cups_array_t *a) // I - Array |
497 | 0 | { |
498 | | // Range check input... |
499 | 0 | if (!a || a->num_elements == 0) |
500 | 0 | return (NULL); |
501 | 0 | else if (a->current == SIZE_MAX) |
502 | 0 | return (cupsArrayGetElement(a, 0)); |
503 | 0 | else |
504 | 0 | return (cupsArrayGetElement(a, a->current + 1)); |
505 | 0 | } |
506 | | |
507 | | |
508 | | // |
509 | | // 'cupsArrayGetPrev()' - Get the previous element in an array. |
510 | | // |
511 | | // This function returns the previous element in an array. The previous element |
512 | | // is undefined until you call @link cupsArrayFind@, @link cupsArrayGetElement@, |
513 | | // @link cupsArrayGetFirst@, or @link cupsArrayGetLast@ to set the current |
514 | | // element. |
515 | | // |
516 | | |
517 | | void * // O - Previous element or @code NULL@ |
518 | | cupsArrayGetPrev(cups_array_t *a) // I - Array |
519 | 0 | { |
520 | | // Range check input... |
521 | 0 | if (!a || a->num_elements == 0 || a->current == 0 || a->current == SIZE_MAX) |
522 | 0 | return (NULL); |
523 | 0 | else |
524 | 0 | return (cupsArrayGetElement(a, a->current - 1)); |
525 | 0 | } |
526 | | |
527 | | |
528 | | // |
529 | | // 'cupsArrayGetUserData()' - Return the user data for an array. |
530 | | // |
531 | | |
532 | | void * // O - User data |
533 | | cupsArrayGetUserData(cups_array_t *a) // I - Array |
534 | 0 | { |
535 | 0 | return (a ? a->data : NULL); |
536 | 0 | } |
537 | | |
538 | | |
539 | | // |
540 | | // 'cupsArrayInsert()' - Insert an element in an array. |
541 | | // |
542 | | // This function inserts an element in an array. When inserting an element |
543 | | // in a sorted array, non-unique elements are inserted at the beginning of the |
544 | | // run of identical elements. For unsorted arrays, the element is inserted at |
545 | | // the beginning of the array. |
546 | | // |
547 | | |
548 | | bool // O - `true` on success, `false` on failure |
549 | | cupsArrayInsert(cups_array_t *a, // I - Array |
550 | | void *e) // I - Element |
551 | 0 | { |
552 | | // Range check input... |
553 | 0 | if (!a || !e) |
554 | 0 | return (false); |
555 | | |
556 | | // Insert the element... |
557 | 0 | return (cups_array_add(a, e, true)); |
558 | 0 | } |
559 | | |
560 | | |
561 | | // |
562 | | // 'cupsArrayNew()' - Create a new array with callback functions. |
563 | | // |
564 | | // This function creates a new array with optional callback functions. The |
565 | | // comparison callback function ("f") is used to create a sorted array. The |
566 | | // function receives pointers to two elements and the user data pointer ("d"). |
567 | | // The user data pointer argument can safely be omitted when not required so |
568 | | // functions like `strcmp` can be used for sorted string arrays. |
569 | | // |
570 | | // ``` |
571 | | // int // Return -1 if a < b, 0 if a == b, and 1 if a > b |
572 | | // compare_cb(void *a, void *b, void *d) |
573 | | // { |
574 | | // ... "a" and "b" are the elements, "d" is the user data pointer |
575 | | // } |
576 | | // ``` |
577 | | // |
578 | | // The hash callback function ("hf") is used to implement cached lookups with |
579 | | // the specified hash size ("hsize"). The function receives a pointer to an |
580 | | // element and the user data pointer ("d") and returns an unsigned integer |
581 | | // representing a hash into the array. The hash value is of type `size_t` which |
582 | | // provides at least 32-bits of resolution. |
583 | | // |
584 | | // ``` |
585 | | // size_t // Return hash value from 0 to (hashsize - 1) |
586 | | // hash_cb(void *e, void *d) |
587 | | // { |
588 | | // ... "e" is the element, "d" is the user data pointer |
589 | | // } |
590 | | // ``` |
591 | | // |
592 | | // The copy callback function ("cf") is used to automatically copy/retain |
593 | | // elements when added to the array or the array is copied with |
594 | | // @link cupsArrayDup@. The function receives a pointer to the element and the |
595 | | // user data pointer ("d") and returns a new pointer that is stored in the array. |
596 | | // |
597 | | // ``` |
598 | | // void * // Return pointer to copied/retained element or NULL |
599 | | // copy_cb(void *e, void *d) |
600 | | // { |
601 | | // ... "e" is the element, "d" is the user data pointer |
602 | | // } |
603 | | // ``` |
604 | | // |
605 | | // Finally, the free callback function ("cf") is used to automatically |
606 | | // free/release elements when removed or the array is deleted. The function |
607 | | // receives a pointer to the element and the user data pointer ("d"). |
608 | | // |
609 | | // ``` |
610 | | // void |
611 | | // free_cb(void *e, void *d) |
612 | | // { |
613 | | // ... "e" is the element, "d" is the user data pointer |
614 | | // } |
615 | | // ``` |
616 | | // |
617 | | |
618 | | cups_array_t * // O - Array |
619 | | cupsArrayNew(cups_array_cb_t f, // I - Comparison callback function or `NULL` for an unsorted array |
620 | | void *d, // I - User data or `NULL` |
621 | | cups_ahash_cb_t hf, // I - Hash callback function or `NULL` for unhashed lookups |
622 | | size_t hsize, // I - Hash size (>= `0`) |
623 | | cups_acopy_cb_t cf, // I - Copy callback function or `NULL` for none |
624 | | cups_afree_cb_t ff) // I - Free callback function or `NULL` for none |
625 | 1 | { |
626 | 1 | cups_array_t *a; // Array |
627 | | |
628 | | |
629 | | // Allocate memory for the array... |
630 | 1 | if ((a = calloc(1, sizeof(cups_array_t))) == NULL) |
631 | 0 | return (NULL); |
632 | | |
633 | 1 | a->compare = f; |
634 | 1 | a->data = d; |
635 | 1 | a->current = SIZE_MAX; |
636 | 1 | a->insert = SIZE_MAX; |
637 | 1 | a->num_saved = 0; |
638 | 1 | a->unique = true; |
639 | | |
640 | 1 | if (hsize > 0 && hf) |
641 | 0 | { |
642 | 0 | a->hashfunc = hf; |
643 | 0 | a->hashsize = hsize; |
644 | 0 | a->hash = malloc((size_t)hsize * sizeof(size_t)); |
645 | |
|
646 | 0 | if (!a->hash) |
647 | 0 | { |
648 | 0 | free(a); |
649 | 0 | return (NULL); |
650 | 0 | } |
651 | | |
652 | 0 | memset(a->hash, -1, (size_t)hsize * sizeof(size_t)); |
653 | 0 | } |
654 | | |
655 | 1 | a->copyfunc = cf; |
656 | 1 | a->freefunc = ff; |
657 | | |
658 | 1 | return (a); |
659 | 1 | } |
660 | | |
661 | | |
662 | | // |
663 | | // 'cupsArrayNewStrings()' - Create a new array of delimited strings. |
664 | | // |
665 | | // This function creates an array that holds zero or more strings. The created |
666 | | // array automatically manages copies of the strings passed and sorts them in |
667 | | // ascending order using a case-sensitive comparison. If the string pointer "s" |
668 | | // is `NULL` or the empty string, no strings are added to the newly created |
669 | | // array. |
670 | | // |
671 | | // Additional strings can be added using the @link cupsArrayAdd@ or |
672 | | // @link cupsArrayAddStrings@ functions. |
673 | | // |
674 | | |
675 | | cups_array_t * // O - Array |
676 | | cupsArrayNewStrings(const char *s, // I - Delimited strings or `NULL` to create an empty array |
677 | | char delim) // I - Delimiter character |
678 | 0 | { |
679 | 0 | cups_array_t *a; // Array |
680 | | |
681 | |
|
682 | 0 | if ((a = cupsArrayNew((cups_array_cb_t)strcmp, NULL, NULL, 0, (cups_acopy_cb_t)_cupsStrAlloc, (cups_afree_cb_t)_cupsStrFree)) != NULL) |
683 | 0 | cupsArrayAddStrings(a, s, delim); |
684 | |
|
685 | 0 | return (a); |
686 | 0 | } |
687 | | |
688 | | |
689 | | // |
690 | | // 'cupsArrayRemove()' - Remove an element from an array. |
691 | | // |
692 | | // This function removes an element from an array. If more than one element |
693 | | // matches "e", only the first matching element is removed. |
694 | | // |
695 | | |
696 | | bool // O - `true` on success, `false` on failure |
697 | | cupsArrayRemove(cups_array_t *a, // I - Array |
698 | | void *e) // I - Element |
699 | 559k | { |
700 | 559k | size_t i, // Looping var |
701 | 559k | current; // Current element |
702 | 559k | int diff; // Difference |
703 | | |
704 | | |
705 | | // Range check input... |
706 | 559k | if (!a || a->num_elements == 0 || !e) |
707 | 0 | return (false); |
708 | | |
709 | | // See if the element is in the array... |
710 | 559k | current = cups_array_find(a, e, a->current, &diff); |
711 | 559k | if (diff) |
712 | 0 | return (false); |
713 | | |
714 | | // Yes, now remove it... |
715 | 559k | a->num_elements --; |
716 | | |
717 | 559k | if (a->freefunc) |
718 | 0 | (a->freefunc)(a->elements[current], a->data); |
719 | | |
720 | 559k | if (current < a->num_elements) |
721 | 557k | memmove(a->elements + current, a->elements + current + 1, (a->num_elements - current) * sizeof(void *)); |
722 | | |
723 | 559k | if (current <= a->current) |
724 | 559k | { |
725 | 559k | if (a->current) |
726 | 559k | a->current --; |
727 | 0 | else |
728 | 0 | a->current = SIZE_MAX; |
729 | 559k | } |
730 | | |
731 | 559k | if (current < a->insert) |
732 | 426k | a->insert --; |
733 | 132k | else if (current == a->insert) |
734 | 6.95k | a->insert = SIZE_MAX; |
735 | | |
736 | 559k | for (i = 0; i < a->num_saved; i ++) |
737 | 0 | { |
738 | 0 | if (current <= a->saved[i]) |
739 | 0 | a->saved[i] --; |
740 | 0 | } |
741 | | |
742 | 559k | if (a->num_elements <= 1) |
743 | 0 | a->unique = true; |
744 | | |
745 | 559k | return (true); |
746 | 559k | } |
747 | | |
748 | | |
749 | | // |
750 | | // 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@. |
751 | | // |
752 | | |
753 | | void * // O - New current element |
754 | | cupsArrayRestore(cups_array_t *a) // I - Array |
755 | 0 | { |
756 | | // Range check input... |
757 | 0 | if (!a || a->num_saved == 0) |
758 | 0 | return (NULL); |
759 | | |
760 | 0 | a->num_saved --; |
761 | 0 | a->current = a->saved[a->num_saved]; |
762 | |
|
763 | 0 | if (a->current < a->num_elements) |
764 | 0 | return (a->elements[a->current]); |
765 | 0 | else |
766 | 0 | return (NULL); |
767 | 0 | } |
768 | | |
769 | | |
770 | | // |
771 | | // 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@. |
772 | | // |
773 | | // The current element is undefined until you call @link cupsArrayFind@, |
774 | | // @link cupsArrayGetElement@, @link cupsArrayGetFirst@, or |
775 | | // @link cupsArrayGetLast@ to set the current element. |
776 | | // |
777 | | // The save/restore stack is guaranteed to be at least 32 elements deep. |
778 | | // |
779 | | |
780 | | bool // O - `true` on success, `false` on failure |
781 | | cupsArraySave(cups_array_t *a) // I - Array |
782 | 0 | { |
783 | | // Range check input... |
784 | 0 | if (!a || a->num_saved >= _CUPS_MAXSAVE) |
785 | 0 | return (false); |
786 | | |
787 | 0 | a->saved[a->num_saved] = a->current; |
788 | 0 | a->num_saved ++; |
789 | |
|
790 | 0 | return (true); |
791 | 0 | } |
792 | | |
793 | | |
794 | | // |
795 | | // 'cups_array_add()' - Insert or append an element to the array. |
796 | | // |
797 | | |
798 | | static bool // O - `true` on success, `false` on failure |
799 | | cups_array_add(cups_array_t *a, // I - Array |
800 | | void *e, // I - Element to add |
801 | | bool insert) // I - `true` = insert, `false` = append |
802 | 563k | { |
803 | 563k | size_t i, // Looping var |
804 | 563k | current; // Current element |
805 | 563k | int diff; // Comparison with current element |
806 | | |
807 | | |
808 | | // Verify we have room for the new element... |
809 | 563k | if (a->num_elements >= a->alloc_elements) |
810 | 42 | { |
811 | | // Allocate additional elements; start with 16 elements, then double the |
812 | | // size until 1024 elements, then add 1024 elements thereafter... |
813 | 42 | void **temp; // New array elements |
814 | 42 | size_t count; // New allocation count |
815 | | |
816 | 42 | if (a->alloc_elements == 0) |
817 | 1 | count = 16; |
818 | 41 | else if (a->alloc_elements < 1024) |
819 | 6 | count = a->alloc_elements * 2; |
820 | 35 | else |
821 | 35 | count = a->alloc_elements + 1024; |
822 | | |
823 | 42 | if ((temp = realloc(a->elements, count * sizeof(void *))) == NULL) |
824 | 0 | return (false); |
825 | | |
826 | 42 | a->alloc_elements = count; |
827 | 42 | a->elements = temp; |
828 | 42 | } |
829 | | |
830 | | // Find the insertion point for the new element; if there is no compare |
831 | | // function or elements, just add it to the beginning or end... |
832 | 563k | if (!a->num_elements || !a->compare) |
833 | 1 | { |
834 | | // No elements or comparison function, insert/append as needed... |
835 | 1 | if (insert) |
836 | 0 | current = 0; // Insert at beginning |
837 | 1 | else |
838 | 1 | current = a->num_elements; // Append to the end |
839 | 1 | } |
840 | 563k | else |
841 | 563k | { |
842 | | // Do a binary search for the insertion point... |
843 | 563k | current = cups_array_find(a, e, a->insert, &diff); |
844 | | |
845 | 563k | if (diff > 0) |
846 | 3.56k | { |
847 | | // Insert after the current element... |
848 | 3.56k | current ++; |
849 | 3.56k | } |
850 | 559k | else if (!diff) |
851 | 0 | { |
852 | | // Compared equal, make sure we add to the begining or end of the current |
853 | | // run of equal elements... |
854 | 0 | a->unique = false; |
855 | |
|
856 | 0 | if (insert) |
857 | 0 | { |
858 | | // Insert at beginning of run... |
859 | 0 | while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], a->data)) |
860 | 0 | current --; |
861 | 0 | } |
862 | 0 | else |
863 | 0 | { |
864 | | // Append at end of run... |
865 | 0 | do |
866 | 0 | { |
867 | 0 | current ++; |
868 | 0 | } |
869 | 0 | while (current < a->num_elements && !(*(a->compare))(e, a->elements[current], a->data)); |
870 | 0 | } |
871 | 0 | } |
872 | 563k | } |
873 | | |
874 | | // Insert or append the element... |
875 | 563k | if (current < a->num_elements) |
876 | 559k | { |
877 | | // Shift other elements to the right... |
878 | 559k | memmove(a->elements + current + 1, a->elements + current, (a->num_elements - current) * sizeof(void *)); |
879 | | |
880 | 559k | if (a->current >= current) |
881 | 559k | a->current ++; |
882 | | |
883 | 559k | for (i = 0; i < a->num_saved; i ++) |
884 | 0 | { |
885 | 0 | if (a->saved[i] >= current) |
886 | 0 | a->saved[i] ++; |
887 | 0 | } |
888 | 559k | } |
889 | | |
890 | 563k | if (a->copyfunc) |
891 | 0 | { |
892 | 0 | if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL) |
893 | 0 | return (false); |
894 | 0 | } |
895 | 563k | else |
896 | 563k | { |
897 | 563k | a->elements[current] = e; |
898 | 563k | } |
899 | | |
900 | 563k | a->num_elements ++; |
901 | 563k | a->insert = current; |
902 | | |
903 | 563k | return (true); |
904 | 563k | } |
905 | | |
906 | | |
907 | | // |
908 | | // 'cups_array_find()' - Find an element in the array. |
909 | | // |
910 | | |
911 | | static size_t // O - Index of match |
912 | | cups_array_find(cups_array_t *a, // I - Array |
913 | | void *e, // I - Element |
914 | | size_t prev, // I - Previous index |
915 | | int *rdiff) // O - Difference of match |
916 | 38.3M | { |
917 | 38.3M | size_t left, // Left side of search |
918 | 38.3M | right, // Right side of search |
919 | 38.3M | current; // Current element |
920 | 38.3M | int diff; // Comparison with current element |
921 | | |
922 | | |
923 | 38.3M | if (a->compare) |
924 | 38.3M | { |
925 | | // Do a binary search for the element... |
926 | 38.3M | if (prev < a->num_elements) |
927 | 38.3M | { |
928 | | // Start search on either side of previous... |
929 | 38.3M | if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 || (diff < 0 && prev == 0) || (diff > 0 && prev == (a->num_elements - 1))) |
930 | 36.1M | { |
931 | | // Exact or edge match, return it! |
932 | 36.1M | *rdiff = diff; |
933 | | |
934 | 36.1M | return (prev); |
935 | 36.1M | } |
936 | 2.14M | else if (diff < 0) |
937 | 782k | { |
938 | | // Start with previous on right side... |
939 | 782k | left = 0; |
940 | 782k | right = prev; |
941 | 782k | } |
942 | 1.36M | else |
943 | 1.36M | { |
944 | | // Start wih previous on left side... |
945 | 1.36M | left = prev; |
946 | 1.36M | right = a->num_elements - 1; |
947 | 1.36M | } |
948 | 38.3M | } |
949 | 10.5k | else |
950 | 10.5k | { |
951 | | // Start search in the middle... |
952 | 10.5k | left = 0; |
953 | 10.5k | right = a->num_elements - 1; |
954 | 10.5k | } |
955 | | |
956 | 2.15M | do |
957 | 24.9M | { |
958 | 24.9M | current = (left + right) / 2; |
959 | 24.9M | diff = (*(a->compare))(e, a->elements[current], a->data); |
960 | | |
961 | 24.9M | if (diff == 0) |
962 | 1.01M | break; |
963 | 23.9M | else if (diff < 0) |
964 | 12.3M | right = current; |
965 | 11.6M | else |
966 | 11.6M | left = current; |
967 | 24.9M | } |
968 | 23.9M | while ((right - left) > 1); |
969 | | |
970 | 2.15M | if (diff != 0) |
971 | 1.14M | { |
972 | | // Check the last 1 or 2 elements... |
973 | 1.14M | if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0) |
974 | 10.0k | { |
975 | 10.0k | current = left; |
976 | 10.0k | } |
977 | 1.13M | else |
978 | 1.13M | { |
979 | 1.13M | diff = (*(a->compare))(e, a->elements[right], a->data); |
980 | 1.13M | current = right; |
981 | 1.13M | } |
982 | 1.14M | } |
983 | 2.15M | } |
984 | 0 | else |
985 | 0 | { |
986 | | // Do a linear pointer search... |
987 | 0 | diff = 1; |
988 | |
|
989 | 0 | for (current = 0; current < a->num_elements; current ++) |
990 | 0 | { |
991 | 0 | if (a->elements[current] == e) |
992 | 0 | { |
993 | 0 | diff = 0; |
994 | 0 | break; |
995 | 0 | } |
996 | 0 | } |
997 | 0 | } |
998 | | |
999 | | // Return the closest element and the difference... |
1000 | 2.15M | *rdiff = diff; |
1001 | | |
1002 | 2.15M | return (current); |
1003 | 38.3M | } |