1 /******************************************************************************
3 ******************************************************************************
4 * Copyright (C) 2018 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
27 * \defgroup vector Vector
31 * This provides convenience helpers for vectors.
37 * A vector is a dynamic array, managed by the vlc_vector_* helpers.
39 * It is generic over the type of its items, so it is implemented as macros.
41 * To use a vector, a new type must be defined:
44 * struct vec_int VLC_VECTOR(int);
47 * The struct may be anonymous:
50 * struct VLC_VECTOR(const char *) names;
53 * It is convenient to define a typedef to an anonymous structure:
56 * typedef struct VLC_VECTOR(int) vec_int_t;
59 * Vector size is accessible via `vec.size`, and items are intended to be
60 * accessed directly, via `vec.data[i]`.
62 * Functions and macros having name ending with '_' are private.
64 #define VLC_VECTOR(type) \
72 * Static initializer for a vector.
74 #define VLC_VECTOR_INITIALIZER { 0, 0, NULL }
77 * Initialize an empty vector.
79 #define vlc_vector_init(pv) (void) \
81 /* cannot be implemened as do-while(0), called from vlc_vector_clear() */ \
90 * The vector may not be used anymore unless vlc_vector_init() is called.
92 #define vlc_vector_destroy(pv) \
98 * Remove all items from the vector.
100 #define vlc_vector_clear(pv) \
102 /* cannot be implemened as do-while(0), called from vlc_vector_resize_() */ \
103 vlc_vector_destroy(pv), \
104 vlc_vector_init(pv) \
108 * The minimal allocation size, in number of items.
112 #define VLC_VECTOR_MINCAP_ ((size_t) 10)
115 vlc_vector_min_(size_t a
, size_t b
)
117 return a
< b
? a
: b
;
121 vlc_vector_max_(size_t a
, size_t b
)
123 return a
> b
? a
: b
;
127 vlc_vector_between_(size_t x
, size_t min
, size_t max
)
129 return vlc_vector_max_(min
, vlc_vector_min_(max
, x
));
133 vlc_vector_enforce_size_t_(size_t value
)
138 #define VLC_VECTOR_FAILFLAG_ (~(((size_t) -1) >> 1)) /* only the MSB */
141 * Realloc data and update vector fields.
143 * On reallocation success, return the reallocated array and update the vector
146 * On reallocation failure, return `ptr`, keep `*psize` untouched, and set the
147 * failflag in `*pcap` to indicate allocation failure (to be consumed by
148 * `vlc_vector_test_and_reset_failflag_()`).
150 * This weird behavior allows to simultaneously:
151 * - not require compiler extensions like "statement expressions"
152 * - keep the vector data, size and capacity unchanged on reallocation failure
153 * - not require output variables other than vector fields from the caller
154 * - not violate the strict aliasing rules
155 * - report the reallocation status (success or failure)
159 * \param ptr the current data to realloc
160 * \param count the requested capacity, in number of items
161 * \param size the size of one item
162 * \param pcap a pointer to the `cap` field of the vector [IN/OUT]
163 * \param psize a pointer to the `size` field of the vector [IN/OUT]
164 * \return the reallocated array, or `ptr` if reallocation failed
167 vlc_vector_reallocdata_(void *ptr
, size_t count
, size_t size
,
168 size_t *restrict pcap
, size_t *restrict psize
)
170 void *n
= vlc_reallocarray(ptr
, count
, size
);
173 /* this vector implementation guarantees that the capacity may not
174 * exceed SIZE_MAX/2 (to prevent overflows), so we can use the MSB to
175 * report allocation failure */
176 *pcap
|= VLC_VECTOR_FAILFLAG_
;
180 *psize
= vlc_vector_min_(*psize
, count
);
185 * Test and reset the fail flag.
187 * \retval true if the flag was set
188 * \retval false if the flag was not set
191 vlc_vector_test_and_reset_failflag_(size_t *pcap
)
193 if (*pcap
& VLC_VECTOR_FAILFLAG_
)
195 *pcap
&= ~VLC_VECTOR_FAILFLAG_
;
202 * Realloc the underlying array to `newcap`.
206 * \param pv a pointer to the vector
207 * \param newcap (size_t) the requested size
208 * \retval true if no allocation failed
209 * \retval false on allocation failure (the vector is left untouched)
211 #define vlc_vector_realloc_(pv, newcap) \
213 (pv)->data = vlc_vector_reallocdata_((pv)->data, newcap, \
214 sizeof(*(pv)->data), \
215 &(pv)->cap, &(pv)->size), \
216 !vlc_vector_test_and_reset_failflag_(&(pv)->cap) \
220 * Resize the vector to `newcap` exactly.
222 * If `newcap` is 0, the vector is cleared.
226 * \param pv a pointer to the vector
227 * \param newcap (size_t) the requested capacity
228 * \retval true if no allocation failed
229 * \retval false on allocation failure (the vector is left untouched)
231 #define vlc_vector_resize_(pv, newcap) \
233 (pv)->cap == (newcap) /* nothing to do */ || \
235 (newcap) > 0 ? vlc_vector_realloc_(pv, newcap) \
236 : (vlc_vector_clear(pv), true) \
241 vlc_vector_growsize_(size_t value
)
243 /* integer multiplication by 1.5 */
244 return value
+ (value
>> 1);
247 /* SIZE_MAX/2 to fit in ssize_t, and so that cap*1.5 does not overflow. */
248 #define vlc_vector_max_cap_(pv) (SIZE_MAX / 2 / sizeof(*(pv)->data))
251 * Increase the capacity of the vector to at least `mincap`.
253 * \param pv a pointer to the vector
254 * \param mincap (size_t) the requested capacity
255 * \retval true if no allocation failed
256 * \retval false on allocation failure (the vector is left untouched)
258 #define vlc_vector_reserve(pv, mincap) \
259 /* avoid to allocate tiny arrays (< VLC_VECTOR_MINCAP_) */ \
260 vlc_vector_reserve_internal_(pv, \
261 vlc_vector_max_(mincap, VLC_VECTOR_MINCAP_))
263 #define vlc_vector_reserve_internal_(pv, mincap) \
265 (mincap) <= (pv)->cap /* nothing to do */ || \
267 (mincap) <= vlc_vector_max_cap_(pv) /* not too big */ && \
268 vlc_vector_realloc_(pv, \
269 /* multiply by 1.5, force between [mincap, maxcap] */ \
270 vlc_vector_between_(vlc_vector_growsize_((pv)->cap), \
272 vlc_vector_max_cap_(pv))) \
277 * Resize the vector so that its capacity equals its actual size.
279 * \param pv a pointer to the vector
281 #define vlc_vector_shrink_to_fit(pv) \
282 (void) /* decreasing the size may not fail */ \
283 vlc_vector_resize_(pv, (pv)->size)
286 * Resize the vector down automatically.
288 * Shrink only when necessary (in practice when cap > (size+5)*1.5)
290 * \param pv a pointer to the vector
292 #define vlc_vector_autoshrink(pv) (void) \
294 (pv)->cap <= VLC_VECTOR_MINCAP_ /* do not shrink to tiny length */ || \
295 (pv)->cap < vlc_vector_growsize_((pv)->size+5) /* no need to shrink */ || \
296 vlc_vector_resize_(pv, vlc_vector_max_((pv)->size+5, VLC_VECTOR_MINCAP_)) \
299 #define vlc_vector_check_same_ptr_type_(a, b) \
300 (void) ((a) == (b)) /* warn on type mismatch */
303 * Push an item at the end of the vector.
305 * The amortized complexity is O(1).
307 * \param pv a pointer to the vector
308 * \param item the item to append
309 * \retval true if no allocation failed
310 * \retval false on allocation failure (the vector is left untouched)
312 #define vlc_vector_push(pv, item) \
314 vlc_vector_reserve(pv, (pv)->size + 1) && \
316 (pv)->data[(pv)->size++] = (item), \
322 * Append `count` items at the end of the vector.
324 * \param pv a pointer to the vector
325 * \param items the items array to append
326 * \param count the number of items in the array
327 * \retval true if no allocation failed
328 * \retval false on allocation failure (the vector is left untouched)
330 #define vlc_vector_push_all(pv, items, count) \
331 vlc_vector_push_all_internal_(pv, items, vlc_vector_enforce_size_t_(count))
333 #define vlc_vector_push_all_internal_(pv, items, count) \
335 vlc_vector_check_same_ptr_type_((pv)->data, items), \
336 vlc_vector_reserve(pv, (pv)->size + (count)) && \
338 memcpy(&(pv)->data[(pv)->size], items, (count) * sizeof(*(pv)->data)), \
339 (pv)->size += (count), \
345 * Insert an hole of size `count` to the given index.
347 * The items in range [index; size-1] will be moved. The items in the hole are
348 * left uninitialized.
350 * \param pv a pointer to the vector
351 * \param index the index where the hole is to be inserted
352 * \param count the number of items in the hole
353 * \retval true if no allocation failed
354 * \retval false on allocation failure (the vector is left untouched)
356 #define vlc_vector_insert_hole(pv, index, count) \
357 vlc_vector_insert_hole_internal_(pv, vlc_vector_enforce_size_t_(index), \
358 vlc_vector_enforce_size_t_(count))
360 #define vlc_vector_insert_hole_internal_(pv, index, count) \
362 vlc_vector_reserve(pv, (pv)->size + (count)) && \
364 (index) == (pv)->size || \
366 memmove(&(pv)->data[(index) + (count)], \
367 &(pv)->data[index], \
368 ((pv)->size - (index)) * sizeof(*(pv)->data)), \
373 (pv)->size += (count), \
379 * Insert an item at the given index.
381 * The items in range [index; size-1] will be moved.
383 * \param pv a pointer to the vector
384 * \param index the index where the item is to be inserted
385 * \param item the item to append
386 * \retval true if no allocation failed
387 * \retval false on allocation failure (the vector is left untouched)
389 #define vlc_vector_insert(pv, index, item) \
391 vlc_vector_insert_hole(pv, index, 1) && \
393 (pv)->data[index] = (item), \
399 * Insert `count` items at the given index.
401 * The items in range [index; size-1] will be moved.
403 * \param pv a pointer to the vector
404 * \param index the index where the items are to be inserted
405 * \param items the items array to append
406 * \param count the number of items in the array
407 * \retval true if no allocation failed
408 * \retval false on allocation failure (the vector is left untouched)
410 #define vlc_vector_insert_all(pv, index, items, count) \
412 vlc_vector_check_same_ptr_type_((pv)->data, items), \
413 vlc_vector_insert_hole(pv, index, count) && \
415 memcpy(&(pv)->data[index], items, (count) * sizeof(*(pv)->data)), \
420 /** Reverse a char array in place. */
422 vlc_vector_reverse_array_(char *array
, size_t len
)
424 for (size_t i
= 0; i
< len
/ 2; ++i
)
427 array
[i
] = array
[len
- i
- 1];
428 array
[len
- i
- 1] = c
;
433 * Right-rotate a (char) array in place.
435 * For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
436 * distance 4 will result in {5, 6, 1, 2, 3, 4}.
441 vlc_vector_rotate_array_left_(char *array
, size_t len
, size_t distance
)
443 vlc_vector_reverse_array_(array
, distance
);
444 vlc_vector_reverse_array_(&array
[distance
], len
- distance
);
445 vlc_vector_reverse_array_(array
, len
);
449 * Right-rotate a (char) array in place.
451 * For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
452 * distance 2 will result in {5, 6, 1, 2, 3, 4}.
457 vlc_vector_rotate_array_right_(char *array
, size_t len
, size_t distance
)
459 vlc_vector_rotate_array_left_(array
, len
, len
- distance
);
463 * Move items in a (char) array in place.
465 * Move slice [index, count] to target.
468 vlc_vector_move_(char *array
, size_t index
, size_t count
, size_t target
)
471 vlc_vector_rotate_array_left_(&array
[index
], target
- index
+ count
,
474 vlc_vector_rotate_array_right_(&array
[target
], index
- target
+ count
,
479 * Move a slice of items to a given target index.
481 * The items in range [index; count] will be moved so that the *new* position
482 * of the first item is `target`.
484 * \param pv a pointer to the vector
485 * \param index the index of the first item to move
486 * \param count the number of items to move
487 * \param target the new index of the moved slice
489 #define vlc_vector_move_slice(pv, index, count, target) \
490 vlc_vector_move_slice_internal_(pv, \
491 vlc_vector_enforce_size_t_(index), \
492 vlc_vector_enforce_size_t_(count), \
493 vlc_vector_enforce_size_t_(target))
495 #define vlc_vector_move_slice_internal_(pv, index, count, target) \
496 vlc_vector_move_((char *) (pv)->data, \
497 (index) * sizeof((pv)->data[0]), \
498 (count) * sizeof((pv)->data[0]), \
499 (target) * sizeof((pv)->data[0]))
502 * Move an item to a given target index.
504 * The items will be moved so that its *new* position is `target`.
506 * \param pv a pointer to the vector
507 * \param index the index of the item to move
508 * \param target the new index of the moved item
510 #define vlc_vector_move(pv, index, target) \
511 vlc_vector_move_slice(pv, index, 1, target)
514 * Remove a slice of items, without shrinking the array.
516 * If you have no good reason to use the _noshrink() version, use
517 * vlc_vector_remove_slice() instead.
519 * The items in range [index+count; size-1] will be moved.
521 * \param pv a pointer to the vector
522 * \param index the index of the first item to remove
523 * \param count the number of items to remove
525 #define vlc_vector_remove_slice_noshrink(pv, index, count) \
526 vlc_vector_remove_slice_noshrink_internal_(pv, \
527 vlc_vector_enforce_size_t_(index), \
528 vlc_vector_enforce_size_t_(count))
530 #define vlc_vector_remove_slice_noshrink_internal_(pv, index, count) \
532 if ((index) + (count) < (pv)->size) \
533 memmove(&(pv)->data[index], \
534 &(pv)->data[(index) + (count)], \
535 ((pv)->size - (index) - (count)) * sizeof(*(pv)->data)); \
536 (pv)->size -= (count); \
540 * Remove a slice of items.
542 * The items in range [index+count; size-1] will be moved.
544 * \param pv a pointer to the vector
545 * \param index the index of the first item to remove
546 * \param count the number of items to remove
548 #define vlc_vector_remove_slice(pv, index, count) \
550 vlc_vector_remove_slice_noshrink(pv, index, count); \
551 vlc_vector_autoshrink(pv); \
555 * Remove an item, without shrinking the array.
557 * If you have no good reason to use the _noshrink() version, use
558 * vlc_vector_remove() instead.
560 * The items in range [index+1; size-1] will be moved.
562 * \param pv a pointer to the vector
563 * \param index the index of item to remove
565 #define vlc_vector_remove_noshrink(pv, index) \
566 vlc_vector_remove_slice_noshrink(pv, index, 1)
571 * The items in range [index+1; size-1] will be moved.
573 * \param pv a pointer to the vector
574 * \param index the index of item to remove
576 #define vlc_vector_remove(pv, index) \
578 vlc_vector_remove_noshrink(pv, index); \
579 vlc_vector_autoshrink(pv); \
585 * The removed item is replaced by the last item of the vector.
587 * This does not preserve ordering, but is O(1). This is useful when the order
588 * of items is not meaningful.
590 * \param pv a pointer to the vector
591 * \param index the index of item to remove
593 #define vlc_vector_swap_remove(pv, index) \
594 (pv)->data[index] = (pv)->data[--(pv)->size]
597 * Return the index of an item.
599 * Iterate over all items to find a given item.
601 * Use only for vectors of primitive types or pointers.
603 * The result is written to `*(pidx)`:
604 * - the index of the item if it is found;
605 * - -1 if it is not found.
607 * \param pv a pointer to the vector
608 * \param item the item to find (compared with ==)
609 * \param a pointer to the result (ssize_t *) [OUT]
611 #define vlc_vector_index_of(pv, item, pidx) \
613 ssize_t *out = pidx; /* warning/error on type mismatch */ \
614 size_t vlc_vector_find_idx_; \
615 for (vlc_vector_find_idx_ = 0; \
616 vlc_vector_find_idx_ < (pv)->size; \
617 ++vlc_vector_find_idx_) \
618 if ((pv)->data[vlc_vector_find_idx_] == (item)) \
620 *out = vlc_vector_find_idx_ == (pv)->size ? -1 : \
621 (ssize_t) vlc_vector_find_idx_; \
627 * Use only for vectors of primitive types or pointers (every struct would be
628 * copied for a vector of structs).
630 * \param item the iteration variable [OUT]
631 * \param pv a pointer to the vector [OUT]
633 #define vlc_vector_foreach(item, pv) \
634 for (size_t vlc_vector_idx_##item = 0; \
635 vlc_vector_idx_##item < (pv)->size && \
636 ((item) = (pv)->data[vlc_vector_idx_##item], true); \
637 ++vlc_vector_idx_##item)