Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/third_party/heimdal/lib/base/array.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2010 Kungliga Tekniska Högskolan
3
 * (Royal Institute of Technology, Stockholm, Sweden).
4
 * All rights reserved.
5
 *
6
 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 *
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * 3. Neither the name of the Institute nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
#include "baselocl.h"
37
38
/*
39
 *
40
 */
41
42
struct heim_array_data {
43
    size_t len;
44
    heim_object_t *val;
45
    size_t allocated_len;
46
    heim_object_t *allocated;
47
};
48
49
static void HEIM_CALLCONV
50
array_dealloc(heim_object_t ptr)
51
0
{
52
0
    heim_array_t array = ptr;
53
0
    size_t n;
54
0
    for (n = 0; n < array->len; n++)
55
0
  heim_release(array->val[n]);
56
0
    free(array->allocated);
57
0
}
58
59
struct heim_type_data array_object = {
60
    HEIM_TID_ARRAY,
61
    "array-object",
62
    NULL,
63
    array_dealloc,
64
    NULL,
65
    NULL,
66
    NULL,
67
    NULL
68
};
69
70
/**
71
 * Allocate an array
72
 *
73
 * @return A new allocated array, free with heim_release()
74
 */
75
76
heim_array_t
77
heim_array_create(void)
78
0
{
79
0
    heim_array_t array;
80
81
0
    array = _heim_alloc_object(&array_object, sizeof(*array));
82
0
    if (array == NULL)
83
0
  return NULL;
84
85
0
    array->allocated = NULL;
86
0
    array->allocated_len = 0;
87
0
    array->val = NULL;
88
0
    array->len = 0;
89
90
0
    return array;
91
0
}
92
93
/**
94
 * Get type id of an dict
95
 *
96
 * @return the type id
97
 */
98
99
heim_tid_t
100
heim_array_get_type_id(void)
101
0
{
102
0
    return HEIM_TID_ARRAY;
103
0
}
104
105
/**
106
 * Append object to array
107
 *
108
 * @param array array to add too
109
 * @param object the object to add
110
 *
111
 * @return zero if added, errno otherwise
112
 */
113
114
int
115
heim_array_append_value(heim_array_t array, heim_object_t object)
116
0
{
117
0
    heim_object_t *ptr;
118
0
    size_t leading = array->val - array->allocated; /* unused leading slots */
119
0
    size_t trailing = array->allocated_len - array->len - leading;
120
0
    size_t new_len;
121
122
0
    if (trailing > 0) {
123
  /* We have pre-allocated space; use it */
124
0
  array->val[array->len++] = heim_retain(object);
125
0
  return 0;
126
0
    }
127
128
0
    if (leading > (array->len + 1)) {
129
  /*
130
   * We must have appending to, and deleting at index 0 from this
131
   * array a lot; don't want to grow forever!
132
   */
133
0
  (void) memmove(&array->allocated[0], &array->val[0],
134
0
           array->len * sizeof(array->val[0]));
135
0
  array->val = array->allocated;
136
137
  /* We have pre-allocated space; use it */
138
0
  array->val[array->len++] = heim_retain(object);
139
0
  return 0;
140
0
    }
141
142
    /* Pre-allocate extra .5 times number of used slots */
143
0
    new_len = leading + array->len + 1 + (array->len >> 1);
144
0
    ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
145
0
    if (ptr == NULL)
146
0
  return ENOMEM;
147
0
    array->allocated = ptr;
148
0
    array->allocated_len = new_len;
149
0
    array->val = &ptr[leading];
150
0
    array->val[array->len++] = heim_retain(object);
151
152
0
    return 0;
153
0
}
154
155
/*
156
 * Internal function to insert at index 0, taking care to optimize the
157
 * case where we're always inserting at index 0, particularly the case
158
 * where we insert at index 0 and delete from the right end.
159
 */
160
static int
161
heim_array_prepend_value(heim_array_t array, heim_object_t object)
162
0
{
163
0
    heim_object_t *ptr;
164
0
    size_t leading = array->val - array->allocated; /* unused leading slots */
165
0
    size_t trailing = array->allocated_len - array->len - leading;
166
0
    size_t new_len;
167
168
0
    if (leading > 0) {
169
  /* We have pre-allocated space; use it */
170
0
  array->val--;
171
0
  array->val[0] = heim_retain(object);
172
0
  array->len++;
173
0
  return 0;
174
0
    }
175
0
    if (trailing > (array->len + 1)) {
176
  /*
177
   * We must have prepending to, and deleting at index
178
   * array->len - 1 from this array a lot; don't want to grow
179
   * forever!
180
   */
181
0
  (void) memmove(&array->allocated[array->len], &array->val[0],
182
0
           array->len * sizeof(array->val[0]));
183
0
  array->val = &array->allocated[array->len];
184
185
  /* We have pre-allocated space; use it */
186
0
  array->val--;
187
0
  array->val[0] = heim_retain(object);
188
0
  array->len++;
189
0
  return 0;
190
0
    }
191
    /* Pre-allocate extra .5 times number of used slots */
192
0
    new_len = array->len + 1 + trailing + (array->len >> 1);
193
0
    ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
194
0
    if (ptr == NULL)
195
0
  return ENOMEM;
196
0
    (void) memmove(&ptr[1], &ptr[0], array->len * sizeof (array->val[0]));
197
0
    array->allocated = ptr;
198
0
    array->allocated_len = new_len;
199
0
    array->val = &ptr[0];
200
0
    array->val[0] = heim_retain(object);
201
0
    array->len++;
202
203
0
    return 0;
204
0
}
205
206
/**
207
 * Insert an object at a given index in an array
208
 *
209
 * @param array array to add too
210
 * @param idx index where to add element (-1 == append, -2 next to last, ...)
211
 * @param object the object to add
212
 *
213
 * @return zero if added, errno otherwise
214
 */
215
216
int
217
heim_array_insert_value(heim_array_t array, size_t idx, heim_object_t object)
218
0
{
219
0
    int ret;
220
221
0
    if (idx == 0)
222
0
  return heim_array_prepend_value(array, object);
223
0
    else if (idx > array->len)
224
0
  heim_abort("index too large");
225
226
    /*
227
     * We cheat: append this element then rotate elements around so we
228
     * have this new element at the desired location, unless we're truly
229
     * appending the new element.  This means reusing array growth in
230
     * heim_array_append_value() instead of duplicating that here.
231
     */
232
0
    ret = heim_array_append_value(array, object);
233
0
    if (ret != 0 || idx == (array->len - 1))
234
0
  return ret;
235
    /*
236
     * Shift to the right by one all the elements after idx, then set
237
     * [idx] to the new object.
238
     */
239
0
    (void) memmove(&array->val[idx + 1], &array->val[idx],
240
0
             (array->len - idx - 1) * sizeof(array->val[0]));
241
0
    array->val[idx] = heim_retain(object);
242
243
0
    return 0;
244
0
}
245
246
/**
247
 * Iterate over all objects in array
248
 *
249
 * @param array array to iterate over
250
 * @param ctx context passed to fn
251
 * @param fn function to call on each object
252
 */
253
254
void
255
heim_array_iterate_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
256
0
{
257
0
    size_t n;
258
0
    int stop = 0;
259
0
    for (n = 0; n < array->len; n++) {
260
0
  fn(array->val[n], ctx, &stop);
261
0
  if (stop)
262
0
      return;
263
0
    }
264
0
}
265
266
#ifdef __BLOCKS__
267
/**
268
 * Iterate over all objects in array
269
 *
270
 * @param array array to iterate over
271
 * @param fn block to call on each object
272
 */
273
274
void
275
heim_array_iterate(heim_array_t array, void (^fn)(heim_object_t, int *))
276
{
277
    size_t n;
278
    int stop = 0;
279
    for (n = 0; n < array->len; n++) {
280
  fn(array->val[n], &stop);
281
  if (stop)
282
      return;
283
    }
284
}
285
#endif
286
287
/**
288
 * Iterate over all objects in array, backwards
289
 *
290
 * @param array array to iterate over
291
 * @param ctx context passed to fn
292
 * @param fn function to call on each object
293
 */
294
295
void
296
heim_array_iterate_reverse_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
297
0
{
298
0
    size_t n;
299
0
    int stop = 0;
300
301
0
    for (n = array->len; n > 0; n--) {
302
0
  fn(array->val[n - 1], ctx, &stop);
303
0
  if (stop)
304
0
      return;
305
0
    }
306
0
}
307
308
#ifdef __BLOCKS__
309
/**
310
 * Iterate over all objects in array, backwards
311
 *
312
 * @param array array to iterate over
313
 * @param fn block to call on each object
314
 */
315
316
void
317
heim_array_iterate_reverse(heim_array_t array, void (^fn)(heim_object_t, int *))
318
{
319
    size_t n;
320
    int stop = 0;
321
    for (n = array->len; n > 0; n--) {
322
  fn(array->val[n - 1], &stop);
323
  if (stop)
324
      return;
325
    }
326
}
327
#endif
328
329
/**
330
 * Get length of array
331
 *
332
 * @param array array to get length of
333
 *
334
 * @return length of array
335
 */
336
337
size_t
338
heim_array_get_length(heim_array_t array)
339
0
{
340
0
    return array->len;
341
0
}
342
343
/**
344
 * Get value of element at array index
345
 *
346
 * @param array array copy object from
347
 * @param idx index of object, 0 based, must be smaller then
348
 *        heim_array_get_length()
349
 *
350
 * @return a not-retained copy of the object
351
 */
352
353
heim_object_t
354
heim_array_get_value(heim_array_t array, size_t idx)
355
0
{
356
0
    if (idx >= array->len)
357
0
  heim_abort("index too large");
358
0
    return array->val[idx];
359
0
}
360
361
/**
362
 * Get value of element at array index
363
 *
364
 * @param array array copy object from
365
 * @param idx index of object, 0 based, must be smaller then
366
 *        heim_array_get_length()
367
 *
368
 * @return a retained copy of the object
369
 */
370
371
heim_object_t
372
heim_array_copy_value(heim_array_t array, size_t idx)
373
0
{
374
0
    if (idx >= array->len)
375
0
  heim_abort("index too large");
376
0
    return heim_retain(array->val[idx]);
377
0
}
378
379
/**
380
 * Set value at array index
381
 *
382
 * @param array array copy object from
383
 * @param idx index of object, 0 based, must be smaller then
384
 *        heim_array_get_length()
385
 * @param value value to set 
386
 *
387
 */
388
389
void
390
heim_array_set_value(heim_array_t array, size_t idx, heim_object_t value)
391
0
{
392
0
    if (idx >= array->len)
393
0
  heim_abort("index too large");
394
0
    heim_release(array->val[idx]);
395
0
    array->val[idx] = heim_retain(value);
396
0
}
397
398
/**
399
 * Delete value at idx
400
 *
401
 * @param array the array to modify
402
 * @param idx the key to delete
403
 */
404
405
void
406
heim_array_delete_value(heim_array_t array, size_t idx)
407
0
{
408
0
    heim_object_t obj;
409
0
    if (idx >= array->len)
410
0
  heim_abort("index too large");
411
0
    obj = array->val[idx];
412
413
0
    array->len--;
414
415
    /*
416
     * Deleting the first or last elements is cheap, as we leave
417
     * allocated space for opportunistic reuse later; no realloc(), no
418
     * memmove().  All others require a memmove().
419
     *
420
     * If we ever need to optimize deletion of non-last/ non-first
421
     * element we can use a tagged object type to signify "deleted
422
     * value" so we can leave holes in the array, avoid memmove()s on
423
     * delete, and opportunistically re-use those holes on insert.
424
     */
425
0
    if (idx == 0)
426
0
  array->val++;
427
0
    else if (idx < array->len)
428
0
  (void) memmove(&array->val[idx], &array->val[idx + 1],
429
0
           (array->len - idx) * sizeof(array->val[0]));
430
431
0
    heim_release(obj);
432
0
}
433
434
/**
435
 * Filter out entres of array when function return true
436
 *
437
 * @param array the array to modify
438
 * @param fn filter function
439
 */
440
441
void
442
heim_array_filter_f(heim_array_t array, void *ctx, heim_array_filter_f_t fn)
443
0
{
444
0
    size_t n = 0;
445
446
0
    while (n < array->len) {
447
0
  if (fn(array->val[n], ctx)) {
448
0
      heim_array_delete_value(array, n);
449
0
  } else {
450
0
      n++;
451
0
  }
452
0
    }
453
0
}
454
455
#ifdef __BLOCKS__
456
457
/**
458
 * Filter out entres of array when block return true
459
 *
460
 * @param array the array to modify
461
 * @param block filter block
462
 */
463
464
void
465
heim_array_filter(heim_array_t array, int (^block)(heim_object_t))
466
{
467
    size_t n = 0;
468
469
    while (n < array->len) {
470
  if (block(array->val[n])) {
471
      heim_array_delete_value(array, n);
472
  } else {
473
      n++;
474
  }
475
    }
476
}
477
478
#endif /* __BLOCKS__ */