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
)
61 v
->_cmp
= cmp
? cmp
: src
->_cmp
;
62 v
->length
= src
->length
;
63 v
->flags
= src
->flags
;
65 git_vector_set_sorted(v
, 0);
69 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bytes
, src
->length
, sizeof(void *));
70 v
->contents
= git__malloc(bytes
);
71 GIT_ERROR_CHECK_ALLOC(v
->contents
);
72 v
->_alloc_size
= src
->length
;
73 memcpy(v
->contents
, src
->contents
, bytes
);
79 void git_vector_free(git_vector
*v
)
84 git__free(v
->contents
);
91 void git_vector_free_deep(git_vector
*v
)
98 for (i
= 0; i
< v
->length
; ++i
) {
99 git__free(v
->contents
[i
]);
100 v
->contents
[i
] = NULL
;
106 int git_vector_init(git_vector
*v
, size_t initial_size
, git_vector_cmp cmp
)
113 v
->flags
= GIT_VECTOR_SORTED
;
116 return resize_vector(v
, max(initial_size
, MIN_ALLOCSIZE
));
119 void **git_vector_detach(size_t *size
, size_t *asize
, git_vector
*v
)
121 void **data
= v
->contents
;
126 *asize
= v
->_alloc_size
;
135 int git_vector_insert(git_vector
*v
, void *element
)
139 if (v
->length
>= v
->_alloc_size
&&
140 resize_vector(v
, compute_new_size(v
)) < 0)
143 v
->contents
[v
->length
++] = element
;
145 git_vector_set_sorted(v
, v
->length
<= 1);
150 int git_vector_insert_sorted(
151 git_vector
*v
, void *element
, int (*on_dup
)(void **old
, void *new))
159 if (!git_vector_is_sorted(v
))
162 if (v
->length
>= v
->_alloc_size
&&
163 resize_vector(v
, compute_new_size(v
)) < 0)
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.
170 if (!git__bsearch(v
->contents
, v
->length
, element
, v
->_cmp
, &pos
) &&
171 on_dup
&& (result
= on_dup(&v
->contents
[pos
], element
)) < 0)
174 /* shift elements to the right */
176 memmove(v
->contents
+ pos
+ 1, v
->contents
+ pos
,
177 (v
->length
- pos
) * sizeof(void *));
179 v
->contents
[pos
] = element
;
185 void git_vector_sort(git_vector
*v
)
187 if (git_vector_is_sorted(v
) || !v
->_cmp
)
191 git__tsort(v
->contents
, v
->length
, v
->_cmp
);
192 git_vector_set_sorted(v
, 1);
195 int git_vector_bsearch2(
198 git_vector_cmp key_lookup
,
203 GIT_ASSERT(key_lookup
);
205 /* need comparison function to sort the vector */
211 return git__bsearch(v
->contents
, v
->length
, key
, key_lookup
, at_pos
);
214 int git_vector_search2(
215 size_t *at_pos
, const git_vector
*v
, git_vector_cmp key_lookup
, const void *key
)
221 GIT_ASSERT(key_lookup
);
223 for (i
= 0; i
< v
->length
; ++i
) {
224 if (key_lookup(key
, v
->contents
[i
]) == 0) {
232 return GIT_ENOTFOUND
;
235 static int strict_comparison(const void *a
, const void *b
)
237 return (a
== b
) ? 0 : -1;
240 int git_vector_search(size_t *at_pos
, const git_vector
*v
, const void *entry
)
242 return git_vector_search2(at_pos
, v
, v
->_cmp
? v
->_cmp
: strict_comparison
, entry
);
245 int git_vector_remove(git_vector
*v
, size_t idx
)
251 if (idx
>= v
->length
)
252 return GIT_ENOTFOUND
;
254 shift_count
= v
->length
- idx
- 1;
257 memmove(&v
->contents
[idx
], &v
->contents
[idx
+ 1],
258 shift_count
* sizeof(void *));
264 void git_vector_pop(git_vector
*v
)
270 void git_vector_uniq(git_vector
*v
, void (*git_free_cb
)(void *))
279 cmp
= v
->_cmp
? v
->_cmp
: strict_comparison
;
281 for (i
= 0, j
= 1 ; j
< v
->length
; ++j
)
282 if (!cmp(v
->contents
[i
], v
->contents
[j
])) {
284 git_free_cb(v
->contents
[i
]);
286 v
->contents
[i
] = v
->contents
[j
];
288 v
->contents
[++i
] = v
->contents
[j
];
290 v
->length
-= j
- i
- 1;
293 void git_vector_remove_matching(
295 int (*match
)(const git_vector
*v
, size_t idx
, void *payload
),
300 for (i
= 0, j
= 0; j
< v
->length
; ++j
) {
301 v
->contents
[i
] = v
->contents
[j
];
303 if (!match(v
, i
, payload
))
310 void git_vector_clear(git_vector
*v
)
313 git_vector_set_sorted(v
, 1);
316 void git_vector_swap(git_vector
*a
, git_vector
*b
)
321 memcpy(&t
, a
, sizeof(t
));
322 memcpy(a
, b
, sizeof(t
));
323 memcpy(b
, &t
, sizeof(t
));
327 int git_vector_resize_to(git_vector
*v
, size_t new_length
)
329 if (new_length
> v
->_alloc_size
&&
330 resize_vector(v
, new_length
) < 0)
333 if (new_length
> v
->length
)
334 memset(&v
->contents
[v
->length
], 0,
335 sizeof(void *) * (new_length
- v
->length
));
337 v
->length
= new_length
;
342 int git_vector_insert_null(git_vector
*v
, size_t idx
, size_t insert_len
)
346 GIT_ASSERT_ARG(insert_len
> 0);
347 GIT_ASSERT_ARG(idx
<= v
->length
);
349 GIT_ERROR_CHECK_ALLOC_ADD(&new_length
, v
->length
, insert_len
);
351 if (new_length
> v
->_alloc_size
&& resize_vector(v
, new_length
) < 0)
354 memmove(&v
->contents
[idx
+ insert_len
], &v
->contents
[idx
],
355 sizeof(void *) * (v
->length
- idx
));
356 memset(&v
->contents
[idx
], 0, sizeof(void *) * insert_len
);
358 v
->length
= new_length
;
362 int git_vector_remove_range(git_vector
*v
, size_t idx
, size_t remove_len
)
364 size_t new_length
= v
->length
- remove_len
;
367 GIT_ASSERT_ARG(remove_len
> 0);
369 if (git__add_sizet_overflow(&end_idx
, idx
, remove_len
))
372 GIT_ASSERT(end_idx
<= v
->length
);
374 if (end_idx
< v
->length
)
375 memmove(&v
->contents
[idx
], &v
->contents
[end_idx
],
376 sizeof(void *) * (v
->length
- end_idx
));
378 memset(&v
->contents
[new_length
], 0, sizeof(void *) * remove_len
);
380 v
->length
= new_length
;
384 int git_vector_set(void **old
, git_vector
*v
, size_t position
, void *value
)
386 if (position
+ 1 > v
->length
) {
387 if (git_vector_resize_to(v
, position
+ 1) < 0)
392 *old
= v
->contents
[position
];
394 v
->contents
[position
] = value
;
399 int git_vector_verify_sorted(const git_vector
*v
)
403 if (!git_vector_is_sorted(v
))
406 for (i
= 1; i
< v
->length
; ++i
) {
407 if (v
->_cmp(v
->contents
[i
- 1], v
->contents
[i
]) > 0)
414 void git_vector_reverse(git_vector
*v
)
425 void *tmp
= v
->contents
[a
];
426 v
->contents
[a
] = v
->contents
[b
];
427 v
->contents
[b
] = tmp
;