Coverage Report

Created: 2026-04-12 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/collections/array.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2014 Tobias Brunner
3
 * Copyright (C) 2013 Martin Willi
4
 *
5
 * Copyright (C) secunet Security Networks AG
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by the
9
 * Free Software Foundation; either version 2 of the License, or (at your
10
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15
 * for more details.
16
 */
17
18
#define _GNU_SOURCE /* for qsort_r() */
19
#include <stdlib.h>
20
21
#include "array.h"
22
23
#ifndef HAVE_QSORT_R
24
#include <threading/thread_value.h>
25
#endif
26
27
/**
28
 * Data is an allocated block, with potentially unused head and tail:
29
 *
30
 *   "esize" each (or sizeof(void*) if esize = 0)
31
 *  /-\ /-\ /-\ /-\ /-\ /-\
32
 *
33
 * +---------------+-------------------------------+---------------+
34
 * | h | e | a | d | e | l | e | m | e | n | t | s | t | a | i | l |
35
 * +---------------+-------------------------------+---------------+
36
 *
37
 * \--------------/ \-----------------------------/ \-------------/
38
 *      unused                    used                   unused
39
 *      "head"                   "count"                 "tail"
40
 *
41
 */
42
struct array_t {
43
  /** number of elements currently in array (not counting head/tail) */
44
  uint32_t count;
45
  /** size of each element, 0 for a pointer based array */
46
  uint16_t esize;
47
  /** allocated but unused elements at array front */
48
  uint8_t head;
49
  /** allocated but unused elements at array end */
50
  uint8_t tail;
51
  /** array elements */
52
  void *data;
53
};
54
55
#ifndef HAVE_QSORT_R
56
  /* store data to replicate qsort_r in thread local storage */
57
  static thread_value_t *sort_data;
58
#endif
59
60
/** maximum number of unused head/tail elements before cleanup */
61
0
#define ARRAY_MAX_UNUSED 32
62
63
/**
64
 * Get the actual size of a number of elements
65
 */
66
static size_t get_size(array_t *array, uint32_t num)
67
513
{
68
513
  if (array->esize)
69
0
  {
70
0
    return (size_t)array->esize * num;
71
0
  }
72
513
  return sizeof(void*) * num;
73
513
}
74
75
/**
76
 * Increase allocated but unused tail room to at least "room"
77
 */
78
static void make_tail_room(array_t *array, uint8_t room)
79
0
{
80
0
  if (array->tail < room)
81
0
  {
82
0
    array->data = realloc(array->data,
83
0
            get_size(array, array->count + array->head + room));
84
0
    array->tail = room;
85
0
  }
86
0
}
87
88
/**
89
 * Increase allocated but unused head room to at least "room"
90
 */
91
static void make_head_room(array_t *array, uint8_t room)
92
29
{
93
29
  if (array->head < room)
94
29
  {
95
29
    uint8_t increase = room - array->head;
96
97
29
    array->data = realloc(array->data,
98
29
            get_size(array, array->count + array->tail + room));
99
29
    memmove(array->data + get_size(array, increase), array->data,
100
29
        get_size(array, array->count + array->tail + array->head));
101
29
    array->head = room;
102
29
  }
103
29
}
104
105
/**
106
 * Make space for an item at index using tail room
107
 */
108
static void insert_tail(array_t *array, int idx)
109
0
{
110
0
  make_tail_room(array, 1);
111
  /* move up all elements after idx by one */
112
0
  memmove(array->data + get_size(array, array->head + idx + 1),
113
0
      array->data + get_size(array, array->head + idx),
114
0
      get_size(array, array->count - idx));
115
116
0
  array->tail--;
117
0
  array->count++;
118
0
}
119
120
/**
121
 * Make space for an item at index using head room
122
 */
123
static void insert_head(array_t *array, int idx)
124
29
{
125
29
  make_head_room(array, 1);
126
  /* move down all elements before idx by one */
127
29
  memmove(array->data + get_size(array, array->head - 1),
128
29
      array->data + get_size(array, array->head),
129
29
      get_size(array, idx));
130
131
29
  array->head--;
132
29
  array->count++;
133
29
}
134
135
/**
136
 * Remove an item, increase tail
137
 */
138
static void remove_tail(array_t *array, int idx)
139
0
{
140
  /* move all items after idx one down */
141
0
  memmove(array->data + get_size(array, idx + array->head),
142
0
      array->data + get_size(array, idx + array->head + 1),
143
0
      get_size(array, array->count - 1 - idx));
144
0
  array->count--;
145
0
  array->tail++;
146
0
}
147
148
/**
149
 * Remove an item, increase head
150
 */
151
static void remove_head(array_t *array, int idx)
152
0
{
153
  /* move all items before idx one up */
154
0
  memmove(array->data + get_size(array, array->head + 1),
155
0
      array->data + get_size(array, array->head), get_size(array, idx));
156
0
  array->count--;
157
0
  array->head++;
158
0
}
159
160
array_t *array_create(u_int esize, uint8_t reserve)
161
31
{
162
31
  array_t *array;
163
164
31
  INIT(array,
165
31
    .esize = esize,
166
31
    .tail = reserve,
167
31
  );
168
31
  if (array->tail)
169
0
  {
170
0
    array->data = malloc(get_size(array, array->tail));
171
0
  }
172
31
  return array;
173
31
}
174
175
int array_count(array_t *array)
176
216
{
177
216
  if (array)
178
177
  {
179
177
    return array->count;
180
177
  }
181
39
  return 0;
182
216
}
183
184
void array_compress(array_t *array)
185
0
{
186
0
  if (array)
187
0
  {
188
0
    uint32_t tail;
189
190
0
    tail = array->tail;
191
0
    if (array->head)
192
0
    {
193
0
      memmove(array->data, array->data + get_size(array, array->head),
194
0
          get_size(array, array->count + array->tail));
195
0
      tail += array->head;
196
0
      array->head = 0;
197
0
    }
198
0
    if (tail)
199
0
    {
200
0
      size_t size = get_size(array, array->count);
201
202
0
      if (size)
203
0
      {
204
0
        array->data = realloc(array->data, size);
205
0
      }
206
0
      else
207
0
      {
208
0
        free(array->data);
209
0
        array->data = NULL;
210
0
      }
211
0
      array->tail = 0;
212
0
    }
213
0
  }
214
0
}
215
216
typedef struct {
217
  /** public enumerator interface */
218
  enumerator_t public;
219
  /** enumerated array */
220
  array_t *array;
221
  /** current index +1, initialized at 0 */
222
  int idx;
223
} array_enumerator_t;
224
225
METHOD(enumerator_t, enumerate, bool,
226
  array_enumerator_t *this, va_list args)
227
0
{
228
0
  void *pos, **out;
229
230
0
  VA_ARGS_VGET(args, out);
231
232
0
  if (this->idx >= this->array->count)
233
0
  {
234
0
    return FALSE;
235
0
  }
236
237
0
  pos = this->array->data +
238
0
      get_size(this->array, this->idx + this->array->head);
239
0
  if (this->array->esize)
240
0
  {
241
    /* for element based arrays we return a pointer to the element */
242
0
    *out = pos;
243
0
  }
244
0
  else
245
0
  {
246
    /* for pointer based arrays we return the pointer directly */
247
0
    *out = *(void**)pos;
248
0
  }
249
0
  this->idx++;
250
0
  return TRUE;
251
0
}
252
253
enumerator_t* array_create_enumerator(array_t *array)
254
4
{
255
4
  array_enumerator_t *enumerator;
256
257
4
  if (!array)
258
4
  {
259
4
    return enumerator_create_empty();
260
4
  }
261
262
4
  INIT(enumerator,
263
0
    .public = {
264
0
      .enumerate = enumerator_enumerate_default,
265
0
      .venumerate = _enumerate,
266
0
      .destroy = (void*)free,
267
0
    },
268
0
    .array = array,
269
0
  );
270
0
  return &enumerator->public;
271
4
}
272
273
void array_remove_at(array_t *array, enumerator_t *public)
274
0
{
275
0
  array_enumerator_t *enumerator = (array_enumerator_t*)public;
276
277
0
  if (enumerator->idx)
278
0
  {
279
0
    array_remove(array, --enumerator->idx, NULL);
280
0
  }
281
0
}
282
283
void array_insert_create(array_t **array, int idx, void *ptr)
284
27
{
285
27
  if (*array == NULL)
286
27
  {
287
27
    *array = array_create(0, 0);
288
27
  }
289
27
  array_insert(*array, idx, ptr);
290
27
}
291
292
void array_insert_create_value(array_t **array, u_int esize,
293
                 int idx, void *val)
294
0
{
295
0
  if (*array == NULL)
296
0
  {
297
0
    *array = array_create(esize, 0);
298
0
  }
299
0
  array_insert(*array, idx, val);
300
0
}
301
302
void array_insert_enumerator(array_t *array, int idx, enumerator_t *enumerator)
303
0
{
304
0
  void *ptr;
305
306
0
  while (enumerator->enumerate(enumerator, &ptr))
307
0
  {
308
0
    array_insert(array, idx, ptr);
309
0
  }
310
0
  enumerator->destroy(enumerator);
311
0
}
312
313
void array_insert(array_t *array, int idx, void *data)
314
29
{
315
29
  if (idx < 0 || idx <= array_count(array))
316
29
  {
317
29
    void *pos;
318
319
29
    if (idx < 0)
320
27
    {
321
27
      idx = array_count(array);
322
27
    }
323
324
29
    if (array->head && !array->tail)
325
0
    {
326
0
      insert_head(array, idx);
327
0
    }
328
29
    else if (array->tail && !array->head)
329
0
    {
330
0
      insert_tail(array, idx);
331
0
    }
332
29
    else if (idx > array_count(array) / 2)
333
0
    {
334
0
      insert_tail(array, idx);
335
0
    }
336
29
    else
337
29
    {
338
29
      insert_head(array, idx);
339
29
    }
340
341
29
    pos = array->data + get_size(array, array->head + idx);
342
29
    if (array->esize)
343
0
    {
344
0
      memcpy(pos, data, get_size(array, 1));
345
0
    }
346
29
    else
347
29
    {
348
      /* pointer based array, copy pointer value */
349
29
      *(void**)pos = data;
350
29
    }
351
29
  }
352
29
}
353
354
bool array_get(array_t *array, int idx, void *data)
355
49
{
356
49
  if (!array)
357
8
  {
358
8
    return FALSE;
359
8
  }
360
41
  if (idx >= 0 && idx >= array_count(array))
361
0
  {
362
0
    return FALSE;
363
0
  }
364
41
  if (idx < 0)
365
4
  {
366
4
    if (array_count(array) == 0)
367
0
    {
368
0
      return FALSE;
369
0
    }
370
4
    idx = array_count(array) - 1;
371
4
  }
372
41
  if (data)
373
41
  {
374
41
    memcpy(data, array->data + get_size(array, array->head + idx),
375
41
         get_size(array, 1));
376
41
  }
377
41
  return TRUE;
378
41
}
379
380
bool array_remove(array_t *array, int idx, void *data)
381
8
{
382
8
  if (!array_get(array, idx, data))
383
8
  {
384
8
    return FALSE;
385
8
  }
386
0
  if (idx < 0)
387
0
  {
388
0
    idx = array_count(array) - 1;
389
0
  }
390
0
  if (idx > array_count(array) / 2)
391
0
  {
392
0
    remove_tail(array, idx);
393
0
  }
394
0
  else
395
0
  {
396
0
    remove_head(array, idx);
397
0
  }
398
0
  if (array->head + array->tail > ARRAY_MAX_UNUSED)
399
0
  {
400
0
    array_compress(array);
401
0
  }
402
0
  return TRUE;
403
8
}
404
405
typedef struct {
406
  /** the array */
407
  array_t *array;
408
  /** comparison function */
409
  int (*cmp)(const void*,const void*,void*);
410
  /** optional user arg */
411
  void *arg;
412
} sort_data_t;
413
414
#ifdef HAVE_QSORT_R_GNU
415
static int compare_elements(const void *a, const void *b, void *arg)
416
#elif defined(HAVE_QSORT_R_BSD)
417
static int compare_elements(void *arg, const void *a, const void *b)
418
#else /* !HAVE_QSORT_R */
419
static int compare_elements(const void *a, const void *b)
420
#endif
421
0
{
422
0
#ifdef HAVE_QSORT_R
423
0
  sort_data_t *data = (sort_data_t*)arg;
424
#else
425
  sort_data_t *data = sort_data->get(sort_data);
426
#endif
427
428
0
  if (data->array->esize)
429
0
  {
430
0
    return data->cmp(a, b, data->arg);
431
0
  }
432
0
  return data->cmp(*(void**)a, *(void**)b, data->arg);
433
0
}
434
435
void array_sort(array_t *array, int (*cmp)(const void*,const void*,void*),
436
        void *user)
437
2
{
438
2
  if (array)
439
2
  {
440
2
    sort_data_t data = {
441
2
      .array = array,
442
2
      .cmp = cmp,
443
2
      .arg = user,
444
2
    };
445
2
    void *start;
446
447
2
    start = array->data + get_size(array, array->head);
448
449
2
#ifdef HAVE_QSORT_R_GNU
450
2
    qsort_r(start, array->count, get_size(array, 1), compare_elements,
451
2
        &data);
452
#elif defined(HAVE_QSORT_R_BSD)
453
    qsort_r(start, array->count, get_size(array, 1), &data,
454
        compare_elements);
455
#else /* !HAVE_QSORT_R */
456
    sort_data_t *recursive;
457
458
    recursive = sort_data->get(sort_data);
459
    sort_data->set(sort_data, &data);
460
    qsort(start, array->count, get_size(array, 1), compare_elements);
461
    sort_data->set(sort_data, recursive);
462
#endif
463
2
  }
464
2
}
465
466
typedef struct {
467
  /** the array */
468
  array_t *array;
469
  /** the key */
470
  const void *key;
471
  /** comparison function */
472
  int (*cmp)(const void*,const void*);
473
} bsearch_data_t;
474
475
static int search_elements(const void *a, const void *b)
476
74
{
477
74
  bsearch_data_t *data = (bsearch_data_t*)a;
478
479
74
  if (data->array->esize)
480
0
  {
481
0
    return data->cmp(data->key, b);
482
0
  }
483
74
  return data->cmp(data->key, *(void**)b);
484
74
}
485
486
int array_bsearch(array_t *array, const void *key,
487
          int (*cmp)(const void*,const void*), void *out)
488
115
{
489
115
  int idx = -1;
490
491
115
  if (array)
492
74
  {
493
74
    bsearch_data_t data = {
494
74
      .array = array,
495
74
      .key = key,
496
74
      .cmp = cmp,
497
74
    };
498
74
    void *start, *item;
499
500
74
    start = array->data + get_size(array, array->head);
501
502
74
    item = bsearch(&data, start, array->count, get_size(array, 1),
503
74
             search_elements);
504
74
    if (item)
505
37
    {
506
37
      if (out)
507
37
      {
508
37
        memcpy(out, item, get_size(array, 1));
509
37
      }
510
37
      idx = (item - start) / get_size(array, 1);
511
37
    }
512
74
  }
513
115
  return idx;
514
115
}
515
516
void array_invoke(array_t *array, array_callback_t cb, void *user)
517
14
{
518
14
  if (array)
519
2
  {
520
2
    void *obj;
521
2
    int i;
522
523
4
    for (i = array->head; i < array->count + array->head; i++)
524
2
    {
525
2
      obj = array->data + get_size(array, i);
526
2
      if (!array->esize)
527
2
      {
528
        /* dereference if we store store pointers */
529
2
        obj = *(void**)obj;
530
2
      }
531
2
      cb(obj, i - array->head, user);
532
2
    }
533
2
  }
534
14
}
535
536
void array_invoke_offset(array_t *array, size_t offset)
537
2.86k
{
538
2.86k
  if (array)
539
0
  {
540
0
    void (*method)(void *data);
541
0
    void *obj;
542
0
    int i;
543
544
0
    for (i = array->head; i < array->count + array->head; i++)
545
0
    {
546
0
      obj = array->data + get_size(array, i);
547
0
      if (!array->esize)
548
0
      {
549
        /* dereference if we store store pointers */
550
0
        obj = *(void**)obj;
551
0
      }
552
0
      method = *(void**)(obj + offset);
553
0
      method(obj);
554
0
    }
555
0
  }
556
2.86k
}
557
558
void array_destroy(array_t *array)
559
2.96k
{
560
2.96k
  if (array)
561
23
  {
562
23
    free(array->data);
563
23
    free(array);
564
23
  }
565
2.96k
}
566
567
void array_destroy_function(array_t *array, array_callback_t cb, void *user)
568
14
{
569
14
  array_invoke(array, cb, user);
570
14
  array_destroy(array);
571
14
}
572
573
void array_destroy_offset(array_t *array, size_t offset)
574
2.86k
{
575
2.86k
  array_invoke_offset(array, offset);
576
2.86k
  array_destroy(array);
577
2.86k
}
578
579
void arrays_init()
580
2
{
581
#ifndef HAVE_QSORT_R
582
  sort_data =  thread_value_create(NULL);
583
#endif
584
2
}
585
586
void arrays_deinit()
587
0
{
588
#ifndef HAVE_QSORT_R
589
  sort_data->destroy(sort_data);
590
#endif
591
0
}