VLC 4.0.0-dev
Loading...
Searching...
No Matches
vlc_vector.h
Go to the documentation of this file.
1/******************************************************************************
2 * vlc_vector.h
3 ******************************************************************************
4 * Copyright (C) 2018 VLC authors and VideoLAN
5 *
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.
10 *
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.
15 *
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 *****************************************************************************/
20#ifndef VLC_VECTOR_H
21#define VLC_VECTOR_H
22
23#include <assert.h>
24#include <stdbool.h>
25#include <stddef.h>
26
27/**
28 * \defgroup vector Vector
29 * \ingroup cext
30 * @{
31 * \file
32 * This provides convenience helpers for vectors.
33 */
34
35/**
36 * Vector struct body.
37 *
38 * A vector is a dynamic array, managed by the vlc_vector_* helpers.
39 *
40 * It is generic over the type of its items, so it is implemented as macros.
41 *
42 * To use a vector, a new type must be defined:
43 *
44 * \code
45 * struct vec_int VLC_VECTOR(int);
46 * \endcode
47 *
48 * The struct may be anonymous:
49 *
50 * \code
51 * struct VLC_VECTOR(const char *) names;
52 * \endcode
53 *
54 * It is convenient to define a typedef to an anonymous structure:
55 *
56 * \code
57 * typedef struct VLC_VECTOR(int) vec_int_t;
58 * \endcode
59 *
60 * Vector size is accessible via `vec.size`, and items are intended to be
61 * accessed directly, via `vec.data[i]`.
62 *
63 * Functions and macros having name ending with '_' are private.
64 */
65#define VLC_VECTOR(type) \
66{ \
67 size_t cap; \
68 size_t size; \
69 type *data; \
70}
71
72/**
73 * Static initializer for a vector.
74 */
75#define VLC_VECTOR_INITIALIZER { 0, 0, NULL }
77/**
78 * Initialize an empty vector.
79 */
80#define vlc_vector_init(pv) (void) \
81( \
82 /* cannot be implemented as do-while(0), called from vlc_vector_clear() */ \
83 (pv)->cap = 0, \
84 (pv)->size = 0, \
85 (pv)->data = NULL \
86)
87
88/**
89 * Destroy a vector.
90 *
91 * The vector may not be used anymore unless vlc_vector_init() is called.
92 */
93#define vlc_vector_destroy(pv) \
94 free((pv)->data)
95
96/**
97 * Clear a vector.
98 *
99 * Remove all items from the vector.
100 */
101#define vlc_vector_clear(pv) \
102( \
103 /* cannot be implemented as do-while(0), called from vlc_vector_resize_() */ \
104 vlc_vector_destroy(pv), \
105 vlc_vector_init(pv) \
106)
107
108/**
109 * The minimal allocation size, in number of items.
110 *
111 * Private.
112 */
113#define VLC_VECTOR_MINCAP_ ((size_t) 10)
115static inline size_t
116vlc_vector_min_(size_t a, size_t b)
118 return a < b ? a : b;
119}
120
121static inline size_t
122vlc_vector_max_(size_t a, size_t b)
124 return a > b ? a : b;
125}
126
127static inline size_t
128vlc_vector_between_(size_t x, size_t min, size_t max)
130 return vlc_vector_max_(min, vlc_vector_min_(max, x));
131}
132
133static inline size_t
134vlc_vector_enforce_size_t_(size_t value)
136 return value;
137}
138
139#define VLC_VECTOR_FAILFLAG_ (~(((size_t) -1) >> 1)) /* only the MSB */
141/**
142 * Realloc data and update vector fields.
143 *
144 * On reallocation success, return the reallocated array and update the vector
145 * capacity and size.
146 *
147 * On reallocation failure, return `ptr`, keep `*psize` untouched, and set the
148 * failflag in `*pcap` to indicate allocation failure (to be consumed by
149 * `vlc_vector_test_and_reset_failflag_()`).
150 *
151 * This weird behavior allows to simultaneously:
152 * - not require compiler extensions like "statement expressions"
153 * - keep the vector data, size and capacity unchanged on reallocation failure
154 * - not require output variables other than vector fields from the caller
155 * - not violate the strict aliasing rules
156 * - report the reallocation status (success or failure)
157 *
158 * Private.
159 *
160 * \param ptr the current data to realloc
161 * \param count the requested capacity, in number of items
162 * \param size the size of one item
163 * \param[in,out] pcap a pointer to the `cap` field of the vector
164 * \param[in,out] psize a pointer to the `size` field of the vector
165 * \return the reallocated array, or `ptr` if reallocation failed
166 */
167static inline void *
168vlc_vector_reallocdata_(void *ptr, size_t count, size_t size,
169 size_t *restrict pcap, size_t *restrict psize)
170{
171 void *n = vlc_reallocarray(ptr, count, size);
172 if (!n)
173 {
174 /* this vector implementation guarantees that the capacity may not
175 * exceed SIZE_MAX/2 (to prevent overflows), so we can use the MSB to
176 * report allocation failure */
177 *pcap |= VLC_VECTOR_FAILFLAG_;
178 return ptr;
179 }
180 *pcap = count;
181 *psize = vlc_vector_min_(*psize, count);
182 return n;
183}
184
185/**
186 * Test and reset the fail flag.
187 *
188 * \retval true if the flag was set
189 * \retval false if the flag was not set
190 */
191static inline bool
194 if (*pcap & VLC_VECTOR_FAILFLAG_)
195 {
196 *pcap &= ~VLC_VECTOR_FAILFLAG_;
197 return true;
198 }
199 return false;
200}
201
202/**
203 * Realloc the underlying array to `newcap`.
204 *
205 * Private.
206 *
207 * \param pv a pointer to the vector
208 * \param newcap (size_t) the requested size
209 * \retval true if no allocation failed
210 * \retval false on allocation failure (the vector is left untouched)
211 */
212#define vlc_vector_realloc_(pv, newcap) \
213( \
214 (pv)->data = vlc_vector_reallocdata_((pv)->data, newcap, \
215 sizeof(*(pv)->data), \
216 &(pv)->cap, &(pv)->size), \
217 !vlc_vector_test_and_reset_failflag_(&(pv)->cap) \
218)
219
220/**
221 * Resize the vector to `newcap` exactly.
222 *
223 * If `newcap` is 0, the vector is cleared.
224 *
225 * Private.
226 *
227 * \param pv a pointer to the vector
228 * \param newcap (size_t) the requested capacity
229 * \retval true if no allocation failed
230 * \retval false on allocation failure (the vector is left untouched)
231 */
232#define vlc_vector_resize_(pv, newcap) \
233( \
234 (pv)->cap == (newcap) /* nothing to do */ || \
235 ( \
236 (newcap) > 0 ? vlc_vector_realloc_(pv, newcap) \
237 : (vlc_vector_clear(pv), true) \
238 ) \
239)
240
241static inline size_t
242vlc_vector_growsize_(size_t value)
244 /* integer multiplication by 1.5 */
245 return value + (value >> 1);
246}
247
248/* SIZE_MAX/2 to fit in ssize_t, and so that cap*1.5 does not overflow. */
249#define vlc_vector_max_cap_(pv) (SIZE_MAX / 2 / sizeof(*(pv)->data))
251/**
252 * Increase the capacity of the vector to at least `mincap`.
253 *
254 * \param pv a pointer to the vector
255 * \param mincap (size_t) the requested capacity
256 * \retval true if no allocation failed
257 * \retval false on allocation failure (the vector is left untouched)
258 */
259#define vlc_vector_reserve(pv, mincap) \
260 /* avoid to allocate tiny arrays (< VLC_VECTOR_MINCAP_) */ \
261 vlc_vector_reserve_internal_(pv, \
262 vlc_vector_max_(mincap, VLC_VECTOR_MINCAP_))
263
264#define vlc_vector_reserve_internal_(pv, mincap) \
265( \
266 (mincap) <= (pv)->cap /* nothing to do */ || \
267 ( \
268 (mincap) <= vlc_vector_max_cap_(pv) /* not too big */ && \
269 vlc_vector_realloc_(pv, \
270 /* multiply by 1.5, force between [mincap, maxcap] */ \
271 vlc_vector_between_(vlc_vector_growsize_((pv)->cap), \
272 mincap, \
273 vlc_vector_max_cap_(pv))) \
274 ) \
275)
276
277/**
278 * Resize the vector so that its capacity equals its actual size.
279 *
280 * \param pv a pointer to the vector
281 */
282#define vlc_vector_shrink_to_fit(pv) \
283 (void) /* decreasing the size may not fail */ \
284 vlc_vector_resize_(pv, (pv)->size)
285
286/**
287 * Resize the vector down automatically.
288 *
289 * Shrink only when necessary (in practice when cap > (size+5)*1.5)
290 *
291 * \param pv a pointer to the vector
292 */
293#define vlc_vector_autoshrink(pv) (void) \
294( \
295 (pv)->cap <= VLC_VECTOR_MINCAP_ /* do not shrink to tiny length */ || \
296 (pv)->cap < vlc_vector_growsize_((pv)->size+5) /* no need to shrink */ || \
297 vlc_vector_resize_(pv, vlc_vector_max_((pv)->size+5, VLC_VECTOR_MINCAP_)) \
298)
299
300#define vlc_vector_check_same_ptr_type_(a, b) \
301 (void) ((a) == (b)) /* warn on type mismatch */
302
303/**
304 * Push an item at the end of the vector.
305 *
306 * The amortized complexity is O(1).
307 *
308 * \param pv a pointer to the vector
309 * \param item the item to append
310 * \retval true if no allocation failed
311 * \retval false on allocation failure (the vector is left untouched)
312 */
313#define vlc_vector_push(pv, item) \
314( \
315 vlc_vector_reserve(pv, (pv)->size + 1) && \
316 ( \
317 (pv)->data[(pv)->size++] = (item), \
318 true \
319 ) \
320)
321
322/**
323 * Push a hole at the end of the vector.
324 *
325 * The amortized complexity is O(1). The items in the hole are left
326 * uninitialized and can be accessed in the range [size-count; size-1].
327 *
328 * \param pv a pointer to the vector
329 * \param count the number of items in the hole
330 * \retval true if no allocation failed
331 * \retval false on allocation failure (the vector is left untouched)
332 */
333#define vlc_vector_push_hole(pv, count) \
334( \
335 vlc_vector_reserve(pv, (pv)->size + vlc_vector_enforce_size_t_(count)) && \
336 ( \
337 (pv)->size += vlc_vector_enforce_size_t_(count), \
338 true \
339 ) \
340)
341
342/**
343 * Append `count` items at the end of the vector.
344 *
345 * \param pv a pointer to the vector
346 * \param items the items array to append
347 * \param count the number of items in the array
348 * \retval true if no allocation failed
349 * \retval false on allocation failure (the vector is left untouched)
350 */
351#define vlc_vector_push_all(pv, items, count) \
352 vlc_vector_push_all_internal_(pv, items, vlc_vector_enforce_size_t_(count))
353
354#define vlc_vector_push_all_internal_(pv, items, count) \
355( \
356 assert(count), \
357 vlc_vector_check_same_ptr_type_((pv)->data, items), \
358 vlc_vector_reserve(pv, (pv)->size + (count)) && \
359 ( \
360 memcpy(&(pv)->data[(pv)->size], items, (count) * sizeof(*(pv)->data)), \
361 (pv)->size += (count), \
362 true \
363 ) \
364)
365
366/**
367 * Insert an hole of size `count` to the given index.
368 *
369 * The items in range [index; size-1] will be moved. The items in the hole are
370 * left uninitialized.
371 *
372 * \param pv a pointer to the vector
373 * \param index the index where the hole is to be inserted
374 * \param count the number of items in the hole
375 * \retval true if no allocation failed
376 * \retval false on allocation failure (the vector is left untouched)
377 */
378#define vlc_vector_insert_hole(pv, index, count) \
379 vlc_vector_insert_hole_internal_(pv, vlc_vector_enforce_size_t_(index), \
380 vlc_vector_enforce_size_t_(count))
381
382#define vlc_vector_insert_hole_internal_(pv, index, count) \
383( \
384 assert(count), \
385 vlc_vector_reserve(pv, (pv)->size + (count)) && \
386 ( \
387 (index) == (pv)->size || \
388 ( \
389 memmove(&(pv)->data[(index) + (count)], \
390 &(pv)->data[index], \
391 ((pv)->size - (index)) * sizeof(*(pv)->data)), \
392 true \
393 ) \
394 ) && \
395 ( \
396 (pv)->size += (count), \
397 true \
398 ) \
399)
400
401/**
402 * Insert an item at the given index.
403 *
404 * The items in range [index; size-1] will be moved.
405 *
406 * \param pv a pointer to the vector
407 * \param index the index where the item is to be inserted
408 * \param item the item to append
409 * \retval true if no allocation failed
410 * \retval false on allocation failure (the vector is left untouched)
411 */
412#define vlc_vector_insert(pv, index, item) \
413( \
414 vlc_vector_insert_hole(pv, index, 1) && \
415 ( \
416 (pv)->data[index] = (item), \
417 true \
418 ) \
419)
420
421/**
422 * Insert `count` items at the given index.
423 *
424 * The items in range [index; size-1] will be moved.
425 *
426 * \param pv a pointer to the vector
427 * \param index the index where the items are to be inserted
428 * \param items the items array to append
429 * \param count the number of items in the array
430 * \retval true if no allocation failed
431 * \retval false on allocation failure (the vector is left untouched)
432 */
433#define vlc_vector_insert_all(pv, index, items, count) \
434( \
435 vlc_vector_check_same_ptr_type_((pv)->data, items), \
436 vlc_vector_insert_hole(pv, index, count) && \
437 ( \
438 memcpy(&(pv)->data[index], items, (count) * sizeof(*(pv)->data)), \
439 true \
440 ) \
441)
442
443/** Reverse a char array in place. */
444static inline void
445vlc_vector_reverse_array_(char *array, size_t len)
447 for (size_t i = 0; i < len / 2; ++i)
448 {
449 char c = array[i];
450 array[i] = array[len - i - 1];
451 array[len - i - 1] = c;
452 }
453}
454
455/**
456 * Right-rotate a (char) array in place.
457 *
458 * For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
459 * distance 4 will result in {5, 6, 1, 2, 3, 4}.
460 *
461 * Private.
462 */
463static inline void
464vlc_vector_rotate_array_left_(char *array, size_t len, size_t distance)
466 vlc_vector_reverse_array_(array, distance);
467 vlc_vector_reverse_array_(&array[distance], len - distance);
468 vlc_vector_reverse_array_(array, len);
469}
470
471/**
472 * Right-rotate a (char) array in place.
473 *
474 * For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
475 * distance 2 will result in {5, 6, 1, 2, 3, 4}.
476 *
477 * Private.
478 */
479static inline void
480vlc_vector_rotate_array_right_(char *array, size_t len, size_t distance)
482 vlc_vector_rotate_array_left_(array, len, len - distance);
483}
484
485/**
486 * Move items in a (char) array in place.
487 *
488 * Move slice [index, count] to target.
489 */
490static inline void
491vlc_vector_move_(char *array, size_t index, size_t count, size_t target)
493 assert(count);
494 if (index < target)
495 vlc_vector_rotate_array_left_(&array[index], target - index + count,
496 count);
497 else
498 vlc_vector_rotate_array_right_(&array[target], index - target + count,
499 count);
500}
501
502/**
503 * Move a slice of items to a given target index.
504 *
505 * The items in range [index; count] will be moved so that the *new* position
506 * of the first item is `target`.
507 *
508 * \param pv a pointer to the vector
509 * \param index the index of the first item to move
510 * \param count the number of items to move
511 * \param target the new index of the moved slice
512 */
513#define vlc_vector_move_slice(pv, index, count, target) \
514 vlc_vector_move_slice_internal_(pv, \
515 vlc_vector_enforce_size_t_(index), \
516 vlc_vector_enforce_size_t_(count), \
517 vlc_vector_enforce_size_t_(target))
518
519#define vlc_vector_move_slice_internal_(pv, index, count, target) \
520 vlc_vector_move_((char *) (pv)->data, \
521 (index) * sizeof((pv)->data[0]), \
522 (count) * sizeof((pv)->data[0]), \
523 (target) * sizeof((pv)->data[0]))
524
525/**
526 * Move an item to a given target index.
527 *
528 * The items will be moved so that its *new* position is `target`.
529 *
530 * \param pv a pointer to the vector
531 * \param index the index of the item to move
532 * \param target the new index of the moved item
533 */
534#define vlc_vector_move(pv, index, target) \
535 vlc_vector_move_slice(pv, index, 1, target)
536
537/**
538 * Remove a slice of items, without shrinking the array.
539 *
540 * If you have no good reason to use the _noshrink() version, use
541 * vlc_vector_remove_slice() instead.
542 *
543 * The items in range [index+count; size-1] will be moved.
544 *
545 * \param pv a pointer to the vector
546 * \param index the index of the first item to remove
547 * \param count the number of items to remove
548 */
549#define vlc_vector_remove_slice_noshrink(pv, index, count) \
550 vlc_vector_remove_slice_noshrink_internal_(pv, \
551 vlc_vector_enforce_size_t_(index), \
552 vlc_vector_enforce_size_t_(count))
553
554#define vlc_vector_remove_slice_noshrink_internal_(pv, index, count) \
555 do { \
556 assert(count); \
557 if ((index) + (count) < (pv)->size) \
558 memmove(&(pv)->data[index], \
559 &(pv)->data[(index) + (count)], \
560 ((pv)->size - (index) - (count)) * sizeof(*(pv)->data)); \
561 (pv)->size -= (count); \
562 } while (0)
563
564/**
565 * Remove a slice of items.
566 *
567 * The items in range [index+count; size-1] will be moved.
568 *
569 * \param pv a pointer to the vector
570 * \param index the index of the first item to remove
571 * \param count the number of items to remove
572 */
573#define vlc_vector_remove_slice(pv, index, count) \
574 do { \
575 vlc_vector_remove_slice_noshrink(pv, index, count); \
576 vlc_vector_autoshrink(pv); \
577 } while (0)
578
579/**
580 * Remove an item, without shrinking the array.
581 *
582 * If you have no good reason to use the _noshrink() version, use
583 * vlc_vector_remove() instead.
584 *
585 * The items in range [index+1; size-1] will be moved.
586 *
587 * \param pv a pointer to the vector
588 * \param index the index of item to remove
589 */
590#define vlc_vector_remove_noshrink(pv, index) \
591 vlc_vector_remove_slice_noshrink(pv, index, 1)
592
593/**
594 * Remove an item.
595 *
596 * The items in range [index+1; size-1] will be moved.
597 *
598 * \param pv a pointer to the vector
599 * \param index the index of item to remove
600 */
601#define vlc_vector_remove(pv, index) \
602 do { \
603 vlc_vector_remove_noshrink(pv, index); \
604 vlc_vector_autoshrink(pv); \
605 } while (0)
606
607/**
608 * Remove an item.
609 *
610 * The removed item is replaced by the last item of the vector.
611 *
612 * This does not preserve ordering, but is O(1). This is useful when the order
613 * of items is not meaningful.
614 *
615 * \param pv a pointer to the vector
616 * \param index the index of item to remove
617 */
618#define vlc_vector_swap_remove(pv, index) \
619 do { \
620 (pv)->data[index] = (pv)->data[(pv)->size-1]; \
621 (pv)->size--; \
622 } while(0)
623
624/**
625 * Return the index of an item.
626 *
627 * Iterate over all items to find a given item.
628 *
629 * Use only for vectors of primitive types or pointers.
630 *
631 * The result is written to `*(pidx)`:
632 * - the index of the item if it is found;
633 * - -1 if it is not found.
634 *
635 * \param pv a pointer to the vector
636 * \param item the item to find (compared with ==)
637 * \param[out] pidx a pointer to the result (ssize_t *)
638 */
639#define vlc_vector_index_of(pv, item, pidx) \
640 do { \
641 ssize_t *out = pidx; /* warning/error on type mismatch */ \
642 size_t vlc_vector_find_idx_; \
643 for (vlc_vector_find_idx_ = 0; \
644 vlc_vector_find_idx_ < (pv)->size; \
645 ++vlc_vector_find_idx_) \
646 if ((pv)->data[vlc_vector_find_idx_] == (item)) \
647 break; \
648 *out = vlc_vector_find_idx_ == (pv)->size ? -1 : \
649 (ssize_t) vlc_vector_find_idx_; \
650 } while (0)
651
652/**
653 * For-each loop.
654 *
655 * Use only for vectors of primitive types or pointers (every struct would be
656 * copied for a vector of structs).
657 *
658 * \param[out] item the iteration variable
659 * \param[out] pv a pointer to the vector
660 */
661#define vlc_vector_foreach(item, pv) \
662 for (size_t vlc_vector_idx_##item = 0; \
663 vlc_vector_idx_##item < (pv)->size && \
664 ((item) = (pv)->data[vlc_vector_idx_##item], true); \
665 ++vlc_vector_idx_##item)
666
667/**
668 * For-each loop with a reference iterator.
669 *
670 * Should be used for vector holding non-trivially copyable data.
671 *
672 * \param[out] ref The reference iterator
673 * \param[in] pv a pointer to the vector
674 */
675#define vlc_vector_foreach_ref(ref, pv) \
676 for (size_t vlc_vector_idx_##ref = 0; \
677 vlc_vector_idx_##ref < (pv)->size && \
678 ((ref) = &(pv)->data[vlc_vector_idx_##ref], true); \
679 ++vlc_vector_idx_##ref)
680
681/**
682 * Returns the vector's last element.
683 */
684#define vlc_vector_last(pv) \
685( \
686 assert((pv)->size != 0), \
687 (pv)->data[(pv)->size - 1] \
688)
689
690/**
691 * Returns a reference on the vector's last element.
692 */
693#define vlc_vector_last_ref(pv) \
694( \
695 assert((pv)->size != 0), \
696 &(pv)->data[(pv)->size - 1] \
697)
698
699/** @} */
700
701#endif
size_t count
Definition core.c:403
static size_t vlc_vector_growsize_(size_t value)
Definition vlc_vector.h:243
static void vlc_vector_reverse_array_(char *array, size_t len)
Reverse a char array in place.
Definition vlc_vector.h:446
static bool vlc_vector_test_and_reset_failflag_(size_t *pcap)
Test and reset the fail flag.
Definition vlc_vector.h:193
static void vlc_vector_rotate_array_right_(char *array, size_t len, size_t distance)
Right-rotate a (char) array in place.
Definition vlc_vector.h:481
static size_t vlc_vector_enforce_size_t_(size_t value)
Definition vlc_vector.h:135
#define VLC_VECTOR_FAILFLAG_
Definition vlc_vector.h:140
static size_t vlc_vector_between_(size_t x, size_t min, size_t max)
Definition vlc_vector.h:129
static void vlc_vector_rotate_array_left_(char *array, size_t len, size_t distance)
Right-rotate a (char) array in place.
Definition vlc_vector.h:465
static size_t vlc_vector_max_(size_t a, size_t b)
Definition vlc_vector.h:123
static void vlc_vector_move_(char *array, size_t index, size_t count, size_t target)
Move items in a (char) array in place.
Definition vlc_vector.h:492
static void * vlc_vector_reallocdata_(void *ptr, size_t count, size_t size, size_t *restrict pcap, size_t *restrict psize)
Realloc data and update vector fields.
Definition vlc_vector.h:169
static size_t vlc_vector_min_(size_t a, size_t b)
Definition vlc_vector.h:117
This file is a collection of common definitions and types.
static void * vlc_reallocarray(void *ptr, size_t count, size_t size)
Definition vlc_common.h:1077