Coverage Report

Created: 2025-11-09 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/dsa/ares_array.c
Line
Count
Source
1
/* MIT License
2
 *
3
 * Copyright (c) 2024 Brad House
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
6
 * of this software and associated documentation files (the "Software"), to deal
7
 * in the Software without restriction, including without limitation the rights
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice (including the next
13
 * paragraph) shall be included in all copies or substantial portions of the
14
 * Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 *
24
 * SPDX-License-Identifier: MIT
25
 */
26
#include "ares_private.h"
27
#include "ares_array.h"
28
29
12.8M
#define ARES__ARRAY_MIN 4
30
31
struct ares_array {
32
  ares_array_destructor_t destruct;
33
  void                   *arr;
34
  size_t                  member_size;
35
  size_t                  cnt;
36
  size_t                  offset;
37
  size_t                  alloc_cnt;
38
};
39
40
ares_array_t *ares_array_create(size_t                  member_size,
41
                                ares_array_destructor_t destruct)
42
151k
{
43
151k
  ares_array_t *arr;
44
45
151k
  if (member_size == 0) {
46
0
    return NULL;
47
0
  }
48
49
151k
  arr = ares_malloc_zero(sizeof(*arr));
50
151k
  if (arr == NULL) {
51
0
    return NULL;
52
0
  }
53
54
151k
  arr->member_size = member_size;
55
151k
  arr->destruct    = destruct;
56
151k
  return arr;
57
151k
}
58
59
size_t ares_array_len(const ares_array_t *arr)
60
18.7M
{
61
18.7M
  if (arr == NULL) {
62
0
    return 0;
63
0
  }
64
18.7M
  return arr->cnt;
65
18.7M
}
66
67
void *ares_array_at(ares_array_t *arr, size_t idx)
68
27.0M
{
69
27.0M
  if (arr == NULL || idx >= arr->cnt) {
70
0
    return NULL;
71
0
  }
72
27.0M
  return (unsigned char *)arr->arr + ((idx + arr->offset) * arr->member_size);
73
27.0M
}
74
75
const void *ares_array_at_const(const ares_array_t *arr, size_t idx)
76
279k
{
77
279k
  if (arr == NULL || idx >= arr->cnt) {
78
0
    return NULL;
79
0
  }
80
279k
  return (unsigned char *)arr->arr + ((idx + arr->offset) * arr->member_size);
81
279k
}
82
83
ares_status_t ares_array_sort(ares_array_t *arr, ares_array_cmp_t cmp)
84
0
{
85
0
  if (arr == NULL || cmp == NULL) {
86
0
    return ARES_EFORMERR;
87
0
  }
88
89
  /* Nothing to sort */
90
0
  if (arr->cnt < 2) {
91
0
    return ARES_SUCCESS;
92
0
  }
93
94
0
  qsort((unsigned char *)arr->arr + (arr->offset * arr->member_size), arr->cnt,
95
0
        arr->member_size, cmp);
96
0
  return ARES_SUCCESS;
97
0
}
98
99
void ares_array_destroy(ares_array_t *arr)
100
143k
{
101
143k
  size_t i;
102
103
143k
  if (arr == NULL) {
104
4.97k
    return;
105
4.97k
  }
106
107
138k
  if (arr->destruct != NULL) {
108
12.2M
    for (i = 0; i < arr->cnt; i++) {
109
12.1M
      arr->destruct(ares_array_at(arr, i));
110
12.1M
    }
111
138k
  }
112
113
138k
  ares_free(arr->arr);
114
138k
  ares_free(arr);
115
138k
}
116
117
/* NOTE: this function operates on actual indexes, NOT indexes using the
118
 *       arr->offset */
119
static ares_status_t ares_array_move(ares_array_t *arr, size_t dest_idx,
120
                                     size_t src_idx)
121
144k
{
122
144k
  void       *dest_ptr;
123
144k
  const void *src_ptr;
124
144k
  size_t      nmembers;
125
126
144k
  if (arr == NULL || dest_idx >= arr->alloc_cnt || src_idx >= arr->alloc_cnt) {
127
0
    return ARES_EFORMERR;
128
0
  }
129
130
  /* Nothing to do */
131
144k
  if (dest_idx == src_idx) {
132
0
    return ARES_SUCCESS;
133
0
  }
134
135
144k
  dest_ptr = (unsigned char *)arr->arr + (dest_idx * arr->member_size);
136
144k
  src_ptr  = (unsigned char *)arr->arr + (src_idx * arr->member_size);
137
138
  /* Check to make sure shifting to the right won't overflow our allocation
139
   * boundary */
140
144k
  if (dest_idx > src_idx && arr->cnt + (dest_idx - src_idx) > arr->alloc_cnt) {
141
0
    return ARES_EFORMERR;
142
0
  }
143
144
144k
  nmembers = arr->cnt - (src_idx - arr->offset);
145
144k
  memmove(dest_ptr, src_ptr, nmembers * arr->member_size);
146
147
144k
  return ARES_SUCCESS;
148
144k
}
149
150
void *ares_array_finish(ares_array_t *arr, size_t *num_members)
151
12.6k
{
152
12.6k
  void *ptr;
153
154
12.6k
  if (arr == NULL || num_members == NULL) {
155
0
    return NULL;
156
0
  }
157
158
  /* Make sure we move data to beginning of allocation */
159
12.6k
  if (arr->offset != 0) {
160
0
    if (ares_array_move(arr, 0, arr->offset) != ARES_SUCCESS) {
161
0
      return NULL;
162
0
    }
163
0
    arr->offset = 0;
164
0
  }
165
166
12.6k
  ptr          = arr->arr;
167
12.6k
  *num_members = arr->cnt;
168
12.6k
  ares_free(arr);
169
12.6k
  return ptr;
170
12.6k
}
171
172
ares_status_t ares_array_set_size(ares_array_t *arr, size_t size)
173
12.6M
{
174
12.6M
  void *temp;
175
176
12.6M
  if (arr == NULL || size == 0 || size < arr->cnt) {
177
0
    return ARES_EFORMERR;
178
0
  }
179
180
  /* Always operate on powers of 2 */
181
12.6M
  size = ares_round_up_pow2(size);
182
183
12.6M
  if (size < ARES__ARRAY_MIN) {
184
226k
    size = ARES__ARRAY_MIN;
185
226k
  }
186
187
  /* If our allocation size is already large enough, skip */
188
12.6M
  if (size <= arr->alloc_cnt) {
189
12.4M
    return ARES_SUCCESS;
190
12.4M
  }
191
192
154k
  temp = ares_realloc_zero(arr->arr, arr->alloc_cnt * arr->member_size,
193
154k
                           size * arr->member_size);
194
154k
  if (temp == NULL) {
195
0
    return ARES_ENOMEM;
196
0
  }
197
154k
  arr->alloc_cnt = size;
198
154k
  arr->arr       = temp;
199
154k
  return ARES_SUCCESS;
200
154k
}
201
202
ares_status_t ares_array_insert_at(void **elem_ptr, ares_array_t *arr,
203
                                   size_t idx)
204
12.6M
{
205
12.6M
  void         *ptr;
206
12.6M
  ares_status_t status;
207
208
12.6M
  if (arr == NULL) {
209
0
    return ARES_EFORMERR;
210
0
  }
211
212
  /* Not >= since we are allowed to append to the end */
213
12.6M
  if (idx > arr->cnt) {
214
0
    return ARES_EFORMERR;
215
0
  }
216
217
  /* Allocate more if needed */
218
12.6M
  status = ares_array_set_size(arr, arr->cnt + 1);
219
12.6M
  if (status != ARES_SUCCESS) {
220
0
    return status;
221
0
  }
222
223
  /* Shift if we have memory but not enough room at the end */
224
12.6M
  if (arr->cnt + 1 + arr->offset > arr->alloc_cnt) {
225
0
    status = ares_array_move(arr, 0, arr->offset);
226
0
    if (status != ARES_SUCCESS) {
227
0
      return status;
228
0
    }
229
0
    arr->offset = 0;
230
0
  }
231
232
  /* If we're inserting anywhere other than the end, we need to move some
233
   * elements out of the way */
234
12.6M
  if (idx != arr->cnt) {
235
0
    status = ares_array_move(arr, idx + arr->offset + 1, idx + arr->offset);
236
0
    if (status != ARES_SUCCESS) {
237
0
      return status;
238
0
    }
239
0
  }
240
241
  /* Ok, we're guaranteed to have a gap where we need it, lets zero it out,
242
   * and return it */
243
12.6M
  ptr = (unsigned char *)arr->arr + ((idx + arr->offset) * arr->member_size);
244
12.6M
  memset(ptr, 0, arr->member_size);
245
12.6M
  arr->cnt++;
246
247
12.6M
  if (elem_ptr) {
248
12.6M
    *elem_ptr = ptr;
249
12.6M
  }
250
251
12.6M
  return ARES_SUCCESS;
252
12.6M
}
253
254
ares_status_t ares_array_insert_last(void **elem_ptr, ares_array_t *arr)
255
12.6M
{
256
12.6M
  return ares_array_insert_at(elem_ptr, arr, ares_array_len(arr));
257
12.6M
}
258
259
ares_status_t ares_array_insert_first(void **elem_ptr, ares_array_t *arr)
260
0
{
261
0
  return ares_array_insert_at(elem_ptr, arr, 0);
262
0
}
263
264
ares_status_t ares_array_insertdata_at(ares_array_t *arr, size_t idx,
265
                                       const void *data_ptr)
266
0
{
267
0
  ares_status_t status;
268
0
  void         *ptr = NULL;
269
270
0
  status = ares_array_insert_at(&ptr, arr, idx);
271
0
  if (status != ARES_SUCCESS) {
272
0
    return status;
273
0
  }
274
0
  memcpy(ptr, data_ptr, arr->member_size);
275
0
  return ARES_SUCCESS;
276
0
}
277
278
ares_status_t ares_array_insertdata_last(ares_array_t *arr,
279
                                         const void   *data_ptr)
280
12.2M
{
281
12.2M
  ares_status_t status;
282
12.2M
  void         *ptr = NULL;
283
284
12.2M
  status = ares_array_insert_last(&ptr, arr);
285
12.2M
  if (status != ARES_SUCCESS) {
286
0
    return status;
287
0
  }
288
12.2M
  memcpy(ptr, data_ptr, arr->member_size);
289
12.2M
  return ARES_SUCCESS;
290
12.2M
}
291
292
ares_status_t ares_array_insertdata_first(ares_array_t *arr,
293
                                          const void   *data_ptr)
294
0
{
295
0
  ares_status_t status;
296
0
  void         *ptr = NULL;
297
298
0
  status = ares_array_insert_last(&ptr, arr);
299
0
  if (status != ARES_SUCCESS) {
300
0
    return status;
301
0
  }
302
0
  memcpy(ptr, data_ptr, arr->member_size);
303
0
  return ARES_SUCCESS;
304
0
}
305
306
void *ares_array_first(ares_array_t *arr)
307
0
{
308
0
  return ares_array_at(arr, 0);
309
0
}
310
311
void *ares_array_last(ares_array_t *arr)
312
25.5k
{
313
25.5k
  size_t cnt = ares_array_len(arr);
314
25.5k
  if (cnt == 0) {
315
0
    return NULL;
316
0
  }
317
25.5k
  return ares_array_at(arr, cnt - 1);
318
25.5k
}
319
320
const void *ares_array_first_const(const ares_array_t *arr)
321
0
{
322
0
  return ares_array_at_const(arr, 0);
323
0
}
324
325
const void *ares_array_last_const(const ares_array_t *arr)
326
0
{
327
0
  size_t cnt = ares_array_len(arr);
328
0
  if (cnt == 0) {
329
0
    return NULL;
330
0
  }
331
0
  return ares_array_at_const(arr, cnt - 1);
332
0
}
333
334
ares_status_t ares_array_claim_at(void *dest, size_t dest_size,
335
                                  ares_array_t *arr, size_t idx)
336
501k
{
337
501k
  ares_status_t status;
338
339
501k
  if (arr == NULL || idx >= arr->cnt) {
340
0
    return ARES_EFORMERR;
341
0
  }
342
343
501k
  if (dest != NULL && dest_size < arr->member_size) {
344
0
    return ARES_EFORMERR;
345
0
  }
346
347
501k
  if (dest) {
348
0
    memcpy(dest, ares_array_at(arr, idx), arr->member_size);
349
0
  }
350
351
501k
  if (idx == 0) {
352
    /* Optimization, if first element, just increment offset, makes removing a
353
     * lot from the start quick */
354
40.0k
    arr->offset++;
355
461k
  } else if (idx != arr->cnt - 1) {
356
    /* Must shift entire array if removing an element from the middle. Does
357
     * nothing if removing last element other than decrement count. */
358
144k
    status = ares_array_move(arr, idx + arr->offset, idx + arr->offset + 1);
359
144k
    if (status != ARES_SUCCESS) {
360
0
      return status;
361
0
    }
362
144k
  }
363
364
501k
  arr->cnt--;
365
501k
  return ARES_SUCCESS;
366
501k
}
367
368
ares_status_t ares_array_remove_at(ares_array_t *arr, size_t idx)
369
501k
{
370
501k
  void *ptr = ares_array_at(arr, idx);
371
501k
  if (arr == NULL || ptr == NULL) {
372
0
    return ARES_EFORMERR;
373
0
  }
374
375
501k
  if (arr->destruct != NULL) {
376
501k
    arr->destruct(ptr);
377
501k
  }
378
379
501k
  return ares_array_claim_at(NULL, 0, arr, idx);
380
501k
}
381
382
ares_status_t ares_array_remove_first(ares_array_t *arr)
383
0
{
384
0
  return ares_array_remove_at(arr, 0);
385
0
}
386
387
ares_status_t ares_array_remove_last(ares_array_t *arr)
388
339k
{
389
339k
  size_t cnt = ares_array_len(arr);
390
339k
  if (cnt == 0) {
391
0
    return ARES_EFORMERR;
392
0
  }
393
339k
  return ares_array_remove_at(arr, cnt - 1);
394
339k
}