/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 | } |