Coverage Report

Created: 2025-11-09 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libgit2/src/util/vector.c
Line
Count
Source
1
/*
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
4
 * This file is part of libgit2, distributed under the GNU GPL v2 with
5
 * a Linking Exception. For full terms see the included COPYING file.
6
 */
7
8
#include "vector.h"
9
10
#include "integer.h"
11
12
/* In elements, not bytes */
13
0
#define MIN_ALLOCSIZE 8
14
15
GIT_INLINE(size_t) compute_new_size(git_vector *v)
16
0
{
17
0
  size_t new_size = v->_alloc_size;
18
19
  /* Use a resize factor of 1.5, which is quick to compute using integer
20
   * instructions and less than the golden ratio (1.618...) */
21
0
  if (new_size < MIN_ALLOCSIZE)
22
0
    new_size = MIN_ALLOCSIZE;
23
0
  else if (new_size <= (SIZE_MAX / 3) * 2)
24
0
    new_size += new_size / 2;
25
0
  else
26
0
    new_size = SIZE_MAX;
27
28
0
  return new_size;
29
0
}
30
31
GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
32
4
{
33
4
  void *new_contents;
34
35
4
  if (new_size == 0)
36
0
    return 0;
37
38
4
  new_contents = git__reallocarray(v->contents, new_size, sizeof(void *));
39
4
  GIT_ERROR_CHECK_ALLOC(new_contents);
40
41
4
  v->_alloc_size = new_size;
42
4
  v->contents = new_contents;
43
44
4
  return 0;
45
4
}
46
47
int git_vector_size_hint(git_vector *v, size_t size_hint)
48
0
{
49
0
  if (v->_alloc_size >= size_hint)
50
0
    return 0;
51
0
  return resize_vector(v, size_hint);
52
0
}
53
54
int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
55
0
{
56
0
  GIT_ASSERT_ARG(v);
57
0
  GIT_ASSERT_ARG(src);
58
59
0
  v->_alloc_size = 0;
60
0
  v->contents = NULL;
61
0
  v->_cmp = cmp ? cmp : src->_cmp;
62
0
  v->length = src->length;
63
0
  v->flags  = src->flags;
64
0
  if (cmp != src->_cmp)
65
0
    git_vector_set_sorted(v, 0);
66
67
0
  if (src->length) {
68
0
    size_t bytes;
69
0
    GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *));
70
0
    v->contents = git__malloc(bytes);
71
0
    GIT_ERROR_CHECK_ALLOC(v->contents);
72
0
    v->_alloc_size = src->length;
73
0
    memcpy(v->contents, src->contents, bytes);
74
0
  }
75
76
0
  return 0;
77
0
}
78
79
void git_vector_dispose(git_vector *v)
80
0
{
81
0
  if (!v)
82
0
    return;
83
84
0
  git__free(v->contents);
85
0
  v->contents = NULL;
86
87
0
  v->length = 0;
88
0
  v->_alloc_size = 0;
89
0
}
90
91
void git_vector_dispose_deep(git_vector *v)
92
0
{
93
0
  size_t i;
94
95
0
  if (!v)
96
0
    return;
97
98
0
  for (i = 0; i < v->length; ++i) {
99
0
    git__free(v->contents[i]);
100
0
    v->contents[i] = NULL;
101
0
  }
102
103
0
  git_vector_dispose(v);
104
0
}
105
106
int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
107
4
{
108
4
  GIT_ASSERT_ARG(v);
109
110
4
  v->_alloc_size = 0;
111
4
  v->_cmp = cmp;
112
4
  v->length = 0;
113
4
  v->flags = GIT_VECTOR_SORTED;
114
4
  v->contents = NULL;
115
116
4
  return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
117
4
}
118
119
void **git_vector_detach(size_t *size, size_t *asize, git_vector *v)
120
0
{
121
0
  void **data = v->contents;
122
123
0
  if (size)
124
0
    *size = v->length;
125
0
  if (asize)
126
0
    *asize = v->_alloc_size;
127
128
0
  v->_alloc_size = 0;
129
0
  v->length   = 0;
130
0
  v->contents = NULL;
131
132
0
  return data;
133
0
}
134
135
int git_vector_insert(git_vector *v, void *element)
136
4
{
137
4
  GIT_ASSERT_ARG(v);
138
139
4
  if (v->length >= v->_alloc_size &&
140
0
    resize_vector(v, compute_new_size(v)) < 0)
141
0
    return -1;
142
143
4
  v->contents[v->length++] = element;
144
145
4
  git_vector_set_sorted(v, v->length <= 1);
146
147
4
  return 0;
148
4
}
149
150
int git_vector_insert_sorted(
151
  git_vector *v, void *element, int (*on_dup)(void **old, void *new))
152
6
{
153
6
  int result;
154
6
  size_t pos;
155
156
6
  GIT_ASSERT_ARG(v);
157
6
  GIT_ASSERT(v->_cmp);
158
159
6
  if (!git_vector_is_sorted(v))
160
0
    git_vector_sort(v);
161
162
6
  if (v->length >= v->_alloc_size &&
163
0
    resize_vector(v, compute_new_size(v)) < 0)
164
0
    return -1;
165
166
  /* If we find the element and have a duplicate handler callback,
167
   * invoke it.  If it returns non-zero, then cancel insert, otherwise
168
   * proceed with normal insert.
169
   */
170
6
  if (!git__bsearch(v->contents, v->length, element, v->_cmp, &pos) &&
171
0
    on_dup && (result = on_dup(&v->contents[pos], element)) < 0)
172
0
    return result;
173
174
  /* shift elements to the right */
175
6
  if (pos < v->length)
176
2
    memmove(v->contents + pos + 1, v->contents + pos,
177
2
            (v->length - pos) * sizeof(void *));
178
179
6
  v->contents[pos] = element;
180
6
  v->length++;
181
182
6
  return 0;
183
6
}
184
185
void git_vector_sort(git_vector *v)
186
4
{
187
4
  if (git_vector_is_sorted(v) || !v->_cmp)
188
2
    return;
189
190
2
  if (v->length > 1)
191
2
    git__tsort(v->contents, v->length, v->_cmp);
192
2
  git_vector_set_sorted(v, 1);
193
2
}
194
195
int git_vector_bsearch2(
196
  size_t *at_pos,
197
  git_vector *v,
198
  git_vector_cmp key_lookup,
199
  const void *key)
200
0
{
201
0
  GIT_ASSERT_ARG(v);
202
0
  GIT_ASSERT_ARG(key);
203
0
  GIT_ASSERT(key_lookup);
204
205
  /* need comparison function to sort the vector */
206
0
  if (!v->_cmp)
207
0
    return -1;
208
209
0
  git_vector_sort(v);
210
211
0
  return git__bsearch(v->contents, v->length, key, key_lookup, at_pos);
212
0
}
213
214
int git_vector_search2(
215
  size_t *at_pos, const git_vector *v, git_vector_cmp key_lookup, const void *key)
216
0
{
217
0
  size_t i;
218
219
0
  GIT_ASSERT_ARG(v);
220
0
  GIT_ASSERT_ARG(key);
221
0
  GIT_ASSERT(key_lookup);
222
223
0
  for (i = 0; i < v->length; ++i) {
224
0
    if (key_lookup(key, v->contents[i]) == 0) {
225
0
      if (at_pos)
226
0
        *at_pos = i;
227
228
0
      return 0;
229
0
    }
230
0
  }
231
232
0
  return GIT_ENOTFOUND;
233
0
}
234
235
static int strict_comparison(const void *a, const void *b)
236
0
{
237
0
  return (a == b) ? 0 : -1;
238
0
}
239
240
int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry)
241
0
{
242
0
  return git_vector_search2(at_pos, v, v->_cmp ? v->_cmp : strict_comparison, entry);
243
0
}
244
245
int git_vector_remove(git_vector *v, size_t idx)
246
0
{
247
0
  size_t shift_count;
248
249
0
  GIT_ASSERT_ARG(v);
250
251
0
  if (idx >= v->length)
252
0
    return GIT_ENOTFOUND;
253
254
0
  shift_count = v->length - idx - 1;
255
256
0
  if (shift_count)
257
0
    memmove(&v->contents[idx], &v->contents[idx + 1],
258
0
      shift_count * sizeof(void *));
259
260
0
  v->length--;
261
0
  return 0;
262
0
}
263
264
void git_vector_pop(git_vector *v)
265
0
{
266
0
  if (v->length > 0)
267
0
    v->length--;
268
0
}
269
270
void git_vector_uniq(git_vector *v, void  (*git_free_cb)(void *))
271
0
{
272
0
  git_vector_cmp cmp;
273
0
  size_t i, j;
274
275
0
  if (v->length <= 1)
276
0
    return;
277
278
0
  git_vector_sort(v);
279
0
  cmp = v->_cmp ? v->_cmp : strict_comparison;
280
281
0
  for (i = 0, j = 1 ; j < v->length; ++j)
282
0
    if (!cmp(v->contents[i], v->contents[j])) {
283
0
      if (git_free_cb)
284
0
        git_free_cb(v->contents[i]);
285
286
0
      v->contents[i] = v->contents[j];
287
0
    } else
288
0
      v->contents[++i] = v->contents[j];
289
290
0
  v->length -= j - i - 1;
291
0
}
292
293
void git_vector_remove_matching(
294
  git_vector *v,
295
  int (*match)(const git_vector *v, size_t idx, void *payload),
296
  void *payload)
297
0
{
298
0
  size_t i, j;
299
300
0
  for (i = 0, j = 0; j < v->length; ++j) {
301
0
    v->contents[i] = v->contents[j];
302
303
0
    if (!match(v, i, payload))
304
0
      i++;
305
0
  }
306
307
0
  v->length = i;
308
0
}
309
310
void git_vector_clear(git_vector *v)
311
0
{
312
0
  v->length = 0;
313
0
  git_vector_set_sorted(v, 1);
314
0
}
315
316
void git_vector_swap(git_vector *a, git_vector *b)
317
0
{
318
0
  git_vector t;
319
320
0
  if (a != b) {
321
0
    memcpy(&t, a, sizeof(t));
322
0
    memcpy(a, b, sizeof(t));
323
0
    memcpy(b, &t, sizeof(t));
324
0
  }
325
0
}
326
327
int git_vector_resize_to(git_vector *v, size_t new_length)
328
0
{
329
0
  if (new_length > v->_alloc_size &&
330
0
    resize_vector(v, new_length) < 0)
331
0
    return -1;
332
333
0
  if (new_length > v->length)
334
0
    memset(&v->contents[v->length], 0,
335
0
      sizeof(void *) * (new_length - v->length));
336
337
0
  v->length = new_length;
338
339
0
  return 0;
340
0
}
341
342
int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len)
343
0
{
344
0
  size_t new_length;
345
346
0
  GIT_ASSERT_ARG(insert_len > 0);
347
0
  GIT_ASSERT_ARG(idx <= v->length);
348
349
0
  GIT_ERROR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len);
350
351
0
  if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0)
352
0
    return -1;
353
354
0
  memmove(&v->contents[idx + insert_len], &v->contents[idx],
355
0
    sizeof(void *) * (v->length - idx));
356
0
  memset(&v->contents[idx], 0, sizeof(void *) * insert_len);
357
358
0
  v->length = new_length;
359
0
  return 0;
360
0
}
361
362
int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len)
363
0
{
364
0
  size_t new_length = v->length - remove_len;
365
0
  size_t end_idx = 0;
366
367
0
  GIT_ASSERT_ARG(remove_len > 0);
368
369
0
  if (git__add_sizet_overflow(&end_idx, idx, remove_len))
370
0
    GIT_ASSERT(0);
371
372
0
  GIT_ASSERT(end_idx <= v->length);
373
374
0
  if (end_idx < v->length)
375
0
    memmove(&v->contents[idx], &v->contents[end_idx],
376
0
      sizeof(void *) * (v->length - end_idx));
377
378
0
  memset(&v->contents[new_length], 0, sizeof(void *) * remove_len);
379
380
0
  v->length = new_length;
381
0
  return 0;
382
0
}
383
384
int git_vector_set(void **old, git_vector *v, size_t position, void *value)
385
0
{
386
0
  if (position + 1 > v->length) {
387
0
    if (git_vector_resize_to(v, position + 1) < 0)
388
0
      return -1;
389
0
  }
390
391
0
  if (old != NULL)
392
0
    *old = v->contents[position];
393
394
0
  v->contents[position] = value;
395
396
0
  return 0;
397
0
}
398
399
int git_vector_verify_sorted(const git_vector *v)
400
0
{
401
0
  size_t i;
402
403
0
  if (!git_vector_is_sorted(v))
404
0
    return -1;
405
406
0
  for (i = 1; i < v->length; ++i) {
407
0
    if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0)
408
0
      return -1;
409
0
  }
410
411
0
  return 0;
412
0
}
413
414
void git_vector_reverse(git_vector *v)
415
0
{
416
0
  size_t a, b;
417
418
0
  if (v->length == 0)
419
0
    return;
420
421
0
  a = 0;
422
0
  b = v->length - 1;
423
424
0
  while (a < b) {
425
0
    void *tmp = v->contents[a];
426
0
    v->contents[a] = v->contents[b];
427
0
    v->contents[b] = tmp;
428
0
    a++;
429
0
    b--;
430
0
  }
431
0
}