2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
12 /* In elements, not bytes */
13 #define MIN_ALLOCSIZE 8
15 GIT_INLINE(size_t) compute_new_size(git_vector
*v
)
17 size_t new_size
= v
->_alloc_size
;
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 if (new_size
< MIN_ALLOCSIZE
)
22 new_size
= MIN_ALLOCSIZE
;
23 else if (new_size
<= (SIZE_MAX
/ 3) * 2)
24 new_size
+= new_size
/ 2;
31 GIT_INLINE(int) resize_vector(git_vector
*v
, size_t new_size
)
38 new_contents
= git__reallocarray(v
->contents
, new_size
, sizeof(void *));
39 GIT_ERROR_CHECK_ALLOC(new_contents
);
41 v
->_alloc_size
= new_size
;
42 v
->contents
= new_contents
;
47 int git_vector_size_hint(git_vector
*v
, size_t size_hint
)
49 if (v
->_alloc_size
>= size_hint
)
51 return resize_vector(v
, size_hint
);
54 int git_vector_dup(git_vector
*v
, const git_vector
*src
, git_vector_cmp cmp
)
60 v
->_cmp
= cmp
? cmp
: src
->_cmp
;
61 v
->length
= src
->length
;
62 v
->flags
= src
->flags
;
64 git_vector_set_sorted(v
, 0);
68 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bytes
, src
->length
, sizeof(void *));
69 v
->contents
= git__malloc(bytes
);
70 GIT_ERROR_CHECK_ALLOC(v
->contents
);
71 v
->_alloc_size
= src
->length
;
72 memcpy(v
->contents
, src
->contents
, bytes
);
78 void git_vector_free(git_vector
*v
)
82 git__free(v
->contents
);
89 void git_vector_free_deep(git_vector
*v
)
95 for (i
= 0; i
< v
->length
; ++i
) {
96 git__free(v
->contents
[i
]);
97 v
->contents
[i
] = NULL
;
103 int git_vector_init(git_vector
*v
, size_t initial_size
, git_vector_cmp cmp
)
110 v
->flags
= GIT_VECTOR_SORTED
;
113 return resize_vector(v
, max(initial_size
, MIN_ALLOCSIZE
));
116 void **git_vector_detach(size_t *size
, size_t *asize
, git_vector
*v
)
118 void **data
= v
->contents
;
123 *asize
= v
->_alloc_size
;
132 int git_vector_insert(git_vector
*v
, void *element
)
136 if (v
->length
>= v
->_alloc_size
&&
137 resize_vector(v
, compute_new_size(v
)) < 0)
140 v
->contents
[v
->length
++] = element
;
142 git_vector_set_sorted(v
, v
->length
<= 1);
147 int git_vector_insert_sorted(
148 git_vector
*v
, void *element
, int (*on_dup
)(void **old
, void *new))
153 assert(v
&& v
->_cmp
);
155 if (!git_vector_is_sorted(v
))
158 if (v
->length
>= v
->_alloc_size
&&
159 resize_vector(v
, compute_new_size(v
)) < 0)
162 /* If we find the element and have a duplicate handler callback,
163 * invoke it. If it returns non-zero, then cancel insert, otherwise
164 * proceed with normal insert.
166 if (!git__bsearch(v
->contents
, v
->length
, element
, v
->_cmp
, &pos
) &&
167 on_dup
&& (result
= on_dup(&v
->contents
[pos
], element
)) < 0)
170 /* shift elements to the right */
172 memmove(v
->contents
+ pos
+ 1, v
->contents
+ pos
,
173 (v
->length
- pos
) * sizeof(void *));
175 v
->contents
[pos
] = element
;
181 void git_vector_sort(git_vector
*v
)
185 if (git_vector_is_sorted(v
) || !v
->_cmp
)
189 git__tsort(v
->contents
, v
->length
, v
->_cmp
);
190 git_vector_set_sorted(v
, 1);
193 int git_vector_bsearch2(
196 git_vector_cmp key_lookup
,
199 assert(v
&& key
&& key_lookup
);
201 /* need comparison function to sort the vector */
207 return git__bsearch(v
->contents
, v
->length
, key
, key_lookup
, at_pos
);
210 int git_vector_search2(
211 size_t *at_pos
, const git_vector
*v
, git_vector_cmp key_lookup
, const void *key
)
215 assert(v
&& key
&& key_lookup
);
217 for (i
= 0; i
< v
->length
; ++i
) {
218 if (key_lookup(key
, v
->contents
[i
]) == 0) {
226 return GIT_ENOTFOUND
;
229 static int strict_comparison(const void *a
, const void *b
)
231 return (a
== b
) ? 0 : -1;
234 int git_vector_search(size_t *at_pos
, const git_vector
*v
, const void *entry
)
236 return git_vector_search2(at_pos
, v
, v
->_cmp
? v
->_cmp
: strict_comparison
, entry
);
239 int git_vector_remove(git_vector
*v
, size_t idx
)
245 if (idx
>= v
->length
)
246 return GIT_ENOTFOUND
;
248 shift_count
= v
->length
- idx
- 1;
251 memmove(&v
->contents
[idx
], &v
->contents
[idx
+ 1],
252 shift_count
* sizeof(void *));
258 void git_vector_pop(git_vector
*v
)
264 void git_vector_uniq(git_vector
*v
, void (*git_free_cb
)(void *))
273 cmp
= v
->_cmp
? v
->_cmp
: strict_comparison
;
275 for (i
= 0, j
= 1 ; j
< v
->length
; ++j
)
276 if (!cmp(v
->contents
[i
], v
->contents
[j
])) {
278 git_free_cb(v
->contents
[i
]);
280 v
->contents
[i
] = v
->contents
[j
];
282 v
->contents
[++i
] = v
->contents
[j
];
284 v
->length
-= j
- i
- 1;
287 void git_vector_remove_matching(
289 int (*match
)(const git_vector
*v
, size_t idx
, void *payload
),
294 for (i
= 0, j
= 0; j
< v
->length
; ++j
) {
295 v
->contents
[i
] = v
->contents
[j
];
297 if (!match(v
, i
, payload
))
304 void git_vector_clear(git_vector
*v
)
308 git_vector_set_sorted(v
, 1);
311 void git_vector_swap(git_vector
*a
, git_vector
*b
)
318 memcpy(&t
, a
, sizeof(t
));
319 memcpy(a
, b
, sizeof(t
));
320 memcpy(b
, &t
, sizeof(t
));
324 int git_vector_resize_to(git_vector
*v
, size_t new_length
)
326 if (new_length
> v
->_alloc_size
&&
327 resize_vector(v
, new_length
) < 0)
330 if (new_length
> v
->length
)
331 memset(&v
->contents
[v
->length
], 0,
332 sizeof(void *) * (new_length
- v
->length
));
334 v
->length
= new_length
;
339 int git_vector_insert_null(git_vector
*v
, size_t idx
, size_t insert_len
)
343 assert(insert_len
> 0 && idx
<= v
->length
);
345 GIT_ERROR_CHECK_ALLOC_ADD(&new_length
, v
->length
, insert_len
);
347 if (new_length
> v
->_alloc_size
&& resize_vector(v
, new_length
) < 0)
350 memmove(&v
->contents
[idx
+ insert_len
], &v
->contents
[idx
],
351 sizeof(void *) * (v
->length
- idx
));
352 memset(&v
->contents
[idx
], 0, sizeof(void *) * insert_len
);
354 v
->length
= new_length
;
358 int git_vector_remove_range(git_vector
*v
, size_t idx
, size_t remove_len
)
360 size_t new_length
= v
->length
- remove_len
;
363 assert(remove_len
> 0);
365 if (git__add_sizet_overflow(&end_idx
, idx
, remove_len
))
368 assert(end_idx
<= v
->length
);
370 if (end_idx
< v
->length
)
371 memmove(&v
->contents
[idx
], &v
->contents
[end_idx
],
372 sizeof(void *) * (v
->length
- end_idx
));
374 memset(&v
->contents
[new_length
], 0, sizeof(void *) * remove_len
);
376 v
->length
= new_length
;
380 int git_vector_set(void **old
, git_vector
*v
, size_t position
, void *value
)
382 if (position
+ 1 > v
->length
) {
383 if (git_vector_resize_to(v
, position
+ 1) < 0)
388 *old
= v
->contents
[position
];
390 v
->contents
[position
] = value
;
395 int git_vector_verify_sorted(const git_vector
*v
)
399 if (!git_vector_is_sorted(v
))
402 for (i
= 1; i
< v
->length
; ++i
) {
403 if (v
->_cmp(v
->contents
[i
- 1], v
->contents
[i
]) > 0)
410 void git_vector_reverse(git_vector
*v
)
421 void *tmp
= v
->contents
[a
];
422 v
->contents
[a
] = v
->contents
[b
];
423 v
->contents
[b
] = tmp
;