File Coverage

deps/libgit2/src/util/vector.c
Criterion Covered Total %
statement 159 223 71.3
branch 75 148 50.6
condition n/a
subroutine n/a
pod n/a
total 234 371 63.0


line stmt bran cond sub pod time code
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             #define MIN_ALLOCSIZE 8
14              
15 1157           GIT_INLINE(size_t) compute_new_size(git_vector *v)
16             {
17 1157           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 1157 100         if (new_size < MIN_ALLOCSIZE)
22 1149           new_size = MIN_ALLOCSIZE;
23 8 50         else if (new_size <= (SIZE_MAX / 3) * 2)
24 8           new_size += new_size / 2;
25             else
26 0           new_size = SIZE_MAX;
27              
28 1157           return new_size;
29             }
30              
31 5147           GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
32             {
33             void *new_contents;
34              
35 5147 50         if (new_size == 0)
36 0           return 0;
37              
38 5147           new_contents = git__reallocarray(v->contents, new_size, sizeof(void *));
39 5147 50         GIT_ERROR_CHECK_ALLOC(new_contents);
40              
41 5147           v->_alloc_size = new_size;
42 5147           v->contents = new_contents;
43              
44 5147           return 0;
45             }
46              
47 25           int git_vector_size_hint(git_vector *v, size_t size_hint)
48             {
49 25 50         if (v->_alloc_size >= size_hint)
50 25           return 0;
51 0           return resize_vector(v, size_hint);
52             }
53              
54 424           int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
55             {
56 424 50         GIT_ASSERT_ARG(v);
57 424 50         GIT_ASSERT_ARG(src);
58              
59 424           v->_alloc_size = 0;
60 424           v->contents = NULL;
61 424 100         v->_cmp = cmp ? cmp : src->_cmp;
62 424           v->length = src->length;
63 424           v->flags = src->flags;
64 424 100         if (cmp != src->_cmp)
65 1           git_vector_set_sorted(v, 0);
66              
67 424 100         if (src->length) {
68             size_t bytes;
69 393 50         GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *));
    50          
70 393           v->contents = git__malloc(bytes);
71 393 50         GIT_ERROR_CHECK_ALLOC(v->contents);
72 393           v->_alloc_size = src->length;
73 393           memcpy(v->contents, src->contents, bytes);
74             }
75              
76 424           return 0;
77             }
78              
79 9035           void git_vector_free(git_vector *v)
80             {
81 9035 50         if (!v)
82 0           return;
83              
84 9035           git__free(v->contents);
85 9035           v->contents = NULL;
86              
87 9035           v->length = 0;
88 9035           v->_alloc_size = 0;
89             }
90              
91 854           void git_vector_free_deep(git_vector *v)
92             {
93             size_t i;
94              
95 854 50         if (!v)
96 0           return;
97              
98 1370 100         for (i = 0; i < v->length; ++i) {
99 516           git__free(v->contents[i]);
100 516           v->contents[i] = NULL;
101             }
102              
103 854           git_vector_free(v);
104             }
105              
106 3990           int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
107             {
108 3990 50         GIT_ASSERT_ARG(v);
109              
110 3990           v->_alloc_size = 0;
111 3990           v->_cmp = cmp;
112 3990           v->length = 0;
113 3990           v->flags = GIT_VECTOR_SORTED;
114 3990           v->contents = NULL;
115              
116 3990           return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
117             }
118              
119 9           void **git_vector_detach(size_t *size, size_t *asize, git_vector *v)
120             {
121 9           void **data = v->contents;
122              
123 9 50         if (size)
124 9           *size = v->length;
125 9 50         if (asize)
126 0           *asize = v->_alloc_size;
127              
128 9           v->_alloc_size = 0;
129 9           v->length = 0;
130 9           v->contents = NULL;
131              
132 9           return data;
133             }
134              
135 5828           int git_vector_insert(git_vector *v, void *element)
136             {
137 5828 50         GIT_ASSERT_ARG(v);
138              
139 6969           if (v->length >= v->_alloc_size &&
140 1141           resize_vector(v, compute_new_size(v)) < 0)
141 0           return -1;
142              
143 5828           v->contents[v->length++] = element;
144              
145 5828 100         git_vector_set_sorted(v, v->length <= 1);
146              
147 5828           return 0;
148             }
149              
150 1552           int git_vector_insert_sorted(
151             git_vector *v, void *element, int (*on_dup)(void **old, void *new))
152             {
153             int result;
154             size_t pos;
155              
156 1552 50         GIT_ASSERT_ARG(v);
157 1552 50         GIT_ASSERT(v->_cmp);
158              
159 1552 100         if (!git_vector_is_sorted(v))
160 16           git_vector_sort(v);
161              
162 1568           if (v->length >= v->_alloc_size &&
163 16           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 1552 100         if (!git__bsearch(v->contents, v->length, element, v->_cmp, &pos) &&
    50          
171 7 50         on_dup && (result = on_dup(&v->contents[pos], element)) < 0)
172 7           return result;
173              
174             /* shift elements to the right */
175 1545 100         if (pos < v->length)
176 145           memmove(v->contents + pos + 1, v->contents + pos,
177 145           (v->length - pos) * sizeof(void *));
178              
179 1545           v->contents[pos] = element;
180 1545           v->length++;
181              
182 1552           return 0;
183             }
184              
185 8529           void git_vector_sort(git_vector *v)
186             {
187 8529 100         if (git_vector_is_sorted(v) || !v->_cmp)
    50          
188 7961           return;
189              
190 568 100         if (v->length > 1)
191 410           git__tsort(v->contents, v->length, v->_cmp);
192 568           git_vector_set_sorted(v, 1);
193             }
194              
195 1955           int git_vector_bsearch2(
196             size_t *at_pos,
197             git_vector *v,
198             git_vector_cmp key_lookup,
199             const void *key)
200             {
201 1955 50         GIT_ASSERT_ARG(v);
202 1955 50         GIT_ASSERT_ARG(key);
203 1955 50         GIT_ASSERT(key_lookup);
204              
205             /* need comparison function to sort the vector */
206 1955 50         if (!v->_cmp)
207 0           return -1;
208              
209 1955           git_vector_sort(v);
210              
211 1955           return git__bsearch(v->contents, v->length, key, key_lookup, at_pos);
212             }
213              
214 16           int git_vector_search2(
215             size_t *at_pos, const git_vector *v, git_vector_cmp key_lookup, const void *key)
216             {
217             size_t i;
218              
219 16 50         GIT_ASSERT_ARG(v);
220 16 50         GIT_ASSERT_ARG(key);
221 16 50         GIT_ASSERT(key_lookup);
222              
223 40 100         for (i = 0; i < v->length; ++i) {
224 29 100         if (key_lookup(key, v->contents[i]) == 0) {
225 5 50         if (at_pos)
226 5           *at_pos = i;
227              
228 5           return 0;
229             }
230             }
231              
232 11           return GIT_ENOTFOUND;
233             }
234              
235 0           static int strict_comparison(const void *a, const void *b)
236             {
237 0 0         return (a == b) ? 0 : -1;
238             }
239              
240 0           int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry)
241             {
242 0 0         return git_vector_search2(at_pos, v, v->_cmp ? v->_cmp : strict_comparison, entry);
243             }
244              
245 183           int git_vector_remove(git_vector *v, size_t idx)
246             {
247             size_t shift_count;
248              
249 183 50         GIT_ASSERT_ARG(v);
250              
251 183 50         if (idx >= v->length)
252 0           return GIT_ENOTFOUND;
253              
254 183           shift_count = v->length - idx - 1;
255              
256 183 100         if (shift_count)
257 17           memmove(&v->contents[idx], &v->contents[idx + 1],
258             shift_count * sizeof(void *));
259              
260 183           v->length--;
261 183           return 0;
262             }
263              
264 435           void git_vector_pop(git_vector *v)
265             {
266 435 100         if (v->length > 0)
267 421           v->length--;
268 435           }
269              
270 3           void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *))
271             {
272             git_vector_cmp cmp;
273             size_t i, j;
274              
275 3 50         if (v->length <= 1)
276 0           return;
277              
278 3           git_vector_sort(v);
279 3 50         cmp = v->_cmp ? v->_cmp : strict_comparison;
280              
281 10 100         for (i = 0, j = 1 ; j < v->length; ++j)
282 7 100         if (!cmp(v->contents[i], v->contents[j])) {
283 3 50         if (git_free_cb)
284 3           git_free_cb(v->contents[i]);
285              
286 3           v->contents[i] = v->contents[j];
287             } else
288 4           v->contents[++i] = v->contents[j];
289              
290 3           v->length -= j - i - 1;
291             }
292              
293 39           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             {
298             size_t i, j;
299              
300 64 100         for (i = 0, j = 0; j < v->length; ++j) {
301 25           v->contents[i] = v->contents[j];
302              
303 25 50         if (!match(v, i, payload))
304 25           i++;
305             }
306              
307 39           v->length = i;
308 39           }
309              
310 841           void git_vector_clear(git_vector *v)
311             {
312 841           v->length = 0;
313 841           git_vector_set_sorted(v, 1);
314 841           }
315              
316 17           void git_vector_swap(git_vector *a, git_vector *b)
317             {
318             git_vector t;
319              
320 17 50         if (a != b) {
321 17           memcpy(&t, a, sizeof(t));
322 17           memcpy(a, b, sizeof(t));
323 17           memcpy(b, &t, sizeof(t));
324             }
325 17           }
326              
327 0           int git_vector_resize_to(git_vector *v, size_t new_length)
328             {
329 0           if (new_length > v->_alloc_size &&
330 0           resize_vector(v, new_length) < 0)
331 0           return -1;
332              
333 0 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             }
341              
342 0           int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len)
343             {
344             size_t new_length;
345              
346 0 0         GIT_ASSERT_ARG(insert_len > 0);
347 0 0         GIT_ASSERT_ARG(idx <= v->length);
348              
349 0 0         GIT_ERROR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len);
    0          
350              
351 0 0         if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0)
    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             }
361              
362 0           int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len)
363             {
364 0           size_t new_length = v->length - remove_len;
365 0           size_t end_idx = 0;
366              
367 0 0         GIT_ASSERT_ARG(remove_len > 0);
368              
369 0 0         if (git__add_sizet_overflow(&end_idx, idx, remove_len))
370 0           GIT_ASSERT(0);
371              
372 0 0         GIT_ASSERT(end_idx <= v->length);
373              
374 0 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             }
383              
384 2           int git_vector_set(void **old, git_vector *v, size_t position, void *value)
385             {
386 2 50         if (position + 1 > v->length) {
387 0 0         if (git_vector_resize_to(v, position + 1) < 0)
388 0           return -1;
389             }
390              
391 2 50         if (old != NULL)
392 0           *old = v->contents[position];
393              
394 2           v->contents[position] = value;
395              
396 2           return 0;
397             }
398              
399 0           int git_vector_verify_sorted(const git_vector *v)
400             {
401             size_t i;
402              
403 0 0         if (!git_vector_is_sorted(v))
404 0           return -1;
405              
406 0 0         for (i = 1; i < v->length; ++i) {
407 0 0         if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0)
408 0           return -1;
409             }
410              
411 0           return 0;
412             }
413              
414 5           void git_vector_reverse(git_vector *v)
415             {
416             size_t a, b;
417              
418 5 50         if (v->length == 0)
419 0           return;
420              
421 5           a = 0;
422 5           b = v->length - 1;
423              
424 5 50         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             }
431             }